mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
mov: Export spherical information
This implements Spherical Video V1 and V2, as described in the spatial-media collection by Google. Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
This commit is contained in:
parent
e7a6f8c972
commit
4dcdecf471
@ -6,6 +6,7 @@ version <next>:
|
|||||||
- add internal ebur128 library, remove external libebur128 dependency
|
- add internal ebur128 library, remove external libebur128 dependency
|
||||||
- Pro-MPEG CoP #3-R2 FEC protocol
|
- Pro-MPEG CoP #3-R2 FEC protocol
|
||||||
- premultiply video filter
|
- premultiply video filter
|
||||||
|
- Support for spherical videos
|
||||||
|
|
||||||
version 3.2:
|
version 3.2:
|
||||||
- libopenmpt demuxer
|
- libopenmpt demuxer
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
#ifndef AVFORMAT_ISOM_H
|
#ifndef AVFORMAT_ISOM_H
|
||||||
#define AVFORMAT_ISOM_H
|
#define AVFORMAT_ISOM_H
|
||||||
|
|
||||||
|
#include "libavutil/spherical.h"
|
||||||
|
#include "libavutil/stereo3d.h"
|
||||||
|
|
||||||
#include "avio.h"
|
#include "avio.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "dv.h"
|
#include "dv.h"
|
||||||
@ -177,6 +180,10 @@ typedef struct MOVStreamContext {
|
|||||||
int stsd_count;
|
int stsd_count;
|
||||||
|
|
||||||
int32_t *display_matrix;
|
int32_t *display_matrix;
|
||||||
|
AVStereo3D *stereo3d;
|
||||||
|
AVSphericalMapping *spherical;
|
||||||
|
size_t spherical_size;
|
||||||
|
|
||||||
uint32_t format;
|
uint32_t format;
|
||||||
|
|
||||||
int has_sidx; // If there is an sidx entry for this stream.
|
int has_sidx; // If there is an sidx entry for this stream.
|
||||||
|
@ -42,6 +42,8 @@
|
|||||||
#include "libavutil/aes.h"
|
#include "libavutil/aes.h"
|
||||||
#include "libavutil/aes_ctr.h"
|
#include "libavutil/aes_ctr.h"
|
||||||
#include "libavutil/sha.h"
|
#include "libavutil/sha.h"
|
||||||
|
#include "libavutil/spherical.h"
|
||||||
|
#include "libavutil/stereo3d.h"
|
||||||
#include "libavutil/timecode.h"
|
#include "libavutil/timecode.h"
|
||||||
#include "libavcodec/ac3tab.h"
|
#include "libavcodec/ac3tab.h"
|
||||||
#include "libavcodec/flac.h"
|
#include "libavcodec/flac.h"
|
||||||
@ -4498,8 +4500,204 @@ static int mov_read_tmcd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
return 0;
|
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 > 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, avio_r8(pb)); /* 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)
|
static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
||||||
{
|
{
|
||||||
|
AVStream *st;
|
||||||
|
MOVStreamContext *sc;
|
||||||
int ret;
|
int ret;
|
||||||
uint8_t uuid[16];
|
uint8_t uuid[16];
|
||||||
static const uint8_t uuid_isml_manifest[] = {
|
static const uint8_t uuid_isml_manifest[] = {
|
||||||
@ -4510,10 +4708,19 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
0xbe, 0x7a, 0xcf, 0xcb, 0x97, 0xa9, 0x42, 0xe8,
|
0xbe, 0x7a, 0xcf, 0xcb, 0x97, 0xa9, 0x42, 0xe8,
|
||||||
0x9c, 0x71, 0x99, 0x94, 0x91, 0xe3, 0xaf, 0xac
|
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 == INT64_MAX)
|
||||||
return AVERROR_INVALIDDATA;
|
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));
|
ret = avio_read(pb, uuid, sizeof(uuid));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
@ -4585,7 +4792,14 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
|
|||||||
av_dict_set(&c->fc->metadata, "xmp", buffer, 0);
|
av_dict_set(&c->fc->metadata, "xmp", buffer, 0);
|
||||||
}
|
}
|
||||||
av_free(buffer);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4973,6 +5187,8 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
|
|||||||
{ MKTAG('s','e','n','c'), mov_read_senc },
|
{ MKTAG('s','e','n','c'), mov_read_senc },
|
||||||
{ MKTAG('s','a','i','z'), mov_read_saiz },
|
{ MKTAG('s','a','i','z'), mov_read_saiz },
|
||||||
{ MKTAG('d','f','L','a'), mov_read_dfla },
|
{ 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 }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -5393,6 +5609,9 @@ static int mov_read_close(AVFormatContext *s)
|
|||||||
av_freep(&sc->cenc.auxiliary_info);
|
av_freep(&sc->cenc.auxiliary_info);
|
||||||
av_freep(&sc->cenc.auxiliary_info_sizes);
|
av_freep(&sc->cenc.auxiliary_info_sizes);
|
||||||
av_aes_ctr_free(sc->cenc.aes_ctr);
|
av_aes_ctr_free(sc->cenc.aes_ctr);
|
||||||
|
|
||||||
|
av_freep(&sc->stereo3d);
|
||||||
|
av_freep(&sc->spherical);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mov->dv_demux) {
|
if (mov->dv_demux) {
|
||||||
@ -5711,6 +5930,24 @@ static int mov_read_header(AVFormatContext *s)
|
|||||||
|
|
||||||
sc->display_matrix = NULL;
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
sc->spherical = NULL;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
// Also please add any ticket numbers that you believe might be affected here
|
// Also please add any ticket numbers that you believe might be affected here
|
||||||
#define LIBAVFORMAT_VERSION_MAJOR 57
|
#define LIBAVFORMAT_VERSION_MAJOR 57
|
||||||
#define LIBAVFORMAT_VERSION_MINOR 58
|
#define LIBAVFORMAT_VERSION_MINOR 58
|
||||||
#define LIBAVFORMAT_VERSION_MICRO 101
|
#define LIBAVFORMAT_VERSION_MICRO 102
|
||||||
|
|
||||||
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
|
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
|
||||||
LIBAVFORMAT_VERSION_MINOR, \
|
LIBAVFORMAT_VERSION_MINOR, \
|
||||||
|
Loading…
Reference in New Issue
Block a user