gstreamer/ext/kate/gstkatetiger.c
Vincent Penquerc'h 0b790b663c kate: ensure the kate pad does not shoot ahead of the video pad
Sync both pads by waiting in the kate chain function.
Do not reset our internal segment from segment updates, in order
to be able to map video running time to kate running time, to
give libtiger the timestamp it expects. This allows us to use
running time to sync to video, which is The Right Way.

https://bugzilla.gnome.org/show_bug.cgi?id=600929
2011-01-14 00:28:41 +00:00

1096 lines
36 KiB
C

/*
* GStreamer
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-tiger
* @see_also: katedec
*
* <refsect2>
* <para>
* This element decodes and renders Kate streams
* <ulink url="http://libkate.googlecode.com/">Kate</ulink> 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) and <ulink url="http://libtiger.googlecode.com/">libtiger</ulink>
* are needed to build this element.
* </para>
* <title>Example pipeline</title>
* <para>
* This pipeline renders a Kate stream on top of a Theora video multiplexed
* in the same stream:
* <programlisting>
* gst-launch \
* filesrc location=video.ogg ! oggdemux name=demux \
* demux. ! queue ! theoradec ! ffmpegcolorspace ! tiger name=tiger \
* demux. ! queue ! kateparse ! tiger. \
* tiger. ! ffmpegcolorspace ! autovideosink
* </programlisting>
* </para>
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include <gst/video/video.h>
#include "gstkate.h"
#include "gstkatetiger.h"
GST_DEBUG_CATEGORY_EXTERN (gst_katetiger_debug);
#define GST_CAT_DEFAULT gst_katetiger_debug
#define GST_KATE_TIGER_MUTEX_LOCK(element) \
do { \
/*GST_LOG_OBJECT ((element), "locking from %s:%d\n",__FILE__,__LINE__);*/ \
g_mutex_lock ((element)->mutex); \
/*GST_LOG_OBJECT ((element), "ready from %s:%d\n",__FILE__,__LINE__);*/ \
} while(0)
#define GST_KATE_TIGER_MUTEX_UNLOCK(element) \
do { \
/*GST_LOG_OBJECT ((element), "unlocking from %s:%d\n",__FILE__,__LINE__);*/ \
g_mutex_unlock ((element)->mutex); \
} while(0)
/* Filter signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_DEFAULT_FONT_DESC = DECODER_BASE_ARG_COUNT,
ARG_QUALITY,
ARG_DEFAULT_FONT_EFFECT,
ARG_DEFAULT_FONT_EFFECT_STRENGTH,
ARG_DEFAULT_FONT_RED,
ARG_DEFAULT_FONT_GREEN,
ARG_DEFAULT_FONT_BLUE,
ARG_DEFAULT_FONT_ALPHA,
ARG_DEFAULT_BACKGROUND_RED,
ARG_DEFAULT_BACKGROUND_GREEN,
ARG_DEFAULT_BACKGROUND_BLUE,
ARG_DEFAULT_BACKGROUND_ALPHA
};
static GstStaticPadTemplate kate_sink_factory =
GST_STATIC_PAD_TEMPLATE ("subtitle_sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("subtitle/x-kate; application/x-kate")
);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define TIGER_VIDEO_CAPS \
GST_VIDEO_CAPS_xRGB ", endianness = (int)1234; " \
GST_VIDEO_CAPS_BGRx ", endianness = (int)4321"
#else
#define TIGER_VIDEO_CAPS \
GST_VIDEO_CAPS_BGRx ", endianness = (int)4321; " \
GST_VIDEO_CAPS_xRGB ", endianness = (int)1234"
#endif
static GstStaticPadTemplate video_sink_factory =
GST_STATIC_PAD_TEMPLATE ("video_sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (TIGER_VIDEO_CAPS));
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (TIGER_VIDEO_CAPS));
GST_BOILERPLATE (GstKateTiger, gst_kate_tiger, GstElement, GST_TYPE_ELEMENT);
static GType
gst_kate_tiger_font_effect_get_type (void)
{
static GType font_effect_type = 0;
if (!font_effect_type) {
static const GEnumValue font_effects[] = {
{tiger_font_plain, "none", "none"},
{tiger_font_shadow, "shadow", "shadow"},
{tiger_font_outline, "outline", "outline"},
{0, NULL, NULL}
};
font_effect_type = g_enum_register_static ("GstFontEffect", font_effects);
}
return font_effect_type;
}
static void gst_kate_tiger_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_kate_tiger_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_kate_tiger_dispose (GObject * object);
static GstFlowReturn gst_kate_tiger_kate_chain (GstPad * pad, GstBuffer * buf);
static GstFlowReturn gst_kate_tiger_video_chain (GstPad * pad, GstBuffer * buf);
static GstStateChangeReturn gst_kate_tiger_change_state (GstElement * element,
GstStateChange transition);
static gboolean gst_kate_tiger_kate_sink_query (GstPad * pad, GstQuery * query);
static gboolean gst_kate_tiger_kate_event (GstPad * pad, GstEvent * event);
static gboolean gst_kate_tiger_video_event (GstPad * pad, GstEvent * event);
static gboolean gst_kate_tiger_video_set_caps (GstPad * pad, GstCaps * caps);
static gboolean gst_kate_tiger_source_event (GstPad * pad, GstEvent * event);
static void
gst_kate_tiger_base_init (gpointer gclass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&kate_sink_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&video_sink_factory));
gst_element_class_set_details_simple (element_class, "Kate stream renderer",
"Mixer/Video/Overlay/Subtitle",
"Decodes and renders Kate streams on top of a video",
"Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>");
}
/* initialize the plugin's class */
static void
gst_kate_tiger_class_init (GstKateTigerClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_kate_tiger_get_property);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_kate_tiger_set_property);
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_kate_tiger_dispose);
gst_kate_util_install_decoder_base_properties (gobject_class);
g_object_class_install_property (gobject_class, ARG_QUALITY,
g_param_spec_double ("quality", "Rendering quality",
"Rendering quality (0 is faster, 1 is best and slower)",
0.0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_DESC,
g_param_spec_string ("default-font-desc", "Default font description",
"Default font description (Pango style) to render text with",
"", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_EFFECT,
g_param_spec_enum ("default-font-effect", "Default font effect",
"Whether to apply an effect to text by default, for increased readability",
gst_kate_tiger_font_effect_get_type (),
tiger_font_outline,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
ARG_DEFAULT_FONT_EFFECT_STRENGTH,
g_param_spec_double ("default-font-effect-strength",
"Default font effect strength",
"How pronounced should the font effect be (effect dependent)", 0.0,
1.0, 0.5, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_RED,
g_param_spec_int ("default-font-red",
"Default font color (red component)",
"Default font color (red component, between 0 and 255) to render text with",
0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_GREEN,
g_param_spec_int ("default-font-green",
"Default font color (green component)",
"Default font color (green component, between 0 and 255) to render text with",
0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_BLUE,
g_param_spec_int ("default-font-blue",
"Default font color (blue component)",
"Default font color (blue component, between 0 and 255) to render text with",
0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_FONT_ALPHA,
g_param_spec_int ("default-font-alpha",
"Default font color (alpha component)",
"Default font color (alpha component, between 0 and 255) to render text with",
0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_BACKGROUND_RED,
g_param_spec_int ("default-background-red",
"Default background color (red component)",
"Default background color (red component, between 0 and 255) to render text with",
0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_BACKGROUND_GREEN,
g_param_spec_int ("default-background-green",
"Default background color (green component)",
"Default background color (green component, between 0 and 255) to render text with",
0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_BACKGROUND_BLUE,
g_param_spec_int ("default-background-blue",
"Default background color (blue component)",
"Default background color (blue component, between 0 and 255) to render text with",
0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_DEFAULT_BACKGROUND_ALPHA,
g_param_spec_int ("default-background-alpha",
"Default background color (alpha component)",
"Default background color (alpha component, between 0 and 255) to render text with",
0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_kate_tiger_change_state);
}
/* initialize the new element
* instantiate pads and add them to element
* set functions
* initialize structure
*/
static void
gst_kate_tiger_init (GstKateTiger * tiger, GstKateTigerClass * gclass)
{
GST_DEBUG_OBJECT (tiger, "gst_kate_tiger_init");
tiger->mutex = g_mutex_new ();
tiger->cond = g_cond_new ();
tiger->katesinkpad =
gst_pad_new_from_static_template (&kate_sink_factory, "subtitle_sink");
gst_pad_set_chain_function (tiger->katesinkpad,
GST_DEBUG_FUNCPTR (gst_kate_tiger_kate_chain));
gst_pad_set_query_function (tiger->katesinkpad,
GST_DEBUG_FUNCPTR (gst_kate_tiger_kate_sink_query));
gst_pad_set_event_function (tiger->katesinkpad,
GST_DEBUG_FUNCPTR (gst_kate_tiger_kate_event));
gst_element_add_pad (GST_ELEMENT (tiger), tiger->katesinkpad);
tiger->videosinkpad =
gst_pad_new_from_static_template (&video_sink_factory, "video_sink");
gst_pad_set_chain_function (tiger->videosinkpad,
GST_DEBUG_FUNCPTR (gst_kate_tiger_video_chain));
gst_pad_use_fixed_caps (tiger->videosinkpad);
gst_pad_set_setcaps_function (tiger->videosinkpad,
GST_DEBUG_FUNCPTR (gst_kate_tiger_video_set_caps));
gst_pad_set_event_function (tiger->videosinkpad,
GST_DEBUG_FUNCPTR (gst_kate_tiger_video_event));
gst_element_add_pad (GST_ELEMENT (tiger), tiger->videosinkpad);
tiger->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
gst_pad_set_event_function (tiger->srcpad, gst_kate_tiger_source_event);
gst_pad_use_fixed_caps (tiger->srcpad);
gst_element_add_pad (GST_ELEMENT (tiger), tiger->srcpad);
gst_kate_util_decode_base_init (&tiger->decoder, FALSE);
tiger->tr = NULL;
tiger->default_font_desc = NULL;
tiger->quality = -1.0;
tiger->default_font_effect = tiger_font_outline;
tiger->default_font_effect_strength = 0.5;
tiger->default_font_r = 255;
tiger->default_font_g = 255;
tiger->default_font_b = 255;
tiger->default_font_a = 255;
tiger->default_background_r = 0;
tiger->default_background_g = 0;
tiger->default_background_b = 0;
tiger->default_background_a = 0;
tiger->video_width = 0;
tiger->video_height = 0;
tiger->seen_header = FALSE;
}
static void
gst_kate_tiger_dispose (GObject * object)
{
GstKateTiger *tiger = GST_KATE_TIGER (object);
GST_LOG_OBJECT (tiger, "disposing");
if (tiger->default_font_desc) {
g_free (tiger->default_font_desc);
tiger->default_font_desc = NULL;
}
g_cond_free (tiger->cond);
tiger->cond = NULL;
g_mutex_free (tiger->mutex);
tiger->mutex = NULL;
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
}
static void
gst_kate_tiger_update_quality (GstKateTiger * tiger)
{
if (tiger->tr && tiger->quality >= 0.0) {
tiger_renderer_set_quality (tiger->tr, tiger->quality);
}
}
static void
gst_kate_tiger_update_default_font_effect (GstKateTiger * tiger)
{
if (tiger->tr) {
tiger_renderer_set_default_font_effect (tiger->tr,
tiger->default_font_effect, tiger->default_font_effect_strength);
}
}
static void
gst_kate_tiger_update_default_font_color (GstKateTiger * tiger)
{
if (tiger->tr) {
tiger_renderer_set_default_font_color (tiger->tr,
tiger->default_font_r / 255.0,
tiger->default_font_g / 255.0,
tiger->default_font_b / 255.0, tiger->default_font_a / 255.0);
}
}
static void
gst_kate_tiger_update_default_background_color (GstKateTiger * tiger)
{
if (tiger->tr) {
tiger_renderer_set_default_background_fill_color (tiger->tr,
tiger->default_background_r / 255.0,
tiger->default_background_g / 255.0,
tiger->default_background_b / 255.0,
tiger->default_background_a / 255.0);
}
}
static void
gst_kate_tiger_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstKateTiger *tiger = GST_KATE_TIGER (object);
const char *str;
GST_KATE_TIGER_MUTEX_LOCK (tiger);
switch (prop_id) {
case ARG_DEFAULT_FONT_DESC:
if (tiger->default_font_desc) {
g_free (tiger->default_font_desc);
tiger->default_font_desc = NULL;
}
str = g_value_get_string (value);
if (str) {
tiger->default_font_desc = g_strdup (str);
if (tiger->tr)
tiger_renderer_set_default_font_description (tiger->tr,
tiger->default_font_desc);
}
break;
case ARG_QUALITY:
tiger->quality = g_value_get_double (value);
gst_kate_tiger_update_quality (tiger);
break;
case ARG_DEFAULT_FONT_EFFECT:
tiger->default_font_effect = g_value_get_enum (value);
gst_kate_tiger_update_default_font_effect (tiger);
break;
case ARG_DEFAULT_FONT_EFFECT_STRENGTH:
tiger->default_font_effect_strength = g_value_get_double (value);
gst_kate_tiger_update_default_font_effect (tiger);
break;
case ARG_DEFAULT_FONT_RED:
tiger->default_font_r = g_value_get_int (value);
gst_kate_tiger_update_default_font_color (tiger);
break;
case ARG_DEFAULT_FONT_GREEN:
tiger->default_font_g = g_value_get_int (value);
gst_kate_tiger_update_default_font_color (tiger);
break;
case ARG_DEFAULT_FONT_BLUE:
tiger->default_font_b = g_value_get_int (value);
gst_kate_tiger_update_default_font_color (tiger);
break;
case ARG_DEFAULT_FONT_ALPHA:
tiger->default_font_a = g_value_get_int (value);
gst_kate_tiger_update_default_font_color (tiger);
break;
case ARG_DEFAULT_BACKGROUND_RED:
tiger->default_background_r = g_value_get_int (value);
gst_kate_tiger_update_default_background_color (tiger);
break;
case ARG_DEFAULT_BACKGROUND_GREEN:
tiger->default_background_g = g_value_get_int (value);
gst_kate_tiger_update_default_background_color (tiger);
break;
case ARG_DEFAULT_BACKGROUND_BLUE:
tiger->default_background_b = g_value_get_int (value);
gst_kate_tiger_update_default_background_color (tiger);
break;
case ARG_DEFAULT_BACKGROUND_ALPHA:
tiger->default_background_a = g_value_get_int (value);
gst_kate_tiger_update_default_background_color (tiger);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
}
static void
gst_kate_tiger_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstKateTiger *tiger = GST_KATE_TIGER (object);
GST_KATE_TIGER_MUTEX_LOCK (tiger);
switch (prop_id) {
case ARG_DEFAULT_FONT_DESC:
g_value_set_string (value,
tiger->default_font_desc ? tiger->default_font_desc : "");
break;
case ARG_QUALITY:
g_value_set_double (value, tiger->quality);
break;
case ARG_DEFAULT_FONT_EFFECT:
g_value_set_enum (value, tiger->default_font_effect);
break;
case ARG_DEFAULT_FONT_EFFECT_STRENGTH:
g_value_set_double (value, tiger->default_font_effect_strength);
break;
case ARG_DEFAULT_FONT_RED:
g_value_set_int (value, tiger->default_font_r);
break;
case ARG_DEFAULT_FONT_GREEN:
g_value_set_int (value, tiger->default_font_g);
break;
case ARG_DEFAULT_FONT_BLUE:
g_value_set_int (value, tiger->default_font_b);
break;
case ARG_DEFAULT_FONT_ALPHA:
g_value_set_int (value, tiger->default_font_a);
break;
case ARG_DEFAULT_BACKGROUND_RED:
g_value_set_int (value, tiger->default_background_r);
break;
case ARG_DEFAULT_BACKGROUND_GREEN:
g_value_set_int (value, tiger->default_background_g);
break;
case ARG_DEFAULT_BACKGROUND_BLUE:
g_value_set_int (value, tiger->default_background_b);
break;
case ARG_DEFAULT_BACKGROUND_ALPHA:
g_value_set_int (value, tiger->default_background_a);
break;
default:
if (!gst_kate_util_decoder_base_get_property (&tiger->decoder, object,
prop_id, value, pspec)) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
break;
}
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
}
/* GstElement vmethod implementations */
/* chain function
* this function does the actual processing
*/
static GstFlowReturn
gst_kate_tiger_kate_chain (GstPad * pad, GstBuffer * buf)
{
GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad));
const kate_event *ev = NULL;
GstFlowReturn rflow = GST_FLOW_OK;
GST_KATE_TIGER_MUTEX_LOCK (tiger);
GST_LOG_OBJECT (tiger, "Got kate buffer, caps %" GST_PTR_FORMAT,
GST_BUFFER_CAPS (buf));
/* Unfortunately, it can happen that the start of the stream is not sent,
for instance if there's a stream selector upstream, which is switched
from another Kate stream. If this happens, then we can fallback on the
headers stored in the caps (if any). */
if (!tiger->seen_header) {
if (GST_BUFFER_SIZE (buf) == 0 || (GST_BUFFER_DATA (buf)[0] & 0x80) == 0) {
/* Not a header, try to fall back on caps */
GstStructure *s;
const GValue *streamheader;
GST_INFO_OBJECT (tiger, "Headers not seen, start of stream is cut off");
s = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
streamheader = gst_structure_get_value (s, "streamheader");
if (streamheader && G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
GstPad *tagpad = gst_pad_get_peer (pad);
GArray *array;
gint i;
GST_INFO_OBJECT (tiger, "Falling back on caps to initialize decoder");
array = g_value_peek_pointer (streamheader);
for (i = 0; i < array->len; i++) {
GValue *value = &g_array_index (array, GValue, i);
if (G_VALUE_TYPE (value) == GST_TYPE_BUFFER) {
GstBuffer *hbuf = g_value_peek_pointer (value);
gst_buffer_ref (hbuf);
rflow =
gst_kate_util_decoder_base_chain_kate_packet (&tiger->decoder,
GST_ELEMENT_CAST (tiger), pad, hbuf, tiger->srcpad, tagpad,
NULL, NULL);
} else {
GST_WARNING_OBJECT (tiger,
"Streamheader index %d does not hold a buffer", i);
}
}
gst_object_unref (tagpad);
tiger->seen_header = TRUE;
} else {
GST_WARNING_OBJECT (tiger, "No headers seen, and no headers on caps");
}
} else {
tiger->seen_header = TRUE;
}
}
if (gst_kate_util_decoder_base_update_segment (&tiger->decoder,
GST_ELEMENT_CAST (tiger), buf)) {
GstPad *tagpad = gst_pad_get_peer (pad);
rflow =
gst_kate_util_decoder_base_chain_kate_packet (&tiger->decoder,
GST_ELEMENT_CAST (tiger), pad, buf, tiger->srcpad, tagpad, NULL, &ev);
if (G_LIKELY (rflow == GST_FLOW_OK)) {
if (ev) {
int ret = tiger_renderer_add_event (tiger->tr, ev->ki, ev);
GST_INFO_OBJECT (tiger, "adding event for %p from %f to %f: %p, \"%s\"",
ev->ki, ev->start_time, ev->end_time, ev->bitmap, ev->text);
if (G_UNLIKELY (ret < 0)) {
GST_WARNING_OBJECT (tiger,
"failed to add Kate event to Tiger renderer: %d", ret);
}
}
}
gst_object_unref (tagpad);
}
/* we want to avoid shooting ahead of the video stream, or we will
get segment updates which will place us ahead of it, and we won't
be able to convert a video timestamp back into a kate timestamp */
if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buf))) {
while (1) {
gint64 kate_time, video_time;
kate_time =
gst_segment_to_running_time (&tiger->decoder.kate_segment,
GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf));
video_time =
gst_segment_to_running_time (&tiger->video_segment, GST_FORMAT_TIME,
tiger->video_segment.last_stop);
GST_DEBUG_OBJECT (tiger, "Kate time %.2f, video time %.2f (kts %ld)",
kate_time / (float) GST_SECOND, video_time / (float) GST_SECOND,
(long) GST_BUFFER_TIMESTAMP (buf));
if (kate_time <= video_time) {
break;
}
GST_LOG_OBJECT (tiger, "Waiting to return from chain function");
g_cond_wait (tiger->cond, tiger->mutex);
if (tiger->decoder.kate_flushing) {
GST_DEBUG_OBJECT (tiger, "Flushing while waiting");
break;
}
GST_LOG_OBJECT (tiger, "Woken up, checking time again");
}
}
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
gst_object_unref (tiger);
gst_buffer_unref (buf);
return rflow;
}
static gboolean
gst_kate_tiger_video_set_caps (GstPad * pad, GstCaps * caps)
{
GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad));
GstVideoFormat format;
gint w, h;
GST_KATE_TIGER_MUTEX_LOCK (tiger);
/* Cairo expects ARGB in native endianness, and that's what we get
as we've forced it in the caps. We might allow swapped red/blue
at some point, and get tiger to swap, to make some cases faster */
tiger->swap_rgb = FALSE;
if (gst_video_format_parse_caps (caps, &format, &w, &h)) {
tiger->video_width = w;
tiger->video_height = h;
}
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
gst_pad_set_caps (tiger->srcpad, caps);
gst_object_unref (tiger);
return TRUE;
}
static gdouble
gst_kate_tiger_get_time (GstKateTiger * tiger)
{
gint64 rt =
gst_segment_to_running_time (&tiger->video_segment, GST_FORMAT_TIME,
tiger->video_segment.last_stop);
gint64 pos =
gst_segment_to_position (&tiger->decoder.kate_segment, GST_FORMAT_TIME,
rt);
return pos / (gdouble) GST_SECOND;
}
static GstFlowReturn
gst_kate_tiger_video_chain (GstPad * pad, GstBuffer * buf)
{
GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad));
GstFlowReturn rflow = GST_FLOW_OK;
unsigned char *ptr;
int ret;
GST_KATE_TIGER_MUTEX_LOCK (tiger);
GST_LOG_OBJECT (tiger, "got video frame, %u bytes", GST_BUFFER_SIZE (buf));
if (G_UNLIKELY (tiger->video_flushing)) {
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
gst_object_unref (tiger);
gst_buffer_unref (buf);
return GST_FLOW_WRONG_STATE;
}
if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buf))) {
gst_segment_set_last_stop (&tiger->video_segment, GST_FORMAT_TIME,
GST_BUFFER_TIMESTAMP (buf));
g_cond_broadcast (tiger->cond);
}
/* draw on it */
buf = gst_buffer_make_writable (buf);
if (G_UNLIKELY (!buf)) {
GST_WARNING_OBJECT (tiger, "Failed to make video buffer writable");
} else {
ptr = GST_BUFFER_DATA (buf);
if (!ptr) {
GST_WARNING_OBJECT (tiger,
"Failed to get a pointer to video buffer data");
} else {
ret =
tiger_renderer_set_buffer (tiger->tr, ptr, tiger->video_width,
tiger->video_height, tiger->video_width * 4, tiger->swap_rgb);
if (G_UNLIKELY (ret < 0)) {
GST_WARNING_OBJECT (tiger,
"Tiger renderer failed to set buffer to video frame: %d", ret);
} else {
kate_float t = gst_kate_tiger_get_time (tiger);
GST_LOG_OBJECT (tiger, "Video segment calc: last stop %ld, time %.3f",
(long) tiger->video_segment.last_stop, t);
ret = tiger_renderer_update (tiger->tr, t, 1);
if (G_UNLIKELY (ret < 0)) {
GST_WARNING_OBJECT (tiger, "Tiger renderer failed to update: %d",
ret);
} else {
ret = tiger_renderer_render (tiger->tr);
if (G_UNLIKELY (ret < 0)) {
GST_WARNING_OBJECT (tiger,
"Tiger renderer failed to render to video frame: %d", ret);
} else {
GST_LOG_OBJECT (tiger,
"Tiger renderer rendered on video frame at %f", t);
}
}
}
}
}
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
rflow = gst_pad_push (tiger->srcpad, buf);
gst_object_unref (tiger);
return rflow;
}
static GstStateChangeReturn
gst_kate_tiger_change_state (GstElement * element, GstStateChange transition)
{
GstKateTiger *tiger = GST_KATE_TIGER (element);
GstStateChangeReturn res;
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_DEBUG_OBJECT (tiger, "PAUSED -> READY, clearing kate state");
GST_KATE_TIGER_MUTEX_LOCK (tiger);
gst_kate_util_decoder_base_set_flushing (&tiger->decoder, TRUE);
g_cond_broadcast (tiger->cond);
if (tiger->tr) {
tiger_renderer_destroy (tiger->tr);
tiger->tr = NULL;
}
gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED);
tiger->video_flushing = TRUE;
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
break;
default:
break;
}
res =
gst_kate_decoder_base_change_state (&tiger->decoder, element,
parent_class, transition);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
GST_DEBUG_OBJECT (tiger, "READY -> PAUSED, initializing kate state");
GST_KATE_TIGER_MUTEX_LOCK (tiger);
if (tiger->decoder.initialized) {
int ret = tiger_renderer_create (&tiger->tr);
if (ret < 0) {
GST_WARNING_OBJECT (tiger, "failed to create tiger renderer: %d",
ret);
} else {
ret =
tiger_renderer_set_default_font_description (tiger->tr,
tiger->default_font_desc);
if (ret < 0) {
GST_WARNING_OBJECT (tiger,
"failed to set tiger default font description: %d", ret);
}
gst_kate_tiger_update_default_font_color (tiger);
gst_kate_tiger_update_default_background_color (tiger);
gst_kate_tiger_update_default_font_effect (tiger);
gst_kate_tiger_update_quality (tiger);
}
}
gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED);
tiger->video_flushing = FALSE;
tiger->seen_header = FALSE;
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
break;
default:
break;
}
return res;
}
static gboolean
gst_kate_tiger_seek (GstKateTiger * tiger, GstPad * pad, GstEvent * event)
{
GstFormat format;
gdouble rate;
GstSeekFlags flags;
GstSeekType cur_type, stop_type;
gint64 cur, stop;
gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
&stop_type, &stop);
GST_KATE_TIGER_MUTEX_LOCK (tiger);
tiger->video_flushing = TRUE;
gst_kate_util_decoder_base_set_flushing (&tiger->decoder, TRUE);
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
if (format == GST_FORMAT_TIME) {
/* if seeking in time, we can update tiger to remove any appropriate events */
kate_float target;
switch (cur_type) {
case GST_SEEK_TYPE_SET:
target = cur / (float) GST_SECOND;
break;
case GST_SEEK_TYPE_CUR:
GST_WARNING_OBJECT (tiger,
"Seeking from the current segment, cannot work out target so flushing everything");
target = (kate_float) 0;
break;
case GST_SEEK_TYPE_END:
GST_WARNING_OBJECT (tiger,
"Seeking from the end, cannot work out target so flushing everything");
target = (kate_float) 0;
break;
default:
GST_WARNING_OBJECT (tiger, "Unexpected seek type");
target = (kate_float) 0;
break;
}
GST_INFO_OBJECT (tiger, "Seeking in time to %f", target);
GST_KATE_TIGER_MUTEX_LOCK (tiger);
tiger_renderer_seek (tiger->tr, target);
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
}
/* forward to both sinks */
gst_event_ref (event);
if (gst_pad_push_event (tiger->videosinkpad, event)) {
int ret = gst_pad_push_event (tiger->katesinkpad, event);
if (ret) {
return TRUE;
} else {
return FALSE;
}
} else {
gst_event_unref (event);
return FALSE;
}
}
static gboolean
gst_kate_tiger_source_event (GstPad * pad, GstEvent * event)
{
GstKateTiger *tiger =
(GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
gboolean res = TRUE;
g_return_val_if_fail (tiger != NULL, FALSE);
GST_LOG_OBJECT (tiger, "Event on source pad: %s",
GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
GST_INFO_OBJECT (tiger, "Seek on source pad");
res = gst_kate_tiger_seek (tiger, pad, event);
break;
default:
res = gst_pad_event_default (pad, event);
break;
}
gst_object_unref (tiger);
return res;
}
static gboolean
gst_kate_tiger_handle_kate_event (GstPad * pad, GstEvent * event)
{
GstKateTiger *tiger =
(GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
gboolean res = TRUE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_NEWSEGMENT:
GST_INFO_OBJECT (tiger, "New segment on Kate pad");
GST_KATE_TIGER_MUTEX_LOCK (tiger);
g_cond_broadcast (tiger->cond);
gst_kate_util_decoder_base_new_segment_event (&tiger->decoder, event);
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
gst_event_unref (event);
break;
case GST_EVENT_FLUSH_START:
GST_KATE_TIGER_MUTEX_LOCK (tiger);
gst_kate_util_decoder_base_set_flushing (&tiger->decoder, TRUE);
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
g_cond_broadcast (tiger->cond);
gst_event_unref (event);
break;
case GST_EVENT_FLUSH_STOP:
GST_KATE_TIGER_MUTEX_LOCK (tiger);
gst_kate_util_decoder_base_set_flushing (&tiger->decoder, FALSE);
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
gst_event_unref (event);
break;
case GST_EVENT_EOS:
/* we ignore this, it just means we don't have anymore Kate packets, but
the Tiger renderer will still draw (if appropriate) on incoming video */
GST_INFO_OBJECT (tiger, "EOS on Kate pad");
GST_KATE_TIGER_MUTEX_LOCK (tiger);
g_cond_broadcast (tiger->cond);
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
gst_event_unref (event);
break;
default:
res = gst_pad_event_default (pad, event);
break;
}
gst_object_unref (tiger);
return res;
}
static gboolean
gst_kate_tiger_kate_event (GstPad * pad, GstEvent * event)
{
GstKateTiger *tiger =
(GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
gboolean res = TRUE;
g_return_val_if_fail (tiger != NULL, FALSE);
GST_LOG_OBJECT (tiger, "Event on Kate pad: %s", GST_EVENT_TYPE_NAME (event));
/* Delay events till we've set caps */
if (gst_kate_util_decoder_base_queue_event (&tiger->decoder, event,
&gst_kate_tiger_handle_kate_event, pad)) {
gst_object_unref (tiger);
return TRUE;
}
res = gst_kate_tiger_handle_kate_event (pad, event);
gst_object_unref (tiger);
return res;
}
static gboolean
gst_kate_tiger_handle_video_event (GstPad * pad, GstEvent * event)
{
GstKateTiger *tiger =
(GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
gboolean res = TRUE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_NEWSEGMENT:
{
gboolean update;
gdouble rate, arate;
GstFormat format;
gint64 start, stop, time;
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
&start, &stop, &time);
if (format == GST_FORMAT_TIME) {
GST_DEBUG_OBJECT (tiger, "video pad segment:"
" Update %d, rate %g arate %g format %d start %" GST_TIME_FORMAT
" %" GST_TIME_FORMAT " position %" GST_TIME_FORMAT,
update, rate, arate, format, GST_TIME_ARGS (start),
GST_TIME_ARGS (stop), GST_TIME_ARGS (time));
GST_KATE_TIGER_MUTEX_LOCK (tiger);
gst_segment_set_newsegment_full (&tiger->video_segment, update, rate,
arate, format, start, stop, time);
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
}
res = gst_pad_event_default (pad, event);
break;
}
case GST_EVENT_FLUSH_START:
GST_KATE_TIGER_MUTEX_LOCK (tiger);
gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED);
tiger->video_flushing = TRUE;
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
g_cond_broadcast (tiger->cond);
res = gst_pad_event_default (pad, event);
break;
case GST_EVENT_FLUSH_STOP:
GST_KATE_TIGER_MUTEX_LOCK (tiger);
gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED);
tiger->video_flushing = FALSE;
GST_KATE_TIGER_MUTEX_UNLOCK (tiger);
res = gst_pad_event_default (pad, event);
break;
default:
res = gst_pad_event_default (pad, event);
break;
}
gst_object_unref (tiger);
return res;
}
static gboolean
gst_kate_tiger_video_event (GstPad * pad, GstEvent * event)
{
GstKateTiger *tiger =
(GstKateTiger *) (gst_object_get_parent (GST_OBJECT (pad)));
gboolean res = TRUE;
g_return_val_if_fail (tiger != NULL, FALSE);
GST_INFO_OBJECT (tiger, "Event on video pad: %s",
GST_EVENT_TYPE_NAME (event));
res = gst_kate_tiger_handle_video_event (pad, event);
gst_object_unref (tiger);
return res;
}
gboolean
gst_kate_tiger_kate_sink_query (GstPad * pad, GstQuery * query)
{
GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad));
gboolean res = gst_kate_decoder_base_sink_query (&tiger->decoder,
GST_ELEMENT_CAST (tiger), pad, query);
GST_INFO_OBJECT (tiger, "Query on Kate pad");
gst_object_unref (tiger);
return res;
}