Skip to content
Snippets Groups Projects
movenc.c 187 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Copyright (c) 2003 Thomas Raivio
     * Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org>
    
     * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
    
     * This file is part of FFmpeg.
     *
     * FFmpeg is free software; you can redistribute it and/or
    
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
    
     * version 2.1 of the License, or (at your option) any later version.
    
     * FFmpeg is distributed in the hope that it will be useful,
    
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser General Public
    
     * License along with FFmpeg; if not, write to the Free Software
    
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    
    
    #include <stdint.h>
    
    #include <inttypes.h>
    
    #include "libavcodec/ac3_parser.h"
    
    #include "libavcodec/get_bits.h"
    
    #include "libavcodec/put_bits.h"
    
    #include "libavcodec/vc1_common.h"
    
    #include "libavcodec/raw.h"
    
    #include "internal.h"
    #include "libavutil/avstring.h"
    
    #include "libavutil/intfloat.h"
    
    #include "libavutil/mathematics.h"
    
    #include "libavutil/opt.h"
    
    #include "libavutil/dict.h"
    
    #include "libavutil/pixdesc.h"
    
    #include "libavutil/timecode.h"
    
    #include "hevc.h"
    
    #include "rtpenc.h"
    
    static const AVOption options[] = {
    
        { "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "rtphint", "Add RTP hint tracks", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_RTP_HINT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "moov_size", "maximum moov size so it can be placed at the begin", offsetof(MOVMuxContext, reserved_moov_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 0 },
    
        { "empty_moov", "Make the initial moov atom empty", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_EMPTY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "frag_keyframe", "Fragment at video keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_KEYFRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
        { "separate_moof", "Write separate moof/mdat atoms for each track", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SEPARATE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
        { "frag_custom", "Flush fragments on caller requests", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_CUSTOM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
        { "isml", "Create a live smooth streaming feed (for pushing to a publishing point)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_ISML}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "faststart", "Run a second pass to put the index (moov atom) at the beginning of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FASTSTART}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "omit_tfhd_offset", "Omit the base data offset in tfhd atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_OMIT_TFHD_OFFSET}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "disable_chpl", "Disable Nero chapter atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DISABLE_CHPL}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "default_base_moof", "Set the default-base-is-moof flag in tfhd atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DEFAULT_BASE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "dash", "Write DASH compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DASH}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
    
        FF_RTP_FLAG_OPTS(MOVMuxContext, rtp_flags),
    
        { "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
    
        { "iods_audio_profile", "iods audio profile atom.", offsetof(MOVMuxContext, iods_audio_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM},
        { "iods_video_profile", "iods video profile atom.", offsetof(MOVMuxContext, iods_video_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM},
        { "frag_duration", "Maximum fragment duration", offsetof(MOVMuxContext, max_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
        { "min_frag_duration", "Minimum fragment duration", offsetof(MOVMuxContext, min_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
        { "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
        { "ism_lookahead", "Number of lookahead entries for ISM files", offsetof(MOVMuxContext, ism_lookahead), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
    
        { "video_track_timescale", "set timescale of all video tracks", offsetof(MOVMuxContext, video_track_timescale), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
    
        { "brand",    "Override major brand", offsetof(MOVMuxContext, major_brand),   AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM },
    
        { "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM},
    
        { "fragment_index", "Fragment number of the next fragment", offsetof(MOVMuxContext, fragments), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
    
    #define MOV_CLASS(flavor)\
    static const AVClass flavor ## _muxer_class = {\
        .class_name = #flavor " muxer",\
        .item_name  = av_default_item_name,\
        .option     = options,\
        .version    = LIBAVUTIL_VERSION_INT,\
    
    static int get_moov_size(AVFormatContext *s);
    
    
    static int utf8len(const uint8_t *b)
    {
        int len = 0;
        int val;
        while (*b) {
            GET_UTF8(val, *b++, return -1;)
            len++;
        }
        return len;
    }
    
    
    Diego Biurrun's avatar
    Diego Biurrun committed
    //FIXME support 64 bit variant with wide placeholders
    
    static int64_t update_size(AVIOContext *pb, int64_t pos)
    
        int64_t curpos = avio_tell(pb);
    
        avio_seek(pb, pos, SEEK_SET);
    
        avio_wb32(pb, curpos - pos); /* rewrite size */
    
        avio_seek(pb, curpos, SEEK_SET);
    
    static int co64_required(const MOVTrack *track)
    
        if (track->entry > 0 && track->cluster[track->entry - 1].pos + track->data_offset > UINT32_MAX)
            return 1;
    
    static int mov_write_stco_tag(AVIOContext *pb, MOVTrack *track)
    
        int mode64 = co64_required(track); // use 32 bit size variant if possible
    
        int64_t pos = avio_tell(pb);
    
        avio_wb32(pb, 0); /* size */
    
        avio_wb32(pb, 0); /* version & flags */
    
        avio_wb32(pb, track->chunkCount); /* entry count */
    
        for (i = 0; i < track->entry; i++) {
    
            if (!track->cluster[i].chunkNum)
    
            if (mode64 == 1)
    
                avio_wb64(pb, track->cluster[i].pos + track->data_offset);
    
                avio_wb32(pb, track->cluster[i].pos + track->data_offset);
    
        return update_size(pb, pos);
    
    static int mov_write_stsz_tag(AVIOContext *pb, MOVTrack *track)
    
        int i, j, entries = 0, tst = -1, oldtst = -1;
    
        int64_t pos = avio_tell(pb);
    
        avio_wb32(pb, 0); /* size */
    
        avio_wb32(pb, 0); /* version & flags */
    
        for (i = 0; i < track->entry; i++) {
            tst = track->cluster[i].size / track->cluster[i].entries;
            if (oldtst != -1 && tst != oldtst)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            entries += track->cluster[i].entries;
    
        if (equalChunks && track->entry) {
    
            int sSize = track->entry ? track->cluster[0].size / track->cluster[0].entries : 0;
    
            sSize = FFMAX(1, sSize); // adpcm mono case could make sSize == 0
    
            avio_wb32(pb, sSize); // sample size
            avio_wb32(pb, entries); // sample count
    
            avio_wb32(pb, 0); // sample size
            avio_wb32(pb, entries); // sample count
    
            for (i = 0; i < track->entry; i++) {
                for (j = 0; j < track->cluster[i].entries; j++) {
    
                    avio_wb32(pb, track->cluster[i].size /
    
                              track->cluster[i].entries);
    
        return update_size(pb, pos);
    
    /* Sample to chunk atom */
    
    static int mov_write_stsc_tag(AVIOContext *pb, MOVTrack *track)
    
        int index = 0, oldval = -1, i;
    
        int64_t entryPos, curpos;
    
        int64_t pos = avio_tell(pb);
    
        avio_wb32(pb, 0); /* size */
    
        avio_wb32(pb, 0); // version & flags
    
        entryPos = avio_tell(pb);
    
        avio_wb32(pb, track->chunkCount); // entry count
    
        for (i = 0; i < track->entry; i++) {
    
            if (oldval != track->cluster[i].samples_in_chunk && track->cluster[i].chunkNum) {
    
                avio_wb32(pb, track->cluster[i].chunkNum); // first chunk
    
                avio_wb32(pb, track->cluster[i].samples_in_chunk); // samples per chunk
    
                avio_wb32(pb, 0x1); // sample description index
    
                oldval = track->cluster[i].samples_in_chunk;
    
        avio_seek(pb, entryPos, SEEK_SET);
    
        avio_wb32(pb, index); // rewrite size
    
        avio_seek(pb, curpos, SEEK_SET);
    
        return update_size(pb, pos);
    
    static int mov_write_stss_tag(AVIOContext *pb, MOVTrack *track, uint32_t flag)
    
        int64_t curpos, entryPos;
    
        int64_t pos = avio_tell(pb);
    
        avio_wb32(pb, 0); // size
    
        ffio_wfourcc(pb, flag == MOV_SYNC_SAMPLE ? "stss" : "stps");
    
        avio_wb32(pb, 0); // version & flags
    
        entryPos = avio_tell(pb);
    
        avio_wb32(pb, track->entry); // entry count
    
        for (i = 0; i < track->entry; i++) {
    
            if (track->cluster[i].flags & flag) {
    
                avio_wb32(pb, i + 1);
    
        avio_seek(pb, entryPos, SEEK_SET);
    
        avio_wb32(pb, index); // rewrite size
    
        avio_seek(pb, curpos, SEEK_SET);
    
        return update_size(pb, pos);
    
    static int mov_write_amr_tag(AVIOContext *pb, MOVTrack *track)
    
        avio_wb32(pb, 0x11); /* size */
    
        if (track->mode == MODE_MOV) ffio_wfourcc(pb, "samr");
        else                         ffio_wfourcc(pb, "damr");
        ffio_wfourcc(pb, "FFMP");
    
        avio_w8(pb, 0); /* decoder version */
    
        avio_wb16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */
        avio_w8(pb, 0x00); /* Mode change period (no restriction) */
        avio_w8(pb, 0x01); /* Frames per sample */
    
        return 0x11;
    }
    
    
    static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track)
    
    {
        GetBitContext gbc;
        PutBitContext pbc;
        uint8_t buf[3];
        int fscod, bsid, bsmod, acmod, lfeon, frmsizecod;
    
    
        if (track->vos_len < 7)
    
        avio_wb32(pb, 11);
    
        init_get_bits(&gbc, track->vos_data + 4, (track->vos_len - 4) * 8);
    
        fscod      = get_bits(&gbc, 2);
        frmsizecod = get_bits(&gbc, 6);
        bsid       = get_bits(&gbc, 5);
        bsmod      = get_bits(&gbc, 3);
        acmod      = get_bits(&gbc, 3);
        if (acmod == 2) {
            skip_bits(&gbc, 2); // dsurmod
        } else {
            if ((acmod & 1) && acmod != 1)
                skip_bits(&gbc, 2); // cmixlev
            if (acmod & 4)
                skip_bits(&gbc, 2); // surmixlev
        }
        lfeon = get_bits1(&gbc);
    
        init_put_bits(&pbc, buf, sizeof(buf));
        put_bits(&pbc, 2, fscod);
        put_bits(&pbc, 5, bsid);
        put_bits(&pbc, 3, bsmod);
        put_bits(&pbc, 3, acmod);
        put_bits(&pbc, 1, lfeon);
    
        put_bits(&pbc, 5, frmsizecod >> 1); // bit_rate_code
    
        put_bits(&pbc, 5, 0); // reserved
    
        flush_put_bits(&pbc);
    
        avio_write(pb, buf, sizeof(buf));
    
    struct eac3_info {
    
        uint8_t ec3_done;
    
    
        /* Layout of the EC3SpecificBox */
        /* maximum bitrate */
        uint16_t data_rate;
        /* number of independent substreams */
        uint8_t  num_ind_sub;
        struct {
            /* sample rate code (see ff_ac3_sample_rate_tab) 2 bits */
            uint8_t fscod;
            /* bit stream identification 5 bits */
            uint8_t bsid;
            /* one bit reserved */
            /* audio service mixing (not supported yet) 1 bit */
            /* bit stream mode 3 bits */
            uint8_t bsmod;
            /* audio coding mode 3 bits */
            uint8_t acmod;
            /* sub woofer on 1 bit */
            uint8_t lfeon;
            /* 3 bits reserved */
            /* number of dependent substreams associated with this substream 4 bits */
            uint8_t num_dep_sub;
            /* channel locations of the dependent substream(s), if any, 9 bits */
            uint16_t chan_loc;
            /* if there is no dependent substream, then one bit reserved instead */
        } substream[1]; /* TODO: support 8 independent substreams */
    };
    
    static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track)
    {
        GetBitContext gbc;
        AC3HeaderInfo tmp, *hdr = &tmp;
        struct eac3_info *info;
        int num_blocks;
    
        if (!track->eac3_priv && !(track->eac3_priv = av_mallocz(sizeof(*info))))
            return AVERROR(ENOMEM);
        info = track->eac3_priv;
    
        init_get_bits(&gbc, pkt->data, pkt->size * 8);
        if (avpriv_ac3_parse_header2(&gbc, &hdr) < 0) {
            /* drop the packets until we see a good one */
            if (!track->entry) {
                av_log(mov, AV_LOG_WARNING, "Dropping invalid packet from start of the stream\n");
                return 0;
            }
            return AVERROR_INVALIDDATA;
        }
    
        info->data_rate = FFMAX(info->data_rate, hdr->bit_rate / 1000);
        num_blocks = hdr->num_blocks;
    
        if (!info->ec3_done) {
            /* AC-3 substream must be the first one */
            if (hdr->bitstream_id <= 10 && hdr->substreamid != 0)
                return AVERROR(EINVAL);
    
            /* this should always be the case, given that our AC-3 parser
             * concatenates dependent frames to their independent parent */
            if (hdr->frame_type == EAC3_FRAME_TYPE_INDEPENDENT) {
                /* substream ids must be incremental */
                if (hdr->substreamid > info->num_ind_sub + 1)
                    return AVERROR(EINVAL);
    
                if (hdr->substreamid == info->num_ind_sub + 1) {
                    //info->num_ind_sub++;
                    avpriv_request_sample(track->enc, "Multiple independent substreams");
                    return AVERROR_PATCHWELCOME;
                } else if (hdr->substreamid < info->num_ind_sub ||
                           hdr->substreamid == 0 && info->substream[0].bsid) {
                    info->ec3_done = 1;
                    goto concatenate;
                }
            }
    
            /* fill the info needed for the "dec3" atom */
            info->substream[hdr->substreamid].fscod = hdr->sr_code;
            info->substream[hdr->substreamid].bsid  = hdr->bitstream_id;
            info->substream[hdr->substreamid].bsmod = hdr->bitstream_mode;
            info->substream[hdr->substreamid].acmod = hdr->channel_mode;
            info->substream[hdr->substreamid].lfeon = hdr->lfe_on;
    
            /* Parse dependent substream(s), if any */
            if (pkt->size != hdr->frame_size) {
                int cumul_size = hdr->frame_size;
                int parent = hdr->substreamid;
    
                while (cumul_size != pkt->size) {
                    int i;
                    init_get_bits(&gbc, pkt->data + cumul_size, (pkt->size - cumul_size) * 8);
                    if (avpriv_ac3_parse_header2(&gbc, &hdr) < 0)
                        return AVERROR_INVALIDDATA;
                    if (hdr->frame_type != EAC3_FRAME_TYPE_DEPENDENT)
                        return AVERROR(EINVAL);
                    cumul_size += hdr->frame_size;
                    info->substream[parent].num_dep_sub++;
    
                    /* header is parsed up to lfeon, but custom channel map may be needed */
                    /* skip bsid */
                    skip_bits(&gbc, 5);
                    /* skip volume control params */
                    for (i = 0; i < (hdr->channel_mode ? 1 : 2); i++) {
                        skip_bits(&gbc, 5); // skip dialog normalization
                        if (get_bits1(&gbc)) {
                            skip_bits(&gbc, 8); // skip compression gain word
                        }
                    }
                    /* get the dependent stream channel map, if exists */
                    if (get_bits1(&gbc))
                        info->substream[parent].chan_loc |= (get_bits(&gbc, 16) >> 5) & 0x1f;
                    else
                        info->substream[parent].chan_loc |= hdr->channel_mode;
                }
            }
        }
    
    concatenate:
    
        if (!info->num_blocks && num_blocks == 6)
            return pkt->size;
        else if (info->num_blocks + num_blocks > 6)
            return AVERROR_INVALIDDATA;
    
        if (!info->num_blocks) {
            int ret;
            if ((ret = av_copy_packet(&info->pkt, pkt)) < 0)
                return ret;
            info->num_blocks = num_blocks;
            return 0;
        } else {
            int ret;
            if ((ret = av_grow_packet(&info->pkt, pkt->size)) < 0)
                return ret;
            memcpy(info->pkt.data + info->pkt.size - pkt->size, pkt->data, pkt->size);
            info->num_blocks += num_blocks;
            info->pkt.duration += pkt->duration;
            if ((ret = av_copy_packet_side_data(&info->pkt, pkt)) < 0)
                return ret;
            if (info->num_blocks != 6)
                return 0;
            av_free_packet(pkt);
            if ((ret = av_copy_packet(pkt, &info->pkt)) < 0)
                return ret;
            av_free_packet(&info->pkt);
            info->num_blocks = 0;
    
        }
    
        return pkt->size;
    }
    
    static int mov_write_eac3_tag(AVIOContext *pb, MOVTrack *track)
    {
        PutBitContext pbc;
        uint8_t *buf;
        struct eac3_info *info;
        int size, i;
    
        if (!track->eac3_priv)
            return AVERROR(EINVAL);
    
        info = track->eac3_priv;
        size = 2 + 4 * (info->num_ind_sub + 1);
        buf = av_malloc(size);
        if (!buf) {
    
        }
    
        init_put_bits(&pbc, buf, size);
        put_bits(&pbc, 13, info->data_rate);
        put_bits(&pbc,  3, info->num_ind_sub);
        for (i = 0; i <= info->num_ind_sub; i++) {
            put_bits(&pbc, 2, info->substream[i].fscod);
            put_bits(&pbc, 5, info->substream[i].bsid);
            put_bits(&pbc, 1, 0); /* reserved */
            put_bits(&pbc, 1, 0); /* asvc */
            put_bits(&pbc, 3, info->substream[i].bsmod);
            put_bits(&pbc, 3, info->substream[i].acmod);
            put_bits(&pbc, 1, info->substream[i].lfeon);
            put_bits(&pbc, 5, 0); /* reserved */
            put_bits(&pbc, 4, info->substream[i].num_dep_sub);
            if (!info->substream[i].num_dep_sub) {
                put_bits(&pbc, 1, 0); /* reserved */
                size--;
            } else {
                put_bits(&pbc, 9, info->substream[i].chan_loc);
            }
        }
        flush_put_bits(&pbc);
    
        avio_wb32(pb, size + 8);
        ffio_wfourcc(pb, "dec3");
        avio_write(pb, buf, size);
    
        av_free(buf);
    
        av_freep(&track->eac3_priv);
    
        return size;
    }
    
    
    /**
     * This function writes extradata "as is".
    
     * Extradata must be formatted like a valid atom (with size and tag).
    
    static int mov_write_extradata_tag(AVIOContext *pb, MOVTrack *track)
    
        avio_write(pb, track->enc->extradata, track->enc->extradata_size);
    
        return track->enc->extradata_size;
    }
    
    
    static int mov_write_enda_tag(AVIOContext *pb)
    
        avio_wb32(pb, 10);
    
        avio_wb16(pb, 1); /* little endian */
    
    static int mov_write_enda_tag_be(AVIOContext *pb)
    {
      avio_wb32(pb, 10);
      ffio_wfourcc(pb, "enda");
      avio_wb16(pb, 0); /* big endian */
      return 10;
    }
    
    
    static void put_descr(AVIOContext *pb, int tag, unsigned int size)
    
        avio_w8(pb, tag);
    
        for (; i > 0; i--)
            avio_w8(pb, (size >> (7 * i)) | 0x80);
    
        avio_w8(pb, size & 0x7F);
    
    static unsigned compute_avg_bitrate(MOVTrack *track)
    {
        uint64_t size = 0;
        int i;
    
        if (!track->track_duration)
            return 0;
    
        for (i = 0; i < track->entry; i++)
            size += track->cluster[i].size;
    
        return size * 8 * track->timescale / track->track_duration;
    
    static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic
    
        int64_t pos = avio_tell(pb);
    
        int decoder_specific_info_len = track->vos_len ? 5 + track->vos_len : 0;
    
        unsigned avg_bitrate;
    
        avio_wb32(pb, 0); // size
    
        avio_wb32(pb, 0); // Version
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    
        // ES descriptor
    
        put_descr(pb, 0x03, 3 + 5+13 + decoder_specific_info_len + 5+1);
        avio_wb16(pb, track->track_id);
    
        avio_w8(pb, 0x00); // flags (= no flags)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    
        // DecoderConfig descriptor
    
        put_descr(pb, 0x04, 13 + decoder_specific_info_len);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    
        // Object type indication
    
        if ((track->enc->codec_id == AV_CODEC_ID_MP2 ||
             track->enc->codec_id == AV_CODEC_ID_MP3) &&
    
            track->enc->sample_rate > 24000)
    
            avio_w8(pb, 0x6B); // 11172-3
    
            avio_w8(pb, ff_codec_get_tag(ff_mp4_obj_type, track->enc->codec_id));
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    
        // the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio)
        // plus 1 bit to indicate upstream and 1 bit set to 1 (reserved)
    
        if (track->enc->codec_id == AV_CODEC_ID_DVD_SUBTITLE)
            avio_w8(pb, (0x38 << 2) | 1); // flags (= NeroSubpicStream)
        else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO)
    
            avio_w8(pb, 0x15); // flags (= Audiostream)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        else
    
            avio_w8(pb, 0x11); // flags (= Visualstream)
    
        avio_wb24(pb, track->enc->rc_buffer_size >> 3); // Buffersize DB
    
        avg_bitrate = compute_avg_bitrate(track);
        // maxbitrate (FIXME should be max rate in any 1 sec window)
        avio_wb32(pb, FFMAX3(track->enc->bit_rate, track->enc->rc_max_rate, avg_bitrate));
        avio_wb32(pb, avg_bitrate);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            // DecoderSpecific info descriptor
    
            put_descr(pb, 0x05, track->vos_len);
            avio_write(pb, track->vos_data, track->vos_len);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        }
    
        // SL descriptor
    
        put_descr(pb, 0x06, 1);
    
        avio_w8(pb, 0x02);
    
        return update_size(pb, pos);
    
    static int mov_pcm_le_gt16(enum AVCodecID codec_id)
    
        return codec_id == AV_CODEC_ID_PCM_S24LE ||
               codec_id == AV_CODEC_ID_PCM_S32LE ||
               codec_id == AV_CODEC_ID_PCM_F32LE ||
               codec_id == AV_CODEC_ID_PCM_F64LE;
    
    static int mov_pcm_be_gt16(enum AVCodecID codec_id)
    {
        return codec_id == AV_CODEC_ID_PCM_S24BE ||
               codec_id == AV_CODEC_ID_PCM_S32BE ||
               codec_id == AV_CODEC_ID_PCM_F32BE ||
               codec_id == AV_CODEC_ID_PCM_F64BE;
    }
    
    
    static int mov_write_ms_tag(AVIOContext *pb, MOVTrack *track)
    
        int64_t pos = avio_tell(pb);
    
        avio_wb32(pb, 0);
        avio_wl32(pb, track->tag); // store it byteswapped
    
        track->enc->codec_tag = av_bswap16(track->tag >> 16);
    
        if ((ret = ff_put_wav_header(pb, track->enc, 0)) < 0)
            return ret;
    
        return update_size(pb, pos);
    
    static int mov_write_wfex_tag(AVIOContext *pb, MOVTrack *track)
    {
    
        int64_t pos = avio_tell(pb);
        avio_wb32(pb, 0);
        ffio_wfourcc(pb, "wfex");
    
        if ((ret = ff_put_wav_header(pb, track->enc, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX)) < 0)
            return ret;
    
        return update_size(pb, pos);
    }
    
    
    static int mov_write_chan_tag(AVIOContext *pb, MOVTrack *track)
    {
        uint32_t layout_tag, bitmap;
        int64_t pos = avio_tell(pb);
    
        layout_tag = ff_mov_get_channel_layout_tag(track->enc->codec_id,
                                                   track->enc->channel_layout,
                                                   &bitmap);
        if (!layout_tag) {
            av_log(track->enc, AV_LOG_WARNING, "not writing 'chan' tag due to "
                   "lack of channel information\n");
            return 0;
        }
    
    
        avio_wb32(pb, 0);           // Size
        ffio_wfourcc(pb, "chan");   // Type
        avio_w8(pb, 0);             // Version
        avio_wb24(pb, 0);           // Flags
        avio_wb32(pb, layout_tag);  // mChannelLayoutTag
        avio_wb32(pb, bitmap);      // mChannelBitmap
        avio_wb32(pb, 0);           // mNumberChannelDescriptions
    
    
        return update_size(pb, pos);
    
    static int mov_write_wave_tag(AVIOContext *pb, MOVTrack *track)
    
        int64_t pos = avio_tell(pb);
    
        avio_wb32(pb, 0);     /* size */
    
        if (track->enc->codec_id != AV_CODEC_ID_QDM2) {
    
        avio_wb32(pb, 12);    /* size */
    
        avio_wl32(pb, track->tag);
    
        if (track->enc->codec_id == AV_CODEC_ID_AAC) {
    
            /* useless atom needed by mplayer, ipod, not needed by quicktime */
    
            avio_wb32(pb, 12); /* size */
    
            avio_wb32(pb, 0);
    
            mov_write_esds_tag(pb, track);
    
        } else if (mov_pcm_le_gt16(track->enc->codec_id))  {
          mov_write_enda_tag(pb);
        } else if (mov_pcm_be_gt16(track->enc->codec_id))  {
          mov_write_enda_tag_be(pb);
    
        } else if (track->enc->codec_id == AV_CODEC_ID_AMR_NB) {
    
            mov_write_amr_tag(pb, track);
    
        } else if (track->enc->codec_id == AV_CODEC_ID_AC3) {
    
            mov_write_ac3_tag(pb, track);
    
        } else if (track->enc->codec_id == AV_CODEC_ID_EAC3) {
            mov_write_eac3_tag(pb, track);
    
        } else if (track->enc->codec_id == AV_CODEC_ID_ALAC ||
                   track->enc->codec_id == AV_CODEC_ID_QDM2) {
    
            mov_write_extradata_tag(pb, track);
    
        } else if (track->enc->codec_id == AV_CODEC_ID_ADPCM_MS ||
                   track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV) {
    
        avio_wb32(pb, 8);     /* size */
        avio_wb32(pb, 0);     /* null tag */
    
        return update_size(pb, pos);
    
    static int mov_write_dvc1_structs(MOVTrack *track, uint8_t *buf)
    {
        uint8_t *unescaped;
        const uint8_t *start, *next, *end = track->vos_data + track->vos_len;
        int unescaped_size, seq_found = 0;
        int level = 0, interlace = 0;
        int packet_seq   = track->vc1_info.packet_seq;
        int packet_entry = track->vc1_info.packet_entry;
        int slices       = track->vc1_info.slices;
        PutBitContext pbc;
    
        if (track->start_dts == AV_NOPTS_VALUE) {
            /* No packets written yet, vc1_info isn't authoritative yet. */
            /* Assume inline sequence and entry headers. This will be
             * overwritten at the end if the file is seekable. */
            packet_seq = packet_entry = 1;
        }
    
        unescaped = av_mallocz(track->vos_len + FF_INPUT_BUFFER_PADDING_SIZE);
        if (!unescaped)
            return AVERROR(ENOMEM);
        start = find_next_marker(track->vos_data, end);
        for (next = start; next < end; start = next) {
            GetBitContext gb;
            int size;
            next = find_next_marker(start + 4, end);
            size = next - start - 4;
            if (size <= 0)
                continue;
            unescaped_size = vc1_unescape_buffer(start + 4, size, unescaped);
            init_get_bits(&gb, unescaped, 8 * unescaped_size);
            if (AV_RB32(start) == VC1_CODE_SEQHDR) {
                int profile = get_bits(&gb, 2);
                if (profile != PROFILE_ADVANCED) {
                    av_free(unescaped);
                    return AVERROR(ENOSYS);
                }
                seq_found = 1;
                level = get_bits(&gb, 3);
                /* chromaformat, frmrtq_postproc, bitrtq_postproc, postprocflag,
                 * width, height */
                skip_bits_long(&gb, 2 + 3 + 5 + 1 + 2*12);
                skip_bits(&gb, 1); /* broadcast */
                interlace = get_bits1(&gb);
                skip_bits(&gb, 4); /* tfcntrflag, finterpflag, reserved, psf */
            }
        }
        if (!seq_found) {
            av_free(unescaped);
            return AVERROR(ENOSYS);
        }
    
        init_put_bits(&pbc, buf, 7);
        /* VC1DecSpecStruc */
        put_bits(&pbc, 4, 12); /* profile - advanced */
        put_bits(&pbc, 3, level);
        put_bits(&pbc, 1, 0); /* reserved */
        /* VC1AdvDecSpecStruc */
        put_bits(&pbc, 3, level);
        put_bits(&pbc, 1, 0); /* cbr */
        put_bits(&pbc, 6, 0); /* reserved */
        put_bits(&pbc, 1, !interlace); /* no interlace */
        put_bits(&pbc, 1, !packet_seq); /* no multiple seq */
        put_bits(&pbc, 1, !packet_entry); /* no multiple entry */
        put_bits(&pbc, 1, !slices); /* no slice code */
        put_bits(&pbc, 1, 0); /* no bframe */
        put_bits(&pbc, 1, 0); /* reserved */
    
    
        /* framerate */
        if (track->st->avg_frame_rate.num > 0 && track->st->avg_frame_rate.den > 0)
            put_bits32(&pbc, track->st->avg_frame_rate.num / track->st->avg_frame_rate.den);
        else
            put_bits32(&pbc, 0xffffffff);
    
    
        flush_put_bits(&pbc);
    
        av_free(unescaped);
    
        return 0;
    }
    
    static int mov_write_dvc1_tag(AVIOContext *pb, MOVTrack *track)
    {
        uint8_t buf[7] = { 0 };
        int ret;
    
        if ((ret = mov_write_dvc1_structs(track, buf)) < 0)
            return ret;
    
        avio_wb32(pb, track->vos_len + 8 + sizeof(buf));
        ffio_wfourcc(pb, "dvc1");
        track->vc1_info.struct_offset = avio_tell(pb);
        avio_write(pb, buf, sizeof(buf));
        avio_write(pb, track->vos_data, track->vos_len);
    
        return 0;
    }
    
    
    static int mov_write_glbl_tag(AVIOContext *pb, MOVTrack *track)
    
        avio_wb32(pb, track->vos_len + 8);
    
        avio_write(pb, track->vos_data, track->vos_len);
        return 8 + track->vos_len;
    
    /**
     * Compute flags for 'lpcm' tag.
     * See CoreAudioTypes and AudioStreamBasicDescription at Apple.
     */
    
    static int mov_get_lpcm_flags(enum AVCodecID codec_id)
    
        case AV_CODEC_ID_PCM_F32BE:
        case AV_CODEC_ID_PCM_F64BE:
    
        case AV_CODEC_ID_PCM_F32LE:
        case AV_CODEC_ID_PCM_F64LE:
    
        case AV_CODEC_ID_PCM_U8:
    
        case AV_CODEC_ID_PCM_S16BE:
        case AV_CODEC_ID_PCM_S24BE:
        case AV_CODEC_ID_PCM_S32BE:
    
        case AV_CODEC_ID_PCM_S8:
        case AV_CODEC_ID_PCM_S16LE:
        case AV_CODEC_ID_PCM_S24LE:
        case AV_CODEC_ID_PCM_S32LE:
    
    static int get_cluster_duration(MOVTrack *track, int cluster_idx)
    {
        int64_t next_dts;
    
        if (cluster_idx >= track->entry)
            return 0;
    
        if (cluster_idx + 1 == track->entry)
            next_dts = track->track_duration + track->start_dts;
        else
            next_dts = track->cluster[cluster_idx + 1].dts;
    
    
        next_dts -= track->cluster[cluster_idx].dts;
    
        av_assert0(next_dts >= 0);
        av_assert0(next_dts <= INT_MAX);
    
        return next_dts;
    
    static int get_samples_per_packet(MOVTrack *track)
    {
        int i, first_duration;
    
    
    // return track->enc->frame_size;
    
    
        /* use 1 for raw PCM */
        if (!track->audio_vbr)
            return 1;
    
        /* check to see if duration is constant for all clusters */
        if (!track->entry)
            return 0;
        first_duration = get_cluster_duration(track, 0);
        for (i = 1; i < track->entry; i++) {
            if (get_cluster_duration(track, i) != first_duration)
                return 0;
        }
        return first_duration;
    }
    
    
    static int mov_write_audio_tag(AVIOContext *pb, MOVTrack *track)
    
        int64_t pos = avio_tell(pb);
    
        int version = 0;
        uint32_t tag = track->tag;
    
        if (track->mode == MODE_MOV) {
    
            if (track->timescale > UINT16_MAX) {
                if (mov_get_lpcm_flags(track->enc->codec_id))
                    tag = AV_RL32("lpcm");
                version = 2;
            } else if (track->audio_vbr || mov_pcm_le_gt16(track->enc->codec_id) ||
    
                       mov_pcm_be_gt16(track->enc->codec_id) ||
    
                       track->enc->codec_id == AV_CODEC_ID_ADPCM_MS ||
                       track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV ||
                       track->enc->codec_id == AV_CODEC_ID_QDM2) {
    
        avio_wb32(pb, 0); /* size */
        avio_wl32(pb, tag); // store it byteswapped
        avio_wb32(pb, 0); /* Reserved */
        avio_wb16(pb, 0); /* Reserved */
        avio_wb16(pb, 1); /* Data-reference index, XXX  == 1 */
    
        avio_wb16(pb, version); /* Version */
        avio_wb16(pb, 0); /* Revision level */
        avio_wb32(pb, 0); /* Reserved */
    
            avio_wb16(pb, 3);
            avio_wb16(pb, 16);
            avio_wb16(pb, 0xfffe);
            avio_wb16(pb, 0);
            avio_wb32(pb, 0x00010000);
            avio_wb32(pb, 72);
    
            avio_wb64(pb, av_double2int(track->enc->sample_rate));
    
            avio_wb32(pb, track->enc->channels);
            avio_wb32(pb, 0x7F000000);
            avio_wb32(pb, av_get_bits_per_sample(track->enc->codec_id));
            avio_wb32(pb, mov_get_lpcm_flags(track->enc->codec_id));
    
            avio_wb32(pb, track->sample_size);
    
            avio_wb32(pb, get_samples_per_packet(track));
    
            if (track->mode == MODE_MOV) {
                avio_wb16(pb, track->enc->channels);
    
                if (track->enc->codec_id == AV_CODEC_ID_PCM_U8 ||
                    track->enc->codec_id == AV_CODEC_ID_PCM_S8)
    
                else if (track->enc->codec_id == AV_CODEC_ID_ADPCM_G726)
                    avio_wb16(pb, track->enc->bits_per_coded_sample);
    
                else
                    avio_wb16(pb, 16);
                avio_wb16(pb, track->audio_vbr ? -2 : 0); /* compression ID */
            } else { /* reserved for mp4/3gp */
                avio_wb16(pb, 2);
                avio_wb16(pb, 16);
                avio_wb16(pb, 0);
            }
    
            avio_wb16(pb, 0); /* packet size (= 0) */
    
            avio_wb16(pb, track->enc->sample_rate <= UINT16_MAX ?
                          track->enc->sample_rate : 0);
    
            avio_wb16(pb, 0); /* Reserved */
    
        if (version == 1) { /* SoundDescription V1 extended info */
    
            if (mov_pcm_le_gt16(track->enc->codec_id) ||
                mov_pcm_be_gt16(track->enc->codec_id))
                avio_wb32(pb, 1); /*  must be 1 for  uncompressed formats */
            else
                avio_wb32(pb, track->enc->frame_size); /* Samples per packet */
    
            avio_wb32(pb, track->sample_size / track->enc->channels); /* Bytes per packet */
            avio_wb32(pb, track->sample_size); /* Bytes per frame */
    
        if (track->mode == MODE_MOV &&
            (track->enc->codec_id == AV_CODEC_ID_AAC           ||
             track->enc->codec_id == AV_CODEC_ID_AC3           ||
    
             track->enc->codec_id == AV_CODEC_ID_EAC3          ||
    
             track->enc->codec_id == AV_CODEC_ID_AMR_NB        ||
             track->enc->codec_id == AV_CODEC_ID_ALAC          ||
             track->enc->codec_id == AV_CODEC_ID_ADPCM_MS      ||
    
             track->enc->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV ||
             track->enc->codec_id == AV_CODEC_ID_QDM2          ||
             (mov_pcm_le_gt16(track->enc->codec_id) && version==1) ||
             (mov_pcm_be_gt16(track->enc->codec_id) && version==1)))
    
            mov_write_wave_tag(pb, track);
    
        else if (track->tag == MKTAG('m','p','4','a'))
    
            mov_write_esds_tag(pb, track);
    
        else if (track->enc->codec_id == AV_CODEC_ID_AMR_NB)
    
            mov_write_amr_tag(pb, track);
    
        else if (track->enc->codec_id == AV_CODEC_ID_AC3)
    
        else if (track->enc->codec_id == AV_CODEC_ID_EAC3)
            mov_write_eac3_tag(pb, track);
    
        else if (track->enc->codec_id == AV_CODEC_ID_ALAC)
    
            mov_write_extradata_tag(pb, track);
    
        else if (track->enc->codec_id == AV_CODEC_ID_WMAPRO)
    
            mov_write_wfex_tag(pb, track);
    
        else if (track->vos_len > 0)
    
        if (track->mode == MODE_MOV && track->enc->codec_type == AVMEDIA_TYPE_AUDIO)
            mov_write_chan_tag(pb, track);
    
    
        return update_size(pb, pos);
    
    static int mov_write_d263_tag(AVIOContext *pb)
    
        avio_wb32(pb, 0xf); /* size */