kate: remove plugin

Bitrotten and not really used in the wild.

The actual render library (libtiger) is not just unmaintained
but the upstream location has disappeared, and it's also not
even packaged by e.g. debian/ubuntu.

Closes #3071

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5408>
This commit is contained in:
Tim-Philipp Müller 2023-09-27 17:47:25 +02:00 committed by GStreamer Marge Bot
parent f6c40bb15c
commit 3438b7a880
26 changed files with 1 additions and 7959 deletions

View file

@ -49,8 +49,6 @@ libamrnb (for AMR-NB support)
(http://www.penguin.cz/~utx/amr) (http://www.penguin.cz/~utx/amr)
libamrwb (for AMR-WB support) libamrwb (for AMR-WB support)
(http://www.penguin.cz/~utx/amr) (http://www.penguin.cz/~utx/amr)
libkate (for Kate support)
(http://libkate.googlecode.com/)
librtmp (for RTMP support) librtmp (for RTMP support)
(http://rtmpdump.mplayerhq.hu/) (http://rtmpdump.mplayerhq.hu/)

View file

@ -34228,373 +34228,6 @@
"tracers": {}, "tracers": {},
"url": "Unknown package origin" "url": "Unknown package origin"
}, },
"kate": {
"description": "Kate plugin",
"elements": {
"katedec": {
"author": "Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>",
"description": "Decodes Kate text streams",
"hierarchy": [
"GstKateDec",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"klass": "Codec/Decoder/Subtitle",
"long-name": "Kate stream text decoder",
"pad-templates": {
"sink": {
"caps": "subtitle/x-kate:\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "text/x-raw:\n format: { pango-markup, utf8 }\nsubpicture/x-dvd:\n",
"direction": "src",
"presence": "always"
}
},
"properties": {
"category": {
"blurb": "The category of the stream",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "NULL",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": false
},
"language": {
"blurb": "The language of the stream",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "NULL",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": false
},
"original-canvas-height": {
"blurb": "The canvas height this stream was authored for (0 is unspecified)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "2147483647",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": false
},
"original-canvas-width": {
"blurb": "The canvas width this stream was authored for",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "2147483647",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": false
},
"remove-markup": {
"blurb": "Remove markup from decoded text ?",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "false",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
}
},
"rank": "primary"
},
"kateenc": {
"author": "Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>",
"description": "Encodes Kate streams from text or subpictures",
"hierarchy": [
"GstKateEnc",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"interfaces": [
"GstTagSetter"
],
"klass": "Codec/Encoder/Subtitle",
"long-name": "Kate stream encoder",
"pad-templates": {
"sink": {
"caps": "text/x-raw:\n format: { pango-markup, utf8 }\nsubpicture/x-dvd:\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "subtitle/x-kate:\napplication/x-kate:\n",
"direction": "src",
"presence": "always"
}
},
"properties": {
"category": {
"blurb": "The category of the stream",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": true
},
"default-spu-duration": {
"blurb": "The assumed max duration (in seconds) of SPUs with no duration specified",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "1.5",
"max": "3.40282e+38",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gfloat",
"writable": true
},
"granule-rate-denominator": {
"blurb": "The denominator of the granule rate",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "1",
"max": "2147483647",
"min": "1",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": true
},
"granule-rate-numerator": {
"blurb": "The numerator of the granule rate",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "1000",
"max": "2147483647",
"min": "1",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": true
},
"granule-shift": {
"blurb": "The granule shift",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "32",
"max": "64",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": true
},
"keepalive-min-time": {
"blurb": "Minimum time to emit keepalive packets (0 disables keepalive packets)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "2.5",
"max": "3.40282e+38",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gfloat",
"writable": true
},
"language": {
"blurb": "The language of the stream (e.g. \"fr\" or \"fr_FR\" for French)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": true
},
"original-canvas-height": {
"blurb": "The height of the canvas this stream was authored for (0 is unspecified)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "2147483647",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": true
},
"original-canvas-width": {
"blurb": "The width of the canvas this stream was authored for (0 is unspecified)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "2147483647",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": true
}
},
"rank": "none"
},
"kateparse": {
"author": "Vincent Penquerc'h <ogg.k.ogg.k at googlemail dot com>",
"description": "parse raw kate streams",
"hierarchy": [
"GstKateParse",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"klass": "Codec/Parser/Subtitle",
"long-name": "Kate stream parser",
"pad-templates": {
"sink": {
"caps": "subtitle/x-kate:\napplication/x-kate:\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "subtitle/x-kate:\napplication/x-kate:\n",
"direction": "src",
"presence": "always"
}
},
"rank": "none"
},
"katetag": {
"author": "Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>",
"description": "Retags kate streams",
"hierarchy": [
"GstKateTag",
"GstKateParse",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"interfaces": [
"GstTagSetter"
],
"klass": "Formatter/Metadata",
"long-name": "Kate stream tagger",
"pad-templates": {
"sink": {
"caps": "subtitle/x-kate:\napplication/x-kate:\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "subtitle/x-kate:\napplication/x-kate:\n",
"direction": "src",
"presence": "always"
}
},
"properties": {
"category": {
"blurb": "Set the category of the stream",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": true
},
"language": {
"blurb": "Set the language of the stream",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": true
},
"original-canvas-height": {
"blurb": "Set the height of the canvas this stream was authored for (0 is unspecified)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "-1",
"max": "2147483647",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": true
},
"original-canvas-width": {
"blurb": "Set the width of the canvas this stream was authored for (0 is unspecified)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "-1",
"max": "2147483647",
"min": "0",
"mutable": "null",
"readable": true,
"type": "gint",
"writable": true
}
},
"rank": "none"
}
},
"filename": "gstkate",
"license": "LGPL",
"other-types": {},
"package": "GStreamer Bad Plug-ins",
"source": "gst-plugins-bad",
"tracers": {},
"url": "Unknown package origin"
},
"kms": { "kms": {
"description": "Video sink using the Linux kernel mode setting API", "description": "Video sink using the Linux kernel mode setting API",
"elements": { "elements": {

View file

@ -1,20 +0,0 @@
Kate is a free codec for text based data, which can also carry images.
It is typically used to create overlays on a video.
libkate, a codec for Kate streams, is required to build this plugin:
http://libkate.googlecode.com/
libtiger, a rendering library for Kate streams, is optional:
http://libtiger.googlecode.com/
The Kate plugin contains various elements to manipulate Kate streams:
- katedec: decodes Kate streams to text and SPU images
- kateenc: encodes Kate streams from text and SPU images
- kateparse: parses Kate streams
- katetag: allows changing metadata in Kate streams
- tiger: decodes and renders Kate streams using libtiger (needs libtiger)
More information about Kate can be found at:
http://wiki.xiph.org/index.php/OggKate

View file

@ -1,75 +0,0 @@
/*
* GStreamer
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstkateelements.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
gboolean ret = FALSE;
ret |= GST_ELEMENT_REGISTER (katedec, plugin);
ret |= GST_ELEMENT_REGISTER (kateenc, plugin);
ret |= GST_ELEMENT_REGISTER (kateparse, plugin);
ret |= GST_ELEMENT_REGISTER (katetag, plugin);
#ifdef HAVE_TIGER
ret |= GST_ELEMENT_REGISTER (tiger, plugin);
#endif
return ret;
}
/* this is the structure that gstreamer looks for to register plugins
*/
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
kate,
"Kate plugin",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -1,561 +0,0 @@
/*
* GStreamer
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright 2008, 2009 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-katedec
* @title: katedec
* @see_also: oggdemux
*
* This element decodes Kate streams.
*
* [Kate](http://libkate.googlecode.com/) is a free codec
* for text based data, such as subtitles. Any number of kate streams can be
* embedded in an Ogg stream.
*
* libkate (see above url) is needed to build this plugin.
*
* ## Example pipeline
*
* This explicitly decodes a Kate stream:
* |[
* gst-launch-1.0 filesrc location=test.ogg ! oggdemux ! katedec ! fakesink silent=TRUE
* ]|
*
* This will automatically detect and use any Kate streams multiplexed
* in an Ogg stream:
* |[
* gst-launch-1.0 playbin uri=file:///tmp/test.ogg
* ]|
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include "gstkateelements.h"
#include "gstkatespu.h"
#include "gstkatedec.h"
GST_DEBUG_CATEGORY_EXTERN (gst_katedec_debug);
#define GST_CAT_DEFAULT gst_katedec_debug
/* Filter signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_REMOVE_MARKUP = DECODER_BASE_ARG_COUNT
};
/* We don't accept application/x-kate here on purpose for now, since we're
* only really interested in subtitle-like things for playback purposes, not
* cracktastic complex overlays or presentation images etc. - those should be
* fed into a tiger overlay plugin directly */
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("subtitle/x-kate")
);
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("text/x-raw, format = { pango-markup, utf8 }; "
GST_KATE_SPU_MIME_TYPE)
);
GST_DEBUG_CATEGORY (gst_katedec_debug);
#define gst_kate_dec_parent_class parent_class
G_DEFINE_TYPE (GstKateDec, gst_kate_dec, GST_TYPE_ELEMENT);
#define _do_init \
kate_element_init (plugin); \
GST_DEBUG_CATEGORY_INIT (gst_katedec_debug, "katedec", 0, "Kate decoder");
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (katedec, "katedec", GST_RANK_PRIMARY,
GST_TYPE_KATE_DEC, _do_init);
static void gst_kate_dec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_kate_dec_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstFlowReturn gst_kate_dec_chain (GstPad * pad, GstObject * parent,
GstBuffer * buf);
static GstStateChangeReturn gst_kate_dec_change_state (GstElement * element,
GstStateChange transition);
static gboolean gst_kate_dec_sink_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static gboolean gst_kate_dec_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_kate_dec_sink_handle_event (GstPad * pad,
GstObject * parent, GstEvent * event);
static gboolean gst_kate_dec_src_query (GstPad * pad, GstObject * parent,
GstQuery * query);
/* initialize the plugin's class */
static void
gst_kate_dec_class_init (GstKateDecClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = gst_kate_dec_set_property;
gobject_class->get_property = gst_kate_dec_get_property;
gst_kate_util_install_decoder_base_properties (gobject_class);
g_object_class_install_property (gobject_class, ARG_REMOVE_MARKUP,
g_param_spec_boolean ("remove-markup", "Remove markup",
"Remove markup from decoded text ?", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_kate_dec_change_state);
gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
gst_element_class_set_static_metadata (gstelement_class,
"Kate stream text decoder", "Codec/Decoder/Subtitle",
"Decodes Kate text streams",
"Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>");
}
/* initialize the new element
* instantiate pads and add them to element
* set functions
* initialize structure
*/
static void
gst_kate_dec_init (GstKateDec * dec)
{
GST_DEBUG_OBJECT (dec, "gst_kate_dec_init");
dec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
gst_pad_set_chain_function (dec->sinkpad,
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_query_function (dec->srcpad,
GST_DEBUG_FUNCPTR (gst_kate_dec_src_query));
gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
gst_kate_util_decode_base_init (&dec->decoder, TRUE);
dec->src_caps = NULL;
dec->output_format = GST_KATE_FORMAT_UNDEFINED;
dec->remove_markup = FALSE;
}
static void
gst_kate_dec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_kate_dec_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstKateDec *kd = GST_KATE_DEC (object);
switch (prop_id) {
case ARG_REMOVE_MARKUP:
g_value_set_boolean (value, kd->remove_markup);
break;
default:
if (!gst_kate_util_decoder_base_get_property (&kd->decoder, object,
prop_id, value, pspec)) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
break;
}
}
static GstFlowReturn
gst_kate_dec_handle_kate_event (GstKateDec * kd, const kate_event * ev)
{
GstFlowReturn rflow = GST_FLOW_OK;
GstKateFormat format = GST_KATE_FORMAT_UNDEFINED;
gchar *escaped;
GstBuffer *buffer;
size_t len;
gboolean plain = TRUE;
if (kd->remove_markup && ev->text_markup_type != kate_markup_none) {
size_t len0 = ev->len + 1;
escaped = g_strdup (ev->text);
if (escaped) {
kate_text_remove_markup (ev->text_encoding, escaped, &len0);
}
plain = TRUE;
} else if (ev->text_markup_type == kate_markup_none) {
/* no pango markup yet, escape text */
/* TODO: actually do the pango thing */
escaped = g_strdup (ev->text);
plain = TRUE;
} else {
escaped = g_strdup (ev->text);
plain = FALSE;
}
if (G_LIKELY (escaped)) {
len = strlen (escaped);
if (len > 0) {
GST_DEBUG_OBJECT (kd, "kate event: %s, escaped %s", ev->text, escaped);
buffer = gst_buffer_new_and_alloc (len + 1);
if (G_LIKELY (buffer)) {
GstCaps *caps;
if (plain)
format = GST_KATE_FORMAT_TEXT_UTF8;
else
format = GST_KATE_FORMAT_TEXT_PANGO_MARKUP;
if (format != kd->output_format) {
caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING,
(format == GST_KATE_FORMAT_TEXT_UTF8) ? "utf8" : "pango-markup",
NULL);
gst_pad_push_event (kd->srcpad, gst_event_new_caps (caps));
gst_caps_unref (caps);
kd->output_format = format;
}
/* allocate and copy the NULs, but don't include them in passed size */
gst_buffer_fill (buffer, 0, escaped, len + 1);
gst_buffer_resize (buffer, 0, len);
GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
GST_BUFFER_DURATION (buffer) =
(ev->end_time - ev->start_time) * GST_SECOND;
rflow = gst_pad_push (kd->srcpad, buffer);
if (rflow == GST_FLOW_NOT_LINKED) {
GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
} else if (rflow != GST_FLOW_OK) {
GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
gst_flow_get_name (rflow));
}
} else {
GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
("Failed to create buffer"));
rflow = GST_FLOW_ERROR;
}
} else {
GST_WARNING_OBJECT (kd, "Empty string, nothing to do");
rflow = GST_FLOW_OK;
}
g_free (escaped);
} else {
GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
("Failed to allocate string"));
rflow = GST_FLOW_ERROR;
}
/* if there's a background paletted bitmap, construct a DVD SPU for it */
if (ev->bitmap && ev->palette) {
GstBuffer *buffer = gst_kate_spu_encode_spu (kd, ev);
if (buffer) {
GstCaps *caps;
GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
GST_BUFFER_DURATION (buffer) =
(ev->end_time - ev->start_time) * GST_SECOND;
if (kd->output_format != GST_KATE_FORMAT_SPU) {
caps = gst_caps_new_empty_simple (GST_KATE_SPU_MIME_TYPE);
gst_pad_push_event (kd->srcpad, gst_event_new_caps (caps));
gst_caps_unref (caps);
kd->output_format = GST_KATE_FORMAT_SPU;
}
rflow = gst_pad_push (kd->srcpad, buffer);
if (rflow == GST_FLOW_NOT_LINKED) {
GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
} else if (rflow != GST_FLOW_OK) {
GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
gst_flow_get_name (rflow));
}
} else {
GST_ELEMENT_ERROR (kd, STREAM, DECODE, (NULL),
("failed to create SPU from paletted bitmap"));
rflow = GST_FLOW_ERROR;
}
}
return rflow;
}
/* GstElement vmethod implementations */
/* chain function
* this function does the actual processing
*/
static GstFlowReturn
gst_kate_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
GstKateDec *kd = GST_KATE_DEC (parent);
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, kd->srcpad, &kd->src_caps,
&ev);
if (G_UNLIKELY (rflow != GST_FLOW_OK)) {
gst_buffer_unref (buf);
return rflow;
}
if (ev) {
rflow = gst_kate_dec_handle_kate_event (kd, ev);
}
not_in_seg:
gst_buffer_unref (buf);
return rflow;
}
static GstStateChangeReturn
gst_kate_dec_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret;
GstKateDec *kd = GST_KATE_DEC (element);
ret = gst_kate_decoder_base_change_state (&kd->decoder, element,
parent_class, transition);
if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) {
gst_caps_replace (&kd->src_caps, NULL);
}
return ret;
}
gboolean
gst_kate_dec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
GstKateDec *kd = GST_KATE_DEC (parent);
gboolean res =
gst_kate_decoder_base_sink_query (&kd->decoder, GST_ELEMENT_CAST (kd),
pad, parent, query);
return res;
}
static gboolean
gst_kate_dec_set_caps (GstKateDec * kd, GstCaps * caps)
{
GstStructure *structure = gst_caps_get_structure (caps, 0);
GstFlowReturn rflow = GST_FLOW_OK;
if (gst_structure_has_field (structure, "streamheader")) {
const GValue *value;
GstBuffer *buf;
const kate_event *ev;
value = gst_structure_get_value (structure, "streamheader");
if (GST_VALUE_HOLDS_BUFFER (value)) {
buf = gst_value_get_buffer (value);
gst_kate_util_decoder_base_chain_kate_packet (&kd->decoder,
GST_ELEMENT_CAST (kd), kd->sinkpad, buf, kd->srcpad, kd->srcpad,
&kd->src_caps, &ev);
if (ev) {
rflow = gst_kate_dec_handle_kate_event (kd, ev);
}
} else if (GST_VALUE_HOLDS_ARRAY (value)) {
gint i, size = gst_value_array_get_size (value);
for (i = 0; i < size; i++) {
const GValue *v = gst_value_array_get_value (value, i);
buf = gst_value_get_buffer (v);
gst_kate_util_decoder_base_chain_kate_packet (&kd->decoder,
GST_ELEMENT_CAST (kd), kd->sinkpad, buf, kd->srcpad, kd->srcpad,
&kd->src_caps, &ev);
if (ev) {
rflow = gst_kate_dec_handle_kate_event (kd, ev);
if (rflow != GST_FLOW_OK && rflow != GST_FLOW_NOT_LINKED)
break;
}
}
} else {
GST_WARNING_OBJECT (kd, "Unhandled streamheader type: %s",
G_VALUE_TYPE_NAME (value));
}
}
return rflow == GST_FLOW_OK || rflow == GST_FLOW_NOT_LINKED;
}
static gboolean
gst_kate_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstKateDec *kd = GST_KATE_DEC (parent);
gboolean res = TRUE;
GST_LOG_OBJECT (pad, "Event on sink pad: %" GST_PTR_FORMAT, event);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
gst_kate_dec_set_caps (kd, caps);
break;
}
default:
break;
}
/* Delay events till we've set caps */
if (gst_kate_util_decoder_base_queue_event (&kd->decoder, event,
&gst_kate_dec_sink_handle_event, parent, pad)) {
return TRUE;
}
res = gst_kate_dec_sink_handle_event (pad, parent, event);
return res;
}
static gboolean
gst_kate_dec_sink_handle_event (GstPad * pad, GstObject * parent,
GstEvent * event)
{
GstKateDec *kd = GST_KATE_DEC (parent);
GST_LOG_OBJECT (pad, "Handling event on sink pad: %s",
GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEGMENT:
break;
case GST_EVENT_FLUSH_START:
gst_kate_util_decoder_base_set_flushing (&kd->decoder, TRUE);
break;
case GST_EVENT_FLUSH_STOP:
gst_kate_util_decoder_base_set_flushing (&kd->decoder, FALSE);
break;
case GST_EVENT_TAG:{
GstTagList *tags;
gst_event_parse_tag (event, &tags);
gst_kate_util_decoder_base_add_tags (&kd->decoder, tags, FALSE);
gst_event_unref (event);
event = gst_kate_util_decoder_base_get_tag_event (&kd->decoder);
break;
}
default:
break;
}
return gst_pad_event_default (pad, parent, event);
}
static gboolean
gst_kate_dec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
GstKateDec *kd = GST_KATE_DEC (parent);
gboolean res = TRUE;
GST_LOG_OBJECT (pad, "Handling query on src pad: %s",
GST_QUERY_TYPE_NAME (query));
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CAPS:{
GstCaps *caps;
if (kd->src_caps) {
GST_DEBUG_OBJECT (kd, "We have src caps %" GST_PTR_FORMAT,
kd->src_caps);
caps = gst_caps_copy (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);
}
gst_query_set_caps_result (query, caps);
gst_caps_unref (caps);
break;
}
default:
res = gst_pad_query_default (pad, parent, query);
break;
}
return res;
}

