static void mp3_update_xing(AVFormatContext *s)
{
    MP3Context  *mp3 = s->priv_data;
    AVReplayGain *rg;
    uint16_t tag_crc;
    uint8_t *toc;
    int i, rg_size;

    /* replace "Xing" identification string with "Info" for CBR files. */
    if (!mp3->has_variable_bitrate)
        AV_WL32(mp3->xing_frame + mp3->xing_offset, MKTAG('I', 'n', 'f', 'o'));

    AV_WB32(mp3->xing_frame + mp3->xing_offset + 8,  mp3->frames);
    AV_WB32(mp3->xing_frame + mp3->xing_offset + 12, mp3->size);

    toc    = mp3->xing_frame + mp3->xing_offset + 16;
    toc[0] = 0;  // first toc entry has to be zero.
    for (i = 1; i < XING_TOC_SIZE; ++i) {
        int j = i * mp3->pos / XING_TOC_SIZE;
        int seek_point = 256LL * mp3->bag[j] / mp3->size;
        toc[i] = FFMIN(seek_point, 255);
    }

    /* write replaygain */
    rg = (AVReplayGain*)av_stream_get_side_data(s->streams[0], AV_PKT_DATA_REPLAYGAIN,
                                                &rg_size);
    if (rg && rg_size >= sizeof(*rg)) {
        uint16_t val;

        AV_WB32(mp3->xing_frame + mp3->xing_offset + 131,
                av_rescale(rg->track_peak, 1 << 23, 100000));

        if (rg->track_gain != INT32_MIN) {
            val  = FFABS(rg->track_gain / 10000) & ((1 << 9) - 1);
            val |= (rg->track_gain < 0) << 9;
            val |= 1 << 13;
            AV_WB16(mp3->xing_frame + mp3->xing_offset + 135, val);
        }

        if (rg->album_gain != INT32_MIN) {
            val  = FFABS(rg->album_gain / 10000) & ((1 << 9) - 1);
            val |= (rg->album_gain < 0) << 9;
            val |= 1 << 14;
            AV_WB16(mp3->xing_frame + mp3->xing_offset + 137, val);
        }
    }

    AV_WB32(mp3->xing_frame + mp3->xing_offset + XING_SIZE - 8, mp3->audio_size);
    AV_WB16(mp3->xing_frame + mp3->xing_offset + XING_SIZE - 4, mp3->audio_crc);

    tag_crc = av_crc(av_crc_get_table(AV_CRC_16_ANSI_LE), 0, mp3->xing_frame, 190);
    AV_WB16(mp3->xing_frame + mp3->xing_offset + XING_SIZE - 2, tag_crc);

    avio_seek(s->pb,  mp3->xing_frame_offset, SEEK_SET);
    avio_write(s->pb, mp3->xing_frame, mp3->xing_frame_size);
    avio_seek(s->pb, 0, SEEK_END);
}