matroska: Implement basic stereoscopic video support

Implement support for the packed video formats WebM
uses, not all the values that Matroska might use.

In practice, it's really hard to find any samples in the
wild of any.

Supported in both the muxer and demuxer.
This commit is contained in:
Jan Schmidt 2015-06-11 00:22:54 +10:00
parent fff76157d8
commit ec5bc9dccb
4 changed files with 123 additions and 2 deletions

View file

@ -731,12 +731,61 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
g_free (data); g_free (data);
break; break;
} }
case GST_MATROSKA_ID_VIDEOSTEREOMODE:
{
guint64 num;
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
break;
GST_DEBUG_OBJECT (demux, "StereoMode: %" G_GUINT64_FORMAT, num);
switch (num) {
case GST_MATROSKA_STEREO_MODE_SBS_RL:
videocontext->multiview_flags =
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
/* fall through */
case GST_MATROSKA_STEREO_MODE_SBS_LR:
videocontext->multiview_mode =
GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE;
break;
case GST_MATROSKA_STEREO_MODE_TB_RL:
videocontext->multiview_flags =
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
/* fall through */
case GST_MATROSKA_STEREO_MODE_TB_LR:
videocontext->multiview_mode =
GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM;
break;
case GST_MATROSKA_STEREO_MODE_CHECKER_RL:
videocontext->multiview_flags =
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
/* fall through */
case GST_MATROSKA_STEREO_MODE_CHECKER_LR:
videocontext->multiview_mode =
GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD;
break;
case GST_MATROSKA_STEREO_MODE_FBF_RL:
videocontext->multiview_flags =
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST;
/* fall through */
case GST_MATROSKA_STEREO_MODE_FBF_LR:
videocontext->multiview_mode =
GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME;
/* FIXME: In frame-by-frame mode, left/right frame buffers are
* laced within one block, and we'll need to apply FIRST_IN_BUNDLE
* accordingly. See http://www.matroska.org/technical/specs/index.html#StereoMode */
GST_FIXME_OBJECT (demux,
"Frame-by-frame stereoscopic mode not fully implemented");
break;
}
break;
}
default: default:
GST_WARNING_OBJECT (demux, GST_WARNING_OBJECT (demux,
"Unknown TrackVideo subelement 0x%x - ignoring", id); "Unknown TrackVideo subelement 0x%x - ignoring", id);
/* fall through */ /* fall through */
case GST_MATROSKA_ID_VIDEOSTEREOMODE:
case GST_MATROSKA_ID_VIDEODISPLAYUNIT: case GST_MATROSKA_ID_VIDEODISPLAYUNIT:
case GST_MATROSKA_ID_VIDEOPIXELCROPBOTTOM: case GST_MATROSKA_ID_VIDEOPIXELCROPBOTTOM:
case GST_MATROSKA_ID_VIDEOPIXELCROPTOP: case GST_MATROSKA_ID_VIDEOPIXELCROPTOP:

View file

@ -59,6 +59,9 @@ gst_matroska_track_init_video_context (GstMatroskaTrackContext ** p_context)
video_context->fourcc = 0; video_context->fourcc = 0;
video_context->default_fps = 0.0; video_context->default_fps = 0.0;
video_context->earliest_time = GST_CLOCK_TIME_NONE; video_context->earliest_time = GST_CLOCK_TIME_NONE;
video_context->multiview_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
video_context->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
return TRUE; return TRUE;
} }

View file

