Commit e90137c0 authored by Vittorio Giovara's avatar Vittorio Giovara

mov: Export spherical information

This implements Spherical Video V1 and V2, as described in the
spatial-media collection by Google.
Signed-off-by: 's avatarVittorio Giovara <vittorio.giovara@gmail.com>
parent 2fb6acd9
...@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release, ...@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest. releases are sorted from youngest to oldest.
version <next>: version <next>:
- Support for spherical videos
version 12: version 12:
......
...@@ -24,6 +24,9 @@ ...@@ -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"
...@@ -145,6 +148,9 @@ typedef struct MOVStreamContext { ...@@ -145,6 +148,9 @@ 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;
} MOVStreamContext; } MOVStreamContext;
typedef struct MOVContext { typedef struct MOVContext {
......
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#include "libavutil/dict.h" #include "libavutil/dict.h"
#include "libavutil/opt.h" #include "libavutil/opt.h"
#include "libavutil/pixdesc.h" #include "libavutil/pixdesc.h"
#include "libavutil/spherical.h"
#include "libavutil/stereo3d.h"
#include "libavcodec/ac3tab.h" #include "libavcodec/ac3tab.h"
#include "avformat.h" #include "avformat.h"
#include "internal.h" #include "internal.h"
...@@ -3174,6 +3176,242 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) ...@@ -3174,6 +3176,242 @@ static int mov_read_elst(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)
{
AVStream *st;
MOVStreamContext *sc;
int ret;
uint8_t uuid[16];
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 = ffio_read_size(pb, uuid, sizeof(uuid));
if (ret < 0)
return ret;
if (!memcmp(uuid, uuid_spherical, sizeof(uuid)) && !sc->spherical) {
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");
} else {
int i;
av_log(c->fc, AV_LOG_VERBOSE, "Unknown UUID found: 0x");
for (i = 0; i < sizeof(uuid); i++)
av_log(c->fc, AV_LOG_WARNING, "%02x", uuid[i]);
av_log(c->fc, AV_LOG_WARNING, "\n");
}
return 0;
}
static const MOVParseTableEntry mov_default_parse_table[] = { static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('a','v','s','s'), mov_read_extradata }, { MKTAG('a','v','s','s'), mov_read_extradata },
{ MKTAG('c','h','p','l'), mov_read_chpl }, { MKTAG('c','h','p','l'), mov_read_chpl },
...@@ -3235,6 +3473,9 @@ static const MOVParseTableEntry mov_default_parse_table[] = { ...@@ -3235,6 +3473,9 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('d','v','c','1'), mov_read_dvc1 }, { MKTAG('d','v','c','1'), mov_read_dvc1 },
{ MKTAG('s','b','g','p'), mov_read_sbgp }, { MKTAG('s','b','g','p'), mov_read_sbgp },
{ MKTAG('h','v','c','C'), mov_read_glbl }, { MKTAG('h','v','c','C'), mov_read_glbl },
{ MKTAG('s','t','3','d'), mov_read_st3d }, /* stereoscopic 3D video box */
{ MKTAG('s','v','3','d'), mov_read_sv3d }, /* spherical video box */
{ MKTAG('u','u','i','d'), mov_read_uuid }, /* universal unique identifier */
{ MKTAG('-','-','-','-'), mov_read_custom }, { MKTAG('-','-','-','-'), mov_read_custom },
{ 0, NULL } { 0, NULL }
}; };
...@@ -3463,6 +3704,9 @@ static int mov_read_close(AVFormatContext *s) ...@@ -3463,6 +3704,9 @@ static int mov_read_close(AVFormatContext *s)
av_free(sc->extradata[j]); av_free(sc->extradata[j]);
av_freep(&sc->extradata); av_freep(&sc->extradata);
av_freep(&sc->extradata_size); av_freep(&sc->extradata_size);
av_freep(&sc->stereo3d);
av_freep(&sc->spherical);
} }
if (mov->dv_demux) { if (mov->dv_demux) {
...@@ -3553,6 +3797,24 @@ static int mov_read_header(AVFormatContext *s) ...@@ -3553,6 +3797,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;
} }
} }
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#define LIBAVFORMAT_VERSION_MAJOR 57 #define LIBAVFORMAT_VERSION_MAJOR 57
#define LIBAVFORMAT_VERSION_MINOR 10 #define LIBAVFORMAT_VERSION_MINOR 10
#define LIBAVFORMAT_VERSION_MICRO 0 #define LIBAVFORMAT_VERSION_MICRO 1
#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, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment