mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-04 05:22:30 +00:00
matroska: add kate subtitle support to matroska muxer and demuxer
See #525743.
This commit is contained in:
parent
b0bcb27517
commit
19b7001bf9
3 changed files with 185 additions and 1 deletions
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue