3 * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
4 * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
5 * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
6 * Copyright 2008, 2009 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * Alternatively, the contents of this file may be used under the
27 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
28 * which case the following provisions apply instead of the ones
31 * This library is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU Library General Public
33 * License as published by the Free Software Foundation; either
34 * version 2 of the License, or (at your option) any later version.
36 * This library is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 * Library General Public License for more details.
41 * You should have received a copy of the GNU Library General Public
42 * License along with this library; if not, write to the
43 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
44 * Boston, MA 02110-1301, USA.
48 * SECTION:element-kateenc
52 * This element encodes Kate streams.
54 * [Kate](http://libkate.googlecode.com/) is a free codec for text based data,
55 * such as subtitles. Any number of kate streams can be embedded in an Ogg
58 * libkate (see above url) is needed to build this plugin.
62 * This encodes a DVD SPU track to a Kate stream:
64 * gst-launch-1.0 dvdreadsrc ! dvddemux ! dvdsubparse ! kateenc category=spu-subtitles ! oggmux ! filesink location=test.ogg
70 * - should we automatically pick up the language code from the
71 * upstream event tags if none was set via the property?
72 * - turn category property into an enum (freestyle text property in
73 * combination with supposedly strictly defined known values that
74 * aren't even particularly human-readable is just not very nice)? */
83 #include <gst/gsttagsetter.h>
84 #include <gst/tag/tag.h>
86 #include "gstkateelements.h"
87 #include "gstkateutil.h"
88 #include "gstkatespu.h"
89 #include "gstkateenc.h"
91 GST_DEBUG_CATEGORY_EXTERN (gst_kateenc_debug);
92 #define GST_CAT_DEFAULT gst_kateenc_debug
94 /* Filter signals and args */
106 ARG_GRANULE_RATE_NUM,
107 ARG_GRANULE_RATE_DEN,
109 ARG_KEEPALIVE_MIN_TIME,
110 ARG_ORIGINAL_CANVAS_WIDTH,
111 ARG_ORIGINAL_CANVAS_HEIGHT,
112 ARG_DEFAULT_SPU_DURATION,
115 #define DEFAULT_KEEPALIVE_MIN_TIME 2.5f
116 #define DEFAULT_DEFAULT_SPU_DURATION 1.5f
118 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
121 GST_STATIC_CAPS ("text/x-raw, format={ pango-markup, utf8 }; "
122 GST_KATE_SPU_MIME_TYPE)
125 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
128 GST_STATIC_CAPS ("subtitle/x-kate; application/x-kate")
131 static void gst_kate_enc_set_property (GObject * object, guint prop_id,
132 const GValue * value, GParamSpec * pspec);
133 static void gst_kate_enc_get_property (GObject * object, guint prop_id,
134 GValue * value, GParamSpec * pspec);
135 static void gst_kate_enc_dispose (GObject * object);
137 static gboolean gst_kate_enc_setcaps (GstKateEnc * ke, GstCaps * caps);
138 static GstFlowReturn gst_kate_enc_chain (GstPad * pad, GstObject * parent,
140 static GstStateChangeReturn gst_kate_enc_change_state (GstElement * element,
141 GstStateChange transition);
142 static gboolean gst_kate_enc_sink_event (GstPad * pad, GstObject * parent,
144 static gboolean gst_kate_enc_source_query (GstPad * pad, GstObject * parent,
147 GST_DEBUG_CATEGORY (gst_kateenc_debug);
149 #define gst_kate_enc_parent_class parent_class
150 G_DEFINE_TYPE_WITH_CODE (GstKateEnc, gst_kate_enc, GST_TYPE_ELEMENT,
151 G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
153 kate_element_init (plugin); \
154 GST_DEBUG_CATEGORY_INIT (gst_kateenc_debug, "kateenc", 0, "Kate encoder");
155 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (kateenc, "kateenc", GST_RANK_NONE,
156 GST_TYPE_KATE_ENC, _do_init);
158 /* initialize the plugin's class */
160 gst_kate_enc_class_init (GstKateEncClass * klass)
162 GObjectClass *gobject_class;
163 GstElementClass *gstelement_class;
165 gobject_class = (GObjectClass *) klass;
166 gstelement_class = (GstElementClass *) klass;
168 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_kate_enc_set_property);
169 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_kate_enc_get_property);
170 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_kate_enc_dispose);
172 g_object_class_install_property (gobject_class, ARG_LANGUAGE,
173 g_param_spec_string ("language", "Language",
174 "The language of the stream (e.g. \"fr\" or \"fr_FR\" for French)",
175 "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
177 g_object_class_install_property (gobject_class, ARG_CATEGORY,
178 g_param_spec_string ("category", "Category",
179 "The category of the stream", "",
180 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
182 g_object_class_install_property (gobject_class, ARG_GRANULE_RATE_NUM,
183 g_param_spec_int ("granule-rate-numerator", "Granule rate numerator",
184 "The numerator of the granule rate",
185 1, G_MAXINT, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
187 g_object_class_install_property (gobject_class, ARG_GRANULE_RATE_DEN,
188 g_param_spec_int ("granule-rate-denominator", "Granule rate denominator",
189 "The denominator of the granule rate",
190 1, G_MAXINT, 1000, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
192 g_object_class_install_property (gobject_class, ARG_GRANULE_SHIFT,
193 g_param_spec_int ("granule-shift", "Granule shift",
194 "The granule shift", 0, 64, 32,
195 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
197 g_object_class_install_property (gobject_class, ARG_ORIGINAL_CANVAS_WIDTH,
198 g_param_spec_int ("original-canvas-width", "Original canvas width",
199 "The width of the canvas this stream was authored for (0 is unspecified)",
200 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
202 g_object_class_install_property (gobject_class, ARG_ORIGINAL_CANVAS_HEIGHT,
203 g_param_spec_int ("original-canvas-height", "Original canvas height",
204 "The height of the canvas this stream was authored for (0 is unspecified)",
205 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
207 g_object_class_install_property (gobject_class, ARG_KEEPALIVE_MIN_TIME,
208 g_param_spec_float ("keepalive-min-time", "Keepalive minimum time",
209 "Minimum time to emit keepalive packets (0 disables keepalive packets)",
210 0.0f, FLT_MAX, DEFAULT_KEEPALIVE_MIN_TIME,
211 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
213 g_object_class_install_property (gobject_class, ARG_DEFAULT_SPU_DURATION,
214 g_param_spec_float ("default-spu-duration", "Default SPU duration",
215 "The assumed max duration (in seconds) of SPUs with no duration specified",
216 0.0f, FLT_MAX, DEFAULT_DEFAULT_SPU_DURATION,
217 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
219 gstelement_class->change_state =
220 GST_DEBUG_FUNCPTR (gst_kate_enc_change_state);
222 gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
223 gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
225 gst_element_class_set_static_metadata (gstelement_class,
226 "Kate stream encoder", "Codec/Encoder/Subtitle",
227 "Encodes Kate streams from text or subpictures",
228 "Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>");
231 /* initialize the new element
232 * instantiate pads and add them to element
234 * initialize structure
237 gst_kate_enc_init (GstKateEnc * ke)
239 GST_DEBUG_OBJECT (ke, "gst_kate_enc_init");
241 ke->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
242 gst_pad_set_chain_function (ke->sinkpad,
243 GST_DEBUG_FUNCPTR (gst_kate_enc_chain));
244 gst_pad_set_event_function (ke->sinkpad,
245 GST_DEBUG_FUNCPTR (gst_kate_enc_sink_event));
246 gst_element_add_pad (GST_ELEMENT (ke), ke->sinkpad);
248 ke->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
249 gst_pad_set_query_function (ke->srcpad,
250 GST_DEBUG_FUNCPTR (gst_kate_enc_source_query));
251 gst_element_add_pad (GST_ELEMENT (ke), ke->srcpad);
253 ke->initialized = FALSE;
254 ke->headers_sent = FALSE;
255 ke->last_timestamp = 0;
256 ke->latest_end_time = 0;
259 ke->format = GST_KATE_FORMAT_UNDEFINED;
260 ke->granule_rate_numerator = 1000;
261 ke->granule_rate_denominator = 1;
262 ke->granule_shift = 32;
263 ke->original_canvas_width = 0;
264 ke->original_canvas_height = 0;
265 ke->keepalive_min_time = DEFAULT_KEEPALIVE_MIN_TIME;
266 ke->default_spu_duration = DEFAULT_DEFAULT_SPU_DURATION;
267 memcpy (ke->spu_clut, gst_kate_spu_default_clut,
268 sizeof (gst_kate_spu_default_clut));
269 ke->delayed_spu = FALSE;
270 ke->delayed_bitmap = NULL;
271 ke->delayed_palette = NULL;
272 ke->delayed_region = NULL;
276 gst_kate_enc_dispose (GObject * object)
278 GstKateEnc *ke = GST_KATE_ENC (object);
280 GST_LOG_OBJECT (ke, "disposing");
283 g_free (ke->language);
287 g_free (ke->category);
291 GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
295 gst_kate_enc_set_property (GObject * object, guint prop_id,
296 const GValue * value, GParamSpec * pspec)
298 GstKateEnc *ke = GST_KATE_ENC (object);
304 g_free (ke->language);
307 str = g_value_get_string (value);
309 ke->language = g_strdup (str);
313 g_free (ke->category);
316 str = g_value_get_string (value);
318 ke->category = g_strdup (str);
320 case ARG_GRANULE_RATE_NUM:
321 ke->granule_rate_numerator = g_value_get_int (value);
323 case ARG_GRANULE_RATE_DEN:
324 ke->granule_rate_denominator = g_value_get_int (value);
326 case ARG_GRANULE_SHIFT:
327 ke->granule_rate_denominator = g_value_get_int (value);
329 case ARG_KEEPALIVE_MIN_TIME:
330 ke->keepalive_min_time = g_value_get_float (value);
332 case ARG_ORIGINAL_CANVAS_WIDTH:
333 ke->original_canvas_width = g_value_get_int (value);
335 case ARG_ORIGINAL_CANVAS_HEIGHT:
336 ke->original_canvas_height = g_value_get_int (value);
338 case ARG_DEFAULT_SPU_DURATION:
339 ke->default_spu_duration = g_value_get_float (value);
342 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
348 gst_kate_enc_get_property (GObject * object, guint prop_id,
349 GValue * value, GParamSpec * pspec)
351 GstKateEnc *ke = GST_KATE_ENC (object);
355 g_value_set_string (value, ke->language ? ke->language : "");
358 g_value_set_string (value, ke->category ? ke->category : "");
360 case ARG_GRANULE_RATE_NUM:
361 g_value_set_int (value, ke->granule_rate_numerator);
363 case ARG_GRANULE_RATE_DEN:
364 g_value_set_int (value, ke->granule_rate_denominator);
366 case ARG_GRANULE_SHIFT:
367 g_value_set_int (value, ke->granule_shift);
369 case ARG_KEEPALIVE_MIN_TIME:
370 g_value_set_float (value, ke->keepalive_min_time);
372 case ARG_ORIGINAL_CANVAS_WIDTH:
373 g_value_set_int (value, ke->original_canvas_width);
375 case ARG_ORIGINAL_CANVAS_HEIGHT:
376 g_value_set_int (value, ke->original_canvas_height);
378 case ARG_DEFAULT_SPU_DURATION:
379 g_value_set_float (value, ke->default_spu_duration);
382 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
387 /* GstElement vmethod implementations */
390 gst_kate_enc_create_buffer (GstKateEnc * ke, kate_packet * kp,
391 kate_int64_t granpos, GstClockTime timestamp, GstClockTime duration,
396 g_return_val_if_fail (kp != NULL, NULL);
397 g_return_val_if_fail (kp->data != NULL, NULL);
399 buffer = gst_buffer_new_allocate (NULL, kp->nbytes, NULL);
400 if (G_UNLIKELY (!buffer)) {
401 GST_WARNING_OBJECT (ke, "Failed to allocate buffer for %u bytes",
406 gst_buffer_fill (buffer, 0, kp->data, kp->nbytes);
408 /* same system as other Ogg codecs, as per ext/ogg/README:
409 OFFSET_END is the granulepos
410 OFFSET is its time representation
412 GST_BUFFER_OFFSET_END (buffer) = granpos;
413 GST_BUFFER_OFFSET (buffer) = timestamp;
414 GST_BUFFER_TIMESTAMP (buffer) = timestamp;
415 GST_BUFFER_DURATION (buffer) = duration;
421 gst_kate_enc_push_buffer (GstKateEnc * ke, GstBuffer * buffer)
425 ke->last_timestamp = GST_BUFFER_TIMESTAMP (buffer);
426 if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) >
427 ke->latest_end_time) {
428 ke->latest_end_time =
429 GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
432 flow = gst_pad_push (ke->srcpad, buffer);
433 if (G_UNLIKELY (flow != GST_FLOW_OK)) {
434 GST_WARNING_OBJECT (ke->srcpad, "push flow: %s", gst_flow_get_name (flow));
441 gst_kate_enc_push_and_free_kate_packet (GstKateEnc * ke, kate_packet * kp,
442 kate_int64_t granpos, GstClockTime timestamp, GstClockTime duration,
447 GST_LOG_OBJECT (ke, "Creating buffer, %u bytes", (guint) kp->nbytes);
449 gst_kate_enc_create_buffer (ke, kp, granpos, timestamp, duration, header);
450 if (G_UNLIKELY (!buffer)) {
451 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
452 ("Failed to create buffer, %u bytes", (guint) kp->nbytes));
453 kate_packet_clear (kp);
454 return GST_FLOW_ERROR;
457 kate_packet_clear (kp);
459 return gst_kate_enc_push_buffer (ke, buffer);
463 gst_kate_enc_metadata_set1 (const GstTagList * list, const gchar * tag,
466 GstKateEnc *ke = GST_KATE_ENC (kateenc);
469 vc_list = gst_tag_to_vorbis_comments (list, tag);
471 for (l = vc_list; l != NULL; l = l->next) {
472 const gchar *vc_string = (const gchar *) l->data;
473 gchar *key = NULL, *val = NULL;
475 GST_LOG_OBJECT (ke, "Kate comment: %s", vc_string);
476 if (gst_tag_parse_extended_comment (vc_string, &key, NULL, &val, TRUE)) {
477 kate_comment_add_tag (&ke->kc, key, val);
483 g_list_foreach (vc_list, (GFunc) g_free, NULL);
484 g_list_free (vc_list);
488 gst_kate_enc_set_metadata (GstKateEnc * ke)
490 GstTagList *merged_tags;
491 const GstTagList *user_tags;
493 user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (ke));
495 GST_DEBUG_OBJECT (ke, "upstream tags = %" GST_PTR_FORMAT, ke->tags);
496 GST_DEBUG_OBJECT (ke, "user-set tags = %" GST_PTR_FORMAT, user_tags);
498 /* gst_tag_list_merge() will handle NULL for either or both lists fine */
499 merged_tags = gst_tag_list_merge (user_tags, ke->tags,
500 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (ke)));
503 GST_DEBUG_OBJECT (ke, "merged tags = %" GST_PTR_FORMAT, merged_tags);
504 gst_tag_list_foreach (merged_tags, gst_kate_enc_metadata_set1, ke);
505 gst_tag_list_unref (merged_tags);
510 gst_kate_enc_setcaps (GstKateEnc * ke, GstCaps * caps)
512 GST_LOG_OBJECT (ke, "input caps: %" GST_PTR_FORMAT, caps);
514 /* One day we could try to automatically set the category based on the
515 * input format, assuming that the input is subtitles. Currently that
516 * doesn't work yet though, because we send the header packets already from
517 * the sink event handler when receiving the newsegment event, so before
518 * the first buffer (might be tricky to change too, given that there could
519 * be no data at the beginning for a long time). So for now we just try to
520 * make sure people didn't set the category to something obviously wrong. */
521 if (ke->category != NULL) {
522 GstStructure *s = gst_caps_get_structure (caps, 0);
524 if (gst_structure_has_name (s, "text/x-raw")) {
527 format = gst_structure_get_string (s, "format");
528 if (strcmp (format, "utf8") == 0) {
529 ke->format = GST_KATE_FORMAT_TEXT_UTF8;
530 } else if (strcmp (format, "pango-markup") == 0) {
531 ke->format = GST_KATE_FORMAT_TEXT_PANGO_MARKUP;
534 if (strcmp (ke->category, "K-SPU") == 0 ||
535 strcmp (ke->category, "spu-subtitles") == 0) {
536 GST_ELEMENT_WARNING (ke, LIBRARY, SETTINGS, (NULL),
537 ("Category set to '%s', but input is text-based.", ke->category));
539 } else if (gst_structure_has_name (s, "subpicture/x-dvd")) {
540 ke->format = GST_KATE_FORMAT_SPU;
541 if (strcmp (ke->category, "SUB") == 0 ||
542 strcmp (ke->category, "subtitles") == 0) {
543 GST_ELEMENT_WARNING (ke, LIBRARY, SETTINGS, (NULL),
544 ("Category set to '%s', but input is subpictures.", ke->category));
547 GST_ERROR_OBJECT (ke, "unexpected input caps %" GST_PTR_FORMAT, caps);
556 gst_kate_enc_is_simple_subtitle_category (GstKateEnc * ke, const char *category)
558 static const char *const simple[] = {
568 for (n = 0; n < G_N_ELEMENTS (simple); ++n) {
569 if (!strcmp (category, simple[n]))
576 gst_kate_enc_send_headers (GstKateEnc * ke)
578 GstFlowReturn rflow = GST_FLOW_OK;
580 GList *headers = NULL, *item;
582 if (G_UNLIKELY (ke->category == NULL || *ke->category == '\0')) {
583 /* The error code is a bit of a lie, but seems most appropriate. */
584 GST_ELEMENT_ERROR (ke, LIBRARY, SETTINGS, (NULL),
585 ("The 'category' property must be set. For subtitles, set it to "
586 "either 'SUB' (text subtitles) or 'K-SPU' (dvd-style subtitles)"));
587 return GST_FLOW_ERROR;
590 gst_kate_enc_set_metadata (ke);
592 /* encode headers and store them in a list */
595 int ret = kate_encode_headers (&ke->k, &ke->kc, &kp);
599 buffer = gst_kate_enc_create_buffer (ke, &kp, 0, 0, 0, TRUE);
601 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
602 ("Failed to create buffer, %u bytes", (guint) kp.nbytes));
603 rflow = GST_FLOW_ERROR;
606 kate_packet_clear (&kp);
608 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER);
609 headers = g_list_append (headers, buffer);
610 } else if (ret > 0) {
611 GST_LOG_OBJECT (ke, "Last header encoded");
614 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
615 ("Failed encoding headers: %s",
616 gst_kate_util_get_error_message (ret)));
617 rflow = GST_FLOW_ERROR;
622 if (rflow == GST_FLOW_OK) {
623 if (gst_kate_enc_is_simple_subtitle_category (ke, ke->category)) {
624 caps = gst_kate_util_set_header_on_caps (&ke->element,
625 gst_caps_from_string ("subtitle/x-kate"), headers);
627 caps = gst_kate_util_set_header_on_caps (&ke->element,
628 gst_caps_from_string ("application/x-kate"), headers);
631 GST_DEBUG_OBJECT (ke, "here are the caps: %" GST_PTR_FORMAT, caps);
632 gst_pad_set_caps (ke->srcpad, caps);
633 gst_caps_unref (caps);
635 if (ke->pending_segment)
636 gst_pad_push_event (ke->srcpad, ke->pending_segment);
637 ke->pending_segment = NULL;
639 GST_LOG_OBJECT (ke, "pushing headers");
642 GstBuffer *buffer = item->data;
643 GST_LOG_OBJECT (ke, "pushing header %p", buffer);
644 gst_kate_enc_push_buffer (ke, buffer);
648 GST_ERROR_OBJECT (ke, "Failed to set headers on caps");
652 g_list_free (headers);
658 gst_kate_enc_flush_headers (GstKateEnc * ke)
660 GstFlowReturn rflow = GST_FLOW_OK;
661 if (!ke->headers_sent) {
662 GST_INFO_OBJECT (ke, "headers not yet sent, flushing");
663 rflow = gst_kate_enc_send_headers (ke);
664 if (rflow == GST_FLOW_OK) {
665 ke->headers_sent = TRUE;
666 GST_INFO_OBJECT (ke, "headers flushed");
668 GST_WARNING_OBJECT (ke, "Failed to flush headers: %s",
669 gst_flow_get_name (rflow));
676 gst_kate_enc_chain_push_packet (GstKateEnc * ke, kate_packet * kp,
677 GstClockTime start, GstClockTime duration)
679 kate_int64_t granpos;
682 granpos = kate_encode_get_granule (&ke->k);
683 if (G_UNLIKELY (granpos < 0)) {
684 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
685 ("Negative granpos for packet"));
686 kate_packet_clear (kp);
687 return GST_FLOW_ERROR;
690 gst_kate_enc_push_and_free_kate_packet (ke, kp, granpos, start, duration,
692 if (G_UNLIKELY (rflow != GST_FLOW_OK)) {
693 GST_WARNING_OBJECT (ke, "Failed to push Kate packet");
699 gst_kate_enc_generate_keepalive (GstKateEnc * ke, GstClockTime timestamp)
703 kate_float t = timestamp / (double) GST_SECOND;
704 GST_DEBUG_OBJECT (ke, "keepalive at %f", t);
705 ret = kate_encode_keepalive (&ke->k, t, &kp);
707 GST_WARNING_OBJECT (ke, "Failed to encode keepalive packet: %s",
708 gst_kate_util_get_error_message (ret));
710 kate_int64_t granpos = kate_encode_get_granule (&ke->k);
711 GST_LOG_OBJECT (ke, "Keepalive packet encoded");
712 if (gst_kate_enc_push_and_free_kate_packet (ke, &kp, granpos, timestamp, 0,
714 GST_WARNING_OBJECT (ke, "Failed to push keepalive packet");
720 gst_kate_enc_flush_waiting (GstKateEnc * ke, GstClockTime now)
722 GstFlowReturn rflow = GST_FLOW_OK;
723 if (ke->delayed_spu) {
726 GstClockTime keepalive_time;
728 kate_float t0 = ke->delayed_start / (double) GST_SECOND;
729 kate_float t1 = now / (double) GST_SECOND;
732 "We had a delayed SPU packet starting at %f, flushing at %f (assumed duration %f)",
735 ret = kate_encode_text (&ke->k, t0, t1, "", 0, &kp);
736 if (G_UNLIKELY (ret < 0)) {
737 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
738 ("Failed to encode text packet: %s",
739 gst_kate_util_get_error_message (ret)));
740 rflow = GST_FLOW_ERROR;
743 gst_kate_enc_chain_push_packet (ke, &kp, ke->delayed_start,
744 now - ke->delayed_start + 1);
747 if (rflow == GST_FLOW_OK) {
748 GST_DEBUG_OBJECT (ke, "delayed SPU packet flushed");
750 GST_WARNING_OBJECT (ke, "Failed to flush delayed SPU packet: %s",
751 gst_flow_get_name (rflow));
754 /* forget it even if we couldn't flush it */
755 ke->delayed_spu = FALSE;
757 /* free the delayed data */
758 g_free (ke->delayed_bitmap->pixels);
759 g_free (ke->delayed_bitmap);
760 ke->delayed_bitmap = NULL;
761 g_free (ke->delayed_palette->colors);
762 g_free (ke->delayed_palette);
763 ke->delayed_palette = NULL;
764 g_free (ke->delayed_region);
765 ke->delayed_region = NULL;
767 /* now that we've flushed the packet, we want to insert keepalives as requested */
768 if (ke->keepalive_min_time > 0.0f && t1 > t0) {
769 GST_INFO_OBJECT (ke, "generating keepalives at %f from %f to %f",
770 ke->keepalive_min_time, t0, t1);
771 for (keepalive_time = ke->delayed_start;
772 (keepalive_time += ke->keepalive_min_time * GST_SECOND) < now;) {
773 GST_INFO_OBJECT (ke, "generating keepalive at %f",
774 keepalive_time / (double) GST_SECOND);
775 gst_kate_enc_generate_keepalive (ke, keepalive_time);
783 gst_kate_enc_chain_spu (GstKateEnc * ke, GstBuffer * buf)
786 kate_region *kregion;
787 kate_bitmap *kbitmap;
788 kate_palette *kpalette;
791 GstClockTime start, stop;
794 /* allocate region, bitmap, and palette, in case we have to delay encoding them */
795 kregion = (kate_region *) g_malloc (sizeof (kate_region));
796 kbitmap = (kate_bitmap *) g_malloc (sizeof (kate_bitmap));
797 kpalette = (kate_palette *) g_malloc (sizeof (kate_palette));
798 if (!kregion || !kpalette || !kbitmap) {
802 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Out of memory"));
803 return GST_FLOW_ERROR;
806 rflow = gst_kate_spu_decode_spu (ke, buf, kregion, kbitmap, kpalette);
807 if (G_UNLIKELY (rflow != GST_FLOW_OK)) {
808 GST_ERROR_OBJECT (ke, "Failed to decode incoming SPU");
811 static int spu_count = 0;
814 snprintf (name, sizeof (name), "/tmp/bad_spu_%04d", spu_count++);
815 name[sizeof (name) - 1] = 0;
816 f = fopen (name, "w");
818 fwrite (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), 1, f);
826 if (G_UNLIKELY (kbitmap->width == 0 || kbitmap->height == 0)) {
827 /* there are some DVDs (well, at least one) where some dimwits put in a wholly transparent full screen 720x576 SPU !!!!?! */
828 GST_WARNING_OBJECT (ke, "SPU is totally invisible - dimwits");
833 /* timestamp offsets are hidden in the SPU packets */
834 start = GST_BUFFER_TIMESTAMP (buf) + GST_KATE_STM_TO_GST (ke->show_time);
835 stop = GST_BUFFER_TIMESTAMP (buf) + GST_KATE_STM_TO_GST (ke->hide_time);
836 t0 = start / (double) GST_SECOND;
837 t1 = stop / (double) GST_SECOND;
838 GST_DEBUG_OBJECT (ke, "buf ts %f, start/show %hu/%hu",
839 GST_BUFFER_TIMESTAMP (buf) / (double) GST_SECOND, ke->show_time,
844 static int spu_count = 0;
847 snprintf (name, sizeof (name), "/tmp/spu_%04d", spu_count++);
848 name[sizeof (name) - 1] = 0;
849 f = fopen (name, "w");
851 fwrite (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), 1, f);
856 GST_DEBUG_OBJECT (ke, "Encoding %" G_GSIZE_FORMAT "x%" G_GSIZE_FORMAT
857 " SPU: (%" G_GSIZE_FORMAT " bytes) from %f to %f",
858 kbitmap->width, kbitmap->height, gst_buffer_get_size (buf), t0, t1);
860 ret = kate_encode_set_region (&ke->k, kregion);
861 if (G_UNLIKELY (ret < 0)) {
862 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
863 ("Failed to set region: %s", gst_kate_util_get_error_message (ret)));
867 ret = kate_encode_set_palette (&ke->k, kpalette);
868 if (G_UNLIKELY (ret < 0)) {
869 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
870 ("Failed to set palette: %s", gst_kate_util_get_error_message (ret)));
874 ret = kate_encode_set_bitmap (&ke->k, kbitmap);
875 if (G_UNLIKELY (ret < 0)) {
876 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
877 ("Failed to set bitmap: %s", gst_kate_util_get_error_message (ret)));
881 /* Some SPUs have no hide time - so I'm going to delay the encoding of the packet
882 till either a suitable event happens, and the time of this event will be used
883 as the end time of this SPU, which will then be encoded and sent off. Suitable
884 events are the arrival of a subsequent SPU (eg, this SPU will replace the one
885 with no end), EOS, a new segment event, or a time threshold being reached */
886 if (ke->hide_time <= ke->show_time) {
888 "Cannot encode SPU packet now, hide time is now known (starting at %f) - delaying",
890 ke->delayed_spu = TRUE;
891 ke->delayed_start = start;
892 ke->delayed_bitmap = kbitmap;
893 ke->delayed_palette = kpalette;
894 ke->delayed_region = kregion;
899 ret = kate_encode_text (&ke->k, t0, t1, "", 0, &kp);
900 if (G_UNLIKELY (ret < 0)) {
901 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
902 ("Failed to encode empty text for SPU buffer: %s",
903 gst_kate_util_get_error_message (ret)));
907 rflow = gst_kate_enc_chain_push_packet (ke, &kp, start, stop - start + 1);
910 /* Cleanup data if we're not keeping it around */
911 if (!ke->delayed_spu) {
912 g_free (kpalette->colors);
914 g_free (kbitmap->pixels);
923 rflow = GST_FLOW_ERROR;
929 gst_kate_enc_chain_text (GstKateEnc * ke, GstBuffer * buf)
931 kate_packet kp = { 0 };
934 GstClockTime start = GST_BUFFER_TIMESTAMP (buf);
935 GstClockTime stop = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf);
937 if (ke->format == GST_KATE_FORMAT_TEXT_PANGO_MARKUP) {
938 ret = kate_encode_set_markup_type (&ke->k, kate_markup_simple);
939 } else if (ke->format == GST_KATE_FORMAT_TEXT_UTF8) {
940 ret = kate_encode_set_markup_type (&ke->k, kate_markup_none);
942 return GST_FLOW_ERROR;
945 if (G_UNLIKELY (ret < 0)) {
946 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
947 ("Failed to set markup type: %s",
948 gst_kate_util_get_error_message (ret)));
949 rflow = GST_FLOW_ERROR;
952 gboolean need_unmap = TRUE;
953 kate_float t0 = start / (double) GST_SECOND;
954 kate_float t1 = stop / (double) GST_SECOND;
956 if (!gst_buffer_map (buf, &info, GST_MAP_READ)) {
960 GST_WARNING_OBJECT (buf, "Failed to map buffer");
963 GST_LOG_OBJECT (ke, "Encoding text: %*.*s (%u bytes) from %f to %f",
964 (int) info.size, (int) info.size, info.data, (int) info.size, t0, t1);
965 ret = kate_encode_text (&ke->k, t0, t1, (const char *) info.data, info.size,
967 if (G_UNLIKELY (ret < 0)) {
968 GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
969 ("Failed to encode text: %s", gst_kate_util_get_error_message (ret)));
970 rflow = GST_FLOW_ERROR;
972 rflow = gst_kate_enc_chain_push_packet (ke, &kp, start, stop - start + 1);
975 gst_buffer_unmap (buf, &info);
982 * this function does the actual processing
985 gst_kate_enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
987 GstKateEnc *ke = GST_KATE_ENC (parent);
990 GST_DEBUG_OBJECT (ke, "got packet, %" G_GSIZE_FORMAT " bytes",
991 gst_buffer_get_size (buf));
993 /* first push headers if we haven't done that yet */
994 rflow = gst_kate_enc_flush_headers (ke);
996 if (G_LIKELY (rflow == GST_FLOW_OK)) {
997 /* flush any packet we had waiting */
998 rflow = gst_kate_enc_flush_waiting (ke, GST_BUFFER_TIMESTAMP (buf));
1000 if (G_LIKELY (rflow == GST_FLOW_OK)) {
1001 if (ke->format == GST_KATE_FORMAT_SPU) {
1002 /* encode a kate_bitmap */
1003 rflow = gst_kate_enc_chain_spu (ke, buf);
1006 rflow = gst_kate_enc_chain_text (ke, buf);
1011 gst_buffer_unref (buf);
1016 static GstStateChangeReturn
1017 gst_kate_enc_change_state (GstElement * element, GstStateChange transition)
1019 GstKateEnc *ke = GST_KATE_ENC (element);
1020 GstStateChangeReturn res;
1023 GST_INFO_OBJECT (ke, "gst_kate_enc_change_state");
1025 switch (transition) {
1026 case GST_STATE_CHANGE_NULL_TO_READY:
1027 ke->tags = gst_tag_list_new_empty ();
1029 case GST_STATE_CHANGE_READY_TO_PAUSED:
1030 GST_DEBUG_OBJECT (ke, "READY -> PAUSED, initializing kate state");
1031 ret = kate_info_init (&ke->ki);
1033 GST_WARNING_OBJECT (ke, "failed to initialize kate info structure: %s",
1034 gst_kate_util_get_error_message (ret));
1038 ret = kate_info_set_language (&ke->ki, ke->language);
1040 GST_WARNING_OBJECT (ke, "failed to set stream language: %s",
1041 gst_kate_util_get_error_message (ret));
1046 ret = kate_info_set_category (&ke->ki, ke->category);
1048 GST_WARNING_OBJECT (ke, "failed to set stream category: %s",
1049 gst_kate_util_get_error_message (ret));
1054 kate_info_set_original_canvas_size (&ke->ki,
1055 ke->original_canvas_width, ke->original_canvas_height);
1057 GST_WARNING_OBJECT (ke, "failed to set original canvas size: %s",
1058 gst_kate_util_get_error_message (ret));
1061 ret = kate_comment_init (&ke->kc);
1063 GST_WARNING_OBJECT (ke,
1064 "failed to initialize kate comment structure: %s",
1065 gst_kate_util_get_error_message (ret));
1068 ret = kate_encode_init (&ke->k, &ke->ki);
1070 GST_WARNING_OBJECT (ke, "failed to initialize kate state: %s",
1071 gst_kate_util_get_error_message (ret));
1074 ke->headers_sent = FALSE;
1075 ke->initialized = TRUE;
1076 ke->last_timestamp = 0;
1077 ke->latest_end_time = 0;
1078 ke->format = GST_KATE_FORMAT_UNDEFINED;
1080 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1082 case GST_STATE_CHANGE_READY_TO_NULL:
1083 gst_tag_list_unref (ke->tags);
1090 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1091 if (res == GST_STATE_CHANGE_FAILURE) {
1092 GST_WARNING_OBJECT (ke, "Parent failed to change state");
1096 switch (transition) {
1097 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1099 case GST_STATE_CHANGE_PAUSED_TO_READY:
1100 GST_DEBUG_OBJECT (ke, "PAUSED -> READY, clearing kate state");
1101 if (ke->initialized) {
1102 kate_clear (&ke->k);
1103 kate_info_clear (&ke->ki);
1104 kate_comment_clear (&ke->kc);
1105 ke->initialized = FALSE;
1106 ke->last_timestamp = 0;
1107 ke->latest_end_time = 0;
1109 gst_event_replace (&ke->pending_segment, NULL);
1111 case GST_STATE_CHANGE_READY_TO_NULL:
1117 GST_DEBUG_OBJECT (ke, "State change done");
1123 gst_kate_enc_granule_time (kate_state * k, gint64 granulepos)
1127 if (granulepos == -1)
1130 t = kate_granule_time (k->ki, granulepos);
1131 return t * GST_SECOND;
1135 conversions on the sink:
1137 conversions on the source:
1138 - default is granules at num/den rate
1139 - default -> time is possible
1140 - bytes do not mean anything, packets can be any number of bytes, and we
1141 have no way to know the number of bytes emitted without decoding
1145 gst_kate_enc_convert (GstPad * pad, GstFormat src_fmt, gint64 src_val,
1146 GstFormat * dest_fmt, gint64 * dest_val)
1149 gboolean res = FALSE;
1151 if (src_fmt == *dest_fmt) {
1152 *dest_val = src_val;
1156 ke = GST_KATE_ENC (gst_pad_get_parent (pad));
1158 if (!ke->initialized) {
1159 GST_WARNING_OBJECT (ke, "not initialized yet");
1160 gst_object_unref (ke);
1164 if (src_fmt == GST_FORMAT_BYTES || *dest_fmt == GST_FORMAT_BYTES) {
1165 GST_WARNING_OBJECT (ke, "unsupported format");
1166 gst_object_unref (ke);
1171 case GST_FORMAT_DEFAULT:
1172 switch (*dest_fmt) {
1173 case GST_FORMAT_TIME:
1174 *dest_val = gst_kate_enc_granule_time (&ke->k, src_val);
1188 GST_WARNING_OBJECT (ke, "unsupported format");
1191 gst_object_unref (ke);
1196 gst_kate_enc_source_query (GstPad * pad, GstObject * parent, GstQuery * query)
1198 gboolean res = FALSE;
1200 GST_DEBUG ("source query %d", GST_QUERY_TYPE (query));
1202 switch (GST_QUERY_TYPE (query)) {
1203 case GST_QUERY_CONVERT:
1205 GstFormat src_fmt, dest_fmt;
1206 gint64 src_val, dest_val;
1208 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
1209 if (!gst_kate_enc_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val)) {
1210 return gst_pad_query_default (pad, parent, query);
1212 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
1217 res = gst_pad_query_default (pad, parent, query);
1225 gst_kate_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
1227 GstKateEnc *ke = GST_KATE_ENC (parent);
1228 const GstStructure *structure;
1231 switch (GST_EVENT_TYPE (event)) {
1232 case GST_EVENT_CAPS:
1236 gst_event_parse_caps (event, &caps);
1237 ret = gst_kate_enc_setcaps (ke, caps);
1238 gst_event_unref (event);
1241 case GST_EVENT_SEGMENT:{
1244 GST_LOG_OBJECT (ke, "Got newsegment event");
1246 gst_event_copy_segment (event, &seg);
1248 if (!ke->headers_sent) {
1249 if (ke->pending_segment)
1250 gst_event_unref (ke->pending_segment);
1251 ke->pending_segment = event;
1255 if (ke->initialized) {
1256 GST_LOG_OBJECT (ke, "ensuring all headers are in");
1257 if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) {
1258 GST_WARNING_OBJECT (ke, "Failed to flush headers");
1260 if (seg.format != GST_FORMAT_TIME
1261 || !GST_CLOCK_TIME_IS_VALID (seg.start)) {
1262 GST_WARNING_OBJECT (ke,
1263 "No time in newsegment event %p, format %d, timestamp %"
1264 G_GINT64_FORMAT, event, (int) seg.format, seg.start);
1265 /* to be safe, we'd need to generate a keepalive anyway, but we'd have to guess at the timestamp to use; a
1266 good guess would be the last known timestamp plus the keepalive time, but if we then get a packet with a
1267 timestamp less than this, it would fail to encode, which would be Bad. If we don't encode a keepalive, we
1268 run the risk of stalling the pipeline and hanging, which is Very Bad. Oh dear. We can't exit(-1), can we ? */
1270 float t = seg.start / (double) GST_SECOND;
1273 && t - ke->delayed_start / (double) GST_SECOND >=
1274 ke->default_spu_duration) {
1275 if (G_UNLIKELY (gst_kate_enc_flush_waiting (ke,
1276 seg.start) != GST_FLOW_OK)) {
1277 GST_WARNING_OBJECT (ke, "Failed to encode delayed packet");
1278 /* continue with new segment handling anyway */
1282 GST_LOG_OBJECT (ke, "ts %f, last %f (min %f)", t,
1283 ke->last_timestamp / (double) GST_SECOND,
1284 ke->keepalive_min_time);
1285 if (ke->keepalive_min_time > 0.0f
1286 && t - ke->last_timestamp / (double) GST_SECOND >=
1287 ke->keepalive_min_time) {
1288 /* we only generate a keepalive if there is no SPU waiting, as it would
1289 mean out of sequence start times - and granulepos */
1290 if (!ke->delayed_spu) {
1291 gst_kate_enc_generate_keepalive (ke, seg.start);
1298 ret = gst_pad_push_event (ke->srcpad, event);
1303 case GST_EVENT_CUSTOM_DOWNSTREAM:
1304 GST_LOG_OBJECT (ke, "Got custom downstream event");
1305 /* adapted from the dvdsubdec element */
1306 structure = gst_event_get_structure (event);
1307 if (structure != NULL
1308 && gst_structure_has_name (structure, "application/x-gst-dvd")) {
1309 if (ke->initialized) {
1310 GST_LOG_OBJECT (ke, "ensuring all headers are in");
1311 if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) {
1312 GST_WARNING_OBJECT (ke, "Failed to flush headers");
1314 const gchar *event_name =
1315 gst_structure_get_string (structure, "event");
1317 if (!strcmp (event_name, "dvd-spu-clut-change")) {
1322 GST_INFO_OBJECT (ke, "New CLUT received");
1323 for (idx = 0; idx < 16; ++idx) {
1324 g_snprintf (name, sizeof (name), "clut%02d", idx);
1325 found = gst_structure_get_int (structure, name, &value);
1327 ke->spu_clut[idx] = value;
1329 GST_WARNING_OBJECT (ke,
1330 "DVD CLUT event did not contain %s field", name);
1333 } else if (!strcmp (event_name, "dvd-lang-codes")) {
1334 /* we can't know which stream corresponds to us */
1337 GST_WARNING_OBJECT (ke, "custom downstream event with no name");
1342 ret = gst_pad_push_event (ke->srcpad, event);
1346 GST_LOG_OBJECT (ke, "Got tag event");
1350 gst_event_parse_tag (event, &list);
1351 gst_tag_list_insert (ke->tags, list,
1352 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (ke)));
1354 g_assert_not_reached ();
1356 ret = gst_pad_event_default (pad, parent, event);
1360 GST_INFO_OBJECT (ke, "Got EOS event");
1361 if (ke->initialized) {
1362 GST_LOG_OBJECT (ke, "ensuring all headers are in");
1363 if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) {
1364 GST_WARNING_OBJECT (ke, "Failed to flush headers");
1368 GstClockTime delayed_end =
1369 ke->delayed_start + ke->default_spu_duration * GST_SECOND;
1371 if (G_UNLIKELY (gst_kate_enc_flush_waiting (ke,
1372 delayed_end) != GST_FLOW_OK)) {
1373 GST_WARNING_OBJECT (ke, "Failed to encode delayed packet");
1374 /* continue with EOS handling anyway */
1377 ret = kate_encode_finish (&ke->k, -1, &kp);
1379 GST_WARNING_OBJECT (ke, "Failed to encode EOS packet: %s",
1380 gst_kate_util_get_error_message (ret));
1382 kate_int64_t granpos = kate_encode_get_granule (&ke->k);
1383 GST_LOG_OBJECT (ke, "EOS packet encoded");
1384 if (gst_kate_enc_push_and_free_kate_packet (ke, &kp, granpos,
1385 ke->latest_end_time, 0, FALSE)) {
1386 GST_WARNING_OBJECT (ke, "Failed to push EOS packet");
1391 ret = gst_pad_event_default (pad, parent, event);
1395 GST_LOG_OBJECT (ke, "Got unhandled event");
1396 ret = gst_pad_event_default (pad, parent, event);