diff options
-rw-r--r-- | ext/kate/Makefile.am | 2 | ||||
-rw-r--r-- | ext/kate/gstkate.c | 3 | ||||
-rw-r--r-- | ext/kate/gstkatedec.c | 105 | ||||
-rw-r--r-- | ext/kate/gstkatedec.h | 4 | ||||
-rw-r--r-- | ext/kate/gstkateparse.c | 5 | ||||
-rw-r--r-- | ext/kate/gstkatespu.c | 8 | ||||
-rw-r--r-- | ext/kate/gstkatetiger.c | 321 | ||||
-rw-r--r-- | ext/kate/gstkatetiger.h | 8 | ||||
-rw-r--r-- | ext/kate/gstkateutil.c | 206 | ||||
-rw-r--r-- | ext/kate/gstkateutil.h | 30 |
10 files changed, 600 insertions, 92 deletions
diff --git a/ext/kate/Makefile.am b/ext/kate/Makefile.am index 6a2152d13..fd7d6ced4 100644 --- a/ext/kate/Makefile.am +++ b/ext/kate/Makefile.am @@ -10,7 +10,7 @@ endif # flags used to compile this plugin libgstkate_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(TIGER_CFLAGS) $(KATE_CFLAGS) -libgstkate_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) $(GST_LIBS) $(TIGER_LIBS) $(KATE_LIBS) +libgstkate_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_MAJORMINOR) -lgsttag-$(GST_MAJORMINOR) $(GST_LIBS) $(TIGER_LIBS) $(KATE_LIBS) libgstkate_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstkate_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/ext/kate/gstkate.c b/ext/kate/gstkate.c index 08adfea8d..91d0579a6 100644 --- a/ext/kate/gstkate.c +++ b/ext/kate/gstkate.c @@ -64,6 +64,7 @@ GST_DEBUG_CATEGORY (gst_katedec_debug); GST_DEBUG_CATEGORY (gst_kateenc_debug); GST_DEBUG_CATEGORY (gst_kateparse_debug); GST_DEBUG_CATEGORY (gst_katetag_debug); +GST_DEBUG_CATEGORY (gst_kateutil_debug); #ifdef HAVE_TIGER GST_DEBUG_CATEGORY (gst_katetiger_debug); #endif @@ -75,6 +76,8 @@ plugin_init (GstPlugin * plugin) GST_DEBUG_CATEGORY_INIT (gst_kateenc_debug, "kateenc", 0, "Kate encoder"); GST_DEBUG_CATEGORY_INIT (gst_kateparse_debug, "kateparse", 0, "Kate parser"); GST_DEBUG_CATEGORY_INIT (gst_katetag_debug, "katetag", 0, "Kate tagger"); + GST_DEBUG_CATEGORY_INIT (gst_kateutil_debug, "kateutil", 0, + "Kate utility functions"); #ifdef HAVE_TIGER GST_DEBUG_CATEGORY_INIT (gst_katetiger_debug, "tiger", 0, "Kate Tiger renderer"); diff --git a/ext/kate/gstkatedec.c b/ext/kate/gstkatedec.c index 322363d81..4dbe6d818 100644 --- a/ext/kate/gstkatedec.c +++ b/ext/kate/gstkatedec.c @@ -128,6 +128,9 @@ static GstFlowReturn gst_kate_dec_chain (GstPad * pad, 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); static void gst_kate_dec_base_init (gpointer gclass) @@ -184,16 +187,21 @@ gst_kate_dec_init (GstKateDec * dec, GstKateDecClass * gclass) GST_DEBUG_FUNCPTR (gst_kate_dec_chain)); gst_pad_set_query_function (dec->sinkpad, GST_DEBUG_FUNCPTR (gst_kate_dec_sink_query)); + gst_pad_set_event_function (dec->sinkpad, + 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)); 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_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - gst_kate_util_decode_base_init (&dec->decoder); + gst_kate_util_decode_base_init (&dec->decoder, TRUE); + dec->src_caps = NULL; dec->remove_markup = FALSE; } @@ -240,9 +248,16 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf) const kate_event *ev = NULL; GstFlowReturn rflow = GST_FLOW_OK; + if (!gst_kate_util_decoder_base_update_segment (&kd->decoder, + GST_ELEMENT_CAST (kd), buf)) { + GST_WARNING_OBJECT (kd, "Out of segment!"); + goto not_in_seg; + } + rflow = gst_kate_util_decoder_base_chain_kate_packet (&kd->decoder, - GST_ELEMENT_CAST (kd), pad, buf, kd->srcpad, &ev); + 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); @@ -336,6 +351,7 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf) } } +not_in_seg: gst_object_unref (kd); gst_buffer_unref (buf); return rflow; @@ -345,6 +361,7 @@ static GstStateChangeReturn gst_kate_dec_change_state (GstElement * element, GstStateChange transition) { GstKateDec *kd = GST_KATE_DEC (element); + return gst_kate_decoder_base_change_state (&kd->decoder, element, parent_class, transition); } @@ -359,3 +376,87 @@ gst_kate_dec_sink_query (GstPad * pad, GstQuery * query) gst_object_unref (kd); return res; } + +static gboolean +gst_kate_dec_sink_event (GstPad * pad, GstEvent * event) +{ + GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad))); + gboolean res = TRUE; + + g_return_val_if_fail (kd != NULL, FALSE); + + GST_LOG_OBJECT (kd, "Event on sink pad: %s", GST_EVENT_TYPE_NAME (event)); + + // 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); + return TRUE; + } + + res = gst_kate_dec_sink_handle_event (pad, event); + + gst_object_unref (kd); + + return res; +} + +static gboolean +gst_kate_dec_sink_handle_event (GstPad * pad, GstEvent * event) +{ + GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad))); + gboolean res = TRUE; + + g_return_val_if_fail (kd != NULL, FALSE); + + GST_LOG_OBJECT (kd, "Handling event on sink pad: %s", + GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + gst_kate_util_decoder_base_new_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; + + default: + res = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (kd); + + return res; +} + +static GstCaps * +gst_kate_dec_src_get_caps (GstPad * pad) +{ + GstKateDec *kd = (GstKateDec *) (gst_object_get_parent (GST_OBJECT (pad))); + GstCaps *caps; + + g_return_val_if_fail (kd != NULL, FALSE); + + if (kd->src_caps) { + GST_DEBUG_OBJECT (kd, "We have src caps (%s)", + gst_caps_to_string (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); + } + + caps = gst_caps_copy (caps); + + gst_object_unref (kd); + return caps; +} diff --git a/ext/kate/gstkatedec.h b/ext/kate/gstkatedec.h index a7011ecbc..b4d99059a 100644 --- a/ext/kate/gstkatedec.h +++ b/ext/kate/gstkatedec.h @@ -67,12 +67,12 @@ typedef struct _GstKateDecClass GstKateDecClass; struct _GstKateDec { - GstElement element; + GstKateDecoderBase decoder; GstPad *sinkpad; GstPad *srcpad; - GstKateDecoderBase decoder; + GstCaps *src_caps; gboolean remove_markup; }; diff --git a/ext/kate/gstkateparse.c b/ext/kate/gstkateparse.c index c41066645..6a72edec9 100644 --- a/ext/kate/gstkateparse.c +++ b/ext/kate/gstkateparse.c @@ -255,11 +255,6 @@ gst_kate_parse_push_buffer (GstKateParse * parse, GstBuffer * buf, GST_BUFFER_OFFSET_END (buf) = granulepos; GST_BUFFER_TIMESTAMP (buf) = GST_BUFFER_OFFSET (buf); - /* Hack to flush each packet on its own page - taken off the CMML encoder element */ - /* TODO: this is shite and needs to go once I find a way to tell Ogg to flush - as it messes up Matroska's track duration */ - GST_BUFFER_DURATION (buf) = G_MAXINT64; - gst_buffer_set_caps (buf, GST_PAD_CAPS (parse->srcpad)); return gst_pad_push (parse->srcpad, buf); diff --git a/ext/kate/gstkatespu.c b/ext/kate/gstkatespu.c index 778288ecf..13f561941 100644 --- a/ext/kate/gstkatespu.c +++ b/ext/kate/gstkatespu.c @@ -804,14 +804,6 @@ gst_kate_spu_encode_spu (GstKateDec * kd, const kate_event * ev) bytes[nbytes++] = ((kp->colors[palette[1]].a / 17) << 4) | (kp->colors[palette[0]].a / 17); -#if 0 - // move to top left - avoids a crash in dvdspu when overlaying on a small video :/ - right -= left; - bottom -= top; - left = 0; - top = 0; -#endif - CHKBUFSPC (7 * 2); bytes[nbytes++] = SPU_CMD_SET_DAREA; bytes[nbytes++] = left >> 4; diff --git a/ext/kate/gstkatetiger.c b/ext/kate/gstkatetiger.c index 93d98e73a..286929796 100644 --- a/ext/kate/gstkatetiger.c +++ b/ext/kate/gstkatetiger.c @@ -88,6 +88,19 @@ 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 { @@ -119,16 +132,28 @@ static GstStaticPadTemplate kate_sink_factory = ); static GstStaticPadTemplate video_sink_factory = -GST_STATIC_PAD_TEMPLATE ("video_sink", + GST_STATIC_PAD_TEMPLATE ("video_sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw-rgb, bpp=(int)32, depth=(int)24") +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB ", endianness = (int) 1234" ";" + GST_VIDEO_CAPS_BGRx ", endianness = (int)4321") +#else + GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB ", endianness = (int) 4321" ";" + GST_VIDEO_CAPS_BGRx ", endianness = (int)1234") +#endif ); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw-rgb, bpp=(int)32, depth=(int)24") +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB ", endianness = (int) 1234" ";" + GST_VIDEO_CAPS_BGRx ", endianness = (int)4321") +#else + GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB ", endianness = (int) 4321" ";" + GST_VIDEO_CAPS_BGRx ", endianness = (int)1234") +#endif ); GST_BOILERPLATE (GstKateTiger, gst_kate_tiger, GstElement, GST_TYPE_ELEMENT); @@ -163,6 +188,7 @@ 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); @@ -302,19 +328,19 @@ gst_kate_tiger_init (GstKateTiger * tiger, GstKateTigerClass * gclass) 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_set_query_function (tiger->videosinkpad, GST_DEBUG_FUNCPTR (gst_kate_tiger_video_sink_query)); gst_pad_use_fixed_caps (tiger->videosinkpad); - gst_pad_set_caps (tiger->videosinkpad, - gst_static_pad_template_get_caps (&video_sink_factory)); 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); + gst_kate_util_decode_base_init (&tiger->decoder, FALSE); tiger->tr = NULL; @@ -400,7 +426,7 @@ gst_kate_tiger_set_property (GObject * object, guint prop_id, GstKateTiger *tiger = GST_KATE_TIGER (object); const char *str; - g_mutex_lock (tiger->mutex); + GST_KATE_TIGER_MUTEX_LOCK (tiger); switch (prop_id) { case ARG_DEFAULT_FONT_DESC: @@ -465,7 +491,7 @@ gst_kate_tiger_set_property (GObject * object, guint prop_id, break; } - g_mutex_unlock (tiger->mutex); + GST_KATE_TIGER_MUTEX_UNLOCK (tiger); } static void @@ -474,7 +500,7 @@ gst_kate_tiger_get_property (GObject * object, guint prop_id, { GstKateTiger *tiger = GST_KATE_TIGER (object); - g_mutex_lock (tiger->mutex); + GST_KATE_TIGER_MUTEX_LOCK (tiger); switch (prop_id) { case ARG_DEFAULT_FONT_DESC: @@ -522,7 +548,7 @@ gst_kate_tiger_get_property (GObject * object, guint prop_id, break; } - g_mutex_unlock (tiger->mutex); + GST_KATE_TIGER_MUTEX_UNLOCK (tiger); } /* GstElement vmethod implementations */ @@ -538,30 +564,36 @@ gst_kate_tiger_kate_chain (GstPad * pad, GstBuffer * buf) const kate_event *ev = NULL; GstFlowReturn rflow = GST_FLOW_OK; - g_mutex_lock (tiger->mutex); - - GST_LOG_OBJECT (tiger, "Got kate buffer"); - - rflow = - gst_kate_util_decoder_base_chain_kate_packet (&tiger->decoder, - GST_ELEMENT_CAST (tiger), pad, buf, tiger->srcpad, &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_KATE_TIGER_MUTEX_LOCK (tiger); + + GST_LOG_OBJECT (tiger, "Got kate buffer, caps %s", + gst_caps_to_string (GST_BUFFER_CAPS (buf))); + + 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); } + GST_KATE_TIGER_MUTEX_UNLOCK (tiger); + gst_object_unref (tiger); gst_buffer_unref (buf); - g_mutex_unlock (tiger->mutex); - return rflow; } @@ -569,28 +601,36 @@ static gboolean gst_kate_tiger_video_set_caps (GstPad * pad, GstCaps * caps) { GstKateTiger *tiger = GST_KATE_TIGER (gst_pad_get_parent (pad)); - GstStructure *s; + GstVideoFormat format; gint w, h; - gboolean res = FALSE; - g_mutex_lock (tiger->mutex); + GST_KATE_TIGER_MUTEX_LOCK (tiger); - s = gst_caps_get_structure (caps, 0); + /* 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 (G_LIKELY (gst_structure_get_int (s, "width", &w)) - && G_LIKELY (gst_structure_get_int (s, "height", &h))) { - GST_INFO_OBJECT (tiger, "video sink: %d %d", w, h); + if (gst_video_format_parse_caps (caps, &format, &w, &h)) { tiger->video_width = w; tiger->video_height = h; - res = TRUE; } - g_mutex_unlock (tiger->mutex); + 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) +{ + return gst_segment_to_running_time (&tiger->video_segment, GST_FORMAT_TIME, + tiger->video_segment.last_stop) / (gdouble) GST_SECOND; +} + static GstFlowReturn gst_kate_tiger_video_chain (GstPad * pad, GstBuffer * buf) { @@ -599,10 +639,22 @@ gst_kate_tiger_video_chain (GstPad * pad, GstBuffer * buf) unsigned char *ptr; int ret; - g_mutex_lock (tiger->mutex); + 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)); + } + /* draw on it */ buf = gst_buffer_make_writable (buf); if (G_UNLIKELY (!buf)) { @@ -613,12 +665,17 @@ gst_kate_tiger_video_chain (GstPad * pad, GstBuffer * buf) 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, 0); // TODO: stride ? + 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_BUFFER_TIMESTAMP (buf) / (gdouble) GST_SECOND; + 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", @@ -636,12 +693,13 @@ gst_kate_tiger_video_chain (GstPad * pad, GstBuffer * buf) } } } + + GST_KATE_TIGER_MUTEX_UNLOCK (tiger); + rflow = gst_pad_push (tiger->srcpad, buf); gst_object_unref (tiger); - g_mutex_unlock (tiger->mutex); - return rflow; } @@ -654,12 +712,14 @@ gst_kate_tiger_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG_OBJECT (tiger, "PAUSED -> READY, clearing kate state"); - g_mutex_lock (tiger->mutex); + GST_KATE_TIGER_MUTEX_LOCK (tiger); if (tiger->tr) { tiger_renderer_destroy (tiger->tr); tiger->tr = NULL; } - g_mutex_unlock (tiger->mutex); + gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED); + tiger->video_flushing = TRUE; + GST_KATE_TIGER_MUTEX_UNLOCK (tiger); break; default: break; @@ -672,7 +732,7 @@ gst_kate_tiger_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: GST_DEBUG_OBJECT (tiger, "READY -> PAUSED, initializing kate state"); - g_mutex_lock (tiger->mutex); + GST_KATE_TIGER_MUTEX_LOCK (tiger); if (tiger->decoder.initialized) { int ret = tiger_renderer_create (&tiger->tr); if (ret < 0) { @@ -692,7 +752,9 @@ gst_kate_tiger_change_state (GstElement * element, GstStateChange transition) gst_kate_tiger_update_quality (tiger); } } - g_mutex_unlock (tiger->mutex); + gst_segment_init (&tiger->video_segment, GST_FORMAT_UNDEFINED); + tiger->video_flushing = FALSE; + GST_KATE_TIGER_MUTEX_UNLOCK (tiger); break; default: break; @@ -713,18 +775,42 @@ gst_kate_tiger_seek (GstKateTiger * tiger, GstPad * pad, GstEvent * event) 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: + target = gst_kate_tiger_get_time (tiger) + cur / (float) GST_SECOND; + 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)) { - if (gst_pad_push_event (tiger->katesinkpad, event)) { - if (format == GST_FORMAT_TIME) { - /* if seeking in time, we can update tiger to remove any appropriate events */ - kate_float target = cur / (gdouble) GST_SECOND; - GST_INFO_OBJECT (tiger, "Seeking in time to %f", target); - g_mutex_lock (tiger->mutex); - tiger_renderer_seek (tiger->tr, target); - g_mutex_unlock (tiger->mutex); - } + int ret = gst_pad_push_event (tiger->katesinkpad, event); + if (ret) { return TRUE; } else { return FALSE; @@ -744,6 +830,9 @@ gst_kate_tiger_source_event (GstPad * pad, GstEvent * event) 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"); @@ -760,17 +849,30 @@ gst_kate_tiger_source_event (GstPad * pad, GstEvent * event) } static gboolean -gst_kate_tiger_kate_event (GstPad * pad, GstEvent * event) +gst_kate_tiger_handle_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); - switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT: GST_INFO_OBJECT (tiger, "New segment on Kate pad"); + GST_KATE_TIGER_MUTEX_LOCK (tiger); + 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); + 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: @@ -789,12 +891,115 @@ gst_kate_tiger_kate_event (GstPad * pad, GstEvent * event) 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); + 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; } diff --git a/ext/kate/gstkatetiger.h b/ext/kate/gstkatetiger.h index 0ab7dc136..353f9f79e 100644 --- a/ext/kate/gstkatetiger.h +++ b/ext/kate/gstkatetiger.h @@ -68,14 +68,12 @@ typedef struct _GstKateTigerClass GstKateTigerClass; struct _GstKateTiger { - GstElement element; + GstKateDecoderBase decoder; GstPad *katesinkpad; GstPad *videosinkpad; GstPad *srcpad; - GstKateDecoderBase decoder; - tiger_renderer *tr; gdouble quality; @@ -93,8 +91,12 @@ struct _GstKateTiger gint video_width; gint video_height; + gboolean swap_rgb; GMutex *mutex; + + GstSegment video_segment; + gboolean video_flushing; }; struct _GstKateTigerClass diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index 5b71b94a3..0bccc4b6f 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -23,10 +23,17 @@ # include "config.h" #endif +#include <string.h> #include <gst/tag/tag.h> #include "gstkate.h" #include "gstkateutil.h" +GST_DEBUG_CATEGORY_EXTERN (gst_kateutil_debug); +#define GST_CAT_DEFAULT gst_kateutil_debug + +static void gst_kate_util_decoder_base_free_event_queue (GstKateDecoderBase * + decoder); + GstCaps * gst_kate_util_set_header_on_caps (GstElement * element, GstCaps * caps, GList * headers) @@ -95,7 +102,8 @@ gst_kate_util_install_decoder_base_properties (GObjectClass * gobject_class) } void -gst_kate_util_decode_base_init (GstKateDecoderBase * decoder) +gst_kate_util_decode_base_init (GstKateDecoderBase * decoder, + gboolean delay_events) { if (G_UNLIKELY (!decoder)) return; @@ -106,6 +114,8 @@ gst_kate_util_decode_base_init (GstKateDecoderBase * decoder) decoder->original_canvas_height = 0; decoder->tags = NULL; decoder->initialized = FALSE; + decoder->delay_events = delay_events; + decoder->event_queue = NULL; } static void @@ -121,10 +131,77 @@ gst_kate_util_decode_base_reset (GstKateDecoderBase * decoder) } decoder->original_canvas_width = 0; decoder->original_canvas_height = 0; + if (decoder->event_queue) { + gst_kate_util_decoder_base_free_event_queue (decoder); + } decoder->initialized = FALSE; } gboolean +gst_kate_util_decoder_base_queue_event (GstKateDecoderBase * decoder, + GstEvent * event, gboolean (*handler) (GstPad *, GstEvent *), GstPad * pad) +{ + gboolean can_be_queued; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_EOS: + can_be_queued = FALSE; + break; + default: + can_be_queued = TRUE; + break; + } + + if (decoder->delay_events && can_be_queued) { + GstKateDecoderBaseQueuedEvent *item; + GST_DEBUG_OBJECT (decoder, "We have to delay the event"); + item = g_slice_new (GstKateDecoderBaseQueuedEvent); + if (item) { + item->event = event; + item->pad = pad; + item->handler = handler; + g_queue_push_tail (decoder->event_queue, item); + return TRUE; + } else { + return FALSE; + } + } else { + return FALSE; + } +} + +static void +gst_kate_util_decoder_base_free_event_queue (GstKateDecoderBase * decoder) +{ + while (decoder->event_queue->length) { + GstKateDecoderBaseQueuedEvent *item = (GstKateDecoderBaseQueuedEvent *) + g_queue_pop_head (decoder->event_queue); + g_slice_free (GstKateDecoderBaseQueuedEvent, item); + } + g_queue_free (decoder->event_queue); + decoder->event_queue = NULL; +} + +static void +gst_kate_util_decoder_base_drain_event_queue (GstKateDecoderBase * decoder) +{ + decoder->delay_events = FALSE; + + if (decoder->event_queue->length == 0) + return; + + GST_DEBUG_OBJECT (decoder, "We can now drain all events!"); + while (decoder->event_queue->length) { + GstKateDecoderBaseQueuedEvent *item = (GstKateDecoderBaseQueuedEvent *) + g_queue_pop_head (decoder->event_queue); + (*item->handler) (item->pad, item->event); + g_slice_free (GstKateDecoderBaseQueuedEvent, item); + } +} + +gboolean gst_kate_util_decoder_base_get_property (GstKateDecoderBase * decoder, GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { @@ -152,38 +229,67 @@ gst_kate_util_decoder_base_get_property (GstKateDecoderBase * decoder, GstFlowReturn gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, GstElement * element, GstPad * pad, GstBuffer * buf, GstPad * srcpad, - const kate_event ** ev) + GstPad * tagpad, GstCaps ** src_caps, const kate_event ** ev) { kate_packet kp; int ret; GstFlowReturn rflow = GST_FLOW_OK; + gboolean is_header; GST_DEBUG_OBJECT (element, "got kate packet, %u bytes, type %02x", GST_BUFFER_SIZE (buf), GST_BUFFER_SIZE (buf) == 0 ? -1 : GST_BUFFER_DATA (buf)[0]); + + is_header = GST_BUFFER_SIZE (buf) > 0 && (GST_BUFFER_DATA (buf)[0] & 0x80); + + if (!is_header && decoder->tags) { + /* after we've processed headers, send any tags before processing the data packet */ + GST_DEBUG_OBJECT (element, "Not a header, sending tags for pad %s:%s", + GST_DEBUG_PAD_NAME (tagpad)); + gst_element_found_tags_for_pad (element, tagpad, decoder->tags); + decoder->tags = NULL; + } + kate_packet_wrap (&kp, GST_BUFFER_SIZE (buf), GST_BUFFER_DATA (buf)); ret = kate_high_decode_packetin (&decoder->k, &kp, ev); if (G_UNLIKELY (ret < 0)) { GST_ELEMENT_ERROR (element, STREAM, DECODE, (NULL), ("Failed to decode Kate packet: %d", ret)); return GST_FLOW_ERROR; - } else if (G_UNLIKELY (ret > 0)) { + } + + if (G_UNLIKELY (ret > 0)) { GST_DEBUG_OBJECT (element, "kate_high_decode_packetin has received EOS packet"); - return GST_FLOW_OK; } /* headers may be interesting to retrieve information from */ - if (G_LIKELY (GST_BUFFER_SIZE (buf) > 0)) + if (G_UNLIKELY (is_header)) { switch (GST_BUFFER_DATA (buf)[0]) { - GstCaps *caps; - case 0x80: /* ID header */ GST_INFO_OBJECT (element, "Parsed ID header: language %s, category %s", decoder->k.ki->language, decoder->k.ki->category); - caps = gst_caps_new_simple ("text/x-pango-markup", NULL); - gst_pad_set_caps (srcpad, caps); - gst_caps_unref (caps); + if (src_caps) { + if (*src_caps) { + gst_caps_unref (*src_caps); + *src_caps = NULL; + } + if (strcmp (decoder->k.ki->category, "K-SPU") == 0 || + strcmp (decoder->k.ki->category, "spu-subtitles") == 0) { + *src_caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL); + } else if (decoder->k.ki->text_markup_type == kate_markup_none) { + *src_caps = gst_caps_new_simple ("text/plain", NULL); + } else { + *src_caps = gst_caps_new_simple ("text/x-pango-markup", NULL); + } + GST_INFO_OBJECT (element, "Setting src caps to %s", + gst_caps_to_string (*src_caps)); + if (!gst_pad_set_caps (srcpad, *src_caps)) { + GST_ERROR_OBJECT (element, + "Failed to renegotiate caps for pad %s:%s", + GST_DEBUG_PAD_NAME (srcpad)); + } + } if (decoder->k.ki->language && *decoder->k.ki->language) { GstTagList *old = decoder->tags, *tags = gst_tag_list_new (); if (tags) { @@ -214,6 +320,9 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, decoder->original_canvas_width = decoder->k.ki->original_canvas_width; decoder->original_canvas_height = decoder->k.ki->original_canvas_height; + /* we can now send away any event we've delayed, as the src pad now has caps */ + gst_kate_util_decoder_base_drain_event_queue (decoder); + break; case 0x81: /* Vorbis comments header */ @@ -247,8 +356,9 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, if (old) gst_tag_list_free (old); +#if 0 if (decoder->initialized) { - gst_element_found_tags_for_pad (element, srcpad, decoder->tags); + gst_element_found_tags_for_pad (element, tagpad, decoder->tags); decoder->tags = NULL; } else { /* Only push them as messages for the time being. * @@ -257,12 +367,14 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, gst_message_new_tag (GST_OBJECT (element), gst_tag_list_copy (decoder->tags))); } +#endif } break; default: break; } + } return rflow; } @@ -285,7 +397,10 @@ gst_kate_decoder_base_change_state (GstKateDecoderBase * decoder, GST_WARNING_OBJECT (element, "failed to initialize kate state: %d", ret); } + gst_segment_init (&decoder->kate_segment, GST_FORMAT_UNDEFINED); + decoder->kate_flushing = FALSE; decoder->initialized = TRUE; + decoder->event_queue = g_queue_new (); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; @@ -304,6 +419,8 @@ gst_kate_decoder_base_change_state (GstKateDecoderBase * decoder, kate_high_decode_clear (&decoder->k); decoder->initialized = FALSE; } + gst_segment_init (&decoder->kate_segment, GST_FORMAT_UNDEFINED); + decoder->kate_flushing = TRUE; gst_kate_util_decode_base_reset (decoder); break; case GST_STATE_CHANGE_READY_TO_NULL: @@ -316,6 +433,73 @@ gst_kate_decoder_base_change_state (GstKateDecoderBase * decoder, return res; } +void +gst_kate_util_decoder_base_set_flushing (GstKateDecoderBase * decoder, + gboolean flushing) +{ + decoder->kate_flushing = flushing; + gst_segment_init (&decoder->kate_segment, GST_FORMAT_UNDEFINED); +} + +void +gst_kate_util_decoder_base_new_segment_event (GstKateDecoderBase * decoder, + GstEvent * event) +{ + gboolean update; + gdouble rate; + GstFormat format; + gint64 start, stop, time; + gdouble arate; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + GST_DEBUG_OBJECT (decoder, "kate 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_segment_set_newsegment_full (&decoder->kate_segment, update, rate, + arate, format, start, stop, time); +} + +gboolean +gst_kate_util_decoder_base_update_segment (GstKateDecoderBase * decoder, + GstElement * element, GstBuffer * buf) +{ + gint64 clip_start = 0, clip_stop = 0; + gboolean in_seg; + + if (decoder->kate_flushing) { + GST_LOG_OBJECT (element, "Kate pad flushing, buffer ignored"); + return FALSE; + } + + if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (buf))) { + GstClockTime stop; + + if (G_LIKELY (GST_BUFFER_DURATION_IS_VALID (buf))) + stop = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf); + else + stop = GST_CLOCK_TIME_NONE; + + in_seg = gst_segment_clip (&decoder->kate_segment, GST_FORMAT_TIME, + GST_BUFFER_TIMESTAMP (buf), stop, &clip_start, &clip_stop); + } else { + in_seg = TRUE; + } + + if (in_seg) { + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + gst_segment_set_last_stop (&decoder->kate_segment, GST_FORMAT_TIME, + clip_start); + } + } else { + GST_INFO_OBJECT (element, "Kate buffer not in segment, ignored"); + } + + return in_seg; +} + static GstClockTime gst_kate_util_granule_time (kate_state * k, gint64 granulepos) { diff --git a/ext/kate/gstkateutil.h b/ext/kate/gstkateutil.h index 6e8ea2e01..27b6f70f3 100644 --- a/ext/kate/gstkateutil.h +++ b/ext/kate/gstkateutil.h @@ -37,6 +37,15 @@ G_BEGIN_DECLS enum typedef struct { + GstEvent * event; + gboolean (*handler)(GstPad *, GstEvent *); + GstPad *pad; +} GstKateDecoderBaseQueuedEvent; + +typedef struct +{ + GstElement element; + kate_state k; gboolean initialized; @@ -49,11 +58,17 @@ typedef struct gint original_canvas_width; gint original_canvas_height; + GstSegment kate_segment; + gboolean kate_flushing; + + gboolean delay_events; + GQueue *event_queue; } GstKateDecoderBase; extern GstCaps *gst_kate_util_set_header_on_caps (GstElement * element, GstCaps * caps, GList * headers); -extern void gst_kate_util_decode_base_init (GstKateDecoderBase * decoder); +extern void gst_kate_util_decode_base_init (GstKateDecoderBase * decoder, + gboolean delay_events); extern void gst_kate_util_install_decoder_base_properties (GObjectClass * gobject_class); extern gboolean gst_kate_util_decoder_base_get_property (GstKateDecoderBase * @@ -62,7 +77,16 @@ extern gboolean gst_kate_util_decoder_base_get_property (GstKateDecoderBase * extern GstFlowReturn gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, GstElement * element, GstPad * pad, GstBuffer * buffer, GstPad * srcpad, - const kate_event ** ev); + GstPad * tagpad, GstCaps **src_caps, const kate_event ** ev); +extern void +gst_kate_util_decoder_base_set_flushing (GstKateDecoderBase * decoder, + gboolean flushing); +extern void +gst_kate_util_decoder_base_new_segment_event (GstKateDecoderBase * decoder, + GstEvent * event); +extern gboolean +gst_kate_util_decoder_base_update_segment (GstKateDecoderBase * decoder, + GstElement * element, GstBuffer * buf); extern GstStateChangeReturn gst_kate_decoder_base_change_state (GstKateDecoderBase * decoder, GstElement * element, GstElementClass * parent_class, @@ -72,6 +96,8 @@ extern gboolean gst_kate_decoder_base_convert (GstKateDecoderBase * decoder, GstFormat * dest_fmt, gint64 * dest_val); extern gboolean gst_kate_decoder_base_sink_query (GstKateDecoderBase * decoder, GstElement * element, GstPad * pad, GstQuery * query); +extern gboolean +gst_kate_util_decoder_base_queue_event (GstKateDecoderBase * decoder, GstEvent * event, gboolean (*handler)(GstPad *, GstEvent *), GstPad * pad); G_END_DECLS #endif /* __GST_KATE_UTIL_H__ */ |