3 * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
4 * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
5 * Copyright 2008, 2009 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
25 * Alternatively, the contents of this file may be used under the
26 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
27 * which case the following provisions apply instead of the ones
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Library General Public
32 * License as published by the Free Software Foundation; either
33 * version 2 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Library General Public License for more details.
40 * You should have received a copy of the GNU Library General Public
41 * License along with this library; if not, write to the
42 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
43 * Boston, MA 02111-1307, USA.
47 * SECTION:element-katedec
52 * This element decodes Kate streams
53 * <ulink url="http://libkate.googlecode.com/">Kate</ulink> is a free codec
54 * for text based data, such as subtitles. Any number of kate streams can be
55 * embedded in an Ogg stream.
58 * libkate (see above url) is needed to build this plugin.
60 * <title>Example pipeline</title>
62 * This explicitely decodes a Kate stream:
64 * gst-launch filesrc location=test.ogg ! oggdemux ! katedec ! fakesink silent=TRUE
68 * This will automatically detect and use any Kate streams multiplexed
71 * gst-launch playbin uri=file:///tmp/test.ogg
86 #include "gstkatespu.h"
87 #include "gstkatedec.h"
89 GST_DEBUG_CATEGORY_EXTERN (gst_katedec_debug);
90 #define GST_CAT_DEFAULT gst_katedec_debug
92 /* Filter signals and args */
101 ARG_REMOVE_MARKUP = DECODER_BASE_ARG_COUNT
104 /* We don't accept application/x-kate here on purpose for now, since we're
105 * only really interested in subtitle-like things for playback purposes, not
106 * cracktastic complex overlays or presentation images etc. - those should be
107 * fed into a tiger overlay plugin directly */
108 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
111 GST_STATIC_CAPS ("subtitle/x-kate")
114 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
117 GST_STATIC_CAPS ("text/plain; text/x-pango-markup; " GST_KATE_SPU_MIME_TYPE)
120 #define gst_kate_dec_parent_class parent_class
121 G_DEFINE_TYPE (GstKateDec, gst_kate_dec, GST_TYPE_ELEMENT);
123 static void gst_kate_dec_set_property (GObject * object, guint prop_id,
124 const GValue * value, GParamSpec * pspec);
125 static void gst_kate_dec_get_property (GObject * object, guint prop_id,
126 GValue * value, GParamSpec * pspec);
128 static GstFlowReturn gst_kate_dec_chain (GstPad * pad, GstBuffer * buf);
129 static GstStateChangeReturn gst_kate_dec_change_state (GstElement * element,
130 GstStateChange transition);
131 static gboolean gst_kate_dec_sink_query (GstPad * pad, GstQuery * query);
132 static gboolean gst_kate_dec_sink_event (GstPad * pad, GstEvent * event);
133 static gboolean gst_kate_dec_sink_handle_event (GstPad * pad, GstEvent * event);
134 static GstCaps *gst_kate_dec_src_get_caps (GstPad * pad, GstCaps * filter);
136 /* initialize the plugin's class */
138 gst_kate_dec_class_init (GstKateDecClass * klass)
140 GObjectClass *gobject_class;
141 GstElementClass *gstelement_class;
143 gobject_class = (GObjectClass *) klass;
144 gstelement_class = (GstElementClass *) klass;
146 gobject_class->set_property = gst_kate_dec_set_property;
147 gobject_class->get_property = gst_kate_dec_get_property;
149 gst_kate_util_install_decoder_base_properties (gobject_class);
151 g_object_class_install_property (gobject_class, ARG_REMOVE_MARKUP,
152 g_param_spec_boolean ("remove-markup", "Remove markup",
153 "Remove markup from decoded text ?", FALSE,
154 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
156 gstelement_class->change_state =
157 GST_DEBUG_FUNCPTR (gst_kate_dec_change_state);
159 gst_element_class_add_pad_template (gstelement_class,
160 gst_static_pad_template_get (&src_factory));
161 gst_element_class_add_pad_template (gstelement_class,
162 gst_static_pad_template_get (&sink_factory));
164 gst_element_class_set_details_simple (gstelement_class,
165 "Kate stream text decoder", "Codec/Decoder/Subtitle",
166 "Decodes Kate text streams",
167 "Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>");
170 /* initialize the new element
171 * instantiate pads and add them to element
173 * initialize structure
176 gst_kate_dec_init (GstKateDec * dec)
178 GST_DEBUG_OBJECT (dec, "gst_kate_dec_init");
180 dec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
181 gst_pad_set_chain_function (dec->sinkpad,
182 GST_DEBUG_FUNCPTR (gst_kate_dec_chain));
183 gst_pad_set_query_function (dec->sinkpad,
184 GST_DEBUG_FUNCPTR (gst_kate_dec_sink_query));
185 gst_pad_set_event_function (dec->sinkpad,
186 GST_DEBUG_FUNCPTR (gst_kate_dec_sink_event));
187 gst_pad_use_fixed_caps (dec->sinkpad);
188 gst_pad_set_caps (dec->sinkpad,
189 gst_static_pad_template_get_caps (&sink_factory));
190 gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
192 dec->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
193 gst_pad_set_getcaps_function (dec->srcpad,
194 GST_DEBUG_FUNCPTR (gst_kate_dec_src_get_caps));
195 gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
197 gst_kate_util_decode_base_init (&dec->decoder, TRUE);
199 dec->src_caps = NULL;
200 dec->remove_markup = FALSE;
204 gst_kate_dec_set_property (GObject * object, guint prop_id,
205 const GValue * value, GParamSpec * pspec)
209 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215 gst_kate_dec_get_property (GObject * object, guint prop_id,
216 GValue * value, GParamSpec * pspec)
218 GstKateDec *kd = GST_KATE_DEC (object);
221 case ARG_REMOVE_MARKUP:
222 g_value_set_boolean (value, kd->remove_markup);
225 if (!gst_kate_util_decoder_base_get_property (&kd->decoder, object,
226 prop_id, value, pspec)) {
227 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
233 /* GstElement vmethod implementations */
236 * this function does the actual processing
240 gst_kate_dec_chain (GstPad * pad, GstBuffer * buf)
242 GstKateDec *kd = GST_KATE_DEC (gst_pad_get_parent (pad));
243 const kate_event *ev = NULL;
244 GstFlowReturn rflow = GST_FLOW_OK;
246 if (!gst_kate_util_decoder_base_update_segment (&kd->decoder,
247 GST_ELEMENT_CAST (kd), buf)) {
248 GST_WARNING_OBJECT (kd, "Out of segment!");
253 gst_kate_util_decoder_base_chain_kate_packet (&kd->decoder,
254 GST_ELEMENT_CAST (kd), pad, buf, kd->srcpad, kd->srcpad, &kd->src_caps,
256 if (G_UNLIKELY (rflow != GST_FLOW_OK)) {
257 gst_object_unref (kd);
258 gst_buffer_unref (buf);
266 gboolean plain = TRUE;
268 if (kd->remove_markup && ev->text_markup_type != kate_markup_none) {
269 size_t len0 = ev->len + 1;
270 escaped = g_strdup (ev->text);
272 kate_text_remove_markup (ev->text_encoding, escaped, &len0);
275 } else if (ev->text_markup_type == kate_markup_none) {
276 /* no pango markup yet, escape text */
277 /* TODO: actually do the pango thing */
278 escaped = g_strdup (ev->text);
281 escaped = g_strdup (ev->text);
285 if (G_LIKELY (escaped)) {
286 len = strlen (escaped);
288 GST_DEBUG_OBJECT (kd, "kate event: %s, escaped %s", ev->text, escaped);
289 buffer = gst_buffer_new_and_alloc (len + 1);
290 if (G_LIKELY (buffer)) {
291 const char *mime = plain ? "text/plain" : "text/x-pango-markup";
292 GstCaps *caps = gst_caps_new_empty_simple (mime);
293 gst_caps_unref (caps);
294 /* allocate and copy the NULs, but don't include them in passed size */
295 gst_buffer_fill (buffer, 0, escaped, len + 1);
296 gst_buffer_resize (buffer, 0, len);
297 GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
298 GST_BUFFER_DURATION (buffer) =
299 (ev->end_time - ev->start_time) * GST_SECOND;
300 rflow = gst_pad_push (kd->srcpad, buffer);
301 if (rflow == GST_FLOW_NOT_LINKED) {
302 GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
303 } else if (rflow != GST_FLOW_OK) {
304 GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
305 gst_flow_get_name (rflow));
308 GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
309 ("Failed to create buffer"));
310 rflow = GST_FLOW_ERROR;
313 GST_WARNING_OBJECT (kd, "Empty string, nothing to do");
318 GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
319 ("Failed to allocate string"));
320 rflow = GST_FLOW_ERROR;
323 // if there's a background paletted bitmap, construct a DVD SPU for it
324 if (ev->bitmap && ev->palette) {
325 GstBuffer *buffer = gst_kate_spu_encode_spu (kd, ev);
327 GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
328 GST_BUFFER_DURATION (buffer) =
329 (ev->end_time - ev->start_time) * GST_SECOND;
330 rflow = gst_pad_push (kd->srcpad, buffer);
331 if (rflow == GST_FLOW_NOT_LINKED) {
332 GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
333 } else if (rflow != GST_FLOW_OK) {
334 GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
335 gst_flow_get_name (rflow));
338 GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
339 ("failed to create SPU from paletted bitmap"));
340 rflow = GST_FLOW_ERROR;
346 gst_object_unref (kd);
347 gst_buffer_unref (buf);
351 static GstStateChangeReturn
352 gst_kate_dec_change_state (GstElement * element, GstStateChange transition)
354 GstStateChangeReturn ret;
355 GstKateDec *kd = GST_KATE_DEC (element);
357 ret = gst_kate_decoder_base_change_state (&kd->decoder, element,
358 parent_class, transition);
360 if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) {
361 gst_caps_replace (&kd->src_caps, NULL);
368 gst_kate_dec_sink_query (GstPad * pad, GstQuery * query)
370 GstKateDec *kd = GST_KATE_DEC (gst_pad_get_parent (pad));
372 gst_kate_decoder_base_sink_query (&kd->decoder, GST_ELEMENT_CAST (kd),
374 gst_object_unref (kd);
379 gst_kate_dec_sink_event (GstPad * pad, GstEvent * event)
381 GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad)));
384 GST_LOG_OBJECT (pad, "Event on sink pad: %s", GST_EVENT_TYPE_NAME (event));
386 /* Delay events till we've set caps */
387 if (gst_kate_util_decoder_base_queue_event (&kd->decoder, event,
388 &gst_kate_dec_sink_handle_event, pad)) {
389 gst_object_unref (kd);
393 res = gst_kate_dec_sink_handle_event (pad, event);
395 gst_object_unref (kd);
401 gst_kate_dec_sink_handle_event (GstPad * pad, GstEvent * event)
403 GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad)));
406 GST_LOG_OBJECT (pad, "Handling event on sink pad: %s",
407 GST_EVENT_TYPE_NAME (event));
409 switch (GST_EVENT_TYPE (event)) {
410 case GST_EVENT_SEGMENT:
411 gst_kate_util_decoder_base_segment_event (&kd->decoder, event);
412 res = gst_pad_event_default (pad, event);
415 case GST_EVENT_FLUSH_START:
416 gst_kate_util_decoder_base_set_flushing (&kd->decoder, TRUE);
417 res = gst_pad_event_default (pad, event);
420 case GST_EVENT_FLUSH_STOP:
421 gst_kate_util_decoder_base_set_flushing (&kd->decoder, FALSE);
422 res = gst_pad_event_default (pad, event);
426 res = gst_pad_event_default (pad, event);
430 gst_object_unref (kd);
436 gst_kate_dec_src_get_caps (GstPad * pad, GstCaps * filter)
438 GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad)));
442 GST_DEBUG_OBJECT (kd, "We have src caps %" GST_PTR_FORMAT, kd->src_caps);
445 GST_DEBUG_OBJECT (kd, "We have no src caps, using template caps");
446 caps = gst_static_pad_template_get_caps (&src_factory);
449 caps = gst_caps_copy (caps);
451 gst_object_unref (kd);