1 /* GStreamer
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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 /**
21 * SECTION:element-smokedec
22 * @title: smokedec
23 *
24 * Decodes images in smoke format.
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 #include <string.h>
31
32 /*#define DEBUG_ENABLED*/
33 #include "gstsmokedec.h"
34 #include <gst/video/video.h>
35
36 GST_DEBUG_CATEGORY_STATIC (smokedec_debug);
37 #define GST_CAT_DEFAULT smokedec_debug
38
39 /* SmokeDec signals and args */
40 enum
41 {
42 LAST_SIGNAL
43 };
44
45 enum
46 {
47 PROP_0
48 };
49
50 static void gst_smokedec_base_init (gpointer g_class);
51 static void gst_smokedec_class_init (GstSmokeDec * klass);
52 static void gst_smokedec_init (GstSmokeDec * smokedec);
53 static void gst_smokedec_finalize (GObject * object);
54
55 static GstStateChangeReturn
56 gst_smokedec_change_state (GstElement * element, GstStateChange transition);
57
58 static GstFlowReturn gst_smokedec_chain (GstPad * pad, GstBuffer * buf);
59
60 static GstElementClass *parent_class = NULL;
61
62 /*static guint gst_smokedec_signals[LAST_SIGNAL] = { 0 }; */
63
64 GType
gst_smokedec_get_type(void)65 gst_smokedec_get_type (void)
66 {
67 static GType smokedec_type = 0;
68
69 if (!smokedec_type) {
70 static const GTypeInfo smokedec_info = {
71 sizeof (GstSmokeDecClass),
72 gst_smokedec_base_init,
73 NULL,
74 (GClassInitFunc) gst_smokedec_class_init,
75 NULL,
76 NULL,
77 sizeof (GstSmokeDec),
78 0,
79 (GInstanceInitFunc) gst_smokedec_init,
80 };
81
82 smokedec_type =
83 g_type_register_static (GST_TYPE_ELEMENT, "GstSmokeDec", &smokedec_info,
84 0);
85 }
86 return smokedec_type;
87 }
88
89 GST_ELEMENT_REGISTER_DEFINE (smokedec, "smokedec", GST_RANK_PRIMARY,
90 GST_TYPE_SMOKEDEC);
91
92 static GstStaticPadTemplate gst_smokedec_src_pad_template =
93 GST_STATIC_PAD_TEMPLATE ("src",
94 GST_PAD_SRC,
95 GST_PAD_ALWAYS,
96 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
97 );
98
99 static GstStaticPadTemplate gst_smokedec_sink_pad_template =
100 GST_STATIC_PAD_TEMPLATE ("sink",
101 GST_PAD_SINK,
102 GST_PAD_ALWAYS,
103 GST_STATIC_CAPS ("video/x-smoke, "
104 "width = (int) [ 16, 4096 ], "
105 "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0/1, MAX ]")
106 );
107
108 static void
gst_smokedec_base_init(gpointer g_class)109 gst_smokedec_base_init (gpointer g_class)
110 {
111 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
112
113 gst_element_class_add_static_pad_template (element_class,
114 &gst_smokedec_src_pad_template);
115 gst_element_class_add_static_pad_template (element_class,
116 &gst_smokedec_sink_pad_template);
117 gst_element_class_set_static_metadata (element_class, "Smoke video decoder",
118 "Codec/Decoder/Video", "Decode video from Smoke format",
119 "Wim Taymans <wim@fluendo.com>");
120 }
121
122 static void
gst_smokedec_class_init(GstSmokeDec * klass)123 gst_smokedec_class_init (GstSmokeDec * klass)
124 {
125 GObjectClass *gobject_class;
126 GstElementClass *gstelement_class;
127
128 gobject_class = (GObjectClass *) klass;
129 gstelement_class = (GstElementClass *) klass;
130
131 parent_class = g_type_class_peek_parent (klass);
132
133 gobject_class->finalize = gst_smokedec_finalize;
134
135 gstelement_class->change_state =
136 GST_DEBUG_FUNCPTR (gst_smokedec_change_state);
137
138 GST_DEBUG_CATEGORY_INIT (smokedec_debug, "smokedec", 0, "Smoke decoder");
139 }
140
141 static void
gst_smokedec_init(GstSmokeDec * smokedec)142 gst_smokedec_init (GstSmokeDec * smokedec)
143 {
144 GST_DEBUG_OBJECT (smokedec, "gst_smokedec_init: initializing");
145 /* create the sink and src pads */
146
147 smokedec->sinkpad =
148 gst_pad_new_from_static_template (&gst_smokedec_sink_pad_template,
149 "sink");
150 gst_pad_set_chain_function (smokedec->sinkpad, gst_smokedec_chain);
151 gst_element_add_pad (GST_ELEMENT (smokedec), smokedec->sinkpad);
152
153 smokedec->srcpad =
154 gst_pad_new_from_static_template (&gst_smokedec_src_pad_template, "src");
155 gst_pad_use_fixed_caps (smokedec->srcpad);
156 gst_element_add_pad (GST_ELEMENT (smokedec), smokedec->srcpad);
157
158 smokecodec_decode_new (&smokedec->info);
159 }
160
161 static void
gst_smokedec_finalize(GObject * object)162 gst_smokedec_finalize (GObject * object)
163 {
164 GstSmokeDec *dec = GST_SMOKEDEC (object);
165
166 smokecodec_info_free (dec->info);
167
168 G_OBJECT_CLASS (parent_class)->finalize (object);
169 }
170
171 static GstFlowReturn
gst_smokedec_chain(GstPad * pad,GstBuffer * buf)172 gst_smokedec_chain (GstPad * pad, GstBuffer * buf)
173 {
174 GstSmokeDec *smokedec;
175 guint8 *data, *outdata;
176 gulong size, outsize;
177 GstBuffer *outbuf;
178 SmokeCodecFlags flags;
179 GstClockTime time;
180 guint width, height;
181 guint fps_num, fps_denom;
182 gint smokeret;
183 GstFlowReturn ret;
184
185 smokedec = GST_SMOKEDEC (gst_pad_get_parent (pad));
186
187 data = GST_BUFFER_DATA (buf);
188 size = GST_BUFFER_SIZE (buf);
189 time = GST_BUFFER_TIMESTAMP (buf);
190
191 if (size < 1)
192 goto too_small;
193
194 GST_LOG_OBJECT (smokedec, "got buffer of %lu bytes", size);
195
196 /* have the ID packet. */
197 if (data[0] == SMOKECODEC_TYPE_ID) {
198 smokeret = smokecodec_parse_id (smokedec->info, data, size);
199 if (smokeret != SMOKECODEC_OK)
200 goto header_error;
201
202 ret = GST_FLOW_OK;
203 goto done;
204 }
205
206 /* now handle data packets */
207 GST_DEBUG_OBJECT (smokedec, "reading header %08lx", *(gulong *) data);
208 smokecodec_parse_header (smokedec->info, data, size, &flags, &width, &height,
209 &fps_num, &fps_denom);
210
211 if (smokedec->height != height || smokedec->width != width ||
212 smokedec->fps_num != fps_num || smokedec->fps_denom != fps_denom) {
213 GstCaps *caps;
214
215 GST_DEBUG_OBJECT (smokedec, "parameter change: %dx%d @ %d/%dfps",
216 width, height, fps_num, fps_denom);
217
218 smokedec->height = height;
219 smokedec->width = width;
220
221 caps = gst_caps_new_simple ("video/x-raw-yuv",
222 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
223 "width", G_TYPE_INT, width,
224 "height", G_TYPE_INT, height,
225 "framerate", GST_TYPE_FRACTION, fps_num, fps_denom, NULL);
226
227 gst_pad_set_caps (smokedec->srcpad, caps);
228 gst_caps_unref (caps);
229 }
230
231 if (smokedec->need_keyframe) {
232 if (!(flags & SMOKECODEC_KEYFRAME))
233 goto keyframe_skip;
234
235 smokedec->need_keyframe = FALSE;
236 }
237
238 outsize = width * height + width * height / 2;
239 outbuf = gst_buffer_new_and_alloc (outsize);
240 outdata = GST_BUFFER_DATA (outbuf);
241
242 GST_BUFFER_DURATION (outbuf) =
243 gst_util_uint64_scale_int (GST_SECOND, fps_denom, fps_num);
244 GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buf);
245 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (smokedec->srcpad));
246
247 if (time == GST_CLOCK_TIME_NONE) {
248 if (GST_BUFFER_OFFSET (buf) == -1) {
249 time = smokedec->next_time;
250 } else {
251 time = GST_BUFFER_OFFSET (buf) * GST_BUFFER_DURATION (outbuf);
252 }
253 }
254 GST_BUFFER_TIMESTAMP (outbuf) = time;
255 if (time != -1)
256 smokedec->next_time = time + GST_BUFFER_DURATION (outbuf);
257 else
258 smokedec->next_time = -1;
259
260 smokeret = smokecodec_decode (smokedec->info, data, size, outdata);
261 if (smokeret != SMOKECODEC_OK)
262 goto decode_error;
263
264 GST_DEBUG_OBJECT (smokedec, "gst_smokedec_chain: sending buffer");
265 ret = gst_pad_push (smokedec->srcpad, outbuf);
266
267 done:
268 gst_buffer_unref (buf);
269 gst_object_unref (smokedec);
270
271 return ret;
272
273 /* ERRORS */
274 too_small:
275 {
276 GST_ELEMENT_ERROR (smokedec, STREAM, DECODE,
277 (NULL), ("Input buffer too small"));
278 ret = GST_FLOW_ERROR;
279 goto done;
280 }
281 header_error:
282 {
283 GST_ELEMENT_ERROR (smokedec, STREAM, DECODE,
284 (NULL), ("Could not parse smoke header, reason: %d", smokeret));
285 ret = GST_FLOW_ERROR;
286 goto done;
287 }
288 keyframe_skip:
289 {
290 GST_DEBUG_OBJECT (smokedec, "dropping buffer while waiting for keyframe");
291 ret = GST_FLOW_OK;
292 goto done;
293 }
294 decode_error:
295 {
296 GST_ELEMENT_ERROR (smokedec, STREAM, DECODE,
297 (NULL), ("Could not decode smoke frame, reason: %d", smokeret));
298 ret = GST_FLOW_ERROR;
299 goto done;
300 }
301 }
302
303 static GstStateChangeReturn
gst_smokedec_change_state(GstElement * element,GstStateChange transition)304 gst_smokedec_change_state (GstElement * element, GstStateChange transition)
305 {
306 GstStateChangeReturn ret;
307 GstSmokeDec *dec;
308
309 dec = GST_SMOKEDEC (element);
310
311 switch (transition) {
312 case GST_STATE_CHANGE_READY_TO_PAUSED:
313 /* reset the initial video state */
314 dec->format = -1;
315 dec->width = -1;
316 dec->height = -1;
317 dec->fps_num = -1;
318 dec->fps_denom = -1;
319 dec->next_time = 0;
320 default:
321 break;
322 }
323
324 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
325 if (ret != GST_STATE_CHANGE_SUCCESS)
326 return ret;
327
328 switch (transition) {
329 case GST_STATE_CHANGE_PAUSED_TO_READY:
330 break;
331 default:
332 break;
333 }
334
335 return ret;
336 }
337