matroska: add kate subtitle support to matroska muxer and demuxer

See #525743.
This commit is contained in:
Vincent Penquerc'h 2009-02-15 18:49:44 +00:00 committed by Tim-Philipp Müller
parent b0bcb27517
commit 19b7001bf9
3 changed files with 185 additions and 1 deletions

View file

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

View file

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

View file

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