static int mov_init(AVFormatContext *s)
{
    MOVMuxContext *mov = s->priv_data;
    AVDictionaryEntry *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
    int i, ret, hint_track = 0, tmcd_track = 0;

    mov->fc = s;

    /* Default mode == MP4 */
    mov->mode = MODE_MP4;

    if (s->oformat) {
        if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP;
        else if (!strcmp("3g2", s->oformat->name)) mov->mode = MODE_3GP|MODE_3G2;
        else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV;
        else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP;
        else if (!strcmp("ipod",s->oformat->name)) mov->mode = MODE_IPOD;
        else if (!strcmp("ismv",s->oformat->name)) mov->mode = MODE_ISM;
        else if (!strcmp("f4v", s->oformat->name)) mov->mode = MODE_F4V;
    }

    if (mov->flags & FF_MOV_FLAG_DELAY_MOOV)
        mov->flags |= FF_MOV_FLAG_EMPTY_MOOV;

    /* Set the FRAGMENT flag if any of the fragmentation methods are
     * enabled. */
    if (mov->max_fragment_duration || mov->max_fragment_size ||
        mov->flags & (FF_MOV_FLAG_EMPTY_MOOV |
                      FF_MOV_FLAG_FRAG_KEYFRAME |
                      FF_MOV_FLAG_FRAG_CUSTOM))
        mov->flags |= FF_MOV_FLAG_FRAGMENT;

    /* Set other implicit flags immediately */
    if (mov->mode == MODE_ISM)
        mov->flags |= FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF |
                      FF_MOV_FLAG_FRAGMENT;
    if (mov->flags & FF_MOV_FLAG_DASH)
        mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV |
                      FF_MOV_FLAG_DEFAULT_BASE_MOOF;

    if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV && s->flags & AVFMT_FLAG_AUTO_BSF) {
        av_log(s, AV_LOG_VERBOSE, "Empty MOOV enabled; disabling automatic bitstream filtering\n");
        s->flags &= ~AVFMT_FLAG_AUTO_BSF;
    }

    if (mov->flags & FF_MOV_FLAG_FASTSTART) {
        mov->reserved_moov_size = -1;
    }

    if (mov->use_editlist < 0) {
        mov->use_editlist = 1;
        if (mov->flags & FF_MOV_FLAG_FRAGMENT &&
            !(mov->flags & FF_MOV_FLAG_DELAY_MOOV)) {
            // If we can avoid needing an edit list by shifting the
            // tracks, prefer that over (trying to) write edit lists
            // in fragmented output.
            if (s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO ||
                s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO)
                mov->use_editlist = 0;
        }
    }
    if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV &&
        !(mov->flags & FF_MOV_FLAG_DELAY_MOOV) && mov->use_editlist)
        av_log(s, AV_LOG_WARNING, "No meaningful edit list will be written when using empty_moov without delay_moov\n");

    if (!mov->use_editlist && s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO)
        s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_ZERO;

    /* Clear the omit_tfhd_offset flag if default_base_moof is set;
     * if the latter is set that's enough and omit_tfhd_offset doesn't
     * add anything extra on top of that. */
    if (mov->flags & FF_MOV_FLAG_OMIT_TFHD_OFFSET &&
        mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)
        mov->flags &= ~FF_MOV_FLAG_OMIT_TFHD_OFFSET;

    if (mov->frag_interleave &&
        mov->flags & (FF_MOV_FLAG_OMIT_TFHD_OFFSET | FF_MOV_FLAG_SEPARATE_MOOF)) {
        av_log(s, AV_LOG_ERROR,
               "Sample interleaving in fragments is mutually exclusive with "
               "omit_tfhd_offset and separate_moof\n");
        return AVERROR(EINVAL);
    }

    /* Non-seekable output is ok if using fragmentation. If ism_lookahead
     * is enabled, we don't support non-seekable output at all. */
    if (!s->pb->seekable &&
        (!(mov->flags & FF_MOV_FLAG_FRAGMENT) || mov->ism_lookahead)) {
        av_log(s, AV_LOG_ERROR, "muxer does not support non seekable output\n");
        return AVERROR(EINVAL);
    }

    mov->nb_streams = s->nb_streams;
    if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
        mov->chapter_track = mov->nb_streams++;

    if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
        /* Add hint tracks for each audio and video stream */
        hint_track = mov->nb_streams;
        for (i = 0; i < s->nb_streams; i++) {
            AVStream *st = s->streams[i];
            if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
                st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
                mov->nb_streams++;
            }
        }
    }

    if (   mov->write_tmcd == -1 && (mov->mode == MODE_MOV || mov->mode == MODE_MP4)
        || mov->write_tmcd == 1) {
        tmcd_track = mov->nb_streams;

        /* +1 tmcd track for each video stream with a timecode */
        for (i = 0; i < s->nb_streams; i++) {
            AVStream *st = s->streams[i];
            AVDictionaryEntry *t = global_tcr;
            if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                (t || (t=av_dict_get(st->metadata, "timecode", NULL, 0)))) {
                AVTimecode tc;
                ret = mov_check_timecode_track(s, &tc, i, t->value);
                if (ret >= 0)
                    mov->nb_meta_tmcd++;
            }
        }

        /* check if there is already a tmcd track to remux */
        if (mov->nb_meta_tmcd) {
            for (i = 0; i < s->nb_streams; i++) {
                AVStream *st = s->streams[i];
                if (st->codecpar->codec_tag == MKTAG('t','m','c','d')) {
                    av_log(s, AV_LOG_WARNING, "You requested a copy of the original timecode track "
                           "so timecode metadata are now ignored\n");
                    mov->nb_meta_tmcd = 0;
                }
            }
        }

        mov->nb_streams += mov->nb_meta_tmcd;
    }

    // Reserve an extra stream for chapters for the case where chapters
    // are written in the trailer
    mov->tracks = av_mallocz_array((mov->nb_streams + 1), sizeof(*mov->tracks));
    if (!mov->tracks)
        return AVERROR(ENOMEM);

    if (mov->encryption_scheme_str != NULL && strcmp(mov->encryption_scheme_str, "none") != 0) {
        if (strcmp(mov->encryption_scheme_str, "cenc-aes-ctr") == 0) {
            mov->encryption_scheme = MOV_ENC_CENC_AES_CTR;

            if (mov->encryption_key_len != AES_CTR_KEY_SIZE) {
                av_log(s, AV_LOG_ERROR, "Invalid encryption key len %d expected %d\n",
                    mov->encryption_key_len, AES_CTR_KEY_SIZE);
                return AVERROR(EINVAL);
            }

            if (mov->encryption_kid_len != CENC_KID_SIZE) {
                av_log(s, AV_LOG_ERROR, "Invalid encryption kid len %d expected %d\n",
                    mov->encryption_kid_len, CENC_KID_SIZE);
                return AVERROR(EINVAL);
            }
        } else {
            av_log(s, AV_LOG_ERROR, "unsupported encryption scheme %s\n",
                mov->encryption_scheme_str);
            return AVERROR(EINVAL);
        }
    }

    for (i = 0; i < s->nb_streams; i++) {
        AVStream *st= s->streams[i];
        MOVTrack *track= &mov->tracks[i];
        AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0);

        track->st  = st;
        track->par = st->codecpar;
        track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV);
        if (track->language < 0)
            track->language = 0;
        track->mode = mov->mode;
        track->tag  = mov_find_codec_tag(s, track);
        if (!track->tag) {
            av_log(s, AV_LOG_ERROR, "Could not find tag for codec %s in stream #%d, "
                   "codec not currently supported in container\n",
                   avcodec_get_name(st->codecpar->codec_id), i);
            return AVERROR(EINVAL);
        }
        /* If hinting of this track is enabled by a later hint track,
         * this is updated. */
        track->hint_track = -1;
        track->start_dts  = AV_NOPTS_VALUE;
        track->start_cts  = AV_NOPTS_VALUE;
        track->end_pts    = AV_NOPTS_VALUE;
        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            if (track->tag == MKTAG('m','x','3','p') || track->tag == MKTAG('m','x','3','n') ||
                track->tag == MKTAG('m','x','4','p') || track->tag == MKTAG('m','x','4','n') ||
                track->tag == MKTAG('m','x','5','p') || track->tag == MKTAG('m','x','5','n')) {
                if (st->codecpar->width != 720 || (st->codecpar->height != 608 && st->codecpar->height != 512)) {
                    av_log(s, AV_LOG_ERROR, "D-10/IMX must use 720x608 or 720x512 video resolution\n");
                    return AVERROR(EINVAL);
                }
                track->height = track->tag >> 24 == 'n' ? 486 : 576;
            }
            if (mov->video_track_timescale) {
                track->timescale = mov->video_track_timescale;
            } else {
                track->timescale = st->time_base.den;
                while(track->timescale < 10000)
                    track->timescale *= 2;
            }
            if (st->codecpar->width > 65535 || st->codecpar->height > 65535) {
                av_log(s, AV_LOG_ERROR, "Resolution %dx%d too large for mov/mp4\n", st->codecpar->width, st->codecpar->height);
                return AVERROR(EINVAL);
            }
            if (track->mode == MODE_MOV && track->timescale > 100000)
                av_log(s, AV_LOG_WARNING,
                       "WARNING codec timebase is very high. If duration is too long,\n"
                       "file may not be playable by quicktime. Specify a shorter timebase\n"
                       "or choose different container.\n");
            if (track->mode == MODE_MOV &&
                track->par->codec_id == AV_CODEC_ID_RAWVIDEO &&
                track->tag == MKTAG('r','a','w',' ')) {
                enum AVPixelFormat pix_fmt = track->par->format;
                if (pix_fmt == AV_PIX_FMT_NONE && track->par->bits_per_coded_sample == 1)
                    pix_fmt = AV_PIX_FMT_MONOWHITE;
                track->is_unaligned_qt_rgb =
                        pix_fmt == AV_PIX_FMT_RGB24 ||
                        pix_fmt == AV_PIX_FMT_BGR24 ||
                        pix_fmt == AV_PIX_FMT_PAL8 ||
                        pix_fmt == AV_PIX_FMT_GRAY8 ||
                        pix_fmt == AV_PIX_FMT_MONOWHITE ||
                        pix_fmt == AV_PIX_FMT_MONOBLACK;
            }
            if (track->mode == MODE_MP4 &&
                track->par->codec_id == AV_CODEC_ID_VP9) {
                if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
                    av_log(s, AV_LOG_ERROR,
                           "VP9 in MP4 support is experimental, add "
                           "'-strict %d' if you want to use it.\n",
                           FF_COMPLIANCE_EXPERIMENTAL);
                    return AVERROR_EXPERIMENTAL;
                }
            }
        } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            track->timescale = st->codecpar->sample_rate;
            if (!st->codecpar->frame_size && !av_get_bits_per_sample(st->codecpar->codec_id)) {
                av_log(s, AV_LOG_WARNING, "track %d: codec frame size is not set\n", i);
                track->audio_vbr = 1;
            }else if (st->codecpar->codec_id == AV_CODEC_ID_ADPCM_MS ||
                     st->codecpar->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV ||
                     st->codecpar->codec_id == AV_CODEC_ID_ILBC){
                if (!st->codecpar->block_align) {
                    av_log(s, AV_LOG_ERROR, "track %d: codec block align is not set for adpcm\n", i);
                    return AVERROR(EINVAL);
                }
                track->sample_size = st->codecpar->block_align;
            }else if (st->codecpar->frame_size > 1){ /* assume compressed audio */
                track->audio_vbr = 1;
            }else{
                track->sample_size = (av_get_bits_per_sample(st->codecpar->codec_id) >> 3) * st->codecpar->channels;
            }
            if (st->codecpar->codec_id == AV_CODEC_ID_ILBC ||
                st->codecpar->codec_id == AV_CODEC_ID_ADPCM_IMA_QT) {
                track->audio_vbr = 1;
            }
            if (track->mode != MODE_MOV &&
                track->par->codec_id == AV_CODEC_ID_MP3 && track->timescale < 16000) {
                if (s->strict_std_compliance >= FF_COMPLIANCE_NORMAL) {
                    av_log(s, AV_LOG_ERROR, "track %d: muxing mp3 at %dhz is not standard, to mux anyway set strict to -1\n",
                        i, track->par->sample_rate);
                    return AVERROR(EINVAL);
                } else {
                    av_log(s, AV_LOG_WARNING, "track %d: muxing mp3 at %dhz is not standard in MP4\n",
                           i, track->par->sample_rate);
                }
            }
        } else if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
            track->timescale = st->time_base.den;
        } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
            track->timescale = st->time_base.den;
        } else {
            track->timescale = MOV_TIMESCALE;
        }
        if (!track->height)
            track->height = st->codecpar->height;
        /* The ism specific timescale isn't mandatory, but is assumed by
         * some tools, such as mp4split. */
        if (mov->mode == MODE_ISM)
            track->timescale = 10000000;

        avpriv_set_pts_info(st, 64, 1, track->timescale);

        if (mov->encryption_scheme == MOV_ENC_CENC_AES_CTR) {
            ret = ff_mov_cenc_init(&track->cenc, mov->encryption_key,
                track->par->codec_id == AV_CODEC_ID_H264, s->flags & AVFMT_FLAG_BITEXACT);
            if (ret)
                return ret;
        }
    }

    enable_tracks(s);
    return 0;
}