From e88984ccbd4b2b7b5527cbc3ff488c5c85bb1e97 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Sun, 15 Feb 2009 18:35:04 +0000 Subject: add new Kate plugin, for Kate overlay streams katedec: Kate decoder (text only) kateenc: Kate encoder (text and DVD SPU only) katetag: Kate tagger kateparse: Kate parser tiger: Kate renderer using the Tiger rendering library Fixes #525743. --- ext/kate/gstkateutil.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 ext/kate/gstkateutil.c (limited to 'ext/kate/gstkateutil.c') diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c new file mode 100644 index 00000000..2928771d --- /dev/null +++ b/ext/kate/gstkateutil.c @@ -0,0 +1,371 @@ +/* GStreamer + * Copyright (C) 2008 Vincent Penquerc'h + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "gstkate.h" +#include "gstkateutil.h" + + +GstCaps * +gst_kate_util_set_header_on_caps (GstElement * element, GstCaps * caps, + GList * headers) +{ + GstStructure *structure; + GValue array = { 0 }; + + if (G_UNLIKELY (!caps)) + return NULL; + if (G_UNLIKELY (!headers)) + return NULL; + + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + g_value_init (&array, GST_TYPE_ARRAY); + + while (headers) { + GValue value = { 0 }; + GstBuffer *buffer = headers->data; + g_assert (buffer); + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS); + g_value_init (&value, GST_TYPE_BUFFER); + /* as in theoraenc, we need to copy to avoid circular references */ + buffer = gst_buffer_copy (buffer); + gst_value_set_buffer (&value, buffer); + gst_buffer_unref (buffer); + gst_value_array_append_value (&array, &value); + g_value_unset (&value); + headers = headers->next; + } + + gst_structure_set_value (structure, "streamheader", &array); + g_value_unset (&array); + GST_LOG_OBJECT (element, "here are the newly set caps: %" GST_PTR_FORMAT, + caps); + + return caps; +} + +void +gst_kate_util_install_decoder_base_properties (GObjectClass * gobject_class) +{ + g_object_class_install_property (gobject_class, ARG_DEC_BASE_LANGUAGE, + g_param_spec_string ("language", "Language", "The language of the stream", + "", G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, ARG_DEC_BASE_CATEGORY, + g_param_spec_string ("category", "Category", "The category of the stream", + "", G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, + ARG_DEC_BASE_ORIGINAL_CANVAS_WIDTH, + g_param_spec_int ("original-canvas-width", + "Original canvas width (0 is unspecified)", + "The canvas width this stream was authored for", 0, G_MAXINT, 0, + G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, + ARG_DEC_BASE_ORIGINAL_CANVAS_HEIGHT, + g_param_spec_int ("original-canvas-height", "Original canvas height", + "The canvas height this stream was authored for (0 is unspecified)", + 0, G_MAXINT, 0, G_PARAM_READABLE)); +} + +void +gst_kate_util_decode_base_init (GstKateDecoderBase * decoder) +{ + if (G_UNLIKELY (!decoder)) + return; + + decoder->language = NULL; + decoder->category = NULL; + decoder->original_canvas_width = 0; + decoder->original_canvas_height = 0; + decoder->tags = NULL; + decoder->initialized = FALSE; +} + +gboolean +gst_kate_util_decoder_base_get_property (GstKateDecoderBase * decoder, + GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) +{ + gboolean res = TRUE; + switch (prop_id) { + case ARG_DEC_BASE_LANGUAGE: + g_value_set_string (value, decoder->language); + break; + case ARG_DEC_BASE_CATEGORY: + g_value_set_string (value, decoder->category); + break; + case ARG_DEC_BASE_ORIGINAL_CANVAS_WIDTH: + g_value_set_int (value, decoder->original_canvas_width); + break; + case ARG_DEC_BASE_ORIGINAL_CANVAS_HEIGHT: + g_value_set_int (value, decoder->original_canvas_height); + break; + default: + res = FALSE; + break; + } + return res; +} + +GstFlowReturn +gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, + GstElement * element, GstPad * pad, GstBuffer * buf, GstPad * srcpad, + const kate_event ** ev) +{ + kate_packet kp; + int ret; + GstFlowReturn rflow = GST_FLOW_OK; + + 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]); + 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_WARNING_OBJECT (element, "kate_high_decode_packetin failed (%d)", ret); + return GST_FLOW_ERROR; + } else 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)) + 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 (decoder->k.ki->language && *decoder->k.ki->language) { + GstTagList *tags = gst_tag_list_new (); + if (tags) { + gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_LANGUAGE_CODE, + decoder->k.ki->language, NULL); + // TODO: category - where should it go ? + decoder->tags = + gst_tag_list_merge (decoder->tags, tags, GST_TAG_MERGE_REPLACE); + gst_tag_list_free (tags); + } + } + + /* update properties */ + if (decoder->language) + g_free (decoder->language); + decoder->language = g_strdup (decoder->k.ki->language); + if (decoder->category) + g_free (decoder->category); + decoder->category = g_strdup (decoder->k.ki->category); + decoder->original_canvas_width = decoder->k.ki->original_canvas_width; + decoder->original_canvas_height = decoder->k.ki->original_canvas_height; + + break; + + case 0x81: /* Vorbis comments header */ + GST_INFO_OBJECT (element, "Parsed comments header"); + { + gchar *encoder = NULL; + GstTagList *list = gst_tag_list_from_vorbiscomment_buffer (buf, + (const guint8 *) "\201kate\0\0\0\0", 9, &encoder); + if (list) { + decoder->tags = + gst_tag_list_merge (decoder->tags, list, GST_TAG_MERGE_REPLACE); + gst_tag_list_free (list); + } + + if (!decoder->tags) { + GST_ERROR_OBJECT (element, "failed to decode comment header"); + decoder->tags = gst_tag_list_new (); + } + if (encoder) { + gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, + GST_TAG_ENCODER, encoder, NULL); + g_free (encoder); + } + gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_CODEC, + "kate", NULL); + gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, + GST_TAG_ENCODER_VERSION, decoder->k.ki->bitstream_version_major, + NULL); + + if (decoder->initialized) { + gst_element_found_tags_for_pad (element, srcpad, decoder->tags); + decoder->tags = NULL; + } else { + /* Only push them as messages for the time being. * + * They will be pushed on the pad once the decoder is initialized */ + gst_element_post_message (element, + gst_message_new_tag (GST_OBJECT (element), + gst_tag_list_copy (decoder->tags))); + } + } + break; + + default: + break; + } + + return rflow; +} + +GstStateChangeReturn +gst_kate_decoder_base_change_state (GstKateDecoderBase * decoder, + GstElement * element, GstElementClass * parent_class, + GstStateChange transition) +{ + GstStateChangeReturn res; + int ret; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_DEBUG_OBJECT (element, "READY -> PAUSED, initializing kate state"); + ret = kate_high_decode_init (&decoder->k); + if (ret < 0) { + GST_WARNING_OBJECT (element, "failed to initialize kate state: %d", + ret); + } + decoder->initialized = TRUE; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + res = parent_class->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_DEBUG_OBJECT (element, "PAUSED -> READY, clearing kate state"); + if (decoder->initialized) { + kate_high_decode_clear (&decoder->k); + decoder->initialized = FALSE; + } + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return res; +} + +static GstClockTime +gst_kate_util_granule_time (kate_state * k, gint64 granulepos) +{ + if (G_UNLIKELY (granulepos == -1)) + return -1; + + return kate_granule_time (k->ki, granulepos) * GST_SECOND; +} + +/* +conversions on the sink: + - default is granules at num/den rate (subject to the granule shift) + - default -> time is possible + - bytes do not mean anything, packets can be any number of bytes, and we + have no way to know the number of bytes emitted without decoding +conversions on the source: + - nothing +*/ + +gboolean +gst_kate_decoder_base_convert (GstKateDecoderBase * decoder, + GstElement * element, GstPad * pad, GstFormat src_fmt, gint64 src_val, + GstFormat * dest_fmt, gint64 * dest_val) +{ + gboolean res = FALSE; + + if (src_fmt == *dest_fmt) { + *dest_val = src_val; + return TRUE; + } + + if (!decoder->initialized) { + GST_WARNING_OBJECT (element, "not initialized yet"); + return FALSE; + } + + if (src_fmt == GST_FORMAT_BYTES || *dest_fmt == GST_FORMAT_BYTES) { + GST_WARNING_OBJECT (element, "unsupported format"); + return FALSE; + } + + switch (src_fmt) { + case GST_FORMAT_DEFAULT: + switch (*dest_fmt) { + case GST_FORMAT_TIME: + *dest_val = gst_kate_util_granule_time (&decoder->k, src_val); + res = TRUE; + break; + default: + res = FALSE; + break; + } + break; + default: + res = FALSE; + break; + } + + if (!res) { + GST_WARNING_OBJECT (element, "unsupported format"); + } + + return res; +} + +gboolean +gst_kate_decoder_base_sink_query (GstKateDecoderBase * decoder, + GstElement * element, GstPad * pad, GstQuery * query) +{ + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + if (!gst_kate_decoder_base_convert (decoder, element, pad, src_fmt, + src_val, &dest_fmt, &dest_val)) { + return gst_pad_query_default (pad, query); + } + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + return TRUE; + } + default: + return gst_pad_query_default (pad, query); + } +} -- cgit v1.2.1 From 71efbb1e73a557d50093ccaf2ecc47009b7edd95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 19 Jul 2009 22:29:19 +0100 Subject: kate: fix up for additional subtitle/x-kate media type --- ext/kate/gstkateutil.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ext/kate/gstkateutil.c') diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index 2928771d..f47ba575 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -33,6 +33,8 @@ gst_kate_util_set_header_on_caps (GstElement * element, GstCaps * caps, GstStructure *structure; GValue array = { 0 }; + GST_LOG_OBJECT (element, "caps: %" GST_PTR_FORMAT, caps); + if (G_UNLIKELY (!caps)) return NULL; if (G_UNLIKELY (!headers)) -- cgit v1.2.1 From 3f347c7edd0796fb033bb58f15017f1f6c6c5ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 19 Jul 2009 23:16:07 +0100 Subject: kate: make sure to free some more stuff --- ext/kate/gstkateutil.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'ext/kate/gstkateutil.c') diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index f47ba575..e25321b0 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -25,6 +25,7 @@ #include "gstkate.h" #include "gstkateutil.h" +/* FIXME: shouldn't all this GstKateDecoderBase stuff really be a base class? */ GstCaps * gst_kate_util_set_header_on_caps (GstElement * element, GstCaps * caps, @@ -107,6 +108,22 @@ gst_kate_util_decode_base_init (GstKateDecoderBase * decoder) decoder->initialized = FALSE; } +static void +gst_kate_util_decode_base_reset (GstKateDecoderBase * decoder) +{ + g_free (decoder->language); + decoder->language = NULL; + g_free (decoder->category); + decoder->category = NULL; + if (decoder->tags) { + gst_tag_list_free (decoder->tags); + decoder->tags = NULL; + } + decoder->original_canvas_width = 0; + decoder->original_canvas_height = 0; + decoder->initialized = FALSE; +} + gboolean gst_kate_util_decoder_base_get_property (GstKateDecoderBase * decoder, GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -274,8 +291,10 @@ gst_kate_decoder_base_change_state (GstKateDecoderBase * decoder, kate_high_decode_clear (&decoder->k); decoder->initialized = FALSE; } + gst_kate_util_decode_base_reset (decoder); break; case GST_STATE_CHANGE_READY_TO_NULL: + gst_kate_util_decode_base_reset (decoder); break; default: break; -- cgit v1.2.1 From 4728d7f18eff48760ff2e753142b7313eaf552d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 19 Jul 2009 23:35:05 +0100 Subject: kate: add some FIXMEs --- ext/kate/gstkateutil.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'ext/kate/gstkateutil.c') diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index e25321b0..78f18dae 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -17,6 +17,9 @@ * Boston, MA 02111-1307, USA. */ +/* FIXME: post appropriate GST_ELEMENT_ERROR when returning FLOW_ERROR */ +/* FIXME: shouldn't all this GstKateDecoderBase stuff really be a base class? */ + #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -25,8 +28,6 @@ #include "gstkate.h" #include "gstkateutil.h" -/* FIXME: shouldn't all this GstKateDecoderBase stuff really be a base class? */ - GstCaps * gst_kate_util_set_header_on_caps (GstElement * element, GstCaps * caps, GList * headers) -- cgit v1.2.1 From 265b75ca38913c7a9c0281d472f93e7936b6fa91 Mon Sep 17 00:00:00 2001 From: "ogg.k.ogg.k" Date: Mon, 20 Jul 2009 16:24:23 +0100 Subject: kate: fix some minor memory leaks Makes 'make check-valgrind' work for the kate unit test (#525743) --- ext/kate/gstkateutil.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'ext/kate/gstkateutil.c') diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index 78f18dae..b8915d70 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -185,7 +185,7 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, gst_pad_set_caps (srcpad, caps); gst_caps_unref (caps); if (decoder->k.ki->language && *decoder->k.ki->language) { - GstTagList *tags = gst_tag_list_new (); + GstTagList *old = decoder->tags, *tags = gst_tag_list_new (); if (tags) { gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_LANGUAGE_CODE, decoder->k.ki->language, NULL); @@ -193,6 +193,8 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, decoder->tags = gst_tag_list_merge (decoder->tags, tags, GST_TAG_MERGE_REPLACE); gst_tag_list_free (tags); + if (old) + gst_tag_list_free (old); } } @@ -212,7 +214,8 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, GST_INFO_OBJECT (element, "Parsed comments header"); { gchar *encoder = NULL; - GstTagList *list = gst_tag_list_from_vorbiscomment_buffer (buf, + GstTagList *old = decoder->tags, *list = + gst_tag_list_from_vorbiscomment_buffer (buf, (const guint8 *) "\201kate\0\0\0\0", 9, &encoder); if (list) { decoder->tags = @@ -235,6 +238,9 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, GST_TAG_ENCODER_VERSION, decoder->k.ki->bitstream_version_major, NULL); + if (old) + gst_tag_list_free (old); + if (decoder->initialized) { gst_element_found_tags_for_pad (element, srcpad, decoder->tags); decoder->tags = NULL; -- cgit v1.2.1 From ced14a1ff7a0a7190be214848d3de5af2ab7b0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 21 Jul 2009 00:54:47 +0100 Subject: katedec: only put primary language tag in GST_TAG_LANGUAGE Only put primary language into GST_TAG_LANGUAGE, and convert to lower case, ie. only use "en" of "en_GB". This is per our tag documentation and hence what apps expect. Also add example to kateenc property description so people know a language code is wanted here. --- ext/kate/gstkateutil.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'ext/kate/gstkateutil.c') diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index b8915d70..3d5d3312 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -187,9 +187,15 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, if (decoder->k.ki->language && *decoder->k.ki->language) { GstTagList *old = decoder->tags, *tags = gst_tag_list_new (); if (tags) { + gchar *lang_code; + + /* en_GB -> en */ + lang_code = g_ascii_strdown (decoder->k.ki->language, -1); + g_strdelimit (lang_code, NULL, '\0'); gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_LANGUAGE_CODE, - decoder->k.ki->language, NULL); - // TODO: category - where should it go ? + lang_code, NULL); + g_free (lang_code); + /* TODO: category - where should it go ? */ decoder->tags = gst_tag_list_merge (decoder->tags, tags, GST_TAG_MERGE_REPLACE); gst_tag_list_free (tags); -- cgit v1.2.1 From af54a9afbad27627216e0b2b1f0a7553c8b18ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 21 Jul 2009 01:06:19 +0100 Subject: kate: use new GST_TAG_SUBITLE_CODEC tag instead of GST_TAG_CODEC --- ext/kate/gstkateutil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ext/kate/gstkateutil.c') diff --git a/ext/kate/gstkateutil.c b/ext/kate/gstkateutil.c index 3d5d3312..ca245f99 100644 --- a/ext/kate/gstkateutil.c +++ b/ext/kate/gstkateutil.c @@ -238,8 +238,8 @@ gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, GST_TAG_ENCODER, encoder, NULL); g_free (encoder); } - gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_CODEC, - "kate", NULL); + gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, + GST_TAG_SUBTITLE_CODEC, "Kate", NULL); gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER_VERSION, decoder->k.ki->bitstream_version_major, NULL); -- cgit v1.2.1