View file

@ -1,90 +0,0 @@
/*
* GStreamer
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_KATE_DEC_H__
#define __GST_KATE_DEC_H__
#include <kate/kate.h>
#include <gst/gst.h>
#include "gstkateutil.h"
G_BEGIN_DECLS
/* #defines don't like whitespacey bits */
#define GST_TYPE_KATE_DEC \
(gst_kate_dec_get_type())
#define GST_KATE_DEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_KATE_DEC,GstKateDec))
#define GST_KATE_DEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_KATE,GstKateDecClass))
#define GST_IS_KATE_DEC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_KATE_DEC))
#define GST_IS_KATE_DEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_KATE_DEC))
typedef struct _GstKateDec GstKateDec;
typedef struct _GstKateDecClass GstKateDecClass;
struct _GstKateDec
{
GstKateDecoderBase decoder;
GstPad *sinkpad;
GstPad *srcpad;
GstCaps *src_caps;
GstKateFormat output_format;
gboolean remove_markup;
};
struct _GstKateDecClass
{
GstElementClass parent_class;
};
GType gst_kate_dec_get_type (void);
G_END_DECLS
#endif /* __GST_KATE_DEC_H__ */

View file

@ -1,72 +0,0 @@
/*
* GStreamer
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include "gstkateelements.h"
#undef HAVE_TIGER
#ifdef HAVE_TIGER
#include "gstkatetiger.h"
#endif
GST_DEBUG_CATEGORY (gst_kateutil_debug);
void
kate_element_init (GstPlugin * plugin)
{
static gsize res = FALSE;
if (g_once_init_enter (&res)) {
GST_DEBUG_CATEGORY_INIT (gst_kateutil_debug, "kateutil", 0,
"Kate utility functions");
g_once_init_leave (&res, TRUE);
}
}

View file

@ -1,62 +0,0 @@
/*
* GStreamer
* Copyright (C) 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_KATE_ELEMENT_H__
#define __GST_KATE_ELEMENT_H__
#include <gst/gst.h>
G_BEGIN_DECLS
void kate_element_init (GstPlugin * plugin);
GST_ELEMENT_REGISTER_DECLARE (katedec);
GST_ELEMENT_REGISTER_DECLARE (kateenc);
GST_ELEMENT_REGISTER_DECLARE (kateparse);
GST_ELEMENT_REGISTER_DECLARE (tiger);
GST_ELEMENT_REGISTER_DECLARE (katetag);
G_END_DECLS
#endif /* __GST_KATE_ELEMENT_H__ */

File diff suppressed because it is too large Load diff

View file

@ -1,129 +0,0 @@
/*
* GStreamer
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_KATE_ENC_H__
#define __GST_KATE_ENC_H__
#include <gst/gst.h>
#include <kate/kate.h>
#include "gstkateutil.h"
G_BEGIN_DECLS
/* #defines don't like whitespacey bits */
#define GST_TYPE_KATE_ENC \
(gst_kate_enc_get_type())
#define GST_KATE_ENC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_KATE_ENC,GstKateEnc))
#define GST_KATE_ENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_KATE,GstKateEncClass))
#define GST_IS_KATE_ENC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_KATE_ENC))
#define GST_IS_KATE_ENC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_KATE_ENC))
typedef struct _GstKateEnc GstKateEnc;
typedef struct _GstKateEncClass GstKateEncClass;
struct _GstKateEnc
{
GstElement element;
GstPad *sinkpad, *srcpad;
kate_info ki;
kate_comment kc;
kate_state k;
GstTagList *tags;
GstClockTime last_timestamp;
GstClockTime latest_end_time;
GstEvent *pending_segment;
gboolean headers_sent;
gboolean initialized;
gboolean delayed_spu;
GstClockTime delayed_start;
kate_bitmap *delayed_bitmap;
kate_palette *delayed_palette;
kate_region *delayed_region;
gchar *language;
gchar *category;
GstKateFormat format;
int granule_rate_numerator;
int granule_rate_denominator;
int granule_shift;
float keepalive_min_time;
float default_spu_duration;
size_t original_canvas_width;
size_t original_canvas_height;
/* SPU decoding */
guint8 spu_colormap[4];
guint32 spu_clut[16];
guint8 spu_alpha[4];
guint16 spu_top;
guint16 spu_left;
guint16 spu_right;
guint16 spu_bottom;
guint16 spu_pix_data[2];
guint16 show_time;
guint16 hide_time;
};
struct _GstKateEncClass
{
GstElementClass parent_class;
};
GType gst_kate_enc_get_type (void);
G_END_DECLS
#endif /* __GST_KATE_ENC_H__ */

View file

@ -1,634 +0,0 @@
/* GStreamer
* Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
* Copyright (C) 2006 Andy Wingo <wingo@pobox.com>
* Copyright (C) 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-kateparse
* @title: kateparse
* @short_description: parses kate streams
* @see_also: katedec, vorbisparse, oggdemux, theoraparse
*
* The kateparse element will parse the header packets of the Kate
* stream and put them as the streamheader in the caps. This is used in the
* multifdsink case where you want to stream live kate streams to multiple
* clients, each client has to receive the streamheaders first before they can
* consume the kate packets.
*
* This element also makes sure that the buffers that it pushes out are properly
* timestamped and that their offset and offset_end are set. The buffers that
* kateparse outputs have all of the metadata that oggmux expects to receive,
* which allows you to (for example) remux an ogg/kate file.
*
* ## Example pipelines
*
* |[
* gst-launch-1.0 -v filesrc location=kate.ogg ! oggdemux ! kateparse ! fakesink
* ]|
* This pipeline shows that the streamheader is set in the caps, and that each
* buffer has the timestamp, duration, offset, and offset_end set.
*
* |[
* gst-launch-1.0 filesrc location=kate.ogg ! oggdemux ! kateparse \
* ! oggmux ! filesink location=kate-remuxed.ogg
* ]|
* This pipeline shows remuxing. kate-remuxed.ogg might not be exactly the same
* as kate.ogg, but they should produce exactly the same decoded data.
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gstkateelements.h"
#include "gstkateutil.h"
#include "gstkateparse.h"
GST_DEBUG_CATEGORY_EXTERN (gst_kateparse_debug);
#define GST_CAT_DEFAULT gst_kateparse_debug
static GstStaticPadTemplate gst_kate_parse_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("subtitle/x-kate; application/x-kate")
);
static GstStaticPadTemplate gst_kate_parse_src_factory =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("subtitle/x-kate; application/x-kate")
);
GST_DEBUG_CATEGORY (gst_kateparse_debug);
#define gst_kate_parse_parent_class parent_class
G_DEFINE_TYPE (GstKateParse, gst_kate_parse, GST_TYPE_ELEMENT);
#define _do_init \
kate_element_init (plugin); \
GST_DEBUG_CATEGORY_INIT (gst_kateparse_debug, "kateparse", 0, "Kate parser");
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (kateparse, "kateparse", GST_RANK_NONE,
GST_TYPE_KATE_PARSE, _do_init);
static GstFlowReturn gst_kate_parse_chain (GstPad * pad, GstObject * parent,
GstBuffer * buffer);
static GstStateChangeReturn gst_kate_parse_change_state (GstElement * element,
GstStateChange transition);
static gboolean gst_kate_parse_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_kate_parse_src_query (GstPad * pad, GstObject * parent,
GstQuery * query);
#if 0
static gboolean gst_kate_parse_convert (GstPad * pad,
GstFormat src_format, gint64 src_value,
GstFormat * dest_format, gint64 * dest_value);
#endif
static GstFlowReturn gst_kate_parse_parse_packet (GstKateParse * parse,
GstBuffer * buf);
static void
gst_kate_parse_class_init (GstKateParseClass * klass)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
gstelement_class->change_state = gst_kate_parse_change_state;
gst_element_class_add_static_pad_template (gstelement_class,
&gst_kate_parse_src_factory);
gst_element_class_add_static_pad_template (gstelement_class,
&gst_kate_parse_sink_factory);
gst_element_class_set_static_metadata (gstelement_class, "Kate stream parser",
"Codec/Parser/Subtitle",
"parse raw kate streams",
"Vincent Penquerc'h <ogg.k.ogg.k at googlemail dot com>");
klass->parse_packet = GST_DEBUG_FUNCPTR (gst_kate_parse_parse_packet);
}
static void
gst_kate_parse_init (GstKateParse * parse)
{
parse->sinkpad =
gst_pad_new_from_static_template (&gst_kate_parse_sink_factory, "sink");
gst_pad_set_chain_function (parse->sinkpad,
GST_DEBUG_FUNCPTR (gst_kate_parse_chain));
gst_pad_set_event_function (parse->sinkpad,
GST_DEBUG_FUNCPTR (gst_kate_parse_sink_event));
gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
parse->srcpad =
gst_pad_new_from_static_template (&gst_kate_parse_src_factory, "src");
gst_pad_set_query_function (parse->srcpad,
GST_DEBUG_FUNCPTR (gst_kate_parse_src_query));
gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
}
static void
gst_kate_parse_drain_event_queue (GstKateParse * parse)
{
while (parse->event_queue->length) {
GstEvent *event;
event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue));
gst_pad_event_default (parse->sinkpad, NULL, event);
}
}
static GstFlowReturn
gst_kate_parse_push_headers (GstKateParse * parse)
{
/* mark and put on caps */
GstCaps *caps;
GstBuffer *outbuf;
kate_packet packet;
GList *headers, *outbuf_list = NULL;
int ret;
gboolean res;
/* get the headers into the caps, passing them to kate as we go */
caps =
gst_kate_util_set_header_on_caps (&parse->element,
gst_pad_get_current_caps (parse->sinkpad), parse->streamheader);
if (G_UNLIKELY (!caps)) {
GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL),
("Failed to set headers on caps"));
return GST_FLOW_ERROR;
}
GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps);
res = gst_pad_set_caps (parse->srcpad, caps);
gst_caps_unref (caps);
if (G_UNLIKELY (!res)) {
GST_WARNING_OBJECT (parse->srcpad, "Failed to set caps on source pad");
return GST_FLOW_NOT_NEGOTIATED;
}
headers = parse->streamheader;
while (headers) {
GstMapInfo info;
outbuf = GST_BUFFER_CAST (headers->data);
if (!gst_buffer_map (outbuf, &info, GST_MAP_READ)) {
GST_WARNING_OBJECT (outbuf, "Failed to map buffer");
continue;
}
kate_packet_wrap (&packet, info.size, info.data);
ret = kate_decode_headerin (&parse->ki, &parse->kc, &packet);
if (G_UNLIKELY (ret < 0)) {
GST_WARNING_OBJECT (parse, "Failed to decode header: %s",
gst_kate_util_get_error_message (ret));
}
gst_buffer_unmap (outbuf, &info);
/* takes ownership of outbuf, which was previously in parse->streamheader */
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_HEADER);
outbuf_list = g_list_append (outbuf_list, outbuf);
headers = headers->next;
}
/* first process queued events */
gst_kate_parse_drain_event_queue (parse);
/* push out buffers, ignoring return value... */
headers = outbuf_list;
while (headers) {
outbuf = GST_BUFFER_CAST (headers->data);
gst_pad_push (parse->srcpad, outbuf);
headers = headers->next;
}
g_list_free (outbuf_list);
g_list_free (parse->streamheader);
parse->streamheader = NULL;
parse->streamheader_sent = TRUE;
return GST_FLOW_OK;
}
static void
gst_kate_parse_clear_queue (GstKateParse * parse)
{
GST_DEBUG_OBJECT (parse, "Clearing queue");
while (parse->buffer_queue->length) {
GstBuffer *buf;
buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
gst_buffer_unref (buf);
}
while (parse->event_queue->length) {
GstEvent *event;
event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue));
gst_event_unref (event);
}
}
static GstFlowReturn
gst_kate_parse_push_buffer (GstKateParse * parse, GstBuffer * buf,
gint64 granulepos)
{
GST_LOG_OBJECT (parse, "granulepos %16" G_GINT64_MODIFIER "x", granulepos);
if (granulepos < 0) {
/* packets coming not from Ogg won't have a granpos in the offset end,
so we have to synthesize one here - only problem is we don't know
the backlink - pretend there's none for now */
GST_INFO_OBJECT (parse, "No granulepos on buffer, synthesizing one");
granulepos =
kate_duration_granule (&parse->ki,
GST_BUFFER_TIMESTAMP (buf) /
(double) GST_SECOND) << kate_granule_shift (&parse->ki);
}
GST_BUFFER_OFFSET (buf) =
kate_granule_time (&parse->ki, granulepos) * GST_SECOND;
GST_BUFFER_OFFSET_END (buf) = granulepos;
GST_BUFFER_TIMESTAMP (buf) = GST_BUFFER_OFFSET (buf);
return gst_pad_push (parse->srcpad, buf);
}
static GstFlowReturn
gst_kate_parse_drain_queue_prematurely (GstKateParse * parse)
{
GstFlowReturn ret = GST_FLOW_OK;
/* got an EOS event, make sure to push out any buffers that were in the queue
* -- won't normally be the case, but this catches the
* didn't-get-a-granulepos-on-the-last-packet case. Assuming a continuous
* stream. */
/* if we got EOS before any buffers came, go ahead and push the other events
* first */
gst_kate_parse_drain_event_queue (parse);
while (!g_queue_is_empty (parse->buffer_queue)) {
GstBuffer *buf;
gint64 granpos;
buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
granpos = GST_BUFFER_OFFSET_END (buf);
ret = gst_kate_parse_push_buffer (parse, buf, granpos);
if (ret != GST_FLOW_OK)
goto done;
}
g_assert (g_queue_is_empty (parse->buffer_queue));
done:
return ret;
}
static GstFlowReturn
gst_kate_parse_drain_queue (GstKateParse * parse, gint64 granulepos)
{
GstFlowReturn ret = GST_FLOW_OK;
if (!g_queue_is_empty (parse->buffer_queue)) {
GstBuffer *buf;
buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
ret = gst_kate_parse_push_buffer (parse, buf, granulepos);
if (ret != GST_FLOW_OK)
goto done;
}
g_assert (g_queue_is_empty (parse->buffer_queue));
done:
return ret;
}
static GstFlowReturn
gst_kate_parse_queue_buffer (GstKateParse * parse, GstBuffer * buf)
{
GstFlowReturn ret = GST_FLOW_OK;
gint64 granpos;
buf = gst_buffer_make_writable (buf);
/* oggdemux stores the granule pos in the offset end */
granpos = GST_BUFFER_OFFSET_END (buf);
GST_LOG_OBJECT (parse, "granpos %16" G_GINT64_MODIFIER "x", granpos);
g_queue_push_tail (parse->buffer_queue, buf);
#if 1
/* if getting buffers from matroska, we won't have a granpos here... */
//if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) {
ret = gst_kate_parse_drain_queue (parse, granpos);
//}
#else
if (granpos >= 0) {
ret = gst_kate_parse_drain_queue (parse, granpos);
} else {
GST_ELEMENT_ERROR (parse, STREAM, DECODE, (NULL),
("Bad granulepos %" G_GINT64_FORMAT, granpos));
ret = GST_FLOW_ERROR;
}
#endif
return ret;
}
static GstFlowReturn
gst_kate_parse_parse_packet (GstKateParse * parse, GstBuffer * buf)
{
GstFlowReturn ret = GST_FLOW_OK;
guint8 header[1];
gsize size;
g_assert (parse);
parse->packetno++;
size = gst_buffer_extract (buf, 0, header, 1);
GST_LOG_OBJECT (parse, "Got packet %02x, %" G_GSIZE_FORMAT " bytes",
size ? header[0] : -1, gst_buffer_get_size (buf));
if (size > 0 && header[0] & 0x80) {
GST_DEBUG_OBJECT (parse, "Found header %02x", header[0]);
/* if 0x80 is set, it's streamheader,
* so put it on the streamheader list and return */
parse->streamheader = g_list_append (parse->streamheader, buf);
ret = GST_FLOW_OK;
} else {
if (!parse->streamheader_sent) {
GST_DEBUG_OBJECT (parse, "Found non header, pushing headers seen so far");
ret = gst_kate_parse_push_headers (parse);
}
if (ret == GST_FLOW_OK) {
ret = gst_kate_parse_queue_buffer (parse, buf);
}
}
return ret;
}
static GstFlowReturn
gst_kate_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
GstKateParseClass *klass;
GstKateParse *parse;
parse = GST_KATE_PARSE (parent);
klass = GST_KATE_PARSE_CLASS (G_OBJECT_GET_CLASS (parse));
g_assert (klass->parse_packet != NULL);
if (G_UNLIKELY (!gst_pad_has_current_caps (pad)))
return GST_FLOW_NOT_NEGOTIATED;
return klass->parse_packet (parse, buffer);
}
static gboolean
gst_kate_parse_queue_event (GstKateParse * parse, GstEvent * event)
{
GstFlowReturn ret = TRUE;
g_queue_push_tail (parse->event_queue, event);
return ret;
}
static gboolean
gst_kate_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
gboolean ret;
GstKateParse *parse;
parse = GST_KATE_PARSE (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP:
gst_kate_parse_clear_queue (parse);
ret = gst_pad_event_default (pad, parent, event);
break;
case GST_EVENT_EOS:
if (!parse->streamheader_sent) {
GST_DEBUG_OBJECT (parse, "Got EOS, pushing headers seen so far");
ret = gst_kate_parse_push_headers (parse);
if (ret != GST_FLOW_OK)
break;
}
gst_kate_parse_drain_queue_prematurely (parse);
ret = gst_pad_event_default (pad, parent, event);
break;
default:
if (!parse->streamheader_sent && GST_EVENT_IS_SERIALIZED (event)
&& GST_EVENT_TYPE (event) > GST_EVENT_CAPS)
ret = gst_kate_parse_queue_event (parse, event);
else
ret = gst_pad_event_default (pad, parent, event);
break;
}
return ret;
}
#if 0
static gboolean
gst_kate_parse_convert (GstPad * pad,
GstFormat src_format, gint64 src_value,
GstFormat * dest_format, gint64 * dest_value)
{
gboolean res = TRUE;
GstKateParse *parse;
parse = GST_KATE_PARSE (GST_PAD_PARENT (pad));
/* fixme: assumes atomic access to lots of instance variables modified from
* the streaming thread, including 64-bit variables */
if (!parse->streamheader_sent)
return FALSE;
if (src_format == *dest_format) {
*dest_value = src_value;
return TRUE;
}
if (parse->sinkpad == pad &&
(src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
return FALSE;
switch (src_format) {
case GST_FORMAT_TIME:
switch (*dest_format) {
default:
res = FALSE;
}
break;
case GST_FORMAT_DEFAULT:
switch (*dest_format) {
case GST_FORMAT_TIME:
*dest_value = kate_granule_time (&parse->ki, src_value) * GST_SECOND;
break;
default:
res = FALSE;
}
break;
default:
res = FALSE;
}
return res;
}
#endif
static gboolean
gst_kate_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
#if 1
// TODO
GST_WARNING ("gst_kate_parse_src_query");
return FALSE;
#else
gint64 granulepos;
GstKateParse *parse;
gboolean res = FALSE;
parse = GST_KATE_PARSE (parent);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:
{
GstFormat format;
gint64 value;
granulepos = parse->prev_granulepos;
gst_query_parse_position (query, &format, NULL);
/* and convert to the final format */
if (!(res =
gst_kate_parse_convert (pad, GST_FORMAT_DEFAULT, granulepos,
&format, &value)))
goto error;
/* fixme: support segments
value = (value - parse->segment_start) + parse->segment_time;
*/
gst_query_set_position (query, format, value);
GST_LOG_OBJECT (parse, "query %p: peer returned granulepos: %"
G_GUINT64_FORMAT " - we return %" G_GUINT64_FORMAT " (format %u)",
query, granulepos, value, format);
break;
}
case GST_QUERY_DURATION:
{
/* fixme: not threadsafe */
/* query peer for total length */
if (!gst_pad_is_linked (parse->sinkpad)) {
GST_WARNING_OBJECT (parse, "sink pad %" GST_PTR_FORMAT " is not linked",
parse->sinkpad);
goto error;
}
if (!(res = gst_pad_query (GST_PAD_PEER (parse->sinkpad), query)))
goto error;
break;
}
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 (!(res =
gst_kate_parse_convert (pad, src_fmt, src_val, &dest_fmt,
&dest_val)))
goto error;
gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
break;
}
default:
res = gst_pad_query_default (pad, query);
break;
}
return res;
error:
{
GST_WARNING_OBJECT (parse, "error handling query");
return res;
}
#endif
}
static void
gst_kate_parse_free_stream_headers (GstKateParse * parse)
{
while (parse->streamheader != NULL) {
gst_buffer_unref (GST_BUFFER (parse->streamheader->data));
parse->streamheader = g_list_delete_link (parse->streamheader,
parse->streamheader);
}
}
static GstStateChangeReturn
gst_kate_parse_change_state (GstElement * element, GstStateChange transition)
{
GstKateParse *parse = GST_KATE_PARSE (element);
GstStateChangeReturn ret;
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
kate_info_init (&parse->ki);
kate_comment_init (&parse->kc);
parse->packetno = 0;
parse->streamheader_sent = FALSE;
parse->streamheader = NULL;
parse->buffer_queue = g_queue_new ();
parse->event_queue = g_queue_new ();
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
kate_info_clear (&parse->ki);
kate_comment_clear (&parse->kc);
gst_kate_parse_clear_queue (parse);
g_queue_free (parse->buffer_queue);
parse->buffer_queue = NULL;
g_queue_free (parse->event_queue);
parse->event_queue = NULL;
gst_kate_parse_free_stream_headers (parse);
break;
default:
break;
}
return ret;
}

