[MOVED FROM GST-P-FARSIGHT] Simplify newsegment code
[platform/upstream/gstreamer.git] / ext / mimic / gstmimdec.c
1 /*
2  * GStreamer
3  * Copyright (c) 2005 INdT.
4  * @author Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
5  * @author Rob Taylor <robtaylor@fastmail.fm>
6  * @author Philippe Khalaf <burger@speedy.org>
7  * @author Ole André Vadla Ravnås <oleavr@gmail.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <gst/gst.h>
30
31 #include "gstmimdec.h"
32
33 GST_DEBUG_CATEGORY (mimdec_debug);
34 #define GST_CAT_DEFAULT (mimdec_debug)
35
36 static GstStaticPadTemplate sink_factory =
37 GST_STATIC_PAD_TEMPLATE (
38   "sink",
39   GST_PAD_SINK,
40   GST_PAD_ALWAYS,
41   GST_STATIC_CAPS ("video/x-msnmsgr-webcam")
42 );
43
44 static GstStaticPadTemplate src_factory =
45 GST_STATIC_PAD_TEMPLATE (
46     "src",
47     GST_PAD_SRC,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS ("video/x-raw-rgb, "
50         "bpp = (int) 24, "
51         "depth = (int) 24, "
52         "endianness = (int) 4321, "
53         "framerate = (fraction) [ 0/1, 30/1 ], "
54         "red_mask = (int) 16711680, "
55         "green_mask = (int) 65280, "
56         "blue_mask = (int) 255, "
57         "height = (int) [16, 4096], "
58         "width = (int) [16, 4096]"
59     )
60 );
61
62 static void          gst_mimdec_finalize      (GObject        *object);
63
64 static GstFlowReturn gst_mimdec_chain        (GstPad         *pad,
65                                               GstBuffer      *in);
66 static GstCaps      *gst_mimdec_src_getcaps  (GstPad         *pad);
67
68 static GstStateChangeReturn
69                      gst_mimdec_change_state (GstElement     *element,
70                                               GstStateChange  transition);
71
72
73 GST_BOILERPLATE (GstMimDec, gst_mimdec, GstElement, GST_TYPE_ELEMENT);
74
75 static void
76 gst_mimdec_base_init (gpointer klass)
77 {
78   static GstElementDetails plugin_details = {
79     "MimDec",
80     "Codec/Decoder/Video",
81     "Mimic decoder",
82     "Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>, "
83     "Rob Taylor <robtaylor@fastmail.fm>, "
84     "Philippe Khalaf <burger@speedy.org>, "
85     "Ole André Vadla Ravnås <oleavr@gmail.com>"
86   };
87   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
88
89   gst_element_class_add_pad_template (element_class,
90           gst_static_pad_template_get (&src_factory));
91   gst_element_class_add_pad_template (element_class,
92           gst_static_pad_template_get (&sink_factory));
93
94   gst_element_class_set_details (element_class, &plugin_details);
95 }
96
97 static void
98 gst_mimdec_class_init (GstMimDecClass *klass)
99 {
100   GObjectClass *gobject_class;
101   GstElementClass *gstelement_class;
102
103   gobject_class = (GObjectClass*) klass;
104   gstelement_class = (GstElementClass*) klass;
105   gstelement_class->change_state = gst_mimdec_change_state;
106
107   gobject_class->finalize = gst_mimdec_finalize;
108
109   GST_DEBUG_CATEGORY_INIT (mimdec_debug, "mimdec", 0, "Mimic decoder plugin");
110 }
111
112 static void
113 gst_mimdec_init (GstMimDec *mimdec, GstMimDecClass *klass)
114 {
115   mimdec->sinkpad = gst_pad_new_from_template (
116           gst_static_pad_template_get (&sink_factory), "sink");
117   gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->sinkpad);
118   gst_pad_set_chain_function (mimdec->sinkpad, gst_mimdec_chain);
119
120   mimdec->srcpad = gst_pad_new_from_template (
121           gst_static_pad_template_get (&src_factory), "src");
122   gst_pad_set_getcaps_function (mimdec->srcpad, gst_mimdec_src_getcaps);
123   gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->srcpad);
124
125   mimdec->adapter = gst_adapter_new ();
126
127   mimdec->dec = NULL;
128   mimdec->buffer_size = -1;
129   mimdec->have_header = FALSE;
130   mimdec->payload_size = -1;
131   mimdec->last_ts = -1;
132   mimdec->current_ts = -1;
133   mimdec->gst_timestamp = -1;
134 }
135
136 static void
137 gst_mimdec_finalize (GObject *object)
138 {
139     GstMimDec *mimdec = GST_MIMDEC (object);
140
141     gst_adapter_clear (mimdec->adapter);
142     g_object_unref (mimdec->adapter);
143 }
144
145 static GstFlowReturn
146 gst_mimdec_chain (GstPad *pad, GstBuffer *in)
147 {
148   GstMimDec *mimdec;
149   GstBuffer *out_buf, *buf;
150   guchar *header, *frame_body;
151   guint32 fourcc;
152   guint16 header_size;
153   gint width, height;
154   GstCaps * caps;
155   GstFlowReturn res = GST_FLOW_OK;
156
157   GST_DEBUG ("in gst_mimdec_chain");
158
159   g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
160
161   mimdec = GST_MIMDEC (gst_pad_get_parent (pad));
162   g_return_val_if_fail (GST_IS_MIMDEC (mimdec), GST_FLOW_ERROR);
163
164   g_return_val_if_fail(GST_PAD_IS_LINKED(mimdec->srcpad), GST_FLOW_ERROR);
165
166   buf = GST_BUFFER (in);
167   gst_adapter_push (mimdec->adapter, buf);
168
169
170   if (mimdec->gst_timestamp == -1) {
171     GstClock *clock;
172     GstClockTime base_time;
173
174     base_time = gst_element_get_base_time (GST_ELEMENT (mimdec));
175
176     clock = gst_element_get_clock (GST_ELEMENT (mimdec));
177     if (clock != NULL) {
178       mimdec->gst_timestamp = gst_clock_get_time (clock) - base_time;
179       gst_object_unref (clock);
180     }
181   }
182
183
184   // do we have enough bytes to read a header
185   while (gst_adapter_available (mimdec->adapter) >= (mimdec->have_header ? mimdec->payload_size : 24)) {
186       if (!mimdec->have_header) {
187           header = (guchar *) gst_adapter_peek (mimdec->adapter, 24);
188           header_size = GUINT16_FROM_LE (*(guint16 *) (header + 0));
189           if (header_size != 24) {
190               GST_WARNING_OBJECT (mimdec,
191                 "invalid frame: header size %d incorrect", header_size);
192               gst_adapter_flush (mimdec->adapter, 24);
193               res = GST_FLOW_ERROR;
194               goto out;
195           }
196
197           fourcc = GST_MAKE_FOURCC ('M', 'L', '2', '0');
198           if (GUINT32_FROM_LE (*((guint32 *) (header + 12))) != fourcc) {
199               GST_WARNING_OBJECT (mimdec, "invalid frame: unknown FOURCC code %d", fourcc);
200               gst_adapter_flush (mimdec->adapter, 24);
201               res = GST_FLOW_ERROR;
202               goto out;
203           }
204
205           mimdec->payload_size = GUINT32_FROM_LE (*((guint32 *) (header + 8)));
206
207           mimdec->current_ts = GUINT32_FROM_LE (*((guint32 *) (header + 20)));
208
209           GST_DEBUG ("Got packet, payload size %d", mimdec->payload_size);
210
211           gst_adapter_flush (mimdec->adapter, 24);
212
213           mimdec->have_header = TRUE;
214       }
215
216       if (gst_adapter_available (mimdec->adapter) < mimdec->payload_size)
217       {
218         goto out;
219       }
220
221       frame_body = (guchar *) gst_adapter_peek (mimdec->adapter, mimdec->payload_size);
222
223       if (mimdec->dec == NULL) {
224           GstEvent * event;
225           gboolean result;
226
227           mimdec->dec = mimic_open ();
228           if (mimdec->dec == NULL) {
229               GST_WARNING_OBJECT (mimdec, "mimic_open error\n");
230
231               gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
232               mimdec->have_header = FALSE;
233               res = GST_FLOW_ERROR;
234               goto out;
235           }
236
237           if (!mimic_decoder_init (mimdec->dec, frame_body)) {
238               GST_WARNING_OBJECT (mimdec, "mimic_decoder_init error\n");
239               mimic_close (mimdec->dec);
240               mimdec->dec = NULL;
241
242               gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
243               mimdec->have_header = FALSE;
244               res = GST_FLOW_ERROR;
245               goto out;
246           }
247
248           if (!mimic_get_property (mimdec->dec, "buffer_size", &mimdec->buffer_size)) {
249               GST_WARNING_OBJECT (mimdec,
250                   "mimic_get_property('buffer_size') error\n");
251               mimic_close (mimdec->dec);
252               mimdec->dec = NULL;
253
254               gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
255               mimdec->have_header = FALSE;
256               res = GST_FLOW_ERROR;
257               goto out;
258           }
259
260           event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
261               0, -1, 0);
262           result = gst_pad_push_event (mimdec->srcpad, event);
263           if (!result)
264           {
265               GST_WARNING_OBJECT (mimdec, "gst_pad_push_event failed");
266               res = GST_FLOW_ERROR;
267               goto out;
268           }
269       }
270
271       out_buf = gst_buffer_new_and_alloc (mimdec->buffer_size);
272
273       if (!mimic_decode_frame (mimdec->dec, frame_body, GST_BUFFER_DATA (out_buf))) {
274           GST_WARNING_OBJECT (mimdec, "mimic_decode_frame error\n");
275
276           gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
277           mimdec->have_header = FALSE;
278
279           gst_buffer_unref (out_buf);
280           res = GST_FLOW_ERROR;
281           goto out;
282       }
283
284       if (mimdec->last_ts != -1) {
285         int diff = mimdec->current_ts - mimdec->last_ts;
286         if (diff < 0 || diff > 5000) {
287           diff = 1000;
288         }
289         mimdec->gst_timestamp += diff * GST_MSECOND;
290       }
291       GST_BUFFER_TIMESTAMP(out_buf) = mimdec->gst_timestamp;
292       mimdec->last_ts = mimdec->current_ts;
293
294
295       mimic_get_property(mimdec->dec, "width", &width);
296       mimic_get_property(mimdec->dec, "height", &height);
297       GST_DEBUG_OBJECT (mimdec,
298           "got WxH %d x %d payload size %d buffer_size %d",
299           width, height, mimdec->payload_size, mimdec->buffer_size);
300       caps = gst_caps_new_simple ("video/x-raw-rgb",
301               "bpp", G_TYPE_INT, 24,
302               "depth", G_TYPE_INT, 24,
303               "endianness", G_TYPE_INT, 4321,
304               "framerate", GST_TYPE_FRACTION, 7, 1,
305               "red_mask", G_TYPE_INT, 16711680,
306               "green_mask", G_TYPE_INT, 65280,
307               "blue_mask", G_TYPE_INT, 255,
308               "width", G_TYPE_INT, width,
309               "height", G_TYPE_INT, height, NULL);
310       gst_buffer_set_caps (out_buf, caps);
311       gst_caps_unref (caps);
312       res = gst_pad_push (mimdec->srcpad, out_buf);
313
314       gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
315       mimdec->have_header = FALSE;
316   }
317
318  out:
319   gst_object_unref (mimdec);
320
321   return res;
322 }
323
324 static GstStateChangeReturn
325 gst_mimdec_change_state (GstElement *element, GstStateChange transition)
326 {
327   GstMimDec *mimdec;
328
329   switch (transition) {
330     case GST_STATE_CHANGE_READY_TO_NULL:
331       mimdec = GST_MIMDEC (element);
332       if (mimdec->dec != NULL) {
333         mimic_close (mimdec->dec);
334         mimdec->dec = NULL;
335         mimdec->buffer_size = -1;
336         mimdec->have_header = FALSE;
337         mimdec->payload_size = -1;
338         mimdec->gst_timestamp = -1;
339         mimdec->current_ts = -1;
340         mimdec->last_ts = -1;
341       }
342       break;
343     default:
344       break;
345   }
346
347   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
348 }
349
350 static GstCaps *
351 gst_mimdec_src_getcaps (GstPad *pad)
352 {
353   GstCaps *caps;
354
355   if (!(caps = GST_PAD_CAPS (pad)))
356     caps = (GstCaps *) gst_pad_get_pad_template_caps (pad);
357   caps = gst_caps_ref (caps);
358
359   return caps;
360 }