mirror of https://github.com/mpv-player/mpv
video: initial Matroska 3D support
This inserts an automatic conversion filter if a Matroska file is marked as 3D (StereoMode element). The basic idea is similar to video rotation and colorspace handling: the 3D mode is added as a property to the video params. Depending on this property, a video filter can be inserted. As of this commit, extending mp_image_params is actually completely unnecessary - but the idea is that it will make it easier to integrate with VOs supporting stereo 3D mogrification. Although vo_opengl does support some stereo rendering, it didn't support the mode my sample file used, so I'll leave that part for later. Not that most mappings from Matroska mode to vf_stereo3d mode are probably wrong, and some are missing. Assuming that Matroska modes, and vf_stereo3d in modes, and out modes are all the same might be an oversimplification - we'll see. See issue #1045.
This commit is contained in:
parent
c80adac077
commit
8599c959fe
|
@ -561,6 +561,22 @@ Video
|
||||||
which means the value ``0`` would rotate the video according to the
|
which means the value ``0`` would rotate the video according to the
|
||||||
rotation metadata.)
|
rotation metadata.)
|
||||||
|
|
||||||
|
``--video-stereo-mode=<mode>``
|
||||||
|
Set the stereo 3D output mode (default: ``mono``). This is done by inserting
|
||||||
|
the ``stereo3d`` conversion filter.
|
||||||
|
|
||||||
|
The mode ``mono`` is an alias to ``ml``, which refers to the left frame in
|
||||||
|
2D. This is the default, which means mpv will try to show 3D movies in 2D,
|
||||||
|
instead of the mangled 3D image not intended for consumption (such as
|
||||||
|
showing the left and right frame side by side, etc.).
|
||||||
|
|
||||||
|
The pseudo-mode ``none`` disables automatic conversion completely.
|
||||||
|
|
||||||
|
Use ``--video-stereo-mode=help`` to list all available modes. Check with
|
||||||
|
the ``stereo3d`` filter documentation to see what the names mean. Note that
|
||||||
|
some names refer to modes not supported by ``stereo3d`` - these modes can
|
||||||
|
appear in files, but can't be handled properly by mpv.
|
||||||
|
|
||||||
``--video-zoom=<value>``
|
``--video-zoom=<value>``
|
||||||
Adjust the video display scale factor by the given value. The unit is in
|
Adjust the video display scale factor by the given value. The unit is in
|
||||||
fractions of the (scaled) window video size.
|
fractions of the (scaled) window video size.
|
||||||
|
|
|
@ -265,6 +265,7 @@ sub define_matroska {
|
||||||
elem('DisplayUnit', '54b2', 'uint'),
|
elem('DisplayUnit', '54b2', 'uint'),
|
||||||
elem('FrameRate', '2383e3', 'float'),
|
elem('FrameRate', '2383e3', 'float'),
|
||||||
elem('ColourSpace', '2eb524', 'binary'),
|
elem('ColourSpace', '2eb524', 'binary'),
|
||||||
|
elem('StereoMode', '53b8', 'uint'),
|
||||||
}),
|
}),
|
||||||
elem('Audio', 'e1', {
|
elem('Audio', 'e1', {
|
||||||
elem('SamplingFrequency', 'b5', 'float'),
|
elem('SamplingFrequency', 'b5', 'float'),
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "options/options.h"
|
#include "options/options.h"
|
||||||
#include "misc/bstr.h"
|
#include "misc/bstr.h"
|
||||||
#include "stream/stream.h"
|
#include "stream/stream.h"
|
||||||
|
#include "video/csputils.h"
|
||||||
#include "demux.h"
|
#include "demux.h"
|
||||||
#include "stheader.h"
|
#include "stheader.h"
|
||||||
#include "ebml.h"
|
#include "ebml.h"
|
||||||
|
@ -102,6 +103,7 @@ typedef struct mkv_track {
|
||||||
bool v_dwidth_set, v_dheight_set;
|
bool v_dwidth_set, v_dheight_set;
|
||||||
double v_frate;
|
double v_frate;
|
||||||
uint32_t colorspace;
|
uint32_t colorspace;
|
||||||
|
int stereo_mode;
|
||||||
|
|
||||||
uint32_t a_formattag;
|
uint32_t a_formattag;
|
||||||
uint32_t a_channels, a_bps;
|
uint32_t a_channels, a_bps;
|
||||||
|
@ -504,6 +506,15 @@ static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
|
||||||
MP_VERBOSE(demuxer, "| + Colorspace: %#x\n",
|
MP_VERBOSE(demuxer, "| + Colorspace: %#x\n",
|
||||||
(unsigned int)track->colorspace);
|
(unsigned int)track->colorspace);
|
||||||
}
|
}
|
||||||
|
if (video->n_stereo_mode) {
|
||||||
|
const char *name = MP_STEREO3D_NAME(video->stereo_mode);
|
||||||
|
if (name) {
|
||||||
|
track->stereo_mode = video->stereo_mode;
|
||||||
|
MP_VERBOSE(demuxer, "| + StereoMode: %s\n", name);
|
||||||
|
} else {
|
||||||
|
MP_WARN(demuxer, "Unknown StereoMode: %d\n", (int)video->stereo_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1276,6 +1287,7 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||||
}
|
}
|
||||||
MP_VERBOSE(demuxer, "Aspect: %f\n", sh_v->aspect);
|
MP_VERBOSE(demuxer, "Aspect: %f\n", sh_v->aspect);
|
||||||
sh_v->avi_dts = track->ms_compat;
|
sh_v->avi_dts = track->ms_compat;
|
||||||
|
sh_v->stereo_mode = track->stereo_mode;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ typedef struct sh_video {
|
||||||
int bitrate; // compressed bits/sec
|
int bitrate; // compressed bits/sec
|
||||||
int disp_w, disp_h; // display size
|
int disp_w, disp_h; // display size
|
||||||
int rotate; // intended display rotation, in degrees, [0, 359]
|
int rotate; // intended display rotation, in degrees, [0, 359]
|
||||||
|
int stereo_mode; // mp_stereo3d_mode (0 if none/unknown)
|
||||||
MP_BITMAPINFOHEADER *bih;
|
MP_BITMAPINFOHEADER *bih;
|
||||||
} sh_video_t;
|
} sh_video_t;
|
||||||
|
|
||||||
|
|
|
@ -1983,6 +1983,59 @@ const m_option_type_t m_option_type_imgfmt = {
|
||||||
.copy = copy_opt,
|
.copy = copy_opt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "video/csputils.h"
|
||||||
|
|
||||||
|
static int parse_stereo_mode(struct mp_log *log, const m_option_t *opt,
|
||||||
|
struct bstr name, struct bstr param, void *dst)
|
||||||
|
{
|
||||||
|
if (param.len == 0)
|
||||||
|
return M_OPT_MISSING_PARAM;
|
||||||
|
|
||||||
|
if (!bstrcmp0(param, "help")) {
|
||||||
|
mp_info(log, "Available modes:");
|
||||||
|
for (int n = 0; n < MP_STEREO3D_COUNT; n++) {
|
||||||
|
if (mp_stereo3d_names[n])
|
||||||
|
mp_info(log, " %s\n", mp_stereo3d_names[n]);
|
||||||
|
}
|
||||||
|
mp_info(log, " none\n");
|
||||||
|
return M_OPT_EXIT - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mode = -1;
|
||||||
|
|
||||||
|
for (int n = 0; n < MP_STEREO3D_COUNT; n++) {
|
||||||
|
if (bstr_equals(param, bstr0(mp_stereo3d_names[n]))) {
|
||||||
|
mode = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode < 0 && !bstr_equals0(param, "none")) {
|
||||||
|
mp_err(log, "Option %.*s: unknown parameter: '%.*s'\n",
|
||||||
|
BSTR_P(name), BSTR_P(param));
|
||||||
|
return M_OPT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst)
|
||||||
|
*((int *)dst) = mode;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *print_stereo_mode(const m_option_t *opt, const void *val)
|
||||||
|
{
|
||||||
|
int mode = *(int *)val;
|
||||||
|
const char *name = mode >= 0 ? MP_STEREO3D_NAME(mode) : "none";
|
||||||
|
return talloc_strdup(NULL, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const m_option_type_t m_option_vid_stereo_mode = {
|
||||||
|
.name = "Stereo 3D mode",
|
||||||
|
.size = sizeof(int),
|
||||||
|
.parse = parse_stereo_mode,
|
||||||
|
.print = print_stereo_mode,
|
||||||
|
};
|
||||||
|
|
||||||
static int parse_fourcc(struct mp_log *log, const m_option_t *opt,
|
static int parse_fourcc(struct mp_log *log, const m_option_t *opt,
|
||||||
struct bstr name, struct bstr param, void *dst)
|
struct bstr name, struct bstr param, void *dst)
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,6 +56,7 @@ extern const m_option_type_t m_option_type_msglevels;
|
||||||
extern const m_option_type_t m_option_type_print_fn;
|
extern const m_option_type_t m_option_type_print_fn;
|
||||||
extern const m_option_type_t m_option_type_subconfig;
|
extern const m_option_type_t m_option_type_subconfig;
|
||||||
extern const m_option_type_t m_option_type_imgfmt;
|
extern const m_option_type_t m_option_type_imgfmt;
|
||||||
|
extern const m_option_type_t m_option_vid_stereo_mode;
|
||||||
extern const m_option_type_t m_option_type_fourcc;
|
extern const m_option_type_t m_option_type_fourcc;
|
||||||
extern const m_option_type_t m_option_type_afmt;
|
extern const m_option_type_t m_option_type_afmt;
|
||||||
extern const m_option_type_t m_option_type_color;
|
extern const m_option_type_t m_option_type_color;
|
||||||
|
@ -648,8 +649,11 @@ extern const char m_option_path_separator;
|
||||||
#define OPT_TRACKCHOICE(name, var) \
|
#define OPT_TRACKCHOICE(name, var) \
|
||||||
OPT_CHOICE_OR_INT(name, var, 0, 1, 8190, ({"no", -2}, {"auto", -1}))
|
OPT_CHOICE_OR_INT(name, var, 0, 1, 8190, ({"no", -2}, {"auto", -1}))
|
||||||
|
|
||||||
|
#define OPT_VID_STEREO_MODE(...) \
|
||||||
|
OPT_GENERAL(int, __VA_ARGS__, .type = &m_option_vid_stereo_mode)
|
||||||
|
|
||||||
#define OPT_STRING_VALIDATE_(optname, varname, flags, validate_fn, ...) \
|
#define OPT_STRING_VALIDATE_(optname, varname, flags, validate_fn, ...) \
|
||||||
OPT_GENERAL(char*, optname, varname, flags, __VA_ARGS__, \
|
OPT_GENERAL(char*, optname, varname, flags, __VA_ARGS__, \
|
||||||
.priv = MP_EXPECT_TYPE(m_opt_string_validate_fn, validate_fn))
|
.priv = MP_EXPECT_TYPE(m_opt_string_validate_fn, validate_fn))
|
||||||
#define OPT_STRING_VALIDATE(...) \
|
#define OPT_STRING_VALIDATE(...) \
|
||||||
OPT_STRING_VALIDATE_(__VA_ARGS__, .type = &m_option_type_string)
|
OPT_STRING_VALIDATE_(__VA_ARGS__, .type = &m_option_type_string)
|
||||||
|
|
|
@ -415,6 +415,7 @@ const m_option_t mp_opts[] = {
|
||||||
{"BT.2020", MP_CSP_PRIM_BT_2020})),
|
{"BT.2020", MP_CSP_PRIM_BT_2020})),
|
||||||
OPT_CHOICE_OR_INT("video-rotate", video_rotate, 0, 0, 359,
|
OPT_CHOICE_OR_INT("video-rotate", video_rotate, 0, 0, 359,
|
||||||
({"no", -1})),
|
({"no", -1})),
|
||||||
|
OPT_VID_STEREO_MODE("video-stereo-mode", video_stereo_mode, 0),
|
||||||
|
|
||||||
OPT_CHOICE_OR_INT("cursor-autohide", cursor_autohide_delay, 0,
|
OPT_CHOICE_OR_INT("cursor-autohide", cursor_autohide_delay, 0,
|
||||||
0, 30000, ({"no", -1}, {"always", -2})),
|
0, 30000, ({"no", -1}, {"always", -2})),
|
||||||
|
|
|
@ -98,6 +98,7 @@ typedef struct MPOpts {
|
||||||
int requested_primaries;
|
int requested_primaries;
|
||||||
|
|
||||||
int video_rotate;
|
int video_rotate;
|
||||||
|
int video_stereo_mode;
|
||||||
|
|
||||||
char *audio_decoders;
|
char *audio_decoders;
|
||||||
char *video_decoders;
|
char *video_decoders;
|
||||||
|
|
|
@ -157,6 +157,18 @@ static void filter_reconfig(struct MPContext *mpctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.stereo_in != params.stereo_out &&
|
||||||
|
params.stereo_in > 0 && params.stereo_out >= 0)
|
||||||
|
{
|
||||||
|
char *from = MP_STEREO3D_NAME(params.stereo_in);
|
||||||
|
char *to = MP_STEREO3D_NAME(params.stereo_out);
|
||||||
|
if (from && to) {
|
||||||
|
char *args[] = {"in", from, "out", to, NULL, NULL};
|
||||||
|
if (try_filter(mpctx, params, "stereo3d", "stereo3d", args) < 0)
|
||||||
|
MP_ERR(mpctx, "Can't insert 3D conversion filter.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recreate_video_filters(struct MPContext *mpctx)
|
static void recreate_video_filters(struct MPContext *mpctx)
|
||||||
|
|
|
@ -77,6 +77,28 @@ const char *const mp_chroma_names[MP_CHROMA_COUNT] = {
|
||||||
"mpeg1/jpeg",
|
"mpeg1/jpeg",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The short name _must_ match with what vf_stereo3d accepts (if supported).
|
||||||
|
// The long name is closer to the Matroska spec (StereoMode element).
|
||||||
|
// If you add entries that don't match Matroska, make sure demux_mkv.c rejects
|
||||||
|
// them properly.
|
||||||
|
// The long name is unused.
|
||||||
|
#define E(index, short, long) [index] = short
|
||||||
|
const char *const mp_stereo3d_names[MP_STEREO3D_COUNT] = {
|
||||||
|
E(0, "mono", "mono"), // unsupported by vf_stereo3d
|
||||||
|
E(1, "sbs2l", "side_by_side_left"),
|
||||||
|
E(2, "abr", "top_bottom_right"),
|
||||||
|
E(3, "abl", "top_bottom_left"),
|
||||||
|
E(4, "checkr", "checkboard_right"), // unsupported by vf_stereo3d
|
||||||
|
E(5, "checkl", "checkboard_left"),
|
||||||
|
E(6, "irr", "row_interleaved_right"),
|
||||||
|
E(7, "irl", "row_interleaved_left"),
|
||||||
|
E(8, "icr", "column_interleaved_right"),// unsupported by vf_stereo3d
|
||||||
|
E(9, "icl", "column_interleaved_left"), // unsupported by vf_stereo3d
|
||||||
|
E(10, "arcc", "anaglyph_cyan_red"), // Matroska: unclear which mode
|
||||||
|
E(11, "sbs2r", "side_by_side_right"),
|
||||||
|
E(12, "agmc", "anaglyph_green_magenta"), // Matroska: unclear which mode
|
||||||
|
};
|
||||||
|
|
||||||
enum mp_csp avcol_spc_to_mp_csp(int avcolorspace)
|
enum mp_csp avcol_spc_to_mp_csp(int avcolorspace)
|
||||||
{
|
{
|
||||||
switch (avcolorspace) {
|
switch (avcolorspace) {
|
||||||
|
|
|
@ -79,6 +79,17 @@ enum mp_render_intent {
|
||||||
MP_INTENT_ABSOLUTE_COLORIMETRIC = 3
|
MP_INTENT_ABSOLUTE_COLORIMETRIC = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum mp_stereo3d_mode {
|
||||||
|
MP_STEREO3D_INVALID = -1,
|
||||||
|
MP_STEREO3D_MONO = 0,
|
||||||
|
MP_STEREO3D_COUNT = 13, // 12 is last valid mode
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const char *const mp_stereo3d_names[MP_STEREO3D_COUNT];
|
||||||
|
|
||||||
|
#define MP_STEREO3D_NAME(x) \
|
||||||
|
((x) >= 0 && (x) < MP_STEREO3D_COUNT ? (char *)mp_stereo3d_names[(x)] : NULL)
|
||||||
|
|
||||||
struct mp_csp_details {
|
struct mp_csp_details {
|
||||||
enum mp_csp format;
|
enum mp_csp format;
|
||||||
enum mp_csp_levels levels_in; // encoded video
|
enum mp_csp_levels levels_in; // encoded video
|
||||||
|
|
|
@ -485,6 +485,7 @@ static void update_image_params(struct dec_video *vd, AVFrame *frame,
|
||||||
.chroma_location =
|
.chroma_location =
|
||||||
avchroma_location_to_mp(ctx->avctx->chroma_sample_location),
|
avchroma_location_to_mp(ctx->avctx->chroma_sample_location),
|
||||||
.rotate = vd->header->video->rotate,
|
.rotate = vd->header->video->rotate,
|
||||||
|
.stereo_in = vd->header->video->stereo_mode,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (opts->video_rotate < 0) {
|
if (opts->video_rotate < 0) {
|
||||||
|
@ -492,6 +493,7 @@ static void update_image_params(struct dec_video *vd, AVFrame *frame,
|
||||||
} else {
|
} else {
|
||||||
out_params->rotate = (out_params->rotate + opts->video_rotate) % 360;
|
out_params->rotate = (out_params->rotate + opts->video_rotate) % 360;
|
||||||
}
|
}
|
||||||
|
out_params->stereo_out = opts->video_stereo_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
|
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
|
||||||
|
|
|
@ -445,6 +445,8 @@ const struct m_opt_choice_alternatives stereo_code_names[] = {
|
||||||
{"interleave_rows_left_first", INTERLEAVE_ROWS_LR},
|
{"interleave_rows_left_first", INTERLEAVE_ROWS_LR},
|
||||||
{"irr", INTERLEAVE_ROWS_RL},
|
{"irr", INTERLEAVE_ROWS_RL},
|
||||||
{"interleave_rows_right_first", INTERLEAVE_ROWS_RL},
|
{"interleave_rows_right_first", INTERLEAVE_ROWS_RL},
|
||||||
|
// convenience alias for MP_STEREO3D_MONO
|
||||||
|
{"mono", MONO_L},
|
||||||
{ NULL, 0}
|
{ NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -364,6 +364,8 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src)
|
||||||
dst->fields = src->fields;
|
dst->fields = src->fields;
|
||||||
dst->qscale_type = src->qscale_type;
|
dst->qscale_type = src->qscale_type;
|
||||||
dst->pts = src->pts;
|
dst->pts = src->pts;
|
||||||
|
dst->params.stereo_in = src->params.stereo_in;
|
||||||
|
dst->params.stereo_out = src->params.stereo_out;
|
||||||
if (dst->w == src->w && dst->h == src->h) {
|
if (dst->w == src->w && dst->h == src->h) {
|
||||||
dst->params.d_w = src->params.d_w;
|
dst->params.d_w = src->params.d_w;
|
||||||
dst->params.d_h = src->params.d_h;
|
dst->params.d_h = src->params.d_h;
|
||||||
|
@ -489,7 +491,9 @@ bool mp_image_params_equal(const struct mp_image_params *p1,
|
||||||
p1->outputlevels == p2->outputlevels &&
|
p1->outputlevels == p2->outputlevels &&
|
||||||
p1->primaries == p2->primaries &&
|
p1->primaries == p2->primaries &&
|
||||||
p1->chroma_location == p2->chroma_location &&
|
p1->chroma_location == p2->chroma_location &&
|
||||||
p1->rotate == p2->rotate;
|
p1->rotate == p2->rotate &&
|
||||||
|
p1->stereo_in == p2->stereo_in &&
|
||||||
|
p1->stereo_out == p2->stereo_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set most image parameters, but not image format or size.
|
// Set most image parameters, but not image format or size.
|
||||||
|
|
|
@ -55,6 +55,8 @@ struct mp_image_params {
|
||||||
enum mp_csp_levels outputlevels;
|
enum mp_csp_levels outputlevels;
|
||||||
// The image should be rotated clockwise (0-359 degrees).
|
// The image should be rotated clockwise (0-359 degrees).
|
||||||
int rotate;
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Memory management:
|
/* Memory management:
|
||||||
|
|
Loading…
Reference in New Issue