X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmov.c;h=9ae4f8c09c9cd9c1a472bff5a832792d168d2e5c;hb=05018c2cdaf3cbf5769bdf90951f707cf99edd52;hp=17d63d66d049b02efd608e2fa12d03db0e917f2a;hpb=cd70ffaac8b74d88ed1460e10c7e9c02d067e3ca;p=ffmpeg diff --git a/libavformat/mov.c b/libavformat/mov.c index 17d63d66d04..9ae4f8c09c9 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" @@ -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--; @@ -1239,6 +1242,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 +1273,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 +2220,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 +2236,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; } @@ -2332,8 +2339,7 @@ static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; MOVStreamContext *sc; - int ret; - int entries; + int ret, entries; if (c->fc->nb_streams < 1) return 0; @@ -2342,7 +2348,7 @@ static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_r8(pb); /* version */ avio_rb24(pb); /* flags */ - entries = avio_rb32(pb); /* entries */ + entries = avio_rb32(pb); if (entries <= 0) { av_log(c->fc, AV_LOG_ERROR, "invalid STSD entries %d\n", entries); @@ -3848,12 +3854,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; @@ -3903,15 +3919,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); @@ -3921,7 +3942,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)) { @@ -3940,7 +3961,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) && @@ -4480,9 +4502,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, @@ -4492,10 +4710,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) 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; @@ -4549,25 +4776,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; } @@ -4743,6 +4981,43 @@ static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +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 cenc_filter(MOVContext *c, MOVStreamContext *sc, uint8_t *input, int size) { uint32_t encrypted_bytes; @@ -4917,6 +5192,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 } }; @@ -5337,6 +5615,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) { @@ -5610,8 +5891,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; + } } } @@ -5620,6 +5908,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; } @@ -5648,21 +5942,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; }