ttml: Add plugin that supports TTML subtitles

Add a parser (ttmlparse) and renderer (ttmlrender) element that handle
subtitles that use the EBU-TT-D profile of TTML1.

https://bugzilla.gnome.org/show_bug.cgi?id=758232
This commit is contained in:
Chris Bass 2016-06-29 09:58:38 +01:00 committed by Sebastian Dröge
parent 280b4ac2ff
commit d82ae6949f
17 changed files with 6338 additions and 2 deletions

View file

@ -2456,6 +2456,16 @@ AG_GST_CHECK_FEATURE(DTLS, [DTLS plugin], dtls, [
])
])
dnl *** ttml ***
translit(dnm, m, l) AM_CONDITIONAL(USE_TTML, true)
AG_GST_CHECK_FEATURE(TTML, [TTML plugin], ttml, [
PKG_CHECK_MODULES(TTML, [ libxml-2.0 pango cairo pangocairo ], [
HAVE_TTML="yes"
], [
HAVE_TTML="no"
])
])
dnl *** linsys ***
translit(dnm, m, l) AM_CONDITIONAL(USE_LINSYS, true)
AG_GST_CHECK_FEATURE(LINSYS, [Linear Systems SDI plugin], linsys, [
@ -3529,6 +3539,7 @@ AM_CONDITIONAL(USE_OPENH264, false)
AM_CONDITIONAL(USE_X265, false)
AM_CONDITIONAL(USE_DTLS, false)
AM_CONDITIONAL(USE_VULKAN, false)
AM_CONDITIONAL(USE_TTML, false)
fi dnl of EXT plugins
@ -3841,6 +3852,7 @@ ext/xvid/Makefile
ext/zbar/Makefile
ext/dtls/Makefile
ext/webrtcdsp/Makefile
ext/ttml/Makefile
po/Makefile.in
docs/Makefile
docs/plugins/Makefile

View file

@ -152,6 +152,8 @@
<xi:include href="xml/element-wavescope.xml" />
<xi:include href="xml/element-webrtcdsp.xml" />
<xi:include href="xml/element-webrtcechoprobe.xml" />
<xi:include href="xml/element-ttmlparse.xml" />
<xi:include href="xml/element-ttmlrender.xml" />
</chapter>
<chapter>
@ -203,5 +205,6 @@
<xi:include href="xml/plugin-voamrwbenc.xml" />
<xi:include href="xml/plugin-webrtcdsp.xml" />
<xi:include href="xml/plugin-zbar.xml" />
<xi:include href="xml/plugin-ttmlsubs.xml" />
</chapter>
</book>

View file

@ -4805,3 +4805,22 @@ GST_TYPE_FFECTS_XRAY
gst_ffects_xray_get_type
</SECTION>
<SECTION>
<FILE>element-ttmlparse</FILE>
<TITLE>ttmlparse</TITLE>
GstTtmlParse
<SUBSECTION Standard>
GstTtmlParseClass
GST_TYPE_TTML_PARSE
gst_ttml_parse_get_type
</SECTION>
<SECTION>
<FILE>element-ttmlrender</FILE>
<TITLE>ttmlrender</TITLE>
GstTtmlRender
<SUBSECTION Standard>
GstTtmlRenderClass
GST_TYPE_TTML_RENDER
gst_ttml_render_get_type
</SECTION>

View file

@ -0,0 +1,61 @@
<plugin>
<name>ttmlsubs</name>
<description>TTML subtitle handling</description>
<filename>../../ext/ttml/.libs/libgstttmlsubs.so</filename>
<basename>libgstttmlsubs.so</basename>
<version>1.9.90</version>
<license>LGPL</license>
<source>gst-plugins-bad</source>
<package>gst-ttml</package>
<origin>http://www.bbc.co.uk/rd</origin>
<elements>
<element>
<name>ttmlparse</name>
<longname>TTML subtitle parser</longname>
<class>Codec/Parser/Subtitle</class>
<description>Parses TTML subtitle files</description>
<author>GStreamer maintainers &lt;gstreamer-devel@lists.sourceforge.net&gt;, Chris Bass &lt;dash@rd.bbc.co.uk&gt;</author>
<pads>
<caps>
<name>sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>application/ttml+xml</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>text/x-raw(meta:GstSubtitleMeta)</details>
</caps>
</pads>
</element>
<element>
<name>ttmlrender</name>
<longname>TTML subtitle renderer</longname>
<class>Overlay/Subtitle</class>
<description>Renders timed-text subtitles on top of video buffers</description>
<author>David Schleef &lt;ds@schleef.org&gt;, Zeeshan Ali &lt;zeeshan.ali@nokia.com&gt;, Chris Bass &lt;dash@rd.bbc.co.uk&gt;</author>
<pads>
<caps>
<name>text_sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>text/x-raw(meta:GstSubtitleMeta)</details>
</caps>
<caps>
<name>video_sink</name>
<direction>sink</direction>
<presence>always</presence>
<details>video/x-raw, format=(string){ BGRx, RGBx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, I420, YV12, AYUV, YUY2, UYVY, v308, Y41B, Y42B, Y444, NV12, NV21, A420, YUV9, YVU9, IYU1, GRAY8 }, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw(ANY), format=(string){ I420, YV12, YUY2, UYVY, AYUV, RGBx, BGRx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, Y41B, Y42B, YVYU, Y444, v210, v216, NV12, NV21, NV16, NV61, NV24, GRAY8, GRAY16_BE, GRAY16_LE, v308, IYU2, RGB16, BGR16, RGB15, BGR15, UYVP, A420, RGB8P, YUV9, YVU9, IYU1, ARGB64, AYUV64, r210, I420_10LE, I420_10BE, I422_10LE, I422_10BE, Y444_10LE, Y444_10BE, GBR, GBR_10LE, GBR_10BE, NV12_64Z32, A420_10LE, A420_10BE, A422_10LE, A422_10BE, A444_10LE, A444_10BE, P010_10LE, P010_10BE }, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
<caps>
<name>src</name>
<direction>source</direction>
<presence>always</presence>
<details>video/x-raw, format=(string){ BGRx, RGBx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, I420, YV12, AYUV, YUY2, UYVY, v308, Y41B, Y42B, Y444, NV12, NV21, A420, YUV9, YVU9, IYU1, GRAY8 }, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw(ANY), format=(string){ I420, YV12, YUY2, UYVY, AYUV, RGBx, BGRx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, Y41B, Y42B, YVYU, Y444, v210, v216, NV12, NV21, NV16, NV61, NV24, GRAY8, GRAY16_BE, GRAY16_LE, v308, IYU2, RGB16, BGR16, RGB15, BGR15, UYVP, A420, RGB8P, YUV9, YVU9, IYU1, ARGB64, AYUV64, r210, I420_10LE, I420_10BE, I422_10LE, I422_10BE, Y444_10LE, Y444_10BE, GBR, GBR_10LE, GBR_10BE, NV12_64Z32, A420_10LE, A420_10BE, A422_10LE, A422_10BE, A444_10LE, A444_10BE, P010_10LE, P010_10BE }, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]</details>
</caps>
</pads>
</element>
</elements>
</plugin>

View file

@ -424,6 +424,12 @@ else
WEBRTCDSP_DIR=
endif
if USE_TTML
TTML_DIR=ttml
else
TTML_DIR=
endif
SUBDIRS=\
$(VOAACENC_DIR) \
$(ASSRENDER_DIR) \
@ -495,7 +501,8 @@ SUBDIRS=\
$(X265_DIR) \
$(DTLS_DIR) \
$(VULKAN_DIR) \
$(WEBRTCDSP_DIR)
$(WEBRTCDSP_DIR) \
$(TTML_DIR)
DIST_SUBDIRS = \
assrender \
@ -565,6 +572,7 @@ DIST_SUBDIRS = \
x265 \
dtls \
vulkan \
webrtcdsp
webrtcdsp \
ttml
include $(top_srcdir)/common/parallel-subdirs.mak

36
ext/ttml/Makefile.am Normal file
View file

@ -0,0 +1,36 @@
plugin_LTLIBRARIES = libgstttmlsubs.la
# sources used to compile this plug-in
libgstttmlsubs_la_SOURCES = \
subtitle.c \
subtitlemeta.c \
gstttmlparse.c \
gstttmlparse.h \
ttmlparse.c \
ttmlparse.h \
gstttmlrender.c \
gstttmlplugin.c
# compiler and linker flags used to compile this plugin, set in configure.ac
libgstttmlsubs_la_CFLAGS = \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(TTML_CFLAGS)
libgstttmlsubs_la_LIBADD = \
$(GST_BASE_LIBS) \
$(GST_LIBS) \
-lgstvideo-$(GST_API_VERSION) \
$(TTML_LIBS)
libgstttmlsubs_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstttmlsubs_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
# headers we need but don't want installed
noinst_HEADERS = \
subtitle.h \
subtitlemeta.h \
gstttmlparse.h \
ttmlparse.h \
gstttmlrender.h

570
ext/ttml/gstttmlparse.c Normal file
View file

@ -0,0 +1,570 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2004 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* Copyright (C) <2015> British Broadcasting Corporation <dash@rd.bbc.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-ttmlparse
*
* Parses timed text subtitle files described using Timed Text Markup Language
* (TTML). Currently, only the EBU-TT-D profile of TTML, designed for
* distribution of subtitles over IP, is supported.
*
* The parser outputs a #GstBuffer for each scene in the input TTML file, a
* scene being a period of time during which a static set of subtitles should
* be visible. The parser places each text element within a scene into its own
* #GstMemory within the scene's buffer, and attaches metadata to the buffer
* describing the styling and layout associated with all the contained text
* elements. A downstream renderer element uses this information to correctly
* render the text on top of video frames.
*
* <refsect2>
* <title>Example launch lines</title>
* |[
* gst-launch-1.0 filesrc location=<media file location> ! video/quicktime ! qtdemux name=q ttmlrender name=r q. ! queue ! h264parse ! avdec_h264 ! autovideoconvert ! r.video_sink filesrc location=<subtitle file location> blocksize=16777216 ! queue ! ttmlparse ! r.text_sink r. ! ximagesink q. ! queue ! aacparse ! avdec_aac ! audioconvert ! alsasink
* ]| Parse and render TTML subtitles contained in a single XML file over an
* MP4 stream containing H.264 video and AAC audio.
* </refsect2>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <glib.h>
#include "gstttmlparse.h"
#include "ttmlparse.h"
GST_DEBUG_CATEGORY_EXTERN (ttmlparse_debug);
#define GST_CAT_DEFAULT ttmlparse_debug
#define DEFAULT_ENCODING NULL
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/ttml+xml")
);
static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("text/x-raw(meta:GstSubtitleMeta)")
);
static gboolean gst_ttml_parse_src_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_ttml_parse_src_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static gboolean gst_ttml_parse_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static GstStateChangeReturn gst_ttml_parse_change_state (GstElement * element,
GstStateChange transition);
static GstFlowReturn gst_ttml_parse_chain (GstPad * sinkpad, GstObject * parent,
GstBuffer * buf);
#define gst_ttml_parse_parent_class parent_class
G_DEFINE_TYPE (GstTtmlParse, gst_ttml_parse, GST_TYPE_ELEMENT);
static void
gst_ttml_parse_dispose (GObject * object)
{
GstTtmlParse *ttmlparse = GST_TTML_PARSE (object);
GST_DEBUG_OBJECT (ttmlparse, "cleaning up subtitle parser");
g_free (ttmlparse->encoding);
ttmlparse->encoding = NULL;
g_free (ttmlparse->detected_encoding);
ttmlparse->detected_encoding = NULL;
if (ttmlparse->adapter) {
g_object_unref (ttmlparse->adapter);
ttmlparse->adapter = NULL;
}
if (ttmlparse->textbuf) {
g_string_free (ttmlparse->textbuf, TRUE);
ttmlparse->textbuf = NULL;
}
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
}
static void
gst_ttml_parse_class_init (GstTtmlParseClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
object_class->dispose = gst_ttml_parse_dispose;
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_templ));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_templ));
gst_element_class_set_static_metadata (element_class,
"TTML subtitle parser", "Codec/Parser/Subtitle",
"Parses TTML subtitle files",
"GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>, "
"Chris Bass <dash@rd.bbc.co.uk>");
element_class->change_state = gst_ttml_parse_change_state;
}
static void
gst_ttml_parse_init (GstTtmlParse * ttmlparse)
{
ttmlparse->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
gst_pad_set_chain_function (ttmlparse->sinkpad,
GST_DEBUG_FUNCPTR (gst_ttml_parse_chain));
gst_pad_set_event_function (ttmlparse->sinkpad,
GST_DEBUG_FUNCPTR (gst_ttml_parse_sink_event));
gst_element_add_pad (GST_ELEMENT (ttmlparse), ttmlparse->sinkpad);
ttmlparse->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
gst_pad_set_event_function (ttmlparse->srcpad,
GST_DEBUG_FUNCPTR (gst_ttml_parse_src_event));
gst_pad_set_query_function (ttmlparse->srcpad,
GST_DEBUG_FUNCPTR (gst_ttml_parse_src_query));
gst_element_add_pad (GST_ELEMENT (ttmlparse), ttmlparse->srcpad);
ttmlparse->textbuf = g_string_new (NULL);
gst_segment_init (&ttmlparse->segment, GST_FORMAT_TIME);
ttmlparse->need_segment = TRUE;
ttmlparse->encoding = g_strdup (DEFAULT_ENCODING);
ttmlparse->detected_encoding = NULL;
ttmlparse->adapter = gst_adapter_new ();
}
/*
* Source pad functions.
*/
static gboolean
gst_ttml_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
GstTtmlParse *self = GST_TTML_PARSE (parent);
gboolean ret = FALSE;
GST_DEBUG ("Handling %s query", GST_QUERY_TYPE_NAME (query));
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:{
GstFormat fmt;
gst_query_parse_position (query, &fmt, NULL);
if (fmt != GST_FORMAT_TIME) {
ret = gst_pad_peer_query (self->sinkpad, query);
} else {
ret = TRUE;
gst_query_set_position (query, GST_FORMAT_TIME, self->segment.position);
}
break;
}
case GST_QUERY_SEEKING:
{
GstFormat fmt;
gboolean seekable = FALSE;
ret = TRUE;
gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
if (fmt == GST_FORMAT_TIME) {
GstQuery *peerquery = gst_query_new_seeking (GST_FORMAT_BYTES);
seekable = gst_pad_peer_query (self->sinkpad, peerquery);
if (seekable)
gst_query_parse_seeking (peerquery, NULL, &seekable, NULL, NULL);
gst_query_unref (peerquery);
}
gst_query_set_seeking (query, fmt, seekable, seekable ? 0 : -1, -1);
break;
}
default:
ret = gst_pad_query_default (pad, parent, query);
break;
}
return ret;
}
static gboolean
gst_ttml_parse_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstTtmlParse *self = GST_TTML_PARSE (parent);
gboolean ret = FALSE;
GST_DEBUG ("Handling %s event", GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
{
GstFormat format;
GstSeekFlags flags;
GstSeekType start_type, stop_type;
gint64 start, stop;
gdouble rate;
gboolean update;
gst_event_parse_seek (event, &rate, &format, &flags,
&start_type, &start, &stop_type, &stop);
if (format != GST_FORMAT_TIME) {
GST_WARNING_OBJECT (self, "we only support seeking in TIME format");
gst_event_unref (event);
goto beach;
}
/* Convert that seek to a seeking in bytes at position 0,
FIXME: could use an index */
ret = gst_pad_push_event (self->sinkpad,
gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, 0));
if (ret) {
/* Apply the seek to our segment */
gst_segment_do_seek (&self->segment, rate, format, flags,
start_type, start, stop_type, stop, &update);
GST_DEBUG_OBJECT (self, "segment after seek: %" GST_SEGMENT_FORMAT,
&self->segment);
self->need_segment = TRUE;
} else {
GST_WARNING_OBJECT (self, "seek to 0 bytes failed");
}
gst_event_unref (event);
break;
}
default:
ret = gst_pad_event_default (pad, parent, event);
break;
}
beach:
return ret;
}
static gchar *
gst_convert_to_utf8 (const gchar * str, gsize len, const gchar * encoding,
gsize * consumed, GError ** err)
{
gchar *ret = NULL;
*consumed = 0;
/* The char cast is necessary in glib < 2.24 */
ret =
g_convert_with_fallback (str, len, "UTF-8", encoding, (char *) "*",
consumed, NULL, err);
if (ret == NULL)
return ret;
/* + 3 to skip UTF-8 BOM if it was added */
len = strlen (ret);
if (len >= 3 && (guint8) ret[0] == 0xEF && (guint8) ret[1] == 0xBB
&& (guint8) ret[2] == 0xBF)
memmove (ret, ret + 3, len + 1 - 3);
return ret;
}
static gchar *
detect_encoding (const gchar * str, gsize len)
{
if (len >= 3 && (guint8) str[0] == 0xEF && (guint8) str[1] == 0xBB
&& (guint8) str[2] == 0xBF)
return g_strdup ("UTF-8");
if (len >= 2 && (guint8) str[0] == 0xFE && (guint8) str[1] == 0xFF)
return g_strdup ("UTF-16BE");
if (len >= 2 && (guint8) str[0] == 0xFF && (guint8) str[1] == 0xFE)
return g_strdup ("UTF-16LE");
if (len >= 4 && (guint8) str[0] == 0x00 && (guint8) str[1] == 0x00
&& (guint8) str[2] == 0xFE && (guint8) str[3] == 0xFF)
return g_strdup ("UTF-32BE");
if (len >= 4 && (guint8) str[0] == 0xFF && (guint8) str[1] == 0xFE
&& (guint8) str[2] == 0x00 && (guint8) str[3] == 0x00)
return g_strdup ("UTF-32LE");
return NULL;
}
static gchar *
convert_encoding (GstTtmlParse * self, const gchar * str, gsize len,
gsize * consumed)
{
const gchar *encoding;
GError *err = NULL;
gchar *ret = NULL;
*consumed = 0;
/* First try any detected encoding */
if (self->detected_encoding) {
ret =
gst_convert_to_utf8 (str, len, self->detected_encoding, consumed, &err);
if (!err)
return ret;
GST_WARNING_OBJECT (self, "could not convert string from '%s' to UTF-8: %s",
self->detected_encoding, err->message);
g_free (self->detected_encoding);
self->detected_encoding = NULL;
g_error_free (err);
}
/* Otherwise check if it's UTF8 */
if (self->valid_utf8) {
if (g_utf8_validate (str, len, NULL)) {
GST_LOG_OBJECT (self, "valid UTF-8, no conversion needed");
*consumed = len;
return g_strndup (str, len);
}
GST_INFO_OBJECT (self, "invalid UTF-8!");
self->valid_utf8 = FALSE;
}
/* Else try fallback */
encoding = self->encoding;
if (encoding == NULL || *encoding == '\0') {
/* if local encoding is UTF-8 and no encoding specified
* via the environment variable, assume ISO-8859-15 */
if (g_get_charset (&encoding)) {
encoding = "ISO-8859-15";
}
}
ret = gst_convert_to_utf8 (str, len, encoding, consumed, &err);
if (err) {
GST_WARNING_OBJECT (self, "could not convert string from '%s' to UTF-8: %s",
encoding, err->message);
g_error_free (err);
/* invalid input encoding, fall back to ISO-8859-15 (always succeeds) */
ret = gst_convert_to_utf8 (str, len, "ISO-8859-15", consumed, NULL);
}
GST_LOG_OBJECT (self,
"successfully converted %" G_GSIZE_FORMAT " characters from %s to UTF-8"
"%s", len, encoding, (err) ? " , using ISO-8859-15 as fallback" : "");
return ret;
}
static GstCaps *
gst_ttml_parse_get_src_caps (GstTtmlParse * self)
{
GstCaps *caps;
GstCapsFeatures *features = gst_caps_features_new ("meta:GstSubtitleMeta",
NULL);
caps = gst_caps_new_empty_simple ("text/x-raw");
gst_caps_set_features (caps, 0, features);
return caps;
}
static void
feed_textbuf (GstTtmlParse * self, GstBuffer * buf)
{
gboolean discont;
gsize consumed;
gchar *input = NULL;
const guint8 *data;
gsize avail;
discont = GST_BUFFER_IS_DISCONT (buf);
if (GST_BUFFER_OFFSET_IS_VALID (buf) &&
GST_BUFFER_OFFSET (buf) != self->offset) {
self->offset = GST_BUFFER_OFFSET (buf);
discont = TRUE;
}
if (discont) {
GST_INFO ("discontinuity");
/* flush the parser state */
g_string_truncate (self->textbuf, 0);
gst_adapter_clear (self->adapter);
/* we could set a flag to make sure that the next buffer we push out also
* has the DISCONT flag set, but there's no point really given that it's
* subtitles which are discontinuous by nature. */
}
self->offset += gst_buffer_get_size (buf);
gst_adapter_push (self->adapter, buf);
avail = gst_adapter_available (self->adapter);
data = gst_adapter_map (self->adapter, avail);
input = convert_encoding (self, (const gchar *) data, avail, &consumed);
if (input && consumed > 0) {
if (self->textbuf) {
g_string_free (self->textbuf, TRUE);
self->textbuf = NULL;
}
self->textbuf = g_string_new (input);
gst_adapter_unmap (self->adapter);
gst_adapter_flush (self->adapter, consumed);
} else {
gst_adapter_unmap (self->adapter);
}
g_free (input);
}
static GstFlowReturn
handle_buffer (GstTtmlParse * self, GstBuffer * buf)
{
GstFlowReturn ret = GST_FLOW_OK;
GstCaps *caps = NULL;
GList *subtitle_list, *subtitle;
GstClockTime begin = GST_BUFFER_PTS (buf);
GstClockTime duration = GST_BUFFER_DURATION (buf);
if (self->first_buffer) {
GstMapInfo map;
gst_buffer_map (buf, &map, GST_MAP_READ);
self->detected_encoding = detect_encoding ((gchar *) map.data, map.size);
gst_buffer_unmap (buf, &map);
self->first_buffer = FALSE;
}
feed_textbuf (self, buf);
if (!(caps = gst_ttml_parse_get_src_caps (self)))
return GST_FLOW_EOS;
/* Push newsegment if needed */
if (self->need_segment) {
GST_LOG_OBJECT (self, "pushing newsegment event with %" GST_SEGMENT_FORMAT,
&self->segment);
gst_pad_push_event (self->srcpad, gst_event_new_segment (&self->segment));
self->need_segment = FALSE;
}
subtitle_list = ttml_parse (self->textbuf->str, begin, duration);
for (subtitle = subtitle_list; subtitle; subtitle = subtitle->next) {
GstBuffer *op_buffer = subtitle->data;
self->segment.position = GST_BUFFER_PTS (op_buffer);
ret = gst_pad_push (self->srcpad, op_buffer);
if (ret != GST_FLOW_OK)
GST_DEBUG_OBJECT (self, "flow: %s", gst_flow_get_name (ret));
}
g_list_free (subtitle_list);
return ret;
}
static GstFlowReturn
gst_ttml_parse_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * buf)
{
GstTtmlParse *self = GST_TTML_PARSE (parent);
return handle_buffer (self, buf);
}
static gboolean
gst_ttml_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstTtmlParse *self = GST_TTML_PARSE (parent);
gboolean ret = FALSE;
GST_DEBUG ("Handling %s event", GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEGMENT:
{
const GstSegment *s;
gst_event_parse_segment (event, &s);
if (s->format == GST_FORMAT_TIME)
gst_event_copy_segment (event, &self->segment);
GST_DEBUG_OBJECT (self, "newsegment (%s)",
gst_format_get_name (self->segment.format));
/* if not time format, we'll either start with a 0 timestamp anyway or
* it's following a seek in which case we'll have saved the requested
* seek segment and don't want to overwrite it (remember that on a seek
* we always just seek back to the start in BYTES format and just throw
* away all text that's before the requested position; if the subtitles
* come from an upstream demuxer, it won't be able to handle our BYTES
* seek request and instead send us a newsegment from the seek request
* it received via its video pads instead, so all is fine then too) */
ret = TRUE;
gst_event_unref (event);
break;
}
default:
ret = gst_pad_event_default (pad, parent, event);
break;
}
return ret;
}
static GstStateChangeReturn
gst_ttml_parse_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstTtmlParse *self = GST_TTML_PARSE (element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
/* format detection will init the parser state */
self->offset = 0;
self->valid_utf8 = TRUE;
self->first_buffer = TRUE;
g_free (self->detected_encoding);
self->detected_encoding = NULL;
g_string_truncate (self->textbuf, 0);
gst_adapter_clear (self->adapter);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
default:
break;
}
return ret;
}

