f5d244cde489379fb7bf1aa6f25b99d98d5a2065
[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 = (double) [1.0, 30.0], "
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_class_init   (GstMimDecClass *klass);
63 static void          gst_mimdec_base_init    (GstMimDecClass *klass);
64 static void          gst_mimdec_init         (GstMimDec      *mimdec);
65 static void          gst_mimdec_finalize      (GObject        *object);
66
67 static GstFlowReturn gst_mimdec_chain        (GstPad         *pad, 
68                                               GstBuffer      *in);
69 static GstCaps      *gst_mimdec_src_getcaps  (GstPad         *pad);
70
71 static GstStateChangeReturn
72                      gst_mimdec_change_state (GstElement     *element, 
73                                               GstStateChange  transition);
74
75 static GstElementClass *parent_class = NULL;
76
77 GType
78 gst_gst_mimdec_get_type (void)
79 {
80   static GType plugin_type = 0;
81
82   if (!plugin_type)
83   {
84     static const GTypeInfo plugin_info =
85     {
86       sizeof (GstMimDecClass),
87       (GBaseInitFunc) gst_mimdec_base_init,
88       NULL,
89       (GClassInitFunc) gst_mimdec_class_init,
90       NULL,
91       NULL,
92       sizeof (GstMimDec),
93       0,
94       (GInstanceInitFunc) gst_mimdec_init,
95     };
96     plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
97                                           "GstMimDec",
98                                           &plugin_info, 0);
99   }
100   return plugin_type;
101 }
102
103 static void
104 gst_mimdec_base_init (GstMimDecClass *klass)
105 {
106   static GstElementDetails plugin_details = {
107     "MimDec",
108     "Codec/Decoder/Video",
109     "Mimic decoder",
110     "Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>, "
111     "Rob Taylor <robtaylor@fastmail.fm>, "
112     "Philippe Khalaf <burger@speedy.org>, "
113     "Ole André Vadla Ravnås <oleavr@gmail.com>"
114   };
115   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
116
117   gst_element_class_add_pad_template (element_class,
118           gst_static_pad_template_get (&src_factory));
119   gst_element_class_add_pad_template (element_class,
120           gst_static_pad_template_get (&sink_factory));
121
122   gst_element_class_set_details (element_class, &plugin_details);
123 }
124
125 static void
126 gst_mimdec_class_init (GstMimDecClass *klass)
127 {
128   GObjectClass *gobject_class;
129   GstElementClass *gstelement_class;
130
131   gobject_class = (GObjectClass*) klass;
132   gstelement_class = (GstElementClass*) klass;
133   gstelement_class->change_state = gst_mimdec_change_state;
134
135   gobject_class->finalize = gst_mimdec_finalize;
136
137   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
138
139   GST_DEBUG_CATEGORY_INIT (mimdec_debug, "mimdec", 0, "Mimic decoder plugin");
140 }
141
142 static void
143 gst_mimdec_init (GstMimDec *mimdec)
144 {
145   mimdec->sinkpad = gst_pad_new_from_template (
146           gst_static_pad_template_get (&sink_factory), "sink");
147   gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->sinkpad);
148   gst_pad_set_chain_function (mimdec->sinkpad, gst_mimdec_chain);
149
150   mimdec->srcpad = gst_pad_new_from_template (
151           gst_static_pad_template_get (&src_factory), "src");
152   gst_pad_set_getcaps_function (mimdec->srcpad, gst_mimdec_src_getcaps);
153   gst_element_add_pad (GST_ELEMENT (mimdec), mimdec->srcpad);
154
155   mimdec->adapter = gst_adapter_new ();
156
157   mimdec->dec = NULL;
158   mimdec->buffer_size = -1;
159   mimdec->have_header = FALSE;
160   mimdec->payload_size = -1;
161   mimdec->last_ts = -1;
162   mimdec->current_ts = -1;
163   mimdec->gst_timestamp = -1;
164 }
165
166 static void
167 gst_mimdec_finalize (GObject *object)
168 {
169     GstMimDec *mimdec = GST_MIMDEC (object);
170
171     gst_adapter_clear (mimdec->adapter);
172     g_object_unref (mimdec->adapter);
173 }
174
175 static GstFlowReturn
176 gst_mimdec_chain (GstPad *pad, GstBuffer *in)
177 {
178   GstMimDec *mimdec;
179   GstBuffer *out_buf, *buf;
180   guchar *header, *frame_body;
181   guint32 fourcc;
182   guint16 header_size;
183   gint width, height;
184   GstCaps * caps;
185   GstFlowReturn res = GST_FLOW_OK;
186
187   GST_DEBUG ("in gst_mimdec_chain");
188
189   g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
190
191   mimdec = GST_MIMDEC (gst_pad_get_parent (pad));
192   g_return_val_if_fail (GST_IS_MIMDEC (mimdec), GST_FLOW_ERROR);
193
194   g_return_val_if_fail(GST_PAD_IS_LINKED(mimdec->srcpad), GST_FLOW_ERROR);
195
196   buf = GST_BUFFER (in);
197   gst_adapter_push (mimdec->adapter, buf);
198
199
200   if (mimdec->gst_timestamp == -1) {
201     GstClock *clock;
202     GstClockTime base_time;
203
204     base_time = gst_element_get_base_time (GST_ELEMENT (mimdec));
205
206     clock = gst_element_get_clock (GST_ELEMENT (mimdec));
207     if (clock != NULL) {
208       mimdec->gst_timestamp = gst_clock_get_time (clock) - base_time;
209       gst_object_unref (clock);
210     }
211   }
212
213
214   // do we have enough bytes to read a header
215   while (gst_adapter_available (mimdec->adapter) >= (mimdec->have_header ? mimdec->payload_size : 24)) {
216       if (!mimdec->have_header) {
217           header = (guchar *) gst_adapter_peek (mimdec->adapter, 24);
218           header_size = GUINT16_FROM_LE (*(guint16 *) (header + 0));
219           if (header_size != 24) {
220               GST_WARNING_OBJECT (mimdec, 
221                 "invalid frame: header size %d incorrect", header_size);
222               gst_adapter_flush (mimdec->adapter, 24);
223               res = GST_FLOW_ERROR;
224               goto out;
225           }
226
227           fourcc = GST_MAKE_FOURCC ('M', 'L', '2', '0');
228           if (GUINT32_FROM_LE (*((guint32 *) (header + 12))) != fourcc) {
229               GST_WARNING_OBJECT (mimdec, "invalid frame: unknown FOURCC code %d", fourcc);
230               gst_adapter_flush (mimdec->adapter, 24);
231               res = GST_FLOW_ERROR;
232               goto out;
233           }
234
235           mimdec->payload_size = GUINT32_FROM_LE (*((guint32 *) (header + 8)));
236           GST_DEBUG ("Got packet, payload size %d", mimdec->payload_size);
237
238           gst_adapter_flush (mimdec->adapter, 24);
239
240           mimdec->have_header = TRUE;
241       }
242
243       if (gst_adapter_available (mimdec->adapter) < mimdec->payload_size)
244       {
245         goto out;
246       }
247
248       frame_body = (guchar *) gst_adapter_peek (mimdec->adapter, mimdec->payload_size);
249
250       if (mimdec->dec == NULL) {
251           GstSegment segment;
252           GstEvent * event;
253           gboolean result;
254
255           mimdec->dec = mimic_open ();
256           if (mimdec->dec == NULL) {
257               GST_WARNING_OBJECT (mimdec, "mimic_open error\n");
258
259               gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
260               mimdec->have_header = FALSE;
261               res = GST_FLOW_ERROR;
262               goto out;
263           }
264
265           if (!mimic_decoder_init (mimdec->dec, frame_body)) {
266               GST_WARNING_OBJECT (mimdec, "mimic_decoder_init error\n");
267               mimic_close (mimdec->dec);
268               mimdec->dec = NULL;
269
270               gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
271               mimdec->have_header = FALSE;
272               res = GST_FLOW_ERROR;
273               goto out;
274           }
275
276           if (!mimic_get_property (mimdec->dec, "buffer_size", &mimdec->buffer_size)) {
277               GST_WARNING_OBJECT (mimdec,
278                   "mimic_get_property('buffer_size') error\n");
279               mimic_close (mimdec->dec);
280               mimdec->dec = NULL;
281
282               gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
283               mimdec->have_header = FALSE;
284               res = GST_FLOW_ERROR;
285               goto out;
286           }
287
288           gst_segment_init (&segment, GST_FORMAT_TIME);
289           event = gst_event_new_new_segment (FALSE, segment.rate,
290               segment.format, segment.start, segment.stop, segment.time);
291
292           result = gst_pad_push_event (mimdec->srcpad, event);
293           if (!result)
294           {
295               GST_WARNING_OBJECT (mimdec, "gst_pad_push_event failed");
296               res = GST_FLOW_ERROR;
297               goto out;
298           }
299       }
300
301       out_buf = gst_buffer_new_and_alloc (mimdec->buffer_size);
302       GST_BUFFER_TIMESTAMP(out_buf) = GST_BUFFER_TIMESTAMP(buf);
303       if (!mimic_decode_frame (mimdec->dec, frame_body, GST_BUFFER_DATA (out_buf))) {
304           GST_WARNING_OBJECT (mimdec, "mimic_decode_frame error\n");
305
306           gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
307           mimdec->have_header = FALSE;
308
309           gst_buffer_unref (out_buf);
310           res = GST_FLOW_ERROR;
311           goto out;
312       }
313       
314       mimic_get_property(mimdec->dec, "width", &width);
315       mimic_get_property(mimdec->dec, "height", &height);
316       GST_DEBUG_OBJECT (mimdec, 
317           "got WxH %d x %d payload size %d buffer_size %d",
318           width, height, mimdec->payload_size, mimdec->buffer_size);
319       caps = gst_caps_new_simple ("video/x-raw-rgb",
320               "bpp", G_TYPE_INT, 24,
321               "depth", G_TYPE_INT, 24,
322               "endianness", G_TYPE_INT, 4321,
323               "framerate", G_TYPE_DOUBLE, 30.0,
324               "red_mask", G_TYPE_INT, 16711680,
325               "green_mask", G_TYPE_INT, 65280,
326               "blue_mask", G_TYPE_INT, 255,
327               "width", G_TYPE_INT, width,
328               "height", G_TYPE_INT, height, NULL);
329       gst_buffer_set_caps (out_buf, caps);
330       gst_caps_unref (caps);
331       res = gst_pad_push (mimdec->srcpad, out_buf);
332
333       gst_adapter_flush (mimdec->adapter, mimdec->payload_size);
334       mimdec->have_header = FALSE;
335   }
336
337  out:
338   gst_object_unref (mimdec);
339
340   return res;
341 }
342
343 static GstStateChangeReturn
344 gst_mimdec_change_state (GstElement *element, GstStateChange transition)
345 {
346   GstMimDec *mimdec;
347
348   switch (transition) {
349     case GST_STATE_CHANGE_READY_TO_NULL:
350       mimdec = GST_MIMDEC (element);
351       if (mimdec->dec != NULL) {
352         mimic_close (mimdec->dec);
353         mimdec->dec = NULL;
354         mimdec->buffer_size = -1;
355         mimdec->have_header = FALSE;
356         mimdec->payload_size = -1;
357       }
358       break;
359     default:
360       break;
361   }
362
363   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
364 }
365
366 static GstCaps *
367 gst_mimdec_src_getcaps (GstPad *pad)
368 {
369   GstCaps *caps;
370
371   if (!(caps = GST_PAD_CAPS (pad)))
372     caps = (GstCaps *) gst_pad_get_pad_template_caps (pad);
373   caps = gst_caps_ref (caps);
374
375   return caps;
376 }