@ -23,6 +23,7 @@
#define __GST_MATROSKA_IDS_H__ #define __GST_MATROSKA_IDS_H__
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/video/video-info.h>
#include "ebml-ids.h" #include "ebml-ids.h"
@ -489,6 +490,16 @@ typedef enum {
GST_MATROSKA_VIDEOTRACK_INTERLACED = (GST_MATROSKA_TRACK_SHIFT<<0) GST_MATROSKA_VIDEOTRACK_INTERLACED = (GST_MATROSKA_TRACK_SHIFT<<0)
} GstMatroskaVideoTrackFlags; } GstMatroskaVideoTrackFlags;
typedef enum {
GST_MATROSKA_STEREO_MODE_SBS_LR = 0x1,
GST_MATROSKA_STEREO_MODE_TB_RL = 0x2,
GST_MATROSKA_STEREO_MODE_TB_LR = 0x3,
GST_MATROSKA_STEREO_MODE_CHECKER_RL = 0x4,
GST_MATROSKA_STEREO_MODE_CHECKER_LR = 0x5,
GST_MATROSKA_STEREO_MODE_SBS_RL = 0x9,
GST_MATROSKA_STEREO_MODE_FBF_LR = 0xD,
GST_MATROSKA_STEREO_MODE_FBF_RL = 0xE
} GstMatroskaStereoMode;
typedef struct _GstMatroskaTrackContext GstMatroskaTrackContext; typedef struct _GstMatroskaTrackContext GstMatroskaTrackContext;
@ -571,6 +582,9 @@ typedef struct _GstMatroskaTrackVideoContext {
GstMatroskaAspectRatioMode asr_mode; GstMatroskaAspectRatioMode asr_mode;
guint32 fourcc; guint32 fourcc;
GstVideoMultiviewMode multiview_mode;
GstVideoMultiviewFlags multiview_flags;
/* QoS */ /* QoS */
GstClockTime earliest_time; GstClockTime earliest_time;

View file

@ -929,7 +929,7 @@ gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
GstMatroskaPad *collect_pad; GstMatroskaPad *collect_pad;
GstStructure *structure; GstStructure *structure;
const gchar *mimetype; const gchar *mimetype;
const gchar *interlace_mode; const gchar *interlace_mode, *s;
const GValue *value = NULL; const GValue *value = NULL;
GstBuffer *codec_buf = NULL; GstBuffer *codec_buf = NULL;
gint width, height, pixel_width, pixel_height; gint width, height, pixel_width, pixel_height;
@ -1002,6 +1002,14 @@ gst_matroska_mux_video_pad_setcaps (GstPad * pad, GstCaps * caps)
videocontext->display_height = 0; videocontext->display_height = 0;
} }
/* Collect stereoscopic info, if any */
if ((s = gst_structure_get_string (structure, "multiview-mode")))
videocontext->multiview_mode =
gst_video_multiview_mode_from_caps_string (s);
gst_structure_get_flagset (structure, "multiview-flags",
&videocontext->multiview_flags, NULL);
skip_details: skip_details:
videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE; videocontext->asr_mode = GST_MATROSKA_ASPECT_RATIO_MODE_FREE;
@ -2432,6 +2440,53 @@ gst_matroska_mux_track_header (GstMatroskaMux * mux,
gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE, gst_ebml_write_binary (ebml, GST_MATROSKA_ID_VIDEOCOLOURSPACE,
(gpointer) & fcc_le, 4); (gpointer) & fcc_le, 4);
} }
if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
guint64 stereo_mode = 0;
switch (videocontext->multiview_mode) {
case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
if (videocontext->multiview_flags &
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_RL;
else
stereo_mode = GST_MATROSKA_STEREO_MODE_SBS_LR;
break;
case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
if (videocontext->multiview_flags &
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
stereo_mode = GST_MATROSKA_STEREO_MODE_TB_RL;
else
stereo_mode = GST_MATROSKA_STEREO_MODE_TB_LR;
break;
case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
if (videocontext->multiview_flags &
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_RL;
else
stereo_mode = GST_MATROSKA_STEREO_MODE_CHECKER_LR;
break;
case GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME:
if (videocontext->multiview_flags &
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST)
stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_RL;
else
stereo_mode = GST_MATROSKA_STEREO_MODE_FBF_LR;
/* FIXME: In frame-by-frame mode, left/right frame buffers need to be
* laced within one block. See http://www.matroska.org/technical/specs/index.html#StereoMode */
GST_FIXME_OBJECT (mux,
"Frame-by-frame stereoscopic mode not fully implemented");
break;
default:
GST_WARNING_OBJECT (mux,
"Multiview mode %d not supported in Matroska/WebM",
videocontext->multiview_mode);
break;
}
if (stereo_mode != 0)
gst_ebml_write_uint (ebml, GST_MATROSKA_ID_VIDEOSTEREOMODE,
stereo_mode);
}
gst_ebml_write_master_finish (ebml, master); gst_ebml_write_master_finish (ebml, master);
break; break;