76
ext/ttml/gstttmlparse.h Normal file
View file

@ -0,0 +1,76 @@
/* GStreamer
* Copyright (C) <2002> David A. Schleef <ds@schleef.org>
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2015> British Broadcasting Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_TTML_PARSE_H__
#define __GST_TTML_PARSE_H__
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
G_BEGIN_DECLS
#define GST_TYPE_TTML_PARSE \
(gst_ttml_parse_get_type ())
#define GST_TTML_PARSE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TTML_PARSE, GstTtmlParse))
#define GST_TTML_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TTML_PARSE, GstTtmlParseClass))
#define GST_IS_TTML_PARSE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TTML_PARSE))
#define GST_IS_TTML_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TTML_PARSE))
typedef struct _GstTtmlParse GstTtmlParse;
typedef struct _GstTtmlParseClass GstTtmlParseClass;
struct _GstTtmlParse {
GstElement element;
GstPad *sinkpad, *srcpad;
/* contains the input in the input encoding */
GstAdapter *adapter;
/* contains the UTF-8 decoded input */
GString *textbuf;
/* seek */
guint64 offset;
/* Segment */
GstSegment segment;
gboolean need_segment;
gboolean valid_utf8;
gchar *detected_encoding;
gchar *encoding;
gboolean first_buffer;
};
struct _GstTtmlParseClass {
GstElementClass parent_class;
};
GType gst_ttml_parse_get_type (void);
G_END_DECLS
#endif /* __GST_TTML_PARSE_H__ */

