static int mkv_write_trailer(AVFormatContext *s)
{
    MatroskaMuxContext *mkv = s->priv_data;
    AVIOContext *pb = s->pb;
    int64_t currentpos, cuespos;
    int ret;

    // check if we have an audio packet cached
    if (mkv->cur_audio_pkt.size > 0) {
        ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt, 0);
        av_free_packet(&mkv->cur_audio_pkt);
        if (ret < 0) {
            av_log(s, AV_LOG_ERROR,
                   "Could not write cached audio packet ret:%d\n", ret);
            return ret;
        }
    }

    if (mkv->dyn_bc) {
        end_ebml_master(mkv->dyn_bc, mkv->cluster);
        mkv_flush_dynbuf(s);
    } else if (mkv->cluster_pos != -1) {
        end_ebml_master(pb, mkv->cluster);
    }

    if (mkv->mode != MODE_WEBM) {
        ret = mkv_write_chapters(s);
        if (ret < 0)
            return ret;
    }

    if (pb->seekable) {
        if (mkv->cues->num_entries) {
            if (mkv->reserve_cues_space) {
                int64_t cues_end;

                currentpos = avio_tell(pb);
                avio_seek(pb, mkv->cues_pos, SEEK_SET);

                cuespos  = mkv_write_cues(s, mkv->cues, mkv->tracks, s->nb_streams);
                cues_end = avio_tell(pb);
                if (cues_end > cuespos + mkv->reserve_cues_space) {
                    av_log(s, AV_LOG_ERROR,
                           "Insufficient space reserved for cues: %d "
                           "(needed: %" PRId64 ").\n",
                           mkv->reserve_cues_space, cues_end - cuespos);
                    return AVERROR(EINVAL);
                }

                if (cues_end < cuespos + mkv->reserve_cues_space)
                    put_ebml_void(pb, mkv->reserve_cues_space -
                                  (cues_end - cuespos));

                avio_seek(pb, currentpos, SEEK_SET);
            } else {
                cuespos = mkv_write_cues(s, mkv->cues, mkv->tracks, s->nb_streams);
            }

            ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CUES,
                                         cuespos);
            if (ret < 0)
                return ret;
        }

        mkv_write_seekhead(pb, mkv);

        // update the duration
        av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration);
        currentpos = avio_tell(pb);
        avio_seek(pb, mkv->duration_offset, SEEK_SET);
        put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration);

        // update stream durations
        if (mkv->stream_durations) {
            int i;
            for (i = 0; i < s->nb_streams; ++i) {
                AVStream *st = s->streams[i];
                double duration_sec = mkv->stream_durations[i] * av_q2d(st->time_base);
                char duration_string[20] = "";

                av_log(s, AV_LOG_DEBUG, "stream %d end duration = %" PRIu64 "\n", i,
                       mkv->stream_durations[i]);

                if (!mkv->is_live && mkv->stream_duration_offsets[i] > 0) {
                    avio_seek(pb, mkv->stream_duration_offsets[i], SEEK_SET);

                    snprintf(duration_string, 20, "%02d:%02d:%012.9f",
                             (int) duration_sec / 3600, ((int) duration_sec / 60) % 60,
                             fmod(duration_sec, 60));

                    put_ebml_binary(pb, MATROSKA_ID_TAGSTRING, duration_string, 20);
                }
            }
        }

        avio_seek(pb, currentpos, SEEK_SET);
    }

    if (!mkv->is_live) {
        end_ebml_master(pb, mkv->segment);
    }

    mkv_free(mkv);
    return 0;
}