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