static int64_t get_stream_info_time(MOVFragmentStreamInfo * frag_stream_info)
{
-
- if (frag_stream_info) {
- if (frag_stream_info->sidx_pts != AV_NOPTS_VALUE)
- return frag_stream_info->sidx_pts;
- if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE)
- return frag_stream_info->first_tfra_pts;
- if (frag_stream_info->tfdt_dts != AV_NOPTS_VALUE)
- return frag_stream_info->tfdt_dts;
- }
- return AV_NOPTS_VALUE;
+ av_assert0(frag_stream_info);
+ if (frag_stream_info->sidx_pts != AV_NOPTS_VALUE)
+ return frag_stream_info->sidx_pts;
+ if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE)
+ return frag_stream_info->first_tfra_pts;
+ return frag_stream_info->tfdt_dts;
}
static int64_t get_frag_time(MOVFragmentIndex *frag_index,
static int search_frag_timestamp(MOVFragmentIndex *frag_index,
AVStream *st, int64_t timestamp)
{
- int a, b, m;
+ int a, b, m, m0;
int64_t frag_time;
int id = -1;
b = frag_index->nb_items;
while (b - a > 1) {
- m = (a + b) >> 1;
- frag_time = get_frag_time(frag_index, m, id);
- if (frag_time != AV_NOPTS_VALUE) {
- if (frag_time >= timestamp)
- b = m;
- if (frag_time <= timestamp)
- a = m;
- }
+ m0 = m = (a + b) >> 1;
+
+ while (m < b &&
+ (frag_time = get_frag_time(frag_index, m, id)) == AV_NOPTS_VALUE)
+ m++;
+
+ if (m < b && frag_time <= timestamp)
+ a = m;
+ else
+ b = m0;
}
+
return a;
}
return -1;
for (i = 0; i < c->fc->nb_streams; i++) {
+ // Avoid building frag index if streams lack track id.
+ if (c->fc->streams[i]->id < 0)
+ return AVERROR_INVALIDDATA;
+
frag_stream_info[i].id = c->fc->streams[i]->id;
frag_stream_info[i].sidx_pts = AV_NOPTS_VALUE;
frag_stream_info[i].tfdt_dts = AV_NOPTS_VALUE;
static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
+ // Set by mov_read_tfhd(). mov_read_trun() will reject files missing tfhd.
+ c->fragment.found_tfhd = 0;
+
if (!c->has_looked_for_mfra && c->use_mfra_for > 0) {
c->has_looked_for_mfra = 1;
if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
if (ret < 0)
return ret;
if (atom.type == MKTAG('h','v','c','C') && st->codecpar->codec_tag == MKTAG('d','v','h','1'))
+ /* HEVC-based Dolby Vision derived from hvc1.
+ Happens to match with an identifier
+ previously utilized for DV. Thus, if we have
+ the hvcC extradata box available as specified,
+ set codec to HEVC */
st->codecpar->codec_id = AV_CODEC_ID_HEVC;
return 0;
id = ff_codec_get_id(ff_codec_movsubtitle_tags, format);
if (id > 0)
st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
+ else
+ id = ff_codec_get_id(ff_codec_movdata_tags, format);
}
}
// Read QT version 1 fields. In version 0 these do not exist.
av_log(c->fc, AV_LOG_TRACE, "version =%d, isom =%d\n", version, c->isom);
if (!c->isom ||
- (compatible_brands && strstr(compatible_brands->value, "qt "))) {
-
+ (compatible_brands && strstr(compatible_brands->value, "qt ")) ||
+ (sc->stsd_version == 0 && version > 0)) {
if (version == 1) {
sc->samples_per_frame = avio_rb32(pb);
avio_rb32(pb); /* bytes per packet */
"size=%"PRId64" 4CC=%s codec_type=%d\n", size,
av_fourcc2str(format), st->codecpar->codec_type);
+ st->codecpar->codec_id = id;
if (st->codecpar->codec_type==AVMEDIA_TYPE_VIDEO) {
- st->codecpar->codec_id = id;
mov_parse_stsd_video(c, pb, st, sc);
} else if (st->codecpar->codec_type==AVMEDIA_TYPE_AUDIO) {
- st->codecpar->codec_id = id;
mov_parse_stsd_audio(c, pb, st, sc);
if (st->codecpar->sample_rate < 0) {
av_log(c->fc, AV_LOG_ERROR, "Invalid sample rate %d\n", st->codecpar->sample_rate);
return AVERROR_INVALIDDATA;
}
} else if (st->codecpar->codec_type==AVMEDIA_TYPE_SUBTITLE){
- st->codecpar->codec_id = id;
mov_parse_stsd_subtitle(c, pb, st, sc,
size - (avio_tell(pb) - start_pos));
} else {
st = c->fc->streams[c->fc->nb_streams - 1];
sc = st->priv_data;
- avio_r8(pb); /* version */
+ sc->stsd_version = avio_r8(pb);
avio_rb24(pb); /* flags */
entries = avio_rb32(pb);
if (mov_stsc_index_valid(index, sc->stsc_count))
chunk_count = sc->stsc_data[index + 1].first - sc->stsc_data[index].first;
- else
+ else {
+ // Validation for stsc / stco happens earlier in mov_read_stsc + mov_read_trak.
+ av_assert0(sc->stsc_data[index].first <= sc->chunk_count);
chunk_count = sc->chunk_count - (sc->stsc_data[index].first - 1);
+ }
return sc->stsc_data[index].count * (int64_t)chunk_count;
}
av_log(c->fc, AV_LOG_TRACE, "sample_count=%d, sample_duration=%d\n",
sample_count, sample_duration);
- if ( i+1 == entries
- && i
- && sample_count == 1
- && total_sample_count > 100
- && sample_duration/10 > duration / total_sample_count)
- sample_duration = duration / total_sample_count;
duration+=(int64_t)sample_duration*(uint64_t)sample_count;
total_sample_count+=sample_count;
}
MOVIndexRange *current_index_range;
int i;
int found_keyframe_after_edit = 0;
+ int found_non_empty_edit = 0;
if (!msc->elst_data || msc->elst_count <= 0 || nb_old <= 0) {
return;
edit_list_dts_counter = edit_list_dts_entry_end;
edit_list_dts_entry_end += edit_list_duration;
num_discarded_begin = 0;
- if (edit_list_media_time == -1) {
+ if (!found_non_empty_edit && edit_list_media_time == -1) {
empty_edits_sum_duration += edit_list_duration;
continue;
}
+ found_non_empty_edit = 1;
// If we encounter a non-negative edit list reset the skip_samples/start_pad fields and set them
// according to the edit list below.
st = avformat_new_stream(c->fc, NULL);
if (!st) return AVERROR(ENOMEM);
- st->id = c->fc->nb_streams;
+ st->id = -1;
sc = av_mallocz(sizeof(MOVStreamContext));
if (!sc) return AVERROR(ENOMEM);
c->trak_index = -1;
+ // Here stsc refers to a chunk not described in stco. This is technically invalid,
+ // but we can overlook it (clearing stsc) whenever stts_count == 0 (indicating no samples).
+ if (!sc->chunk_count && !sc->stts_count && sc->stsc_count) {
+ sc->stsc_count = 0;
+ av_freep(&sc->stsc_data);
+ }
+
/* sanity checks */
if ((sc->chunk_count && (!sc->stts_count || !sc->stsc_count ||
(!sc->sample_size && !sc->sample_count))) ||
st->index);
return 0;
}
- if (sc->chunk_count && sc->stsc_count && sc->stsc_data[ sc->stsc_count - 1 ].first > sc->chunk_count) {
+ if (sc->stsc_count && sc->stsc_data[ sc->stsc_count - 1 ].first > sc->chunk_count) {
av_log(c->fc, AV_LOG_ERROR, "stream %d, contradictionary STSC and STCO\n",
st->index);
return AVERROR_INVALIDDATA;
st = c->fc->streams[c->fc->nb_streams-1];
sc = st->priv_data;
+ // Each stream (trak) should have exactly 1 tkhd. This catches bad files and
+ // avoids corrupting AVStreams mapped to an earlier tkhd.
+ if (st->id != -1)
+ return AVERROR_INVALIDDATA;
+
version = avio_r8(pb);
flags = avio_rb24(pb);
st->disposition |= (flags & MOV_TKHD_FLAG_ENABLED) ? AV_DISPOSITION_DEFAULT : 0;
MOVTrackExt *trex = NULL;
int flags, track_id, i;
+ c->fragment.found_tfhd = 1;
+
avio_r8(pb); /* version */
flags = avio_rb24(pb);
track_id = avio_rb32(pb);
if (!track_id)
return AVERROR_INVALIDDATA;
- frag->track_id = track_id;
- set_frag_stream(&c->frag_index, track_id);
for (i = 0; i < c->trex_count; i++)
- if (c->trex_data[i].track_id == frag->track_id) {
+ if (c->trex_data[i].track_id == track_id) {
trex = &c->trex_data[i];
break;
}
if (!trex) {
- av_log(c->fc, AV_LOG_ERROR, "could not find corresponding trex\n");
- return AVERROR_INVALIDDATA;
+ av_log(c->fc, AV_LOG_WARNING, "could not find corresponding trex (id %u)\n", track_id);
+ return 0;
}
+ frag->track_id = track_id;
+ set_frag_stream(&c->frag_index, track_id);
frag->base_data_offset = flags & MOV_TFHD_BASE_DATA_OFFSET ?
avio_rb64(pb) : flags & MOV_TFHD_DEFAULT_BASE_IS_MOOF ?
}
}
if (!st) {
- av_log(c->fc, AV_LOG_ERROR, "could not find corresponding track id %u\n", frag->track_id);
- return AVERROR_INVALIDDATA;
+ av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %u\n", frag->track_id);
+ return 0;
}
sc = st->priv_data;
if (sc->pseudo_stream_id + 1 != frag->stsd_id && sc->pseudo_stream_id != -1)
AVIndexEntry *new_entries;
MOVFragmentStreamInfo * frag_stream_info;
+ if (!frag->found_tfhd) {
+ av_log(c->fc, AV_LOG_ERROR, "trun track id unknown, no tfhd was found\n");
+ return AVERROR_INVALIDDATA;
+ }
+
for (i = 0; i < c->fc->nb_streams; i++) {
if (c->fc->streams[i]->id == frag->track_id) {
st = c->fc->streams[i];
}
}
if (!st) {
- av_log(c->fc, AV_LOG_ERROR, "could not find corresponding track id %u\n", frag->track_id);
- return AVERROR_INVALIDDATA;
+ av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %u\n", frag->track_id);
+ return 0;
}
sc = st->priv_data;
if (sc->pseudo_stream_id+1 != frag->stsd_id && sc->pseudo_stream_id != -1)
break;
}
}
+ av_assert0(index_entry_pos <= st->nb_index_entries);
avio_r8(pb); /* version */
flags = avio_rb24(pb);
av_log(c->fc, AV_LOG_TRACE, "first sample flags 0x%x\n", first_sample_flags);
// realloc space for new index entries
- if((unsigned)st->nb_index_entries + entries >= UINT_MAX / sizeof(AVIndexEntry)) {
+ if((uint64_t)st->nb_index_entries + entries >= UINT_MAX / sizeof(AVIndexEntry)) {
entries = UINT_MAX / sizeof(AVIndexEntry) - st->nb_index_entries;
av_log(c->fc, AV_LOG_ERROR, "Failed to add index entry\n");
}
}
}
}
- for (i = 0; i < c->fc->nb_streams; i++) {
+ if (ref_st) for (i = 0; i < c->fc->nb_streams; i++) {
st = c->fc->streams[i];
sc = st->priv_data;
if (!sc->has_sidx) {
static int mov_read_smdm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
MOVStreamContext *sc;
- const int chroma_den = 50000;
- const int luma_den = 10000;
- int i, j, version;
+ int i, version;
if (c->fc->nb_streams < 1)
return AVERROR_INVALIDDATA;
if (!sc->mastering)
return AVERROR(ENOMEM);
- for (i = 0; i < 3; i++)
- for (j = 0; j < 2; j++)
- sc->mastering->display_primaries[i][j] =
- av_make_q(lrint(((double)avio_rb16(pb) / (1 << 16)) * chroma_den), chroma_den);
- for (i = 0; i < 2; i++)
- sc->mastering->white_point[i] =
- av_make_q(lrint(((double)avio_rb16(pb) / (1 << 16)) * chroma_den), chroma_den);
- sc->mastering->max_luminance =
- av_make_q(lrint(((double)avio_rb32(pb) / (1 << 8)) * luma_den), luma_den);
- sc->mastering->min_luminance =
- av_make_q(lrint(((double)avio_rb32(pb) / (1 << 14)) * luma_den), luma_den);
+ for (i = 0; i < 3; i++) {
+ sc->mastering->display_primaries[i][0] = av_make_q(avio_rb16(pb), 1 << 16);
+ sc->mastering->display_primaries[i][1] = av_make_q(avio_rb16(pb), 1 << 16);
+ }
+ sc->mastering->white_point[0] = av_make_q(avio_rb16(pb), 1 << 16);
+ sc->mastering->white_point[1] = av_make_q(avio_rb16(pb), 1 << 16);
+
+ sc->mastering->max_luminance = av_make_q(avio_rb32(pb), 1 << 8);
+ sc->mastering->min_luminance = av_make_q(avio_rb32(pb), 1 << 14);
sc->mastering->has_primaries = 1;
sc->mastering->has_luminance = 1;
return 0;
}
-static int cenc_filter(MOVContext *mov, MOVStreamContext *sc, AVPacket *pkt, int current_index)
+static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPacket *pkt, int current_index)
{
MOVFragmentStreamInfo *frag_stream_info;
MOVEncryptionIndex *encryption_index;
AVEncryptionInfo *encrypted_sample;
int encrypted_index, ret;
- frag_stream_info = get_current_frag_stream_info(&mov->frag_index);
+ frag_stream_info = get_frag_stream_info(&mov->frag_index, mov->frag_index.current, st->id);
encrypted_index = current_index;
encryption_index = NULL;
if (frag_stream_info) {
return 0;
}
-static int mov_probe(AVProbeData *p)
+static int mov_probe(const AVProbeData *p)
{
int64_t offset;
uint32_t tag;
if (mov->aax_mode)
aax_filter(pkt->data, pkt->size, mov);
- ret = cenc_filter(mov, sc, pkt, current_index);
+ ret = cenc_filter(mov, st, sc, pkt, current_index);
if (ret < 0)
return ret;