summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-08-21 14:56:07 +0200
committerwm4 <wm4@nowhere>2017-08-21 14:56:07 +0200
commit028faacff56d7f90f6b119b0b0ac686fd614825f (patch)
tree33d77aa61946f3bebfdb28fa8670065fdb79aed8
parent82d9419f62c90cecc13c492e3b68feebe0229daa (diff)
downloadmpv-028faacff56d7f90f6b119b0b0ac686fd614825f.tar.bz2
mpv-028faacff56d7f90f6b119b0b0ac686fd614825f.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. They can be added later as needed. Also raise the maximum sizes of stringified image params, since they can get really long.
-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.c15
-rw-r--r--video/mp_image.c28
-rw-r--r--video/mp_image.h17
-rw-r--r--wscript6
11 files changed, 152 insertions, 4 deletions
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index 9ed8f2f047..0c43698149 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
+ :equirect: 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..581bbe332f 100644
--- a/video/filter/vf_format.c
+++ b/video/filter/vf_format.c
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
+#include <math.h>
#include <libavutil/rational.h>
@@ -46,6 +47,8 @@ struct vf_priv_s {
int rotate;
int dw, dh;
double dar;
+ int spherical;
+ float spherical_ref_angles[3];
};
static bool is_compatible(int fmt1, int fmt2)
@@ -127,6 +130,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 (isfinite(p->spherical_ref_angles[n]))
+ 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 +175,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_FLOAT("spherical-yaw", spherical_ref_angles[0], 0),
+ OPT_FLOAT("spherical-pitch", spherical_ref_angles[1], 0),
+ OPT_FLOAT("spherical-roll", spherical_ref_angles[2], 0),
OPT_REMOVED("outputlevels", "use the --video-output-levels global option"),
OPT_REMOVED("peak", "use sig-peak instead (changed value scale!)"),
{0}
@@ -178,5 +192,6 @@ const vf_info_t vf_info_format = {
.options = vf_opts_fields,
.priv_defaults = &(const struct vf_priv_s){
.rotate = -1,
+ .spherical_ref_angles = {NAN, NAN, NAN},
},
};
diff --git a/video/mp_image.c b/video/mp_image.c
index 765289f8ff..45e5330947 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},
+ {"equirect", 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..53b25c5999 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,7 @@ 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
+ struct mp_spherical_params spherical;
};
/* Memory management:
@@ -144,7 +159,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 20241b1697..9d885884d7 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'),
},
]