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