summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-08-18 17:54:23 +0200
committerwm4 <wm4@nowhere>2017-08-18 17:54:23 +0200
commitfc5ad325a76d317a203e4e5d3ed311c09049cd20 (patch)
tree2a33cf0a1c736a711217e05bbc97c917891cdc79
parent1f7fe1597db4d6d984fe8f9235223a404b4280f1 (diff)
downloadmpv-fc5ad325a76d317a203e4e5d3ed311c09049cd20.tar.bz2
mpv-fc5ad325a76d317a203e4e5d3ed311c09049cd20.tar.xz
video: add metadata handling for spherical video
This adds handling of spherical video metadata: retrieving it from demux_lavf and demux_mkv, passing it through filters, and adjusting it with vf_format. This does not include support for rendering this type of video. We don't expect we need/want to support the other projection types like cube maps, so we don't include that for now.
-rw-r--r--DOCS/man/vf.rst10
-rwxr-xr-xTOOLS/matroska.py7
-rw-r--r--demux/demux_lavf.c17
-rw-r--r--demux/demux_mkv.c47
-rw-r--r--demux/stheader.h3
-rw-r--r--video/decode/dec_video.c4
-rw-r--r--video/filter/vf.c2
-rw-r--r--video/filter/vf_format.c14
-rw-r--r--video/mp_image.c28
-rw-r--r--video/mp_image.h19
-rw-r--r--wscript6
11 files changed, 153 insertions, 4 deletions
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index 9ed8f2f047..4b851f457b 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -414,6 +414,16 @@ Available mpv-only filters are:
but values such as ``[16:9]`` can be passed too (``[...]`` for quoting
to prevent the option parser from interpreting the ``:`` character).
+ ``<spherical-type>``
+ Type of the spherical projection:
+
+ :auto: As indicated by the file (default)
+ :none: Normal video
+ :equi: Equirectangular
+ :unknown: Unknown projection
+
+ ``<spherical-yaw>``, ``<spherical-pitch>``, ``<spherical-roll>``
+ Reference angle in degree, if spherical video is used.
``noformat[=fmt]``
Restricts the color space for the next filter without doing any conversion.
diff --git a/TOOLS/matroska.py b/TOOLS/matroska.py
index 2b59d2d8c0..bf9a0f2eef 100755
--- a/TOOLS/matroska.py
+++ b/TOOLS/matroska.py
@@ -137,6 +137,13 @@ elements_matroska = (
'LuminanceMin, 55DA, float',
),
),
+ 'Projection, 7670, sub', (
+ 'ProjectionType, 7671, uint',
+ 'ProjectionPrivate, 7672, binary',
+ 'ProjectionPoseYaw, 7673, float',
+ 'ProjectionPosePitch, 7674, float',
+ 'ProjectionPoseRoll, 7675, float',
+ ),
),
'Audio, e1, sub', (
'SamplingFrequency, b5, float',
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index f890b5d693..e61529fb71 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -38,6 +38,10 @@
#include <libavutil/display.h>
#include <libavutil/opt.h>
+#if HAVE_AVUTIL_SPHERICAL
+#include <libavutil/spherical.h>
+#endif
+
#include "common/msg.h"
#include "common/tags.h"
#include "common/av_common.h"
@@ -641,6 +645,19 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
sh->codec->rotate = (((int)(-r) % 360) + 360) % 360;
}
+#if HAVE_AVUTIL_SPHERICAL
+ sd = av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL, NULL);
+ if (sd) {
+ AVSphericalMapping *sp = (void *)sd;
+ struct mp_spherical_params *mpsp = &sh->codec->spherical;
+ mpsp->type = sp->projection == AV_SPHERICAL_EQUIRECTANGULAR ?
+ MP_SPHERICAL_EQUIRECTANGULAR : MP_SPHERICAL_UNKNOWN;
+ mpsp->ref_angles[0] = sp->yaw / (float)(1 << 16);
+ mpsp->ref_angles[1] = sp->pitch / (float)(1 << 16);
+ mpsp->ref_angles[2] = sp->roll / (float)(1 << 16);
+ }
+#endif
+
// This also applies to vfw-muxed mkv, but we can't detect these easily.
sh->codec->avi_dts = matches_avinputformat_name(priv, "avi");
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index 9f670e2aa5..4b9551adae 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -109,6 +109,7 @@ typedef struct mkv_track {
uint32_t colorspace;
int stereo_mode;
struct mp_colorspace color;
+ struct mp_spherical_params spherical;
uint32_t a_channels, a_bps;
float a_sfreq;
@@ -584,6 +585,49 @@ static void parse_trackcolour(struct demuxer *demuxer, struct mkv_track *track,
}
}
+static void parse_trackprojection(struct demuxer *demuxer, struct mkv_track *track,
+ struct ebml_projection *projection)
+{
+ if (projection->n_projection_type) {
+ const char *name;
+ switch (projection->projection_type) {
+ case 0:
+ name = "rectangular";
+ track->spherical.type = MP_SPHERICAL_NONE;
+ break;
+ case 1:
+ name = "equirectangular";
+ track->spherical.type = MP_SPHERICAL_EQUIRECTANGULAR;
+ break;
+ default:
+ name = "unknown";
+ track->spherical.type = MP_SPHERICAL_UNKNOWN;
+ }
+ MP_VERBOSE(demuxer, "| + ProjectionType: %s (%"PRIu64")\n", name,
+ projection->projection_type);
+ }
+ if (projection->n_projection_private) {
+ MP_VERBOSE(demuxer, "| + ProjectionPrivate: %zd bytes\n",
+ projection->projection_private.len);
+ MP_WARN(demuxer, "Unknown ProjectionPrivate element.\n");
+ }
+ if (projection->n_projection_pose_yaw) {
+ track->spherical.ref_angles[0] = projection->projection_pose_yaw;
+ MP_VERBOSE(demuxer, "| + ProjectionPoseYaw: %f\n",
+ projection->projection_pose_yaw);
+ }
+ if (projection->n_projection_pose_pitch) {
+ track->spherical.ref_angles[1] = projection->projection_pose_pitch;
+ MP_VERBOSE(demuxer, "| + ProjectionPosePitch: %f\n",
+ projection->projection_pose_pitch);
+ }
+ if (projection->n_projection_pose_roll) {
+ track->spherical.ref_angles[2] = projection->projection_pose_roll;
+ MP_VERBOSE(demuxer, "| + ProjectionPoseRoll: %f\n",
+ projection->projection_pose_roll);
+ }
+}
+
static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
struct ebml_video *video)
{
@@ -628,6 +672,8 @@ static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
}
if (video->n_colour)
parse_trackcolour(demuxer, track, &video->colour);
+ if (video->n_projection)
+ parse_trackprojection(demuxer, track, &video->projection);
}
/**
@@ -1454,6 +1500,7 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
sh_v->stereo_mode = track->stereo_mode;
sh_v->color = track->color;
+ sh_v->spherical = track->spherical;
done:
demux_add_sh_stream(demuxer, sh);
diff --git a/demux/stheader.h b/demux/stheader.h
index 04f8198df4..a0820f55b7 100644
--- a/demux/stheader.h
+++ b/demux/stheader.h
@@ -22,7 +22,7 @@
#include "common/common.h"
#include "audio/chmap.h"
-#include "video/csputils.h"
+#include "video/mp_image.h"
struct MPOpts;
struct demuxer;
@@ -93,6 +93,7 @@ struct mp_codec_params {
int rotate; // intended display rotation, in degrees, [0, 359]
int stereo_mode; // mp_stereo3d_mode (0 if none/unknown)
struct mp_colorspace color; // colorspace info where available
+ struct mp_spherical_params spherical;
// STREAM_VIDEO + STREAM_AUDIO
int bits_per_coded_sample;
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
index 8211d1f3b2..04e428246b 100644
--- a/video/decode/dec_video.c
+++ b/video/decode/dec_video.c
@@ -244,6 +244,10 @@ static void fix_image_params(struct dec_video *d_video,
p.color.sig_peak = 0.0;
}
+ p.spherical = c->spherical;
+ if (p.spherical.type == MP_SPHERICAL_AUTO)
+ p.spherical.type = MP_SPHERICAL_NONE;
+
// Guess missing colorspace fields from metadata. This guarantees all
// fields are at least set to legal values afterwards.
mp_image_params_guess_csp(&p);
diff --git a/video/filter/vf.c b/video/filter/vf.c
index a126007498..0db6f2a286 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -224,7 +224,7 @@ void vf_print_filter_chain(struct vf_chain *c, int msglevel,
return;
for (vf_instance_t *f = c->first; f; f = f->next) {
- char b[128] = {0};
+ char b[256] = {0};
mp_snprintf_cat(b, sizeof(b), " [%s] ", f->full_name);
if (f->label)
mp_snprintf_cat(b, sizeof(b), "\"%s\" ", f->label);
diff --git a/video/filter/vf_format.c b/video/filter/vf_format.c
index 1953b61ab6..406180ba34 100644
--- a/video/filter/vf_format.c
+++ b/video/filter/vf_format.c
@@ -46,6 +46,8 @@ struct vf_priv_s {
int rotate;
int dw, dh;
double dar;
+ int spherical;
+ int spherical_ref_angles[3];
};
static bool is_compatible(int fmt1, int fmt2)
@@ -127,6 +129,13 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
dsize = av_d2q(p->dar, INT_MAX);
mp_image_params_set_dsize(out, dsize.num, dsize.den);
+ if (p->spherical)
+ out->spherical.type = p->spherical;
+ for (int n = 0; n < 3; n++) {
+ if (p->spherical_ref_angles[n] != -1000)
+ out->spherical.ref_angles[n] = p->spherical_ref_angles[n];
+ }
+
// Make sure the user-overrides are consistent (no RGB csp for YUV, etc.).
mp_image_params_guess_csp(out);
@@ -165,6 +174,10 @@ static const m_option_t vf_opts_fields[] = {
OPT_INT("dw", dw, 0),
OPT_INT("dh", dh, 0),
OPT_DOUBLE("dar", dar, 0),
+ OPT_CHOICE_C("spherical", spherical, 0, mp_spherical_names),
+ OPT_INT("spherical-yaw", spherical_ref_angles[0], 0),
+ OPT_INT("spherical-pitch", spherical_ref_angles[1], 0),
+ OPT_INT("spherical-roll", spherical_ref_angles[1], 0),
OPT_REMOVED("outputlevels", "use the --video-output-levels global option"),
OPT_REMOVED("peak", "use sig-peak instead (changed value scale!)"),
{0}
@@ -178,5 +191,6 @@ const vf_info_t vf_info_format = {
.options = vf_opts_fields,
.priv_defaults = &(const struct vf_priv_s){
.rotate = -1,
+ .spherical_ref_angles = {-1000, -1000, -1000},
},
};
diff --git a/video/mp_image.c b/video/mp_image.c
index 765289f8ff..f1d2a269a1 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -41,6 +41,14 @@
#define HAVE_OPAQUE_REF (LIBAVUTIL_VERSION_MICRO >= 100 && \
LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 47, 100))
+const struct m_opt_choice_alternatives mp_spherical_names[] = {
+ {"auto", MP_SPHERICAL_AUTO},
+ {"none", MP_SPHERICAL_NONE},
+ {"unknown", MP_SPHERICAL_UNKNOWN},
+ {"equi", MP_SPHERICAL_EQUIRECTANGULAR},
+ {0}
+};
+
// Determine strides, plane sizes, and total required size for an image
// allocation. Returns total size on success, <0 on error. Unused planes
// have out_stride/out_plane_size to 0, and out_plane_offset set to -1 up
@@ -525,6 +533,7 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src)
dst->params.color.levels = src->params.color.levels;
dst->params.chroma_location = src->params.chroma_location;
}
+ dst->params.spherical = src->params.spherical;
mp_image_params_guess_csp(&dst->params); // ensure colorspace consistency
if ((dst->fmt.flags & MP_IMGFLAG_PAL) && (src->fmt.flags & MP_IMGFLAG_PAL)) {
if (dst->planes[1] && src->planes[1]) {
@@ -657,6 +666,12 @@ char *mp_image_params_to_str_buf(char *b, size_t bs,
MP_STEREO3D_NAME_DEF(p->stereo_in, "?"),
MP_STEREO3D_NAME_DEF(p->stereo_out, "?"));
}
+ if (p->spherical.type != MP_SPHERICAL_NONE) {
+ const float *a = p->spherical.ref_angles;
+ mp_snprintf_cat(b, bs, " (%s %f/%f/%f)",
+ m_opt_choice_str(mp_spherical_names, p->spherical.type),
+ a[0], a[1], a[2]);
+ }
} else {
snprintf(b, bs, "???");
}
@@ -691,6 +706,16 @@ bool mp_image_params_valid(const struct mp_image_params *p)
return true;
}
+static bool mp_spherical_equal(const struct mp_spherical_params *p1,
+ const struct mp_spherical_params *p2)
+{
+ for (int n = 0; n < 3; n++) {
+ if (p1->ref_angles[n] != p2->ref_angles[n])
+ return false;
+ }
+ return p1->type == p2->type;
+}
+
bool mp_image_params_equal(const struct mp_image_params *p1,
const struct mp_image_params *p2)
{
@@ -702,7 +727,8 @@ bool mp_image_params_equal(const struct mp_image_params *p1,
p1->chroma_location == p2->chroma_location &&
p1->rotate == p2->rotate &&
p1->stereo_in == p2->stereo_in &&
- p1->stereo_out == p2->stereo_out;
+ p1->stereo_out == p2->stereo_out &&
+ mp_spherical_equal(&p1->spherical, &p2->spherical);
}
// Set most image parameters, but not image format or size.
diff --git a/video/mp_image.h b/video/mp_image.h
index 640e2709e9..4f1c682ed3 100644
--- a/video/mp_image.h
+++ b/video/mp_image.h
@@ -36,6 +36,20 @@
#define MP_IMGFIELD_REPEAT_FIRST 0x04
#define MP_IMGFIELD_INTERLACED 0x20
+enum mp_spherical_type {
+ MP_SPHERICAL_AUTO = 0,
+ MP_SPHERICAL_NONE, // normal video
+ MP_SPHERICAL_UNKNOWN, // unknown projection
+ MP_SPHERICAL_EQUIRECTANGULAR, // (untiled)
+};
+
+extern const struct m_opt_choice_alternatives mp_spherical_names[];
+
+struct mp_spherical_params {
+ enum mp_spherical_type type;
+ float ref_angles[3]; // yaw/pitch/roll, refer to AVSphericalMapping
+};
+
// Describes image parameters that usually stay constant.
// New fields can be added in the future. Code changing the parameters should
// usually copy the whole struct, so that fields added later will be preserved.
@@ -50,6 +64,9 @@ struct mp_image_params {
int rotate;
enum mp_stereo3d_mode stereo_in; // image is encoded with this mode
enum mp_stereo3d_mode stereo_out; // should be displayed with this mode
+ // Spherical projection - if combined with stereo_in, each view uses its
+ // own projection, but all of the same type.
+ struct mp_spherical_params spherical;
};
/* Memory management:
@@ -144,7 +161,7 @@ void mp_image_params_guess_csp(struct mp_image_params *params);
char *mp_image_params_to_str_buf(char *b, size_t bs,
const struct mp_image_params *p);
-#define mp_image_params_to_str(p) mp_image_params_to_str_buf((char[99]){0}, 99, p)
+#define mp_image_params_to_str(p) mp_image_params_to_str_buf((char[256]){0}, 256, p)
bool mp_image_params_valid(const struct mp_image_params *p);
bool mp_image_params_equal(const struct mp_image_params *p1,
diff --git a/wscript b/wscript
index 2fb995041f..452c6a4130 100644
--- a/wscript
+++ b/wscript
@@ -481,6 +481,12 @@ FFmpeg/Libav libraries. You need at least {0}. Aborting.".format(libav_versions_
'func': check_statement('libavutil/frame.h',
'AV_FRAME_DATA_ICC_PROFILE',
use='libav'),
+ }, {
+ 'name': 'avutil-spherical',
+ 'desc': 'libavutil spherical side data',
+ 'func': check_statement('libavutil/spherical.h',
+ 'AV_SPHERICAL_EQUIRECTANGULAR',
+ use='libav'),
},
]