View file

@ -1,78 +0,0 @@
/* -*- c-basic-offset: 2 -*-
* GStreamer
* Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
* Copyright (C) <2008> Vincent Penquerc'h <ogg.k.ogg.k at googlemail dot com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_KATE_PARSE_H__
#define __GST_KATE_PARSE_H__
#include <gst/gst.h>
#include <kate/kate.h>
G_BEGIN_DECLS
#define GST_TYPE_KATE_PARSE \
(gst_kate_parse_get_type())
#define GST_KATE_PARSE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_KATE_PARSE,GstKateParse))
#define GST_KATE_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_KATE_PARSE,GstKateParseClass))
#define GST_IS_KATE_PARSE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_KATE_PARSE))
#define GST_IS_KATE_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_KATE_PARSE))
typedef struct _GstKateParse GstKateParse;
typedef struct _GstKateParseClass GstKateParseClass;
/**
* GstKateParse:
*
* Opaque data structure.
*/
struct _GstKateParse
{
GstElement element;
GstPad *sinkpad;
GstPad *srcpad;
guint packetno;
gboolean streamheader_sent;
GList *streamheader;
GQueue *event_queue;
GQueue *buffer_queue;
kate_info ki;
kate_comment kc;
};
struct _GstKateParseClass
{
GstElementClass parent_class;
/* virtual functions */
GstFlowReturn (*parse_packet) (GstKateParse * parse, GstBuffer * buf);
};
GType gst_kate_parse_get_type (void);
G_END_DECLS
#endif /* __GST_KATE_PARSE_H__ */

View file

@ -1,916 +0,0 @@
/* GStreamer
* Copyright (C) 2009 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <kate/kate.h>
#include <gst/gst.h>
#include <gst/gstpad.h>
#include "gstkatespu.h"
#define MAX_SPU_SIZE 53220
GST_DEBUG_CATEGORY_EXTERN (gst_kateenc_debug);
GST_DEBUG_CATEGORY_EXTERN (gst_katedec_debug);
/* taken off the dvdsubdec element */
const guint32 gst_kate_spu_default_clut[16] = {
0xb48080, 0x248080, 0x628080, 0xd78080,
0x808080, 0x808080, 0x808080, 0x808080,
0x808080, 0x808080, 0x808080, 0x808080,
0x808080, 0x808080, 0x808080, 0x808080
};
#define GST_CAT_DEFAULT gst_kateenc_debug
static void
gst_kate_spu_decode_colormap (GstKateEnc * ke, const guint8 * ptr)
{
ke->spu_colormap[3] = ptr[0] >> 4;
ke->spu_colormap[2] = ptr[0] & 0x0f;
ke->spu_colormap[1] = ptr[1] >> 4;
ke->spu_colormap[0] = ptr[1] & 0x0f;
}
static void
gst_kate_spu_decode_alpha (GstKateEnc * ke, const guint8 * ptr)
{
ke->spu_alpha[3] = ptr[0] >> 4;
ke->spu_alpha[2] = ptr[0] & 0x0f;
ke->spu_alpha[1] = ptr[1] >> 4;
ke->spu_alpha[0] = ptr[1] & 0x0f;
}
static void
gst_kate_spu_decode_area (GstKateEnc * ke, const guint8 * ptr)
{
ke->spu_left = ((((guint16) ptr[0]) & 0xff) << 4) | (ptr[1] >> 4);
ke->spu_top = ((((guint16) ptr[3]) & 0xff) << 4) | (ptr[4] >> 4);
ke->spu_right = ((((guint16) ptr[1]) & 0x0f) << 8) | ptr[2];
ke->spu_bottom = ((((guint16) ptr[4]) & 0x0f) << 8) | ptr[5];
GST_DEBUG_OBJECT (ke, "SPU area %u %u -> %u %d", ke->spu_left, ke->spu_top,
ke->spu_right, ke->spu_bottom);
}
static void
gst_kate_spu_decode_pixaddr (GstKateEnc * ke, const guint8 * ptr)
{
ke->spu_pix_data[0] = GST_KATE_UINT16_BE (ptr + 0);
ke->spu_pix_data[1] = GST_KATE_UINT16_BE (ptr + 2);
}
/* heavily inspired from dvdspudec */
static guint16
gst_kate_spu_decode_colcon (GstKateEnc * ke, const guint8 * ptr, guint16 sz)
{
guint16 nbytes = GST_KATE_UINT16_BE (ptr + 0);
guint16 nbytes_left = nbytes;
GST_LOG_OBJECT (ke, "Number of bytes in color/contrast change command is %u",
nbytes);
if (G_UNLIKELY (nbytes < 2)) {
GST_WARNING_OBJECT (ke,
"Number of bytes in color/contrast change command is %u, should be at least 2",
nbytes);
return 0;
}
if (G_UNLIKELY (nbytes > sz)) {
GST_WARNING_OBJECT (ke,
"Number of bytes in color/contrast change command is %u, but the buffer "
"only contains %u byte(s)", nbytes, sz);
return 0;
}
ptr += 2;
nbytes_left -= 2;
/* we will just skip that data for now */
while (nbytes_left > 0) {
guint32 entry, nchanges, sz;
GST_LOG_OBJECT (ke, "Reading a color/contrast change entry, %u bytes left",
nbytes_left);
if (G_UNLIKELY (nbytes_left < 4)) {
GST_WARNING_OBJECT (ke,
"Not enough bytes to read a full color/contrast entry header");
break;
}
entry = GST_READ_UINT32_BE (ptr);
GST_LOG_OBJECT (ke, "Color/contrast change entry header is %08x", entry);
nchanges = CLAMP ((ptr[2] >> 4), 1, 8);
ptr += 4;
nbytes_left -= 4;
if (entry == 0x0fffffff) {
GST_LOG_OBJECT (ke,
"Encountered color/contrast change termination code, breaking, %u bytes left",
nbytes_left);
break;
}
GST_LOG_OBJECT (ke, "Color/contrast change entry has %u changes", nchanges);
sz = 6 * nchanges;
if (G_UNLIKELY (sz > nbytes_left)) {
GST_WARNING_OBJECT (ke,
"Not enough bytes to read a full color/contrast entry");
break;
}
ptr += sz;
nbytes_left -= sz;
}
return nbytes - nbytes_left;
}
static inline guint8
gst_kate_spu_get_nybble (const guint8 * nybbles, size_t *nybble_offset)
{
guint8 ret;
ret = nybbles[(*nybble_offset) / 2];
/* If the offset is even, we shift the answer down 4 bits, otherwise not */
if ((*nybble_offset) & 0x01)
ret &= 0x0f;
else
ret = ret >> 4;
(*nybble_offset)++;
return ret;
}
static guint16
gst_kate_spu_get_rle_code (const guint8 * nybbles, size_t *nybble_offset)
{
guint16 code;
code = gst_kate_spu_get_nybble (nybbles, nybble_offset);
if (code < 0x4) { /* 4 .. f */
code = (code << 4) | gst_kate_spu_get_nybble (nybbles, nybble_offset);
if (code < 0x10) { /* 1x .. 3x */
code = (code << 4) | gst_kate_spu_get_nybble (nybbles, nybble_offset);
if (code < 0x40) { /* 04x .. 0fx */
code = (code << 4) | gst_kate_spu_get_nybble (nybbles, nybble_offset);
}
}
}
return code;
}
static void
gst_kate_spu_crop_bitmap (GstKateEnc * ke, kate_bitmap * kb, guint16 * dx,
guint16 * dy)
{
int top, bottom, left, right;
guint8 zero = 0;
size_t n, x, y, w, h;
#if 0
/* find the zero */
zero = kb->pixels[0];
for (x = 0; x < kb->width; ++x) {
if (kb->pixels[x] != zero) {
GST_LOG_OBJECT (ke, "top line at %u is not zero: %u", x, kb->pixels[x]);
return;
}
}
#endif
/* top */
for (top = 0; top < kb->height; ++top) {
int empty = 1;
for (x = 0; x < kb->width; ++x) {
if (G_UNLIKELY (kb->pixels[x + top * kb->width] != zero)) {
empty = 0;
break;
}
}
if (!empty)
break;
}
/* bottom */
for (bottom = kb->height - 1; bottom >= top; --bottom) {
int empty = 1;
for (x = 0; x < kb->width; ++x) {
if (G_UNLIKELY (kb->pixels[x + bottom * kb->width] != zero)) {
empty = 0;
break;
}
}
if (!empty)
break;
}
/* left */
for (left = 0; left < kb->width; ++left) {
int empty = 1;
for (y = top; y <= bottom; ++y) {
if (G_UNLIKELY (kb->pixels[left + y * kb->width] != zero)) {
empty = 0;
break;
}
}
if (!empty)
break;
}
/* right */
for (right = kb->width - 1; right >= left; --right) {
int empty = 1;
for (y = top; y <= bottom; ++y) {
if (G_UNLIKELY (kb->pixels[right + y * kb->width] != zero)) {
empty = 0;
break;
}
}
if (!empty)
break;
}
w = right - left + 1;
h = bottom - top + 1;
GST_LOG_OBJECT (ke, "cropped from %" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT
" to %" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT, kb->width, kb->height, w, h);
*dx += left;
*dy += top;
n = 0;
for (y = 0; y < h; ++y) {
memmove (kb->pixels + n, kb->pixels + kb->width * (y + top) + left, w);
n += w;
}
kb->width = w;
kb->height = h;
}
#define CHECK(x) G_STMT_START { \
guint16 _ = (x); \
if (G_UNLIKELY((_) > sz)) { \
GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Read outside buffer")); \
return GST_FLOW_ERROR; \
} \
} G_STMT_END
#define ADVANCE(x) G_STMT_START { \
guint16 _ = (x); ptr += (_); sz -= (_); \
} G_STMT_END
#define IGNORE(x) G_STMT_START { \
guint16 __ = (x); \
CHECK (__); \
ADVANCE (__); \
} G_STMT_END
static GstFlowReturn
gst_kate_spu_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf,
guint16 command_sequence_offset)
{
guint16 date;
guint16 next_command_sequence;
const guint8 *ptr;
GstMapInfo info;
guint16 sz;
if (!gst_buffer_map (buf, &info, GST_MAP_READ)) {
GST_ERROR_OBJECT (ke, "Failed to map buffer");
return GST_FLOW_ERROR;
}
if (command_sequence_offset >= info.size)
goto out_of_range;
ptr = info.data + command_sequence_offset;
sz = info.size - command_sequence_offset;
GST_DEBUG_OBJECT (ke, "Decoding command sequence at %u (%u bytes)",
command_sequence_offset, sz);
CHECK (2);
date = GST_KATE_UINT16_BE (ptr);
ADVANCE (2);
GST_DEBUG_OBJECT (ke, "date %u", date);
CHECK (2);
next_command_sequence = GST_KATE_UINT16_BE (ptr);
ADVANCE (2);
GST_DEBUG_OBJECT (ke, "next command sequence at %u", next_command_sequence);
while (sz) {
guint8 cmd = *ptr++;
switch (cmd) {
case SPU_CMD_FSTA_DSP: /* 0x00 */
GST_DEBUG_OBJECT (ke, "[0] DISPLAY");
break;
case SPU_CMD_DSP: /* 0x01 */
GST_DEBUG_OBJECT (ke, "[1] SHOW");
ke->show_time = date;
break;
case SPU_CMD_STP_DSP: /* 0x02 */
GST_DEBUG_OBJECT (ke, "[2] HIDE");
ke->hide_time = date;
break;
case SPU_CMD_SET_COLOR: /* 0x03 */
GST_DEBUG_OBJECT (ke, "[3] SET COLOR");
CHECK (2);
gst_kate_spu_decode_colormap (ke, ptr);
ADVANCE (2);
break;
case SPU_CMD_SET_ALPHA: /* 0x04 */
GST_DEBUG_OBJECT (ke, "[4] SET ALPHA");
CHECK (2);
gst_kate_spu_decode_alpha (ke, ptr);
ADVANCE (2);
break;
case SPU_CMD_SET_DAREA: /* 0x05 */
GST_DEBUG_OBJECT (ke, "[5] SET DISPLAY AREA");
CHECK (6);
gst_kate_spu_decode_area (ke, ptr);
ADVANCE (6);
break;
case SPU_CMD_DSPXA: /* 0x06 */
GST_DEBUG_OBJECT (ke, "[6] SET PIXEL ADDRESSES");
CHECK (4);
gst_kate_spu_decode_pixaddr (ke, ptr);
GST_DEBUG_OBJECT (ke, " -> first pixel address %u",
ke->spu_pix_data[0]);
GST_DEBUG_OBJECT (ke, " -> second pixel address %u",
ke->spu_pix_data[1]);
ADVANCE (4);
break;
case SPU_CMD_CHG_COLCON: /* 0x07 */
GST_DEBUG_OBJECT (ke, "[7] CHANGE COLOR/CONTRAST");
CHECK (2);
ADVANCE (gst_kate_spu_decode_colcon (ke, ptr, sz));
break;
case SPU_CMD_END: /* 0xff */
GST_DEBUG_OBJECT (ke, "[0xff] END");
if (next_command_sequence != command_sequence_offset) {
GST_DEBUG_OBJECT (ke, "Jumping to next sequence at offset %u",
next_command_sequence);
gst_buffer_unmap (buf, &info);
return gst_kate_spu_decode_command_sequence (ke, buf,
next_command_sequence);
} else {
gst_buffer_unmap (buf, &info);
GST_DEBUG_OBJECT (ke, "No more sequences to decode");
return GST_FLOW_OK;
}
break;
default:
gst_buffer_unmap (buf, &info);
GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
("Invalid SPU command: %u", cmd));
return GST_FLOW_ERROR;
}
}
gst_buffer_unmap (buf, &info);
GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Error parsing SPU"));
return GST_FLOW_ERROR;
/* ERRORS */
out_of_range:
{
gst_buffer_unmap (buf, &info);
GST_ELEMENT_ERROR (ke, STREAM, DECODE, (NULL),
("Command sequence offset %u is out of range %" G_GSIZE_FORMAT,
command_sequence_offset, info.size));
return GST_FLOW_ERROR;
}
}
static inline int
gst_kate_spu_clamp (int value)
{
if (value < 0)
return 0;
if (value > 255)
return 255;
return value;
}
static void
gst_kate_spu_yuv2rgb (int y, int u, int v, int *r, int *g, int *b)
{
#if 0
*r = gst_kate_spu_clamp (y + 1.371 * v);
*g = gst_kate_spu_clamp (y - 0.698 * v - 0.336 * u);
*b = gst_kate_spu_clamp (y + 1.732 * u);
#elif 0
*r = gst_kate_spu_clamp (y + u);
*g = gst_kate_spu_clamp (y - (76 * u - 26 * v) / 256);
*b = gst_kate_spu_clamp (y + v);
#else
y = (y - 16) * 255 / 219;
u = (u - 128) * 255 / 224;
v = (v - 128) * 255 / 224;
*r = gst_kate_spu_clamp (y + 1.402 * v);
*g = gst_kate_spu_clamp (y - 0.34414 * u - 0.71414 * v);
*b = gst_kate_spu_clamp (y + 1.772 * u);
#endif
}
static GstFlowReturn
gst_kate_spu_create_spu_palette (GstKateEnc * ke, kate_palette * kp)
{
size_t n;
kate_palette_init (kp);
kp->ncolors = 4;
kp->colors = (kate_color *) g_malloc (kp->ncolors * sizeof (kate_color));
if (G_UNLIKELY (!kp->colors)) {
GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL), ("Out of memory"));
return GST_FLOW_ERROR;
}
#if 1
for (n = 0; n < kp->ncolors; ++n) {
int idx = ke->spu_colormap[n];
guint32 color = ke->spu_clut[idx];
int y = (color >> 16) & 0xff;
int v = (color >> 8) & 0xff;
int u = color & 0xff;
int r, g, b;
gst_kate_spu_yuv2rgb (y, u, v, &r, &g, &b);
kp->colors[n].r = r;
kp->colors[n].g = g;
kp->colors[n].b = b;
kp->colors[n].a = ke->spu_alpha[n] * 17;
}
#else
/* just make a ramp from 0 to 255 for those non transparent colors */
for (n = 0; n < kp->ncolors; ++n)
if (ke->spu_alpha[n] == 0)
++ntrans;
for (n = 0; n < kp->ncolors; ++n) {
kp->colors[n].r = luma;
kp->colors[n].g = luma;
kp->colors[n].b = luma;
kp->colors[n].a = ke->spu_alpha[n] * 17;
if (ke->spu_alpha[n])
luma /= 2;
}
#endif
return GST_FLOW_OK;
}
GstFlowReturn
gst_kate_spu_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr,
kate_bitmap * kb, kate_palette * kp)
{
GstMapInfo info;
const guint8 *ptr;
size_t sz;
guint16 packet_size;
guint16 x, y;
size_t n;
guint8 *pixptr[2];
size_t nybble_offset[2];
size_t max_nybbles[2];
GstFlowReturn rflow;
guint16 next_command_sequence;
guint16 code;
if (!gst_buffer_map (buf, &info, GST_MAP_READ)) {
GST_ERROR_OBJECT (ke, "Failed to map buffer");
}
ptr = info.data;
sz = info.size;
/* before decoding anything, initialize to sensible defaults */
memset (ke->spu_colormap, 0, sizeof (ke->spu_colormap));
memset (ke->spu_alpha, 0, sizeof (ke->spu_alpha));
ke->spu_top = ke->spu_left = 1;
ke->spu_bottom = ke->spu_right = 0;
ke->spu_pix_data[0] = ke->spu_pix_data[1] = 0;
ke->show_time = ke->hide_time = 0;
/* read sizes and get to the start of the data */
CHECK (2);
packet_size = GST_KATE_UINT16_BE (ptr);
ADVANCE (2);
GST_DEBUG_OBJECT (ke, "packet size %d (GstBuffer size %" G_GSIZE_FORMAT ")",
packet_size, info.size);
CHECK (2);
next_command_sequence = GST_KATE_UINT16_BE (ptr);
ADVANCE (2);
ptr = info.data + next_command_sequence;
sz = info.size - next_command_sequence;
GST_DEBUG_OBJECT (ke, "next command sequence at %u for %u",
next_command_sequence, (guint) sz);
rflow = gst_kate_spu_decode_command_sequence (ke, buf, next_command_sequence);
if (G_UNLIKELY (rflow != GST_FLOW_OK)) {
gst_buffer_unmap (buf, &info);
return rflow;
}
/* if no addresses or sizes were given, or if they define an empty SPU, nothing more to do */
if (G_UNLIKELY (ke->spu_right - ke->spu_left < 0
|| ke->spu_bottom - ke->spu_top < 0 || ke->spu_pix_data[0] == 0
|| ke->spu_pix_data[1] == 0)) {
GST_DEBUG_OBJECT (ke,
"left %d, right %d, top %d, bottom %d, pix data %d %d", ke->spu_left,
ke->spu_right, ke->spu_top, ke->spu_bottom, ke->spu_pix_data[0],
ke->spu_pix_data[1]);
GST_WARNING_OBJECT (ke, "SPU area is empty, nothing to encode");
kate_bitmap_init (kb);
kb->width = kb->height = 0;
gst_buffer_unmap (buf, &info);
return GST_FLOW_OK;
}
/* create the palette */
rflow = gst_kate_spu_create_spu_palette (ke, kp);
if (G_UNLIKELY (rflow != GST_FLOW_OK)) {
gst_buffer_unmap (buf, &info);
return rflow;
}
/* create the bitmap */
kate_bitmap_init (kb);
kb->width = ke->spu_right - ke->spu_left + 1;
kb->height = ke->spu_bottom - ke->spu_top + 1;
kb->bpp = 2;
kb->type = kate_bitmap_type_paletted;
kb->pixels = (unsigned char *) g_malloc (kb->width * kb->height);
if (G_UNLIKELY (!kb->pixels)) {
gst_buffer_unmap (buf, &info);
GST_ELEMENT_ERROR (ke, STREAM, ENCODE, (NULL),
("Failed to allocate memory for pixel data"));
return GST_FLOW_ERROR;
}
n = 0;
pixptr[0] = info.data + ke->spu_pix_data[0];
pixptr[1] = info.data + ke->spu_pix_data[1];
nybble_offset[0] = 0;
nybble_offset[1] = 0;
max_nybbles[0] = 2 * (packet_size - ke->spu_pix_data[0]);
max_nybbles[1] = 2 * (packet_size - ke->spu_pix_data[1]);
for (y = 0; y < kb->height; ++y) {
nybble_offset[y & 1] = GST_ROUND_UP_2 (nybble_offset[y & 1]);
for (x = 0; x < kb->width;) {
if (G_UNLIKELY (nybble_offset[y & 1] >= max_nybbles[y & 1])) {
GST_DEBUG_OBJECT (ke, "RLE overflow, clearing the remainder");
memset (kb->pixels + n, 0, kb->width - x);
n += kb->width - x;
break;
}
code = gst_kate_spu_get_rle_code (pixptr[y & 1], &nybble_offset[y & 1]);
if (code == 0) {
memset (kb->pixels + n, 0, kb->width - x);
n += kb->width - x;
break;
} else {
guint16 npixels = code >> 2;
guint16 pixel = code & 3;
if (npixels > kb->width - x) {
npixels = kb->width - x;
}
memset (kb->pixels + n, pixel, npixels);
n += npixels;
x += npixels;
}
}
}
GST_LOG_OBJECT (ke, "%u/%u bytes left in the data packet",
(guint) (max_nybbles[0] - nybble_offset[0]),
(guint) (max_nybbles[1] - nybble_offset[1]));
/* some streams seem to have huge uncropped SPUs, fix those up */
x = ke->spu_left;
y = ke->spu_top;
gst_kate_spu_crop_bitmap (ke, kb, &x, &y);
/* create the region */
kate_region_init (kr);
if (ke->original_canvas_width > 0 && ke->original_canvas_height > 0) {
/* prefer relative sizes in case we're encoding for a different resolution
that what the SPU was created for */
kr->metric = kate_millionths;
kr->x = 1000000 * (size_t) x / ke->original_canvas_width;
kr->y = 1000000 * (size_t) y / ke->original_canvas_height;
kr->w = 1000000 * kb->width / ke->original_canvas_width;
kr->h = 1000000 * kb->height / ke->original_canvas_height;
} else {
kr->metric = kate_pixel;
kr->x = x;
kr->y = y;
kr->w = kb->width;
kr->h = kb->height;
}
/* some SPUs have no hide time */
if (ke->hide_time == 0) {
GST_INFO_OBJECT (ke, "SPU has no hide time");
/* now, we don't know when the next SPU is scheduled to go, since we probably
haven't received it yet, so we'll just make it a 1 second delay, which is
probably going to end before the next one while being readable */
//ke->hide_time = ke->show_time + (1000 * 90 / 1024);
}
gst_buffer_unmap (buf, &info);
return GST_FLOW_OK;
}
#undef IGNORE
#undef ADVANCE
#undef CHECK
#undef GST_CAT_DEFAULT
#define GST_CAT_DEFAULT gst_katedec_debug
static void
gst_kate_spu_add_nybble (unsigned char *bytes, size_t nbytes, int nybble_offset,
unsigned char nybble)
{
unsigned char *ptr = bytes + nbytes + nybble_offset / 2;
if (!(nybble_offset & 1)) {
*ptr = nybble << 4;
} else {
*ptr |= nybble;
}
}
static void
gst_kate_spu_rgb2yuv (int r, int g, int b, int *y, int *u, int *v)
{
*y = gst_kate_spu_clamp (r * 0.299 * 219 / 255 + g * 0.587 * 219 / 255 +
b * 0.114 * 219 / 255 + 16);
*u = gst_kate_spu_clamp (-r * 0.16874 * 224 / 255 - g * 0.33126 * 224 / 255 +
b * 0.5 * 224 / 255 + 128);
*v = gst_kate_spu_clamp (r * 0.5 * 224 / 255 - g * 0.41869 * 224 / 255 -
b * 0.08131 * 224 / 255 + 128);
}
static void
gst_kate_spu_make_palette (GstKateDec * kd, int palette[4],
const kate_palette * kp)
{
int n;
GstStructure *structure;
GstEvent *event;
char name[16];
int y, u, v;
palette[0] = 0;
palette[1] = 1;
palette[2] = 2;
palette[3] = 3;
structure = gst_structure_new ("application/x-gst-dvd",
"event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
/* Create a separate field for each value in the table. */
for (n = 0; n < 16; n++) {
guint32 color = 0;
if (n < 4) {
gst_kate_spu_rgb2yuv (kp->colors[n].r, kp->colors[n].g, kp->colors[n].b,
&y, &u, &v);
color = (y << 16) | (v << 8) | u;
}
g_snprintf (name, sizeof (name), "clut%02d", n);
gst_structure_set (structure, name, G_TYPE_INT, (int) color, NULL);
}
/* Create the DVD event and put the structure into it. */
event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
GST_LOG_OBJECT (kd, "preparing clut change event %" GST_PTR_FORMAT, event);
gst_pad_push_event (kd->srcpad, event);
}
GstBuffer *
gst_kate_spu_encode_spu (GstKateDec * kd, const kate_event * ev)
{
kate_tracker kin;
unsigned char *bytes = NULL;
size_t nbytes = 0;
GstBuffer *buffer = NULL;
int ret;
int ocw, och;
int top, left, right, bottom;
int pass, line, row;
int lines_offset[2];
int first_commands_offset, second_commands_offset;
int nybble_count;
const kate_bitmap *kb;
const kate_palette *kp;
int palette[4];
int delay;
/* we need a region, a bitmap, and a palette */
if (!ev || !ev->region || !ev->bitmap || !ev->palette)
return NULL;
kb = ev->bitmap;
kp = ev->palette;
/* these need particular properties */
if (kb->type != kate_bitmap_type_paletted || kb->bpp != 2)
return NULL;
if (kp->ncolors != 4)
return NULL;
ret = kate_tracker_init (&kin, ev->ki, ev);
if (ret < 0) {
GST_WARNING_OBJECT (kd, "Failed to initialize kate tracker");
return NULL;
}
ocw = ev->ki->original_canvas_width;
och = ev->ki->original_canvas_height;
ret = kate_tracker_update (&kin, (kate_float) 0, ocw, och, 0, 0, ocw, och);
if (ret < 0)
goto error;
if (kin.has.region) {
top = (int) (kin.region_y + (kate_float) 0.5);
left = (int) (kin.region_x + (kate_float) 0.5);
} else {
GST_WARNING_OBJECT (kd,
"No region information to place SPU, placing at 0 0");
top = left = 0;
}
right = left + kb->width - 1;
bottom = top + kb->height - 1;
/* Allocate space to build the SPU */
bytes = g_malloc (MAX_SPU_SIZE);
if (G_UNLIKELY (!bytes)) {
GST_WARNING_OBJECT (kd,
"Failed to allocate %" G_GSIZE_FORMAT " byte buffer", nbytes);
goto error;
}
nbytes = 4;
nybble_count = 0;
#define CHKBUFSPC(nybbles) \
do { \
if ((nbytes + (nybbles + nybble_count + 1) / 2) > MAX_SPU_SIZE) { \
GST_WARNING_OBJECT (kd, "Not enough space in SPU buffer"); \
goto error; \
} \
} while(0)
/* encode lines */
for (pass = 0; pass <= 1; ++pass) {
lines_offset[pass] = nbytes;
for (line = pass; line < bottom - top + 1; line += 2) {
const unsigned char *ptr = kb->pixels + line * kb->width;
for (row = 0; row < kb->width;) {
int run = 1;
while (row + run < kb->width && run < 255 && ptr[row + run] == ptr[row])
++run;
if (run >= 63 && row + run == kb->width) {
/* special end of line marker */
CHKBUFSPC (4);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, ptr[row]);
} else if (run >= 1 && run <= 3) {
CHKBUFSPC (1);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
(run << 2) | ptr[row]);
} else if (run <= 15) {
CHKBUFSPC (2);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, run >> 2);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
((run & 3) << 2) | ptr[row]);
} else if (run <= 63) {
CHKBUFSPC (3);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, run >> 2);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
((run & 3) << 2) | ptr[row]);
} else {
CHKBUFSPC (4);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, (run >> 6));
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
(run >> 2) & 0xf);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
((run & 3) << 2) | ptr[row]);
}
row += run;
}
if (nybble_count & 1) {
CHKBUFSPC (1);
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
}
nbytes += nybble_count / 2;
nybble_count = 0;
}
}
first_commands_offset = nbytes;
gst_kate_spu_make_palette (kd, palette, kp);
/* Commands header */
CHKBUFSPC (4 * 2);
bytes[nbytes++] = 0;
bytes[nbytes++] = 0;
/* link to next command chunk will be filled later, when we know where it is */
bytes[nbytes++] = 0;
bytes[nbytes++] = 0;
CHKBUFSPC (3 * 2);
bytes[nbytes++] = SPU_CMD_SET_COLOR;
bytes[nbytes++] = (palette[3] << 4) | palette[2];
bytes[nbytes++] = (palette[1] << 4) | palette[0];
CHKBUFSPC (3 * 2);
bytes[nbytes++] = SPU_CMD_SET_ALPHA;
bytes[nbytes++] =
((kp->colors[palette[3]].a / 17) << 4) | (kp->colors[palette[2]].a / 17);
bytes[nbytes++] =
((kp->colors[palette[1]].a / 17) << 4) | (kp->colors[palette[0]].a / 17);
CHKBUFSPC (7 * 2);
bytes[nbytes++] = SPU_CMD_SET_DAREA;
bytes[nbytes++] = left >> 4;
bytes[nbytes++] = ((left & 0xf) << 4) | (right >> 8);
bytes[nbytes++] = right & 0xff;
bytes[nbytes++] = top >> 4;
bytes[nbytes++] = ((top & 0xf) << 4) | (bottom >> 8);
bytes[nbytes++] = bottom & 0xff;
CHKBUFSPC (5 * 2);
bytes[nbytes++] = SPU_CMD_DSPXA;
bytes[nbytes++] = (lines_offset[0] >> 8) & 0xff;
bytes[nbytes++] = lines_offset[0] & 0xff;
bytes[nbytes++] = (lines_offset[1] >> 8) & 0xff;
bytes[nbytes++] = lines_offset[1] & 0xff;
CHKBUFSPC (1 * 2);
bytes[nbytes++] = SPU_CMD_DSP;
CHKBUFSPC (1 * 2);
bytes[nbytes++] = SPU_CMD_END;
/* stop display chunk */
CHKBUFSPC (4 * 2);
second_commands_offset = nbytes;
bytes[first_commands_offset + 2] = (second_commands_offset >> 8) & 0xff;
bytes[first_commands_offset + 3] = second_commands_offset & 0xff;
delay = GST_KATE_GST_TO_STM (ev->end_time - ev->start_time);
bytes[nbytes++] = (delay >> 8) & 0xff;
bytes[nbytes++] = delay & 0xff;
/* close the loop by linking back to self */
bytes[nbytes++] = (second_commands_offset >> 8) & 0xff;
bytes[nbytes++] = second_commands_offset & 0xff;
CHKBUFSPC (1 * 2);
bytes[nbytes++] = SPU_CMD_STP_DSP;
CHKBUFSPC (1 * 2);
bytes[nbytes++] = SPU_CMD_END;
/* Now that we know the size of the SPU, update the size and pointers */
bytes[0] = (nbytes >> 8) & 0xff;
bytes[1] = nbytes & 0xff;
bytes[2] = (first_commands_offset >> 8) & 0xff;
bytes[3] = first_commands_offset & 0xff;
/* Create a buffer with those values */
buffer = gst_buffer_new_wrapped (bytes, nbytes);
if (G_UNLIKELY (!buffer)) {
GST_WARNING_OBJECT (kd,
"Failed to allocate %" G_GSIZE_FORMAT " byte buffer", nbytes);
goto error;
}
GST_BUFFER_OFFSET_END (buffer) = GST_SECOND * (ev->end_time);
GST_BUFFER_OFFSET (buffer) = GST_SECOND * (ev->start_time);
GST_BUFFER_TIMESTAMP (buffer) = GST_SECOND * (ev->start_time);
GST_BUFFER_DURATION (buffer) = GST_SECOND * (ev->end_time - ev->start_time);
GST_DEBUG_OBJECT (kd, "SPU uses %" G_GSIZE_FORMAT " bytes", nbytes);
kate_tracker_clear (&kin);
return buffer;
error:
kate_tracker_clear (&kin);
g_free (bytes);
return NULL;
}

View file

@ -1,65 +0,0 @@
/* -*- c-basic-offset: 2 -*-
* GStreamer
* Copyright (C) <2009> ogg.k.ogg.k <ogg.k.ogg.k at googlemail dot com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_KATE_SPU_H__
#define __GST_KATE_SPU_H__
#include <gst/gst.h>
#include <kate/kate.h>
#include "gstkateenc.h"
#include "gstkatedec.h"
#define GST_KATE_UINT16_BE(ptr) ( ( ((guint16)((ptr)[0])) <<8) | ((ptr)[1]) )
/* taken off the DVD SPU decoder - now is time for today's WTF ???? */
#define GST_KATE_STM_TO_GST(stm) ((GST_MSECOND * 1024 * (stm)) / 90)
#define GST_KATE_GST_TO_STM(gst) ((int)(((gst) * 90000 ) / 1024))
#define GST_KATE_SPU_MIME_TYPE "subpicture/x-dvd"
G_BEGIN_DECLS
enum GstKateSpuCmd
{
SPU_CMD_FSTA_DSP = 0x00, /* Forced Display */
SPU_CMD_DSP = 0x01, /* Display Start */
SPU_CMD_STP_DSP = 0x02, /* Display Off */
SPU_CMD_SET_COLOR = 0x03, /* Set the color indexes for the palette */
SPU_CMD_SET_ALPHA = 0x04, /* Set the alpha indexes for the palette */
SPU_CMD_SET_DAREA = 0x05, /* Set the display area for the SPU */
SPU_CMD_DSPXA = 0x06, /* Pixel data addresses */
SPU_CMD_CHG_COLCON = 0x07, /* Change Color & Contrast */
SPU_CMD_END = 0xff
};
extern const guint32 gst_kate_spu_default_clut[16];
extern GstFlowReturn
gst_kate_spu_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr,
kate_bitmap * kb, kate_palette * kp);
extern GstBuffer*
gst_kate_spu_encode_spu (GstKateDec * kd, const kate_event * ev);
G_END_DECLS
#endif /* __GST_KATE_SPU_H__ */

View file

@ -1,346 +0,0 @@
/*
* GStreamer
* Copyright (C) 2006 James Livingston <doclivingston@gmail.com>
* Copyright (C) 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-katetag
* @title: katetag
* @see_also: #oggdemux, #oggmux, #kateparse, #GstTagSetter
* @short_description: retags kate streams
*
* The katetag element can change the tag contained within a raw
* kate stream. Specifically, it modifies the comments header packet
* of the kate stream, as well as the language and category of the
* kate stream.
*
* The element will also process the stream as the #kateparse element does
* so it can be used when remuxing an Ogg Kate stream, without additional
* elements.
*
* Applications can set the tags to write using the #GstTagSetter interface.
* Tags contained within the kate stream will be picked up
* automatically (and merged according to the merge mode set via the tag
* setter interface).
*
* ## Example pipelines
*
* This element is only useful with gst-launch-1.0 for modifying the language
* and/or category (which are properties of the stream located in the kate
* beginning of stream header), because it does not support setting the tags
* on a #GstTagSetter interface. Conceptually, the element will usually be
* used like:
* |[
* gst-launch-1.0 -v filesrc location=foo.ogg ! oggdemux ! katetag ! oggmux ! filesink location=bar.ogg
* ]|
*
* This pipeline will set the language and category of the stream to the
* given values:
* |[
* gst-launch-1.0 -v filesrc location=foo.ogg ! oggdemux ! katetag language=pt_BR category=subtitles ! oggmux ! filesink location=bar.ogg
* ]|
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <glib.h>
#include <gst/tag/tag.h>
#include <gst/gsttagsetter.h>
#include <kate/kate.h>
#include "gstkateelements.h"
#include "gstkatetag.h"
GST_DEBUG_CATEGORY_EXTERN (gst_katetag_debug);
#define GST_CAT_DEFAULT gst_katetag_debug
enum
{
ARG_0,
ARG_LANGUAGE,
ARG_CATEGORY,
ARG_ORIGINAL_CANVAS_WIDTH,
ARG_ORIGINAL_CANVAS_HEIGHT,
};
static GstFlowReturn gst_kate_tag_parse_packet (GstKateParse * parse,
GstBuffer * buffer);
static void gst_kate_tag_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_kate_tag_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_kate_tag_dispose (GObject * object);
GST_DEBUG_CATEGORY (gst_katetag_debug);
#define gst_kate_tag_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstKateTag, gst_kate_tag, GST_TYPE_KATE_PARSE,
G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
#define _do_init \
kate_element_init (plugin); \
GST_DEBUG_CATEGORY_INIT (gst_katetag_debug, "katetag", 0, "Kate tagger");
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (katetag, "katetag", GST_RANK_NONE,
GST_TYPE_KATE_TAG, _do_init);
static void
gst_kate_tag_class_init (GstKateTagClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstKateParseClass *gstkateparse_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstkateparse_class = GST_KATE_PARSE_CLASS (klass);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_kate_tag_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_kate_tag_get_property);
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_kate_tag_dispose);
g_object_class_install_property (gobject_class, ARG_LANGUAGE,
g_param_spec_string ("language", "Language",
"Set the language of the stream", "",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_CATEGORY,
g_param_spec_string ("category", "Category",
"Set the category of the stream", "",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_ORIGINAL_CANVAS_WIDTH,
g_param_spec_int ("original-canvas-width", "Original canvas width",
"Set the width of the canvas this stream was authored for (0 is unspecified)",
0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_ORIGINAL_CANVAS_HEIGHT,
g_param_spec_int ("original-canvas-height", "Original canvas height",
"Set the height of the canvas this stream was authored for (0 is unspecified)",
0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_set_static_metadata (gstelement_class, "Kate stream tagger",
"Formatter/Metadata",
"Retags kate streams", "Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>");
gstkateparse_class->parse_packet =
GST_DEBUG_FUNCPTR (gst_kate_tag_parse_packet);
}
static void
gst_kate_tag_init (GstKateTag * kt)
{
kt->language = NULL;
kt->category = NULL;
kt->original_canvas_width = -1;
kt->original_canvas_height = -1;
}
static void
gst_kate_tag_dispose (GObject * object)
{
GstKateTag *kt = GST_KATE_TAG (object);
GST_LOG_OBJECT (kt, "disposing");
if (kt->language) {
g_free (kt->language);
kt->language = NULL;
}
if (kt->category) {
g_free (kt->category);
kt->category = NULL;
}
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
}
static void
gst_kate_tag_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstKateTag *kt = GST_KATE_TAG (object);
const char *str;
switch (prop_id) {
case ARG_LANGUAGE:
if (kt->language) {
g_free (kt->language);
kt->language = NULL;
}
str = g_value_get_string (value);
if (str)
kt->language = g_strdup (str);
break;
case ARG_CATEGORY:
if (kt->category) {
g_free (kt->category);
kt->category = NULL;
}
str = g_value_get_string (value);
if (str)
kt->category = g_strdup (str);
break;
case ARG_ORIGINAL_CANVAS_WIDTH:
kt->original_canvas_width = g_value_get_int (value);
break;
case ARG_ORIGINAL_CANVAS_HEIGHT:
kt->original_canvas_height = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_kate_tag_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstKateTag *kt = GST_KATE_TAG (object);
switch (prop_id) {
case ARG_LANGUAGE:
g_value_set_string (value, kt->language ? kt->language : "");
break;
case ARG_CATEGORY:
g_value_set_string (value, kt->category ? kt->category : "");
break;
case ARG_ORIGINAL_CANVAS_WIDTH:
g_value_set_int (value, kt->original_canvas_width);
break;
case ARG_ORIGINAL_CANVAS_HEIGHT:
g_value_set_int (value, kt->original_canvas_height);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static guint16
encode_canvas_size (size_t size)
{
size_t base = size;
size_t shift = 0;
int value;
while (base & ~((1 << 12) - 1)) {
/* we have a high bit we can't fit, increase shift if we wouldn't lose low bits */
if ((size >> shift) & 1)
return 0;
++shift;
base >>= 1;
}
if (G_UNLIKELY (shift >= 16))
return 0;
/* the size can be represented in our encoding */
value = (base << 4) | shift;
return (guint16) value;
}
static GstFlowReturn
gst_kate_tag_parse_packet (GstKateParse * parse, GstBuffer * buffer)
{
GstTagList *old_tags, *new_tags;
const GstTagList *user_tags;
GstKateTag *kt;
gchar *encoder = NULL;
GstBuffer *new_buf;
GstMapInfo info;
kt = GST_KATE_TAG (parse);
if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
GST_ERROR_OBJECT (parse, "Failed to map buffer");
return GST_FLOW_ERROR;
}
/* rewrite the language and category */
if (info.size >= 64 && info.data[0] == 0x80) {
GstBuffer *new_buffer;
gst_buffer_unmap (buffer, &info);
new_buffer = gst_buffer_copy (buffer);
gst_buffer_unref (buffer);
buffer = new_buffer;
if (!gst_buffer_map (buffer, &info, GST_MAP_READWRITE)) {
GST_ERROR_OBJECT (parse, "Failed to map copied buffer READWRITE");
return GST_FLOW_ERROR;
}
/* language is at offset 32, 16 bytes, zero terminated */
if (kt->language) {
strncpy ((char *) info.data + 32, kt->language, 15);
info.data[47] = 0;
}
/* category is at offset 48, 16 bytes, zero terminated */
if (kt->category) {
strncpy ((char *) info.data + 48, kt->category, 15);
info.data[63] = 0;
}
if (kt->original_canvas_width >= 0) {
guint16 v = encode_canvas_size (kt->original_canvas_width);
info.data[16] = v & 0xff;
info.data[17] = (v >> 8) & 0xff;
}
if (kt->original_canvas_height >= 0) {
guint16 v = encode_canvas_size (kt->original_canvas_height);
info.data[18] = v & 0xff;
info.data[19] = (v >> 8) & 0xff;
}
}
/* rewrite the comments packet */
if (info.size >= 9 && info.data[0] == 0x81) {
old_tags =
gst_tag_list_from_vorbiscomment (info.data, info.size,
(const guint8 *) "\201kate\0\0\0\0", 9, &encoder);
user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (kt));
gst_buffer_unmap (buffer, &info);
/* build new tag list */
new_tags = gst_tag_list_merge (user_tags, old_tags,
gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (kt)));
gst_tag_list_unref (old_tags);
new_buf =
gst_tag_list_to_vorbiscomment_buffer (new_tags,
(const guint8 *) "\201kate\0\0\0\0", 9, encoder);
gst_buffer_copy_into (new_buf, buffer, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
gst_tag_list_unref (new_tags);
g_free (encoder);
gst_buffer_unref (buffer);
/* the buffer will have the framing bit used by Vorbis, but we don't use it */
gst_buffer_resize (new_buf, 0, gst_buffer_get_size (new_buf) - 1);
buffer = new_buf;
} else {
gst_buffer_unmap (buffer, &info);
}
return GST_KATE_PARSE_CLASS (parent_class)->parse_packet (parse, buffer);
}

View file

@ -1,65 +0,0 @@
/* -*- c-basic-offset: 2 -*-
* GStreamer
* Copyright (C) <2006> James Livingston <doclivingston@gmail.com>
* Copyright (C) <2008> Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_KATE_TAG_H__
#define __GST_KATE_TAG_H__
#include "gstkateparse.h"
G_BEGIN_DECLS
#define GST_TYPE_KATE_TAG \
(gst_kate_tag_get_type())
#define GST_KATE_TAG(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_KATE_TAG,GstKateTag))
#define GST_KATE_TAG_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_KATE_TAG,GstKateTagClass))
#define GST_IS_KATE_TAG(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_KATE_TAG))
#define GST_IS_KATE_TAG_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_KATE_TAG))
typedef struct _GstKateTag GstKateTag;
typedef struct _GstKateTagClass GstKateTagClass;
/**
* GstKateTag:
*
* Opaque data structure.
*/
struct _GstKateTag
{
GstKateParse parse;
gchar *language;
gchar *category;
gint original_canvas_width;
gint original_canvas_height;
};
struct _GstKateTagClass
{
GstKateParseClass parent_class;
};
GType gst_kate_tag_get_type (void);
G_END_DECLS
#endif /* __GST_KATE_TAG_H__ */

File diff suppressed because it is too large Load diff

View file

@ -1,118 +0,0 @@
/*
* GStreamer
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Alternatively, the contents of this file may be used under the
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
* which case the following provisions apply instead of the ones
* mentioned above:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_KATE_TIGER_H__
#define __GST_KATE_TIGER_H__
#include <kate/kate.h>
#include <tiger/tiger.h>
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/video-overlay-composition.h>
#include "gstkateutil.h"
G_BEGIN_DECLS
/* #defines don't like whitespacey bits */
#define GST_TYPE_KATE_TIGER \
(gst_kate_tiger_get_type())
#define GST_KATE_TIGER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_KATE_TIGER,GstKateTiger))
#define GST_KATE_TIGER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_KATE,GstKateTigerClass))
#define GST_IS_KATE_TIGER(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_KATE_TIGER))
#define GST_IS_KATE_TIGER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_KATE_TIGER))
typedef struct _GstKateTiger GstKateTiger;
typedef struct _GstKateTigerClass GstKateTigerClass;
struct _GstKateTiger
{
GstKateDecoderBase decoder;
GstPad *katesinkpad;
GstPad *videosinkpad;
GstPad *srcpad;
tiger_renderer *tr;
gdouble quality;
gchar *default_font_desc;
gboolean default_font_effect;
gdouble default_font_effect_strength;
guchar default_font_r;
guchar default_font_g;
guchar default_font_b;
guchar default_font_a;
guchar default_background_r;
guchar default_background_g;
guchar default_background_b;
guchar default_background_a;
gboolean silent;
GstVideoFormat video_format;
gint video_width;
gint video_height;
gboolean swap_rgb;
GstBuffer *render_buffer;
GstVideoOverlayComposition *composition;
GMutex *mutex;
GCond *cond;
GstSegment video_segment;
gboolean video_flushing;
gboolean seen_header;
};
struct _GstKateTigerClass
{
GstElementClass parent_class;
};
GType gst_kate_tiger_get_type (void);
G_END_DECLS
#endif /* __GST_KATE_TIGER_H__ */

View file

@ -1,702 +0,0 @@
/* GStreamer
* Copyright (C) 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* FIXME: shouldn't all this GstKateDecoderBase stuff really be a base class? */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#ifdef HAVE_TIGER
#include <tiger/tiger.h>
#endif
#include <gst/tag/tag.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)
{
GstStructure *structure;
GValue array = { 0 };
GST_LOG_OBJECT (element, "caps: %" GST_PTR_FORMAT, caps);
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);
g_value_init (&value, GST_TYPE_BUFFER);
buffer = gst_buffer_copy (buffer);
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER);
gst_value_take_buffer (&value, buffer);
gst_value_array_append_value (&array, &value);
g_value_unset (&value);
headers = headers->next;
}
gst_structure_take_value (structure, "streamheader", &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_PARAM_STATIC_STRINGS));
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_PARAM_STATIC_STRINGS));
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_PARAM_STATIC_STRINGS));
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 | G_PARAM_STATIC_STRINGS));
}
void
gst_kate_util_decode_base_init (GstKateDecoderBase * decoder,
gboolean delay_events)
{
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->tags_changed = FALSE;
decoder->initialized = FALSE;
decoder->delay_events = delay_events;
decoder->event_queue = NULL;
}
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_unref (decoder->tags);
decoder->tags = NULL;
}
decoder->tags_changed = FALSE;
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 *, GstObject *, GstEvent *),
GstObject * parent, 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;
case GST_EVENT_SEGMENT:
gst_kate_util_decoder_base_segment_event (decoder, event);
can_be_queued = TRUE;
break;
default:
can_be_queued = TRUE;
break;
}
if (GST_EVENT_IS_STICKY (event) && GST_EVENT_TYPE (event) < GST_EVENT_CAPS)
can_be_queued = FALSE;
if (decoder->delay_events && can_be_queued) {
GstKateDecoderBaseQueuedEvent *item;
GST_DEBUG_OBJECT (decoder, "We have to delay the event");
item = g_new (GstKateDecoderBaseQueuedEvent, 1);
if (item) {
item->event = event;
item->parent = parent;
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);
gst_event_unref (item->event);
g_free (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->parent, item->event);
g_free (item);
}
}
void
gst_kate_util_decoder_base_add_tags (GstKateDecoderBase * decoder,
GstTagList * tags, gboolean take_ownership_of_tags)
{
if (!decoder->tags) {
if (!take_ownership_of_tags)
tags = gst_tag_list_ref (tags);
decoder->tags = tags;
} else {
GstTagList *old = decoder->tags;
decoder->tags = gst_tag_list_merge (old, tags, GST_TAG_MERGE_REPLACE);
gst_tag_list_unref (old);
if (take_ownership_of_tags)
gst_tag_list_unref (tags);
}
decoder->tags_changed = TRUE;
}
GstEvent *
gst_kate_util_decoder_base_get_tag_event (GstKateDecoderBase * decoder)
{
if (!decoder->tags)
return NULL;
decoder->tags_changed = FALSE;
return gst_event_new_tag (gst_tag_list_ref (decoder->tags));
}
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;
}
static inline gboolean
gst_kate_util_is_utf8_string (const char *value, size_t len)
{
if (len == 0)
return FALSE;
if (memchr (value, 0, len - 1))
return FALSE;
if (value[len - 1])
return FALSE;
return (kate_text_validate (kate_utf8, value, len) >= 0);
}
GstFlowReturn
gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder,
GstElement * element, GstPad * pad, GstBuffer * buf, GstPad * srcpad,
GstPad * tagpad, GstCaps ** src_caps, const kate_event ** ev)
{
kate_packet kp;
int ret;
GstFlowReturn rflow = GST_FLOW_OK;
gboolean is_header;
GstMapInfo info;
gsize header_size;
guint8 header[1];
header_size = gst_buffer_extract (buf, 0, header, 1);
GST_DEBUG_OBJECT (element,
"got kate packet, %" G_GSIZE_FORMAT " bytes, type %02x",
gst_buffer_get_size (buf), header_size == 0 ? -1 : header[0]);
is_header = header_size > 0 && (header[0] & 0x80);
if (!is_header && decoder->tags_changed) {
/* 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_pad_push_event (tagpad,
gst_kate_util_decoder_base_get_tag_event (decoder));
}
if (gst_buffer_map (buf, &info, GST_MAP_READ)) {
kate_packet_wrap (&kp, info.size, info.data);
ret = kate_high_decode_packetin (&decoder->k, &kp, ev);
gst_buffer_unmap (buf, &info);
} else {
GST_ELEMENT_ERROR (element, STREAM, DECODE, (NULL),
("Failed to map buffer"));
return GST_FLOW_ERROR;
}
if (G_UNLIKELY (ret < 0)) {
GST_ELEMENT_ERROR (element, STREAM, DECODE, (NULL),
("Failed to decode Kate packet: %s",
gst_kate_util_get_error_message (ret)));
return GST_FLOW_ERROR;
}
if (G_UNLIKELY (ret > 0)) {
GST_DEBUG_OBJECT (element,
"kate_high_decode_packetin has received EOS packet");
}
/* headers may be interesting to retrieve information from */
if (G_UNLIKELY (is_header)) {
switch (header[0]) {
case 0x80: /* ID header */
GST_INFO_OBJECT (element, "Parsed ID header: language %s, category %s",
decoder->k.ki->language, decoder->k.ki->category);
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_empty_simple ("subpicture/x-dvd");
} else if (decoder->k.ki->text_markup_type == kate_markup_none) {
*src_caps = gst_caps_new_simple ("text/x-raw", "format",
G_TYPE_STRING, "utf8", NULL);
} else {
*src_caps = gst_caps_new_simple ("text/x-raw", "format",
G_TYPE_STRING, "pango-markup", NULL);
}
GST_INFO_OBJECT (srcpad, "Setting caps: %" GST_PTR_FORMAT, *src_caps);
if (!gst_pad_set_caps (srcpad, *src_caps)) {
GST_ERROR_OBJECT (srcpad, "Failed to set caps %" GST_PTR_FORMAT,
*src_caps);
}
}
if (decoder->k.ki->language && *decoder->k.ki->language) {
GstTagList *tags = gst_tag_list_new_empty ();
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,
lang_code, NULL);
g_free (lang_code);
/* TODO: category - where should it go ? */
gst_kate_util_decoder_base_add_tags (decoder, tags, TRUE);
}
/* update properties */
g_free (decoder->language);
decoder->language = g_strdup (decoder->k.ki->language);
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;
/* 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 */
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) {
GST_ERROR_OBJECT (element, "failed to decode comment header");
list = gst_tag_list_new_empty ();
}
if (encoder) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_ENCODER, encoder, NULL);
g_free (encoder);
}
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_SUBTITLE_CODEC, "Kate", NULL);
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_ENCODER_VERSION, decoder->k.ki->bitstream_version_major,
NULL);
gst_kate_util_decoder_base_add_tags (decoder, list, TRUE);
if (decoder->initialized) {
gst_pad_push_event (tagpad,
gst_event_new_tag (gst_tag_list_ref (decoder->tags)));
}
}
break;
default:
break;
}
}
#if ((KATE_VERSION_MAJOR<<16)|(KATE_VERSION_MINOR<<8)|KATE_VERSION_PATCH) >= 0x000400
else if (*ev && (*ev)->meta) {
int count = kate_meta_query_count ((*ev)->meta);
if (count > 0) {
GstTagList *evtags = gst_tag_list_new_empty ();
int idx;
GST_DEBUG_OBJECT (decoder, "Kate event has %d attached metadata", count);
for (idx = 0; idx < count; ++idx) {
const char *tag, *value;
size_t len;
if (kate_meta_query ((*ev)->meta, idx, &tag, &value, &len) < 0) {
GST_WARNING_OBJECT (decoder, "Failed to retrieve metadata %d", idx);
} else {
if (gst_kate_util_is_utf8_string (value, len)) {
gchar *compound = g_strdup_printf ("%s=%s", tag, value);
GST_DEBUG_OBJECT (decoder,
"Metadata %d: %s=%s (%" G_GSIZE_FORMAT " bytes)", idx, tag,
value, len);
gst_tag_list_add (evtags, GST_TAG_MERGE_APPEND,
GST_TAG_EXTENDED_COMMENT, compound, NULL);
g_free (compound);
} else {
GST_INFO_OBJECT (decoder,
"Metadata %d, (%s, %" G_GSIZE_FORMAT
" bytes) is binary, ignored", idx, tag, len);
}
}
}
gst_kate_util_decoder_base_add_tags (decoder, evtags, TRUE);
gst_pad_push_event (tagpad,
gst_kate_util_decoder_base_get_tag_event (decoder));
}
}
#endif
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: %s",
gst_kate_util_get_error_message (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;
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;
}
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:
gst_kate_util_decode_base_reset (decoder);
break;
default:
break;
}
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_segment_event (GstKateDecoderBase * decoder,
GstEvent * event)
{
GstSegment seg;
gst_event_copy_segment (event, &seg);
GST_DEBUG_OBJECT (decoder, "kate pad segment: %" GST_SEGMENT_FORMAT, &seg);
decoder->kate_segment = seg;
}
gboolean
gst_kate_util_decoder_base_update_segment (GstKateDecoderBase * decoder,
GstElement * element, GstBuffer * buf)
{
guint64 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)) {
decoder->kate_segment.position = 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)
{
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, GstObject * parent, 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, parent, query);
}
gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
return TRUE;
}
default:
return gst_pad_query_default (pad, parent, query);
}
}
const char *
gst_kate_util_get_error_message (int ret)
{
switch (ret) {
case KATE_E_NOT_FOUND:
return "value not found";
case KATE_E_INVALID_PARAMETER:
return "invalid parameter";
case KATE_E_OUT_OF_MEMORY:
return "out of memory";
case KATE_E_BAD_GRANULE:
return "bad granule";
case KATE_E_INIT:
return "initialization error";
case KATE_E_BAD_PACKET:
return "bad packet";
case KATE_E_TEXT:
return "invalid/truncated text";
case KATE_E_LIMIT:
return "a limit was exceeded";
case KATE_E_VERSION:
return "unsupported bitstream version";
case KATE_E_NOT_KATE:
return "not a kate bitstream";
case KATE_E_BAD_TAG:
return "bad tag";
case KATE_E_IMPL:
return "not implemented";
#ifdef HAVE_TIGER
case TIGER_E_NOT_FOUND:
return "value not found";
case TIGER_E_INVALID_PARAMETER:
return "invalid parameter";
case TIGER_E_OUT_OF_MEMORY:
return "out of memory";
case TIGER_E_CAIRO_ERROR:
return "Cairo error";
case TIGER_E_BAD_SURFACE_TYPE:
return "bad surface type";
#endif
default:
return "unknown error";
}
}

View file

