static int matroska_parse_tracks(AVFormatContext *s)

{

    MatroskaDemuxContext *matroska = s->priv_data;

    MatroskaTrack *tracks = matroska->tracks.elem;

    AVStream *st;

    int i, j, ret;

    int k;



    for (i = 0; i < matroska->tracks.nb_elem; i++) {

        MatroskaTrack *track = &tracks[i];

        enum AVCodecID codec_id = AV_CODEC_ID_NONE;

        EbmlList *encodings_list = &track->encodings;

        MatroskaTrackEncoding *encodings = encodings_list->elem;

        uint8_t *extradata = NULL;

        int extradata_size = 0;

        int extradata_offset = 0;

        uint32_t fourcc = 0;

        AVIOContext b;

        char* key_id_base64 = NULL;

        int bit_depth = -1;



        /* Apply some sanity checks. */

        if (track->type != MATROSKA_TRACK_TYPE_VIDEO &&

            track->type != MATROSKA_TRACK_TYPE_AUDIO &&

            track->type != MATROSKA_TRACK_TYPE_SUBTITLE &&

            track->type != MATROSKA_TRACK_TYPE_METADATA) {

            av_log(matroska->ctx, AV_LOG_INFO,

                   "Unknown or unsupported track type %"PRIu64"\n",

                   track->type);

            continue;

        }

        if (!track->codec_id)

            continue;



        if (track->audio.samplerate < 0 || track->audio.samplerate > INT_MAX ||

            isnan(track->audio.samplerate)) {

            av_log(matroska->ctx, AV_LOG_WARNING,

                   "Invalid sample rate %f, defaulting to 8000 instead.\n",

                   track->audio.samplerate);

            track->audio.samplerate = 8000;

        }



        if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {

            if (!track->default_duration && track->video.frame_rate > 0)

                track->default_duration = 1000000000 / track->video.frame_rate;

            if (track->video.display_width == -1)

                track->video.display_width = track->video.pixel_width;

            if (track->video.display_height == -1)

                track->video.display_height = track->video.pixel_height;

            if (track->video.color_space.size == 4)

                fourcc = AV_RL32(track->video.color_space.data);

        } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {

            if (!track->audio.out_samplerate)

                track->audio.out_samplerate = track->audio.samplerate;

        }

        if (encodings_list->nb_elem > 1) {

            av_log(matroska->ctx, AV_LOG_ERROR,

                   "Multiple combined encodings not supported");

        } else if (encodings_list->nb_elem == 1) {

            if (encodings[0].type) {

                if (encodings[0].encryption.key_id.size > 0) {

                    /* Save the encryption key id to be stored later as a

                       metadata tag. */

                    const int b64_size = AV_BASE64_SIZE(encodings[0].encryption.key_id.size);

                    key_id_base64 = av_malloc(b64_size);

                    if (key_id_base64 == NULL)

                        return AVERROR(ENOMEM);



                    av_base64_encode(key_id_base64, b64_size,

                                     encodings[0].encryption.key_id.data,

                                     encodings[0].encryption.key_id.size);

                } else {

                    encodings[0].scope = 0;

                    av_log(matroska->ctx, AV_LOG_ERROR,

                           "Unsupported encoding type");

                }

            } else if (

#if CONFIG_ZLIB

                 encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB  &&

#endif

#if CONFIG_BZLIB

                 encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_BZLIB &&

#endif

#if CONFIG_LZO

                 encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_LZO   &&

#endif

                 encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP) {

                encodings[0].scope = 0;

                av_log(matroska->ctx, AV_LOG_ERROR,

                       "Unsupported encoding type");

            } else if (track->codec_priv.size && encodings[0].scope & 2) {

                uint8_t *codec_priv = track->codec_priv.data;

                int ret = matroska_decode_buffer(&track->codec_priv.data,

                                                 &track->codec_priv.size,

                                                 track);

                if (ret < 0) {

                    track->codec_priv.data = NULL;

                    track->codec_priv.size = 0;

                    av_log(matroska->ctx, AV_LOG_ERROR,

                           "Failed to decode codec private data\n");

                }



                if (codec_priv != track->codec_priv.data)

                    av_free(codec_priv);

            }

        }



        for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) {

            if (!strncmp(ff_mkv_codec_tags[j].str, track->codec_id,

                         strlen(ff_mkv_codec_tags[j].str))) {

                codec_id = ff_mkv_codec_tags[j].id;

                break;

            }

        }



        st = track->stream = avformat_new_stream(s, NULL);

        if (!st) {

            av_free(key_id_base64);

            return AVERROR(ENOMEM);

        }



        if (key_id_base64) {

            /* export encryption key id as base64 metadata tag */

            av_dict_set(&st->metadata, "enc_key_id", key_id_base64, 0);

            av_freep(&key_id_base64);

        }



        if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") &&

             track->codec_priv.size >= 40               &&

            track->codec_priv.data) {

            track->ms_compat    = 1;

            bit_depth           = AV_RL16(track->codec_priv.data + 14);

            fourcc              = AV_RL32(track->codec_priv.data + 16);

            codec_id            = ff_codec_get_id(ff_codec_bmp_tags,

                                                  fourcc);

            if (!codec_id)

                codec_id        = ff_codec_get_id(ff_codec_movvideo_tags,

                                                  fourcc);

            extradata_offset    = 40;

        } else if (!strcmp(track->codec_id, "A_MS/ACM") &&

                   track->codec_priv.size >= 14         &&

                   track->codec_priv.data) {

            int ret;

            ffio_init_context(&b, track->codec_priv.data,

                              track->codec_priv.size,

                              0, NULL, NULL, NULL, NULL);

            ret = ff_get_wav_header(s, &b, st->codecpar, track->codec_priv.size, 0);

            if (ret < 0)

                return ret;

            codec_id         = st->codecpar->codec_id;

            fourcc           = st->codecpar->codec_tag;

            extradata_offset = FFMIN(track->codec_priv.size, 18);

        } else if (!strcmp(track->codec_id, "A_QUICKTIME")

                   /* Normally 36, but allow noncompliant private data */

                   && (track->codec_priv.size >= 32)

                   && (track->codec_priv.data)) {

            uint16_t sample_size;

            int ret = get_qt_codec(track, &fourcc, &codec_id);

            if (ret < 0)

                return ret;

            sample_size = AV_RB16(track->codec_priv.data + 26);

            if (fourcc == 0) {

                if (sample_size == 8) {

                    fourcc = MKTAG('r','a','w',' ');

                    codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);

                } else if (sample_size == 16) {

                    fourcc = MKTAG('t','w','o','s');

                    codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);

                }

            }

            if ((fourcc == MKTAG('t','w','o','s') ||

                    fourcc == MKTAG('s','o','w','t')) &&

                    sample_size == 8)

                codec_id = AV_CODEC_ID_PCM_S8;

        } else if (!strcmp(track->codec_id, "V_QUICKTIME") &&

                   (track->codec_priv.size >= 21)          &&

                   (track->codec_priv.data)) {

            int ret = get_qt_codec(track, &fourcc, &codec_id);

            if (ret < 0)

                return ret;

            if (codec_id == AV_CODEC_ID_NONE && AV_RL32(track->codec_priv.data+4) == AV_RL32("SMI ")) {

                fourcc = MKTAG('S','V','Q','3');

                codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc);

            }

            if (codec_id == AV_CODEC_ID_NONE)

                av_log(matroska->ctx, AV_LOG_ERROR,

                       "mov FourCC not found %s.\n", av_fourcc2str(fourcc));

            if (track->codec_priv.size >= 86) {

                bit_depth = AV_RB16(track->codec_priv.data + 82);

                ffio_init_context(&b, track->codec_priv.data,

                                  track->codec_priv.size,

                                  0, NULL, NULL, NULL, NULL);

                if (ff_get_qtpalette(codec_id, &b, track->palette)) {

                    bit_depth &= 0x1F;

                    track->has_palette = 1;

                }

            }

        } else if (codec_id == AV_CODEC_ID_PCM_S16BE) {

            switch (track->audio.bitdepth) {

            case  8:

                codec_id = AV_CODEC_ID_PCM_U8;

                break;

            case 24:

                codec_id = AV_CODEC_ID_PCM_S24BE;

                break;

            case 32:

                codec_id = AV_CODEC_ID_PCM_S32BE;

                break;

            }

        } else if (codec_id == AV_CODEC_ID_PCM_S16LE) {

            switch (track->audio.bitdepth) {

            case  8:

                codec_id = AV_CODEC_ID_PCM_U8;

                break;

            case 24:

                codec_id = AV_CODEC_ID_PCM_S24LE;

                break;

            case 32:

                codec_id = AV_CODEC_ID_PCM_S32LE;

                break;

            }

        } else if (codec_id == AV_CODEC_ID_PCM_F32LE &&

                   track->audio.bitdepth == 64) {

            codec_id = AV_CODEC_ID_PCM_F64LE;

        } else if (codec_id == AV_CODEC_ID_AAC && !track->codec_priv.size) {

            int profile = matroska_aac_profile(track->codec_id);

            int sri     = matroska_aac_sri(track->audio.samplerate);

            extradata   = av_mallocz(5 + AV_INPUT_BUFFER_PADDING_SIZE);

            if (!extradata)

                return AVERROR(ENOMEM);

            extradata[0] = (profile << 3) | ((sri & 0x0E) >> 1);

            extradata[1] = ((sri & 0x01) << 7) | (track->audio.channels << 3);

            if (strstr(track->codec_id, "SBR")) {

                sri            = matroska_aac_sri(track->audio.out_samplerate);

                extradata[2]   = 0x56;

                extradata[3]   = 0xE5;

                extradata[4]   = 0x80 | (sri << 3);

                extradata_size = 5;

            } else

                extradata_size = 2;

        } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size && track->codec_priv.size < INT_MAX - 12 - AV_INPUT_BUFFER_PADDING_SIZE) {

            /* Only ALAC's magic cookie is stored in Matroska's track headers.

             * Create the "atom size", "tag", and "tag version" fields the

             * decoder expects manually. */

            extradata_size = 12 + track->codec_priv.size;

            extradata      = av_mallocz(extradata_size +

                                        AV_INPUT_BUFFER_PADDING_SIZE);

            if (!extradata)

                return AVERROR(ENOMEM);

            AV_WB32(extradata, extradata_size);

            memcpy(&extradata[4], "alac", 4);

            AV_WB32(&extradata[8], 0);

            memcpy(&extradata[12], track->codec_priv.data,

                   track->codec_priv.size);

        } else if (codec_id == AV_CODEC_ID_TTA) {

            extradata_size = 30;

            extradata      = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);

            if (!extradata)

                return AVERROR(ENOMEM);

            ffio_init_context(&b, extradata, extradata_size, 1,

                              NULL, NULL, NULL, NULL);

            avio_write(&b, "TTA1", 4);

            avio_wl16(&b, 1);

            if (track->audio.channels > UINT16_MAX ||

                track->audio.bitdepth > UINT16_MAX) {

                av_log(matroska->ctx, AV_LOG_WARNING,

                       "Too large audio channel number %"PRIu64

                       " or bitdepth %"PRIu64". Skipping track.\n",

                       track->audio.channels, track->audio.bitdepth);

                av_freep(&extradata);

                if (matroska->ctx->error_recognition & AV_EF_EXPLODE)

                    return AVERROR_INVALIDDATA;

                else

                    continue;

            }

            avio_wl16(&b, track->audio.channels);

            avio_wl16(&b, track->audio.bitdepth);

            if (track->audio.out_samplerate < 0 || track->audio.out_samplerate > INT_MAX)

                return AVERROR_INVALIDDATA;

            avio_wl32(&b, track->audio.out_samplerate);

            avio_wl32(&b, av_rescale((matroska->duration * matroska->time_scale),

                                     track->audio.out_samplerate,

                                     AV_TIME_BASE * 1000));

        } else if (codec_id == AV_CODEC_ID_RV10 ||

                   codec_id == AV_CODEC_ID_RV20 ||

                   codec_id == AV_CODEC_ID_RV30 ||

                   codec_id == AV_CODEC_ID_RV40) {

            extradata_offset = 26;

        } else if (codec_id == AV_CODEC_ID_RA_144) {

            track->audio.out_samplerate = 8000;

            track->audio.channels       = 1;

        } else if ((codec_id == AV_CODEC_ID_RA_288 ||

                    codec_id == AV_CODEC_ID_COOK   ||

                    codec_id == AV_CODEC_ID_ATRAC3 ||

                    codec_id == AV_CODEC_ID_SIPR)

                      && track->codec_priv.data) {

            int flavor;



            ffio_init_context(&b, track->codec_priv.data,

                              track->codec_priv.size,

                              0, NULL, NULL, NULL, NULL);

            avio_skip(&b, 22);

            flavor                       = avio_rb16(&b);

            track->audio.coded_framesize = avio_rb32(&b);

            avio_skip(&b, 12);

            track->audio.sub_packet_h    = avio_rb16(&b);

            track->audio.frame_size      = avio_rb16(&b);

            track->audio.sub_packet_size = avio_rb16(&b);

            if (flavor                        < 0 ||

                track->audio.coded_framesize <= 0 ||

                track->audio.sub_packet_h    <= 0 ||

                track->audio.frame_size      <= 0 ||

                track->audio.sub_packet_size <= 0 && codec_id != AV_CODEC_ID_SIPR)

                return AVERROR_INVALIDDATA;

            track->audio.buf = av_malloc_array(track->audio.sub_packet_h,

                                               track->audio.frame_size);

            if (!track->audio.buf)

                return AVERROR(ENOMEM);

            if (codec_id == AV_CODEC_ID_RA_288) {

                st->codecpar->block_align = track->audio.coded_framesize;

                track->codec_priv.size = 0;

            } else {

                if (codec_id == AV_CODEC_ID_SIPR && flavor < 4) {

                    static const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 };

                    track->audio.sub_packet_size = ff_sipr_subpk_size[flavor];

                    st->codecpar->bit_rate          = sipr_bit_rate[flavor];

                }

                st->codecpar->block_align = track->audio.sub_packet_size;

                extradata_offset       = 78;

            }

        } else if (codec_id == AV_CODEC_ID_FLAC && track->codec_priv.size) {

            ret = matroska_parse_flac(s, track, &extradata_offset);

            if (ret < 0)

                return ret;

        } else if (codec_id == AV_CODEC_ID_PRORES && track->codec_priv.size == 4) {

            fourcc = AV_RL32(track->codec_priv.data);

        }

        track->codec_priv.size -= extradata_offset;



        if (codec_id == AV_CODEC_ID_NONE)

            av_log(matroska->ctx, AV_LOG_INFO,

                   "Unknown/unsupported AVCodecID %s.\n", track->codec_id);



        if (track->time_scale < 0.01)

            track->time_scale = 1.0;

        avpriv_set_pts_info(st, 64, matroska->time_scale * track->time_scale,

                            1000 * 1000 * 1000);    /* 64 bit pts in ns */



        /* convert the delay from ns to the track timebase */

        track->codec_delay_in_track_tb = av_rescale_q(track->codec_delay,

                                          (AVRational){ 1, 1000000000 },

                                          st->time_base);



        st->codecpar->codec_id = codec_id;



        if (strcmp(track->language, "und"))

            av_dict_set(&st->metadata, "language", track->language, 0);

        av_dict_set(&st->metadata, "title", track->name, 0);



        if (track->flag_default)

            st->disposition |= AV_DISPOSITION_DEFAULT;

        if (track->flag_forced)

            st->disposition |= AV_DISPOSITION_FORCED;



        if (!st->codecpar->extradata) {

            if (extradata) {

                st->codecpar->extradata      = extradata;

                st->codecpar->extradata_size = extradata_size;

            } else if (track->codec_priv.data && track->codec_priv.size > 0) {

                if (ff_alloc_extradata(st->codecpar, track->codec_priv.size))

                    return AVERROR(ENOMEM);

                memcpy(st->codecpar->extradata,

                       track->codec_priv.data + extradata_offset,

                       track->codec_priv.size);

            }

        }



        if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {

            MatroskaTrackPlane *planes = track->operation.combine_planes.elem;

            int display_width_mul  = 1;

            int display_height_mul = 1;



            st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;

            st->codecpar->codec_tag  = fourcc;

            if (bit_depth >= 0)

                st->codecpar->bits_per_coded_sample = bit_depth;

            st->codecpar->width      = track->video.pixel_width;

            st->codecpar->height     = track->video.pixel_height;



            if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_INTERLACED)

                st->codecpar->field_order = mkv_field_order(matroska, track->video.field_order);

            else if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_PROGRESSIVE)

                st->codecpar->field_order = AV_FIELD_PROGRESSIVE;



            if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB)

                mkv_stereo_mode_display_mul(track->video.stereo_mode, &display_width_mul, &display_height_mul);



            if (track->video.display_unit < MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN) {

                av_reduce(&st->sample_aspect_ratio.num,

                          &st->sample_aspect_ratio.den,

                          st->codecpar->height * track->video.display_width  * display_width_mul,

                          st->codecpar->width  * track->video.display_height * display_height_mul,

                          255);

            }

            if (st->codecpar->codec_id != AV_CODEC_ID_HEVC)

                st->need_parsing = AVSTREAM_PARSE_HEADERS;



            if (track->default_duration) {

                av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,

                          1000000000, track->default_duration, 30000);

