1 /* GStreamer DVD subtitle parser
2 * Copyright (C) 2007 Mark Nauwelaerts <mnauw@users.sourceforge.net>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <string.h>
25 #include <gst/gst.h>
26 #include "gstdvdsubparse.h"
27
28 GST_DEBUG_CATEGORY_STATIC (dvdsubparse_debug);
29 #define GST_CAT_DEFAULT dvdsubparse_debug
30
31 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
32 GST_PAD_SRC,
33 GST_PAD_ALWAYS,
34 GST_STATIC_CAPS ("subpicture/x-dvd, parsed=(boolean)true")
35 );
36
37 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
38 GST_PAD_SINK,
39 GST_PAD_ALWAYS,
40 GST_STATIC_CAPS ("subpicture/x-dvd")
41 );
42
43 static void gst_dvd_sub_parse_finalize (GObject * object);
44
45 static void gst_dvd_sub_parse_reset (GstDvdSubParse * parse);
46
47 static gboolean gst_dvd_sub_parse_event (GstPad * pad, GstObject * parent,
48 GstEvent * event);
49 static GstFlowReturn gst_dvd_sub_parse_chain (GstPad * pad, GstObject * parent,
50 GstBuffer * buf);
51
52 static GstStateChangeReturn gst_dvd_sub_parse_change_state (GstElement *
53 element, GstStateChange transition);
54
55 #define gst_dvd_sub_parse_parent_class parent_class
56 G_DEFINE_TYPE (GstDvdSubParse, gst_dvd_sub_parse, GST_TYPE_ELEMENT);
57
58 static void
gst_dvd_sub_parse_class_init(GstDvdSubParseClass * klass)59 gst_dvd_sub_parse_class_init (GstDvdSubParseClass * klass)
60 {
61 GObjectClass *gobject_class;
62 GstElementClass *gstelement_class;
63
64 gobject_class = (GObjectClass *) klass;
65 gstelement_class = (GstElementClass *) klass;
66
67 gobject_class->finalize = gst_dvd_sub_parse_finalize;
68
69 GST_DEBUG_CATEGORY_INIT (dvdsubparse_debug, "dvdsubparse", 0,
70 "DVD subtitle parser");
71
72 gstelement_class->change_state =
73 GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_change_state);
74
75 gst_element_class_add_static_pad_template (gstelement_class, &src_template);
76 gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
77
78 gst_element_class_set_static_metadata (gstelement_class,
79 "DVD subtitle parser", "Codec/Parser/Subtitle",
80 "Parses and packetizes DVD subtitle streams",
81 "Mark Nauwelaerts <mnauw@users.sourceforge.net>");
82 }
83
84 static void
gst_dvd_sub_parse_finalize(GObject * object)85 gst_dvd_sub_parse_finalize (GObject * object)
86 {
87 GstDvdSubParse *parse = GST_DVD_SUB_PARSE (object);
88
89 g_object_unref (parse->adapter);
90 parse->adapter = NULL;
91
92 G_OBJECT_CLASS (parent_class)->finalize (object);
93 }
94
95 static void
gst_dvd_sub_parse_init(GstDvdSubParse * parse)96 gst_dvd_sub_parse_init (GstDvdSubParse * parse)
97 {
98 parse->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
99 gst_pad_set_chain_function (parse->sinkpad,
100 GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_chain));
101 gst_pad_set_event_function (parse->sinkpad,
102 GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_event));
103 gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
104
105 parse->srcpad = gst_pad_new_from_static_template (&src_template, "src");
106 gst_pad_use_fixed_caps (parse->srcpad);
107 gst_pad_set_caps (parse->srcpad,
108 gst_static_pad_template_get_caps (&src_template));
109 gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
110
111 /* remainder */
112 parse->adapter = gst_adapter_new ();
113 gst_dvd_sub_parse_reset (parse);
114 }
115
116 static void
gst_dvd_sub_parse_reset(GstDvdSubParse * parse)117 gst_dvd_sub_parse_reset (GstDvdSubParse * parse)
118 {
119 parse->needed = 0;
120 parse->stamp = GST_CLOCK_TIME_NONE;
121 gst_adapter_clear (parse->adapter);
122 }
123
124 static gboolean
gst_dvd_sub_parse_event(GstPad * pad,GstObject * parent,GstEvent * event)125 gst_dvd_sub_parse_event (GstPad * pad, GstObject * parent, GstEvent * event)
126 {
127 GstDvdSubParse *parse;
128 gboolean ret;
129
130 parse = GST_DVD_SUB_PARSE (parent);
131
132 switch (GST_EVENT_TYPE (event)) {
133 case GST_EVENT_CAPS:
134 {
135 GstCaps *caps;
136
137 gst_event_unref (event);
138 caps = gst_static_pad_template_get_caps (&src_template);
139 gst_pad_push_event (parse->srcpad, gst_event_new_caps (caps));
140 gst_caps_unref (caps);
141 ret = TRUE;
142 break;
143 }
144 case GST_EVENT_FLUSH_STOP:
145 gst_dvd_sub_parse_reset (parse);
146 /* fall-through */
147 default:
148 ret = gst_pad_event_default (pad, parent, event);
149 break;
150 }
151
152 return ret;
153 }
154
155
156 static GstFlowReturn
gst_dvd_sub_parse_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)157 gst_dvd_sub_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
158 {
159 GstDvdSubParse *parse = GST_DVD_SUB_PARSE (parent);
160 GstAdapter *adapter;
161 GstBuffer *outbuf = NULL;
162 GstFlowReturn ret = GST_FLOW_OK;
163
164 adapter = parse->adapter;
165
166 GST_LOG_OBJECT (parse, "%" G_GSIZE_FORMAT " bytes, ts: %" GST_TIME_FORMAT,
167 gst_buffer_get_size (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
168
169 gst_adapter_push (adapter, buf);
170
171 if (!parse->needed) {
172 guint8 data[2];
173
174 gst_adapter_copy (adapter, data, 0, 2);
175 parse->needed = GST_READ_UINT16_BE (data);
176 }
177
178 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
179 if (GST_CLOCK_TIME_IS_VALID (parse->stamp))
180 /* normally, we expect only the first fragment to carry a timestamp */
181 GST_WARNING_OBJECT (parse, "Received more timestamps than expected.");
182 else
183 parse->stamp = GST_BUFFER_TIMESTAMP (buf);
184 }
185
186 if (parse->needed) {
187 guint av;
188
189 av = gst_adapter_available (adapter);
190 if (av >= parse->needed) {
191 if (av > parse->needed) {
192 /* normally, we expect several fragment, boundary aligned */
193 GST_WARNING_OBJECT (parse, "Unexpected: needed %d, "
194 "but more (%d) is available.", parse->needed, av);
195 }
196 outbuf = gst_adapter_take_buffer (adapter, parse->needed);
197 /* decorate buffer */
198 GST_BUFFER_TIMESTAMP (outbuf) = parse->stamp;
199 /* reset state */
200 parse->stamp = GST_CLOCK_TIME_NONE;
201 parse->needed = 0;
202 /* and send along */
203 ret = gst_pad_push (parse->srcpad, outbuf);
204 }
205 }
206
207 return ret;
208 }
209
210 static GstStateChangeReturn
gst_dvd_sub_parse_change_state(GstElement * element,GstStateChange transition)211 gst_dvd_sub_parse_change_state (GstElement * element, GstStateChange transition)
212 {
213 GstDvdSubParse *parse = GST_DVD_SUB_PARSE (element);
214 GstStateChangeReturn ret;
215
216 switch (transition) {
217 case GST_STATE_CHANGE_NULL_TO_READY:
218 case GST_STATE_CHANGE_READY_TO_PAUSED:
219 break;
220 case GST_STATE_CHANGE_PAUSED_TO_READY:
221 break;
222 default:
223 break;
224 }
225
226 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
227 if (ret != GST_STATE_CHANGE_SUCCESS)
228 return ret;
229
230 switch (transition) {
231 case GST_STATE_CHANGE_PAUSED_TO_READY:
232 gst_dvd_sub_parse_reset (parse);
233 break;
234 default:
235 break;
236 }
237
238 return GST_STATE_CHANGE_SUCCESS;
239 }
240