mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-05 06:58:56 +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_PAD_SOMETIMES,
|
||||||
GST_STATIC_CAPS ("text/plain; application/x-ssa; application/x-ass; "
|
GST_STATIC_CAPS ("text/plain; application/x-ssa; application/x-ass; "
|
||||||
"application/x-usf; video/x-dvd-subpicture; "
|
"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 *
|
static GstFlowReturn gst_matroska_demux_parse_contents (GstMatroskaDemux *
|
||||||
|
@ -5771,6 +5772,9 @@ gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
|
||||||
((GstMatroskaTrackContext *) subtitlecontext)->send_dvd_event = TRUE;
|
((GstMatroskaTrackContext *) subtitlecontext)->send_dvd_event = TRUE;
|
||||||
} else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_HDMVPGS)) {
|
} else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_SUBTITLE_HDMVPGS)) {
|
||||||
caps = gst_caps_new_simple ("subpicture/x-pgs", NULL);
|
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 {
|
} else {
|
||||||
GST_DEBUG ("Unknown subtitle stream: codec_id='%s'", codec_id);
|
GST_DEBUG ("Unknown subtitle stream: codec_id='%s'", codec_id);
|
||||||
caps = gst_caps_new_simple ("application/x-subtitle-unknown", NULL);
|
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_VOBSUB "S_VOBSUB"
|
||||||
#define GST_MATROSKA_CODEC_ID_SUBTITLE_HDMVPGS "S_HDMV/PGS"
|
#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_BMP "S_IMAGE/BMP"
|
||||||
|
#define GST_MATROSKA_CODEC_ID_SUBTITLE_KATE "S_KATE"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Matroska tags. Strings.
|
* Matroska tags. Strings.
|
||||||
|
|
|
@ -228,6 +228,8 @@ static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
GstMatroskaTrackContext * context);
|
GstMatroskaTrackContext * context);
|
||||||
static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
|
static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
GstMatroskaTrackContext * context);
|
GstMatroskaTrackContext * context);
|
||||||
|
static gboolean kate_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
|
GstMatroskaTrackContext * context);
|
||||||
static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
|
static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
GstMatroskaTrackContext * context);
|
GstMatroskaTrackContext * context);
|
||||||
|
|
||||||
|
@ -897,6 +899,109 @@ skip_details:
|
||||||
return FALSE;
|
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
|
static gboolean
|
||||||
xiph3_streamheader_to_codecdata (const GValue * streamheader,
|
xiph3_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
GstMatroskaTrackContext * context, GstBuffer ** p_buf0)
|
GstMatroskaTrackContext * context, GstBuffer ** p_buf0)
|
||||||
|
@ -994,6 +1099,7 @@ vorbis_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
{
|
{
|
||||||
GstBuffer *buf0 = NULL;
|
GstBuffer *buf0 = NULL;
|
||||||
|
|
||||||
|
/* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
|
||||||
if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
|
if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -1023,6 +1129,7 @@ theora_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
{
|
{
|
||||||
GstBuffer *buf0 = NULL;
|
GstBuffer *buf0 = NULL;
|
||||||
|
|
||||||
|
/* FIXME: change to use xiphN_streamheader_to_codecdata() after release */
|
||||||
if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
|
if (!xiph3_streamheader_to_codecdata (streamheader, context, &buf0))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -1074,6 +1181,27 @@ theora_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
return TRUE;
|
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
|
static gboolean
|
||||||
flac_streamheader_to_codecdata (const GValue * streamheader,
|
flac_streamheader_to_codecdata (const GValue * streamheader,
|
||||||
GstMatroskaTrackContext * context)
|
GstMatroskaTrackContext * context)
|
||||||
|
@ -1538,6 +1666,57 @@ gst_matroska_mux_subtitle_pad_setcaps (GstPad * pad, GstCaps * caps)
|
||||||
* no single subtitle creation element in GStreamer,
|
* no single subtitle creation element in GStreamer,
|
||||||
* neither do I know how subtitling works at all. */
|
* 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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue