Fix close routine at decoder plugin.
[platform/adaptation/emulator/gst-plugins-emulator.git] / src / gstmarudec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /* Modifications by Samsung Electronics Co., Ltd.
22  * 1. Provide a hardware buffer in order to avoid additional memcpy operations.
23  */
24
25 #include "gstmarudevice.h"
26 #include "gstmaruutils.h"
27 #include "gstmaruinterface.h"
28
29 #define GST_MARUDEC_PARAMS_QDATA g_quark_from_static_string("marudec-params")
30
31 /* indicate dts, pts, offset in the stream */
32 #define GST_TS_INFO_NONE &ts_info_none
33 static const GstTSInfo ts_info_none = { -1, -1, -1, -1 };
34
35 typedef struct _GstMaruDecClass
36 {
37   GstElementClass parent_class;
38
39   CodecElement *codec;
40   GstPadTemplate *sinktempl;
41   GstPadTemplate *srctempl;
42 } GstMaruDecClass;
43
44
45 static GstElementClass *parent_class = NULL;
46
47 static void gst_marudec_base_init (GstMaruDecClass *klass);
48 static void gst_marudec_class_init (GstMaruDecClass *klass);
49 static void gst_marudec_init (GstMaruDec *marudec);
50 static void gst_marudec_finalize (GObject *object);
51
52 static gboolean gst_marudec_setcaps (GstPad *pad, GstCaps *caps);
53
54 // sinkpad
55 static gboolean gst_marudec_sink_event (GstPad *pad, GstEvent *event);
56 static GstFlowReturn gst_marudec_chain (GstPad *pad, GstBuffer *buffer);
57
58 // srcpad
59 static gboolean gst_marudec_src_event (GstPad *pad, GstEvent *event);
60 static GstStateChangeReturn gst_marudec_change_state (GstElement *element,
61                                                 GstStateChange transition);
62
63 static gboolean gst_marudec_negotiate (GstMaruDec *dec, gboolean force);
64
65 static gint gst_marudec_frame (GstMaruDec *marudec, guint8 *data,
66                               guint size, gint *got_data,
67                               const GstTSInfo *dec_info, gint64 in_offset, GstFlowReturn *ret);
68
69 static gboolean gst_marudec_open (GstMaruDec *marudec);
70 static void gst_marudec_close (GstMaruDec *marudec);
71
72
73 static const GstTSInfo *
74 gst_ts_info_store (GstMaruDec *dec, GstClockTime timestamp,
75     GstClockTime duration, gint64 offset)
76 {
77   gint idx = dec->ts_idx;
78   dec->ts_info[idx].idx = idx;
79   dec->ts_info[idx].timestamp = timestamp;
80   dec->ts_info[idx].duration = duration;
81   dec->ts_info[idx].offset = offset;
82   dec->ts_idx = (idx + 1) & MAX_TS_MASK;
83
84   return &dec->ts_info[idx];
85 }
86
87 static const GstTSInfo *
88 gst_ts_info_get (GstMaruDec *dec, gint idx)
89 {
90   if (G_UNLIKELY (idx < 0 || idx > MAX_TS_MASK))
91     return GST_TS_INFO_NONE;
92
93   return &dec->ts_info[idx];
94 }
95
96 static void
97 gst_marudec_reset_ts (GstMaruDec *marudec)
98 {
99   marudec->next_out = GST_CLOCK_TIME_NONE;
100 }
101
102 static void
103 gst_marudec_update_qos (GstMaruDec *marudec, gdouble proportion,
104   GstClockTime timestamp)
105 {
106   GST_LOG_OBJECT (marudec, "update QOS: %f, %" GST_TIME_FORMAT,
107       proportion, GST_TIME_ARGS (timestamp));
108
109   GST_OBJECT_LOCK (marudec);
110   marudec->proportion = proportion;
111   marudec->earliest_time = timestamp;
112   GST_OBJECT_UNLOCK (marudec);
113 }
114
115 static void
116 gst_marudec_reset_qos (GstMaruDec *marudec)
117 {
118   gst_marudec_update_qos (marudec, 0.5, GST_CLOCK_TIME_NONE);
119   marudec->processed = 0;
120   marudec->dropped = 0;
121 }
122
123 static gboolean
124 gst_marudec_do_qos (GstMaruDec *marudec, GstClockTime timestamp,
125   gboolean *mode_switch)
126 {
127   GstClockTimeDiff diff;
128   gdouble proportion;
129   GstClockTime qostime, earliest_time;
130   gboolean res = TRUE;
131
132   *mode_switch = FALSE;
133
134   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
135     marudec->processed++;
136     return TRUE;
137   }
138
139   proportion = marudec->proportion;
140   earliest_time = marudec->earliest_time;
141
142   qostime = gst_segment_to_running_time (&marudec->segment, GST_FORMAT_TIME,
143     timestamp);
144
145   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime))) {
146     marudec->processed++;
147     return TRUE;
148   }
149
150   diff = GST_CLOCK_DIFF (qostime, earliest_time);
151
152   if (proportion < 0.4 && diff < 0 ){
153     marudec->processed++;
154     return TRUE;
155   } else {
156     if (diff >= 0) {
157       if (0) {
158         res = FALSE;
159       }
160
161       GstClockTime stream_time, jitter;
162       GstMessage *qos_msg;
163
164       marudec->dropped++;
165       stream_time =
166           gst_segment_to_stream_time (&marudec->segment, GST_FORMAT_TIME,
167                   timestamp);
168       jitter = GST_CLOCK_DIFF (qostime, earliest_time);
169       qos_msg =
170           gst_message_new_qos (GST_OBJECT_CAST (marudec), FALSE, qostime,
171                   stream_time, timestamp, GST_CLOCK_TIME_NONE);
172       gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
173       gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS,
174               marudec->processed, marudec->dropped);
175       gst_element_post_message (GST_ELEMENT_CAST (marudec), qos_msg);
176
177       return res;
178     }
179   }
180
181   marudec->processed++;
182   return TRUE;
183 }
184
185 static void
186 clear_queued (GstMaruDec *marudec)
187 {
188   g_list_foreach (marudec->queued, (GFunc) gst_mini_object_unref, NULL);
189   g_list_free (marudec->queued);
190   marudec->queued = NULL;
191 }
192
193 static GstFlowReturn
194 flush_queued (GstMaruDec *marudec)
195 {
196   GstFlowReturn res = GST_FLOW_OK;
197
198   GST_DEBUG_OBJECT (marudec, "flush queued");
199
200   while (marudec->queued) {
201     GstBuffer *buf = GST_BUFFER_CAST (marudec->queued->data);
202
203     GST_LOG_OBJECT (marudec, "pushing buffer %p, offset %"
204       G_GUINT64_FORMAT ", timestamp %"
205       GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, buf,
206       GST_BUFFER_OFFSET (buf),
207       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
208       GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
209
210     res = gst_pad_push (marudec->srcpad, buf);
211
212     marudec->queued =
213       g_list_delete_link (marudec->queued, marudec->queued);
214   }
215
216   return res;
217 }
218
219 static void
220 gst_marudec_drain (GstMaruDec *marudec)
221 {
222 #if 0
223   GstMaruDecClass *oclass;
224   oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
225 #endif
226
227   GST_DEBUG_OBJECT (marudec, "drain frame");
228   {
229     gint have_data, len, try = 0;
230
231     do {
232       GstFlowReturn ret;
233
234       len =
235         gst_marudec_frame (marudec, NULL, 0, &have_data, &ts_info_none, 0, &ret);
236
237       if (len < 0 || have_data == 0) {
238         break;
239       }
240     } while (try++ < 10);
241   }
242
243   if (marudec->segment.rate < 0.0) {
244     GST_DEBUG_OBJECT (marudec, "reverse playback");
245     flush_queued (marudec);
246   }
247 }
248
249 /*
250  * Implementation
251  */
252 static void
253 gst_marudec_base_init (GstMaruDecClass *klass)
254 {
255   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
256   GstCaps *sinkcaps = NULL, *srccaps = NULL;
257   GstPadTemplate *sinktempl, *srctempl;
258   CodecElement *codec;
259   gchar *longname, *classification, *description;
260
261   codec =
262       (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
263                                       GST_MARUDEC_PARAMS_QDATA);
264
265   longname = g_strdup_printf ("%s Decoder", codec->longname);
266   classification = g_strdup_printf ("Codec/Decoder/%s",
267                     (codec->media_type == AVMEDIA_TYPE_VIDEO) ?
268                     "Video" : "Audio");
269   description = g_strdup_printf("%s Decoder", codec->name);
270
271   gst_element_class_set_details_simple (element_class,
272             longname,
273             classification,
274             description,
275             "Kitae Kim <kt920.kim@samsung.com>");
276
277   g_free (longname);
278   g_free (classification);
279   g_free (description);
280
281   sinkcaps = gst_maru_codecname_to_caps (codec->name, NULL, FALSE);
282   if (!sinkcaps) {
283     sinkcaps = gst_caps_from_string ("unknown/unknown");
284   }
285
286   switch (codec->media_type) {
287   case AVMEDIA_TYPE_VIDEO:
288     srccaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv");
289     break;
290   case AVMEDIA_TYPE_AUDIO:
291     srccaps = gst_maru_codectype_to_audio_caps (NULL, codec->name, FALSE, codec);
292     break;
293   default:
294     GST_LOG("unknown media type");
295     break;
296   }
297
298   if (!srccaps) {
299     srccaps = gst_caps_from_string ("unknown/unknown");
300   }
301
302   sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
303                 GST_PAD_ALWAYS, sinkcaps);
304   srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
305                 GST_PAD_ALWAYS, srccaps);
306
307   gst_element_class_add_pad_template (element_class, srctempl);
308   gst_element_class_add_pad_template (element_class, sinktempl);
309
310   klass->codec = codec;
311   klass->sinktempl = sinktempl;
312   klass->srctempl = srctempl;
313 }
314
315 static void
316 gst_marudec_class_init (GstMaruDecClass *klass)
317 {
318   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
319   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
320
321   parent_class = g_type_class_peek_parent (klass);
322
323 #if 0
324   gobject_class->set_property = gst_marudec_set_property
325   gobject_class->get_property = gst_marudec_get_property
326 #endif
327
328   gobject_class->finalize = gst_marudec_finalize;
329   gstelement_class->change_state = gst_marudec_change_state;
330 }
331
332 static void
333 gst_marudec_init (GstMaruDec *marudec)
334 {
335   GstMaruDecClass *oclass;
336
337   oclass = (GstMaruDecClass*) (G_OBJECT_GET_CLASS(marudec));
338
339   marudec->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
340   gst_pad_set_setcaps_function (marudec->sinkpad,
341     GST_DEBUG_FUNCPTR(gst_marudec_setcaps));
342   gst_pad_set_event_function (marudec->sinkpad,
343     GST_DEBUG_FUNCPTR(gst_marudec_sink_event));
344   gst_pad_set_chain_function (marudec->sinkpad,
345     GST_DEBUG_FUNCPTR(gst_marudec_chain));
346
347   marudec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src") ;
348   gst_pad_use_fixed_caps (marudec->srcpad);
349   gst_pad_set_event_function (marudec->srcpad,
350     GST_DEBUG_FUNCPTR(gst_marudec_src_event));
351
352   gst_element_add_pad (GST_ELEMENT(marudec), marudec->sinkpad);
353   gst_element_add_pad (GST_ELEMENT(marudec), marudec->srcpad);
354
355   marudec->context = g_malloc0 (sizeof(CodecContext));
356   marudec->context->video.pix_fmt = PIX_FMT_NONE;
357   marudec->context->audio.sample_fmt = SAMPLE_FMT_NONE;
358
359   marudec->opened = FALSE;
360   marudec->format.video.par_n = -1;
361   marudec->format.video.fps_n = -1;
362   marudec->format.video.old_fps_n = -1;
363
364   marudec->queued = NULL;
365   gst_segment_init (&marudec->segment, GST_FORMAT_TIME);
366 }
367
368 static void
369 gst_marudec_finalize (GObject *object)
370 {
371   GstMaruDec *marudec = (GstMaruDec *) object;
372
373   GST_DEBUG_OBJECT (marudec, "finalize object and release context");
374   g_free (marudec->context);
375   marudec->context = NULL;
376
377   G_OBJECT_CLASS (parent_class)->finalize (object);
378 }
379
380 static gboolean
381 gst_marudec_src_event (GstPad *pad, GstEvent *event)
382 {
383   GstMaruDec *marudec;
384   gboolean res;
385
386   marudec = (GstMaruDec *) gst_pad_get_parent (pad);
387
388   switch (GST_EVENT_TYPE (event)) {
389     /* Quality Of Service (QOS) event contains a report
390       about the current real-time performance of the stream.*/
391   case GST_EVENT_QOS:
392   {
393     gdouble proportion;
394     GstClockTimeDiff diff;
395     GstClockTime timestamp;
396
397     gst_event_parse_qos (event, &proportion, &diff, &timestamp);
398
399     /* update our QoS values */
400     gst_marudec_update_qos (marudec, proportion, timestamp + diff);
401     break;
402   }
403   default:
404     break;
405   }
406
407   /* forward upstream */
408   res = gst_pad_push_event (marudec->sinkpad, event);
409
410   gst_object_unref (marudec);
411
412   return res;
413 }
414
415 static gboolean
416 gst_marudec_sink_event (GstPad *pad, GstEvent *event)
417 {
418   GstMaruDec *marudec;
419   gboolean ret = FALSE;
420
421   marudec = (GstMaruDec *) gst_pad_get_parent (pad);
422
423   GST_DEBUG_OBJECT (marudec, "Handling %s event",
424     GST_EVENT_TYPE_NAME (event));
425
426   switch (GST_EVENT_TYPE (event)) {
427   case GST_EVENT_EOS:
428     gst_marudec_drain (marudec);
429     break;
430   case GST_EVENT_FLUSH_STOP:
431   {
432     if (marudec->opened) {
433       GST_DEBUG_OBJECT (marudec, "flush decoded buffers");
434       interface->flush_buffers (marudec->context, marudec->dev);
435     }
436
437     gst_marudec_reset_ts (marudec);
438     gst_marudec_reset_qos (marudec);
439     gst_segment_init (&marudec->segment, GST_FORMAT_TIME);
440     clear_queued (marudec);
441   }
442     break;
443   case GST_EVENT_NEWSEGMENT:
444   {
445     gboolean update;
446     GstFormat format;
447     gint64 start, stop, time;
448     gdouble rate, arate;
449
450     gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
451         &start, &stop, &time);
452
453     switch (format) {
454     case GST_FORMAT_TIME:
455       break;
456     case GST_FORMAT_BYTES:
457     {
458       gint bit_rate;
459       bit_rate = marudec->context->bit_rate;
460
461       if (!bit_rate) {
462         GST_WARNING_OBJECT (marudec, "no bitrate to convert BYTES to TIME");
463         gst_event_unref (event);
464         gst_object_unref (marudec);
465         return ret;
466       }
467
468       GST_DEBUG_OBJECT (marudec, "bitrate: %d", bit_rate);
469
470       if (start != -1) {
471         start = gst_util_uint64_scale_int (start, GST_SECOND, bit_rate);
472       }
473       if (stop != -1) {
474         stop = gst_util_uint64_scale_int (stop, GST_SECOND, bit_rate);
475       }
476       if (time != -1) {
477         time = gst_util_uint64_scale_int (time, GST_SECOND, bit_rate);
478       }
479
480       gst_event_unref (event);
481
482       format = GST_FORMAT_TIME;
483
484       stop = -1;
485       event = gst_event_new_new_segment (update, rate, format,
486           start, stop, time);
487       break;
488     }
489     default:
490       GST_WARNING_OBJECT (marudec, "unknown format received in NEWSEGMENT");
491       gst_event_unref (event);
492       gst_object_unref (marudec);
493       return ret;
494     }
495
496     if (marudec->opened) {
497       gst_marudec_drain (marudec);
498     }
499
500     GST_DEBUG_OBJECT (marudec,
501       "NEWSEGMENT in time start %" GST_TIME_FORMAT " -- stop %"
502       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
503
504     gst_segment_set_newsegment_full (&marudec->segment, update,
505         rate, arate, format, start, stop, time);
506     break;
507   }
508   default:
509     break;
510   }
511
512   ret = gst_pad_push_event (marudec->srcpad, event);
513
514   gst_object_unref (marudec);
515
516   return ret;
517 }
518
519
520
521 static gboolean
522 gst_marudec_setcaps (GstPad *pad, GstCaps *caps)
523 {
524   GstMaruDec *marudec;
525   GstMaruDecClass *oclass;
526   GstStructure *structure;
527   const GValue *par;
528   const GValue *fps;
529   gboolean ret = TRUE;
530
531   GST_DEBUG_OBJECT (pad, "setcaps called.");
532
533   marudec = (GstMaruDec *) (gst_pad_get_parent (pad));
534   oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
535
536   GST_OBJECT_LOCK (marudec);
537
538   if (marudec->opened) {
539     GST_OBJECT_UNLOCK (marudec);
540     gst_marudec_drain (marudec);
541     GST_OBJECT_LOCK (marudec);
542   }
543   gst_marudec_close (marudec);
544
545   GST_LOG_OBJECT (marudec, "size %dx%d", marudec->context->video.width,
546       marudec->context->video.height);
547
548   if (!strcmp(oclass->codec->name, "wmv3") ||
549       !strcmp(oclass->codec->name, "vc1")) {
550     gst_maru_caps_to_codecname (caps, oclass->codec->name, NULL);
551   }
552
553   gst_maru_caps_with_codecname (oclass->codec->name, oclass->codec->media_type,
554                                 caps, marudec->context);
555
556   GST_LOG_OBJECT (marudec, "size after %dx%d", marudec->context->video.width,
557       marudec->context->video.height);
558
559   if (!marudec->context->video.fps_d || !marudec->context->video.fps_n) {
560     GST_DEBUG_OBJECT (marudec, "forcing 25/1 framerate");
561     marudec->context->video.fps_n = 1;
562     marudec->context->video.fps_d = 25;
563   }
564
565   structure = gst_caps_get_structure (caps, 0);
566
567   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
568   if (par) {
569     GST_DEBUG_OBJECT (marudec, "sink caps have pixel-aspect-ratio of %d:%d",
570         gst_value_get_fraction_numerator (par),
571         gst_value_get_fraction_denominator (par));
572   }
573
574   fps = gst_structure_get_value (structure, "framerate");
575   if (fps != NULL && GST_VALUE_HOLDS_FRACTION (fps)) {
576     marudec->format.video.fps_n = gst_value_get_fraction_numerator (fps);
577     marudec->format.video.fps_d = gst_value_get_fraction_denominator (fps);
578     GST_DEBUG_OBJECT (marudec, "Using framerate %d/%d from incoming",
579         marudec->format.video.fps_n, marudec->format.video.fps_d);
580   } else {
581     marudec->format.video.fps_n = -1;
582     GST_DEBUG_OBJECT (marudec, "Using framerate from codec");
583   }
584
585 #if 0
586   if (strcmp (oclass->codec->name, "aac") == 0) {
587     const gchar *format = gst_structure_get_string (structure, "stream-format");
588     if (format == NULL || strcmp ("format", "raw") == 0) {
589       marudec->turnoff_parser = TRUE;
590     }
591   }
592 #endif
593
594   if (!gst_marudec_open (marudec)) {
595     GST_DEBUG_OBJECT (marudec, "Failed to open");
596     GST_OBJECT_UNLOCK (marudec);
597     gst_object_unref (marudec);
598
599     return FALSE;
600   }
601
602   gst_structure_get_int (structure, "width",
603     &marudec->format.video.clip_width);
604   gst_structure_get_int (structure, "height",
605     &marudec->format.video.clip_height);
606
607   GST_DEBUG_OBJECT (pad, "clipping to %dx%d",
608     marudec->format.video.clip_width, marudec->format.video.clip_height);
609
610   GST_OBJECT_UNLOCK (marudec);
611   gst_object_unref (marudec);
612
613   return ret;
614 }
615
616 static gboolean
617 gst_marudec_open (GstMaruDec *marudec)
618 {
619   GstMaruDecClass *oclass;
620
621   oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
622
623   marudec->dev = g_try_malloc0 (sizeof(CodecDevice));
624   if (!marudec->dev) {
625     GST_ERROR_OBJECT (marudec, "failed to allocate memory for CodecDevice");
626     return FALSE;
627   }
628
629   if (gst_maru_avcodec_open (marudec->context, oclass->codec, marudec->dev) < 0) {
630     g_free(marudec->dev);
631     marudec->dev = NULL;
632     GST_ERROR_OBJECT (marudec,
633       "maru_%sdec: Failed to open codec", oclass->codec->name);
634     return FALSE;
635   }
636
637   marudec->opened = TRUE;
638   GST_LOG_OBJECT (marudec, "Opened codec %s", oclass->codec->name);
639
640   switch (oclass->codec->media_type) {
641   case AVMEDIA_TYPE_VIDEO:
642     marudec->format.video.width = 0;
643     marudec->format.video.height = 0;
644     marudec->format.video.clip_width = -1;
645     marudec->format.video.clip_height = -1;
646     marudec->format.video.pix_fmt = PIX_FMT_NB;
647     marudec->format.video.interlaced = FALSE;
648     break;
649   case AVMEDIA_TYPE_AUDIO:
650     marudec->format.audio.samplerate = 0;
651     marudec->format.audio.channels = 0;
652     marudec->format.audio.depth = 0;
653     break;
654   default:
655     break;
656   }
657
658   gst_marudec_reset_ts (marudec);
659
660   marudec->proportion = 0.0;
661   marudec->earliest_time = -1;
662
663   return TRUE;
664 }
665
666 static void
667 gst_marudec_close (GstMaruDec *marudec)
668 {
669   if (!marudec->opened) {
670     GST_DEBUG_OBJECT (marudec, "not opened yet");
671     return;
672   }
673
674   if (marudec->context) {
675     g_free(marudec->context->codecdata);
676     marudec->context->codecdata = NULL;
677   }
678
679   if (!marudec->dev) {
680     return;
681   }
682
683   gst_maru_avcodec_close (marudec->context, marudec->dev);
684   marudec->opened = FALSE;
685
686   if (marudec->dev) {
687     g_free(marudec->dev);
688     marudec->dev = NULL;
689   }
690 }
691
692
693 static gboolean
694 gst_marudec_negotiate (GstMaruDec *marudec, gboolean force)
695 {
696   GstMaruDecClass *oclass;
697   GstCaps *caps;
698
699   oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
700
701   switch (oclass->codec->media_type) {
702   case AVMEDIA_TYPE_VIDEO:
703     if (!force && marudec->format.video.width == marudec->context->video.width
704       && marudec->format.video.height == marudec->context->video.height
705       && marudec->format.video.fps_n == marudec->format.video.old_fps_n
706       && marudec->format.video.fps_d == marudec->format.video.old_fps_d
707       && marudec->format.video.pix_fmt == marudec->context->video.pix_fmt
708       && marudec->format.video.par_n == marudec->context->video.par_n
709       && marudec->format.video.par_d == marudec->context->video.par_d) {
710       return TRUE;
711     }
712     marudec->format.video.width = marudec->context->video.width;
713     marudec->format.video.height = marudec->context->video.height;
714     marudec->format.video.old_fps_n = marudec->format.video.fps_n;
715     marudec->format.video.old_fps_d = marudec->format.video.fps_d;
716     marudec->format.video.pix_fmt = marudec->context->video.pix_fmt;
717     marudec->format.video.par_n = marudec->context->video.par_n;
718     marudec->format.video.par_d = marudec->context->video.par_d;
719     break;
720   case AVMEDIA_TYPE_AUDIO:
721   {
722     gint depth = gst_maru_smpfmt_depth (marudec->context->audio.sample_fmt);
723     if (!force && marudec->format.audio.samplerate ==
724       marudec->context->audio.sample_rate &&
725       marudec->format.audio.channels == marudec->context->audio.channels &&
726       marudec->format.audio.depth == depth) {
727       return TRUE;
728     }
729     marudec->format.audio.samplerate = marudec->context->audio.sample_rate;
730     marudec->format.audio.channels = marudec->context->audio.channels;
731     marudec->format.audio.depth = depth;
732   }
733     break;
734   default:
735     break;
736   }
737
738   caps =
739     gst_maru_codectype_to_caps (oclass->codec->media_type, marudec->context,
740       oclass->codec->name, FALSE);
741
742   if (caps == NULL) {
743     GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION,
744       ("Could not find GStreamer caps mapping for codec '%s'.",
745       oclass->codec->name), (NULL));
746     return FALSE;
747   }
748
749   switch (oclass->codec->media_type) {
750   case AVMEDIA_TYPE_VIDEO:
751   {
752     gint width, height;
753     gboolean interlaced;
754
755     width = marudec->format.video.clip_width;
756     height = marudec->format.video.clip_height;
757     interlaced = marudec->format.video.interlaced;
758
759     if (width != -1 && height != -1) {
760       if (width < marudec->context->video.width) {
761         gst_caps_set_simple (caps, "width", G_TYPE_INT, width, NULL);
762       }
763       if (height < marudec->context->video.height) {
764           gst_caps_set_simple (caps, "height", G_TYPE_INT, height, NULL);
765       }
766       gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced,
767         NULL);
768
769       if (marudec->format.video.fps_n != -1) {
770           gst_caps_set_simple (caps, "framerate",
771             GST_TYPE_FRACTION, marudec->format.video.fps_n,
772             marudec->format.video.fps_d, NULL);
773       }
774     }
775   }
776     break;
777   case AVMEDIA_TYPE_AUDIO:
778     break;
779   default:
780     break;
781   }
782
783   if (!gst_pad_set_caps (marudec->srcpad, caps)) {
784     GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION, (NULL),
785       ("Could not set caps for decoder (%s), not fixed?",
786       oclass->codec->name));
787     gst_caps_unref (caps);
788     return FALSE;
789   }
790
791   gst_caps_unref (caps);
792
793   return TRUE;
794 }
795
796 GstBuffer *
797 new_aligned_buffer (gint size, GstCaps *caps)
798 {
799   GstBuffer *buf;
800
801   buf = gst_buffer_new ();
802   GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = g_malloc0 (size);
803   GST_BUFFER_SIZE (buf) = size;
804   GST_BUFFER_FREE_FUNC (buf) = g_free;
805
806   if (caps) {
807     gst_buffer_set_caps (buf, caps);
808   }
809
810   return buf;
811 }
812
813 static GstFlowReturn
814 get_output_buffer (GstMaruDec *marudec, GstBuffer **outbuf)
815 {
816   gint pict_size;
817   GstFlowReturn ret;
818
819   ret = GST_FLOW_OK;
820
821   *outbuf = NULL;
822
823   if (G_UNLIKELY (!gst_marudec_negotiate (marudec, FALSE))) {
824     GST_DEBUG_OBJECT (marudec, "negotiate failed");
825     return GST_FLOW_NOT_NEGOTIATED;
826   }
827
828   pict_size = gst_maru_avpicture_size (marudec->context->video.pix_fmt,
829     marudec->context->video.width, marudec->context->video.height);
830   if (pict_size < 0) {
831     GST_DEBUG_OBJECT (marudec, "size of a picture is negative. "
832       "pixel format: %d, width: %d, height: %d",
833       marudec->context->video.pix_fmt, marudec->context->video.width,
834       marudec->context->video.height);
835     return GST_FLOW_ERROR;
836   }
837
838   GST_DEBUG_OBJECT (marudec, "outbuf size of decoded video %d", pict_size);
839
840   gst_pad_set_element_private(GST_PAD_PEER(marudec->srcpad), (gpointer)marudec);
841
842   /* GstPadBufferAllocFunction is mostly overridden by elements that can
843    * provide a hardware buffer in order to avoid additional memcpy operations.
844    */
845   gst_pad_set_bufferalloc_function(
846     GST_PAD_PEER(marudec->srcpad),
847     (GstPadBufferAllocFunction) interface->buffer_alloc_and_copy);
848
849   ret = gst_pad_alloc_buffer_and_set_caps (marudec->srcpad,
850     GST_BUFFER_OFFSET_NONE, pict_size,
851     GST_PAD_CAPS (marudec->srcpad), outbuf);
852   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
853     GST_DEBUG_OBJECT (marudec, "pad_alloc failed %d (%s)", ret,
854       gst_flow_get_name (ret));
855     return ret;
856   }
857
858   return ret;
859 }
860
861 static gboolean
862 clip_video_buffer (GstMaruDec *dec, GstBuffer *buf,
863     GstClockTime in_ts, GstClockTime in_dur)
864 {
865   gboolean res = TRUE;
866
867   return res;
868 }
869
870 static gboolean
871 clip_audio_buffer (GstMaruDec *dec, GstBuffer *buf,
872     GstClockTime in_ts, GstClockTime in_dur)
873 {
874   GstClockTime stop;
875   gint64 diff, cstart, cstop;
876   gboolean res = TRUE;
877
878   if (G_UNLIKELY (dec->segment.format != GST_FORMAT_TIME)) {
879     GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
880     return res;
881   }
882
883   // in_ts: in_timestamp. check a start time.
884   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts))) {
885     GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
886     return res;
887   }
888
889   stop =
890     GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE;
891
892   res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME, in_ts,
893                           stop, &cstart, &cstop);
894   if (G_UNLIKELY (!res)) {
895     GST_LOG_OBJECT (dec, "out of segment");
896     GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
897     return res;
898   }
899
900   if (G_UNLIKELY ((diff = cstart - in_ts) > 0)) {
901     diff =
902       gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, GST_SECOND) *
903         (dec->format.audio.depth * dec->format.audio.channels);
904
905     GST_DEBUG_OBJECT (dec, "clipping start to %" GST_TIME_FORMAT " %"
906         G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
907
908     GST_BUFFER_SIZE (buf) -= diff;
909     GST_BUFFER_DATA (buf) += diff;
910
911   }
912
913   if (G_UNLIKELY ((diff = stop - cstop) > 0)) {
914     diff =
915       gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, GST_SECOND) *
916         (dec->format.audio.depth * dec->format.audio.channels);
917
918     GST_DEBUG_OBJECT (dec, "clipping stop to %" GST_TIME_FORMAT " %"
919         G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstop), diff);
920
921     GST_BUFFER_SIZE (buf) -= diff;
922   }
923
924   GST_BUFFER_TIMESTAMP (buf) = cstart;
925   GST_BUFFER_DURATION (buf) = cstop - cstart;
926
927   GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
928   return res;
929 }
930
931 static gint
932 gst_marudec_video_frame (GstMaruDec *marudec, guint8 *data, guint size,
933     const GstTSInfo *dec_info, gint64 in_offset, GstBuffer **outbuf,
934     GstFlowReturn *ret)
935 {
936   gint len = -1;
937   gboolean mode_switch;
938   gboolean decode;
939   GstClockTime out_timestamp, out_duration, out_pts;
940   gint64 out_offset;
941   const GstTSInfo *out_info;
942   int have_data;
943
944   decode = gst_marudec_do_qos (marudec, dec_info->timestamp, &mode_switch);
945
946   GST_DEBUG_OBJECT (marudec, "decode video: input buffer size %d", size);
947
948   len = interface->decode_video (marudec, data, size,
949         dec_info->idx, in_offset, outbuf, &have_data);
950   if (len < 0 || !have_data) {
951     return len;
952   }
953
954   *ret = get_output_buffer (marudec, outbuf);
955   if (G_UNLIKELY (*ret != GST_FLOW_OK)) {
956     GST_DEBUG_OBJECT (marudec, "no output buffer");
957     len = -1;
958     GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
959       *ret, *outbuf, len);
960     return len;
961   }
962
963   out_info = gst_ts_info_get (marudec, dec_info->idx);
964   out_pts = out_info->timestamp;
965   out_duration = out_info->duration;
966   out_offset = out_info->offset;
967
968   /* Timestamps */
969   out_timestamp = -1;
970   if (out_pts != -1) {
971     out_timestamp = (GstClockTime) out_pts;
972     GST_LOG_OBJECT (marudec, "using timestamp %" GST_TIME_FORMAT
973       " returned by ffmpeg", GST_TIME_ARGS (out_timestamp));
974   }
975
976   if (!GST_CLOCK_TIME_IS_VALID (out_timestamp) && marudec->next_out != -1) {
977     out_timestamp = marudec->next_out;
978     GST_LOG_OBJECT (marudec, "using next timestamp %" GST_TIME_FORMAT,
979       GST_TIME_ARGS (out_timestamp));
980   }
981
982   if (!GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
983     out_timestamp = dec_info->timestamp;
984     GST_LOG_OBJECT (marudec, "using in timestamp %" GST_TIME_FORMAT,
985       GST_TIME_ARGS (out_timestamp));
986   }
987   GST_BUFFER_TIMESTAMP (*outbuf) = out_timestamp;
988
989   /* Offset */
990   if (out_offset != GST_BUFFER_OFFSET_NONE) {
991     GST_LOG_OBJECT (marudec, "Using offset returned by ffmpeg");
992   } else if (out_timestamp != GST_CLOCK_TIME_NONE) {
993     GstFormat out_fmt = GST_FORMAT_DEFAULT;
994     GST_LOG_OBJECT (marudec, "Using offset converted from timestamp");
995
996     gst_pad_query_peer_convert (marudec->sinkpad,
997       GST_FORMAT_TIME, out_timestamp, &out_fmt, &out_offset);
998   } else if (dec_info->offset != GST_BUFFER_OFFSET_NONE) {
999     GST_LOG_OBJECT (marudec, "using in_offset %" G_GINT64_FORMAT,
1000       dec_info->offset);
1001     out_offset = dec_info->offset;
1002   } else {
1003     GST_LOG_OBJECT (marudec, "no valid offset found");
1004     out_offset = GST_BUFFER_OFFSET_NONE;
1005   }
1006   GST_BUFFER_OFFSET (*outbuf) = out_offset;
1007
1008   /* Duration */
1009   if (GST_CLOCK_TIME_IS_VALID (out_duration)) {
1010     GST_LOG_OBJECT (marudec, "Using duration returned by ffmpeg");
1011   } else if (GST_CLOCK_TIME_IS_VALID (dec_info->duration)) {
1012     GST_LOG_OBJECT (marudec, "Using in_duration");
1013     out_duration = dec_info->duration;
1014   } else {
1015     if (marudec->format.video.fps_n != -1 &&
1016         (marudec->format.video.fps_n != 1000 &&
1017         marudec->format.video.fps_d != 1)) {
1018       GST_LOG_OBJECT (marudec, "using input framerate for duration");
1019       out_duration = gst_util_uint64_scale_int (GST_SECOND,
1020         marudec->format.video.fps_d, marudec->format.video.fps_n);
1021     } else {
1022       if (marudec->context->video.fps_n != 0 &&
1023           (marudec->context->video.fps_d > 0 &&
1024             marudec->context->video.fps_d < 1000)) {
1025         GST_LOG_OBJECT (marudec, "using decoder's framerate for duration");
1026         out_duration = gst_util_uint64_scale_int (GST_SECOND,
1027           marudec->context->video.fps_n * 1,
1028           marudec->context->video.fps_d);
1029       } else {
1030         GST_LOG_OBJECT (marudec, "no valid duration found");
1031       }
1032     }
1033   }
1034
1035   if (G_UNLIKELY (!clip_video_buffer (marudec, *outbuf, out_timestamp,
1036       out_duration))) {
1037     GST_DEBUG_OBJECT (marudec, "buffer clipped");
1038     gst_buffer_unref (*outbuf);
1039     *outbuf = NULL;
1040     GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1041       *ret, *outbuf, len);
1042     return len;
1043   }
1044
1045   GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1046     *ret, *outbuf, len);
1047   return len;
1048 }
1049
1050 static gint
1051 gst_marudec_audio_frame (GstMaruDec *marudec, CodecElement *codec,
1052                           guint8 *data, guint size,
1053                           const GstTSInfo *dec_info, GstBuffer **outbuf,
1054                           GstFlowReturn *ret)
1055 {
1056   gint len = -1;
1057   gint have_data = FF_MAX_AUDIO_FRAME_SIZE;
1058   GstClockTime out_timestamp, out_duration;
1059   gint64 out_offset;
1060
1061   *outbuf =
1062       new_aligned_buffer (FF_MAX_AUDIO_FRAME_SIZE,
1063           GST_PAD_CAPS (marudec->srcpad));
1064
1065   GST_DEBUG_OBJECT (marudec, "decode audio, input buffer size %d", size);
1066
1067   len = interface->decode_audio (marudec->context,
1068       (int16_t *) GST_BUFFER_DATA (*outbuf), &have_data,
1069       data, size, marudec->dev);
1070
1071   GST_DEBUG_OBJECT (marudec,
1072     "Decode audio: len=%d, have_data=%d", len, have_data);
1073
1074   if (len >= 0 && have_data > 0) {
1075     GST_DEBUG_OBJECT (marudec, "Creating output buffer");
1076     if (!gst_marudec_negotiate (marudec, FALSE)) {
1077       gst_buffer_unref (*outbuf);
1078       *outbuf = NULL;
1079       len = -1;
1080       GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1081         *ret, *outbuf, len);
1082       return len;
1083     }
1084
1085     GST_BUFFER_SIZE (*outbuf) = len;
1086
1087     if (GST_CLOCK_TIME_IS_VALID (dec_info->timestamp)) {
1088       out_timestamp = dec_info->timestamp;
1089     } else {
1090       out_timestamp = marudec->next_out;
1091     }
1092
1093     /* calculate based on number of samples */
1094     out_duration = gst_util_uint64_scale (have_data, GST_SECOND,
1095         marudec->format.audio.depth * marudec->format.audio.channels *
1096         marudec->format.audio.samplerate);
1097
1098     out_offset = dec_info->offset;
1099
1100     GST_DEBUG_OBJECT (marudec,
1101         "Buffer created. Size: %d, timestamp: %" GST_TIME_FORMAT
1102         ", duration: %" GST_TIME_FORMAT, have_data,
1103         GST_TIME_ARGS (out_timestamp), GST_TIME_ARGS (out_duration));
1104
1105     GST_BUFFER_TIMESTAMP (*outbuf) = out_timestamp;
1106     GST_BUFFER_DURATION (*outbuf) = out_duration;
1107     GST_BUFFER_OFFSET (*outbuf) = out_offset;
1108     gst_buffer_set_caps (*outbuf, GST_PAD_CAPS (marudec->srcpad));
1109
1110     if (GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
1111       marudec->next_out = out_timestamp + out_duration;
1112     }
1113
1114     if (G_UNLIKELY (!clip_audio_buffer (marudec, *outbuf,
1115         out_timestamp, out_duration))) {
1116       GST_DEBUG_OBJECT (marudec, "buffer_clipped");
1117       gst_buffer_unref (*outbuf);
1118       *outbuf = NULL;
1119       GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d", *ret, *outbuf, len);
1120       return len;
1121     }
1122   } else {
1123     gst_buffer_unref (*outbuf);
1124     *outbuf = NULL;
1125   }
1126
1127   if (len == -1 && !strcmp(codec->name, "aac")) {
1128     GST_ELEMENT_ERROR (marudec, STREAM, DECODE, (NULL),
1129         ("Decoding of AAC stream by FFMPEG failed."));
1130     *ret = GST_FLOW_ERROR;
1131   }
1132
1133   GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1134     *ret, *outbuf, len);
1135   return len;
1136 }
1137
1138 static gint
1139 gst_marudec_frame (GstMaruDec *marudec, guint8 *data, guint size,
1140     gint *got_data, const GstTSInfo *dec_info, gint64 in_offset, GstFlowReturn *ret)
1141 {
1142   GstMaruDecClass *oclass;
1143   GstBuffer *outbuf = NULL;
1144   gint have_data = 0, len = 0;
1145
1146   if (G_UNLIKELY (marudec->context->codec == NULL)) {
1147     GST_ERROR_OBJECT (marudec, "no codec context");
1148     return -1;
1149   }
1150
1151   *ret = GST_FLOW_OK;
1152   oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1153
1154   switch (oclass->codec->media_type) {
1155   case AVMEDIA_TYPE_VIDEO:
1156     len = gst_marudec_video_frame (marudec, data, size,
1157         dec_info, in_offset, &outbuf, ret);
1158     break;
1159   case AVMEDIA_TYPE_AUDIO:
1160     len = gst_marudec_audio_frame (marudec, oclass->codec, data, size,
1161         dec_info, &outbuf, ret);
1162     if (outbuf == NULL && marudec->discont) {
1163       GST_DEBUG_OBJECT (marudec, "no buffer but keeping timestamp");
1164     }
1165     break;
1166   default:
1167     GST_ERROR_OBJECT (marudec, "Asked to decode non-audio/video frame!");
1168     g_assert_not_reached ();
1169     break;
1170   }
1171
1172   if (outbuf) {
1173     have_data = 1;
1174   }
1175
1176   if (len < 0 || have_data < 0) {
1177     GST_WARNING_OBJECT (marudec,
1178         "maru_%sdec: decoding error (len: %d, have_data: %d)",
1179         oclass->codec->name, len, have_data);
1180     *got_data = 0;
1181     return len;
1182   } else if (len == 0 && have_data == 0) {
1183     *got_data = 0;
1184     return len;
1185   } else {
1186     *got_data = 1;
1187   }
1188
1189   if (outbuf) {
1190     GST_LOG_OBJECT (marudec,
1191         "Decoded data, now pushing buffer %p with offset %" G_GINT64_FORMAT
1192         ", timestamp %" GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT,
1193         outbuf, GST_BUFFER_OFFSET (outbuf),
1194         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1195         GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
1196
1197     if (marudec->discont) {
1198       /* GST_BUFFER_FLAG_DISCONT :
1199        * the buffer marks a data discontinuity in the stream. This typically
1200        * occurs after a seek or a dropped buffer from a live or network source.
1201        */
1202       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
1203       marudec->discont = FALSE;
1204     }
1205
1206     if (marudec->segment.rate > 0.0) {
1207       // push forward
1208       *ret = gst_pad_push (marudec->srcpad, outbuf);
1209     } else {
1210       // push reverse
1211       GST_DEBUG_OBJECT (marudec, "queued frame");
1212       marudec->queued = g_list_prepend (marudec->queued, outbuf);
1213       *ret = GST_FLOW_OK;
1214     }
1215   } else {
1216     GST_DEBUG_OBJECT (marudec, "Didn't get a decoded buffer");
1217   }
1218
1219   return len;
1220 }
1221
1222 static GstFlowReturn
1223 gst_marudec_chain (GstPad *pad, GstBuffer *buffer)
1224 {
1225   GstMaruDec *marudec;
1226   GstMaruDecClass *oclass;
1227   guint8 *in_buf;
1228   gint in_size, have_data;
1229   GstFlowReturn ret = GST_FLOW_OK;
1230   GstClockTime in_timestamp;
1231   GstClockTime in_duration;
1232   gboolean discont;
1233   gint64 in_offset;
1234   const GstTSInfo *in_info;
1235   const GstTSInfo *dec_info;
1236
1237   marudec = (GstMaruDec *) (GST_PAD_PARENT (pad));
1238
1239   if (G_UNLIKELY (!marudec->opened)) {
1240     // not_negotiated
1241     oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1242     GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION, (NULL),
1243       ("maru_%sdec: input format was not set before data start",
1244         oclass->codec->name));
1245     gst_buffer_unref (buffer);
1246     return GST_FLOW_NOT_NEGOTIATED;
1247   }
1248
1249   discont = GST_BUFFER_IS_DISCONT (buffer);
1250   if (G_UNLIKELY (discont)) {
1251     GST_DEBUG_OBJECT (marudec, "received DISCONT");
1252     gst_marudec_drain (marudec);
1253     interface->flush_buffers (marudec->context, marudec->dev);
1254     marudec->discont = TRUE;
1255     gst_marudec_reset_ts (marudec);
1256   }
1257
1258   oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1259
1260   in_timestamp = GST_BUFFER_TIMESTAMP (buffer);
1261   in_duration = GST_BUFFER_DURATION (buffer);
1262   in_offset = GST_BUFFER_OFFSET (buffer);
1263
1264   in_info = gst_ts_info_store (marudec, in_timestamp, in_duration, in_offset);
1265   GST_LOG_OBJECT (marudec,
1266     "Received new data of size %u, offset: %" G_GUINT64_FORMAT ", ts:%"
1267     GST_TIME_FORMAT ", dur: %" GST_TIME_FORMAT ", info %d",
1268     GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer),
1269     GST_TIME_ARGS (in_timestamp), GST_TIME_ARGS (in_duration), in_info->idx);
1270
1271   in_buf = GST_BUFFER_DATA (buffer);
1272   in_size = GST_BUFFER_SIZE (buffer);
1273
1274   dec_info = in_info;
1275
1276   gst_marudec_frame (marudec, in_buf, in_size, &have_data, dec_info, in_offset, &ret);
1277
1278   gst_buffer_unref (buffer);
1279
1280   return ret;
1281 }
1282
1283 static GstStateChangeReturn
1284 gst_marudec_change_state (GstElement *element, GstStateChange transition)
1285 {
1286   GstMaruDec *marudec = (GstMaruDec *) element;
1287   GstStateChangeReturn ret;
1288
1289   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1290
1291   switch (transition) {
1292   case GST_STATE_CHANGE_PAUSED_TO_READY:
1293     GST_OBJECT_LOCK (marudec);
1294     gst_marudec_close (marudec);
1295     GST_OBJECT_UNLOCK (marudec);
1296
1297     /* clear queue */
1298     clear_queued (marudec);
1299     break;
1300   default:
1301     break;
1302   }
1303
1304   return ret;
1305 }
1306
1307 gboolean
1308 gst_marudec_register (GstPlugin *plugin, GList *element)
1309 {
1310   GTypeInfo typeinfo = {
1311       sizeof (GstMaruDecClass),
1312       (GBaseInitFunc) gst_marudec_base_init,
1313       NULL,
1314       (GClassInitFunc) gst_marudec_class_init,
1315       NULL,
1316       NULL,
1317       sizeof (GstMaruDec),
1318       0,
1319       (GInstanceInitFunc) gst_marudec_init,
1320   };
1321
1322   GType type;
1323   gchar *type_name;
1324   gint rank = GST_RANK_PRIMARY;
1325   GList *elem = element;
1326   CodecElement *codec = NULL;
1327
1328   if (!elem) {
1329     return FALSE;
1330   }
1331
1332   /* register element */
1333   do {
1334     codec = (CodecElement *)(elem->data);
1335     if (!codec) {
1336       return FALSE;
1337     }
1338
1339     if (codec->codec_type != CODEC_TYPE_DECODE) {
1340       continue;
1341     }
1342
1343     type_name = g_strdup_printf ("maru_%sdec", codec->name);
1344     type = g_type_from_name (type_name);
1345     if (!type) {
1346       type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1347       g_type_set_qdata (type, GST_MARUDEC_PARAMS_QDATA, (gpointer) codec);
1348     }
1349
1350     if (!gst_element_register (plugin, type_name, rank, type)) {
1351       g_free (type_name);
1352       return FALSE;
1353     }
1354     g_free (type_name);
1355   } while ((elem = elem->next));
1356
1357   return TRUE;
1358 }