53
ext/ttml/gstttmlplugin.c Normal file
View file

@ -0,0 +1,53 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2004 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* Copyright (C) <2015> British Broadcasting Corporation <dash@rd.bbc.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "gstttmlparse.h"
#include "gstttmlrender.h"
GST_DEBUG_CATEGORY (ttmlparse_debug);
GST_DEBUG_CATEGORY (ttmlrender_debug);
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_element_register (plugin, "ttmlparse", GST_RANK_PRIMARY,
GST_TYPE_TTML_PARSE))
return FALSE;
if (!gst_element_register (plugin, "ttmlrender", GST_RANK_PRIMARY,
GST_TYPE_TTML_RENDER))
return FALSE;
GST_DEBUG_CATEGORY_INIT (ttmlparse_debug, "ttmlparse", 0, "TTML parser");
GST_DEBUG_CATEGORY_INIT (ttmlrender_debug, "ttmlrender", 0, "TTML renderer");
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
ttmlsubs,
"TTML subtitle handling",
plugin_init, VERSION, "LGPL", "gst-ttml", "http://www.bbc.co.uk/rd")

2456
ext/ttml/gstttmlrender.c Normal file

File diff suppressed because it is too large Load diff

124
ext/ttml/gstttmlrender.h Normal file
View file

@ -0,0 +1,124 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2003> David Schleef <ds@schleef.org>
* Copyright (C) <2006> Julien Moutte <julien@moutte.net>
* Copyright (C) <2006> Zeeshan Ali <zeeshan.ali@nokia.com>
* Copyright (C) <2006-2008> Tim-Philipp Müller <tim centricular net>
* Copyright (C) <2009> Young-Ho Cha <ganadist@gmail.com>
* Copyright (C) <2015> British Broadcasting Corporation <dash@rd.bbc.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_TTML_RENDER_H__
#define __GST_TTML_RENDER_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include <pango/pango.h>
G_BEGIN_DECLS
#define GST_TYPE_TTML_RENDER (gst_ttml_render_get_type())
#define GST_TTML_RENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
GST_TYPE_TTML_RENDER, GstTtmlRender))
#define GST_TTML_RENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\
GST_TYPE_TTML_RENDER, \
GstTtmlRenderClass))
#define GST_TTML_RENDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
GST_TYPE_TTML_RENDER, \
GstTtmlRenderClass))
#define GST_IS_TTML_RENDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
GST_TYPE_TTML_RENDER))
#define GST_IS_TTML_RENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\
GST_TYPE_TTML_RENDER))
typedef struct _GstTtmlRender GstTtmlRender;
typedef struct _GstTtmlRenderClass GstTtmlRenderClass;
typedef struct _GstTtmlRenderRenderedImage GstTtmlRenderRenderedImage;
typedef struct _GstTtmlRenderRenderedText GstTtmlRenderRenderedText;
struct _GstTtmlRenderRenderedImage {
GstBuffer *image;
gint x;
gint y;
guint width;
guint height;
};
struct _GstTtmlRenderRenderedText {
GstTtmlRenderRenderedImage *text_image;
/* In order to get the positions of characters within a paragraph rendered by
* pango we need to retain a reference to the PangoLayout object that was
* used to render that paragraph. */
PangoLayout *layout;
/* The coordinates in @layout will be offset horizontally with respect to the
* position of those characters in @text_image. Store that offset here so
* that the information in @layout can be used to locate the position and
* extent of text areas in @text_image. */
guint horiz_offset;
};
struct _GstTtmlRender {
GstElement element;
GstPad *video_sinkpad;
GstPad *text_sinkpad;
GstPad *srcpad;
GstSegment segment;
GstSegment text_segment;
GstBuffer *text_buffer;
gboolean text_linked;
gboolean video_flushing;
gboolean video_eos;
gboolean text_flushing;
gboolean text_eos;
GMutex lock;
GCond cond; /* to signal removal of a queued text
* buffer, arrival of a text buffer,
* a text segment update, or a change
* in status (e.g. shutdown, flushing) */
GstVideoInfo info;
GstVideoFormat format;
gint width;
gint height;
gboolean want_background;
gboolean wait_text;
gboolean need_render;
GList * compositions;
};
struct _GstTtmlRenderClass {
GstElementClass parent_class;
PangoContext *pango_context;
GMutex *pango_lock;
};
GType gst_ttml_render_get_type(void) G_GNUC_CONST;
G_END_DECLS
#endif /* __GST_TTML_RENDER_H */

