X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmov.c;h=2a7cbfe14283e1487d2f10e4e658e8065dc24e26;hb=a63496cc882428aefafc85d2f60e0908b020bffe;hp=8d6cc12baca02c6aa4302cdf5f578030431ce963;hpb=7e9474ca47fd4d267bdafcc8162fded67f6410e5;p=ffmpeg diff --git a/libavformat/mov.c b/libavformat/mov.c index 8d6cc12baca..2a7cbfe1428 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -42,8 +42,11 @@ #include "libavutil/aes.h" #include "libavutil/aes_ctr.h" #include "libavutil/sha.h" +#include "libavutil/spherical.h" +#include "libavutil/stereo3d.h" #include "libavutil/timecode.h" #include "libavcodec/ac3tab.h" +#include "libavcodec/flac.h" #include "libavcodec/mpegaudiodecheader.h" #include "avformat.h" #include "internal.h" @@ -404,11 +407,11 @@ retry: return ret; } else if (!key && c->found_hdlr_mdta && c->meta_keys) { uint32_t index = AV_RB32(&atom.type); - if (index < c->meta_keys_count) { + if (index < c->meta_keys_count && index > 0) { key = c->meta_keys[index]; } else { av_log(c->fc, AV_LOG_WARNING, - "The index of 'data' is out of range: %d >= %d.\n", + "The index of 'data' is out of range: %d < 1 or >= %d.\n", index, c->meta_keys_count); } } @@ -686,7 +689,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_skip(pb, len); } } else { - av_log(c->fc, AV_LOG_DEBUG, "Unknown dref type 0x08%x size %d\n", + av_log(c->fc, AV_LOG_DEBUG, "Unknown dref type 0x%08x size %d\n", dref->type, size); entries--; i--; @@ -739,6 +742,8 @@ static int mov_read_hdlr(MOVContext *c, AVIOContext *pb, MOVAtom atom) title_size = atom.size - 24; if (title_size > 0) { + if (title_size > FFMIN(INT_MAX, SIZE_MAX-1)) + return AVERROR_INVALIDDATA; title_str = av_malloc(title_size + 1); /* Add null terminator */ if (!title_str) return AVERROR(ENOMEM); @@ -1239,6 +1244,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) { + int i; int64_t creation_time; int version = avio_r8(pb); /* version */ avio_rb24(pb); /* flags */ @@ -1269,7 +1275,12 @@ static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_skip(pb, 10); /* reserved */ - avio_skip(pb, 36); /* display matrix */ + /* movie display matrix, store it in main context and use it later on */ + for (i = 0; i < 3; i++) { + c->movie_display_matrix[i][0] = avio_rb32(pb); // 16.16 fixed point + c->movie_display_matrix[i][1] = avio_rb32(pb); // 16.16 fixed point + c->movie_display_matrix[i][2] = avio_rb32(pb); // 2.30 fixed point + } avio_rb32(pb); /* preview time */ avio_rb32(pb); /* preview duration */ @@ -2211,6 +2222,8 @@ static int mov_skip_multiple_stsd(MOVContext *c, AVIOContext *pb, if (codec_tag && (codec_tag != format && + // AVID 1:1 samples with differing data format and codec tag exist + (codec_tag != AV_RL32("AV1x") || format != AV_RL32("AVup")) && // prores is allowed to have differing data format and codec tag codec_tag != AV_RL32("apcn") && codec_tag != AV_RL32("apch") && // so is dv (sigh) @@ -2225,10 +2238,6 @@ static int mov_skip_multiple_stsd(MOVContext *c, AVIOContext *pb, avio_skip(pb, size); return 1; } - 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; } @@ -2425,12 +2434,14 @@ static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +#define mov_stsc_index_valid(index, count) ((index) < (count) - 1) + /* 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) + if (mov_stsc_index_valid(index, sc->stsc_count)) 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); @@ -2836,11 +2847,23 @@ static int64_t find_prev_closest_index(AVStream *st, AVIndexEntry *e_keep = st->index_entries; int nb_keep = st->nb_index_entries; int64_t found = -1; + int64_t i = 0; st->index_entries = e_old; st->nb_index_entries = nb_old; found = av_index_search_timestamp(st, timestamp, flag | AVSEEK_FLAG_BACKWARD); + // Keep going backwards in the index entries until the timestamp is the same. + if (found >= 0) { + for (i = found; i > 0 && e_old[i].timestamp == e_old[i - 1].timestamp; + i--) { + if ((flag & AVSEEK_FLAG_ANY) || + (e_old[i - 1].flags & AVINDEX_KEYFRAME)) { + found = i - 1; + } + } + } + /* restore AVStream state*/ st->index_entries = e_keep; st->nb_index_entries = nb_keep; @@ -2940,6 +2963,52 @@ static int64_t add_ctts_entry(MOVStts** ctts_data, unsigned int* ctts_count, uns return *ctts_count; } +static void mov_current_sample_inc(MOVStreamContext *sc) +{ + sc->current_sample++; + sc->current_index++; + if (sc->index_ranges && + sc->current_index >= sc->current_index_range->end && + sc->current_index_range->end) { + sc->current_index_range++; + sc->current_index = sc->current_index_range->start; + } +} + +static void mov_current_sample_dec(MOVStreamContext *sc) +{ + sc->current_sample--; + sc->current_index--; + if (sc->index_ranges && + sc->current_index < sc->current_index_range->start && + sc->current_index_range > sc->index_ranges) { + sc->current_index_range--; + sc->current_index = sc->current_index_range->end - 1; + } +} + +static void mov_current_sample_set(MOVStreamContext *sc, int current_sample) +{ + int64_t range_size; + + sc->current_sample = current_sample; + sc->current_index = current_sample; + if (!sc->index_ranges) { + return; + } + + for (sc->current_index_range = sc->index_ranges; + sc->current_index_range->end; + sc->current_index_range++) { + range_size = sc->current_index_range->end - sc->current_index_range->start; + if (range_size > current_sample) { + sc->current_index = sc->current_index_range->start + current_sample; + break; + } + current_sample -= range_size; + } +} + /** * Fix st->index_entries, so that it contains only the entries (and the entries * which are needed to decode them) that fall in the edit list time ranges. @@ -2977,10 +3046,21 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) int num_discarded_begin = 0; int first_non_zero_audio_edit = -1; int packet_skip_samples = 0; + MOVIndexRange *current_index_range; if (!msc->elst_data || msc->elst_count <= 0 || nb_old <= 0) { return; } + + // allocate the index ranges array + msc->index_ranges = av_malloc((msc->elst_count + 1) * sizeof(msc->index_ranges[0])); + if (!msc->index_ranges) { + av_log(mov->fc, AV_LOG_ERROR, "Cannot allocate index ranges buffer\n"); + return; + } + msc->current_index_range = msc->index_ranges; + current_index_range = msc->index_ranges - 1; + // Clean AVStream from traces of old index st->index_entries = NULL; st->index_entries_allocated_size = 0; @@ -3175,6 +3255,13 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) break; } + // Update the index ranges array + if (current_index_range < msc->index_ranges || index != current_index_range->end) { + current_index_range++; + current_index_range->start = index; + } + current_index_range->end = index + 1; + // Only start incrementing DTS in frame_duration amounts, when we encounter a frame in edit list. if (edit_list_start_encountered > 0) { edit_list_dts_counter = edit_list_dts_counter + frame_duration; @@ -3205,6 +3292,12 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) // Free the old index and the old CTTS structures av_free(e_old); av_free(ctts_data_old); + + // Null terminate the index ranges array + current_index_range++; + current_index_range->start = 0; + current_index_range->end = 0; + msc->current_index = msc->index_ranges[0].start; } static void mov_build_index(MOVContext *mov, AVStream *st) @@ -3276,7 +3369,7 @@ static void mov_build_index(MOVContext *mov, AVStream *st) for (i = 0; i < sc->chunk_count; i++) { int64_t next_offset = i+1 < sc->chunk_count ? sc->chunk_offsets[i+1] : INT64_MAX; current_offset = sc->chunk_offsets[i]; - while (stsc_index + 1 < sc->stsc_count && + while (mov_stsc_index_valid(stsc_index, sc->stsc_count) && i + 1 == sc->stsc_data[stsc_index + 1].first) stsc_index++; @@ -3399,7 +3492,7 @@ static void mov_build_index(MOVContext *mov, AVStream *st) count = (chunk_samples+1023) / 1024; } - if (i < sc->stsc_count - 1) + if (mov_stsc_index_valid(i, sc->stsc_count)) chunk_count = sc->stsc_data[i+1].first - sc->stsc_data[i].first; else chunk_count = sc->chunk_count - (sc->stsc_data[i].first - 1); @@ -3420,7 +3513,7 @@ static void mov_build_index(MOVContext *mov, AVStream *st) // populate index for (i = 0; i < sc->chunk_count; i++) { current_offset = sc->chunk_offsets[i]; - if (stsc_index + 1 < sc->stsc_count && + if (mov_stsc_index_valid(stsc_index, sc->stsc_count) && i + 1 == sc->stsc_data[stsc_index + 1].first) stsc_index++; chunk_samples = sc->stsc_data[stsc_index].count; @@ -3847,12 +3940,22 @@ static int mov_read_meta(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +// return 1 when matrix is identity, 0 otherwise +#define IS_MATRIX_IDENT(matrix) \ + ( (matrix)[0][0] == (1 << 16) && \ + (matrix)[1][1] == (1 << 16) && \ + (matrix)[2][2] == (1 << 30) && \ + !(matrix)[0][1] && !(matrix)[0][2] && \ + !(matrix)[1][0] && !(matrix)[1][2] && \ + !(matrix)[2][0] && !(matrix)[2][1]) + static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) { - int i; + int i, j, e; int width; int height; int display_matrix[3][3]; + int res_display_matrix[3][3] = { { 0 } }; AVStream *st; MOVStreamContext *sc; int version; @@ -3902,15 +4005,20 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->width = width >> 16; sc->height = height >> 16; - // save the matrix and add rotate metadata when it is not the default - // identity - if (display_matrix[0][0] != (1 << 16) || - display_matrix[1][1] != (1 << 16) || - display_matrix[2][2] != (1 << 30) || - display_matrix[0][1] || display_matrix[0][2] || - display_matrix[1][0] || display_matrix[1][2] || - display_matrix[2][0] || display_matrix[2][1]) { - int i, j; + // apply the moov display matrix (after the tkhd one) + for (i = 0; i < 3; i++) { + const int sh[3] = { 16, 16, 30 }; + for (j = 0; j < 3; j++) { + for (e = 0; e < 3; e++) { + res_display_matrix[i][j] += + ((int64_t) display_matrix[i][e] * + c->movie_display_matrix[e][j]) >> sh[e]; + } + } + } + + // save the matrix when it is not the default identity + if (!IS_MATRIX_IDENT(res_display_matrix)) { double rotate; av_freep(&sc->display_matrix); @@ -3920,7 +4028,7 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) - sc->display_matrix[i * 3 + j] = display_matrix[i][j]; + sc->display_matrix[i * 3 + j] = res_display_matrix[i][j]; rotate = av_display_rotation_get(sc->display_matrix); if (!isnan(rotate)) { @@ -3939,7 +4047,8 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) double disp_transform[2]; for (i = 0; i < 2; i++) - disp_transform[i] = hypot(display_matrix[i][0], display_matrix[i][1]); + disp_transform[i] = hypot(sc->display_matrix[0 + i], + sc->display_matrix[3 + i]); if (disp_transform[0] > 0 && disp_transform[1] > 0 && disp_transform[0] < (1<<24) && disp_transform[1] < (1<<24) && @@ -4479,9 +4588,205 @@ static int mov_read_tmcd(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_st3d(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + enum AVStereo3DType type; + int mode; + + if (c->fc->nb_streams < 1) + return 0; + + st = c->fc->streams[c->fc->nb_streams - 1]; + sc = st->priv_data; + + if (atom.size < 5) { + av_log(c->fc, AV_LOG_ERROR, "Empty stereoscopic video box\n"); + return AVERROR_INVALIDDATA; + } + avio_skip(pb, 4); /* version + flags */ + + mode = avio_r8(pb); + switch (mode) { + case 0: + type = AV_STEREO3D_2D; + break; + case 1: + type = AV_STEREO3D_TOPBOTTOM; + break; + case 2: + type = AV_STEREO3D_SIDEBYSIDE; + break; + default: + av_log(c->fc, AV_LOG_WARNING, "Unknown st3d mode value %d\n", mode); + return 0; + } + + sc->stereo3d = av_stereo3d_alloc(); + if (!sc->stereo3d) + return AVERROR(ENOMEM); + + sc->stereo3d->type = type; + return 0; +} + +static int mov_read_sv3d(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + int size; + int32_t yaw, pitch, roll; + uint32_t tag; + enum AVSphericalProjection projection; + + if (c->fc->nb_streams < 1) + return 0; + + st = c->fc->streams[c->fc->nb_streams - 1]; + sc = st->priv_data; + + if (atom.size < 8) { + av_log(c->fc, AV_LOG_ERROR, "Empty spherical video box\n"); + return AVERROR_INVALIDDATA; + } + + size = avio_rb32(pb); + if (size <= 12 || size > atom.size) + return AVERROR_INVALIDDATA; + + tag = avio_rl32(pb); + if (tag != MKTAG('s','v','h','d')) { + av_log(c->fc, AV_LOG_ERROR, "Missing spherical video header\n"); + return 0; + } + avio_skip(pb, 4); /* version + flags */ + avio_skip(pb, size - 12); /* metadata_source */ + + size = avio_rb32(pb); + if (size > atom.size) + return AVERROR_INVALIDDATA; + + tag = avio_rl32(pb); + if (tag != MKTAG('p','r','o','j')) { + av_log(c->fc, AV_LOG_ERROR, "Missing projection box\n"); + return 0; + } + + size = avio_rb32(pb); + if (size > atom.size) + return AVERROR_INVALIDDATA; + + tag = avio_rl32(pb); + if (tag != MKTAG('p','r','h','d')) { + av_log(c->fc, AV_LOG_ERROR, "Missing projection header box\n"); + return 0; + } + avio_skip(pb, 4); /* version + flags */ + + /* 16.16 fixed point */ + yaw = avio_rb32(pb); + pitch = avio_rb32(pb); + roll = avio_rb32(pb); + + size = avio_rb32(pb); + if (size > atom.size) + return AVERROR_INVALIDDATA; + + tag = avio_rl32(pb); + avio_skip(pb, 4); /* version + flags */ + switch (tag) { + case MKTAG('c','b','m','p'): + projection = AV_SPHERICAL_CUBEMAP; + break; + case MKTAG('e','q','u','i'): + projection = AV_SPHERICAL_EQUIRECTANGULAR; + break; + default: + av_log(c->fc, AV_LOG_ERROR, "Unknown projection type\n"); + return 0; + } + + sc->spherical = av_spherical_alloc(&sc->spherical_size); + if (!sc->spherical) + return AVERROR(ENOMEM); + + sc->spherical->projection = projection; + + sc->spherical->yaw = yaw; + sc->spherical->pitch = pitch; + sc->spherical->roll = roll; + + return 0; +} + +static int mov_parse_uuid_spherical(MOVStreamContext *sc, AVIOContext *pb, size_t len) +{ + int ret = 0; + uint8_t *buffer = av_malloc(len + 1); + const char *val; + + if (!buffer) + return AVERROR(ENOMEM); + buffer[len] = '\0'; + + ret = ffio_read_size(pb, buffer, len); + if (ret < 0) + goto out; + + /* Check for mandatory keys and values, try to support XML as best-effort */ + if (av_stristr(buffer, "") && + (val = av_stristr(buffer, "")) && + av_stristr(val, "true") && + (val = av_stristr(buffer, "")) && + av_stristr(val, "true") && + (val = av_stristr(buffer, "")) && + av_stristr(val, "equirectangular")) { + sc->spherical = av_spherical_alloc(&sc->spherical_size); + if (!sc->spherical) + goto out; + + sc->spherical->projection = AV_SPHERICAL_EQUIRECTANGULAR; + + if (av_stristr(buffer, "")) { + enum AVStereo3DType mode; + + if (av_stristr(buffer, "left-right")) + mode = AV_STEREO3D_SIDEBYSIDE; + else if (av_stristr(buffer, "top-bottom")) + mode = AV_STEREO3D_TOPBOTTOM; + else + mode = AV_STEREO3D_2D; + + sc->stereo3d = av_stereo3d_alloc(); + if (!sc->stereo3d) + goto out; + + sc->stereo3d->type = mode; + } + + /* orientation */ + val = av_stristr(buffer, ""); + if (val) + sc->spherical->yaw = strtol(val, NULL, 10) * (1 << 16); + val = av_stristr(buffer, ""); + if (val) + sc->spherical->pitch = strtol(val, NULL, 10) * (1 << 16); + val = av_stristr(buffer, ""); + if (val) + sc->spherical->roll = strtol(val, NULL, 10) * (1 << 16); + } + +out: + av_free(buffer); + return ret; +} + static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom) { - int ret; + AVStream *st; + MOVStreamContext *sc; + int64_t ret; uint8_t uuid[16]; static const uint8_t uuid_isml_manifest[] = { 0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd, @@ -4491,10 +4796,19 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom) 0xbe, 0x7a, 0xcf, 0xcb, 0x97, 0xa9, 0x42, 0xe8, 0x9c, 0x71, 0x99, 0x94, 0x91, 0xe3, 0xaf, 0xac }; + static const uint8_t uuid_spherical[] = { + 0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93, + 0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd, + }; - if (atom.size < sizeof(uuid) || atom.size == INT64_MAX) + if (atom.size < sizeof(uuid) || atom.size >= FFMIN(INT_MAX, SIZE_MAX)) return AVERROR_INVALIDDATA; + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams - 1]; + sc = st->priv_data; + ret = avio_read(pb, uuid, sizeof(uuid)); if (ret < 0) { return ret; @@ -4548,25 +4862,36 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom) } else if (!memcmp(uuid, uuid_xmp, sizeof(uuid))) { uint8_t *buffer; size_t len = atom.size - sizeof(uuid); - - buffer = av_mallocz(len + 1); - if (!buffer) { - return AVERROR(ENOMEM); - } - ret = avio_read(pb, buffer, len); - if (ret < 0) { - av_free(buffer); - return ret; - } else if (ret != len) { - av_free(buffer); - return AVERROR_INVALIDDATA; - } if (c->export_xmp) { + buffer = av_mallocz(len + 1); + if (!buffer) { + return AVERROR(ENOMEM); + } + ret = avio_read(pb, buffer, len); + if (ret < 0) { + av_free(buffer); + return ret; + } else if (ret != len) { + av_free(buffer); + return AVERROR_INVALIDDATA; + } buffer[len] = '\0'; av_dict_set(&c->fc->metadata, "xmp", buffer, 0); + av_free(buffer); + } else { + // skip all uuid atom, which makes it fast for long uuid-xmp file + ret = avio_skip(pb, len); + if (ret < 0) + return ret; } - av_free(buffer); - } + } else if (!memcmp(uuid, uuid_spherical, sizeof(uuid))) { + size_t len = atom.size - sizeof(uuid); + ret = mov_parse_uuid_spherical(sc, pb, len); + if (ret < 0) + return ret; + if (!sc->spherical) + av_log(c->fc, AV_LOG_WARNING, "Invalid spherical metadata found\n"); } + return 0; } @@ -4655,8 +4980,8 @@ static int mov_read_senc(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb32(pb); /* entries */ - if (atom.size < 8) { - av_log(c->fc, AV_LOG_ERROR, "senc atom size %"PRId64" too small\n", atom.size); + if (atom.size < 8 || atom.size > FFMIN(INT_MAX, SIZE_MAX)) { + av_log(c->fc, AV_LOG_ERROR, "senc atom size %"PRId64" invalid\n", atom.size); return AVERROR_INVALIDDATA; } @@ -4669,8 +4994,8 @@ static int mov_read_senc(MOVContext *c, AVIOContext *pb, MOVAtom atom) } sc->cenc.auxiliary_info_end = sc->cenc.auxiliary_info + auxiliary_info_size; - sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info; + sc->cenc.auxiliary_info_index = 0; if (avio_read(pb, sc->cenc.auxiliary_info, auxiliary_info_size) != auxiliary_info_size) { av_log(c->fc, AV_LOG_ERROR, "failed to read the auxiliary info"); @@ -4724,6 +5049,11 @@ static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } + if (atom.size > FFMIN(INT_MAX, SIZE_MAX)) { + av_log(c->fc, AV_LOG_ERROR, "saiz atom auxiliary_info_sizes size %"PRId64" invalid\n", atom.size); + return AVERROR_INVALIDDATA; + } + /* save the auxiliary info sizes as is */ data_size = atom.size - atom_header_size; @@ -4742,12 +5072,87 @@ static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } -static int cenc_filter(MOVContext *c, MOVStreamContext *sc, uint8_t *input, int size) +static int mov_read_dfla(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + int last, type, size, ret; + uint8_t buf[4]; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if ((uint64_t)atom.size > (1<<30) || atom.size < 42) + return AVERROR_INVALIDDATA; + + /* Check FlacSpecificBox version. */ + if (avio_r8(pb) != 0) + return AVERROR_INVALIDDATA; + + avio_rb24(pb); /* Flags */ + + avio_read(pb, buf, sizeof(buf)); + flac_parse_block_header(buf, &last, &type, &size); + + if (type != FLAC_METADATA_TYPE_STREAMINFO || size != FLAC_STREAMINFO_SIZE) { + av_log(c->fc, AV_LOG_ERROR, "STREAMINFO must be first FLACMetadataBlock\n"); + return AVERROR_INVALIDDATA; + } + + ret = ff_get_extradata(c->fc, st->codecpar, pb, size); + if (ret < 0) + return ret; + + if (!last) + av_log(c->fc, AV_LOG_WARNING, "non-STREAMINFO FLACMetadataBlock(s) ignored\n"); + + return 0; +} + +static int mov_seek_auxiliary_info(MOVContext *c, MOVStreamContext *sc, int64_t index) +{ + size_t auxiliary_info_seek_offset = 0; + int i; + + if (sc->cenc.auxiliary_info_default_size) { + auxiliary_info_seek_offset = (size_t)sc->cenc.auxiliary_info_default_size * index; + } else if (sc->cenc.auxiliary_info_sizes) { + if (index > sc->cenc.auxiliary_info_sizes_count) { + av_log(c, AV_LOG_ERROR, "current sample %"PRId64" greater than the number of auxiliary info sample sizes %"SIZE_SPECIFIER"\n", + index, sc->cenc.auxiliary_info_sizes_count); + return AVERROR_INVALIDDATA; + } + + for (i = 0; i < index; i++) { + auxiliary_info_seek_offset += sc->cenc.auxiliary_info_sizes[i]; + } + } + + if (auxiliary_info_seek_offset > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info) { + av_log(c, AV_LOG_ERROR, "auxiliary info offset %"SIZE_SPECIFIER" greater than auxiliary info size %"SIZE_SPECIFIER"\n", + auxiliary_info_seek_offset, (size_t)(sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info)); + return AVERROR_INVALIDDATA; + } + + sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info + auxiliary_info_seek_offset; + sc->cenc.auxiliary_info_index = index; + return 0; +} + +static int cenc_filter(MOVContext *c, MOVStreamContext *sc, int64_t index, uint8_t *input, int size) { uint32_t encrypted_bytes; uint16_t subsample_count; uint16_t clear_bytes; uint8_t* input_end = input + size; + int ret; + + if (index != sc->cenc.auxiliary_info_index) { + ret = mov_seek_auxiliary_info(c, sc, index); + if (ret < 0) { + return ret; + } + } /* read the iv */ if (AES_CTR_IV_SIZE > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) { @@ -4805,36 +5210,7 @@ static int cenc_filter(MOVContext *c, MOVStreamContext *sc, uint8_t *input, int return AVERROR_INVALIDDATA; } - return 0; -} - -static int mov_seek_auxiliary_info(AVFormatContext *s, MOVStreamContext *sc) -{ - size_t auxiliary_info_seek_offset = 0; - int i; - - if (sc->cenc.auxiliary_info_default_size) { - auxiliary_info_seek_offset = (size_t)sc->cenc.auxiliary_info_default_size * sc->current_sample; - } else if (sc->cenc.auxiliary_info_sizes) { - if (sc->current_sample > sc->cenc.auxiliary_info_sizes_count) { - av_log(s, AV_LOG_ERROR, "current sample %d greater than the number of auxiliary info sample sizes %"SIZE_SPECIFIER"\n", - sc->current_sample, sc->cenc.auxiliary_info_sizes_count); - return AVERROR_INVALIDDATA; - } - - for (i = 0; i < sc->current_sample; i++) { - auxiliary_info_seek_offset += sc->cenc.auxiliary_info_sizes[i]; - } - } - - if (auxiliary_info_seek_offset > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info) { - av_log(s, AV_LOG_ERROR, "auxiliary info offset %"SIZE_SPECIFIER" greater than auxiliary info size %"SIZE_SPECIFIER"\n", - auxiliary_info_seek_offset, (size_t)(sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info)); - return AVERROR_INVALIDDATA; - } - - sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info + auxiliary_info_seek_offset; - + sc->cenc.auxiliary_info_index++; return 0; } @@ -4916,6 +5292,9 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('f','r','m','a'), mov_read_frma }, { MKTAG('s','e','n','c'), mov_read_senc }, { MKTAG('s','a','i','z'), mov_read_saiz }, +{ MKTAG('d','f','L','a'), mov_read_dfla }, +{ MKTAG('s','t','3','d'), mov_read_st3d }, /* stereoscopic 3D video box */ +{ MKTAG('s','v','3','d'), mov_read_sv3d }, /* spherical video box */ { 0, NULL } }; @@ -5326,6 +5705,7 @@ static int mov_read_close(AVFormatContext *s) av_freep(&sc->elst_data); av_freep(&sc->rap_group); av_freep(&sc->display_matrix); + av_freep(&sc->index_ranges); if (sc->extradata) for (j = 0; j < sc->stsd_count; j++) @@ -5336,6 +5716,9 @@ static int mov_read_close(AVFormatContext *s) av_freep(&sc->cenc.auxiliary_info); av_freep(&sc->cenc.auxiliary_info_sizes); av_aes_ctr_free(sc->cenc.aes_ctr); + + av_freep(&sc->stereo3d); + av_freep(&sc->spherical); } if (mov->dv_demux) { @@ -5609,8 +5992,15 @@ static int mov_read_header(AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; MOVStreamContext *sc = st->priv_data; - if (st->duration > 0) + if (st->duration > 0) { + if (sc->data_size > INT64_MAX / sc->time_scale / 8) { + av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %"PRId64" * 8 * %d\n", + sc->data_size, sc->time_scale); + mov_read_close(s); + return AVERROR_INVALIDDATA; + } st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale / st->duration; + } } } @@ -5619,6 +6009,12 @@ static int mov_read_header(AVFormatContext *s) AVStream *st = s->streams[i]; MOVStreamContext *sc = st->priv_data; if (sc->duration_for_fps > 0) { + if (sc->data_size > INT64_MAX / sc->time_scale / 8) { + av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %"PRId64" * 8 * %d\n", + sc->data_size, sc->time_scale); + mov_read_close(s); + return AVERROR_INVALIDDATA; + } st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale / sc->duration_for_fps; } @@ -5647,21 +6043,30 @@ static int mov_read_header(AVFormatContext *s) break; case AVMEDIA_TYPE_VIDEO: if (sc->display_matrix) { - AVPacketSideData *sd, *tmp; + err = av_stream_add_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, (uint8_t*)sc->display_matrix, + sizeof(int32_t) * 9); + if (err < 0) + return err; - tmp = av_realloc_array(st->side_data, - st->nb_side_data + 1, sizeof(*tmp)); - if (!tmp) - return AVERROR(ENOMEM); + sc->display_matrix = NULL; + } + if (sc->stereo3d) { + err = av_stream_add_side_data(st, AV_PKT_DATA_STEREO3D, + (uint8_t *)sc->stereo3d, + sizeof(*sc->stereo3d)); + if (err < 0) + return err; - st->side_data = tmp; - st->nb_side_data++; + sc->stereo3d = NULL; + } + if (sc->spherical) { + err = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL, + (uint8_t *)sc->spherical, + sc->spherical_size); + if (err < 0) + return err; - sd = &st->side_data[st->nb_side_data - 1]; - sd->type = AV_PKT_DATA_DISPLAYMATRIX; - sd->size = sizeof(int32_t) * 9; - sd->data = (uint8_t*)sc->display_matrix; - sc->display_matrix = NULL; + sc->spherical = NULL; } break; } @@ -5779,6 +6184,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) MOVStreamContext *sc; AVIndexEntry *sample; AVStream *st = NULL; + int64_t current_index; int ret; mov->fc = s; retry: @@ -5792,7 +6198,8 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) } sc = st->priv_data; /* must be done just before reading, to avoid infinite loop on sample */ - sc->current_sample++; + current_index = sc->current_index; + mov_current_sample_inc(sc); if (mov->next_root_atom) { sample->pos = FFMIN(sample->pos, mov->next_root_atom); @@ -5804,7 +6211,9 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) if (ret64 != sample->pos) { av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n", sc->ffindex, sample->pos); - sc->current_sample -= should_retry(sc->pb, ret64); + if (should_retry(sc->pb, ret64)) { + mov_current_sample_dec(sc); + } return AVERROR_INVALIDDATA; } @@ -5815,7 +6224,9 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) ret = av_get_packet(sc->pb, pkt, sample->size); if (ret < 0) { - sc->current_sample -= should_retry(sc->pb, ret); + if (should_retry(sc->pb, ret)) { + mov_current_sample_dec(sc); + } return ret; } if (sc->has_palette) { @@ -5875,7 +6286,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) /* 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 && + if (mov_stsc_index_valid(sc->stsc_index, sc->stsc_count) && mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) { sc->stsc_index++; sc->stsc_sample = 0; @@ -5893,7 +6304,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) aax_filter(pkt->data, pkt->size, mov); if (sc->cenc.aes_ctr) { - ret = cenc_filter(mov, sc, pkt->data, pkt->size); + ret = cenc_filter(mov, sc, current_index, pkt->data, pkt->size); if (ret) { return ret; } @@ -5944,7 +6355,7 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, sample = 0; if (sample < 0) /* not sure what to do */ return AVERROR_INVALIDDATA; - sc->current_sample = sample; + mov_current_sample_set(sc, sample); av_log(s, AV_LOG_TRACE, "stream %d, found sample %d\n", st->index, sc->current_sample); /* adjust ctts index */ if (sc->ctts_data) { @@ -5972,11 +6383,6 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, time_sample = next; } - ret = mov_seek_auxiliary_info(s, sc); - if (ret < 0) { - return ret; - } - return sample; } @@ -6016,7 +6422,7 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti MOVStreamContext *sc; st = s->streams[i]; sc = st->priv_data; - sc->current_sample = 0; + mov_current_sample_set(sc, 0); } while (1) { MOVStreamContext *sc; @@ -6026,7 +6432,7 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti sc = st->priv_data; if (sc->ffindex == stream_index && sc->current_sample == sample) break; - sc->current_sample++; + mov_current_sample_inc(sc); } } return 0;