From 19b7001bf99ea37a91af6061ca98e50b08064017 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Sun, 15 Feb 2009 18:49:44 +0000 Subject: matroska: add kate subtitle support to matroska muxer and demuxer See #525743. --- gst/matroska/matroska-demux.c | 6 +- gst/matroska/matroska-ids.h | 1 + gst/matroska/matroska-mux.c | 179 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) (limited to 'gst/matroska') 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; @@ -1074,6 +1181,27 @@ theora_streamheader_to_codecdata (const GValue * streamheader, return TRUE; } +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; } -- cgit