312
ext/ttml/subtitle.c Normal file
View file

@ -0,0 +1,312 @@
/* GStreamer
* Copyright (C) <2015> British Broadcasting Corporation
* Author: Chris Bass <dash@rd.bbc.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:gstsubtitle
* @short_description: Library for describing sets of static subtitles.
*
* This library enables the description of static text scenes made up of a
* number of regions, which may contain a number of block and inline text
* elements. It is derived from the concepts and features defined in the Timed
* Text Markup Language 1 (TTML1), Second Edition
* (http://www.w3.org/TR/ttaf1-dfxp), and the EBU-TT-D profile of TTML1
* (https://tech.ebu.ch/files/live/sites/tech/files/shared/tech/tech3380.pdf).
*/
#include "subtitle.h"
/**
* gst_subtitle_style_set_new:
*
* Create a new #GstSubtitleStyleSet with default values for all properties.
*
* Returns: (transfer full): A newly-allocated #GstSubtitleStyleSet.
*/
GstSubtitleStyleSet *
gst_subtitle_style_set_new (void)
{
GstSubtitleStyleSet *ret = g_slice_new0 (GstSubtitleStyleSet);
GstSubtitleColor white = { 255, 255, 255, 255 };
GstSubtitleColor transparent = { 0, 0, 0, 0 };
ret->font_family = g_strdup ("default");
ret->font_size = 1.0;
ret->line_height = 1.25;
ret->color = white;
ret->background_color = transparent;
ret->line_padding = 0.0;
ret->origin_x = ret->origin_y = 0.0;
ret->extent_w = ret->extent_h = 0.0;
ret->padding_start = ret->padding_end
= ret->padding_before = ret->padding_after = 0.0;
return ret;
}
/**
* gst_subtitle_style_set_free:
* @style_set: A #GstSubtitleStyleSet.
*
* Free @style_set and its associated memory.
*/
void
gst_subtitle_style_set_free (GstSubtitleStyleSet * style_set)
{
g_return_if_fail (style_set != NULL);
g_free (style_set->font_family);
g_slice_free (GstSubtitleStyleSet, style_set);
}
static void
_gst_subtitle_element_free (GstSubtitleElement * element)
{
g_return_if_fail (element != NULL);
gst_subtitle_style_set_free (element->style_set);
g_slice_free (GstSubtitleElement, element);
}
GST_DEFINE_MINI_OBJECT_TYPE (GstSubtitleElement, gst_subtitle_element);
/**
* gst_subtitle_element_new:
* @style_set: (transfer full): A #GstSubtitleStyleSet that defines the styling
* and layout associated with this inline text element.
* @text_index: The index within a #GstBuffer of the #GstMemory that contains
* the text of this inline text element.
*
* Allocates a new #GstSubtitleElement.
*
* Returns: (transfer full): A newly-allocated #GstSubtitleElement. Unref
* with gst_subtitle_element_unref() when no longer needed.
*/
GstSubtitleElement *
gst_subtitle_element_new (GstSubtitleStyleSet * style_set,
guint text_index, gboolean suppress_whitespace)
{
GstSubtitleElement *element;
g_return_val_if_fail (style_set != NULL, NULL);
element = g_slice_new0 (GstSubtitleElement);
gst_mini_object_init (GST_MINI_OBJECT_CAST (element), 0,
gst_subtitle_element_get_type (), NULL, NULL,
(GstMiniObjectFreeFunction) _gst_subtitle_element_free);
element->style_set = style_set;
element->text_index = text_index;
element->suppress_whitespace = suppress_whitespace;
return element;
}
static void
_gst_subtitle_block_free (GstSubtitleBlock * block)
{
g_return_if_fail (block != NULL);
gst_subtitle_style_set_free (block->style_set);
g_ptr_array_unref (block->elements);
g_slice_free (GstSubtitleBlock, block);
}
GST_DEFINE_MINI_OBJECT_TYPE (GstSubtitleBlock, gst_subtitle_block);
/**
* gst_subtitle_block_new:
* @style_set: (transfer full): A #GstSubtitleStyleSet that defines the styling
* and layout associated with this block of text elements.
*
* Allocates a new #GstSubtitleBlock.
*
* Returns: (transfer full): A newly-allocated #GstSubtitleBlock. Unref
* with gst_subtitle_block_unref() when no longer needed.
*/
GstSubtitleBlock *
gst_subtitle_block_new (GstSubtitleStyleSet * style_set)
{
GstSubtitleBlock *block;
g_return_val_if_fail (style_set != NULL, NULL);
block = g_slice_new0 (GstSubtitleBlock);
gst_mini_object_init (GST_MINI_OBJECT_CAST (block), 0,
gst_subtitle_block_get_type (), NULL, NULL,
(GstMiniObjectFreeFunction) _gst_subtitle_block_free);
block->style_set = style_set;
block->elements = g_ptr_array_new_with_free_func (
(GDestroyNotify) gst_subtitle_element_unref);
return block;
}
/**
* gst_subtitle_block_add_element:
* @block: A #GstSubtitleBlock.
* @element: (transfer full): A #GstSubtitleElement to add.
*
* Adds a #GstSubtitleElement to @block.
*/
void
gst_subtitle_block_add_element (GstSubtitleBlock * block,
GstSubtitleElement * element)
{
g_return_if_fail (block != NULL);
g_return_if_fail (element != NULL);
g_ptr_array_add (block->elements, element);
}
/**
* gst_subtitle_block_get_element_count:
* @block: A #GstSubtitleBlock.
*
* Returns: The number of #GstSubtitleElements in @block.
*/
guint
gst_subtitle_block_get_element_count (const GstSubtitleBlock * block)
{
g_return_val_if_fail (block != NULL, 0);
return block->elements->len;
}
/**
* gst_subtitle_block_get_element:
* @block: A #GstSubtitleBlock.
* @index: Index of the element to get.
*
* Gets the #GstSubtitleElement at @index in the array of elements held by
* @block.
*
* Returns: (transfer none): The #GstSubtitleElement at @index in the array of
* elements held by @block, or %NULL if @index is out-of-bounds. The
* function does not return a reference; the caller should obtain a reference
* using gst_subtitle_element_ref(), if needed.
*/
const GstSubtitleElement *
gst_subtitle_block_get_element (const GstSubtitleBlock * block, guint index)
{
g_return_val_if_fail (block != NULL, NULL);
if (index >= block->elements->len)
return NULL;
else
return g_ptr_array_index (block->elements, index);
}
static void
_gst_subtitle_region_free (GstSubtitleRegion * region)
{
g_return_if_fail (region != NULL);
gst_subtitle_style_set_free (region->style_set);
g_ptr_array_unref (region->blocks);
g_slice_free (GstSubtitleRegion, region);
}
GST_DEFINE_MINI_OBJECT_TYPE (GstSubtitleRegion, gst_subtitle_region);
/**
* gst_subtitle_region_new:
* @style_set: (transfer full): A #GstSubtitleStyleSet that defines the styling
* and layout associated with this region.
*
* Allocates a new #GstSubtitleRegion.
*
* Returns: (transfer full): A newly-allocated #GstSubtitleRegion. Unref
* with gst_subtitle_region_unref() when no longer needed.
*/
GstSubtitleRegion *
gst_subtitle_region_new (GstSubtitleStyleSet * style_set)
{
GstSubtitleRegion *region;
g_return_val_if_fail (style_set != NULL, NULL);
region = g_slice_new0 (GstSubtitleRegion);
gst_mini_object_init (GST_MINI_OBJECT_CAST (region), 0,
gst_subtitle_region_get_type (), NULL, NULL,
(GstMiniObjectFreeFunction) _gst_subtitle_region_free);
region->style_set = style_set;
region->blocks = g_ptr_array_new_with_free_func (
(GDestroyNotify) gst_subtitle_block_unref);
return region;
}
/**
* gst_subtitle_region_add_block:
* @region: A #GstSubtitleRegion.
* @block: (transfer full): A #GstSubtitleBlock which should be added
* to @region's array of blocks.
*
* Adds a #GstSubtitleBlock to the end of the array of blocks held by @region.
* @region will take ownership of @block, and will unref it when @region
* is freed.
*/
void
gst_subtitle_region_add_block (GstSubtitleRegion * region,
GstSubtitleBlock * block)
{
g_return_if_fail (region != NULL);
g_return_if_fail (block != NULL);
g_ptr_array_add (region->blocks, block);
}
/**
* gst_subtitle_region_get_block_count:
* @region: A #GstSubtitleRegion.
*
* Returns: The number of blocks in @region.
*/
guint
gst_subtitle_region_get_block_count (const GstSubtitleRegion * region)
{
g_return_val_if_fail (region != NULL, 0);
return region->blocks->len;
}
/**
* gst_subtitle_region_get_block:
* @region: A #GstSubtitleRegion.
* @index: Index of the block to get.
*
* Gets the block at @index in the array of blocks held by @region.
*
* Returns: (transfer none): The #GstSubtitleBlock at @index in the array of
* blocks held by @region, or %NULL if @index is out-of-bounds. The
* function does not return a reference; the caller should obtain a reference
* using gst_subtitle_block_ref(), if needed.
*/
const GstSubtitleBlock *
gst_subtitle_region_get_block (const GstSubtitleRegion * region, guint index)
{
g_return_val_if_fail (region != NULL, NULL);
if (index >= region->blocks->len)
return NULL;
else
return g_ptr_array_index (region->blocks, index);
}

