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