#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"
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);
}
}
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--;
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);
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 */
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 */
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)
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;
}
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);
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;
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.
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;
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;
// 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)
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++;
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);
// 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;
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;
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);
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)) {
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) &&
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, "<GSpherical:StitchingSoftware>") &&
+ (val = av_stristr(buffer, "<GSpherical:Spherical>")) &&
+ av_stristr(val, "true") &&
+ (val = av_stristr(buffer, "<GSpherical:Stitched>")) &&
+ av_stristr(val, "true") &&
+ (val = av_stristr(buffer, "<GSpherical:ProjectionType>")) &&
+ 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, "<GSpherical:StereoMode>")) {
+ 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, "<GSpherical:InitialViewHeadingDegrees>");
+ if (val)
+ sc->spherical->yaw = strtol(val, NULL, 10) * (1 << 16);
+ val = av_stristr(buffer, "<GSpherical:InitialViewPitchDegrees>");
+ if (val)
+ sc->spherical->pitch = strtol(val, NULL, 10) * (1 << 16);
+ val = av_stristr(buffer, "<GSpherical:InitialViewRollDegrees>");
+ 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,
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;
} 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;
}
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;
}
}
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");
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;
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) {
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;
}
{ 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 }
};
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++)
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) {
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;
+ }
}
}
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;
}
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;
}
MOVStreamContext *sc;
AVIndexEntry *sample;
AVStream *st = NULL;
+ int64_t current_index;
int ret;
mov->fc = s;
retry:
}
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);
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;
}
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) {
/* 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;
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;
}
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) {
time_sample = next;
}
- ret = mov_seek_auxiliary_info(s, sc);
- if (ret < 0) {
- return ret;
- }
-
return sample;
}
MOVStreamContext *sc;
st = s->streams[i];
sc = st->priv_data;
- sc->current_sample = 0;
+ mov_current_sample_set(sc, 0);
}
while (1) {
MOVStreamContext *sc;
sc = st->priv_data;
if (sc->ffindex == stream_index && sc->current_sample == sample)
break;
- sc->current_sample++;
+ mov_current_sample_inc(sc);
}
}
return 0;