592
ext/ttml/subtitle.h Normal file
View file

@ -0,0 +1,592 @@
/* GStreamer
* Copyright (C) <2015> British Broadcasting Corporation
* Author: Chris Bass <dash@rd.bbc.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_SUBTITLE_H__
#define __GST_SUBTITLE_H__
#include <glib.h>
#include <gst/gst.h>
#include <gst/gstminiobject.h>
G_BEGIN_DECLS
typedef struct _GstSubtitleColor GstSubtitleColor;
typedef struct _GstSubtitleStyleSet GstSubtitleStyleSet;
typedef struct _GstSubtitleElement GstSubtitleElement;
typedef struct _GstSubtitleBlock GstSubtitleBlock;
typedef struct _GstSubtitleRegion GstSubtitleRegion;
/**
* GstSubtitleWritingMode:
* @GST_SUBTITLE_WRITING_MODE_LRTB: Text progression is left-to-right,
* top-to-bottom.
* @GST_SUBTITLE_WRITING_MODE_RLTB: Text progression is right-to-left,
* top-to-bottom.
* @GST_SUBTITLE_WRITING_MODE_TBRL: Text progression is top-to-bottom,
* right-to-left.
* @GST_SUBTITLE_WRITING_MODE_TBLR: Text progression is top-to-bottom,
* left-to-right.
*
* Writing mode of text content. The values define the direction of progression
* of both inline text (#GstSubtitleElements) and blocks of text
* (#GstSubtitleBlocks).
*/
typedef enum {
GST_SUBTITLE_WRITING_MODE_LRTB,
GST_SUBTITLE_WRITING_MODE_RLTB,
GST_SUBTITLE_WRITING_MODE_TBRL,
GST_SUBTITLE_WRITING_MODE_TBLR
} GstSubtitleWritingMode;
/**
* GstSubtitleDisplayAlign:
* @GST_SUBTITLE_DISPLAY_ALIGN_BEFORE: Blocks should be aligned at the start of
* the containing region.
* @GST_SUBTITLE_DISPLAY_ALIGN_CENTER: Blocks should be aligned in the center
* of the containing region.
* @GST_SUBTITLE_DISPLAY_ALIGN_AFTER: Blocks should be aligned to the end of
* the containing region.
*
* Defines the alignment of text blocks within a region in the direction in
* which blocks are being stacked. For text that is written left-to-right and
* top-to-bottom, this corresponds to the vertical alignment of text blocks.
*/
typedef enum {
GST_SUBTITLE_DISPLAY_ALIGN_BEFORE,
GST_SUBTITLE_DISPLAY_ALIGN_CENTER,
GST_SUBTITLE_DISPLAY_ALIGN_AFTER
} GstSubtitleDisplayAlign;
/**
* GstSubtitleBackgroundMode:
* @GST_SUBTITLE_BACKGROUND_MODE_ALWAYS: Background rectangle should be visible
* at all times.
* @GST_SUBTITLE_BACKGROUND_MODE_WHEN_ACTIVE: Background rectangle should be
* visible only when text is rendered into the corresponding region.
*
* Defines whether the background rectangle of a region should be visible at
* all times or only when text is rendered within it.
*/
typedef enum {
GST_SUBTITLE_BACKGROUND_MODE_ALWAYS,
GST_SUBTITLE_BACKGROUND_MODE_WHEN_ACTIVE
} GstSubtitleBackgroundMode;
/**
* GstSubtitleOverflowMode:
* @GST_SUBTITLE_OVERFLOW_MODE_HIDDEN: If text and/or background rectangles
* flowed into the region overflow the bounds of that region, they should
* be clipped at the region boundary.
* @GST_SUBTITLE_OVERFLOW_MODE_VISIBLE: If text and/or background rectangles
* flowed into the region overflow the bounds of that region, they should be
* allowed to overflow the region boundary.
*
* Defines what should happen to text that overflows its containing region.
*/
typedef enum {
GST_SUBTITLE_OVERFLOW_MODE_HIDDEN,
GST_SUBTITLE_OVERFLOW_MODE_VISIBLE
} GstSubtitleOverflowMode;
/**
* GstSubtitleColor:
* @r: Red value.
* @g: Green value.
* @b: Blue value.
* @a: Alpha value (0 = totally transparent; 255 = totally opaque).
*
* Describes an RGBA color.
*/
struct _GstSubtitleColor {
guint8 r;
guint8 g;
guint8 b;
guint8 a;
};
/**
* GstSubtitleTextDirection:
* @GST_SUBTITLE_TEXT_DIRECTION_LTR: Text direction is left-to-right.
* @GST_SUBTITLE_TEXT_DIRECTION_RTL: Text direction is right-to-left.
*
* Defines the progression direction of unicode text that is being treated by
* the unicode bidirectional algorithm as embedded or overidden (see
* http://unicode.org/reports/tr9/ for more details of the unicode
* bidirectional algorithm).
*/
typedef enum {
GST_SUBTITLE_TEXT_DIRECTION_LTR,
GST_SUBTITLE_TEXT_DIRECTION_RTL
} GstSubtitleTextDirection;
/**
* GstSubtitleTextAlign:
* @GST_SUBTITLE_TEXT_ALIGN_START: Text areas should be rendered at the
* start of the block area, with respect to the direction in which text is
* being rendered. For text that is rendered left-to-right this corresponds to
* the left of the block area; for text that is rendered right-to-left this
* corresponds to the right of the block area.
* @GST_SUBTITLE_TEXT_ALIGN_LEFT: Text areas should be rendered at the left of
* the block area.
* @GST_SUBTITLE_TEXT_ALIGN_CENTER: Text areas should be rendered at the center
* of the block area.
* @GST_SUBTITLE_TEXT_ALIGN_RIGHT: Text areas should be rendered at the right
* of the block area.
* @GST_SUBTITLE_TEXT_ALIGN_END: Text areas should be rendered at the end of
* the block area, with respect to the direction in which text is being
* rendered. For text that is rendered left-to-right this corresponds to the
* right of the block area; for text that is rendered right-to-left this
* corresponds to end of the block area.
*
* Defines how inline text areas within a block should be aligned within the
* block area.
*/
typedef enum {
GST_SUBTITLE_TEXT_ALIGN_START,
GST_SUBTITLE_TEXT_ALIGN_LEFT,
GST_SUBTITLE_TEXT_ALIGN_CENTER,
GST_SUBTITLE_TEXT_ALIGN_RIGHT,
GST_SUBTITLE_TEXT_ALIGN_END
} GstSubtitleTextAlign;
/**
* GstSubtitleFontStyle:
* @GST_SUBTITLE_FONT_STYLE_NORMAL: Normal font style.
* @GST_SUBTITLE_FONT_STYLE_ITALIC: Italic font style.
*
* Defines styling that should be applied to the glyphs of a font used to
* render text within an inline text element.
*/
typedef enum {
GST_SUBTITLE_FONT_STYLE_NORMAL,
GST_SUBTITLE_FONT_STYLE_ITALIC
} GstSubtitleFontStyle;
/**
* GstSubtitleFontWeight:
* @GST_SUBTITLE_FONT_WEIGHT_NORMAL: Normal weight.
* @GST_SUBTITLE_FONT_WEIGHT_BOLD: Bold weight.
*
* Defines the font weight that should be applied to the glyphs of a font used
* to render text within an inline text element.
*/
typedef enum {
GST_SUBTITLE_FONT_WEIGHT_NORMAL,
GST_SUBTITLE_FONT_WEIGHT_BOLD
} GstSubtitleFontWeight;
/**
* GstSubtitleTextDecoration:
* @GST_SUBTITLE_TEXT_DECORATION_NONE: Text should not be decorated.
* @GST_SUBTITLE_TEXT_DECORATION_UNDERLINE: Text should be underlined.
*
* Defines the decoration that should be applied to the glyphs of a font used
* to render text within an inline text element.
*/
typedef enum {
GST_SUBTITLE_TEXT_DECORATION_NONE,
GST_SUBTITLE_TEXT_DECORATION_UNDERLINE
} GstSubtitleTextDecoration;
/**
* GstSubtitleUnicodeBidi:
* @GST_SUBTITLE_UNICODE_BIDI_NORMAL: Text should progress according the the
* default behaviour of the Unicode bidirectional algorithm.
* @GST_SUBTITLE_UNICODE_BIDI_EMBED: Text should be treated as being embedded
* with a specific direction (given by a #GstSubtitleTextDecoration value
* defined elsewhere).
* @GST_SUBTITLE_UNICODE_BIDI_OVERRIDE: Text should be forced to have a
* specific direction (given by a #GstSubtitleTextDecoration value defined
* elsewhere).
*
* Defines directional embedding or override according to the Unicode
* bidirectional algorithm. See http://unicode.org/reports/tr9/ for more
* details of the Unicode bidirectional algorithm.
*/
typedef enum {
GST_SUBTITLE_UNICODE_BIDI_NORMAL,
GST_SUBTITLE_UNICODE_BIDI_EMBED,
GST_SUBTITLE_UNICODE_BIDI_OVERRIDE
} GstSubtitleUnicodeBidi;
/**
* GstSubtitleWrapping:
* @GST_SUBTITLE_WRAPPING_ON: Lines that overflow the region boundary should be
* wrapped.
* @GST_SUBTITLE_WRAPPING_OFF: Lines that overflow the region boundary should
* not be wrapped.
*
* Defines how a renderer should treat lines of text that overflow the boundary
* of the region into which they are being rendered.
*/
typedef enum {
GST_SUBTITLE_WRAPPING_ON,
GST_SUBTITLE_WRAPPING_OFF
} GstSubtitleWrapping;
/**
* GstSubtitleMultiRowAlign:
* @GST_SUBTITLE_MULTI_ROW_ALIGN_AUTO: Lines should be aligned according to the
* value of #GstSubtitleTextAlign associated with that text.
* @GST_SUBTITLE_MULTI_ROW_ALIGN_START: Lines should be aligned at their
* starting edge. The edge that is considered the starting edge depends upon
* the direction of that text.
* @GST_SUBTITLE_MULTI_ROW_ALIGN_CENTER: Lines should be center-aligned.
* @GST_SUBTITLE_MULTI_ROW_ALIGN_END: Lines should be aligned at their trailing
* edge. The edge that is considered the trailing edge depends upon the
* direction of that text.
*
* Defines how multiple 'rows' (i.e, lines) in a block should be aligned
* relative to each other.
*
* This is based upon the ebutts:multiRowAlign attribute defined in the
* EBU-TT-D specification.
*/
typedef enum {
GST_SUBTITLE_MULTI_ROW_ALIGN_AUTO,
GST_SUBTITLE_MULTI_ROW_ALIGN_START,
GST_SUBTITLE_MULTI_ROW_ALIGN_CENTER,
GST_SUBTITLE_MULTI_ROW_ALIGN_END
} GstSubtitleMultiRowAlign;
/**
* GstSubtitleStyleSet:
* @text_direction: Defines the direction of text that has been declared by the
* #GstSubtitleStyleSet:unicode_bidi attribute to be embbedded or overridden.
* Applies to both #GstSubtitleBlocks and #GstSubtitleElements.
* @font_family: The name of the font family that should be used to render the
* text of an inline element. Applies only to #GstSubtitleElements.
* @font_size: The size of the font that should be used to render the text
* of an inline element. The size is given as a multiple of the display height,
* where 1.0 equals the height of the display. Applies only to
* #GstSubtitleElements.
* @line_height: The inter-baseline separation between lines generated when
* rendering inline text elements within a block area. The height is given as a
* multiple of the the overall display height, where 1.0 equals the height of
* the display. Applies only to #GstSubtitleBlocks.
* @text_align: Controls the alignent of lines of text within a block area.
* Note that this attribute does not control the alignment of lines relative to
* each other within a block area: that is determined by
* #GstSubtitleStyleSet:multi_row_align. Applies only to #GstSubtitleBlocks.
* @color: The color that should be used when rendering the text of an inline
* element. Applies only to #GstSubtitleElements.
* @background_color: The color of the rectangle that should be rendered behind
* the contents of a #GstSubtitleRegion, #GstSubtitleBlock or
* #GstSubtitleElement.
* @font_style: The style of the font that should be used to render the text
* of an inline element. Applies only to #GstSubtitleElements.
* @font_weight: The weight of the font that should be used to render the text
* of an inline element. Applies only to #GstSubtitleElements.
* @text_decoration: The decoration that should be applied to the text of an
* inline element. Applies only to #GstSubtitleElements.
* @unicode_bidi: Controls how unicode text within a block or inline element
* should be treated by the unicode bidirectional algorithm. Applies to both
* #GstSubtitleBlocks and #GstSubtitleElements.
* @wrap_option: Defines whether or not automatic line breaking should apply to
* the lines generated when rendering a block of text elements. Applies only to
* #GstSubtitleBlocks.
* @multi_row_align: Defines how 'rows' (i.e., lines) within a text block
* should be aligned relative to each other. Note that this attribute does not
* determine how a block of text is aligned within that block area: that is
* determined by @text_align. Applies only to #GstSubtitleBlocks.
* @line_padding: Defines how much horizontal padding should be added on the
* start and end of each rendered line; this allows the insertion of space
* between the start/end of text lines and their background rectangles for
* better-looking subtitles. This is based upon the ebutts:linePadding
* attribute defined in the EBU-TT-D specification. Applies only to
* #GstSubtitleBlocks.
* @origin_x: The horizontal origin of a region into which text blocks may be
* rendered. Given as a multiple of the overall display width, where 1.0 equals
* the width of the display. Applies only to #GstSubtitleRegions.
* @origin_y: The vertical origin of a region into which text blocks may be
* rendered. Given as a multiple of the overall display height, where 1.0
* equals the height of the display. Applies only to #GstSubtitleRegions.
* @extent_w: The horizontal extent of a region into which text blocks may be
* rendered. Given as a multiple of the overall display width, where 1.0 equals
* the width of the display. Applies only to #GstSubtitleRegions.
* @extent_h: The vertical extent of a region into which text blocks may be
* rendered. Given as a multiple of the overall display height, where 1.0
* equals the height of the display. Applies only to #GstSubtitleRegions.
* @display_align: The alignment of generated text blocks in the direction in
* which blocks are being stacked. For text that flows left-to-right and
* top-to-bottom, for example, this corresponds to the vertical alignment of
* text blocks. Applies only to #GstSubtitleRegions.
* @padding_start: The horizontal indent of text from the leading edge of a
* region into which blocks may be rendered. Given as a multiple of the overall
* display width, where 1.0 equals the width of the display. Applies only to
* #GstSubtitleRegions.
* @padding_end: The horizontal indent of text from the trailing edge of a
* region into which blocks may be rendered. Given as a multiple of the overall
* display width, where 1.0 equals the width of the display. Applies only to
* #GstSubtitleRegions.
* @padding_before: The vertical indent of text from the top edge of a region
* into which blocks may be rendered. Given as a multiple of the overall
* display height, where 1.0 equals the height of the display. Applies only to
* #GstSubtitleRegions.
* @padding_after: The vertical indent of text from the bottom edge of a
* region into which blocks may be rendered. Given as a multiple of the overall
* display height, where 1.0 equals the height of the display. Applies only to
* #GstSubtitleRegions.
* @writing_mode: Defines the direction in which both inline elements and
* blocks should be stacked when rendered into an on-screen region. Applies
* only to #GstSubtitleRegions.
* @show_background: Defines whether the background of a region should be
* displayed at all times or only when it has text rendered into it. Applies
* only to #GstSubtitleRegions.
* @overflow: Defines what should happen if text and background rectangles
* generated by rendering text blocks overflow the size of their containing
* region. Applies only to #GstSubtitleRegions.
*
* Holds a set of attributes that describes the styling and layout that apply
* to #GstSubtitleRegion, #GstSubtitleBlock and/or #GstSubtitleElement objects.
*
* Note that, though each of the above object types have an associated
* #GstSubtitleStyleSet, not all attributes in a #GstSubtitleStyleSet type
* apply to all object types: #GstSubtitleStyleSet:overflow applies only to
* #GstSubtitleRegions, for example, while #GstSubtitleStyleSet:font_style
* applies only to #GstSubtitleElements. Some attributes apply to multiple
* object types: #GstSubtitleStyleSet:background_color, for example, applies to
* all object types. The types to which each attribute applies is given in the
* description of that attribute below.
*/
struct _GstSubtitleStyleSet {
GstSubtitleTextDirection text_direction;
gchar *font_family;
gdouble font_size;
gdouble line_height;
GstSubtitleTextAlign text_align;
GstSubtitleColor color;
GstSubtitleColor background_color;
GstSubtitleFontStyle font_style;
GstSubtitleFontWeight font_weight;
GstSubtitleTextDecoration text_decoration;
GstSubtitleUnicodeBidi unicode_bidi;
GstSubtitleWrapping wrap_option;
GstSubtitleMultiRowAlign multi_row_align;
gdouble line_padding;
gdouble origin_x, origin_y;
gdouble extent_w, extent_h;
GstSubtitleDisplayAlign display_align;
gdouble padding_start, padding_end, padding_before, padding_after;
GstSubtitleWritingMode writing_mode;
GstSubtitleBackgroundMode show_background;
GstSubtitleOverflowMode overflow;
};
GstSubtitleStyleSet * gst_subtitle_style_set_new (void);
void gst_subtitle_style_set_free (GstSubtitleStyleSet * style_set);
/**
* GstSubtitleElement:
* @mini_object: The parent #GstMiniObject.
* @style_set: Styling associated with this element.
* @text_index: Index into the #GstBuffer associated with this
* #GstSubtitleElement; the index identifies the #GstMemory within the
* #GstBuffer that holds the #GstSubtitleElement's text.
* @suppress_whitespace: Indicates whether or not a renderer should suppress
* whitespace in the element's text.
*
* Represents an inline text element.
*
* In TTML this would correspond to inline text resulting from a &lt;span&gt;
* element, an anonymous span (e.g., text within a &lt;p&gt; tag), or a
* &lt;br&gt; element.
*/
struct _GstSubtitleElement
{
GstMiniObject mini_object;
GstSubtitleStyleSet *style_set;
guint text_index;
gboolean suppress_whitespace;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
GType gst_subtitle_element_get_type (void);
GstSubtitleElement * gst_subtitle_element_new (GstSubtitleStyleSet * style_set,
guint text_index, gboolean suppress_whitespace);
/**
* gst_subtitle_element_ref:
* @element: A #GstSubtitleElement.
*
* Increments the refcount of @element.
*
* Returns: (transfer full): @element.
*/
static inline GstSubtitleElement *
gst_subtitle_element_ref (GstSubtitleElement * element)
{
return (GstSubtitleElement *)
gst_mini_object_ref (GST_MINI_OBJECT_CAST (element));
}
/**
* gst_subtitle_element_unref:
* @element: (transfer full): A #GstSubtitleElement.
*
* Decrements the refcount of @element. If the refcount reaches 0, @element
* will be freed.
*/
static inline void
gst_subtitle_element_unref (GstSubtitleElement * element)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (element));
}
/**
* GstSubtitleBlock:
* @mini_object: The parent #GstMiniObject.
* @style_set: Styling associated with this block.
*
* Represents a text block made up of one or more inline text elements (i.e.,
* one or more #GstSubtitleElements).
*
* In TTML this would correspond to the block of text resulting from the inline
* elements within a single &lt;p&gt;.
*/
struct _GstSubtitleBlock
{
GstMiniObject mini_object;
GstSubtitleStyleSet *style_set;
/*< private >*/
GPtrArray *elements;
gpointer _gst_reserved[GST_PADDING];
};
GType gst_subtitle_block_get_type (void);
GstSubtitleBlock * gst_subtitle_block_new (GstSubtitleStyleSet * style_set);
void gst_subtitle_block_add_element (
GstSubtitleBlock * block,
GstSubtitleElement * element);
guint gst_subtitle_block_get_element_count (const GstSubtitleBlock * block);
const GstSubtitleElement * gst_subtitle_block_get_element (
const GstSubtitleBlock * block, guint index);
/**
* gst_subtitle_block_ref:
* @block: A #GstSubtitleBlock.
*
* Increments the refcount of @block.
*
* Returns: (transfer full): @block.
*/
static inline GstSubtitleBlock *
gst_subtitle_block_ref (GstSubtitleBlock * block)
{
return (GstSubtitleBlock *)
gst_mini_object_ref (GST_MINI_OBJECT_CAST (block));
}
/**
* gst_subtitle_block_unref:
* @block: (transfer full): A #GstSubtitleBlock.
*
* Decrements the refcount of @block. If the refcount reaches 0, @block will
* be freed.
*/
static inline void
gst_subtitle_block_unref (GstSubtitleBlock * block)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (block));
}
/**
* GstSubtitleRegion:
* @mini_object: The parent #GstMiniObject.
* @style_set: Styling associated with this region.
*
* Represents an on-screen region in which is displayed zero or more
* #GstSubtitleBlocks.
*
* In TTML this corresponds to a &lt;region&gt; into which zero or more
* &lt;p&gt;s may be rendered. A #GstSubtitleRegion allows a background
* rectangle to be displayed in a region area even if no text blocks are
* rendered into it, as per the behaviour allowed by TTML regions whose
* tts:showBackground style attribute is set to "always".
*/
struct _GstSubtitleRegion
{
GstMiniObject mini_object;
GstSubtitleStyleSet *style_set;
/*< private >*/
GPtrArray *blocks;
gpointer _gst_reserved[GST_PADDING];
};
GType gst_subtitle_region_get_type (void);
GstSubtitleRegion * gst_subtitle_region_new (GstSubtitleStyleSet * style_set);
void gst_subtitle_region_add_block (
GstSubtitleRegion * region,
GstSubtitleBlock * block);
guint gst_subtitle_region_get_block_count (const GstSubtitleRegion * region);
const GstSubtitleBlock * gst_subtitle_region_get_block (
const GstSubtitleRegion * region, guint index);
/**
* gst_subtitle_region_ref:
* @region: A #GstSubtitleRegion.
*
* Increments the refcount of @region.
*
* Returns: (transfer full): @region.
*/
static inline GstSubtitleRegion *
gst_subtitle_region_ref (GstSubtitleRegion * region)
{
return (GstSubtitleRegion *)
gst_mini_object_ref (GST_MINI_OBJECT_CAST (region));
}
/**
* gst_subtitle_region_unref:
* @region: (transfer full): A #GstSubtitleRegion.
*
* Decrements the refcount of @region. If the refcount reaches 0, @region will
* be freed.
*/
static inline void
gst_subtitle_region_unref (GstSubtitleRegion * region)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (region));
}
G_END_DECLS
#endif /* __GST_SUBTITLE_H__ */

