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.
This commit is contained in:
wm4 2017-08-21 14:56:07 +02:00
parent 82d9419f62
commit 028faacff5
11 changed files with 152 additions and 4 deletions

View File

@ -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.

View File

@ -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',

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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},
},
};

View File

@ -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.

View File

@ -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,

View File

@ -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'),
},
]