ffmpeg | branch: master | Matthieu Bouron <[email protected]> | 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 <[email protected]> > 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; } _______________________________________________ ffmpeg-cvslog mailing list [email protected] http://ffmpeg.org/mailman/listinfo/ffmpeg-cvslog