@ -1,123 +0,0 @@
/* -*- c-basic-offset: 2 -*-
* GStreamer
* Copyright (C) <2008> Vincent Penquerc'h <ogg.k.ogg.k at googlemail dot com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_KATE_UTIL_H__
#define __GST_KATE_UTIL_H__
#include <kate/kate.h>
#include <gst/gst.h>
G_BEGIN_DECLS
typedef enum {
GST_KATE_FORMAT_UNDEFINED,
GST_KATE_FORMAT_SPU,
GST_KATE_FORMAT_TEXT_UTF8,
GST_KATE_FORMAT_TEXT_PANGO_MARKUP
} GstKateFormat;
enum
{
ARG_DEC_BASE_0,
ARG_DEC_BASE_LANGUAGE,
ARG_DEC_BASE_CATEGORY,
ARG_DEC_BASE_ORIGINAL_CANVAS_WIDTH,
ARG_DEC_BASE_ORIGINAL_CANVAS_HEIGHT,
DECODER_BASE_ARG_COUNT
};
typedef struct
{
GstEvent * event;
gboolean (*handler)(GstPad *, GstObject*, GstEvent *);
GstObject * parent;
GstPad *pad;
} GstKateDecoderBaseQueuedEvent;
typedef struct
{
GstElement element;
kate_state k;
gboolean initialized;
GstTagList *tags;
gboolean tags_changed;
gchar *language;
gchar *category;
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,
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 *
decoder, GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec);
extern GstFlowReturn
gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder,
GstElement * element, GstPad * pad, GstBuffer * buffer, GstPad * srcpad,
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_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,
GstStateChange transition);
extern gboolean gst_kate_decoder_base_convert (GstKateDecoderBase * decoder,
GstElement * element, GstPad * pad, GstFormat src_fmt, gint64 src_val,
GstFormat * dest_fmt, gint64 * dest_val);
extern gboolean gst_kate_decoder_base_sink_query (GstKateDecoderBase * decoder,
GstElement * element, GstPad * pad, GstObject * parent, GstQuery * query);
extern gboolean
gst_kate_util_decoder_base_queue_event (GstKateDecoderBase * decoder,
GstEvent * event, gboolean (*handler)(GstPad *, GstObject *, GstEvent *),
GstObject * parent, GstPad * pad);
extern void
gst_kate_util_decoder_base_add_tags (GstKateDecoderBase * decoder,
GstTagList * tags, gboolean take_ownership_of_tags);
extern GstEvent *
gst_kate_util_decoder_base_get_tag_event (GstKateDecoderBase * decoder);
extern const char *
gst_kate_util_get_error_message (int ret);
G_END_DECLS
#endif /* __GST_KATE_UTIL_H__ */

View file

@ -1,26 +0,0 @@
kate_sources = [
'gstkate.c',
'gstkateelement.c',
'gstkatedec.c',
'gstkateenc.c',
'gstkateparse.c',
'gstkatetag.c',
'gstkateutil.c',
'gstkatespu.c',
]
kate_dep = dependency('kate', version : '>=0.1.7', required : get_option('kate'))
# Not used
#tiger_dep = dependency('tiger', version : '>=0.3.2', required : get_option('kate'))
if kate_dep.found()
gstkate = library('gstkate',
kate_sources,
c_args : gst_plugins_bad_args,
link_args : noseh_link_args,
include_directories : [configinc, libsinc],
dependencies : [gstbase_dep, gstvideo_dep, gsttag_dep, kate_dep],
install : true,
install_dir : plugins_install_dir)
plugins += [gstkate]
endif

View file

@ -26,7 +26,6 @@ subdir('gtk')
subdir('hls') subdir('hls')
subdir('iqa') subdir('iqa')
subdir('isac') subdir('isac')
subdir('kate')
subdir('ladspa') subdir('ladspa')
subdir('lc3') subdir('lc3')
subdir('ldac') subdir('ldac')

View file

@ -26,7 +26,7 @@
* *
* ## Example launch line * ## Example launch line
* |[ * |[
* gst-launch-1.0 -v intersubsrc ! kateenc ! oggmux ! filesink location=out.ogv * gst-launch-1.0 -v intersubsrc ! srtenc ! filesink location=subtitles.srt
* ]| * ]|
* *
* The intersubsrc element cannot be used effectively with gst-launch-1.0, * The intersubsrc element cannot be used effectively with gst-launch-1.0,

View file