101
ext/ttml/subtitlemeta.c Normal file
View file

@ -0,0 +1,101 @@
/* GStreamer
* Copyright (C) <2015> British Broadcasting Corporation
* Author: Chris Bass <dash@rd.bbc.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:gstsubtitlemeta
* @short_description: Metadata class for timed-text subtitles.
*
* The GstSubtitleMeta class enables the layout and styling information needed
* to render subtitle text to be attached to a #GstBuffer containing that text.
*/
#include "subtitlemeta.h"
GType
gst_subtitle_meta_api_get_type (void)
{
static volatile GType type;
static const gchar *tags[] = { "memory", NULL };
if (g_once_init_enter (&type)) {
GType _type = gst_meta_api_type_register ("GstSubtitleMetaAPI", tags);
g_once_init_leave (&type, _type);
}
return type;
}
gboolean
gst_subtitle_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
{
GstSubtitleMeta *subtitle_meta = (GstSubtitleMeta *) meta;
subtitle_meta->regions = NULL;
return TRUE;
}
void
gst_subtitle_meta_free (GstMeta * meta, GstBuffer * buffer)
{
GstSubtitleMeta *subtitle_meta = (GstSubtitleMeta *) meta;
if (subtitle_meta->regions)
g_ptr_array_unref (subtitle_meta->regions);
}
const GstMetaInfo *
gst_subtitle_meta_get_info (void)
{
static const GstMetaInfo *subtitle_meta_info = NULL;
if (g_once_init_enter (&subtitle_meta_info)) {
const GstMetaInfo *meta =
gst_meta_register (GST_SUBTITLE_META_API_TYPE, "GstSubtitleMeta",
sizeof (GstSubtitleMeta), gst_subtitle_meta_init,
gst_subtitle_meta_free, (GstMetaTransformFunction) NULL);
g_once_init_leave (&subtitle_meta_info, meta);
}
return subtitle_meta_info;
}
/**
* gst_buffer_add_subtitle_meta:
* @buffer: (transfer none): #GstBuffer holding subtitle text, to which
* subtitle metadata should be added.
* @regions: (transfer full): A #GPtrArray of #GstSubtitleRegions.
*
* Attaches subtitle metadata to a #GstBuffer.
*
* Returns: A pointer to the added #GstSubtitleMeta if successful; %NULL if
* unsuccessful.
*/
GstSubtitleMeta *
gst_buffer_add_subtitle_meta (GstBuffer * buffer, GPtrArray * regions)
{
GstSubtitleMeta *meta;
g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
g_return_val_if_fail (regions != NULL, NULL);
meta = (GstSubtitleMeta *) gst_buffer_add_meta (buffer,
GST_SUBTITLE_META_INFO, NULL);
meta->regions = regions;
return meta;
}

