On Wed, Jul 13, 2016 at 4:55 PM, Matthieu Bouron <g...@videolan.org> wrote:
> ffmpeg | branch: master | Matthieu Bouron <matthieu.bou...@stupeflix.com> > | Wed Jul 13 16:34:39 2016 +0200| > [3c058f570128dcfa3a68f0860e2be7f098e8d6e1] | committer: Matthieu Bouron > > Merge commit '76729970049fe95659346503f7401a5d869f9959' > > * commit '76729970049fe95659346503f7401a5d869f9959': > mov: Implement support for multiple sample description tables > > Notes: > * The sc->stsc_data[index].id checks have been moved from the > mov_read_stsc > to mov_read_packet before the value is used in mov_change_extradata to > not break playback of samples with broken stsc entries (see sample of > ticket #1918). > > * sc->stsc_index is now checked against sc->stsc_count - 1 before it > is incremented so it remains lesser than sc->stsc_count. Fixes a crash > with: > > ./ffmpeg -i matrixbench_mpeg2.mpg -t 1 -frag_duration 200k test.mov > ./ffprobe -show_packets test.mov > > Merged-by: Matthieu Bouron <matthieu.bou...@stupeflix.com> > > > > http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=3c058f570128dcfa3a68f0860e2be7f098e8d6e1 > --- > > libavformat/isom.h | 8 ++++ > libavformat/mov.c | 123 > +++++++++++++++++++++++++++++++++++++++++++++++++--- > 2 files changed, 125 insertions(+), 6 deletions(-) > > diff --git a/libavformat/isom.h b/libavformat/isom.h > index 726f350..df6c15a 100644 > --- a/libavformat/isom.h > +++ b/libavformat/isom.h > @@ -128,6 +128,8 @@ typedef struct MOVStreamContext { > MOVStts *ctts_data; > unsigned int stsc_count; > MOVStsc *stsc_data; > + int stsc_index; > + int stsc_sample; > unsigned int stps_count; > unsigned *stps_data; ///< partial sync sample for mpeg-2 open gop > MOVElst *elst_data; > @@ -169,6 +171,12 @@ typedef struct MOVStreamContext { > int nb_frames_for_fps; > int64_t duration_for_fps; > > + /** extradata array (and size) for multiple stsd */ > + uint8_t **extradata; > + int *extradata_size; > + int last_stsd_index; > + int stsd_count; > + > int32_t *display_matrix; > uint32_t format; > > diff --git a/libavformat/mov.c b/libavformat/mov.c > index 485bb0b..756d0e8 100644 > --- a/libavformat/mov.c > +++ b/libavformat/mov.c > @@ -2215,8 +2215,7 @@ static int mov_skip_multiple_stsd(MOVContext *c, > AVIOContext *pb, > avio_skip(pb, size); > return 1; > } > - if ( codec_tag == AV_RL32("avc1") || > - codec_tag == AV_RL32("hvc1") || > + if ( codec_tag == AV_RL32("hvc1") || > codec_tag == AV_RL32("hev1") > ) > av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 or H.265 might > not play correctly.\n"); > @@ -2294,6 +2293,19 @@ int ff_mov_read_stsd_entries(MOVContext *c, > AVIOContext *pb, int entries) > return ret; > } else if (a.size > 0) > avio_skip(pb, a.size); > + > + if (sc->extradata) { > + int extra_size = st->codecpar->extradata_size; > + > + /* Move the current stream extradata to the stream context > one. */ > + sc->extradata_size[pseudo_stream_id] = extra_size; > + sc->extradata[pseudo_stream_id] = av_malloc(extra_size + > AV_INPUT_BUFFER_PADDING_SIZE); > + if (!sc->extradata[pseudo_stream_id]) > + return AVERROR(ENOMEM); > + memcpy(sc->extradata[pseudo_stream_id], > st->codecpar->extradata, extra_size); > + av_freep(&st->codecpar->extradata); > + st->codecpar->extradata_size = 0; > + } > } > > if (pb->eof_reached) > @@ -2304,13 +2316,41 @@ int ff_mov_read_stsd_entries(MOVContext *c, > AVIOContext *pb, int entries) > > static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom) > { > - int entries; > + AVStream *st; > + MOVStreamContext *sc; > + int ret; > + > + if (c->fc->nb_streams < 1) > + return 0; > + st = c->fc->streams[c->fc->nb_streams - 1]; > + sc = st->priv_data; > > avio_r8(pb); /* version */ > avio_rb24(pb); /* flags */ > - entries = avio_rb32(pb); > + sc->stsd_count = avio_rb32(pb); /* entries */ > + > + /* Prepare space for hosting multiple extradata. */ > + sc->extradata = av_mallocz_array(sc->stsd_count, > sizeof(*sc->extradata)); > + if (!sc->extradata) > + return AVERROR(ENOMEM); > + > + sc->extradata_size = av_mallocz_array(sc->stsd_count, > sizeof(sc->extradata_size)); > + if (!sc->extradata_size) > + return AVERROR(ENOMEM); > + > + ret = ff_mov_read_stsd_entries(c, pb, sc->stsd_count); > + if (ret < 0) > + return ret; > + > + /* Restore back the primary extradata. */ > + av_free(st->codecpar->extradata); > + st->codecpar->extradata_size = sc->extradata_size[0]; > + st->codecpar->extradata = av_mallocz(sc->extradata_size[0] + > AV_INPUT_BUFFER_PADDING_SIZE); > + if (!st->codecpar->extradata) > + return AVERROR(ENOMEM); > + memcpy(st->codecpar->extradata, sc->extradata[0], > sc->extradata_size[0]); > > - return ff_mov_read_stsd_entries(c, pb, entries); > + return 0; > } > > static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom) > @@ -2355,6 +2395,19 @@ static int mov_read_stsc(MOVContext *c, AVIOContext > *pb, MOVAtom atom) > return 0; > } > > +/* Compute the samples value for the stsc entry at the given index. */ > +static inline int mov_get_stsc_samples(MOVStreamContext *sc, int index) > +{ > + int chunk_count; > + > + if (index < sc->stsc_count - 1) > + chunk_count = sc->stsc_data[index + 1].first - > sc->stsc_data[index].first; > + else > + chunk_count = sc->chunk_count - (sc->stsc_data[index].first - 1); > + > + return sc->stsc_data[index].count * chunk_count; > +} > + > static int mov_read_stps(MOVContext *c, AVIOContext *pb, MOVAtom atom) > { > AVStream *st; > @@ -3212,7 +3265,6 @@ static int mov_read_trak(MOVContext *c, AVIOContext > *pb, MOVAtom atom) > } > /* Do not need those anymore. */ > av_freep(&sc->chunk_offsets); > - av_freep(&sc->stsc_data); > av_freep(&sc->sample_sizes); > av_freep(&sc->keyframes); > av_freep(&sc->stts_data); > @@ -4773,6 +4825,11 @@ static int mov_read_close(AVFormatContext *s) > av_freep(&sc->rap_group); > av_freep(&sc->display_matrix); > > + for (j = 0; j < sc->stsd_count; j++) > + av_free(sc->extradata[j]); > + av_freep(&sc->extradata); > + av_freep(&sc->extradata_size); > + > av_freep(&sc->cenc.auxiliary_info); > av_freep(&sc->cenc.auxiliary_info_sizes); > av_aes_ctr_free(sc->cenc.aes_ctr); > @@ -5190,6 +5247,29 @@ static int mov_switch_root(AVFormatContext *s, > int64_t target) > return 1; > } > > +static int mov_change_extradata(MOVStreamContext *sc, AVPacket *pkt) > +{ > + uint8_t *side, *extradata; > + int extradata_size; > + > + /* Save the current index. */ > + sc->last_stsd_index = sc->stsc_data[sc->stsc_index].id - 1; > + > + /* Notify the decoder that extradata changed. */ > + extradata_size = sc->extradata_size[sc->last_stsd_index]; > + extradata = sc->extradata[sc->last_stsd_index]; > + if (extradata_size > 0 && extradata) { > + side = av_packet_new_side_data(pkt, > + AV_PKT_DATA_NEW_EXTRADATA, > + extradata_size); > + if (!side) > + return AVERROR(ENOMEM); > + memcpy(side, extradata, extradata_size); > + } > + > + return 0; > +} > + > static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) > { > MOVContext *mov = s->priv_data; > @@ -5282,6 +5362,25 @@ static int mov_read_packet(AVFormatContext *s, > AVPacket *pkt) > pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0; > pkt->pos = sample->pos; > > + /* Multiple stsd handling. */ > + if (sc->stsc_data) { > + /* Keep track of the stsc index for the given sample, then check > + * if the stsd index is different from the last used one. */ > + sc->stsc_sample++; > + if (sc->stsc_index < sc->stsc_count - 1 && > + mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) { > + sc->stsc_index++; > + sc->stsc_sample = 0; > + /* Do not check indexes after a switch. */ > + } else if (sc->stsc_data[sc->stsc_index].id > 0 && > + sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count > && > + sc->stsc_data[sc->stsc_index].id - 1 != > sc->last_stsd_index) { > + ret = mov_change_extradata(sc, pkt); > + if (ret < 0) > + return ret; > + } > + } > + > if (mov->aax_mode) > aax_filter(pkt->data, pkt->size, mov); > > @@ -5352,6 +5451,18 @@ static int mov_seek_stream(AVFormatContext *s, > AVStream *st, int64_t timestamp, > } > } > > + /* adjust stsd index */ > + time_sample = 0; > + for (i = 0; i < sc->stsc_count; i++) { > + int next = time_sample + mov_get_stsc_samples(sc, i); > + if (next > sc->current_sample) { > + sc->stsc_index = i; > + sc->stsc_sample = sc->current_sample - time_sample; > + break; > + } > + time_sample = next; > + } > + > ret = mov_seek_auxiliary_info(s, sc); > if (ret < 0) { > return ret; > > > ====================================================================== > > diff --cc libavformat/isom.h > index 726f350,75aa70b..df6c15a > --- a/libavformat/isom.h > +++ b/libavformat/isom.h > @@@ -128,14 -105,13 +128,16 @@@ typedef struct MOVStreamContext > MOVStts *ctts_data; > unsigned int stsc_count; > MOVStsc *stsc_data; > + int stsc_index; > + int stsc_sample; > unsigned int stps_count; > unsigned *stps_data; ///< partial sync sample for mpeg-2 open gop > + MOVElst *elst_data; > + unsigned int elst_count; > int ctts_index; > int ctts_sample; > - unsigned int sample_size; > + unsigned int sample_size; ///< may contain value calculated from > stsd or value from stsz atom > + unsigned int stsz_sample_size; ///< always contains sample size from > stsz atom > unsigned int sample_count; > int *sample_sizes; > int keyframe_absent; > @@@ -166,22 -139,13 +168,28 @@@ > unsigned int rap_group_count; > MOVSbgp *rap_group; > > + int nb_frames_for_fps; > + int64_t duration_for_fps; > + > + /** extradata array (and size) for multiple stsd */ > + uint8_t **extradata; > + int *extradata_size; > + int last_stsd_index; > + int stsd_count; > + > int32_t *display_matrix; > + uint32_t format; > + > + struct { > + int use_subsamples; > + uint8_t* auxiliary_info; > + uint8_t* auxiliary_info_end; > + uint8_t* auxiliary_info_pos; > + uint8_t auxiliary_info_default_size; > + uint8_t* auxiliary_info_sizes; > + size_t auxiliary_info_sizes_count; > + struct AVAESCTR* aes_ctr; > + } cenc; > } MOVStreamContext; > > typedef struct MOVContext { > diff --cc libavformat/mov.c > index 485bb0b,a9b826f..756d0e8 > --- a/libavformat/mov.c > +++ b/libavformat/mov.c > @@@ -2215,11 -1784,6 +2215,10 @@@ static int mov_skip_multiple_stsd(MOVCo > avio_skip(pb, size); > return 1; > } > - if ( codec_tag == AV_RL32("avc1") || > - codec_tag == AV_RL32("hvc1") || > ++ if ( codec_tag == AV_RL32("hvc1") || > + codec_tag == AV_RL32("hev1") > + ) > + av_log(c->fc, AV_LOG_WARNING, "Concatenated H.264 or H.265 might > not play correctly.\n"); > > return 0; > } > @@@ -3201,18 -2615,13 +3254,17 @@@ static int mov_read_trak(MOVContext *c > st->codecpar->width = 0; /* let decoder init width/height */ > st->codecpar->height= 0; > break; > - case AV_CODEC_ID_MP3: > - st->need_parsing = AVSTREAM_PARSE_FULL; > - break; > } > > + // If the duration of the mp3 packets is not constant, then they > could need a parser > + if (st->codecpar->codec_id == AV_CODEC_ID_MP3 > + && sc->stts_count > 3 > + && sc->stts_count*10 > st->nb_frames > + && sc->time_scale == st->codecpar->sample_rate) { > + st->need_parsing = AVSTREAM_PARSE_FULL; > + } > /* Do not need those anymore. */ > av_freep(&sc->chunk_offsets); > - av_freep(&sc->stsc_data); > av_freep(&sc->sample_sizes); > av_freep(&sc->keyframes); > av_freep(&sc->stts_data); > @@@ -4773,9 -3431,10 +4825,14 @@@ static int mov_read_close(AVFormatConte > av_freep(&sc->rap_group); > av_freep(&sc->display_matrix); > > + for (j = 0; j < sc->stsd_count; j++) > + av_free(sc->extradata[j]); > + av_freep(&sc->extradata); > + av_freep(&sc->extradata_size); > ++ > + av_freep(&sc->cenc.auxiliary_info); > + av_freep(&sc->cenc.auxiliary_info_sizes); > + av_aes_ctr_free(sc->cenc.aes_ctr); > } > > if (mov->dv_demux) { > @@@ -5137,59 -3566,29 +5194,82 @@@ static AVIndexEntry *mov_find_next_samp > return sample; > } > > +static int should_retry(AVIOContext *pb, int error_code) { > + if (error_code == AVERROR_EOF || avio_feof(pb)) > + return 0; > + > + return 1; > +} > + > +static int mov_switch_root(AVFormatContext *s, int64_t target) > +{ > + MOVContext *mov = s->priv_data; > + int i, j; > + int already_read = 0; > + > + if (avio_seek(s->pb, target, SEEK_SET) != target) { > + av_log(mov->fc, AV_LOG_ERROR, "root atom offset 0x%"PRIx64": > partial file\n", target); > + return AVERROR_INVALIDDATA; > + } > + > + mov->next_root_atom = 0; > + > + for (i = 0; i < mov->fragment_index_count; i++) { > + MOVFragmentIndex *index = mov->fragment_index_data[i]; > + int found = 0; > + for (j = 0; j < index->item_count; j++) { > + MOVFragmentIndexItem *item = &index->items[j]; > + if (found) { > + mov->next_root_atom = item->moof_offset; > + break; // Advance to next index in outer loop > + } else if (item->moof_offset == target) { > + index->current_item = FFMIN(j, index->current_item); > + if (item->headers_read) > + already_read = 1; > + item->headers_read = 1; > + found = 1; > + } > + } > + if (!found) > + index->current_item = 0; > + } > + > + if (already_read) > + return 0; > + > + mov->found_mdat = 0; > + > + if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), > INT64_MAX }) < 0 || > + avio_feof(s->pb)) > + return AVERROR_EOF; > + av_log(s, AV_LOG_TRACE, "read fragments, offset 0x%"PRIx64"\n", > avio_tell(s->pb)); > + > + return 1; > +} > + > + static int mov_change_extradata(MOVStreamContext *sc, AVPacket *pkt) > + { > + uint8_t *side, *extradata; > + int extradata_size; > + > + /* Save the current index. */ > + sc->last_stsd_index = sc->stsc_data[sc->stsc_index].id - 1; > + > + /* Notify the decoder that extradata changed. */ > + extradata_size = sc->extradata_size[sc->last_stsd_index]; > + extradata = sc->extradata[sc->last_stsd_index]; > + if (extradata_size > 0 && extradata) { > + side = av_packet_new_side_data(pkt, > + AV_PKT_DATA_NEW_EXTRADATA, > + extradata_size); > + if (!side) > + return AVERROR(ENOMEM); > + memcpy(side, extradata, extradata_size); > + } > + > + return 0; > + } > + > static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) > { > MOVContext *mov = s->priv_data; > @@@ -5281,42 -3669,26 +5361,61 @@@ > goto retry; > pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0; > pkt->pos = sample->pos; > - av_log(s, AV_LOG_TRACE, "stream %d, pts %"PRId64", dts %"PRId64", > pos 0x%"PRIx64", duration %"PRId64"\n", > - pkt->stream_index, pkt->pts, pkt->dts, pkt->pos, > pkt->duration); > > + /* Multiple stsd handling. */ > + if (sc->stsc_data) { > + /* Keep track of the stsc index for the given sample, then check > + * if the stsd index is different from the last used one. */ > + sc->stsc_sample++; > - if (sc->stsc_index < sc->stsc_count && > ++ if (sc->stsc_index < sc->stsc_count - 1 && > + mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) > { > + sc->stsc_index++; > + sc->stsc_sample = 0; > + /* Do not check indexes after a switch. */ > - } else if (sc->stsc_data[sc->stsc_index].id - 1 != > sc->last_stsd_index) { > ++ } else if (sc->stsc_data[sc->stsc_index].id > 0 && > ++ sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count > && > ++ sc->stsc_data[sc->stsc_index].id - 1 != > sc->last_stsd_index) { > + ret = mov_change_extradata(sc, pkt); > + if (ret < 0) > + return ret; > + } > + } > + > + if (mov->aax_mode) > + aax_filter(pkt->data, pkt->size, mov); > + > + if (sc->cenc.aes_ctr) { > + ret = cenc_filter(mov, sc, pkt->data, pkt->size); > + if (ret) { > + return ret; > + } > + } > + > + return 0; > +} > + > +static int mov_seek_fragment(AVFormatContext *s, AVStream *st, int64_t > timestamp) > +{ > + MOVContext *mov = s->priv_data; > + int i, j; > + > + if (!mov->fragment_index_complete) > + return 0; > + > + for (i = 0; i < mov->fragment_index_count; i++) { > + if (mov->fragment_index_data[i]->track_id == st->id) { > + MOVFragmentIndex *index = mov->fragment_index_data[i]; > + for (j = index->item_count - 1; j >= 0; j--) { > + if (index->items[j].time <= timestamp) { > + if (index->items[j].headers_read) > + return 0; > + > + return mov_switch_root(s, > index->items[j].moof_offset); > + } > + } > + } > + } > + > return 0; > } > > @@@ -5352,11 -3720,18 +5451,23 @@@ static int mov_seek_stream(AVFormatCont > } > } > > + /* adjust stsd index */ > + time_sample = 0; > + for (i = 0; i < sc->stsc_count; i++) { > + int next = time_sample + mov_get_stsc_samples(sc, i); > + if (next > sc->current_sample) { > + sc->stsc_index = i; > + sc->stsc_sample = sc->current_sample - time_sample; > + break; > + } > + time_sample = next; > + } > + > + ret = mov_seek_auxiliary_info(s, sc); > + if (ret < 0) { > + return ret; > + } > + > return sample; > } > > > Don't know if that's come to the author's attention. This caused a regression, see http://trac.ffmpeg.org/ticket/5723 _______________________________________________ ffmpeg-cvslog mailing list ffmpeg-cvslog@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-cvslog