summaryrefslogtreecommitdiffstats
path: root/gst/matroska
diff options
context:
space:
mode:
authorVincent Penquerc'h <ogg.k.ogg.k@googlemail.com>2009-02-15 18:49:44 +0000
committerTim-Philipp Müller <tim.muller@collabora.co.uk>2009-08-08 12:54:48 +0100
commit19b7001bf99ea37a91af6061ca98e50b08064017 (patch)
treee1d710e64bc6c2191a8eafc11e1663adea31cbd7 /gst/matroska
parentb0bcb27517fe10666c8d16f0ba4812e1495bd105 (diff)
matroska: add kate subtitle support to matroska muxer and demuxer
See #525743.
Diffstat (limited to 'gst/matroska')
-rw-r--r--gst/matroska/matroska-demux.c6
-rw-r--r--gst/matroska/matroska-ids.h1
-rw-r--r--gst/matroska/matroska-mux.c179
3 files changed, 185 insertions, 1 deletions
diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c
index 945f35a4..1384ee4f 100644
--- a/gst/matroska/matroska-demux.c
+++ b/gst/matroska/matroska-demux.c
@@ -121,7 +121,8 @@ static GstStaticPadTemplate subtitle_src_templ =
GST_PAD_SOMETIMES,
GST_STATIC_CAPS ("text/plain; application/x-ssa; application/x-ass; "
"application/x-usf; video/x-dvd-subpicture; "
- "subpicture/x-pgs; " "application/x-subtitle-unknown")
+ "subpicture/x-pgs; subtitle/x-kate; "
+ "application/x-subtitle-unknown")
);
static GstFlowReturn gst_matroska_demux_parse_contents (GstMatroskaDemux *
@@ -5771,6 +5772,9 @@ gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
((GstMatroskaTrackContext *) subtitlecontext)->send_dvd_event = TRUE;
} else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_HDMVPGS)) {
caps = gst_caps_new_simple ("subpicture/x-pgs", NULL);
+ } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_KATE)) {
+ caps = gst_caps_new_simple ("subtitle/x-kate", NULL);
+ context->send_xiph_headers = TRUE;
} else {
GST_DEBUG ("Unknown subtitle stream: codec_id='%s'", codec_id);
caps = gst_caps_new_simple ("application/x-subtitle-unknown", NULL);
diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h
index a9d3b7e9..d8edc378 100644
--- a/gst/matroska/matroska-ids.h
+++ b/gst/matroska/matroska-ids.h
@@ -379,6 +379,7 @@
#define GST_MATROSKA_CODEC_ID_SUBTITLE_VOBSUB "S_VOBSUB"
#define GST_MATROSKA_CODEC_ID_SUBTITLE_HDMVPGS "S_HDMV/PGS"
#define GST_MATROSKA_CODEC_ID_SUBTITLE_BMP "S_IMAGE/BMP"
+#define GST_MATROSKA_CODEC_ID_SUBTITLE_KATE "S_KATE"
/*
* Matroska tags. Strings.
diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c
index 3c39191d..421dcd50 100644
--- a/gst/matroska/matroska-mux.c
+++ b/gst/matroska/matroska-mux.c
@@ -228,6 +228,8 @@ static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
GstMatroskaTrackContext * context);
static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
GstMatroskaTrackContext * context);
+static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
+ GstMatroskaTrackContext * context);
static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
GstMatroskaTrackContext * context);
@@ -897,6 +899,109 @@ skip_details:
return FALSE;
}
+/* N > 0 to expect a particular number of headers, negative if the
+ number of headers is variable */
+static gboolean
+xiphN_streamheader_to_codecdata (const GValue * streamheader,
+ GstMatroskaTrackContext * context, GstBuffer ** p_buf0, int N)
+{
+ GstBuffer **buf = NULL;
+ GArray *bufarr;
+ guint8 *priv_data;
+ guint bufi, i, offset, priv_data_size;
+
+ if (streamheader == NULL)
+ goto no_stream_headers;
+
+ if (G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY)
+ goto wrong_type;
+
+ bufarr = g_value_peek_pointer (streamheader);
+ if (bufarr->len <= 0 || bufarr->len > 255) /* at least one header, and count stored in a byte */
+ goto wrong_count;
+ if (N > 0 && bufarr->len != N)
+ goto wrong_count;
+
+ context->xiph_headers_to_skip = bufarr->len;
+
+ buf = (GstBuffer **) g_malloc0 (sizeof (GstBuffer *) * bufarr->len);
+ for (i = 0; i < bufarr->len; i++) {
+ GValue *bufval = &g_array_index (bufarr, GValue, i);
+
+ if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
+ g_free (buf);
+ goto wrong_content_type;
+ }
+
+ buf[i] = g_value_peek_pointer (bufval);
+ }
+
+ priv_data_size = 1;
+ if (bufarr->len > 0) {
+ for (i = 0; i < bufarr->len - 1; i++) {
+ priv_data_size += GST_BUFFER_SIZE (buf[i]) / 0xff + 1;
+ }
+ }
+
+ for (i = 0; i < bufarr->len; ++i) {
+ priv_data_size += GST_BUFFER_SIZE (buf[i]);
+ }
+
+ priv_data = g_malloc0 (priv_data_size);
+
+ priv_data[0] = bufarr->len - 1;
+ offset = 1;
+
+ if (bufarr->len > 0) {
+ for (bufi = 0; bufi < bufarr->len - 1; bufi++) {
+ for (i = 0; i < GST_BUFFER_SIZE (buf[bufi]) / 0xff; ++i) {
+ priv_data[offset++] = 0xff;
+ }
+ priv_data[offset++] = GST_BUFFER_SIZE (buf[bufi]) % 0xff;
+ }
+ }
+
+ for (i = 0; i < bufarr->len; ++i) {
+ memcpy (priv_data + offset, GST_BUFFER_DATA (buf[i]),
+ GST_BUFFER_SIZE (buf[i]));
+ offset += GST_BUFFER_SIZE (buf[i]);
+ }
+
+ context->codec_priv = priv_data;
+ context->codec_priv_size = priv_data_size;
+
+ if (p_buf0)
+ *p_buf0 = gst_buffer_ref (buf[0]);
+
+ g_free (buf);
+
+ return TRUE;
+
+/* ERRORS */
+no_stream_headers:
+ {
+ GST_WARNING ("required streamheaders missing in sink caps!");
+ return FALSE;
+ }
+wrong_type:
+ {
+ GST_WARNING ("streamheaders are not a GST_TYPE_ARRAY, but a %s",
+ G_VALUE_TYPE_NAME (streamheader));
+ return FALSE;
+ }
+wrong_count:
+ {
+ GST_WARNING ("got %u streamheaders, not 3 as expected", bufarr->len);
+ return FALSE;
+ }
+wrong_content_type:
+ {
+ GST_WARNING ("streamheaders array does not contain GstBuffers");
+ return FALSE;
+ }
+}
+
+/* FIXME: after release make all code use xiph3_streamheader_to_codecdata() */
static gboolean
xiph3_streamheader_to_codecdata (const GValue * streamheader,
GstMatroskaTrackContext * context, GstBuffer ** p_buf0)
@@ -994,6 +1099,7 @@ vorbis_streamheader_to_codecdata (const GValue * streamheader,
{
GstBuffer *buf0 = NULL;
+ /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
return FALSE;
@@ -1023,6 +1129,7 @@ theora_streamheader_to_codecdata (const GValue * streamheader,
{
GstBuffer *buf0 = NULL;
+ /* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
return FALSE;
@@ -1075,6 +1182,27 @@ theora_streamheader_to_codecdata (const GValue * streamheader,
}
static gboolean
+kate_streamheader_to_codecdata (const GValue * streamheader,
+ GstMatroskaTrackContext * context)
+{
+ GstBuffer *buf0 = NULL;
+
+ if (!xiphN_streamheader_to_codecdata (streamheader, context, &buf0, -1))
+ return FALSE;
+
+ if (buf0 == NULL || GST_BUFFER_SIZE (buf0) < 64) { /* Kate ID header is 64 bytes */
+ GST_WARNING ("First kate header too small, ignoring");
+ } else if (memcmp (GST_BUFFER_DATA (buf0), "\200kate\0\0\0", 8) != 0) {
+ GST_WARNING ("First header not a kate identification header, ignoring");
+ }
+
+ if (buf0)
+ gst_buffer_unref (buf0);
+
+ return TRUE;
+}
+
+static gboolean
flac_streamheader_to_codecdata (const GValue * streamheader,
GstMatroskaTrackContext * context)
{
@@ -1538,6 +1666,57 @@ gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
* no single subtitle creation element in GStreamer,
* neither do I know how subtitling works at all. */
+ /* There is now (at least) one such alement (kateenc), and I'm going
+ to handle it here and claim it works when it can be piped back
+ through GStreamer and VLC */
+
+ GstMatroskaTrackContext *context = NULL;
+ GstMatroskaTrackSubtitleContext *scontext;
+ GstMatroskaMux *mux;
+ GstMatroskaPad *collect_pad;
+ const gchar *mimetype;
+ GstStructure *structure;
+
+ mux = GST_MATROSKA_MUX (GST_PAD_PARENT (pad));
+
+ /* find context */
+ collect_pad = (GstMatroskaPad *) gst_pad_get_element_private (pad);
+ g_assert (collect_pad);
+ context = collect_pad->track;
+ g_assert (context);
+ g_assert (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE);
+ scontext = (GstMatroskaTrackSubtitleContext *) context;
+
+ structure = gst_caps_get_structure (caps, 0);
+ mimetype = gst_structure_get_name (structure);
+
+ /* general setup */
+ scontext->check_utf8 = 1;
+ scontext->invalid_utf8 = 0;
+ context->default_duration = 0;
+
+ /* TODO: - other format than Kate */
+
+ if (!strcmp (mimetype, "subtitle/x-kate")) {
+ const GValue *streamheader;
+
+ context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_SUBTITLE_KATE);
+
+ if (context->codec_priv != NULL) {
+ g_free (context->codec_priv);
+ context->codec_priv = NULL;
+ context->codec_priv_size = 0;
+ }
+
+ streamheader = gst_structure_get_value (structure, "streamheader");
+ if (!kate_streamheader_to_codecdata (streamheader, context)) {
+ GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
+ ("kate stream headers missing or malformed"));
+ return FALSE;
+ }
+ return TRUE;
+ }
+
return FALSE;
}