3 * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
4 * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
5 * Copyright 2008 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., 51 Franklin St, Fifth Floor,
43 * Boston, MA 02110-1301, USA.
47 * SECTION:element-tiger
51 * This element decodes and renders Kate streams.
52 * [Kate](http://libkate.googlecode.com/) is a free codec for text based data,
53 * such as subtitles. Any number of kate streams can be embedded in an Ogg
56 * libkate (see above url) and [libtiger](http://libtiger.googlecode.com/)
57 * are needed to build this element.
61 * This pipeline renders a Kate stream on top of a Theora video multiplexed
65 * filesrc location=video.ogg ! oggdemux name=demux \
66 * demux. ! queue ! theoradec ! videoconvert ! tiger name=tiger \
67 * demux. ! queue ! kateparse ! tiger. \
68 * tiger. ! videoconvert ! autovideosink
80 #include <gst/glib-compat-private.h>
81 #include <gst/video/video.h>
83 #include "gstkateelements.h"
84 #include "gstkatetiger.h"
86 GST_DEBUG_CATEGORY_EXTERN (gst_katetiger_debug);
87 #define GST_CAT_DEFAULT gst_katetiger_debug
89 #define GST_KATE_TIGER_MUTEX_LOCK(element) \
91 /*GST_LOG_OBJECT ((element), "locking from %s:%d",__FILE__,__LINE__);*/ \
92 g_mutex_lock ((element)->mutex); \
93 /*GST_LOG_OBJECT ((element), "ready from %s:%d",__FILE__,__LINE__);*/ \
96 #define GST_KATE_TIGER_MUTEX_UNLOCK(element) \
98 /*GST_LOG_OBJECT ((element), "unlocking from %s:%d",__FILE__,__LINE__);*/ \
99 g_mutex_unlock ((element)->mutex); \
102 /* Filter signals and args */
111 ARG_DEFAULT_FONT_DESC = DECODER_BASE_ARG_COUNT,
113 ARG_DEFAULT_FONT_EFFECT,
114 ARG_DEFAULT_FONT_EFFECT_STRENGTH,
115 ARG_DEFAULT_FONT_RED,
116 ARG_DEFAULT_FONT_GREEN,
117 ARG_DEFAULT_FONT_BLUE,
118 ARG_DEFAULT_FONT_ALPHA,
119 ARG_DEFAULT_BACKGROUND_RED,
120 ARG_DEFAULT_BACKGROUND_GREEN,
121 ARG_DEFAULT_BACKGROUND_BLUE,
122 ARG_DEFAULT_BACKGROUND_ALPHA,
126 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
127 # define TIGER_ARGB_A 3
128 # define TIGER_ARGB_R 2
129 # define TIGER_ARGB_G 1
130 # define TIGER_ARGB_B 0
132 # define TIGER_ARGB_A 0
133 # define TIGER_ARGB_R 1
134 # define TIGER_ARGB_G 2
135 # define TIGER_ARGB_B 3
138 #define TIGER_UNPREMULTIPLY(a,r,g,b) G_STMT_START { \
139 b = (a > 0) ? MIN ((b * 255 + a / 2) / a, 255) : 0; \
140 g = (a > 0) ? MIN ((g * 255 + a / 2) / a, 255) : 0; \
141 r = (a > 0) ? MIN ((r * 255 + a / 2) / a, 255) : 0; \
144 static GstStaticPadTemplate kate_sink_factory =
145 GST_STATIC_PAD_TEMPLATE ("subtitle_sink",
148 GST_STATIC_CAPS ("subtitle/x-kate; application/x-kate")
151 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
152 #define TIGER_VIDEO_CAPS \
153 GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_BGRx ";" \
154 GST_VIDEO_CAPS_YUV ("{I420, YV12, AYUV, YUY2, UYVY, v308, v210," \
155 " v216, Y41B, Y42B, Y444, Y800, Y16, NV12, NV21, UYVP, A420," \
159 #define TIGER_VIDEO_CAPS \
160 GST_VIDEO_CAPS_BGRx ";" GST_VIDEO_CAPS_xRGB ";" \
161 GST_VIDEO_CAPS_YUV ("{I420, YV12, AYUV, YUY2, UYVY, v308, v210," \
162 " v216, Y41B, Y42B, Y444, Y800, Y16, NV12, NV21, UYVP, A420," \
166 static GstStaticPadTemplate video_sink_factory =
167 GST_STATIC_PAD_TEMPLATE ("video_sink",
170 GST_STATIC_CAPS (TIGER_VIDEO_CAPS));
172 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
175 GST_STATIC_CAPS (TIGER_VIDEO_CAPS));
177 GST_DEBUG_CATEGORY (gst_katetiger_debug);
179 GST_BOILERPLATE (GstKateTiger, gst_kate_tiger, GstElement, GST_TYPE_ELEMENT);
181 kate_element_init (plugin); \
182 GST_DEBUG_CATEGORY_INIT (gst_katetiger_debug, "tiger", 0, \
183 "Kate Tiger renderer");
184 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (tiger, "tiger", GST_RANK_NONE,
185 GST_TYPE_KATE_TIGER, _do_init);
188 gst_kate_tiger_font_effect_get_type (void)
190 static GType font_effect_type = 0;
192 if (!font_effect_type) {
193 static const GEnumValue font_effects[] = {
194 {tiger_font_plain, "none", "none"},
195 {tiger_font_shadow, "shadow", "shadow"},
196 {tiger_font_outline, "outline", "outline"},
199 font_effect_type = g_enum_register_static ("GstFontEffect", font_effects);
202 return font_effect_type;
205 static void gst_kate_tiger_set_property (GObject * object, guint prop_id,
206 const GValue * value, GParamSpec * pspec);
207 static void gst_kate_tiger_get_property (GObject * object, guint prop_id,
208 GValue * value, GParamSpec * pspec);
209 static void gst_kate_tiger_dispose (GObject * object);
211 static GstFlowReturn gst_kate_tiger_kate_chain (GstPad * pad, GstBuffer * buf);
212 static GstFlowReturn gst_kate_tiger_video_chain (GstPad * pad, GstBuffer * buf);
213 static GstStateChangeReturn gst_kate_tiger_change_state (GstElement * element,
214 GstStateChange transition);
215 static gboolean gst_kate_tiger_kate_sink_query (GstPad * pad, GstQuery * query);
216 static gboolean gst_kate_tiger_kate_event (GstPad * pad, GstEvent * event);
217 static gboolean gst_kate_tiger_video_event (GstPad * pad, GstEvent * event);
218 static gboolean gst_kate_tiger_video_set_caps (GstPad * pad, GstCaps * caps);
219 static gboolean gst_kate_tiger_source_event (GstPad * pad, GstEvent * event);
222 gst_kate_tiger_base_init (gpointer gclass)
225 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
227 gst_element_class_add_static_pad_template (element_class, &src_factory);
228 gst_element_class_add_static_pad_template (element_class, &kate_sink_factory);
229 gst_element_class_add_static_pad_template (element_class,
230 &video_sink_factory);
231 gst_element_class_set_static_metadata (element_class, "Kate stream renderer",
232 "Mixer/Video/Overlay/Subtitle",
233 "Decodes and renders Kate streams on top of a video",
234 "Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>");
237 /* initialize the plugin's class */
239 gst_kate_tiger_class_init (GstKateTigerClass * klass)
241 GObjectClass *gobject_class;
242 GstElementClass *gstelement_class;
244 gobject_class = (GObjectClass *) klass;
245 gstelement_class = (GstElementClass *) klass;
247 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_kate_tiger_get_property);
248 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_kate_tiger_set_property);
249 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_kate_tiger_dispose);
251 gst_kate_util_install_decoder_base_properties (gobject_class);
253 g_object_class_install_property (gobject_class, ARG_QUALITY,
254 g_param_spec_double ("quality", "Rendering quality",
255 "Rendering quality (0 is faster, 1 is best and slower)",
256 0.0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
258 g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_DESC,
259 g_param_spec_string ("default-font-desc", "Default font description",
260 "Default font description (Pango style) to render text with",
261 "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
263 g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_EFFECT,
264 g_param_spec_enum ("default-font-effect", "Default font effect",
265 "Whether to apply an effect to text by default, for increased readability",
266 gst_kate_tiger_font_effect_get_type (),
268 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
270 g_object_class_install_property (gobject_class,
271 ARG_DEFAULT_FONT_EFFECT_STRENGTH,
272 g_param_spec_double ("default-font-effect-strength",
273 "Default font effect strength",
274 "How pronounced should the font effect be (effect dependent)", 0.0,
275 1.0, 0.5, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
277 g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_RED,
278 g_param_spec_int ("default-font-red",
279 "Default font color (red component)",
280 "Default font color (red component, between 0 and 255) to render text with",
281 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283 g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_GREEN,
284 g_param_spec_int ("default-font-green",
285 "Default font color (green component)",
286 "Default font color (green component, between 0 and 255) to render text with",
287 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
289 g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_BLUE,
290 g_param_spec_int ("default-font-blue",
291 "Default font color (blue component)",
292 "Default font color (blue component, between 0 and 255) to render text with",
293 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
295 g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_ALPHA,
296 g_param_spec_int ("default-font-alpha",
297 "Default font color (alpha component)",
298 "Default font color (alpha component, between 0 and 255) to render text with",
299 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
301 g_object_class_install_property (gobject_class, ARG_DEFAULT_BACKGROUND_RED,
302 g_param_spec_int ("default-background-red",
303 "Default background color (red component)",
304 "Default background color (red component, between 0 and 255) to render text with",
305 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
307 g_object_class_install_property (gobject_class, ARG_DEFAULT_BACKGROUND_GREEN,
308 g_param_spec_int ("default-background-green",
309 "Default background color (green component)",
310 "Default background color (green component, between 0 and 255) to render text with",
311 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
313 g_object_class_install_property (gobject_class, ARG_DEFAULT_BACKGROUND_BLUE,
314 g_param_spec_int ("default-background-blue",
315 "Default background color (blue component)",
316 "Default background color (blue component, between 0 and 255) to render text with",
317 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
319 g_object_class_install_property (gobject_class, ARG_DEFAULT_BACKGROUND_ALPHA,
320 g_param_spec_int ("default-background-alpha",
321 "Default background color (alpha component)",
322 "Default background color (alpha component, between 0 and 255) to render text with",
323 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
325 /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
326 g_object_class_install_property (gobject_class, ARG_SILENT,
327 g_param_spec_boolean ("silent", "silent",
328 "Whether to render the stream",
329 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
331 gstelement_class->change_state =
332 GST_DEBUG_FUNCPTR (gst_kate_tiger_change_state);
335 /* initialize the new element
336 * instantiate pads and add them to element
338 * initialize structure
341 gst_kate_tiger_init (GstKateTiger * tiger, GstKateTigerClass * gclass)
343 GST_DEBUG_OBJECT (tiger, "gst_kate_tiger_init");
345 tiger->mutex = g_mutex_new ();
346 tiger->cond = g_cond_new ();
349 gst_pad_new_from_static_template (&kate_sink_factory, "subtitle_sink");
350 gst_pad_set_chain_function (tiger->katesinkpad,
351 GST_DEBUG_FUNCPTR (gst_kate_tiger_kate_chain));
352 gst_pad_set_query_function (tiger->katesinkpad,
353 GST_DEBUG_FUNCPTR (gst_kate_tiger_kate_sink_query));
354 gst_pad_set_event_function (tiger->katesinkpad,
355 GST_DEBUG_FUNCPTR (gst_kate_tiger_kate_event));
356 gst_element_add_pad (GST_ELEMENT (tiger), tiger->katesinkpad);
358 tiger->videosinkpad =
359 gst_pad_new_from_static_template (&video_sink_factory, "video_sink");
360 gst_pad_set_chain_function (tiger->videosinkpad,
361 GST_DEBUG_FUNCPTR (gst_kate_tiger_video_chain));
362 gst_pad_use_fixed_caps (tiger->videosinkpad);
363 gst_pad_set_setcaps_function (tiger->videosinkpad,
364 GST_DEBUG_FUNCPTR (gst_kate_tiger_video_set_caps));
365 gst_pad_set_event_function (tiger->videosinkpad,
366 GST_DEBUG_FUNCPTR (gst_kate_tiger_video_event));
367 gst_element_add_pad (GST_ELEMENT (tiger), tiger->videosinkpad);
369 tiger->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
370 gst_pad_set_event_function (tiger->srcpad, gst_kate_tiger_source_event);
371 gst_pad_use_fixed_caps (tiger->srcpad);
372 gst_element_add_pad (GST_ELEMENT (tiger), tiger->srcpad);
374 gst_kate_util_decode_base_init (&tiger->decoder, FALSE);
378 tiger->default_font_desc = NULL;
379 tiger->quality = -1.0;
380 tiger->default_font_effect = tiger_font_outline;
381 tiger->default_font_effect_strength = 0.5;
382 tiger->default_font_r = 255;
383 tiger->default_font_g = 255;
384 tiger->default_font_b = 255;
385 tiger->default_font_a = 255;
386 tiger->default_background_r = 0;
387 tiger->default_background_g = 0;
388 tiger->default_background_b = 0;
389 tiger->default_background_a = 0;
390 tiger->silent = FALSE;
392 tiger->video_width = 0;
393 tiger->video_height = 0;
395 tiger->composition = NULL;
397 tiger->seen_header = FALSE;
401 gst_kate_tiger_dispose (GObject * object)
403 GstKateTiger *tiger = GST_KATE_TIGER (object);
405 GST_LOG_OBJECT (tiger, "disposing");
407 if (tiger->default_font_desc) {
408 g_free (tiger->default_font_desc);
409 tiger->default_font_desc = NULL;
412 if (tiger->render_buffer) {
413 gst_buffer_unref (tiger->render_buffer);
414 tiger->render_buffer = NULL;
417 g_cond_free (tiger->cond);
420 g_mutex_free (tiger->mutex);
423 if (tiger->composition) {
424 gst_video_overlay_composition_unref (tiger->composition);
425 tiger->composition = NULL;
428 GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
432 gst_kate_tiger_update_quality (GstKateTiger * tiger)
434 if (tiger->tr && tiger->quality >= 0.0) {
435 tiger_renderer_set_quality (tiger->tr, tiger->quality);
440 gst_kate_tiger_update_default_font_effect (GstKateTiger * tiger)
443 tiger_renderer_set_default_font_effect (tiger->tr,
444 tiger->default_font_effect, tiger->default_font_effect_strength);
449 gst_kate_tiger_update_default_font_color (GstKateTiger * tiger)
452 tiger_renderer_set_default_font_color (tiger->tr,
453 tiger->default_font_r / 255.0,
454 tiger->default_font_g / 255.0,
455 tiger->default_font_b / 255.0, tiger->default_font_a / 255.0);
460 gst_kate_tiger_update_default_background_color (GstKateTiger * tiger)
463 tiger_renderer_set_default_background_fill_color (tiger->tr,
464 tiger->default_background_r / 255.0,
465 tiger->default_background_g / 255.0,
466 tiger->default_background_b / 255.0,
467 tiger->default_background_a / 255.0);
472 gst_kate_tiger_set_property (GObject * object, guint prop_id,
473 const GValue * value, GParamSpec * pspec)
475 GstKateTiger *tiger = GST_KATE_TIGER (object);
478 GST_KATE_TIGER_MUTEX_LOCK (tiger);
481 case ARG_DEFAULT_FONT_DESC:
482 if (tiger->default_font_desc) {
483 g_free (tiger->default_font_desc);
484 tiger->default_font_desc = NULL;
486 str = g_value_get_string (value);
488 tiger->default_font_desc = g_strdup (str);
490 tiger_renderer_set_default_font_description (tiger->tr,
491 tiger->default_font_desc);
495 tiger->quality = g_value_get_double (value);
496 gst_kate_tiger_update_quality (tiger);
498 case ARG_DEFAULT_FONT_EFFECT:
499 tiger->default_font_effect = g_value_get_enum (value);
500 gst_kate_tiger_update_default_font_effect (tiger);
502 case ARG_DEFAULT_FONT_EFFECT_STRENGTH:
503 tiger->default_font_effect_strength = g_value_get_double (value);
504 gst_kate_tiger_update_default_font_effect (tiger);
506 case ARG_DEFAULT_FONT_RED:
507 tiger->default_font_r = g_value_get_int (value);
508 gst_kate_tiger_update_default_font_color (tiger);
510 case ARG_DEFAULT_FONT_GREEN:
511 tiger->default_font_g = g_value_get_int (value);
512 gst_kate_tiger_update_default_font_color (tiger);
514 case ARG_DEFAULT_FONT_BLUE:
515 tiger->default_font_b = g_value_get_int (value);
516 gst_kate_tiger_update_default_font_color (tiger);
518 case ARG_DEFAULT_FONT_ALPHA:
519 tiger->default_font_a = g_value_get_int (value);
520 gst_kate_tiger_update_default_font_color (tiger);
522 case ARG_DEFAULT_BACKGROUND_RED:
523 tiger->default_background_r = g_value_get_int (value);
524 gst_kate_tiger_update_default_background_color (tiger);
526 case ARG_DEFAULT_BACKGROUND_GREEN:
527 tiger->default_background_g = g_value_get_int (value);
528 gst_kate_tiger_update_default_background_color (tiger);
530 case ARG_DEFAULT_BACKGROUND_BLUE:
531 tiger->default_background_b = g_value_get_int (value);
532 gst_kate_tiger_update_default_background_color (tiger);
534 case ARG_DEFAULT_BACKGROUND_ALPHA:
535 tiger->default_background_a = g_value_get_int (value);
536 gst_kate_tiger_update_default_background_color (tiger);
539 tiger->silent = g_value_get_boolean (value);
542 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
546 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
550 gst_kate_tiger_get_property (GObject * object, guint prop_id,
551 GValue * value, GParamSpec * pspec)
553 GstKateTiger *tiger = GST_KATE_TIGER (object);
555 GST_KATE_TIGER_MUTEX_LOCK (tiger);
558 case ARG_DEFAULT_FONT_DESC:
559 g_value_set_string (value,
560 tiger->default_font_desc ? tiger->default_font_desc : "");
563 g_value_set_double (value, tiger->quality);
565 case ARG_DEFAULT_FONT_EFFECT:
566 g_value_set_enum (value, tiger->default_font_effect);
568 case ARG_DEFAULT_FONT_EFFECT_STRENGTH:
569 g_value_set_double (value, tiger->default_font_effect_strength);
571 case ARG_DEFAULT_FONT_RED:
572 g_value_set_int (value, tiger->default_font_r);
574 case ARG_DEFAULT_FONT_GREEN:
575 g_value_set_int (value, tiger->default_font_g);
577 case ARG_DEFAULT_FONT_BLUE:
578 g_value_set_int (value, tiger->default_font_b);
580 case ARG_DEFAULT_FONT_ALPHA:
581 g_value_set_int (value, tiger->default_font_a);
583 case ARG_DEFAULT_BACKGROUND_RED:
584 g_value_set_int (value, tiger->default_background_r);
586 case ARG_DEFAULT_BACKGROUND_GREEN:
587 g_value_set_int (value, tiger->default_background_g);
589 case ARG_DEFAULT_BACKGROUND_BLUE:
590 g_value_set_int (value, tiger->default_background_b);
592 case ARG_DEFAULT_BACKGROUND_ALPHA:
593 g_value_set_int (value, tiger->default_background_a);
596 g_value_set_boolean (value, tiger->silent);
599 if (!gst_kate_util_decoder_base_get_property (&tiger->decoder, object,
600 prop_id, value, pspec)) {
601 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
606 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
609 /* GstElement vmethod implementations */
612 * this function does the actual processing
616 gst_kate_tiger_kate_chain (GstPad * pad, GstBuffer * buf)
618 GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad));
619 const kate_event *ev = NULL;
620 GstFlowReturn rflow = GST_FLOW_OK;
622 GST_KATE_TIGER_MUTEX_LOCK (tiger);
624 GST_LOG_OBJECT (tiger, "Got kate buffer, caps %" GST_PTR_FORMAT,
625 GST_BUFFER_CAPS (buf));
627 /* Now that we have the lock, check if we're flushing */
628 if (tiger->decoder.kate_flushing) {
629 GST_DEBUG_OBJECT (tiger, "Flushing, disregarding buffer");
633 /* Unfortunately, it can happen that the start of the stream is not sent,
634 for instance if there's a stream selector upstream, which is switched
635 from another Kate stream. If this happens, then we can fallback on the
636 headers stored in the caps (if any). */
637 if (!tiger->seen_header) {
638 if (GST_BUFFER_SIZE (buf) == 0 || (GST_BUFFER_DATA (buf)[0] & 0x80) == 0) {
639 /* Not a header, try to fall back on caps */
641 const GValue *streamheader;
643 GST_INFO_OBJECT (tiger, "Headers not seen, start of stream is cut off");
644 s = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
645 streamheader = gst_structure_get_value (s, "streamheader");
646 if (streamheader && G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
647 GstPad *tagpad = gst_pad_get_peer (pad);
651 GST_INFO_OBJECT (tiger, "Falling back on caps to initialize decoder");
652 array = g_value_peek_pointer (streamheader);
653 for (i = 0; i < array->len; i++) {
654 GValue *value = &g_array_index (array, GValue, i);
655 if (G_VALUE_TYPE (value) == GST_TYPE_BUFFER) {
656 GstBuffer *hbuf = g_value_peek_pointer (value);
657 gst_buffer_ref (hbuf);
659 gst_kate_util_decoder_base_chain_kate_packet (&tiger->decoder,
660 GST_ELEMENT_CAST (tiger), pad, hbuf, tiger->srcpad, tagpad,
663 GST_WARNING_OBJECT (tiger,
664 "Streamheader index %d does not hold a buffer", i);
667 gst_object_unref (tagpad);
668 tiger->seen_header = TRUE;
670 GST_WARNING_OBJECT (tiger, "No headers seen, and no headers on caps");
673 tiger->seen_header = TRUE;
677 if (gst_kate_util_decoder_base_update_segment (&tiger->decoder,
678 GST_ELEMENT_CAST (tiger), buf)) {
679 GstPad *tagpad = gst_pad_get_peer (pad);
681 gst_kate_util_decoder_base_chain_kate_packet (&tiger->decoder,
682 GST_ELEMENT_CAST (tiger), pad, buf, tiger->srcpad, tagpad, NULL, &ev);
683 if (G_LIKELY (rflow == GST_FLOW_OK)) {
685 int ret = tiger_renderer_add_event (tiger->tr, ev->ki, ev);
686 GST_INFO_OBJECT (tiger, "adding event for %p from %f to %f: %p, \"%s\"",
687 ev->ki, ev->start_time, ev->end_time, ev->bitmap, ev->text);
688 if (G_UNLIKELY (ret < 0)) {
689 GST_WARNING_OBJECT (tiger,
690 "failed to add Kate event to Tiger renderer: %s",
691 gst_kate_util_get_error_message (ret));
695 gst_object_unref (tagpad);
698 /* we want to avoid shooting ahead of the video stream, or we will
699 get segment updates which will place us ahead of it, and we won't
700 be able to convert a video timestamp back into a kate timestamp */
701 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buf))) {
703 gint64 kate_time, video_time;
705 gst_segment_to_running_time (&tiger->decoder.kate_segment,
706 GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf));
708 gst_segment_to_running_time (&tiger->video_segment, GST_FORMAT_TIME,
709 tiger->video_segment.last_stop);
710 GST_DEBUG_OBJECT (tiger, "Kate time %.2f, video time %.2f (kts %ld)",
711 kate_time / (float) GST_SECOND, video_time / (float) GST_SECOND,
712 (long) GST_BUFFER_TIMESTAMP (buf));
713 if (kate_time <= video_time) {
716 GST_LOG_OBJECT (tiger, "Waiting to return from chain function");
717 g_cond_wait (tiger->cond, tiger->mutex);
718 if (tiger->decoder.kate_flushing) {
719 GST_DEBUG_OBJECT (tiger, "Flushing while waiting");
722 GST_LOG_OBJECT (tiger, "Woken up, checking time again");
727 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
729 gst_object_unref (tiger);
730 gst_buffer_unref (buf);
736 gst_kate_tiger_video_set_caps (GstPad * pad, GstCaps * caps)
738 GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad));
739 GstVideoFormat format;
743 GST_KATE_TIGER_MUTEX_LOCK (tiger);
745 /* Cairo expects ARGB in native endianness, and that's what we get
746 as we've forced it in the caps. We might allow swapped red/blue
747 at some point, and get tiger to swap, to make some cases faster */
748 tiger->swap_rgb = FALSE;
750 if (gst_video_format_parse_caps (caps, &format, &w, &h)) {
751 tiger->video_format = format;
752 tiger->video_width = w;
753 tiger->video_height = h;
756 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
758 ret = gst_pad_set_caps (tiger->srcpad, caps);
760 gst_object_unref (tiger);
765 gst_kate_tiger_get_time (GstKateTiger * tiger)
768 gst_segment_to_running_time (&tiger->video_segment, GST_FORMAT_TIME,
769 tiger->video_segment.last_stop);
771 gst_segment_to_position (&tiger->decoder.kate_segment, GST_FORMAT_TIME,
773 return pos / (gdouble) GST_SECOND;
777 gst_kate_tiger_set_composition (GstKateTiger * tiger)
779 GstVideoOverlayRectangle *rectangle;
781 if (tiger->render_buffer) {
782 rectangle = gst_video_overlay_rectangle_new_argb (tiger->render_buffer,
783 tiger->video_width, tiger->video_height, 4 * tiger->video_width,
784 0, 0, tiger->video_width, tiger->video_height,
785 GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA);
787 if (tiger->composition)
788 gst_video_overlay_composition_unref (tiger->composition);
789 tiger->composition = gst_video_overlay_composition_new (rectangle);
790 gst_video_overlay_rectangle_unref (rectangle);
792 } else if (tiger->composition) {
793 gst_video_overlay_composition_unref (tiger->composition);
794 tiger->composition = NULL;
799 gst_kate_tiger_video_chain (GstPad * pad, GstBuffer * buf)
801 GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad));
802 GstFlowReturn rflow = GST_FLOW_OK;
807 GST_KATE_TIGER_MUTEX_LOCK (tiger);
809 GST_LOG_OBJECT (tiger, "got video frame, %u bytes", GST_BUFFER_SIZE (buf));
811 if (G_UNLIKELY (tiger->video_flushing)) {
812 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
813 gst_object_unref (tiger);
814 gst_buffer_unref (buf);
815 return GST_FLOW_FLUSHING;
818 if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buf))) {
819 gst_segment_set_last_stop (&tiger->video_segment, GST_FORMAT_TIME,
820 GST_BUFFER_TIMESTAMP (buf));
821 g_cond_broadcast (tiger->cond);
824 /* Update first with a dummy buffer pointer we cannot write to, but with the
825 right dimensions. If there is nothing to draw, we will not have to make
827 ptr = GST_BUFFER_DATA (buf);
829 tiger_renderer_set_buffer (tiger->tr, ptr, tiger->video_width,
830 tiger->video_height, tiger->video_width * 4, tiger->swap_rgb);
831 if (G_UNLIKELY (ret < 0)) {
832 GST_WARNING_OBJECT (tiger,
833 "Tiger renderer failed to set buffer to video frame: %s",
834 gst_kate_util_get_error_message (ret));
838 /* update the renderer at the time of the video frame */
839 t = gst_kate_tiger_get_time (tiger);
840 GST_LOG_OBJECT (tiger, "Video segment calc: last stop %ld, time %.3f",
841 (long) tiger->video_segment.last_stop, t);
842 ret = tiger_renderer_update (tiger->tr, t, 1);
843 if (G_UNLIKELY (ret < 0)) {
844 GST_WARNING_OBJECT (tiger, "Tiger renderer failed to update: %s",
845 gst_kate_util_get_error_message (ret));
849 /* if there nothing to draw, we can just push the video buffer as is */
850 if (ret > 0 || tiger->silent)
853 /* there is something to draw, so first make the buffer writable */
854 buf = gst_buffer_make_writable (buf);
855 if (G_UNLIKELY (!buf)) {
856 GST_WARNING_OBJECT (tiger, "Failed to make video buffer writable");
860 /* and setup that buffer before rendering */
861 if (gst_video_format_is_yuv (tiger->video_format)) {
862 if (!tiger->render_buffer) {
863 tiger->render_buffer =
864 gst_buffer_new_and_alloc (tiger->video_width * tiger->video_height *
867 ptr = GST_BUFFER_DATA (tiger->render_buffer);
868 tiger_renderer_set_surface_clear_color (tiger->tr, 1, 0.0, 0.0, 0.0, 0.0);
870 ptr = GST_BUFFER_DATA (buf);
873 tiger_renderer_set_buffer (tiger->tr, ptr, tiger->video_width,
874 tiger->video_height, tiger->video_width * 4, tiger->swap_rgb);
875 if (G_UNLIKELY (ret < 0)) {
876 GST_WARNING_OBJECT (tiger,
877 "Tiger renderer failed to set buffer to video frame: %s",
878 gst_kate_util_get_error_message (ret));
881 ret = tiger_renderer_render (tiger->tr);
882 if (G_UNLIKELY (ret < 0)) {
883 GST_WARNING_OBJECT (tiger,
884 "Tiger renderer failed to render to video frame: %s",
885 gst_kate_util_get_error_message (ret));
887 GST_LOG_OBJECT (tiger, "Tiger renderer rendered on video frame at %f", t);
890 if (gst_video_format_is_yuv (tiger->video_format)) {
891 gst_kate_tiger_set_composition (tiger);
892 if (tiger->composition)
893 gst_video_overlay_composition_blend (tiger->composition, buf);
897 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
899 rflow = gst_pad_push (tiger->srcpad, buf);
901 gst_object_unref (tiger);
906 static GstStateChangeReturn
907 gst_kate_tiger_change_state (GstElement * element, GstStateChange transition)
909 GstKateTiger *tiger = GST_KATE_TIGER (element);
910 GstStateChangeReturn res;
912 switch (transition) {
913 case GST_STATE_CHANGE_PAUSED_TO_READY:
914 GST_DEBUG_OBJECT (tiger, "PAUSED -> READY, clearing kate state");
915 GST_KATE_TIGER_MUTEX_LOCK (tiger);
916 gst_kate_util_decoder_base_set_flushing (&tiger->decoder, TRUE);
917 g_cond_broadcast (tiger->cond);
919 tiger_renderer_destroy (tiger->tr);
922 gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED);
923 tiger->video_flushing = TRUE;
924 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
931 gst_kate_decoder_base_change_state (&tiger->decoder, element,
932 parent_class, transition);
934 switch (transition) {
935 case GST_STATE_CHANGE_READY_TO_PAUSED:
936 GST_DEBUG_OBJECT (tiger, "READY -> PAUSED, initializing kate state");
937 GST_KATE_TIGER_MUTEX_LOCK (tiger);
938 if (tiger->decoder.initialized) {
939 int ret = tiger_renderer_create (&tiger->tr);
941 GST_WARNING_OBJECT (tiger, "failed to create tiger renderer: %s",
942 gst_kate_util_get_error_message (ret));
945 tiger_renderer_set_default_font_description (tiger->tr,
946 tiger->default_font_desc);
948 GST_WARNING_OBJECT (tiger,
949 "failed to set tiger default font description: %s",
950 gst_kate_util_get_error_message (ret));
952 gst_kate_tiger_update_default_font_color (tiger);
953 gst_kate_tiger_update_default_background_color (tiger);
954 gst_kate_tiger_update_default_font_effect (tiger);
955 gst_kate_tiger_update_quality (tiger);
958 gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED);
959 tiger->video_flushing = FALSE;
960 tiger->seen_header = FALSE;
961 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
971 gst_kate_tiger_seek (GstKateTiger * tiger, GstPad * pad, GstEvent * event)
976 GstSeekType cur_type, stop_type;
979 gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
982 if (flags & GST_SEEK_FLAG_FLUSH)
983 gst_pad_push_event (tiger->srcpad, gst_event_new_flush_start ());
985 GST_KATE_TIGER_MUTEX_LOCK (tiger);
986 tiger->video_flushing = TRUE;
987 gst_kate_util_decoder_base_set_flushing (&tiger->decoder, TRUE);
988 g_cond_broadcast (tiger->cond);
989 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
991 if (format == GST_FORMAT_TIME) {
992 /* if seeking in time, we can update tiger to remove any appropriate events */
995 case GST_SEEK_TYPE_SET:
996 target = cur / (float) GST_SECOND;
998 case GST_SEEK_TYPE_CUR:
999 GST_WARNING_OBJECT (tiger,
1000 "Seeking from the current segment, cannot work out target so flushing everything");
1001 target = (kate_float) 0;
1003 case GST_SEEK_TYPE_END:
1004 GST_WARNING_OBJECT (tiger,
1005 "Seeking from the end, cannot work out target so flushing everything");
1006 target = (kate_float) 0;
1009 GST_WARNING_OBJECT (tiger, "Unexpected seek type");
1010 target = (kate_float) 0;
1013 GST_INFO_OBJECT (tiger, "Seeking in time to %f", target);
1014 GST_KATE_TIGER_MUTEX_LOCK (tiger);
1015 tiger_renderer_seek (tiger->tr, target);
1016 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
1019 /* forward to both sinks */
1020 gst_event_ref (event);
1021 if (gst_pad_push_event (tiger->videosinkpad, event)) {
1022 int ret = gst_pad_push_event (tiger->katesinkpad, event);
1029 gst_event_unref (event);
1035 gst_kate_tiger_source_event (GstPad * pad, GstEvent * event)
1037 GstKateTiger *tiger =
1038 (GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
1039 gboolean res = TRUE;
1041 g_return_val_if_fail (tiger != NULL, FALSE);
1043 GST_LOG_OBJECT (tiger, "Event on source pad: %s",
1044 GST_EVENT_TYPE_NAME (event));
1046 switch (GST_EVENT_TYPE (event)) {
1047 case GST_EVENT_SEEK:
1048 GST_INFO_OBJECT (tiger, "Seek on source pad");
1049 res = gst_kate_tiger_seek (tiger, pad, event);
1052 res = gst_pad_event_default (pad, event);
1056 gst_object_unref (tiger);
1062 gst_kate_tiger_handle_kate_event (GstPad * pad, GstEvent * event)
1064 GstKateTiger *tiger =
1065 (GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
1066 gboolean res = TRUE;
1068 switch (GST_EVENT_TYPE (event)) {
1069 case GST_EVENT_NEWSEGMENT:
1070 GST_INFO_OBJECT (tiger, "New segment on Kate pad");
1071 GST_KATE_TIGER_MUTEX_LOCK (tiger);
1072 g_cond_broadcast (tiger->cond);
1073 gst_kate_util_decoder_base_new_segment_event (&tiger->decoder, event);
1074 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
1075 gst_event_unref (event);
1077 case GST_EVENT_FLUSH_START:
1078 GST_KATE_TIGER_MUTEX_LOCK (tiger);
1079 gst_kate_util_decoder_base_set_flushing (&tiger->decoder, TRUE);
1080 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
1081 g_cond_broadcast (tiger->cond);
1082 gst_event_unref (event);
1084 case GST_EVENT_FLUSH_STOP:
1085 GST_KATE_TIGER_MUTEX_LOCK (tiger);
1086 gst_kate_util_decoder_base_set_flushing (&tiger->decoder, FALSE);
1087 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
1088 gst_event_unref (event);
1091 /* we ignore this, it just means we don't have anymore Kate packets, but
1092 the Tiger renderer will still draw (if appropriate) on incoming video */
1093 GST_INFO_OBJECT (tiger, "EOS on Kate pad");
1094 GST_KATE_TIGER_MUTEX_LOCK (tiger);
1095 g_cond_broadcast (tiger->cond);
1096 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
1097 gst_event_unref (event);
1100 res = gst_pad_event_default (pad, event);
1104 gst_object_unref (tiger);
1110 gst_kate_tiger_kate_event (GstPad * pad, GstEvent * event)
1112 GstKateTiger *tiger =
1113 (GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
1114 gboolean res = TRUE;
1116 g_return_val_if_fail (tiger != NULL, FALSE);
1118 GST_LOG_OBJECT (tiger, "Event on Kate pad: %s", GST_EVENT_TYPE_NAME (event));
1120 /* Delay events till we've set caps */
1121 if (gst_kate_util_decoder_base_queue_event (&tiger->decoder, event,
1122 &gst_kate_tiger_handle_kate_event, pad)) {
1123 gst_object_unref (tiger);
1127 res = gst_kate_tiger_handle_kate_event (pad, event);
1129 gst_object_unref (tiger);
1135 gst_kate_tiger_handle_video_event (GstPad * pad, GstEvent * event)
1137 GstKateTiger *tiger =
1138 (GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
1139 gboolean res = TRUE;
1141 switch (GST_EVENT_TYPE (event)) {
1142 case GST_EVENT_NEWSEGMENT:
1145 gdouble rate, arate;
1147 gint64 start, stop, time;
1149 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
1150 &start, &stop, &time);
1152 if (format == GST_FORMAT_TIME) {
1153 GST_DEBUG_OBJECT (tiger, "video pad segment:"
1154 " Update %d, rate %g arate %g format %d start %" GST_TIME_FORMAT
1155 " %" GST_TIME_FORMAT " position %" GST_TIME_FORMAT,
1156 update, rate, arate, format, GST_TIME_ARGS (start),
1157 GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
1159 GST_KATE_TIGER_MUTEX_LOCK (tiger);
1160 gst_segment_set_newsegment_full (&tiger->video_segment, update, rate,
1161 arate, format, start, stop, time);
1162 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
1165 res = gst_pad_event_default (pad, event);
1168 case GST_EVENT_FLUSH_START:
1169 GST_KATE_TIGER_MUTEX_LOCK (tiger);
1170 gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED);
1171 tiger->video_flushing = TRUE;
1172 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
1173 g_cond_broadcast (tiger->cond);
1174 res = gst_pad_event_default (pad, event);
1176 case GST_EVENT_FLUSH_STOP:
1177 GST_KATE_TIGER_MUTEX_LOCK (tiger);
1178 gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED);
1179 tiger->video_flushing = FALSE;
1180 GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
1181 res = gst_pad_event_default (pad, event);
1184 res = gst_pad_event_default (pad, event);
1188 gst_object_unref (tiger);
1194 gst_kate_tiger_video_event (GstPad * pad, GstEvent * event)
1196 GstKateTiger *tiger =
1197 (GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
1198 gboolean res = TRUE;
1200 g_return_val_if_fail (tiger != NULL, FALSE);
1202 GST_INFO_OBJECT (tiger, "Event on video pad: %s",
1203 GST_EVENT_TYPE_NAME (event));
1205 res = gst_kate_tiger_handle_video_event (pad, event);
1207 gst_object_unref (tiger);
1213 gst_kate_tiger_kate_sink_query (GstPad * pad, GstQuery * query)
1215 GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad));
1216 gboolean res = gst_kate_decoder_base_sink_query (&tiger->decoder,
1217 GST_ELEMENT_CAST (tiger), pad, query);
1218 GST_INFO_OBJECT (tiger, "Query on Kate pad");
1219 gst_object_unref (tiger);