*
* 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-katedec
+ * @title: katedec
* @see_also: oggdemux
*
- * <refsect2>
- * <para>
- * This element decodes Kate streams
- * <ulink url="http://libkate.googlecode.com/">Kate</ulink> is a free codec
+ * This element decodes Kate streams.
+ *
+ * [Kate](http://libkate.googlecode.com/) is a free codec
* for text based data, such as subtitles. Any number of kate streams can be
* embedded in an Ogg stream.
- * </para>
- * <para>
+ *
* libkate (see above url) is needed to build this plugin.
- * </para>
- * <title>Example pipeline</title>
- * <para>
- * This explicitely decodes a Kate stream:
- * <programlisting>
- * gst-launch filesrc location=test.ogg ! oggdemux ! katedec ! fakesink silent=TRUE
- * </programlisting>
- * </para>
- * <para>
+ *
+ * ## Example pipeline
+ *
+ * This explicitly decodes a Kate stream:
+ * |[
+ * gst-launch-1.0 filesrc location=test.ogg ! oggdemux ! katedec ! fakesink silent=TRUE
+ * ]|
+ *
* This will automatically detect and use any Kate streams multiplexed
* in an Ogg stream:
- * <programlisting>
- * gst-launch playbin uri=file:///tmp/test.ogg
- * </programlisting>
- * </para>
- * </refsect2>
+ * |[
+ * gst-launch-1.0 playbin uri=file:///tmp/test.ogg
+ * ]|
+ *
*/
#ifdef HAVE_CONFIG_H
#include <gst/gst.h>
-#include "gstkate.h"
+#include "gstkateelements.h"
#include "gstkatespu.h"
#include "gstkatedec.h"
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("text/plain; text/x-pango-markup; " GST_KATE_SPU_MIME_TYPE)
+ GST_STATIC_CAPS ("text/x-raw, format = { pango-markup, utf8 }; "
+ GST_KATE_SPU_MIME_TYPE)
);
+GST_DEBUG_CATEGORY (gst_katedec_debug);
+
#define gst_kate_dec_parent_class parent_class
G_DEFINE_TYPE (GstKateDec, gst_kate_dec, GST_TYPE_ELEMENT);
+#define _do_init \
+ kate_element_init (plugin); \
+ GST_DEBUG_CATEGORY_INIT (gst_katedec_debug, "katedec", 0, "Kate decoder");
+GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (katedec, "katedec", GST_RANK_PRIMARY,
+ GST_TYPE_KATE_DEC, _do_init);
static void gst_kate_dec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_kate_dec_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
-static GstFlowReturn gst_kate_dec_chain (GstPad * pad, GstBuffer * buf);
+static GstFlowReturn gst_kate_dec_chain (GstPad * pad, GstObject * parent,
+ GstBuffer * buf);
static GstStateChangeReturn gst_kate_dec_change_state (GstElement * element,
GstStateChange transition);
-static gboolean gst_kate_dec_sink_query (GstPad * pad, GstQuery * query);
-static gboolean gst_kate_dec_sink_event (GstPad * pad, GstEvent * event);
-static gboolean gst_kate_dec_sink_handle_event (GstPad * pad, GstEvent * event);
-static GstCaps *gst_kate_dec_src_get_caps (GstPad * pad, GstCaps * filter);
+static gboolean gst_kate_dec_sink_query (GstPad * pad, GstObject * parent,
+ GstQuery * query);
+static gboolean gst_kate_dec_sink_event (GstPad * pad, GstObject * parent,
+ GstEvent * event);
+static gboolean gst_kate_dec_sink_handle_event (GstPad * pad,
+ GstObject * parent, GstEvent * event);
+static gboolean gst_kate_dec_src_query (GstPad * pad, GstObject * parent,
+ GstQuery * query);
/* initialize the plugin's class */
static void
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_kate_dec_change_state);
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&src_factory));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&sink_factory));
+ gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
+ gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
- gst_element_class_set_details_simple (gstelement_class,
+ gst_element_class_set_static_metadata (gstelement_class,
"Kate stream text decoder", "Codec/Decoder/Subtitle",
"Decodes Kate text streams",
"Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>");
static void
gst_kate_dec_init (GstKateDec * dec)
{
+ GstCaps *tmp = NULL;
GST_DEBUG_OBJECT (dec, "gst_kate_dec_init");
dec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
GST_DEBUG_FUNCPTR (gst_kate_dec_sink_event));
gst_pad_use_fixed_caps (dec->sinkpad);
gst_pad_set_caps (dec->sinkpad,
- gst_static_pad_template_get_caps (&sink_factory));
+ tmp = gst_static_pad_template_get_caps (&sink_factory));
gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
dec->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
- gst_pad_set_getcaps_function (dec->srcpad,
- GST_DEBUG_FUNCPTR (gst_kate_dec_src_get_caps));
+ gst_pad_set_query_function (dec->srcpad,
+ GST_DEBUG_FUNCPTR (gst_kate_dec_src_query));
gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
gst_kate_util_decode_base_init (&dec->decoder, TRUE);
+ gst_caps_unref(tmp);
dec->src_caps = NULL;
+ dec->output_format = GST_KATE_FORMAT_UNDEFINED;
dec->remove_markup = FALSE;
}
}
}
+static GstFlowReturn
+gst_kate_dec_handle_kate_event (GstKateDec * kd, const kate_event * ev)
+{
+ GstFlowReturn rflow = GST_FLOW_OK;
+ GstKateFormat format = GST_KATE_FORMAT_UNDEFINED;
+ gchar *escaped;
+ GstBuffer *buffer;
+ size_t len;
+ gboolean plain = TRUE;
+
+ if (kd->remove_markup && ev->text_markup_type != kate_markup_none) {
+ size_t len0 = ev->len + 1;
+ escaped = g_strdup (ev->text);
+ if (escaped) {
+ kate_text_remove_markup (ev->text_encoding, escaped, &len0);
+ }
+ plain = TRUE;
+ } else if (ev->text_markup_type == kate_markup_none) {
+ /* no pango markup yet, escape text */
+ /* TODO: actually do the pango thing */
+ escaped = g_strdup (ev->text);
+ plain = TRUE;
+ } else {
+ escaped = g_strdup (ev->text);
+ plain = FALSE;
+ }
+
+ if (G_LIKELY (escaped)) {
+ len = strlen (escaped);
+ if (len > 0) {
+ GST_DEBUG_OBJECT (kd, "kate event: %s, escaped %s", ev->text, escaped);
+ buffer = gst_buffer_new_and_alloc (len + 1);
+ if (G_LIKELY (buffer)) {
+ GstCaps *caps;
+ if (plain)
+ format = GST_KATE_FORMAT_TEXT_UTF8;
+ else
+ format = GST_KATE_FORMAT_TEXT_PANGO_MARKUP;
+ if (format != kd->output_format) {
+ caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
+ (format == GST_KATE_FORMAT_TEXT_UTF8) ? "utf8" : "pango-markup",
+ NULL);
+ gst_pad_push_event (kd->srcpad, gst_event_new_caps (caps));
+ gst_caps_unref (caps);
+ kd->output_format = format;
+ }
+ /* allocate and copy the NULs, but don't include them in passed size */
+ gst_buffer_fill (buffer, 0, escaped, len + 1);
+ gst_buffer_resize (buffer, 0, len);
+ GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
+ GST_BUFFER_DURATION (buffer) =
+ (ev->end_time - ev->start_time) * GST_SECOND;
+ rflow = gst_pad_push (kd->srcpad, buffer);
+ if (rflow == GST_FLOW_NOT_LINKED) {
+ GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
+ } else if (rflow != GST_FLOW_OK) {
+ GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
+ gst_flow_get_name (rflow));
+ }
+ } else {
+ GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
+ ("Failed to create buffer"));
+ rflow = GST_FLOW_ERROR;
+ }
+ } else {
+ GST_WARNING_OBJECT (kd, "Empty string, nothing to do");
+ rflow = GST_FLOW_OK;
+ }
+ g_free (escaped);
+ } else {
+ GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
+ ("Failed to allocate string"));
+ rflow = GST_FLOW_ERROR;
+ }
+
+ /* if there's a background paletted bitmap, construct a DVD SPU for it */
+ if (ev->bitmap && ev->palette) {
+ GstBuffer *buffer = gst_kate_spu_encode_spu (kd, ev);
+ if (buffer) {
+ GstCaps *caps;
+
+ GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
+ GST_BUFFER_DURATION (buffer) =
+ (ev->end_time - ev->start_time) * GST_SECOND;
+
+ if (kd->output_format != GST_KATE_FORMAT_SPU) {
+ caps = gst_caps_new_empty_simple (GST_KATE_SPU_MIME_TYPE);
+ gst_pad_push_event (kd->srcpad, gst_event_new_caps (caps));
+ gst_caps_unref (caps);
+ kd->output_format = GST_KATE_FORMAT_SPU;
+ }
+
+ rflow = gst_pad_push (kd->srcpad, buffer);
+ if (rflow == GST_FLOW_NOT_LINKED) {
+ GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
+ } else if (rflow != GST_FLOW_OK) {
+ GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
+ gst_flow_get_name (rflow));
+ }
+ } else {
+ GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
+ ("failed to create SPU from paletted bitmap"));
+ rflow = GST_FLOW_ERROR;
+ }
+ }
+ return rflow;
+}
+
/* GstElement vmethod implementations */
/* chain function
*/
static GstFlowReturn
-gst_kate_dec_chain (GstPad * pad, GstBuffer * buf)
+gst_kate_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
- GstKateDec *kd = GST_KATE_DEC (gst_pad_get_parent (pad));
+ GstKateDec *kd = GST_KATE_DEC (parent);
const kate_event *ev = NULL;
GstFlowReturn rflow = GST_FLOW_OK;
GST_ELEMENT_CAST (kd), pad, buf, kd->srcpad, kd->srcpad, &kd->src_caps,
&ev);
if (G_UNLIKELY (rflow != GST_FLOW_OK)) {
- gst_object_unref (kd);
gst_buffer_unref (buf);
return rflow;
}
if (ev) {
- gchar *escaped;
- GstBuffer *buffer;
- size_t len;
- gboolean plain = TRUE;
-
- if (kd->remove_markup && ev->text_markup_type != kate_markup_none) {
- size_t len0 = ev->len + 1;
- escaped = g_strdup (ev->text);
- if (escaped) {
- kate_text_remove_markup (ev->text_encoding, escaped, &len0);
- }
- plain = TRUE;
- } else if (ev->text_markup_type == kate_markup_none) {
- /* no pango markup yet, escape text */
- /* TODO: actually do the pango thing */
- escaped = g_strdup (ev->text);
- plain = TRUE;
- } else {
- escaped = g_strdup (ev->text);
- plain = FALSE;
- }
-
- if (G_LIKELY (escaped)) {
- len = strlen (escaped);
- if (len > 0) {
- GST_DEBUG_OBJECT (kd, "kate event: %s, escaped %s", ev->text, escaped);
- buffer = gst_buffer_new_and_alloc (len + 1);
- if (G_LIKELY (buffer)) {
- const char *mime = plain ? "text/plain" : "text/x-pango-markup";
- GstCaps *caps = gst_caps_new_empty_simple (mime);
- gst_caps_unref (caps);
- /* allocate and copy the NULs, but don't include them in passed size */
- gst_buffer_fill (buffer, 0, escaped, len + 1);
- gst_buffer_resize (buffer, 0, len);
- GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
- GST_BUFFER_DURATION (buffer) =
- (ev->end_time - ev->start_time) * GST_SECOND;
- rflow = gst_pad_push (kd->srcpad, buffer);
- if (rflow == GST_FLOW_NOT_LINKED) {
- GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
- } else if (rflow != GST_FLOW_OK) {
- GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
- gst_flow_get_name (rflow));
- }
- } else {
- GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
- ("Failed to create buffer"));
- rflow = GST_FLOW_ERROR;
- }
- } else {
- GST_WARNING_OBJECT (kd, "Empty string, nothing to do");
- rflow = GST_FLOW_OK;
- }
- g_free (escaped);
- } else {
- GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
- ("Failed to allocate string"));
- rflow = GST_FLOW_ERROR;
- }
-
- // if there's a background paletted bitmap, construct a DVD SPU for it
- if (ev->bitmap && ev->palette) {
- GstBuffer *buffer = gst_kate_spu_encode_spu (kd, ev);
- if (buffer) {
- GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
- GST_BUFFER_DURATION (buffer) =
- (ev->end_time - ev->start_time) * GST_SECOND;
- rflow = gst_pad_push (kd->srcpad, buffer);
- if (rflow == GST_FLOW_NOT_LINKED) {
- GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
- } else if (rflow != GST_FLOW_OK) {
- GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
- gst_flow_get_name (rflow));
- }
- } else {
- GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
- ("failed to create SPU from paletted bitmap"));
- rflow = GST_FLOW_ERROR;
- }
- }
+ rflow = gst_kate_dec_handle_kate_event (kd, ev);
}
not_in_seg:
- gst_object_unref (kd);
gst_buffer_unref (buf);
return rflow;
}
}
gboolean
-gst_kate_dec_sink_query (GstPad * pad, GstQuery * query)
+gst_kate_dec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
- GstKateDec *kd = GST_KATE_DEC (gst_pad_get_parent (pad));
+ GstKateDec *kd = GST_KATE_DEC (parent);
gboolean res =
gst_kate_decoder_base_sink_query (&kd->decoder, GST_ELEMENT_CAST (kd),
- pad, query);
- gst_object_unref (kd);
+ pad, parent, query);
return res;
}
static gboolean
-gst_kate_dec_sink_event (GstPad * pad, GstEvent * event)
+gst_kate_dec_set_caps (GstKateDec * kd, GstCaps * caps)
{
- GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad)));
+ GstStructure *structure = gst_caps_get_structure (caps, 0);
+ GstFlowReturn rflow = GST_FLOW_OK;
+
+ if (gst_structure_has_field (structure, "streamheader")) {
+ const GValue *value;
+ GstBuffer *buf;
+ const kate_event *ev;
+
+ value = gst_structure_get_value (structure, "streamheader");
+
+ if (GST_VALUE_HOLDS_BUFFER (value)) {
+ buf = gst_value_get_buffer (value);
+
+ gst_kate_util_decoder_base_chain_kate_packet (&kd->decoder,
+ GST_ELEMENT_CAST (kd), kd->sinkpad, buf, kd->srcpad, kd->srcpad,
+ &kd->src_caps, &ev);
+
+ if (ev) {
+ rflow = gst_kate_dec_handle_kate_event (kd, ev);
+ }
+ } else if (GST_VALUE_HOLDS_ARRAY (value)) {
+ gint i, size = gst_value_array_get_size (value);
+
+ for (i = 0; i < size; i++) {
+ const GValue *v = gst_value_array_get_value (value, i);
+
+ buf = gst_value_get_buffer (v);
+ gst_kate_util_decoder_base_chain_kate_packet (&kd->decoder,
+ GST_ELEMENT_CAST (kd), kd->sinkpad, buf, kd->srcpad, kd->srcpad,
+ &kd->src_caps, &ev);
+
+ if (ev) {
+ rflow = gst_kate_dec_handle_kate_event (kd, ev);
+ if (rflow != GST_FLOW_OK && rflow != GST_FLOW_NOT_LINKED)
+ break;
+ }
+ }
+ } else {
+ GST_WARNING_OBJECT (kd, "Unhandled streamheader type: %s",
+ G_VALUE_TYPE_NAME (value));
+ }
+ }
+
+ return rflow == GST_FLOW_OK || rflow == GST_FLOW_NOT_LINKED;
+}
+
+static gboolean
+gst_kate_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ GstKateDec *kd = GST_KATE_DEC (parent);
gboolean res = TRUE;
- GST_LOG_OBJECT (pad, "Event on sink pad: %s", GST_EVENT_TYPE_NAME (event));
+ GST_LOG_OBJECT (pad, "Event on sink pad: %" GST_PTR_FORMAT, event);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CAPS:{
+ GstCaps *caps;
+
+ gst_event_parse_caps (event, &caps);
+ gst_kate_dec_set_caps (kd, caps);
+ break;
+ }
+ default:
+ break;
+ }
/* Delay events till we've set caps */
if (gst_kate_util_decoder_base_queue_event (&kd->decoder, event,
- &gst_kate_dec_sink_handle_event, pad)) {
- gst_object_unref (kd);
+ &gst_kate_dec_sink_handle_event, parent, pad)) {
return TRUE;
}
- res = gst_kate_dec_sink_handle_event (pad, event);
-
- gst_object_unref (kd);
+ res = gst_kate_dec_sink_handle_event (pad, parent, event);
return res;
}
static gboolean
-gst_kate_dec_sink_handle_event (GstPad * pad, GstEvent * event)
+gst_kate_dec_sink_handle_event (GstPad * pad, GstObject * parent,
+ GstEvent * event)
{
- GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad)));
- gboolean res = TRUE;
+ GstKateDec *kd = GST_KATE_DEC (parent);
GST_LOG_OBJECT (pad, "Handling event on sink pad: %s",
GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEGMENT:
- gst_kate_util_decoder_base_segment_event (&kd->decoder, event);
- res = gst_pad_event_default (pad, event);
break;
case GST_EVENT_FLUSH_START:
gst_kate_util_decoder_base_set_flushing (&kd->decoder, TRUE);
- res = gst_pad_event_default (pad, event);
break;
case GST_EVENT_FLUSH_STOP:
gst_kate_util_decoder_base_set_flushing (&kd->decoder, FALSE);
- res = gst_pad_event_default (pad, event);
break;
+ case GST_EVENT_TAG:{
+ GstTagList *tags;
+ gst_event_parse_tag (event, &tags);
+ gst_kate_util_decoder_base_add_tags (&kd->decoder, tags, FALSE);
+ gst_event_unref (event);
+ event = gst_kate_util_decoder_base_get_tag_event (&kd->decoder);
+ break;
+ }
default:
- res = gst_pad_event_default (pad, event);
break;
}
- gst_object_unref (kd);
-
- return res;
+ return gst_pad_event_default (pad, parent, event);
}
-static GstCaps *
-gst_kate_dec_src_get_caps (GstPad * pad, GstCaps * filter)
+static gboolean
+gst_kate_dec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
- GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad)));
- GstCaps *caps;
+ GstKateDec *kd = GST_KATE_DEC (parent);
+ gboolean res = TRUE;
- if (kd->src_caps) {
- GST_DEBUG_OBJECT (kd, "We have src caps %" GST_PTR_FORMAT, kd->src_caps);
- caps = kd->src_caps;
- } else {
- GST_DEBUG_OBJECT (kd, "We have no src caps, using template caps");
- caps = gst_static_pad_template_get_caps (&src_factory);
- }
+ GST_LOG_OBJECT (pad, "Handling query on src pad: %s",
+ GST_QUERY_TYPE_NAME (query));
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_CAPS:{
+ GstCaps *caps;
- caps = gst_caps_copy (caps);
+ if (kd->src_caps) {
+ GST_DEBUG_OBJECT (kd, "We have src caps %" GST_PTR_FORMAT,
+ kd->src_caps);
+ caps = gst_caps_copy (kd->src_caps);
+ } else {
+ GST_DEBUG_OBJECT (kd, "We have no src caps, using template caps");
+ caps = gst_static_pad_template_get_caps (&src_factory);
+ }
- gst_object_unref (kd);
- return caps;
+ gst_query_set_caps_result (query, caps);
+ gst_caps_unref (caps);
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, parent, query);
+ break;
+ }
+
+ return res;
}