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