build: add '-Werror' Cflag option
[platform/adaptation/emulator/gst-plugins-emulator.git] / src / gstmaruviddec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /* Modifications by Samsung Electronics Co., Ltd.
22  * 1. Provide a hardware buffer in order to avoid additional memcpy operations.
23  */
24
25 #include "gstmarudevice.h"
26 #include "gstmaruutils.h"
27 #include "gstmaruinterface.h"
28
29 #define GST_MARUDEC_PARAMS_QDATA g_quark_from_static_string("marudec-params")
30
31 /* indicate dts, pts, offset in the stream */
32 #define GST_TS_INFO_NONE &ts_info_none
33 static const GstTSInfo ts_info_none = { -1, -1, -1, -1 };
34
35 typedef struct _GstMaruVidDecClass
36 {
37   GstVideoDecoderClass parent_class;
38
39   CodecElement *codec;
40 } GstMaruVidDecClass;
41
42 typedef struct _GstMaruDecClass
43 {
44   GstElementClass parent_class;
45
46   CodecElement *codec;
47   GstPadTemplate *sinktempl;
48   GstPadTemplate *srctempl;
49 } GstMaruDecClass;
50
51
52 static GstElementClass *parent_class = NULL;
53
54 static void gst_maruviddec_base_init (GstMaruVidDecClass *klass);
55 static void gst_maruviddec_class_init (GstMaruVidDecClass *klass);
56 static void gst_maruviddec_init (GstMaruVidDec *marudec);
57 static void gst_maruviddec_finalize (GObject *object);
58
59 static gboolean gst_marudec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state);
60 static GstFlowReturn gst_maruviddec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame);
61 static gboolean gst_marudec_negotiate (GstMaruVidDec *dec, gboolean force);
62 static gint gst_maruviddec_frame (GstMaruVidDec *marudec, guint8 *data, guint size, gint *got_data,
63                   const GstTSInfo *dec_info, gint64 in_offset, GstVideoCodecFrame * frame, GstFlowReturn *ret);
64
65 static gboolean gst_marudec_open (GstMaruVidDec *marudec);
66 static gboolean gst_marudec_close (GstMaruVidDec *marudec);
67
68 GstFlowReturn alloc_and_copy (GstMaruVidDec *marudec, guint64 offset, guint size,
69                   GstCaps *caps, GstBuffer **buf);
70
71 // for profile
72 static GTimer* profile_decode_timer = NULL;
73 static gdouble elapsed_decode_time = 0;
74 static int decoded_frame_cnt = 0;
75 static int last_frame_cnt = 0;
76 static GMutex profile_mutex;
77 static int profile_init = 0;
78
79 static gboolean
80 maru_profile_cb (gpointer user_data)
81 {
82   GST_DEBUG (" >> ENTER ");
83   int decoding_fps = 0;
84   gdouble decoding_time = 0;
85
86   g_mutex_lock (&profile_mutex);
87   if (decoded_frame_cnt < 0) {
88     decoded_frame_cnt = 0;
89     last_frame_cnt = 0;
90     elapsed_decode_time = 0;
91     g_mutex_unlock (&profile_mutex);
92     return FALSE;
93   }
94
95   decoding_fps = decoded_frame_cnt - last_frame_cnt;
96   last_frame_cnt = decoded_frame_cnt;
97
98   decoding_time = elapsed_decode_time;
99   elapsed_decode_time = 0;
100   g_mutex_unlock (&profile_mutex);
101
102   GST_DEBUG ("decoding fps=%d, latency=%f\n", decoding_fps, decoding_time/decoding_fps);
103   return TRUE;
104 }
105
106 static void init_codec_profile(void)
107 {
108   GST_DEBUG (" >> ENTER ");
109   if (!profile_init) {
110     profile_init = 1;
111     profile_decode_timer = g_timer_new();
112   }
113 }
114
115 static void reset_codec_profile(void)
116 {
117   GST_DEBUG (" >> ENTER ");
118   g_mutex_lock (&profile_mutex);
119   decoded_frame_cnt = -1;
120   g_mutex_lock (&profile_mutex);
121 }
122
123 static void begin_video_decode_profile(void)
124 {
125   GST_DEBUG (" >> ENTER ");
126   g_timer_start(profile_decode_timer);
127 }
128
129 static void end_video_decode_profile(void)
130 {
131   GST_DEBUG (" >> ENTER ");
132   g_timer_stop(profile_decode_timer);
133
134   g_mutex_lock (&profile_mutex);
135   if (decoded_frame_cnt == 0) {
136     g_timeout_add_seconds(1, maru_profile_cb, NULL);
137   }
138
139   elapsed_decode_time += g_timer_elapsed(profile_decode_timer, NULL);
140   decoded_frame_cnt++;
141   g_mutex_unlock (&profile_mutex);
142 }
143
144 #define INIT_CODEC_PROFILE(fd)              \
145   if (interface->get_profile_status(fd)) {  \
146     init_codec_profile();                   \
147   }
148 #define RESET_CODEC_PROFILE(s)  \
149   if (profile_init) {           \
150     reset_codec_profile();      \
151   }
152 #define BEGIN_VIDEO_DECODE_PROFILE()  \
153   if (profile_init) {                 \
154     begin_video_decode_profile();     \
155   }
156 #define END_VIDEO_DECODE_PROFILE()  \
157   if (profile_init) {               \
158     end_video_decode_profile();     \
159   }
160
161
162 static const GstTSInfo *
163 gst_ts_info_store (GstMaruVidDec *dec, GstClockTime timestamp,
164     GstClockTime duration, gint64 offset)
165 {
166   GST_DEBUG (" >> ENTER ");
167   gint idx = dec->ts_idx;
168   dec->ts_info[idx].idx = idx;
169   dec->ts_info[idx].timestamp = timestamp;
170   dec->ts_info[idx].duration = duration;
171   dec->ts_info[idx].offset = offset;
172   dec->ts_idx = (idx + 1) & MAX_TS_MASK;
173
174   return &dec->ts_info[idx];
175 }
176
177 static const GstTSInfo *
178 gst_ts_info_get (GstMaruVidDec *dec, gint idx)
179 {
180   GST_DEBUG (" >> ENTER ");
181   if (G_UNLIKELY (idx < 0 || idx > MAX_TS_MASK)){
182   GST_DEBUG (" >> LEAVE 0");
183     return GST_TS_INFO_NONE;
184   }
185
186   GST_DEBUG (" >> LEAVE ");
187   return &dec->ts_info[idx];
188 }
189
190 static void
191 gst_marudec_reset_ts (GstMaruVidDec *marudec)
192 {
193   GST_DEBUG (" >> ENTER ");
194   marudec->next_out = GST_CLOCK_TIME_NONE;
195 }
196
197 static void
198 gst_marudec_do_qos (GstMaruVidDec *marudec, GstVideoCodecFrame * frame,
199     GstClockTime timestamp, gboolean *mode_switch)
200 {
201   GST_DEBUG (" >> ENTER ");
202   GstClockTimeDiff diff;
203
204   *mode_switch = FALSE;
205
206   if (G_UNLIKELY (frame == NULL)) {
207     marudec->processed++;
208     GST_DEBUG (" >> LEAVE ");
209     return ;
210   }
211
212   diff = gst_video_decoder_get_max_decode_time (GST_VIDEO_DECODER (marudec), frame);
213   /* if we don't have timing info, then we don't do QoS */
214   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (diff))) {
215     marudec->processed++;
216     GST_DEBUG (" >> LEAVE ");
217     return ;
218   }
219
220   if (diff > 0) {
221     marudec->processed++;
222     GST_DEBUG (" >> LEAVE ");
223   } else if (diff <= 0) {
224     GST_DEBUG (" >> LEAVE ");
225   }
226 }
227
228 static void
229 gst_marudec_drain (GstMaruVidDec *marudec)
230 {
231   GST_DEBUG (" >> ENTER ");
232   GST_DEBUG_OBJECT (marudec, "drain frame");
233
234   {
235     gint have_data, len, try = 0;
236
237     do {
238       GstFlowReturn ret;
239
240       len =
241         gst_maruviddec_frame (marudec, NULL, 0, &have_data, &ts_info_none, 0, NULL, &ret);
242
243       if (len < 0 || have_data == 0) {
244         break;
245       }
246     } while (try++ < 10);
247   }
248 }
249
250 /*
251  * Implementation
252  */
253 static void
254 gst_maruviddec_base_init (GstMaruVidDecClass *klass)
255 {
256   GST_DEBUG (" >> ENTER ");
257   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
258   GstPadTemplate *sinktempl, *srctempl;
259   GstCaps *sinkcaps, *srccaps;
260   CodecElement *codec;
261   gchar *longname, *description;
262
263   codec =
264       (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
265                                       GST_MARUDEC_PARAMS_QDATA);
266   g_assert (codec != NULL);
267
268   longname = g_strdup_printf ("%s Decoder long", codec->longname);
269   description = g_strdup_printf("%s Decoder desc", codec->name);
270
271   gst_element_class_set_metadata (element_class,
272             longname,
273             "Codec/Decoder/Video sims",
274             description,
275             "Sooyoung Ha <yoosah.ha@samsung.com>");
276
277   g_free (longname);
278   g_free (description);
279
280   /* get the caps */
281   sinkcaps = gst_maru_codecname_to_caps (codec->name, NULL, FALSE);
282   if (!sinkcaps) {
283     sinkcaps = gst_caps_new_empty_simple ("unknown/unknown");
284   }
285
286   srccaps = gst_maru_codectype_to_video_caps (NULL, codec->name, FALSE, codec);
287
288   if (!srccaps) {
289     srccaps = gst_caps_from_string ("video/x-raw");
290   }
291
292   /* pad templates */
293   sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
294                 GST_PAD_ALWAYS, sinkcaps);
295   srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
296                 GST_PAD_ALWAYS, srccaps);
297
298   gst_element_class_add_pad_template (element_class, srctempl);
299   gst_element_class_add_pad_template (element_class, sinktempl);
300
301   klass->codec = codec;
302 }
303
304 static void
305 gst_maruviddec_class_init (GstMaruVidDecClass *klass)
306 {
307   GST_DEBUG (" >> ENTER ");
308   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
309   GstVideoDecoderClass *viddec_class = GST_VIDEO_DECODER_CLASS (klass);
310
311   parent_class = g_type_class_peek_parent (klass);
312
313   gobject_class->finalize = gst_maruviddec_finalize;
314
315   /* use these function when defines new properties.
316   gobject_class->set_property = gst_marudec_set_property
317   gobject_class->get_property = gst_marudec_get_property
318   */
319
320   viddec_class->set_format = gst_marudec_set_format;
321   viddec_class->handle_frame = gst_maruviddec_handle_frame;
322 }
323
324 static void
325 gst_maruviddec_init (GstMaruVidDec *marudec)
326 {
327   GST_DEBUG (" >> ENTER ");
328   marudec->context = g_malloc0 (sizeof(CodecContext));
329   marudec->context->video.pix_fmt = PIX_FMT_NONE;
330   marudec->context->audio.sample_fmt = SAMPLE_FMT_NONE;
331
332   marudec->opened = FALSE;
333 }
334
335 static void
336 gst_maruviddec_finalize (GObject *object)
337 {
338   GST_DEBUG (" >> ENTER ");
339   GstMaruVidDec *marudec = (GstMaruVidDec *) object;
340
341   GST_DEBUG_OBJECT (marudec, "finalize object and release context");
342   g_free (marudec->context);
343   marudec->context = NULL;
344
345   G_OBJECT_CLASS (parent_class)->finalize (object);
346 }
347
348 static gboolean
349 gst_marudec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
350 {
351   GST_DEBUG (" >> ENTER ");
352   GstMaruVidDec *marudec;
353   GstMaruVidDecClass *oclass;
354   gboolean ret = FALSE;
355
356   marudec = (GstMaruVidDec *) decoder;
357   oclass = (GstMaruVidDecClass *) (G_OBJECT_GET_CLASS (marudec));
358   if (!marudec) {
359       GST_ERROR ("invalid marudec");
360     return FALSE;
361   }
362   if (marudec->last_caps != NULL &&
363       gst_caps_is_equal (marudec->last_caps, state->caps)) {
364     return TRUE;
365   }
366
367   GST_DEBUG_OBJECT (marudec, "setcaps called.");
368
369   GST_OBJECT_LOCK (marudec);
370
371   /* stupid check for VC1 */
372   if (!strcmp(oclass->codec->name, "wmv3") ||
373       !strcmp(oclass->codec->name, "vc1")) {
374     gst_maru_caps_to_codecname (state->caps, oclass->codec->name, NULL);
375   }
376
377   /* close old session */
378   if (marudec->opened) {
379     GST_OBJECT_UNLOCK (marudec);
380     gst_marudec_drain (marudec);
381     GST_OBJECT_LOCK (marudec);
382     if (!gst_marudec_close (marudec)) {
383       GST_OBJECT_UNLOCK (marudec);
384       return FALSE;
385     }
386   }
387
388   gst_caps_replace (&marudec->last_caps, state->caps);
389
390   GST_LOG_OBJECT (marudec, "size %dx%d", marudec->context->video.width,
391       marudec->context->video.height);
392
393   gst_maru_caps_with_codecname (oclass->codec->name, oclass->codec->media_type,
394                                 state->caps, marudec->context);
395
396   GST_LOG_OBJECT (marudec, "size after %dx%d", marudec->context->video.width,
397       marudec->context->video.height);
398
399   if (!marudec->context->video.fps_d || !marudec->context->video.fps_n) {
400     GST_DEBUG_OBJECT (marudec, "forcing 25/1 framerate");
401     marudec->context->video.fps_n = 1;
402     marudec->context->video.fps_d = 25;
403   }
404
405   GstQuery *query;
406   gboolean is_live;
407
408   query = gst_query_new_latency ();
409   is_live = FALSE;
410   /* Check if upstream is live. If it isn't we can enable frame based
411    * threading, which is adding latency */
412   if (gst_pad_peer_query (GST_VIDEO_DECODER_SINK_PAD (marudec), query)) {
413     gst_query_parse_latency (query, &is_live, NULL, NULL);
414   }
415   gst_query_unref (query);
416
417   if (!gst_marudec_open (marudec)) {
418     GST_DEBUG_OBJECT (marudec, "Failed to open");
419
420     ret = FALSE;
421   } else {
422     ret = TRUE;
423   }
424   if (marudec->input_state) {
425     gst_video_codec_state_unref (marudec->input_state);
426   }
427   marudec->input_state = gst_video_codec_state_ref (state);
428
429   if (marudec->input_state->info.fps_n) {
430     GstVideoInfo *info = &marudec->input_state->info;
431     gst_util_uint64_scale_ceil (GST_SECOND, info->fps_d, info->fps_n);
432   }
433   GST_OBJECT_UNLOCK (marudec);
434
435   return ret;
436 }
437
438 static gboolean
439 gst_marudec_open (GstMaruVidDec *marudec)
440 {
441   GST_DEBUG (" >> ENTER ");
442   GstMaruVidDecClass *oclass;
443
444   oclass = (GstMaruVidDecClass *) (G_OBJECT_GET_CLASS (marudec));
445
446   marudec->dev = g_try_malloc0 (sizeof(CodecDevice));
447   if (!marudec->dev) {
448     GST_ERROR_OBJECT (marudec, "failed to allocate memory for CodecDevice");
449     return FALSE;
450   }
451
452   if (gst_maru_avcodec_open (marudec->context, oclass->codec, marudec->dev) < 0) {
453     g_free(marudec->dev);
454     marudec->dev = NULL;
455     GST_ERROR_OBJECT (marudec,
456       "maru_%sdec: Failed to open codec", oclass->codec->name);
457     return FALSE;
458   }
459
460   marudec->opened = TRUE;
461
462   GST_LOG_OBJECT (marudec, "Opened codec %s", oclass->codec->name);
463
464   gst_marudec_reset_ts (marudec);
465
466   // initialize profile resource
467   INIT_CODEC_PROFILE(marudec->dev->fd);
468
469   return TRUE;
470 }
471
472 static gboolean
473 gst_marudec_close (GstMaruVidDec *marudec)
474 {
475   GST_DEBUG (" >> ENTER ");
476   if (!marudec->opened) {
477     GST_DEBUG_OBJECT (marudec, "not opened yet");
478     return FALSE;
479   }
480
481   if (marudec->context) {
482     g_free(marudec->context->codecdata);
483     marudec->context->codecdata = NULL;
484   }
485
486   if (!marudec->dev) {
487     return FALSE;
488   }
489
490   gst_maru_avcodec_close (marudec->context, marudec->dev);
491   marudec->opened = FALSE;
492
493   if (marudec->dev) {
494     g_free(marudec->dev);
495     marudec->dev = NULL;
496   }
497
498   // reset profile resource
499   RESET_CODEC_PROFILE();
500   return TRUE;
501 }
502
503 static gboolean
504 update_video_context (GstMaruVidDec * marudec, CodecContext * context,
505     gboolean force)
506 {
507   GST_DEBUG (" >> ENTER ");
508   if (!force && marudec->ctx_width == context->video.width
509       && marudec->ctx_height == context->video.height
510       && marudec->ctx_ticks == context->video.ticks_per_frame
511       && marudec->ctx_time_n == context->video.fps_n
512       && marudec->ctx_time_d == context->video.fps_d
513       && marudec->ctx_pix_fmt == context->video.pix_fmt
514       && marudec->ctx_par_n == context->video.par_n
515       && marudec->ctx_par_d == context->video.par_d) {
516     return FALSE;
517   }
518   marudec->ctx_width = context->video.width;
519   marudec->ctx_height = context->video.height;
520   marudec->ctx_ticks = context->video.ticks_per_frame;
521   marudec->ctx_time_n = context->video.fps_n;
522   marudec->ctx_time_d = context->video.fps_d;
523   marudec->ctx_pix_fmt = context->video.pix_fmt;
524   marudec->ctx_par_n = context->video.par_n;
525   marudec->ctx_par_d = context->video.par_d;
526
527   return TRUE;
528 }
529
530 static void
531 gst_maruviddec_update_par (GstMaruVidDec * marudec,
532     GstVideoInfo * in_info, GstVideoInfo * out_info)
533 {
534   GST_DEBUG (" >> ENTER");
535   gboolean demuxer_par_set = FALSE;
536   gboolean decoder_par_set = FALSE;
537   gint demuxer_num = 1, demuxer_denom = 1;
538   gint decoder_num = 1, decoder_denom = 1;
539
540   if (in_info->par_n && in_info->par_d) {
541     demuxer_num = in_info->par_n;
542     demuxer_denom = in_info->par_d;
543     demuxer_par_set = TRUE;
544     GST_DEBUG_OBJECT (marudec, "Demuxer PAR: %d:%d", demuxer_num,
545         demuxer_denom);
546   }
547
548   if (marudec->ctx_par_n && marudec->ctx_par_d) {
549     decoder_num = marudec->ctx_par_n;
550     decoder_denom = marudec->ctx_par_d;
551     decoder_par_set = TRUE;
552     GST_DEBUG_OBJECT (marudec, "Decoder PAR: %d:%d", decoder_num,
553         decoder_denom);
554   }
555
556   if (!demuxer_par_set && !decoder_par_set)
557     goto no_par;
558
559   if (demuxer_par_set && !decoder_par_set)
560     goto use_demuxer_par;
561
562   if (decoder_par_set && !demuxer_par_set)
563     goto use_decoder_par;
564
565   /* Both the demuxer and the decoder provide a PAR. If one of
566    * the two PARs is 1:1 and the other one is not, use the one
567    * that is not 1:1. */
568   if (demuxer_num == demuxer_denom && decoder_num != decoder_denom)
569     goto use_decoder_par;
570
571   if (decoder_num == decoder_denom && demuxer_num != demuxer_denom)
572     goto use_demuxer_par;
573
574   /* Both PARs are non-1:1, so use the PAR provided by the demuxer */
575   goto use_demuxer_par;
576
577 use_decoder_par:
578   {
579     GST_DEBUG_OBJECT (marudec,
580         "Setting decoder provided pixel-aspect-ratio of %u:%u", decoder_num,
581         decoder_denom);
582     out_info->par_n = decoder_num;
583     out_info->par_d = decoder_denom;
584     return;
585   }
586 use_demuxer_par:
587   {
588     GST_DEBUG_OBJECT (marudec,
589         "Setting demuxer provided pixel-aspect-ratio of %u:%u", demuxer_num,
590         demuxer_denom);
591     out_info->par_n = demuxer_num;
592     out_info->par_d = demuxer_denom;
593     return;
594   }
595 no_par:
596   {
597     GST_DEBUG_OBJECT (marudec,
598         "Neither demuxer nor codec provide a pixel-aspect-ratio");
599     out_info->par_n = 1;
600     out_info->par_d = 1;
601     return;
602   }
603 }
604
605
606 static gboolean
607 gst_marudec_negotiate (GstMaruVidDec *marudec, gboolean force)
608 {
609   GST_DEBUG (" >> ENTER ");
610   CodecContext *context = marudec->context;
611   GstVideoFormat fmt;
612   GstVideoInfo *in_info, *out_info;
613   GstVideoCodecState *output_state;
614   gint fps_n, fps_d;
615
616   if (!update_video_context (marudec, context, force))
617     return TRUE;
618
619   fmt = gst_maru_pixfmt_to_videoformat (marudec->ctx_pix_fmt);
620   if (G_UNLIKELY (fmt == GST_VIDEO_FORMAT_UNKNOWN))
621     goto unknown_format;
622
623   output_state =
624       gst_video_decoder_set_output_state (GST_VIDEO_DECODER (marudec), fmt,
625       marudec->ctx_width, marudec->ctx_height, marudec->input_state);
626   if (marudec->output_state)
627     gst_video_codec_state_unref (marudec->output_state);
628   marudec->output_state = output_state;
629
630   in_info = &marudec->input_state->info;
631   out_info = &marudec->output_state->info;
632   out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
633
634   /* try to find a good framerate */
635   if ((in_info->fps_d && in_info->fps_n) ||
636       GST_VIDEO_INFO_FLAG_IS_SET (in_info, GST_VIDEO_FLAG_VARIABLE_FPS)) {
637     /* take framerate from input when it was specified (#313970) */
638     fps_n = in_info->fps_n;
639     fps_d = in_info->fps_d;
640   } else {
641     fps_n = marudec->ctx_time_d / marudec->ctx_ticks;
642     fps_d = marudec->ctx_time_n;
643
644     if (!fps_d) {
645       GST_LOG_OBJECT (marudec, "invalid framerate: %d/0, -> %d/1", fps_n,
646           fps_n);
647       fps_d = 1;
648     }
649     if (gst_util_fraction_compare (fps_n, fps_d, 1000, 1) > 0) {
650       GST_LOG_OBJECT (marudec, "excessive framerate: %d/%d, -> 0/1", fps_n,
651           fps_d);
652       fps_n = 0;
653       fps_d = 1;
654     }
655   }
656   GST_LOG_OBJECT (marudec, "setting framerate: %d/%d", fps_n, fps_d);
657   out_info->fps_n = fps_n;
658   out_info->fps_d = fps_d;
659
660   /* calculate and update par now */
661   gst_maruviddec_update_par (marudec, in_info, out_info);
662
663   if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (marudec)))
664     goto negotiate_failed;
665
666   return TRUE;
667
668   /* ERRORS */
669 unknown_format:
670   {
671     GST_ERROR_OBJECT (marudec,
672         "decoder requires a video format unsupported by GStreamer");
673     return FALSE;
674   }
675 negotiate_failed:
676   {
677     /* Reset so we try again next time even if force==FALSE */
678     marudec->ctx_width = 0;
679     marudec->ctx_height = 0;
680     marudec->ctx_ticks = 0;
681     marudec->ctx_time_n = 0;
682     marudec->ctx_time_d = 0;
683     marudec->ctx_pix_fmt = 0;
684     marudec->ctx_par_n = 0;
685     marudec->ctx_par_d = 0;
686
687     GST_ERROR_OBJECT (marudec, "negotiation failed");
688     return FALSE;
689   }
690 }
691
692 static GstFlowReturn
693 get_output_buffer (GstMaruVidDec *marudec, GstVideoCodecFrame * frame)
694 {
695   GST_DEBUG (" >> ENTER ");
696   gint pict_size;
697   GstFlowReturn ret = GST_FLOW_OK;
698
699   if (G_UNLIKELY (!gst_marudec_negotiate (marudec, FALSE))) {
700     GST_DEBUG_OBJECT (marudec, "negotiate failed");
701     return GST_FLOW_NOT_NEGOTIATED;
702   }
703   pict_size = gst_maru_avpicture_size (marudec->context->video.pix_fmt,
704     marudec->context->video.width, marudec->context->video.height);
705   if (pict_size < 0) {
706     GST_DEBUG_OBJECT (marudec, "size of a picture is negative. "
707       "pixel format: %d, width: %d, height: %d",
708       marudec->context->video.pix_fmt, marudec->context->video.width,
709       marudec->context->video.height);
710     return GST_FLOW_ERROR;
711   }
712
713   GST_DEBUG_OBJECT (marudec, "outbuf size of decoded video %d", pict_size);
714
715   ret = gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (marudec), frame);
716
717   alloc_and_copy(marudec, 0, pict_size, NULL, &(frame->output_buffer));
718
719   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
720     GST_ERROR ("alloc output buffer failed");
721   }
722
723   return ret;
724 }
725
726 static gint
727 gst_maruviddec_video_frame (GstMaruVidDec *marudec, guint8 *data, guint size,
728     const GstTSInfo *dec_info, gint64 in_offset,
729     GstVideoCodecFrame * frame, GstFlowReturn *ret)
730 {
731   GST_DEBUG (" >> ENTER ");
732   gint len = -1;
733   gboolean mode_switch;
734   GstClockTime out_timestamp, out_duration, out_pts;
735   gint64 out_offset;
736   const GstTSInfo *out_info;
737   int have_data;
738
739   /* run QoS code, we don't stop decoding the frame when we are late because
740    * else we might skip a reference frame */
741   gst_marudec_do_qos (marudec, frame, dec_info->timestamp, &mode_switch);
742
743   GST_DEBUG_OBJECT (marudec, "decode video: input buffer size %d", size);
744
745   // begin video decode profile
746   BEGIN_VIDEO_DECODE_PROFILE();
747
748   len = interface->decode_video (marudec, data, size,
749         dec_info->idx, in_offset, NULL, &have_data);
750   if (len < 0 || !have_data) {
751     GST_ERROR ("decode video failed, len = %d", len);
752     return len;
753   }
754
755   // end video decode profile
756   END_VIDEO_DECODE_PROFILE();
757
758   *ret = get_output_buffer (marudec, frame);
759   if (G_UNLIKELY (*ret != GST_FLOW_OK)) {
760     GST_DEBUG_OBJECT (marudec, "no output buffer");
761     len = -1;
762     GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
763       *ret, (void *) (frame->output_buffer), len);
764     return len;
765   }
766
767   out_info = gst_ts_info_get (marudec, dec_info->idx);
768   out_pts = out_info->timestamp;
769   out_duration = out_info->duration;
770   out_offset = out_info->offset;
771
772   /* Timestamps */
773   out_timestamp = -1;
774   if (out_pts != -1) {
775     out_timestamp = (GstClockTime) out_pts;
776     GST_LOG_OBJECT (marudec, "using timestamp %" GST_TIME_FORMAT
777       " returned by ffmpeg", GST_TIME_ARGS (out_timestamp));
778   }
779
780   if (!GST_CLOCK_TIME_IS_VALID (out_timestamp) && marudec->next_out != -1) {
781     out_timestamp = marudec->next_out;
782     GST_LOG_OBJECT (marudec, "using next timestamp %" GST_TIME_FORMAT,
783       GST_TIME_ARGS (out_timestamp));
784   }
785
786   if (!GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
787     out_timestamp = dec_info->timestamp;
788     GST_LOG_OBJECT (marudec, "using in timestamp %" GST_TIME_FORMAT,
789       GST_TIME_ARGS (out_timestamp));
790   }
791   GST_BUFFER_TIMESTAMP (frame->output_buffer) = out_timestamp;
792
793   /* Offset */
794   if (out_offset != GST_BUFFER_OFFSET_NONE) {
795     GST_LOG_OBJECT (marudec, "Using offset returned by ffmpeg");
796   } else if (out_timestamp != GST_CLOCK_TIME_NONE) {
797 /* TODO: check this is needed.
798     GstFormat out_fmt = GST_FORMAT_DEFAULT;
799     GST_LOG_OBJECT (marudec, "Using offset converted from timestamp");
800
801     gst_pad_query_peer_convert (marudec->sinkpad,
802       GST_FORMAT_TIME, out_timestamp, &out_fmt, &out_offset);
803 */
804   } else if (dec_info->offset != GST_BUFFER_OFFSET_NONE) {
805     GST_LOG_OBJECT (marudec, "using in_offset %" G_GINT64_FORMAT,
806       dec_info->offset);
807     out_offset = dec_info->offset;
808   } else {
809     GST_LOG_OBJECT (marudec, "no valid offset found");
810     out_offset = GST_BUFFER_OFFSET_NONE;
811   }
812   GST_BUFFER_OFFSET (frame->output_buffer) = out_offset;
813
814   /* Duration */
815   if (GST_CLOCK_TIME_IS_VALID (out_duration)) {
816     GST_LOG_OBJECT (marudec, "Using duration returned by ffmpeg");
817   } else if (GST_CLOCK_TIME_IS_VALID (dec_info->duration)) {
818     GST_LOG_OBJECT (marudec, "Using in_duration");
819     out_duration = dec_info->duration;
820   } else {
821     if (marudec->ctx_time_n != -1 &&
822         (marudec->ctx_time_n != 1000 &&
823         marudec->ctx_time_d != 1)) {
824       GST_LOG_OBJECT (marudec, "using input framerate for duration");
825       out_duration = gst_util_uint64_scale_int (GST_SECOND,
826         marudec->ctx_time_d, marudec->ctx_time_n);
827     } else {
828       if (marudec->context->video.fps_n != 0 &&
829           (marudec->context->video.fps_d > 0 &&
830             marudec->context->video.fps_d < 1000)) {
831         GST_LOG_OBJECT (marudec, "using decoder's framerate for duration");
832         out_duration = gst_util_uint64_scale_int (GST_SECOND,
833           marudec->context->video.fps_n * 1,
834           marudec->context->video.fps_d);
835       } else {
836         GST_LOG_OBJECT (marudec, "no valid duration found");
837       }
838     }
839   }
840
841   GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
842     *ret, (void *) (frame->output_buffer), len);
843
844  *ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (marudec), frame);
845
846   return len;
847 }
848
849 static gint
850 gst_maruviddec_frame (GstMaruVidDec *marudec, guint8 *data, guint size,
851     gint *got_data, const GstTSInfo *dec_info, gint64 in_offset,
852     GstVideoCodecFrame * frame, GstFlowReturn *ret)
853 {
854   GST_DEBUG (" >> ENTER ");
855   GstMaruVidDecClass *oclass;
856   gint have_data = 0, len = 0;
857
858   if (G_UNLIKELY (marudec->context->codec == NULL)) {
859     GST_ERROR_OBJECT (marudec, "no codec context");
860     return -1;
861   }
862   GST_LOG_OBJECT (marudec, "data:%p, size:%d", data, size);
863
864   *ret = GST_FLOW_OK;
865   oclass = (GstMaruVidDecClass *) (G_OBJECT_GET_CLASS (marudec));
866
867   switch (oclass->codec->media_type) {
868   case AVMEDIA_TYPE_VIDEO:
869     len = gst_maruviddec_video_frame (marudec, data, size,
870         dec_info, in_offset, frame, ret);
871     break;
872   default:
873     GST_ERROR_OBJECT (marudec, "Asked to decode non-audio/video frame!");
874     g_assert_not_reached ();
875     break;
876   }
877
878   if (frame && frame->output_buffer) {
879     have_data = 1;
880   }
881
882   if (len < 0 || have_data < 0) {
883     GST_WARNING_OBJECT (marudec,
884         "maru_%sdec: decoding error (len: %d, have_data: %d)",
885         oclass->codec->name, len, have_data);
886     *got_data = 0;
887     return len;
888   } else if (len == 0 && have_data == 0) {
889     *got_data = 0;
890     return len;
891   } else {
892     *got_data = 1;
893   }
894
895   return len;
896 }
897
898 static GstFlowReturn
899 gst_maruviddec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
900 {
901   GST_DEBUG (" >> ENTER ");
902   GstMaruVidDec *marudec = (GstMaruVidDec *) decoder;
903   gint have_data;
904   GstMapInfo mapinfo;
905   GstFlowReturn ret = GST_FLOW_OK;
906
907   guint8 *in_buf;
908   gint in_size;
909   GstClockTime in_timestamp;
910   GstClockTime in_duration;
911   gint64 in_offset;
912   const GstTSInfo *in_info;
913   const GstTSInfo *dec_info;
914
915   if (!gst_buffer_map (frame->input_buffer, &mapinfo, GST_MAP_READ)) {
916     GST_ERROR_OBJECT (marudec, "Failed to map buffer");
917     return GST_FLOW_ERROR;
918   }
919
920   in_timestamp = GST_BUFFER_TIMESTAMP (frame->input_buffer);
921   in_duration = GST_BUFFER_DURATION (frame->input_buffer);
922   in_offset = GST_BUFFER_OFFSET (frame->input_buffer);
923
924   in_info = gst_ts_info_store (marudec, in_timestamp, in_duration, in_offset);
925   GST_LOG_OBJECT (marudec,
926     "Received new data of size %zu, offset: %" G_GUINT64_FORMAT ", ts:%"
927     GST_TIME_FORMAT ", dur: %" GST_TIME_FORMAT ", info %d",
928     mapinfo.size, GST_BUFFER_OFFSET (frame->input_buffer),
929     GST_TIME_ARGS (in_timestamp), GST_TIME_ARGS (in_duration), in_info->idx);
930
931   in_size = mapinfo.size;
932   in_buf = (guint8 *) mapinfo.data;
933
934   dec_info = in_info;
935
936   gst_buffer_unmap (frame->input_buffer, &mapinfo);
937
938   gst_maruviddec_frame (marudec, in_buf, in_size, &have_data, dec_info, in_offset, frame, &ret);
939
940   return ret;
941 }
942
943 gboolean
944 gst_maruviddec_register (GstPlugin *plugin, GList *element)
945 {
946   GTypeInfo typeinfo = {
947       sizeof (GstMaruVidDecClass),
948       (GBaseInitFunc) gst_maruviddec_base_init,
949       NULL,
950       (GClassInitFunc) gst_maruviddec_class_init,
951       NULL,
952       NULL,
953       sizeof (GstMaruVidDec),
954       0,
955       (GInstanceInitFunc) gst_maruviddec_init,
956   };
957
958   GType type;
959   gchar *type_name;
960   gint rank = GST_RANK_PRIMARY;
961   GList *elem = element;
962   CodecElement *codec = NULL;
963
964   if (!elem) {
965     return FALSE;
966   }
967
968   /* register element */
969   do {
970     codec = (CodecElement *)(elem->data);
971     if (!codec) {
972       return FALSE;
973     }
974
975     if (codec->codec_type != CODEC_TYPE_DECODE || codec->media_type != AVMEDIA_TYPE_VIDEO) {
976       continue;
977     }
978
979     type_name = g_strdup_printf ("maru_%sdec", codec->name);
980     type = g_type_from_name (type_name);
981     if (!type) {
982       type = g_type_register_static (GST_TYPE_VIDEO_DECODER, type_name, &typeinfo, 0);
983       g_type_set_qdata (type, GST_MARUDEC_PARAMS_QDATA, (gpointer) codec);
984     }
985
986     if (!gst_element_register (plugin, type_name, rank, type)) {
987       g_free (type_name);
988       return FALSE;
989     }
990     g_free (type_name);
991   } while ((elem = elem->next));
992
993   return TRUE;
994 }