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