• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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