@ -126,7 +126,6 @@ option('gsm', type : 'feature', value : 'auto', description : 'GSM encoder/decod
option('gtk3', type : 'feature', value : 'auto', description : 'GTK+ video sink plugin') option('gtk3', type : 'feature', value : 'auto', description : 'GTK+ video sink plugin')
option('ipcpipeline', type : 'feature', value : 'auto', description : 'Inter-process communication plugin') option('ipcpipeline', type : 'feature', value : 'auto', description : 'Inter-process communication plugin')
option('iqa', type : 'feature', value : 'auto', description : 'Image quality assessment plugin (AGPL - only built if gpl option is also enabled!)') option('iqa', type : 'feature', value : 'auto', description : 'Image quality assessment plugin (AGPL - only built if gpl option is also enabled!)')
option('kate', type : 'feature', value : 'auto', description : 'Kate subtitle parser, tagger, and codec plugin')
option('kms', type : 'feature', value : 'auto', description : 'KMS video sink plugin') option('kms', type : 'feature', value : 'auto', description : 'KMS video sink plugin')
option('ladspa', type : 'feature', value : 'auto', description : 'LADSPA plugin bridge') option('ladspa', type : 'feature', value : 'auto', description : 'LADSPA plugin bridge')
option('lc3', type : 'feature', value : 'auto', description : 'LC3 (Bluetooth) LE audio codec plugin') option('lc3', type : 'feature', value : 'auto', description : 'LC3 (Bluetooth) LE audio codec plugin')

View file

@ -1,881 +0,0 @@
/* GStreamer
*
* unit test for kate
*
* Copyright (C) <2007> Stefan Kost <ensonic@users.sf.net>
* Copyright (C) <2008> ogg.k.ogg.k <ogg.k.ogg.k@googlemail.com>
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <unistd.h>
#include <gst/check/gstcheck.h>
#include <gst/base/gsttypefindhelper.h>
static const guint8 kate_header_0x80[64] = {
0x80, 0x6b, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x00, 0x20, /* .kate...... ... */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* ................ */
0x65, 0x6e, 0x5f, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* en_GB........... */
0x6e, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* none............ */
};
static const guint8 kate_header_0x81[53] = {
0x81, 0x6b, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x6c, 0x69, 0x62, /* .kate........lib */
0x6b, 0x61, 0x74, 0x65, 0x20, 0x30, 0x2e, 0x31, 0x2e, 0x30, 0x20, 0x28, 0x54, 0x69, 0x67, 0x65, /* kate 0.1.0 (Tige */
0x72, 0x29, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x54, 0x49, 0x54, 0x4c, 0x45, 0x3d, /* r)........TITLE= */
0x54, 0x69, 0x67, 0x65, 0x72, /* Tiger */
};
static const guint8 kate_header_0x8x[10] = {
0x80, 0x6b, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00
};
static const guint8 kate_header_0x88[11] = {
0x88, 0x6b, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const guint8 kate_header_0x00[45] = {
0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, /* ................ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x70, 0x6c, 0x61, /* .............pla */
0x69, 0x6e, 0x20, 0x6f, 0x6c, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x08, 0x00 /* in old text.. */
};
static const guint8 kate_header_0x7f[1] = {
0x7f
};
static const unsigned char kate_spu[] = {
0x00, 0x1b, /* size */
0x00, 0x06, /* commands at offset 6 */
0x45, /* first line data - 2 pixels of colors 0 and 1 */
0x76, /* first line data - 2 pixels of colors 3 and 2 */
0x00, 0x00, /* timestamp */
0x00, 0x06, /* link to next command sequence - points back to the current one to mark no more */
0x06, 0x00, 0x04, 0x00, 0x05, /* pointers to data */
0x05, 0x00, 0x30, 0x04, 0x00, 0x10, 0x02, /* area: 3x1 -> 4x2 */
0x04, 0x0f, 0xff, /* alpha: color 0 transparent, all others opaque */
0x01, /* show */
0xff /* end */
};
/* A lot of these taken from the vorbisdec test */
/* For ease of programming we use globals to keep refs for our floating
* src and sink pads we create; otherwise we always have to do get_pad,
* get_peer, and then remove references in every test function */
static GstPad *mydecsrcpad, *mydecsinkpad;
static GstPad *myencsrcpad, *myencsinkpad;
static GstPad *myparsesrcpad, *myparsesinkpad;
static GstPad *mytagsrcpad, *mytagsinkpad;
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GList *events = NULL;
static gboolean
collect_events_func (GstPad * pad, GstObject * parent, GstEvent * event)
{
GST_LOG ("event: %" GST_PTR_FORMAT, event);
events = g_list_append (events, gst_event_ref (event));
return gst_pad_event_default (pad, parent, event);
}
static GstElement *
setup_katedec (void)
{
GstElement *katedec;
GST_DEBUG ("setup_katedec");
katedec = gst_check_setup_element ("katedec");
mydecsrcpad = gst_check_setup_src_pad (katedec, &srctemplate);
mydecsinkpad = gst_check_setup_sink_pad (katedec, &sinktemplate);
gst_pad_set_event_function (mydecsinkpad, collect_events_func);
gst_pad_set_active (mydecsrcpad, TRUE);
gst_pad_set_active (mydecsinkpad, TRUE);
return katedec;
}
static void
cleanup_katedec (GstElement * katedec)
{
GList *l;
GST_DEBUG ("cleanup_katedec");
gst_element_set_state (katedec, GST_STATE_NULL);
for (l = events; l != NULL; l = l->next)
gst_event_unref (GST_EVENT (l->data));
g_list_free (events);
events = NULL;
gst_pad_set_active (mydecsrcpad, FALSE);
gst_pad_set_active (mydecsinkpad, FALSE);
gst_check_teardown_src_pad (katedec);
gst_check_teardown_sink_pad (katedec);
gst_check_teardown_element (katedec);
}
static GstElement *
setup_kateenc (void)
{
GstElement *kateenc;
GST_DEBUG ("setup_kateenc");
kateenc = gst_check_setup_element ("kateenc");
myencsrcpad = gst_check_setup_src_pad (kateenc, &srctemplate);
myencsinkpad = gst_check_setup_sink_pad (kateenc, &sinktemplate);
gst_pad_set_active (myencsrcpad, TRUE);
gst_pad_set_active (myencsinkpad, TRUE);
return kateenc;
}
static void
cleanup_kateenc (GstElement * kateenc)
{
GST_DEBUG ("cleanup_kateenc");
gst_element_set_state (kateenc, GST_STATE_NULL);
gst_pad_set_active (myencsrcpad, FALSE);
gst_pad_set_active (myencsinkpad, FALSE);
gst_check_teardown_src_pad (kateenc);
gst_check_teardown_sink_pad (kateenc);
gst_check_teardown_element (kateenc);
}
static GstElement *
setup_kateparse (void)
{
GstElement *kateparse;
GST_DEBUG ("setup_kateparse");
kateparse = gst_check_setup_element ("kateparse");
myparsesrcpad = gst_check_setup_src_pad (kateparse, &srctemplate);
myparsesinkpad = gst_check_setup_sink_pad (kateparse, &sinktemplate);
gst_pad_set_active (myparsesrcpad, TRUE);
gst_pad_set_active (myparsesinkpad, TRUE);
return kateparse;
}
static void
cleanup_kateparse (GstElement * kateparse)
{
GST_DEBUG ("cleanup_kateparse");
gst_element_set_state (kateparse, GST_STATE_NULL);
gst_pad_set_active (myparsesrcpad, FALSE);
gst_pad_set_active (myparsesinkpad, FALSE);
gst_check_teardown_src_pad (kateparse);
gst_check_teardown_sink_pad (kateparse);
gst_check_teardown_element (kateparse);
}
static GstElement *
setup_katetag (void)
{
GstElement *katetag;
GST_DEBUG ("setup_katetag");
katetag = gst_check_setup_element ("katetag");
mytagsrcpad = gst_check_setup_src_pad (katetag, &srctemplate);
mytagsinkpad = gst_check_setup_sink_pad (katetag, &sinktemplate);
gst_pad_set_active (mytagsrcpad, TRUE);
gst_pad_set_active (mytagsinkpad, TRUE);
return katetag;
}
static void
cleanup_katetag (GstElement * katetag)
{
GST_DEBUG ("cleanup_katetag");
gst_element_set_state (katetag, GST_STATE_NULL);
gst_pad_set_active (mytagsrcpad, FALSE);
gst_pad_set_active (mytagsinkpad, FALSE);
gst_check_teardown_src_pad (katetag);
gst_check_teardown_sink_pad (katetag);
gst_check_teardown_element (katetag);
}
static void
check_buffers (guint expected, gboolean headers_in_caps)
{
GstBuffer *outbuffer;
GstMapInfo info;
guint i, num_buffers;
const int num_headers = 9;
unsigned char packet_type;
/* check buffers are the type we expect */
num_buffers = g_list_length (buffers);
fail_unless (num_buffers >= num_headers + expected); /* at least 9 headers, plus a variable number of data packets */
for (i = 0; i < num_buffers; ++i) {
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
fail_if (gst_buffer_get_size (outbuffer) == 0);
assert_equals_int (gst_buffer_map (outbuffer, &info, GST_MAP_READ), TRUE);
if (i < num_headers) {
/* different headers packets */
packet_type = (0x80 | i);
fail_unless (info.data[0] == packet_type);
/* headers could be in caps, so would have an extra ref */
} else if (i == num_buffers - 1) {
/* eos data packet */
packet_type = 0x7f;
fail_unless (info.data[0] == packet_type);
} else {
/* data packet */
packet_type = 0;
fail_unless (info.data[0] >= 0 && info.data[0] < 0x7f);
}
gst_buffer_unmap (outbuffer, &info);
buffers = g_list_remove (buffers, outbuffer);
ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
gst_buffer_unref (outbuffer);
outbuffer = NULL;
}
}
GST_START_TEST (test_kate_typefind)
{
GstTypeFindProbability prob;
const gchar *type;
GstBuffer *buf;
GstCaps *caps = NULL;
buf = gst_buffer_new_memdup (kate_header_0x80, sizeof (kate_header_0x80));
GST_BUFFER_OFFSET (buf) = 0;
caps = gst_type_find_helper_for_buffer (NULL, buf, &prob);
fail_unless (caps != NULL);
GST_LOG ("Found type: %" GST_PTR_FORMAT, caps);
type = gst_structure_get_name (gst_caps_get_structure (caps, 0));
fail_unless_equals_string (type, "application/x-kate");
fail_unless (prob > GST_TYPE_FIND_MINIMUM && prob <= GST_TYPE_FIND_MAXIMUM);
gst_buffer_unref (buf);
gst_caps_unref (caps);
}
GST_END_TEST;
GST_START_TEST (test_kate_empty_identification_header)
{
GstElement *katedec;
GstBuffer *inbuffer;
GstBus *bus;
GstCaps *caps;
katedec = setup_katedec ();
bus = gst_bus_new ();
fail_unless (gst_element_set_state (katedec,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
caps = gst_caps_new_empty_simple ("subtitle/x-kate");
gst_check_setup_events (mydecsrcpad, katedec, caps, GST_FORMAT_TIME);
gst_caps_unref (caps);
inbuffer = gst_buffer_new_and_alloc (0);
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
/* set a bus here so we avoid getting state change messages */
gst_element_set_bus (katedec, bus);
fail_unless_equals_int (gst_pad_push (mydecsrcpad, inbuffer), GST_FLOW_ERROR);
/* ... but it ends up being collected on the global buffer list */
fail_unless_equals_int (g_list_length (buffers), 0);
gst_element_set_bus (katedec, NULL);
/* cleanup */
gst_object_unref (GST_OBJECT (bus));
cleanup_katedec (katedec);
}
GST_END_TEST;
/* FIXME: also tests comment header */
GST_START_TEST (test_kate_identification_header)
{
GstElement *katedec;
GstBuffer *inbuffer;
GstBus *bus;
GstCaps *caps;
katedec = setup_katedec ();
fail_unless (gst_element_set_state (katedec,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
bus = gst_bus_new ();
caps = gst_caps_new_empty_simple ("subtitle/x-kate");
gst_check_setup_events (mydecsrcpad, katedec, caps, GST_FORMAT_TIME);
gst_caps_unref (caps);
inbuffer =
gst_buffer_new_memdup (kate_header_0x80, sizeof (kate_header_0x80));
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
gst_buffer_ref (inbuffer);
gst_element_set_bus (katedec, bus);
/* pushing gives away my reference ... */
fail_unless (gst_pad_push (mydecsrcpad, inbuffer) == GST_FLOW_OK);
/* ... and nothing ends up on the global buffer list */
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
gst_buffer_unref (inbuffer);
fail_unless (g_list_length (buffers) == 0);
inbuffer =
gst_buffer_new_memdup (kate_header_0x81, sizeof (kate_header_0x81));
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
gst_buffer_ref (inbuffer);
/* pushing gives away my reference ... */
fail_unless (gst_pad_push (mydecsrcpad, inbuffer) == GST_FLOW_OK);
/* ... and nothing ends up on the global buffer list */
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
gst_buffer_unref (inbuffer);
fail_unless (g_list_length (buffers) == 0);
/* there should've been a tag event */
{
gboolean found_tags = FALSE;
GList *l;
for (l = events; l != NULL; l = l->next) {
GstEvent *event = GST_EVENT (l->data);
if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
GstTagList *tags = NULL;
gchar *language;
gchar *title;
found_tags = TRUE;
gst_event_parse_tag (event, &tags);
fail_unless (tags != NULL);
fail_unless (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM);
fail_unless_equals_int (gst_tag_list_get_tag_size (tags,
GST_TAG_LANGUAGE_CODE), 1);
fail_unless (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE,
&language));
fail_unless_equals_string (language, "en");
g_free (language);
fail_unless_equals_int (gst_tag_list_get_tag_size (tags, "title"), 1);
fail_unless (gst_tag_list_get_string (tags, GST_TAG_TITLE, &title));
fail_unless_equals_string (title, "Tiger");
g_free (title);
}
}
fail_unless (found_tags);
}
/* cleanup */
gst_bus_set_flushing (bus, TRUE);
gst_element_set_bus (katedec, NULL);
gst_object_unref (GST_OBJECT (bus));
cleanup_katedec (katedec);
}
GST_END_TEST;
GST_START_TEST (test_kate_encode_nothing)
{
GstElement *kateenc;
kateenc = setup_kateenc ();
fail_unless (gst_element_set_state (kateenc,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_eos ()) == TRUE);
fail_unless (gst_element_set_state (kateenc,
GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
"could not set to ready");
/* cleanup */
cleanup_kateenc (kateenc);
}
GST_END_TEST;
GST_START_TEST (test_kate_encode_empty)
{
GstElement *kateenc;
GstBuffer *inbuffer;
GstBus *bus;
GstCaps *caps;
kateenc = setup_kateenc ();
fail_unless (gst_element_set_state (kateenc,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
bus = gst_bus_new ();
inbuffer = gst_buffer_new_and_alloc (0);
GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) =
1 * GST_SECOND;
GST_BUFFER_DURATION (inbuffer) = 5 * GST_SECOND;
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
caps = gst_caps_from_string ("text/x-raw, format=utf8");
fail_unless (caps != NULL);
gst_check_setup_events (myencsrcpad, kateenc, caps, GST_FORMAT_TIME);
gst_caps_unref (caps);
gst_element_set_bus (kateenc, bus);
/* pushing gives away my reference ... */
fail_unless (gst_pad_push (myencsrcpad, inbuffer) == GST_FLOW_ERROR);
fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_eos ()) == TRUE);
fail_unless (gst_element_set_state (kateenc,
GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
"could not set to ready");
gst_element_set_bus (kateenc, NULL);
/* cleanup */
gst_object_unref (GST_OBJECT (bus));
cleanup_kateenc (kateenc);
}
GST_END_TEST;
GST_START_TEST (test_kate_encode_simple)
{
GstElement *kateenc;
GstBuffer *inbuffer;
GstBus *bus;
const gchar *test_string = "";
GstCaps *caps;
kateenc = setup_kateenc ();
g_object_set (kateenc, "category", "subtitles", NULL);
fail_unless (gst_element_set_state (kateenc,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
bus = gst_bus_new ();
inbuffer = gst_buffer_new_memdup (test_string, strlen (test_string) + 1);
GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) =
1 * GST_SECOND;
GST_BUFFER_DURATION (inbuffer) = 5 * GST_SECOND;
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
caps = gst_caps_from_string ("text/x-raw, format=utf8");
fail_unless (caps != NULL);
gst_check_setup_events (myencsrcpad, kateenc, caps, GST_FORMAT_TIME);
gst_caps_unref (caps);
gst_buffer_ref (inbuffer);
gst_element_set_bus (kateenc, bus);
/* pushing gives away my reference ... */
fail_unless_equals_int (gst_pad_push (myencsrcpad, inbuffer), GST_FLOW_OK);
/* ... and nothing ends up on the global buffer list */
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
gst_buffer_unref (inbuffer);
fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_eos ()) == TRUE);
fail_unless (gst_element_set_state (kateenc,
GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
"could not set to ready");
/* at least one data packet and one EOS packet should have been emitted */
check_buffers (1 + 1, FALSE);
/* cleanup */
gst_bus_set_flushing (bus, TRUE);
gst_element_set_bus (kateenc, NULL);
gst_object_unref (GST_OBJECT (bus));
cleanup_kateenc (kateenc);
gst_check_drop_buffers ();
}
GST_END_TEST;
GST_START_TEST (test_kate_encode_spu)
{
GstElement *kateenc;
GstBuffer *inbuffer;
GstBus *bus;
GstCaps *caps;
kateenc = setup_kateenc ();
g_object_set (kateenc, "category", "spu-subtitles", NULL);
fail_unless (gst_element_set_state (kateenc,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
bus = gst_bus_new ();
inbuffer = gst_buffer_new_memdup (kate_spu, sizeof (kate_spu));
GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) =
1 * GST_SECOND;
GST_BUFFER_DURATION (inbuffer) = 5 * GST_SECOND;
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
caps = gst_caps_from_string ("subpicture/x-dvd");
fail_unless (caps != NULL);
gst_check_setup_events (myencsrcpad, kateenc, caps, GST_FORMAT_TIME);
gst_caps_unref (caps);
gst_buffer_ref (inbuffer);
gst_element_set_bus (kateenc, bus);
/* pushing gives away my reference ... */
fail_unless_equals_int (gst_pad_push (myencsrcpad, inbuffer), GST_FLOW_OK);
/* ... and nothing ends up on the global buffer list */
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
gst_buffer_unref (inbuffer);
fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_eos ()) == TRUE);
fail_unless (gst_element_set_state (kateenc,
GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
"could not set to ready");
/* at least one data packet and one EOS packet should have been emitted */
check_buffers (2, FALSE);
/* cleanup */
gst_bus_set_flushing (bus, TRUE);
gst_element_set_bus (kateenc, NULL);
gst_object_unref (GST_OBJECT (bus));
cleanup_kateenc (kateenc);
gst_check_drop_buffers ();
}
GST_END_TEST;
GST_START_TEST (test_kate_encode_keepalives)
{
GstElement *kateenc;
GstBus *bus;
guint i, round;
GstSegment segment;
enum
{ n_keepalives = 1000 };
static const struct
{
gdouble keepalive_min_time;
gint packets;
} cfg[3] = {
{
0.5, n_keepalives}, {
2.0, n_keepalives / 2}, {
5.0, n_keepalives / 5},
};
for (round = 0; round < 3; ++round) {
kateenc = setup_kateenc ();
/* doesn't matter here, since we never send a packet */
g_object_set (kateenc, "category", "subtitles", NULL);
fail_unless (gst_element_set_state (kateenc,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
bus = gst_bus_new ();
gst_element_set_bus (kateenc, bus);
g_object_set (kateenc, "keepalive-min-time", cfg[round].keepalive_min_time,
NULL);
gst_pad_push_event (myencsrcpad, gst_event_new_stream_start ("test"));
/* the second one here should not emit a keepalive since the time since last packet
is less than the keepalive delay */
for (i = 1; i <= n_keepalives; ++i) {
gint64 t = i * GST_SECOND;
gst_segment_init (&segment, GST_FORMAT_TIME);
segment.start = t;
segment.position = 0;
fail_unless (gst_pad_push_event (myencsrcpad,
gst_event_new_segment (&segment)) == TRUE);
}
fail_unless (gst_pad_push_event (myencsrcpad,
gst_event_new_eos ()) == TRUE);
fail_unless (gst_element_set_state (kateenc,
GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
"could not set to ready");
/* at least a number data packet and an EOS packet should have been emitted */
check_buffers (cfg[round].packets + 1, FALSE);
/* cleanup */
gst_bus_set_flushing (bus, TRUE);
gst_element_set_bus (kateenc, NULL);
gst_object_unref (GST_OBJECT (bus));
cleanup_kateenc (kateenc);
gst_check_drop_buffers ();
}
}
GST_END_TEST;
static void
test_kate_send_headers (GstElement * element, GstPad * pad)
{
GstBuffer *inbuffer;
GstCaps *caps;
GstMapInfo info;
int i;
caps = gst_caps_new_simple ("subtitle/x-kate", NULL, NULL);
gst_check_setup_events (pad, element, caps, GST_FORMAT_TIME);
gst_caps_unref (caps);
/* push headers */
inbuffer =
gst_buffer_new_memdup (kate_header_0x80, sizeof (kate_header_0x80));
GST_BUFFER_OFFSET (inbuffer) = GST_BUFFER_OFFSET_END (inbuffer) = 0;
fail_unless_equals_int (gst_pad_push (pad, inbuffer), GST_FLOW_OK);
inbuffer =
gst_buffer_new_memdup (kate_header_0x81, sizeof (kate_header_0x81));
GST_BUFFER_OFFSET (inbuffer) = GST_BUFFER_OFFSET_END (inbuffer) = 0;
fail_unless_equals_int (gst_pad_push (pad, inbuffer), GST_FLOW_OK);
for (i = 2; i < 8; ++i) {
inbuffer =
gst_buffer_new_memdup (kate_header_0x8x, sizeof (kate_header_0x8x));
fail_if (gst_buffer_map (inbuffer, &info, GST_MAP_WRITE) != TRUE);
info.data[0] = 0x80 | i;
gst_buffer_unmap (inbuffer, &info);
GST_BUFFER_OFFSET (inbuffer) = GST_BUFFER_OFFSET_END (inbuffer) = 0;
fail_unless_equals_int (gst_pad_push (pad, inbuffer), GST_FLOW_OK);
}
inbuffer =
gst_buffer_new_memdup (kate_header_0x88, sizeof (kate_header_0x88));
GST_BUFFER_OFFSET (inbuffer) = GST_BUFFER_OFFSET_END (inbuffer) = 0;
fail_unless_equals_int (gst_pad_push (pad, inbuffer), GST_FLOW_OK);
}
GST_START_TEST (test_kate_parse)
{
GstElement *kateparse;
GstBuffer *inbuffer;
GstBus *bus;
kateparse = setup_kateparse ();
fail_unless (gst_element_set_state (kateparse,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
bus = gst_bus_new ();
gst_element_set_bus (kateparse, bus);
test_kate_send_headers (kateparse, myparsesrcpad);
/* push a text packet */
inbuffer =
gst_buffer_new_memdup (kate_header_0x00, sizeof (kate_header_0x00));
GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) =
1 * GST_SECOND;
GST_BUFFER_DURATION (inbuffer) = 5 * GST_SECOND;
GST_BUFFER_OFFSET_END (inbuffer) = (GST_BUFFER_TIMESTAMP (inbuffer) << 32); /* granpos */
fail_unless_equals_int (gst_pad_push (myparsesrcpad, inbuffer), GST_FLOW_OK);
/* push a eos packet */
inbuffer =
gst_buffer_new_memdup (kate_header_0x7f, sizeof (kate_header_0x7f));
GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) =
6 * GST_SECOND;
GST_BUFFER_DURATION (inbuffer) = 0;
GST_BUFFER_OFFSET_END (inbuffer) = (GST_BUFFER_TIMESTAMP (inbuffer) << 32); /* granpos */
fail_unless_equals_int (gst_pad_push (myparsesrcpad, inbuffer), GST_FLOW_OK);
/* signal eos */
fail_unless (gst_pad_push_event (myparsesrcpad,
gst_event_new_eos ()) == TRUE);
fail_unless (gst_element_set_state (kateparse,
GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
"could not set to ready");
/* at least one data packet and one EOS packet should have been emitted */
check_buffers (2, TRUE);
/* cleanup */
gst_bus_set_flushing (bus, TRUE);
gst_element_set_bus (kateparse, NULL);
gst_object_unref (GST_OBJECT (bus));
cleanup_kateparse (kateparse);
g_list_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
gst_check_drop_buffers ();
}
GST_END_TEST;
GST_START_TEST (test_kate_tag_passthrough)
{
GstElement *katetag;
GstBus *bus;
GstBuffer *outbuffer;
GList *list;
GstMapInfo info;
katetag = setup_katetag ();
fail_unless (gst_element_set_state (katetag,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
bus = gst_bus_new ();
gst_element_set_bus (katetag, bus);
test_kate_send_headers (katetag, mytagsrcpad);
/* signal eos */
fail_unless (gst_pad_push_event (mytagsrcpad, gst_event_new_eos ()) == TRUE);
fail_unless (gst_element_set_state (katetag,
GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
"could not set to ready");
/* get the first buffer and check language/category */
fail_unless (g_list_length (buffers) >= 2); /* ID header, Vorbis comments header */
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
/* check identification header is unchanged */
list = g_list_nth (buffers, 0);
fail_unless (list != NULL);
outbuffer = list->data;
fail_if (gst_buffer_map (outbuffer, &info, GST_MAP_READ) != TRUE);
fail_unless_equals_int (info.size, sizeof (kate_header_0x80));
fail_unless_equals_int (memcmp (info.data, kate_header_0x80,
sizeof (kate_header_0x80)), 0);
gst_buffer_unmap (outbuffer, &info);
/* check comment header is unchanged */
list = g_list_nth (buffers, 1);
fail_unless (list != NULL);
outbuffer = list->data;
fail_if (gst_buffer_map (outbuffer, &info, GST_MAP_READ) != TRUE);
fail_unless_equals_int (info.size, sizeof (kate_header_0x81));
fail_unless_equals_int (memcmp (info.data, kate_header_0x81,
sizeof (kate_header_0x81)), 0);
gst_buffer_unmap (outbuffer, &info);
/* all headers should have been emitted, but no particular packets */
check_buffers (0, TRUE);
/* cleanup */
gst_bus_set_flushing (bus, TRUE);
gst_element_set_bus (katetag, NULL);
gst_object_unref (GST_OBJECT (bus));
cleanup_katetag (katetag);
gst_check_drop_buffers ();
}
GST_END_TEST;
GST_START_TEST (test_kate_tag)
{
GstElement *katetag;
GstBus *bus;
GstBuffer *outbuffer;
GstMapInfo info;
katetag = setup_katetag ();
fail_unless (gst_element_set_state (katetag,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
bus = gst_bus_new ();
gst_element_set_bus (katetag, bus);
g_object_set (katetag, "language", "cy", NULL);
g_object_set (katetag, "category", "subtitles", NULL);
test_kate_send_headers (katetag, mytagsrcpad);
/* signal eos */
fail_unless (gst_pad_push_event (mytagsrcpad, gst_event_new_eos ()) == TRUE);
fail_unless (gst_element_set_state (katetag,
GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
"could not set to ready");
/* get the first buffer and check language/category */
fail_unless (g_list_length (buffers) >= 1);
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
assert_equals_int (gst_buffer_map (outbuffer, &info, GST_MAP_READ), TRUE);
fail_if (info.size != 64);
fail_if (strcmp ((const char *) info.data + 32, "cy"));
fail_if (strcmp ((const char *) info.data + 48, "subtitles"));
gst_buffer_unmap (outbuffer, &info);
/* all headers should have been emitted, but no particular packets */
check_buffers (0, TRUE);
/* cleanup */
gst_bus_set_flushing (bus, TRUE);
gst_element_set_bus (katetag, NULL);
gst_object_unref (GST_OBJECT (bus));
cleanup_katetag (katetag);
gst_check_drop_buffers ();
}
GST_END_TEST;
static Suite *
kate_suite (void)
{
Suite *s = suite_create ("kate");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_kate_typefind);
tcase_add_test (tc_chain, test_kate_empty_identification_header);
tcase_add_test (tc_chain, test_kate_identification_header);
tcase_add_test (tc_chain, test_kate_encode_nothing);
tcase_add_test (tc_chain, test_kate_encode_empty);
tcase_add_test (tc_chain, test_kate_encode_simple);
tcase_add_test (tc_chain, test_kate_encode_spu);
tcase_add_test (tc_chain, test_kate_encode_keepalives);
tcase_add_test (tc_chain, test_kate_parse);
tcase_add_test (tc_chain, test_kate_tag_passthrough);
tcase_add_test (tc_chain, test_kate_tag);
return s;
}
GST_CHECK_MAIN (kate);

View file

@ -140,8 +140,6 @@ if host_machine.system() != 'windows'
[['elements/jifmux.c'], [['elements/jifmux.c'],
not exif_dep.found() or not cdata.has('HAVE_UNISTD_H'), [exif_dep]], not exif_dep.found() or not cdata.has('HAVE_UNISTD_H'), [exif_dep]],
[['elements/jpegparse.c'], not cdata.has('HAVE_UNISTD_H')], [['elements/jpegparse.c'], not cdata.has('HAVE_UNISTD_H')],
[['elements/kate.c'],
not kate_dep.found() or not cdata.has('HAVE_UNISTD_H'), [kate_dep]],
[['elements/netsim.c']], [['elements/netsim.c']],
[['elements/shm.c'], not shm_enabled, shm_deps], [['elements/shm.c'], not shm_enabled, shm_deps],
[['elements/voaacenc.c'], [['elements/voaacenc.c'],