2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
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.
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.
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.
21 /* Modifications by Samsung Electronics Co., Ltd.
22 * 1. Provide a hardware buffer in order to avoid additional memcpy operations.
25 #include "gstmarudevice.h"
26 #include "gstmaruutils.h"
27 #include "gstmaruinterface.h"
29 #define GST_MARUDEC_PARAMS_QDATA g_quark_from_static_string("marudec-params")
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 };
35 typedef struct _GstMaruDecClass
37 GstElementClass parent_class;
40 GstPadTemplate *sinktempl;
41 GstPadTemplate *srctempl;
45 static GstElementClass *parent_class = NULL;
47 static void gst_marudec_base_init (GstMaruDecClass *klass);
48 static void gst_marudec_class_init (GstMaruDecClass *klass);
49 static void gst_marudec_init (GstMaruDec *marudec);
50 static void gst_marudec_finalize (GObject *object);
52 static gboolean gst_marudec_setcaps (GstPad *pad, GstCaps *caps);
55 static gboolean gst_marudec_sink_event (GstPad *pad, GstEvent *event);
56 static GstFlowReturn gst_marudec_chain (GstPad *pad, GstBuffer *buffer);
59 static gboolean gst_marudec_src_event (GstPad *pad, GstEvent *event);
60 static GstStateChangeReturn gst_marudec_change_state (GstElement *element,
61 GstStateChange transition);
63 static gboolean gst_marudec_negotiate (GstMaruDec *dec, gboolean force);
65 static gint gst_marudec_frame (GstMaruDec *marudec, guint8 *data,
66 guint size, gint *got_data,
67 const GstTSInfo *dec_info, gint64 in_offset, GstFlowReturn *ret);
69 static gboolean gst_marudec_open (GstMaruDec *marudec);
70 static int gst_marudec_close (GstMaruDec *marudec);
73 static const GstTSInfo *
74 gst_ts_info_store (GstMaruDec *dec, GstClockTime timestamp,
75 GstClockTime duration, gint64 offset)
77 gint idx = dec->ts_idx;
78 dec->ts_info[idx].idx = idx;
79 dec->ts_info[idx].timestamp = timestamp;
80 dec->ts_info[idx].duration = duration;
81 dec->ts_info[idx].offset = offset;
82 dec->ts_idx = (idx + 1) & MAX_TS_MASK;
84 return &dec->ts_info[idx];
87 static const GstTSInfo *
88 gst_ts_info_get (GstMaruDec *dec, gint idx)
90 if (G_UNLIKELY (idx < 0 || idx > MAX_TS_MASK))
91 return GST_TS_INFO_NONE;
93 return &dec->ts_info[idx];
97 gst_marudec_reset_ts (GstMaruDec *marudec)
99 marudec->next_out = GST_CLOCK_TIME_NONE;
103 gst_marudec_update_qos (GstMaruDec *marudec, gdouble proportion,
104 GstClockTime timestamp)
106 GST_LOG_OBJECT (marudec, "update QOS: %f, %" GST_TIME_FORMAT,
107 proportion, GST_TIME_ARGS (timestamp));
109 GST_OBJECT_LOCK (marudec);
110 marudec->proportion = proportion;
111 marudec->earliest_time = timestamp;
112 GST_OBJECT_UNLOCK (marudec);
116 gst_marudec_reset_qos (GstMaruDec *marudec)
118 gst_marudec_update_qos (marudec, 0.5, GST_CLOCK_TIME_NONE);
119 marudec->processed = 0;
120 marudec->dropped = 0;
124 gst_marudec_do_qos (GstMaruDec *marudec, GstClockTime timestamp,
125 gboolean *mode_switch)
127 GstClockTimeDiff diff;
129 GstClockTime qostime, earliest_time;
132 *mode_switch = FALSE;
134 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
135 marudec->processed++;
139 proportion = marudec->proportion;
140 earliest_time = marudec->earliest_time;
142 qostime = gst_segment_to_running_time (&marudec->segment, GST_FORMAT_TIME,
145 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime))) {
146 marudec->processed++;
150 diff = GST_CLOCK_DIFF (qostime, earliest_time);
152 if (proportion < 0.4 && diff < 0 ){
153 marudec->processed++;
157 // if (marudec->waiting_for_key) {
166 GstClockTime stream_time, jitter;
171 gst_segment_to_stream_time (&marudec->segment, GST_FORMAT_TIME,
173 jitter = GST_CLOCK_DIFF (qostime, earliest_time);
175 gst_message_new_qos (GST_OBJECT_CAST (marudec), FALSE, qostime,
176 stream_time, timestamp, GST_CLOCK_TIME_NONE);
177 gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
178 gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS,
179 marudec->processed, marudec->dropped);
180 gst_element_post_message (GST_ELEMENT_CAST (marudec), qos_msg);
186 marudec->processed++;
191 clear_queued (GstMaruDec *marudec)
193 g_list_foreach (marudec->queued, (GFunc) gst_mini_object_unref, NULL);
194 g_list_free (marudec->queued);
195 marudec->queued = NULL;
199 flush_queued (GstMaruDec *marudec)
201 GstFlowReturn res = GST_FLOW_OK;
203 GST_DEBUG_OBJECT (marudec, "flush queued");
205 while (marudec->queued) {
206 GstBuffer *buf = GST_BUFFER_CAST (marudec->queued->data);
208 GST_LOG_OBJECT (marudec, "pushing buffer %p, offset %"
209 G_GUINT64_FORMAT ", timestamp %"
210 GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, buf,
211 GST_BUFFER_OFFSET (buf),
212 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
213 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
215 res = gst_pad_push (marudec->srcpad, buf);
218 g_list_delete_link (marudec->queued, marudec->queued);
225 gst_marudec_drain (GstMaruDec *marudec)
228 GstMaruDecClass *oclass;
229 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
232 GST_DEBUG_OBJECT (marudec, "drain frame");
234 gint have_data, len, try = 0;
240 gst_marudec_frame (marudec, NULL, 0, &have_data, &ts_info_none, 0, &ret);
242 if (len < 0 || have_data == 0) {
245 } while (try++ < 10);
248 if (marudec->segment.rate < 0.0) {
249 GST_DEBUG_OBJECT (marudec, "reverse playback");
250 flush_queued (marudec);
258 gst_marudec_base_init (GstMaruDecClass *klass)
260 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
261 GstCaps *sinkcaps = NULL, *srccaps = NULL;
262 GstPadTemplate *sinktempl, *srctempl;
264 gchar *longname, *classification, *description;
267 (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
268 GST_MARUDEC_PARAMS_QDATA);
270 longname = g_strdup_printf ("%s Decoder", codec->longname);
271 classification = g_strdup_printf ("Codec/Decoder/%s",
272 (codec->media_type == AVMEDIA_TYPE_VIDEO) ?
274 description = g_strdup_printf("%s Decoder", codec->name);
276 gst_element_class_set_details_simple (element_class,
280 "Kitae Kim <kt920.kim@samsung.com>");
283 g_free (classification);
284 g_free (description);
286 sinkcaps = gst_maru_codecname_to_caps (codec->name, NULL, FALSE);
288 sinkcaps = gst_caps_from_string ("unknown/unknown");
291 switch (codec->media_type) {
292 case AVMEDIA_TYPE_VIDEO:
293 srccaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv");
295 case AVMEDIA_TYPE_AUDIO:
296 srccaps = gst_maru_codectype_to_audio_caps (NULL, codec->name, FALSE, codec);
299 GST_LOG("unknown media type.\n");
304 srccaps = gst_caps_from_string ("unknown/unknown");
307 sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
308 GST_PAD_ALWAYS, sinkcaps);
309 srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
310 GST_PAD_ALWAYS, srccaps);
312 gst_element_class_add_pad_template (element_class, srctempl);
313 gst_element_class_add_pad_template (element_class, sinktempl);
315 klass->codec = codec;
316 klass->sinktempl = sinktempl;
317 klass->srctempl = srctempl;
321 gst_marudec_class_init (GstMaruDecClass *klass)
323 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
324 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
326 parent_class = g_type_class_peek_parent (klass);
329 gobject_class->set_property = gst_marudec_set_property
330 gobject_class->get_property = gst_marudec_get_property
333 gobject_class->finalize = gst_marudec_finalize;
334 gstelement_class->change_state = gst_marudec_change_state;
338 gst_marudec_init (GstMaruDec *marudec)
340 GstMaruDecClass *oclass;
342 oclass = (GstMaruDecClass*) (G_OBJECT_GET_CLASS(marudec));
344 marudec->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
345 gst_pad_set_setcaps_function (marudec->sinkpad,
346 GST_DEBUG_FUNCPTR(gst_marudec_setcaps));
347 gst_pad_set_event_function (marudec->sinkpad,
348 GST_DEBUG_FUNCPTR(gst_marudec_sink_event));
349 gst_pad_set_chain_function (marudec->sinkpad,
350 GST_DEBUG_FUNCPTR(gst_marudec_chain));
352 marudec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src") ;
353 gst_pad_use_fixed_caps (marudec->srcpad);
354 gst_pad_set_event_function (marudec->srcpad,
355 GST_DEBUG_FUNCPTR(gst_marudec_src_event));
357 gst_element_add_pad (GST_ELEMENT(marudec), marudec->sinkpad);
358 gst_element_add_pad (GST_ELEMENT(marudec), marudec->srcpad);
360 marudec->context = g_malloc0 (sizeof(CodecContext));
361 marudec->context->video.pix_fmt = PIX_FMT_NONE;
362 marudec->context->audio.sample_fmt = SAMPLE_FMT_NONE;
364 marudec->opened = FALSE;
365 marudec->format.video.par_n = -1;
366 marudec->format.video.fps_n = -1;
367 marudec->format.video.old_fps_n = -1;
369 marudec->queued = NULL;
370 gst_segment_init (&marudec->segment, GST_FORMAT_TIME);
372 marudec->dev = g_malloc0 (sizeof(CodecDevice));
374 GST_ERROR_OBJECT (marudec, "failed to allocate memory for CodecDevice");
379 gst_marudec_finalize (GObject *object)
381 GstMaruDec *marudec = (GstMaruDec *) object;
383 if (marudec->context) {
384 g_free (marudec->context);
385 marudec->context = NULL;
388 G_OBJECT_CLASS (parent_class)->finalize (object);
392 gst_marudec_src_event (GstPad *pad, GstEvent *event)
397 marudec = (GstMaruDec *) gst_pad_get_parent (pad);
399 switch (GST_EVENT_TYPE (event)) {
400 /* Quality Of Service (QOS) event contains a report
401 about the current real-time performance of the stream.*/
405 GstClockTimeDiff diff;
406 GstClockTime timestamp;
408 gst_event_parse_qos (event, &proportion, &diff, ×tamp);
410 /* update our QoS values */
411 gst_marudec_update_qos (marudec, proportion, timestamp + diff);
418 /* forward upstream */
419 res = gst_pad_push_event (marudec->sinkpad, event);
421 gst_object_unref (marudec);
427 gst_marudec_sink_event (GstPad *pad, GstEvent *event)
430 gboolean ret = FALSE;
432 marudec = (GstMaruDec *) gst_pad_get_parent (pad);
434 GST_DEBUG_OBJECT (marudec, "Handling %s event",
435 GST_EVENT_TYPE_NAME (event));
437 switch (GST_EVENT_TYPE (event)) {
439 gst_marudec_drain (marudec);
441 case GST_EVENT_FLUSH_STOP:
443 if (marudec->opened) {
444 codec_flush_buffers (marudec->context, marudec->dev);
447 gst_marudec_reset_ts (marudec);
448 gst_marudec_reset_qos (marudec);
450 gst_marudec_flush_pcache (marudec);
451 marudec->waiting_for_key = TRUE;
453 gst_segment_init (&marudec->segment, GST_FORMAT_TIME);
454 clear_queued (marudec);
457 case GST_EVENT_NEWSEGMENT:
461 gint64 start, stop, time;
464 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
465 &start, &stop, &time);
468 case GST_FORMAT_TIME:
470 case GST_FORMAT_BYTES:
473 bit_rate = marudec->context->bit_rate;
476 GST_WARNING_OBJECT (marudec, "no bitrate to convert BYTES to TIME");
477 gst_event_unref (event);
478 gst_object_unref (marudec);
482 GST_DEBUG_OBJECT (marudec, "bitrate: %d", bit_rate);
485 start = gst_util_uint64_scale_int (start, GST_SECOND, bit_rate);
488 stop = gst_util_uint64_scale_int (stop, GST_SECOND, bit_rate);
491 time = gst_util_uint64_scale_int (time, GST_SECOND, bit_rate);
494 gst_event_unref (event);
496 format = GST_FORMAT_TIME;
499 event = gst_event_new_new_segment (update, rate, format,
504 GST_WARNING_OBJECT (marudec, "unknown format received in NEWSEGMENT");
505 gst_event_unref (event);
506 gst_object_unref (marudec);
510 if (marudec->context->codec) {
511 gst_marudec_drain (marudec);
514 GST_DEBUG_OBJECT (marudec,
515 "NEWSEGMENT in time start %" GST_TIME_FORMAT " -- stop %"
516 GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
518 gst_segment_set_newsegment_full (&marudec->segment, update,
519 rate, arate, format, start, stop, time);
526 ret = gst_pad_push_event (marudec->srcpad, event);
528 gst_object_unref (marudec);
536 gst_marudec_setcaps (GstPad *pad, GstCaps *caps)
539 GstMaruDecClass *oclass;
540 GstStructure *structure;
545 GST_DEBUG_OBJECT (pad, "setcaps called.");
547 marudec = (GstMaruDec *) (gst_pad_get_parent (pad));
548 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
550 GST_OBJECT_LOCK (marudec);
552 if (marudec->opened) {
553 GST_OBJECT_UNLOCK (marudec);
554 gst_marudec_drain (marudec);
555 GST_OBJECT_LOCK (marudec);
556 gst_marudec_close (marudec);
559 GST_LOG_OBJECT (marudec, "size %dx%d", marudec->context->video.width,
560 marudec->context->video.height);
562 if (!strcmp(oclass->codec->name, "wmv3") ||
563 !strcmp(oclass->codec->name, "vc1")) {
564 gst_maru_caps_to_codecname (caps, oclass->codec->name, NULL);
567 gst_maru_caps_with_codecname (oclass->codec->name, oclass->codec->media_type,
568 caps, marudec->context);
570 GST_LOG_OBJECT (marudec, "size after %dx%d", marudec->context->video.width,
571 marudec->context->video.height);
573 if (!marudec->context->video.fps_d || !marudec->context->video.fps_n) {
574 GST_DEBUG_OBJECT (marudec, "forcing 25/1 framerate");
575 marudec->context->video.fps_n = 1;
576 marudec->context->video.fps_d = 25;
579 structure = gst_caps_get_structure (caps, 0);
581 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
583 GST_DEBUG_OBJECT (marudec, "sink caps have pixel-aspect-ratio of %d:%d",
584 gst_value_get_fraction_numerator (par),
585 gst_value_get_fraction_denominator (par));
589 g_free(marudec->par);
591 marudec->par = g_new0 (GValue, 1);
592 gst_value_init_and_copy (marudec->par, par);
596 fps = gst_structure_get_value (structure, "framerate");
597 if (fps != NULL && GST_VALUE_HOLDS_FRACTION (fps)) {
598 marudec->format.video.fps_n = gst_value_get_fraction_numerator (fps);
599 marudec->format.video.fps_d = gst_value_get_fraction_denominator (fps);
600 GST_DEBUG_OBJECT (marudec, "Using framerate %d/%d from incoming",
601 marudec->format.video.fps_n, marudec->format.video.fps_d);
603 marudec->format.video.fps_n = -1;
604 GST_DEBUG_OBJECT (marudec, "Using framerate from codec");
608 if (strcmp (oclass->codec->name, "aac") == 0) {
609 const gchar *format = gst_structure_get_string (structure, "stream-format");
610 if (format == NULL || strcmp ("format", "raw") == 0) {
611 marudec->turnoff_parser = TRUE;
616 if (!gst_marudec_open (marudec)) {
617 GST_DEBUG_OBJECT (marudec, "Failed to open");
620 g_free(marudec->par);
624 GST_OBJECT_UNLOCK (marudec);
625 gst_object_unref (marudec);
630 gst_structure_get_int (structure, "width",
631 &marudec->format.video.clip_width);
632 gst_structure_get_int (structure, "height",
633 &marudec->format.video.clip_height);
635 GST_DEBUG_OBJECT (pad, "clipping to %dx%d",
636 marudec->format.video.clip_width, marudec->format.video.clip_height);
638 GST_OBJECT_UNLOCK (marudec);
639 gst_object_unref (marudec);
645 gst_marudec_open (GstMaruDec *marudec)
647 GstMaruDecClass *oclass;
649 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
655 if (gst_maru_avcodec_open (marudec->context,
656 oclass->codec, marudec->dev) < 0) {
657 gst_marudec_close (marudec);
658 GST_ERROR_OBJECT (marudec,
659 "maru_%sdec: Failed to open codec", oclass->codec->name);
663 marudec->opened = TRUE;
664 GST_LOG_OBJECT (marudec, "Opened codec %s", oclass->codec->name);
666 switch (oclass->codec->media_type) {
667 case AVMEDIA_TYPE_VIDEO:
668 marudec->format.video.width = 0;
669 marudec->format.video.height = 0;
670 marudec->format.video.clip_width = -1;
671 marudec->format.video.clip_height = -1;
672 marudec->format.video.pix_fmt = PIX_FMT_NB;
673 marudec->format.video.interlaced = FALSE;
675 case AVMEDIA_TYPE_AUDIO:
676 marudec->format.audio.samplerate = 0;
677 marudec->format.audio.channels = 0;
678 marudec->format.audio.depth = 0;
684 gst_marudec_reset_ts (marudec);
686 marudec->proportion = 0.0;
687 marudec->earliest_time = -1;
693 gst_marudec_close (GstMaruDec *marudec)
697 if (marudec->context->codecdata) {
698 g_free(marudec->context->codecdata);
699 marudec->context->codecdata = NULL;
706 ret = gst_maru_avcodec_close (marudec->context, marudec->dev);
709 g_free(marudec->dev);
718 gst_marudec_negotiate (GstMaruDec *marudec, gboolean force)
720 GstMaruDecClass *oclass;
723 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
725 switch (oclass->codec->media_type) {
726 case AVMEDIA_TYPE_VIDEO:
727 if (!force && marudec->format.video.width == marudec->context->video.width
728 && marudec->format.video.height == marudec->context->video.height
729 && marudec->format.video.fps_n == marudec->format.video.old_fps_n
730 && marudec->format.video.fps_d == marudec->format.video.old_fps_d
731 && marudec->format.video.pix_fmt == marudec->context->video.pix_fmt
732 && marudec->format.video.par_n == marudec->context->video.par_n
733 && marudec->format.video.par_d == marudec->context->video.par_d) {
736 marudec->format.video.width = marudec->context->video.width;
737 marudec->format.video.height = marudec->context->video.height;
738 marudec->format.video.old_fps_n = marudec->format.video.fps_n;
739 marudec->format.video.old_fps_d = marudec->format.video.fps_d;
740 marudec->format.video.pix_fmt = marudec->context->video.pix_fmt;
741 marudec->format.video.par_n = marudec->context->video.par_n;
742 marudec->format.video.par_d = marudec->context->video.par_d;
744 case AVMEDIA_TYPE_AUDIO:
746 gint depth = gst_maru_smpfmt_depth (marudec->context->audio.sample_fmt);
747 if (!force && marudec->format.audio.samplerate ==
748 marudec->context->audio.sample_rate &&
749 marudec->format.audio.channels == marudec->context->audio.channels &&
750 marudec->format.audio.depth == depth) {
753 marudec->format.audio.samplerate = marudec->context->audio.sample_rate;
754 marudec->format.audio.channels = marudec->context->audio.channels;
755 marudec->format.audio.depth = depth;
763 gst_maru_codectype_to_caps (oclass->codec->media_type, marudec->context,
764 oclass->codec->name, FALSE);
767 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION,
768 ("Could not find GStreamer caps mapping for codec '%s'.",
769 oclass->codec->name), (NULL));
773 switch (oclass->codec->media_type) {
774 case AVMEDIA_TYPE_VIDEO:
779 width = marudec->format.video.clip_width;
780 height = marudec->format.video.clip_height;
781 interlaced = marudec->format.video.interlaced;
783 if (width != -1 && height != -1) {
784 if (width < marudec->context->video.width) {
785 gst_caps_set_simple (caps, "width", G_TYPE_INT, width, NULL);
787 if (height < marudec->context->video.height) {
788 gst_caps_set_simple (caps, "height", G_TYPE_INT, height, NULL);
790 gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced,
793 if (marudec->format.video.fps_n != -1) {
794 gst_caps_set_simple (caps, "framerate",
795 GST_TYPE_FRACTION, marudec->format.video.fps_n,
796 marudec->format.video.fps_d, NULL);
799 gst_marudec_add_pixel_aspect_ratio (marudec,
800 gst_caps_get_structure (caps, 0));
805 case AVMEDIA_TYPE_AUDIO:
811 if (!gst_pad_set_caps (marudec->srcpad, caps)) {
812 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION, (NULL),
813 ("Could not set caps for decoder (%s), not fixed?",
814 oclass->codec->name));
815 gst_caps_unref (caps);
819 gst_caps_unref (caps);
825 new_aligned_buffer (gint size, GstCaps *caps)
829 buf = gst_buffer_new ();
830 GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = g_malloc0 (size);
831 GST_BUFFER_SIZE (buf) = size;
832 GST_BUFFER_FREE_FUNC (buf) = g_free;
835 gst_buffer_set_caps (buf, caps);
842 get_output_buffer (GstMaruDec *marudec, GstBuffer **outbuf)
851 if (G_UNLIKELY (!gst_marudec_negotiate (marudec, FALSE))) {
852 GST_DEBUG_OBJECT (marudec, "negotiate failed");
853 return GST_FLOW_NOT_NEGOTIATED;
856 pict_size = gst_maru_avpicture_size (marudec->context->video.pix_fmt,
857 marudec->context->video.width, marudec->context->video.height);
859 GST_DEBUG_OBJECT (marudec, "size of a picture is negative. "
860 "pixel format: %d, width: %d, height: %d",
861 marudec->context->video.pix_fmt, marudec->context->video.width,
862 marudec->context->video.height);
863 return GST_FLOW_ERROR;
866 GST_DEBUG_OBJECT (marudec, "outbuf size of decoded video %d", pict_size);
868 gst_pad_set_element_private(GST_PAD_PEER(marudec->srcpad), (gpointer)marudec);
870 /* GstPadBufferAllocFunction is mostly overridden by elements that can
871 * provide a hardware buffer in order to avoid additional memcpy operations.
873 gst_pad_set_bufferalloc_function(
874 GST_PAD_PEER(marudec->srcpad),
875 (GstPadBufferAllocFunction) codec_buffer_alloc_and_copy);
877 ret = gst_pad_alloc_buffer_and_set_caps (marudec->srcpad,
878 GST_BUFFER_OFFSET_NONE, pict_size,
879 GST_PAD_CAPS (marudec->srcpad), outbuf);
880 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
881 GST_DEBUG_OBJECT (marudec, "pad_alloc failed %d (%s)", ret,
882 gst_flow_get_name (ret));
890 clip_video_buffer (GstMaruDec *dec, GstBuffer *buf,
891 GstClockTime in_ts, GstClockTime in_dur)
899 clip_audio_buffer (GstMaruDec *dec, GstBuffer *buf,
900 GstClockTime in_ts, GstClockTime in_dur)
903 gint64 diff, cstart, cstop;
906 if (G_UNLIKELY (dec->segment.format != GST_FORMAT_TIME)) {
907 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
911 // in_ts: in_timestamp. check a start time.
912 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts))) {
913 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
918 GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE;
920 res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME, in_ts,
921 stop, &cstart, &cstop);
922 if (G_UNLIKELY (!res)) {
923 GST_LOG_OBJECT (dec, "out of segment");
924 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
928 if (G_UNLIKELY ((diff = cstart - in_ts) > 0)) {
930 gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, GST_SECOND) *
931 (dec->format.audio.depth * dec->format.audio.channels);
933 GST_DEBUG_OBJECT (dec, "clipping start to %" GST_TIME_FORMAT " %"
934 G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
936 GST_BUFFER_SIZE (buf) -= diff;
937 GST_BUFFER_DATA (buf) += diff;
941 if (G_UNLIKELY ((diff = stop - cstop) > 0)) {
943 gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, GST_SECOND) *
944 (dec->format.audio.depth * dec->format.audio.channels);
946 GST_DEBUG_OBJECT (dec, "clipping stop to %" GST_TIME_FORMAT " %"
947 G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstop), diff);
949 GST_BUFFER_SIZE (buf) -= diff;
952 GST_BUFFER_TIMESTAMP (buf) = cstart;
953 GST_BUFFER_DURATION (buf) = cstop - cstart;
955 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
960 gst_marudec_video_frame (GstMaruDec *marudec, guint8 *data, guint size,
961 const GstTSInfo *dec_info, gint64 in_offset, GstBuffer **outbuf,
965 gboolean mode_switch;
967 GstClockTime out_timestamp, out_duration, out_pts;
969 const GstTSInfo *out_info;
972 decode = gst_marudec_do_qos (marudec, dec_info->timestamp, &mode_switch);
978 GST_DEBUG_OBJECT (marudec, "decode video: input buffer size %d", size);
980 len = codec_decode_video (marudec, data, size,
981 dec_info->idx, in_offset, outbuf, &have_data);
982 if (len < 0 || !have_data) {
986 *ret = get_output_buffer (marudec, outbuf);
988 if (G_UNLIKELY (*ret != GST_FLOW_OK)) {
989 GST_DEBUG_OBJECT (marudec, "no output buffer");
991 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
996 out_info = gst_ts_info_get (marudec, dec_info->idx);
997 out_pts = out_info->timestamp;
998 out_duration = out_info->duration;
999 out_offset = out_info->offset;
1003 if (out_pts != -1) {
1004 out_timestamp = (GstClockTime) out_pts;
1005 GST_LOG_OBJECT (marudec, "using timestamp %" GST_TIME_FORMAT
1006 " returned by ffmpeg", GST_TIME_ARGS (out_timestamp));
1009 if (!GST_CLOCK_TIME_IS_VALID (out_timestamp) && marudec->next_out != -1) {
1010 out_timestamp = marudec->next_out;
1011 GST_LOG_OBJECT (marudec, "using next timestamp %" GST_TIME_FORMAT,
1012 GST_TIME_ARGS (out_timestamp));
1015 if (!GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
1016 out_timestamp = dec_info->timestamp;
1017 GST_LOG_OBJECT (marudec, "using in timestamp %" GST_TIME_FORMAT,
1018 GST_TIME_ARGS (out_timestamp));
1020 GST_BUFFER_TIMESTAMP (*outbuf) = out_timestamp;
1023 if (out_offset != GST_BUFFER_OFFSET_NONE) {
1024 GST_LOG_OBJECT (marudec, "Using offset returned by ffmpeg");
1025 } else if (out_timestamp != GST_CLOCK_TIME_NONE) {
1026 GstFormat out_fmt = GST_FORMAT_DEFAULT;
1027 GST_LOG_OBJECT (marudec, "Using offset converted from timestamp");
1029 gst_pad_query_peer_convert (marudec->sinkpad,
1030 GST_FORMAT_TIME, out_timestamp, &out_fmt, &out_offset);
1031 } else if (dec_info->offset != GST_BUFFER_OFFSET_NONE) {
1032 GST_LOG_OBJECT (marudec, "using in_offset %" G_GINT64_FORMAT,
1034 out_offset = dec_info->offset;
1036 GST_LOG_OBJECT (marudec, "no valid offset found");
1037 out_offset = GST_BUFFER_OFFSET_NONE;
1039 GST_BUFFER_OFFSET (*outbuf) = out_offset;
1042 if (GST_CLOCK_TIME_IS_VALID (out_duration)) {
1043 GST_LOG_OBJECT (marudec, "Using duration returned by ffmpeg");
1044 } else if (GST_CLOCK_TIME_IS_VALID (dec_info->duration)) {
1045 GST_LOG_OBJECT (marudec, "Using in_duration");
1046 out_duration = dec_info->duration;
1048 if (marudec->format.video.fps_n != -1 &&
1049 (marudec->format.video.fps_n != 1000 &&
1050 marudec->format.video.fps_d != 1)) {
1051 GST_LOG_OBJECT (marudec, "using input framerate for duration");
1052 out_duration = gst_util_uint64_scale_int (GST_SECOND,
1053 marudec->format.video.fps_d, marudec->format.video.fps_n);
1055 if (marudec->context->video.fps_n != 0 &&
1056 (marudec->context->video.fps_d > 0 &&
1057 marudec->context->video.fps_d < 1000)) {
1058 GST_LOG_OBJECT (marudec, "using decoder's framerate for duration");
1059 out_duration = gst_util_uint64_scale_int (GST_SECOND,
1060 marudec->context->video.fps_n * 1,
1061 marudec->context->video.fps_d);
1063 GST_LOG_OBJECT (marudec, "no valid duration found");
1068 if (G_UNLIKELY (!clip_video_buffer (marudec, *outbuf, out_timestamp,
1070 GST_DEBUG_OBJECT (marudec, "buffer clipped");
1071 gst_buffer_unref (*outbuf);
1073 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1074 *ret, *outbuf, len);
1078 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1079 *ret, *outbuf, len);
1084 gst_marudec_audio_frame (GstMaruDec *marudec, CodecElement *codec,
1085 guint8 *data, guint size,
1086 const GstTSInfo *dec_info, GstBuffer **outbuf,
1090 gint have_data = FF_MAX_AUDIO_FRAME_SIZE;
1091 GstClockTime out_timestamp, out_duration;
1095 new_aligned_buffer (FF_MAX_AUDIO_FRAME_SIZE,
1096 GST_PAD_CAPS (marudec->srcpad));
1098 GST_DEBUG_OBJECT (marudec, "decode audio, input buffer size %d", size);
1100 len = codec_decode_audio (marudec->context,
1101 (int16_t *) GST_BUFFER_DATA (*outbuf), &have_data,
1102 data, size, marudec->dev);
1104 GST_DEBUG_OBJECT (marudec,
1105 "Decode audio: len=%d, have_data=%d", len, have_data);
1107 if (len >= 0 && have_data > 0) {
1108 GST_DEBUG_OBJECT (marudec, "Creating output buffer");
1109 if (!gst_marudec_negotiate (marudec, FALSE)) {
1110 gst_buffer_unref (*outbuf);
1113 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1114 *ret, *outbuf, len);
1118 // GST_BUFFER_SIZE (*outbuf) = have_data;
1119 GST_BUFFER_SIZE (*outbuf) = len;
1121 if (GST_CLOCK_TIME_IS_VALID (dec_info->timestamp)) {
1122 out_timestamp = dec_info->timestamp;
1124 out_timestamp = marudec->next_out;
1127 /* calculate based on number of samples */
1128 out_duration = gst_util_uint64_scale (have_data, GST_SECOND,
1129 marudec->format.audio.depth * marudec->format.audio.channels *
1130 marudec->format.audio.samplerate);
1132 out_offset = dec_info->offset;
1134 GST_DEBUG_OBJECT (marudec,
1135 "Buffer created. Size: %d, timestamp: %" GST_TIME_FORMAT
1136 ", duration: %" GST_TIME_FORMAT, have_data,
1137 GST_TIME_ARGS (out_timestamp), GST_TIME_ARGS (out_duration));
1139 GST_BUFFER_TIMESTAMP (*outbuf) = out_timestamp;
1140 GST_BUFFER_DURATION (*outbuf) = out_duration;
1141 GST_BUFFER_OFFSET (*outbuf) = out_offset;
1142 gst_buffer_set_caps (*outbuf, GST_PAD_CAPS (marudec->srcpad));
1144 if (GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
1145 marudec->next_out = out_timestamp + out_duration;
1148 if (G_UNLIKELY (!clip_audio_buffer (marudec, *outbuf,
1149 out_timestamp, out_duration))) {
1150 GST_DEBUG_OBJECT (marudec, "buffer_clipped");
1151 gst_buffer_unref (*outbuf);
1153 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d", *ret, *outbuf, len);
1157 gst_buffer_unref (*outbuf);
1161 if (len == -1 && !strcmp(codec->name, "aac")) {
1162 GST_ELEMENT_ERROR (marudec, STREAM, DECODE, (NULL),
1163 ("Decoding of AAC stream by FFMPEG failed."));
1164 *ret = GST_FLOW_ERROR;
1167 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1168 *ret, *outbuf, len);
1173 gst_marudec_frame (GstMaruDec *marudec, guint8 *data, guint size,
1174 gint *got_data, const GstTSInfo *dec_info, gint64 in_offset, GstFlowReturn *ret)
1176 GstMaruDecClass *oclass;
1177 GstBuffer *outbuf = NULL;
1178 gint have_data = 0, len = 0;
1180 if (G_UNLIKELY (marudec->context->codec == NULL)) {
1181 GST_ERROR_OBJECT (marudec, "no codec context");
1186 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1188 switch (oclass->codec->media_type) {
1189 case AVMEDIA_TYPE_VIDEO:
1190 len = gst_marudec_video_frame (marudec, data, size,
1191 dec_info, in_offset, &outbuf, ret);
1193 case AVMEDIA_TYPE_AUDIO:
1194 len = gst_marudec_audio_frame (marudec, oclass->codec, data, size,
1195 dec_info, &outbuf, ret);
1196 if (outbuf == NULL && marudec->discont) {
1197 GST_DEBUG_OBJECT (marudec, "no buffer but keeping timestamp");
1198 // marudec->clear_ts = FALSE;
1202 GST_ERROR_OBJECT (marudec, "Asked to decode non-audio/video frame!");
1203 g_assert_not_reached ();
1211 if (len < 0 || have_data < 0) {
1212 GST_WARNING_OBJECT (marudec,
1213 "maru_%sdec: decoding error (len: %d, have_data: %d)",
1214 oclass->codec->name, len, have_data);
1217 } else if (len == 0 && have_data == 0) {
1225 GST_LOG_OBJECT (marudec,
1226 "Decoded data, now pushing buffer %p with offset %" G_GINT64_FORMAT
1227 ", timestamp %" GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT,
1228 outbuf, GST_BUFFER_OFFSET (outbuf),
1229 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1230 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
1232 if (marudec->discont) {
1233 /* GST_BUFFER_FLAG_DISCONT :
1234 * the buffer marks a data discontinuity in the stream. This typically
1235 * occurs after a seek or a dropped buffer from a live or network source.
1237 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
1238 marudec->discont = FALSE;
1241 if (marudec->segment.rate > 0.0) {
1243 *ret = gst_pad_push (marudec->srcpad, outbuf);
1246 GST_DEBUG_OBJECT (marudec, "queued frame");
1247 marudec->queued = g_list_prepend (marudec->queued, outbuf);
1251 GST_DEBUG_OBJECT (marudec, "Didn't get a decoded buffer");
1257 static GstFlowReturn
1258 gst_marudec_chain (GstPad *pad, GstBuffer *buffer)
1260 GstMaruDec *marudec;
1261 GstMaruDecClass *oclass;
1263 gint in_size, have_data;
1264 GstFlowReturn ret = GST_FLOW_OK;
1265 GstClockTime in_timestamp;
1266 GstClockTime in_duration;
1269 const GstTSInfo *in_info;
1270 const GstTSInfo *dec_info;
1272 marudec = (GstMaruDec *) (GST_PAD_PARENT (pad));
1274 if (G_UNLIKELY (!marudec->opened)) {
1276 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1277 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION, (NULL),
1278 ("maru_%sdec: input format was not set before data start",
1279 oclass->codec->name));
1280 gst_buffer_unref (buffer);
1281 return GST_FLOW_NOT_NEGOTIATED;
1284 discont = GST_BUFFER_IS_DISCONT (buffer);
1287 if (G_UNLIKELY (discont)) {
1288 GST_DEBUG_OBJECT (marudec, "received DISCONT");
1289 gst_marudec_drain (marudec);
1290 // gst_marudec_flush_pcache (marudec);
1291 codec_flush_buffers (marudec->context, marudec->dev);
1292 marudec->discont = TRUE;
1293 gst_marudec_reset_ts (marudec);
1295 // marudec->clear_ts = TRUE;
1297 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1299 if (G_UNLIKELY (marudec->waiting_for_key)) {
1300 if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT) &&
1301 oclass->codec->media_type != AVMEDIA_TYPE_AUDIO) {
1304 marudec->waiting_for_key = FALSE;
1307 if (marudec->pcache) {
1308 GST_LOG_OBJECT (marudec, "join parse cache");
1309 buffer = gst_buffer_join (marudec->pcache, buffer);
1310 marudec->pcache = NULL;
1314 in_timestamp = GST_BUFFER_TIMESTAMP (buffer);
1315 in_duration = GST_BUFFER_DURATION (buffer);
1316 in_offset = GST_BUFFER_OFFSET (buffer);
1318 in_info = gst_ts_info_store (marudec, in_timestamp, in_duration, in_offset);
1321 if (in_timestamp != -1) {
1322 if (!marudec->reordered_in && marudec->last_in != -1) {
1323 if (in_timestamp < marudec->last_in) {
1324 GST_LOG_OBJECT (marudec, "detected reordered input timestamps");
1325 marudec->reordered_in = TRUE;
1326 marudec->last_diff = GST_CLOCK_TIME_NONE;
1327 } else if (in_timestamp > marudec->last_in) {
1329 diff = in_timestamp - marudec->last_in;
1330 if (marudec->last_frames) {
1331 diff /= marudec->last_frames;
1334 GST_LOG_OBJECT (marudec, "estimated duration %" GST_TIME_FORMAT " %u",
1335 GST_TIME_ARGS (diff), marudec->last_frames);
1337 marudec->last_diff = diff;
1340 marudec->last_in = in_timestamp;
1341 marudec->last_frames;
1345 GST_LOG_OBJECT (marudec,
1346 "Received new data of size %u, offset: %" G_GUINT64_FORMAT ", ts:%"
1347 GST_TIME_FORMAT ", dur: %" GST_TIME_FORMAT ", info %d",
1348 GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer),
1349 GST_TIME_ARGS (in_timestamp), GST_TIME_ARGS (in_duration), in_info->idx);
1351 in_buf = GST_BUFFER_DATA (buffer);
1352 in_size = GST_BUFFER_SIZE (buffer);
1356 gst_marudec_frame (marudec, in_buf, in_size, &have_data, dec_info, in_offset, &ret);
1359 if (marudec->clear_ts) {
1360 in_timestamp = GST_CLOCK_TIME_NONE;
1361 in_duration = GST_CLOCK_TIME_NONE;
1362 in_offset = GST_BUFFER_OFFSET_NONE;
1363 in_info = GST_TS_INFO_NONE;
1365 marudec->clear_ts = TRUE;
1369 gst_buffer_unref (buffer);
1374 static GstStateChangeReturn
1375 gst_marudec_change_state (GstElement *element, GstStateChange transition)
1377 GstMaruDec *marudec = (GstMaruDec *) element;
1378 GstStateChangeReturn ret;
1380 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1382 switch (transition) {
1383 case GST_STATE_CHANGE_PAUSED_TO_READY:
1384 GST_OBJECT_LOCK (marudec);
1385 gst_marudec_close (marudec);
1386 GST_OBJECT_UNLOCK (marudec);
1389 clear_queued (marudec);
1399 gst_marudec_register (GstPlugin *plugin, GList *element)
1401 GTypeInfo typeinfo = {
1402 sizeof (GstMaruDecClass),
1403 (GBaseInitFunc) gst_marudec_base_init,
1405 (GClassInitFunc) gst_marudec_class_init,
1408 sizeof (GstMaruDec),
1410 (GInstanceInitFunc) gst_marudec_init,
1415 gint rank = GST_RANK_PRIMARY;
1416 GList *elem = element;
1417 CodecElement *codec = NULL;
1423 /* register element */
1425 codec = (CodecElement *)(elem->data);
1430 if (codec->codec_type != CODEC_TYPE_DECODE) {
1434 type_name = g_strdup_printf ("maru_%sdec", codec->name);
1435 type = g_type_from_name (type_name);
1437 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1438 g_type_set_qdata (type, GST_MARUDEC_PARAMS_QDATA, (gpointer) codec);
1441 if (!gst_element_register (plugin, type_name, rank, type)) {
1446 } while ((elem = elem->next));