#if FF_API_R_FRAME_RATE

                if (   st->avg_frame_rate.num < st->avg_frame_rate.den * 1000LL

                    && st->avg_frame_rate.num > st->avg_frame_rate.den * 5LL)

                    st->r_frame_rate = st->avg_frame_rate;

#endif

            }



            /* export stereo mode flag as metadata tag */

            if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB)

                av_dict_set(&st->metadata, "stereo_mode", ff_matroska_video_stereo_mode[track->video.stereo_mode], 0);



            /* export alpha mode flag as metadata tag  */

            if (track->video.alpha_mode)

                av_dict_set(&st->metadata, "alpha_mode", "1", 0);



            /* if we have virtual track, mark the real tracks */

            for (j=0; j < track->operation.combine_planes.nb_elem; j++) {

                char buf[32];

                if (planes[j].type >= MATROSKA_VIDEO_STEREO_PLANE_COUNT)

                    continue;

                snprintf(buf, sizeof(buf), "%s_%d",

                         ff_matroska_video_stereo_plane[planes[j].type], i);

                for (k=0; k < matroska->tracks.nb_elem; k++)

                    if (planes[j].uid == tracks[k].uid && tracks[k].stream) {

                        av_dict_set(&tracks[k].stream->metadata,

                                    "stereo_mode", buf, 0);

                        break;

                    }

            }

            // add stream level stereo3d side data if it is a supported format

            if (track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB &&

                track->video.stereo_mode != 10 && track->video.stereo_mode != 12) {

                int ret = ff_mkv_stereo3d_conv(st, track->video.stereo_mode);

                if (ret < 0)

                    return ret;

            }



            ret = mkv_parse_video_color(st, track);

            if (ret < 0)

                return ret;

            ret = mkv_parse_video_projection(st, track);

            if (ret < 0)

                return ret;

        } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {

            st->codecpar->codec_type  = AVMEDIA_TYPE_AUDIO;

            st->codecpar->codec_tag   = fourcc;

            st->codecpar->sample_rate = track->audio.out_samplerate;

            st->codecpar->channels    = track->audio.channels;

            if (!st->codecpar->bits_per_coded_sample)

                st->codecpar->bits_per_coded_sample = track->audio.bitdepth;

            if (st->codecpar->codec_id == AV_CODEC_ID_MP3)

                st->need_parsing = AVSTREAM_PARSE_FULL;

            else if (st->codecpar->codec_id != AV_CODEC_ID_AAC)

                st->need_parsing = AVSTREAM_PARSE_HEADERS;

            if (track->codec_delay > 0) {

                st->codecpar->initial_padding = av_rescale_q(track->codec_delay,

                                                             (AVRational){1, 1000000000},

                                                             (AVRational){1, st->codecpar->codec_id == AV_CODEC_ID_OPUS ?

                                                                             48000 : st->codecpar->sample_rate});

            }

            if (track->seek_preroll > 0) {

                st->codecpar->seek_preroll = av_rescale_q(track->seek_preroll,

                                                          (AVRational){1, 1000000000},

                                                          (AVRational){1, st->codecpar->sample_rate});

            }

        } else if (codec_id == AV_CODEC_ID_WEBVTT) {

            st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;



            if (!strcmp(track->codec_id, "D_WEBVTT/CAPTIONS")) {

                st->disposition |= AV_DISPOSITION_CAPTIONS;

            } else if (!strcmp(track->codec_id, "D_WEBVTT/DESCRIPTIONS")) {

                st->disposition |= AV_DISPOSITION_DESCRIPTIONS;

            } else if (!strcmp(track->codec_id, "D_WEBVTT/METADATA")) {

                st->disposition |= AV_DISPOSITION_METADATA;

            }

        } else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) {

            st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;

            if (st->codecpar->codec_id == AV_CODEC_ID_ASS)

                matroska->contains_ssa = 1;

        }

    }



    return 0;

}