66
ext/ttml/subtitlemeta.h Normal file
View file

@ -0,0 +1,66 @@
/* GStreamer
* Copyright (C) <2015> British Broadcasting Corporation
* Author: Chris Bass <dash@rd.bbc.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_SUBTITLE_META_H__
#define __GST_SUBTITLE_META_H__
#include <gst/gst.h>
#include "subtitle.h"
G_BEGIN_DECLS
typedef struct _GstSubtitleMeta GstSubtitleMeta;
/**
* GstSubtitleMeta:
* @meta: The parent #GstMeta.
* @regions: The #GstSubtitleRegions containing layout and styling information
* needed to render the subtitle text contained in the associated #GstBuffer.
*
* Metadata type that describes the layout and styling of subtitle text
* contained in a #GstBuffer.
*/
struct _GstSubtitleMeta {
GstMeta meta;
GPtrArray *regions;
};
GType gst_subtitle_meta_api_get_type (void);
#define GST_SUBTITLE_META_API_TYPE (gst_subtitle_meta_api_get_type())
#define gst_buffer_get_subtitle_meta(b) \
((GstSubtitleMeta*)gst_buffer_get_meta ((b), GST_SUBTITLE_META_API_TYPE))
#define GST_SUBTITLE_META_INFO (gst_subtitle_meta_get_info())
gboolean gst_subtitle_meta_init (GstMeta * meta, gpointer params,
GstBuffer * buffer);
void gst_subtitle_meta_free (GstMeta * meta, GstBuffer * buffer);
const GstMetaInfo * gst_subtitle_meta_get_info (void);
GstSubtitleMeta * gst_buffer_add_subtitle_meta (GstBuffer * buffer,
GPtrArray * regions);
G_END_DECLS
#endif /* __GST_SUBTITLE_META_H__ */

1813
ext/ttml/ttmlparse.c Normal file

File diff suppressed because it is too large Load diff

34
ext/ttml/ttmlparse.h Normal file
View file

@ -0,0 +1,34 @@
/* GStreamer TTML subtitle parser
* Copyright (C) <2015> British Broadcasting Corporation
* Authors:
* Chris Bass <dash@rd.bbc.co.uk>
* Peter Taylour <dash@rd.bbc.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _TTML_PARSE_H_
#define _TTML_PARSE_H_
#include <gst/gst.h>
G_BEGIN_DECLS
GList *ttml_parse (const gchar * file, GstClockTime begin,
GstClockTime duration);
G_END_DECLS
#endif /* _TTML_PARSE_H_ */