2 * GStreamer codec plugin for Tizen Emulator.
4 * Copyright (C) 2013 Samsung Electronics Co., Ltd. All rights reserved.
7 * KiTae Kim <kt920.kim@samsung.com>
8 * SeokYeon Hwang <syeon.hwang@samsung.com>
9 * YeongKyoon Lee <yeongkyoon.lee@samsung.com>
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.
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.
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.
31 // #include "gstmaru.h"
32 #include "gstmarudevice.h"
33 #include "gstmaruutils.h"
34 #include "gstmaruinterface.h"
35 // #include "gstmarudevice.h"
37 #define GST_MARUDEC_PARAMS_QDATA g_quark_from_static_string("marudec-params")
39 /* indicate dts, pts, offset in the stream */
43 GstClockTime timestamp;
44 GstClockTime duration;
48 #define GST_TS_INFO_NONE &ts_info_none
49 static const GstTSInfo ts_info_none = { -1, -1, -1, -1 };
51 #define MAX_TS_MASK 0xff
53 typedef struct _GstMaruDec
60 CodecContext *context;
66 gint clip_width, clip_height;
69 gint old_fps_n, old_fps_d;
72 enum PixelFormat pix_fmt;
85 /* tracking DTS/PTS */
86 GstClockTime next_out;
90 GstClockTime earliest_time;
95 /* GstSegment can be used for two purposes:
96 * 1. performing seeks (handling seek events)
97 * 2. tracking playback regions (handling newsegment events)
101 GstTSInfo ts_info[MAX_TS_MASK + 1];
104 /* reverse playback queue */
109 typedef struct _GstMaruDecClass
111 GstElementClass parent_class;
114 GstPadTemplate *sinktempl;
115 GstPadTemplate *srctempl;
119 static GstElementClass *parent_class = NULL;
121 static void gst_marudec_base_init (GstMaruDecClass *klass);
122 static void gst_marudec_class_init (GstMaruDecClass *klass);
123 static void gst_marudec_init (GstMaruDec *marudec);
124 static void gst_marudec_finalize (GObject *object);
126 static gboolean gst_marudec_setcaps (GstPad *pad, GstCaps *caps);
129 static gboolean gst_marudec_sink_event (GstPad *pad, GstEvent *event);
130 static GstFlowReturn gst_marudec_chain (GstPad *pad, GstBuffer *buffer);
133 static gboolean gst_marudec_src_event (GstPad *pad, GstEvent *event);
134 static GstStateChangeReturn gst_marudec_change_state (GstElement *element,
135 GstStateChange transition);
137 static gboolean gst_marudec_negotiate (GstMaruDec *dec, gboolean force);
139 static gint gst_marudec_frame (GstMaruDec *marudec, guint8 *data,
140 guint size, gint *got_data,
141 const GstTSInfo *dec_info, gint64 in_offset, GstFlowReturn *ret);
143 static gboolean gst_marudec_open (GstMaruDec *marudec);
144 static int gst_marudec_close (GstMaruDec *marudec);
147 static const GstTSInfo *
148 gst_ts_info_store (GstMaruDec *dec, GstClockTime timestamp,
149 GstClockTime duration, gint64 offset)
151 gint idx = dec->ts_idx;
152 dec->ts_info[idx].idx = idx;
153 dec->ts_info[idx].timestamp = timestamp;
154 dec->ts_info[idx].duration = duration;
155 dec->ts_info[idx].offset = offset;
156 dec->ts_idx = (idx + 1) & MAX_TS_MASK;
158 return &dec->ts_info[idx];
161 static const GstTSInfo *
162 gst_ts_info_get (GstMaruDec *dec, gint idx)
164 if (G_UNLIKELY (idx < 0 || idx > MAX_TS_MASK))
165 return GST_TS_INFO_NONE;
167 return &dec->ts_info[idx];
171 gst_marudec_reset_ts (GstMaruDec *marudec)
173 marudec->next_out = GST_CLOCK_TIME_NONE;
177 gst_marudec_update_qos (GstMaruDec *marudec, gdouble proportion,
178 GstClockTime timestamp)
180 GST_LOG_OBJECT (marudec, "update QOS: %f, %" GST_TIME_FORMAT,
181 proportion, GST_TIME_ARGS (timestamp));
183 GST_OBJECT_LOCK (marudec);
184 marudec->proportion = proportion;
185 marudec->earliest_time = timestamp;
186 GST_OBJECT_UNLOCK (marudec);
190 gst_marudec_reset_qos (GstMaruDec *marudec)
192 gst_marudec_update_qos (marudec, 0.5, GST_CLOCK_TIME_NONE);
193 marudec->processed = 0;
194 marudec->dropped = 0;
198 gst_marudec_do_qos (GstMaruDec *marudec, GstClockTime timestamp,
199 gboolean *mode_switch)
201 GstClockTimeDiff diff;
203 GstClockTime qostime, earliest_time;
206 *mode_switch = FALSE;
208 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
209 marudec->processed++;
213 proportion = marudec->proportion;
214 earliest_time = marudec->earliest_time;
216 qostime = gst_segment_to_running_time (&marudec->segment, GST_FORMAT_TIME,
219 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime))) {
220 marudec->processed++;
224 diff = GST_CLOCK_DIFF (qostime, earliest_time);
226 if (proportion < 0.4 && diff < 0 ){
227 marudec->processed++;
231 // if (marudec->waiting_for_key) {
240 GstClockTime stream_time, jitter;
245 gst_segment_to_stream_time (&marudec->segment, GST_FORMAT_TIME,
247 jitter = GST_CLOCK_DIFF (qostime, earliest_time);
249 gst_message_new_qos (GST_OBJECT_CAST (marudec), FALSE, qostime,
250 stream_time, timestamp, GST_CLOCK_TIME_NONE);
251 gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
252 gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS,
253 marudec->processed, marudec->dropped);
254 gst_element_post_message (GST_ELEMENT_CAST (marudec), qos_msg);
260 marudec->processed++;
265 clear_queued (GstMaruDec *marudec)
267 g_list_foreach (marudec->queued, (GFunc) gst_mini_object_unref, NULL);
268 g_list_free (marudec->queued);
269 marudec->queued = NULL;
273 flush_queued (GstMaruDec *marudec)
275 GstFlowReturn res = GST_FLOW_OK;
277 CODEC_LOG (DEBUG, "flush queued\n");
279 while (marudec->queued) {
280 GstBuffer *buf = GST_BUFFER_CAST (marudec->queued->data);
282 GST_LOG_OBJECT (marudec, "pushing buffer %p, offset %"
283 G_GUINT64_FORMAT ", timestamp %"
284 GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, buf,
285 GST_BUFFER_OFFSET (buf),
286 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
287 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
289 res = gst_pad_push (marudec->srcpad, buf);
292 g_list_delete_link (marudec->queued, marudec->queued);
299 gst_marudec_drain (GstMaruDec *marudec)
301 GstMaruDecClass *oclass;
303 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
305 CODEC_LOG (DEBUG, "drain frame\n");
308 gint have_data, len, try = 0;
314 gst_marudec_frame (marudec, NULL, 0, &have_data, &ts_info_none, 0, &ret);
316 if (len < 0 || have_data == 0) {
319 } while (try++ < 10);
323 if (marudec->segment.rate < 0.0) {
324 CODEC_LOG (DEBUG, "reverse playback\n");
325 flush_queued (marudec);
333 gst_marudec_base_init (GstMaruDecClass *klass)
335 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
336 GstCaps *sinkcaps = NULL, *srccaps = NULL;
337 GstPadTemplate *sinktempl, *srctempl;
339 gchar *longname, *classification, *description;
342 (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
343 GST_MARUDEC_PARAMS_QDATA);
345 longname = g_strdup_printf ("%s Decoder", codec->longname);
346 classification = g_strdup_printf ("Codec/Decoder/%s",
347 (codec->media_type == AVMEDIA_TYPE_VIDEO) ?
349 description = g_strdup_printf("%s Decoder", codec->name);
351 gst_element_class_set_details_simple (element_class,
355 "Kitae Kim <kt920.kim@samsung.com>");
358 g_free (classification);
359 g_free (description);
361 sinkcaps = gst_maru_codecname_to_caps (codec->name, NULL, FALSE);
363 sinkcaps = gst_caps_from_string ("unknown/unknown");
366 switch (codec->media_type) {
367 case AVMEDIA_TYPE_VIDEO:
368 srccaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv");
370 case AVMEDIA_TYPE_AUDIO:
371 srccaps = gst_maru_codectype_to_audio_caps (NULL, codec->name, FALSE, codec);
374 GST_LOG("unknown media type.\n");
379 srccaps = gst_caps_from_string ("unknown/unknown");
382 sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
383 GST_PAD_ALWAYS, sinkcaps);
384 srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
385 GST_PAD_ALWAYS, srccaps);
387 gst_element_class_add_pad_template (element_class, srctempl);
388 gst_element_class_add_pad_template (element_class, sinktempl);
390 klass->codec = codec;
391 klass->sinktempl = sinktempl;
392 klass->srctempl = srctempl;
396 gst_marudec_class_init (GstMaruDecClass *klass)
398 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
399 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
401 parent_class = g_type_class_peek_parent (klass);
404 gobject_class->set_property = gst_marudec_set_property
405 gobject_class->get_property = gst_marudec_get_property
408 gobject_class->finalize = gst_marudec_finalize;
409 gstelement_class->change_state = gst_marudec_change_state;
413 gst_marudec_init (GstMaruDec *marudec)
415 GstMaruDecClass *oclass;
417 oclass = (GstMaruDecClass*) (G_OBJECT_GET_CLASS(marudec));
419 marudec->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
420 gst_pad_set_setcaps_function (marudec->sinkpad,
421 GST_DEBUG_FUNCPTR(gst_marudec_setcaps));
422 gst_pad_set_event_function (marudec->sinkpad,
423 GST_DEBUG_FUNCPTR(gst_marudec_sink_event));
424 gst_pad_set_chain_function (marudec->sinkpad,
425 GST_DEBUG_FUNCPTR(gst_marudec_chain));
427 marudec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src") ;
428 gst_pad_use_fixed_caps (marudec->srcpad);
429 gst_pad_set_event_function (marudec->srcpad,
430 GST_DEBUG_FUNCPTR(gst_marudec_src_event));
432 gst_element_add_pad (GST_ELEMENT(marudec), marudec->sinkpad);
433 gst_element_add_pad (GST_ELEMENT(marudec), marudec->srcpad);
435 marudec->context = g_malloc0 (sizeof(CodecContext));
436 marudec->context->video.pix_fmt = PIX_FMT_NONE;
437 marudec->context->audio.sample_fmt = SAMPLE_FMT_NONE;
439 marudec->opened = FALSE;
440 marudec->format.video.par_n = -1;
441 marudec->format.video.fps_n = -1;
442 marudec->format.video.old_fps_n = -1;
444 marudec->queued = NULL;
445 gst_segment_init (&marudec->segment, GST_FORMAT_TIME);
447 marudec->dev = g_malloc0 (sizeof(CodecDevice));
449 CODEC_LOG (ERR, "failed to allocate memory.\n");
454 gst_marudec_finalize (GObject *object)
456 GstMaruDec *marudec = (GstMaruDec *) object;
458 if (marudec->context) {
459 g_free (marudec->context);
460 marudec->context = NULL;
463 G_OBJECT_CLASS (parent_class)->finalize (object);
467 gst_marudec_src_event (GstPad *pad, GstEvent *event)
472 marudec = (GstMaruDec *) gst_pad_get_parent (pad);
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.*/
480 GstClockTimeDiff diff;
481 GstClockTime timestamp;
483 gst_event_parse_qos (event, &proportion, &diff, ×tamp);
485 /* update our QoS values */
486 gst_marudec_update_qos (marudec, proportion, timestamp + diff);
493 /* forward upstream */
494 res = gst_pad_push_event (marudec->sinkpad, event);
496 gst_object_unref (marudec);
502 gst_marudec_sink_event (GstPad *pad, GstEvent *event)
505 gboolean ret = FALSE;
507 marudec = (GstMaruDec *) gst_pad_get_parent (pad);
509 GST_DEBUG_OBJECT (marudec, "Handling %s event",
510 GST_EVENT_TYPE_NAME (event));
512 switch (GST_EVENT_TYPE (event)) {
514 gst_marudec_drain (marudec);
516 case GST_EVENT_FLUSH_STOP:
518 if (marudec->opened) {
519 codec_flush_buffers (marudec->context, marudec->dev);
522 gst_marudec_reset_ts (marudec);
523 gst_marudec_reset_qos (marudec);
525 gst_marudec_flush_pcache (marudec);
526 marudec->waiting_for_key = TRUE;
528 gst_segment_init (&marudec->segment, GST_FORMAT_TIME);
529 clear_queued (marudec);
532 case GST_EVENT_NEWSEGMENT:
536 gint64 start, stop, time;
539 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
540 &start, &stop, &time);
543 case GST_FORMAT_TIME:
545 case GST_FORMAT_BYTES:
548 bit_rate = marudec->context->bit_rate;
551 GST_WARNING_OBJECT (marudec, "no bitrate to convert BYTES to TIME");
552 gst_event_unref (event);
553 gst_object_unref (marudec);
557 GST_DEBUG_OBJECT (marudec, "bitrate: %d", bit_rate);
560 start = gst_util_uint64_scale_int (start, GST_SECOND, bit_rate);
563 stop = gst_util_uint64_scale_int (stop, GST_SECOND, bit_rate);
566 time = gst_util_uint64_scale_int (time, GST_SECOND, bit_rate);
569 gst_event_unref (event);
571 format = GST_FORMAT_TIME;
574 event = gst_event_new_new_segment (update, rate, format,
579 GST_WARNING_OBJECT (marudec, "unknown format received in NEWSEGMENT");
580 gst_event_unref (event);
581 gst_object_unref (marudec);
585 if (marudec->context->codec) {
586 gst_marudec_drain (marudec);
589 GST_DEBUG_OBJECT (marudec,
590 "NEWSEGMENT in time start %" GST_TIME_FORMAT " -- stop %"
591 GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
593 gst_segment_set_newsegment_full (&marudec->segment, update,
594 rate, arate, format, start, stop, time);
601 ret = gst_pad_push_event (marudec->srcpad, event);
603 gst_object_unref (marudec);
611 gst_marudec_setcaps (GstPad *pad, GstCaps *caps)
614 GstMaruDecClass *oclass;
615 GstStructure *structure;
620 GST_DEBUG_OBJECT (pad, "setcaps called.");
622 marudec = (GstMaruDec *) (gst_pad_get_parent (pad));
623 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
625 GST_OBJECT_LOCK (marudec);
627 if (marudec->opened) {
628 GST_OBJECT_UNLOCK (marudec);
629 gst_marudec_drain (marudec);
630 GST_OBJECT_LOCK (marudec);
631 gst_marudec_close (marudec);
634 GST_LOG_OBJECT (marudec, "size %dx%d", marudec->context->video.width,
635 marudec->context->video.height);
637 if (!strcmp(oclass->codec->name, "wmv3") ||
638 !strcmp(oclass->codec->name, "vc1")) {
639 gst_maru_caps_to_codecname (caps, oclass->codec->name, NULL);
642 gst_maru_caps_with_codecname (oclass->codec->name, oclass->codec->media_type,
643 caps, marudec->context);
645 GST_LOG_OBJECT (marudec, "size after %dx%d", marudec->context->video.width,
646 marudec->context->video.height);
648 if (!marudec->context->video.fps_d || !marudec->context->video.fps_n) {
649 GST_DEBUG_OBJECT (marudec, "forcing 25/1 framerate");
650 marudec->context->video.fps_n = 1;
651 marudec->context->video.fps_d = 25;
654 structure = gst_caps_get_structure (caps, 0);
656 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
658 GST_DEBUG_OBJECT (marudec, "sink caps have pixel-aspect-ratio of %d:%d",
659 gst_value_get_fraction_numerator (par),
660 gst_value_get_fraction_denominator (par));
664 g_free(marudec->par);
666 marudec->par = g_new0 (GValue, 1);
667 gst_value_init_and_copy (marudec->par, par);
671 fps = gst_structure_get_value (structure, "framerate");
672 if (fps != NULL && GST_VALUE_HOLDS_FRACTION (fps)) {
673 marudec->format.video.fps_n = gst_value_get_fraction_numerator (fps);
674 marudec->format.video.fps_d = gst_value_get_fraction_denominator (fps);
675 GST_DEBUG_OBJECT (marudec, "Using framerate %d/%d from incoming",
676 marudec->format.video.fps_n, marudec->format.video.fps_d);
678 marudec->format.video.fps_n = -1;
679 GST_DEBUG_OBJECT (marudec, "Using framerate from codec");
683 if (strcmp (oclass->codec->name, "aac") == 0) {
684 const gchar *format = gst_structure_get_string (structure, "stream-format");
685 if (format == NULL || strcmp ("format", "raw") == 0) {
686 marudec->turnoff_parser = TRUE;
691 if (!gst_marudec_open (marudec)) {
692 GST_DEBUG_OBJECT (marudec, "Failed to open");
695 g_free(marudec->par);
699 GST_OBJECT_UNLOCK (marudec);
700 gst_object_unref (marudec);
705 gst_structure_get_int (structure, "width",
706 &marudec->format.video.clip_width);
707 gst_structure_get_int (structure, "height",
708 &marudec->format.video.clip_height);
710 GST_DEBUG_OBJECT (pad, "clipping to %dx%d",
711 marudec->format.video.clip_width, marudec->format.video.clip_height);
713 GST_OBJECT_UNLOCK (marudec);
714 gst_object_unref (marudec);
720 gst_marudec_open (GstMaruDec *marudec)
722 GstMaruDecClass *oclass;
724 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
730 if (gst_maru_avcodec_open (marudec->context,
731 oclass->codec, marudec->dev) < 0) {
732 gst_marudec_close (marudec);
733 GST_ERROR_OBJECT (marudec,
734 "maru_%sdec: Failed to open codec", oclass->codec->name);
738 marudec->opened = TRUE;
739 GST_LOG_OBJECT (marudec, "Opened codec %s", oclass->codec->name);
741 switch (oclass->codec->media_type) {
742 case AVMEDIA_TYPE_VIDEO:
743 marudec->format.video.width = 0;
744 marudec->format.video.height = 0;
745 marudec->format.video.clip_width = -1;
746 marudec->format.video.clip_height = -1;
747 marudec->format.video.pix_fmt = PIX_FMT_NB;
748 marudec->format.video.interlaced = FALSE;
750 case AVMEDIA_TYPE_AUDIO:
751 marudec->format.audio.samplerate = 0;
752 marudec->format.audio.channels = 0;
753 marudec->format.audio.depth = 0;
759 gst_marudec_reset_ts (marudec);
761 marudec->proportion = 0.0;
762 marudec->earliest_time = -1;
768 gst_marudec_close (GstMaruDec *marudec)
772 if (marudec->context->codecdata) {
773 g_free(marudec->context->codecdata);
774 marudec->context->codecdata = NULL;
781 ret = gst_maru_avcodec_close (marudec->context, marudec->dev);
784 g_free(marudec->dev);
793 gst_marudec_negotiate (GstMaruDec *marudec, gboolean force)
795 GstMaruDecClass *oclass;
798 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
800 switch (oclass->codec->media_type) {
801 case AVMEDIA_TYPE_VIDEO:
802 if (!force && marudec->format.video.width == marudec->context->video.width
803 && marudec->format.video.height == marudec->context->video.height
804 && marudec->format.video.fps_n == marudec->format.video.old_fps_n
805 && marudec->format.video.fps_d == marudec->format.video.old_fps_d
806 && marudec->format.video.pix_fmt == marudec->context->video.pix_fmt
807 && marudec->format.video.par_n == marudec->context->video.par_n
808 && marudec->format.video.par_d == marudec->context->video.par_d) {
811 marudec->format.video.width = marudec->context->video.width;
812 marudec->format.video.height = marudec->context->video.height;
813 marudec->format.video.old_fps_n = marudec->format.video.fps_n;
814 marudec->format.video.old_fps_d = marudec->format.video.fps_d;
815 marudec->format.video.pix_fmt = marudec->context->video.pix_fmt;
816 marudec->format.video.par_n = marudec->context->video.par_n;
817 marudec->format.video.par_d = marudec->context->video.par_d;
819 case AVMEDIA_TYPE_AUDIO:
821 gint depth = gst_maru_smpfmt_depth (marudec->context->audio.sample_fmt);
822 if (!force && marudec->format.audio.samplerate ==
823 marudec->context->audio.sample_rate &&
824 marudec->format.audio.channels == marudec->context->audio.channels &&
825 marudec->format.audio.depth == depth) {
828 marudec->format.audio.samplerate = marudec->context->audio.sample_rate;
829 marudec->format.audio.channels = marudec->context->audio.channels;
830 marudec->format.audio.depth = depth;
838 gst_maru_codectype_to_caps (oclass->codec->media_type, marudec->context,
839 oclass->codec->name, FALSE);
842 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION,
843 ("Could not find GStreamer caps mapping for codec '%s'.",
844 oclass->codec->name), (NULL));
848 switch (oclass->codec->media_type) {
849 case AVMEDIA_TYPE_VIDEO:
854 width = marudec->format.video.clip_width;
855 height = marudec->format.video.clip_height;
856 interlaced = marudec->format.video.interlaced;
858 if (width != -1 && height != -1) {
859 if (width < marudec->context->video.width) {
860 gst_caps_set_simple (caps, "width", G_TYPE_INT, width, NULL);
862 if (height < marudec->context->video.height) {
863 gst_caps_set_simple (caps, "height", G_TYPE_INT, height, NULL);
865 gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced,
868 if (marudec->format.video.fps_n != -1) {
869 gst_caps_set_simple (caps, "framerate",
870 GST_TYPE_FRACTION, marudec->format.video.fps_n,
871 marudec->format.video.fps_d, NULL);
874 gst_marudec_add_pixel_aspect_ratio (marudec,
875 gst_caps_get_structure (caps, 0));
880 case AVMEDIA_TYPE_AUDIO:
886 if (!gst_pad_set_caps (marudec->srcpad, caps)) {
887 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION, (NULL),
888 ("Could not set caps for decoder (%s), not fixed?",
889 oclass->codec->name));
890 gst_caps_unref (caps);
894 gst_caps_unref (caps);
900 new_aligned_buffer (gint size, GstCaps *caps)
904 buf = gst_buffer_new ();
905 GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = g_malloc0 (size);
906 GST_BUFFER_SIZE (buf) = size;
907 GST_BUFFER_FREE_FUNC (buf) = g_free;
910 gst_buffer_set_caps (buf, caps);
917 get_output_buffer (GstMaruDec *marudec, GstBuffer **outbuf)
926 if (G_UNLIKELY (!gst_marudec_negotiate (marudec, FALSE))) {
927 GST_DEBUG_OBJECT (marudec, "negotiate failed");
928 return GST_FLOW_NOT_NEGOTIATED;
931 pict_size = gst_maru_avpicture_size (marudec->context->video.pix_fmt,
932 marudec->context->video.width, marudec->context->video.height);
934 GST_DEBUG_OBJECT (marudec, "size of a picture is negative. "
935 "pixel format: %d, width: %d, height: %d",
936 marudec->context->video.pix_fmt, marudec->context->video.width,
937 marudec->context->video.height);
938 return GST_FLOW_ERROR;
941 CODEC_LOG (DEBUG, "outbuf size of decoded video: %d\n", pict_size);
943 if (pict_size < (256 * 1024)) {
944 /* GstPadBufferAllocFunction is mostly overridden by elements that can
945 * provide a hardware buffer in order to avoid additional memcpy operations.
947 gst_pad_set_bufferalloc_function(
948 GST_PAD_PEER(marudec->srcpad),
949 (GstPadBufferAllocFunction) codec_buffer_alloc);
951 CODEC_LOG (DEBUG, "request large size of memory. pict_size: %d\n", pict_size);
954 ret = gst_pad_alloc_buffer_and_set_caps (marudec->srcpad,
955 GST_BUFFER_OFFSET_NONE, pict_size,
956 GST_PAD_CAPS (marudec->srcpad), outbuf);
957 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
958 GST_DEBUG_OBJECT (marudec, "pad_alloc failed %d (%s)", ret,
959 gst_flow_get_name (ret));
963 if ((uintptr_t) GST_BUFFER_DATA (*outbuf) % 16) {
964 GST_DEBUG_OBJECT (marudec,
965 "Downstream can't allocate aligned buffers.");
966 gst_buffer_unref (*outbuf);
967 *outbuf = new_aligned_buffer (pict_size, GST_PAD_CAPS (marudec->srcpad));
970 codec_picture_copy (marudec->context, GST_BUFFER_DATA (*outbuf),
971 GST_BUFFER_SIZE (*outbuf), marudec->dev);
977 clip_video_buffer (GstMaruDec *dec, GstBuffer *buf,
978 GstClockTime in_ts, GstClockTime in_dur)
986 clip_audio_buffer (GstMaruDec *dec, GstBuffer *buf,
987 GstClockTime in_ts, GstClockTime in_dur)
990 gint64 diff, cstart, cstop;
993 if (G_UNLIKELY (dec->segment.format != GST_FORMAT_TIME)) {
994 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
998 // in_ts: in_timestamp. check a start time.
999 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts))) {
1000 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
1005 GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE;
1007 res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME, in_ts,
1008 stop, &cstart, &cstop);
1009 if (G_UNLIKELY (!res)) {
1010 GST_LOG_OBJECT (dec, "out of segment");
1011 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
1015 if (G_UNLIKELY ((diff = cstart - in_ts) > 0)) {
1017 gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, GST_SECOND) *
1018 (dec->format.audio.depth * dec->format.audio.channels);
1020 GST_DEBUG_OBJECT (dec, "clipping start to %" GST_TIME_FORMAT " %"
1021 G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
1023 GST_BUFFER_SIZE (buf) -= diff;
1024 GST_BUFFER_DATA (buf) += diff;
1028 if (G_UNLIKELY ((diff = stop - cstop) > 0)) {
1030 gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, GST_SECOND) *
1031 (dec->format.audio.depth * dec->format.audio.channels);
1033 GST_DEBUG_OBJECT (dec, "clipping stop to %" GST_TIME_FORMAT " %"
1034 G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstop), diff);
1036 GST_BUFFER_SIZE (buf) -= diff;
1039 GST_BUFFER_TIMESTAMP (buf) = cstart;
1040 GST_BUFFER_DURATION (buf) = cstop - cstart;
1042 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
1047 gst_marudec_video_frame (GstMaruDec *marudec, guint8 *data, guint size,
1048 const GstTSInfo *dec_info, gint64 in_offset, GstBuffer **outbuf,
1051 gint len = -1, have_data;
1052 gboolean mode_switch;
1054 GstClockTime out_timestamp, out_duration, out_pts;
1056 const GstTSInfo *out_info;
1058 decode = gst_marudec_do_qos (marudec, dec_info->timestamp, &mode_switch);
1060 CODEC_LOG (DEBUG, "decode video: input buffer size: %d\n", size);
1062 codec_decode_video (marudec->context, data, size,
1063 dec_info->idx, in_offset, outbuf,
1064 &have_data, marudec->dev);
1070 GST_DEBUG_OBJECT (marudec, "after decode: len %d, have_data %d",
1074 if (len < 0 && (mode_switch || marudec->context->skip_frame)) {
1078 if (len > 0 && have_data <= 0 && (mode_switch
1079 || marudec->context->skip_frame)) {
1080 marudec->last_out = -1;
1084 if (len < 0 || have_data <= 0) {
1085 // if (len < 0) { // have_data <= 0) {
1086 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1087 *ret, *outbuf, len);
1090 "return flow %d, out %p, len %d, have_data: %d\n",
1091 *ret, *outbuf, len, have_data);
1096 out_info = gst_ts_info_get (marudec, dec_info->idx);
1097 out_pts = out_info->timestamp;
1098 out_duration = out_info->duration;
1099 out_offset = out_info->offset;
1101 *ret = get_output_buffer (marudec, outbuf);
1102 if (G_UNLIKELY (*ret != GST_FLOW_OK)) {
1103 GST_DEBUG_OBJECT (marudec, "no output buffer");
1105 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1106 *ret, *outbuf, len);
1112 if (out_pts != -1) {
1113 out_timestamp = (GstClockTime) out_pts;
1114 GST_LOG_OBJECT (marudec, "using timestamp %" GST_TIME_FORMAT
1115 " returned by ffmpeg", GST_TIME_ARGS (out_timestamp));
1118 if (!GST_CLOCK_TIME_IS_VALID (out_timestamp) && marudec->next_out != -1) {
1119 out_timestamp = marudec->next_out;
1120 GST_LOG_OBJECT (marudec, "using next timestamp %" GST_TIME_FORMAT,
1121 GST_TIME_ARGS (out_timestamp));
1124 if (!GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
1125 out_timestamp = dec_info->timestamp;
1126 GST_LOG_OBJECT (marudec, "using in timestamp %" GST_TIME_FORMAT,
1127 GST_TIME_ARGS (out_timestamp));
1129 GST_BUFFER_TIMESTAMP (*outbuf) = out_timestamp;
1132 if (out_offset != GST_BUFFER_OFFSET_NONE) {
1133 GST_LOG_OBJECT (marudec, "Using offset returned by ffmpeg");
1134 } else if (out_timestamp != GST_CLOCK_TIME_NONE) {
1135 GstFormat out_fmt = GST_FORMAT_DEFAULT;
1136 GST_LOG_OBJECT (marudec, "Using offset converted from timestamp");
1138 gst_pad_query_peer_convert (marudec->sinkpad,
1139 GST_FORMAT_TIME, out_timestamp, &out_fmt, &out_offset);
1140 } else if (dec_info->offset != GST_BUFFER_OFFSET_NONE) {
1141 GST_LOG_OBJECT (marudec, "using in_offset %" G_GINT64_FORMAT,
1143 out_offset = dec_info->offset;
1145 GST_LOG_OBJECT (marudec, "no valid offset found");
1146 out_offset = GST_BUFFER_OFFSET_NONE;
1148 GST_BUFFER_OFFSET (*outbuf) = out_offset;
1151 if (GST_CLOCK_TIME_IS_VALID (out_duration)) {
1152 GST_LOG_OBJECT (marudec, "Using duration returned by ffmpeg");
1153 } else if (GST_CLOCK_TIME_IS_VALID (dec_info->duration)) {
1154 GST_LOG_OBJECT (marudec, "Using in_duration");
1155 out_duration = dec_info->duration;
1157 } else if (GST_CLOCK_TIME_IS_VALID (marudec->last_diff)) {
1158 GST_LOG_OBJECT (marudec, "Using last-diff");
1159 out_duration = marudec->last_diff;
1162 if (marudec->format.video.fps_n != -1 &&
1163 (marudec->format.video.fps_n != 1000 &&
1164 marudec->format.video.fps_d != 1)) {
1165 GST_LOG_OBJECT (marudec, "using input framerate for duration");
1166 out_duration = gst_util_uint64_scale_int (GST_SECOND,
1167 marudec->format.video.fps_d, marudec->format.video.fps_n);
1169 if (marudec->context->video.fps_n != 0 &&
1170 (marudec->context->video.fps_d > 0 &&
1171 marudec->context->video.fps_d < 1000)) {
1172 GST_LOG_OBJECT (marudec, "using decoder's framerate for duration");
1173 out_duration = gst_util_uint64_scale_int (GST_SECOND,
1174 marudec->context->video.fps_n * 1,
1175 marudec->context->video.fps_d);
1177 GST_LOG_OBJECT (marudec, "no valid duration found");
1183 if (GST_CLOCK_TIME_IS_VALID (out_duration)) {
1184 out_duration += out_duration * marudec->picture->repeat_pict / 2;
1186 GST_BUFFER_DURATION (*outbuf) = out_duration;
1188 if (out_timestamp != -1 && out_duration != -1 && out_duration != 0) {
1189 marudec->next_out = out_timestamp + out_duration;
1191 marudec->next_out = -1;
1195 if (G_UNLIKELY (!clip_video_buffer (marudec, *outbuf, out_timestamp,
1197 GST_DEBUG_OBJECT (marudec, "buffer clipped");
1198 gst_buffer_unref (*outbuf);
1200 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1201 *ret, *outbuf, len);
1205 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1206 *ret, *outbuf, len);
1211 gst_marudec_audio_frame (GstMaruDec *marudec, CodecElement *codec,
1212 guint8 *data, guint size,
1213 const GstTSInfo *dec_info, GstBuffer **outbuf,
1217 gint have_data = FF_MAX_AUDIO_FRAME_SIZE;
1218 GstClockTime out_timestamp, out_duration;
1222 new_aligned_buffer (FF_MAX_AUDIO_FRAME_SIZE,
1223 GST_PAD_CAPS (marudec->srcpad));
1225 CODEC_LOG (DEBUG, "decode audio, input buffer size: %d\n", size);
1227 len = codec_decode_audio (marudec->context,
1228 (int16_t *) GST_BUFFER_DATA (*outbuf), &have_data,
1229 data, size, marudec->dev);
1231 GST_DEBUG_OBJECT (marudec,
1232 "Decode audio: len=%d, have_data=%d", len, have_data);
1234 if (len >= 0 && have_data > 0) {
1235 GST_DEBUG_OBJECT (marudec, "Creating output buffer");
1236 if (!gst_marudec_negotiate (marudec, FALSE)) {
1237 gst_buffer_unref (*outbuf);
1240 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1241 *ret, *outbuf, len);
1245 GST_BUFFER_SIZE (*outbuf) = have_data;
1247 if (GST_CLOCK_TIME_IS_VALID (dec_info->timestamp)) {
1248 out_timestamp = dec_info->timestamp;
1250 out_timestamp = marudec->next_out;
1253 /* calculate based on number of samples */
1254 out_duration = gst_util_uint64_scale (have_data, GST_SECOND,
1255 marudec->format.audio.depth * marudec->format.audio.channels *
1256 marudec->format.audio.samplerate);
1258 out_offset = dec_info->offset;
1260 GST_DEBUG_OBJECT (marudec,
1261 "Buffer created. Size: %d, timestamp: %" GST_TIME_FORMAT
1262 ", duration: %" GST_TIME_FORMAT, have_data,
1263 GST_TIME_ARGS (out_timestamp), GST_TIME_ARGS (out_duration));
1265 GST_BUFFER_TIMESTAMP (*outbuf) = out_timestamp;
1266 GST_BUFFER_DURATION (*outbuf) = out_duration;
1267 GST_BUFFER_OFFSET (*outbuf) = out_offset;
1268 gst_buffer_set_caps (*outbuf, GST_PAD_CAPS (marudec->srcpad));
1270 if (GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
1271 marudec->next_out = out_timestamp + out_duration;
1274 if (G_UNLIKELY (!clip_audio_buffer (marudec, *outbuf,
1275 out_timestamp, out_duration))) {
1276 GST_DEBUG_OBJECT (marudec, "buffer_clipped");
1277 gst_buffer_unref (*outbuf);
1279 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d", *ret, *outbuf, len);
1283 gst_buffer_unref (*outbuf);
1287 if (len == -1 && !strcmp(codec->name, "aac")) {
1288 GST_ELEMENT_ERROR (marudec, STREAM, DECODE, (NULL),
1289 ("Decoding of AAC stream by FFMPEG failed."));
1290 *ret = GST_FLOW_ERROR;
1293 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1294 *ret, *outbuf, len);
1299 gst_marudec_frame (GstMaruDec *marudec, guint8 *data, guint size,
1300 gint *got_data, const GstTSInfo *dec_info, gint64 in_offset, GstFlowReturn *ret)
1302 GstMaruDecClass *oclass;
1303 GstBuffer *outbuf = NULL;
1304 gint have_data = 0, len = 0;
1306 if (G_UNLIKELY (marudec->context->codec == NULL)) {
1307 GST_ERROR_OBJECT (marudec, "no codec context");
1312 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1314 switch (oclass->codec->media_type) {
1315 case AVMEDIA_TYPE_VIDEO:
1316 len = gst_marudec_video_frame (marudec, data, size,
1317 dec_info, in_offset, &outbuf, ret);
1319 case AVMEDIA_TYPE_AUDIO:
1320 len = gst_marudec_audio_frame (marudec, oclass->codec, data, size,
1321 dec_info, &outbuf, ret);
1322 if (outbuf == NULL && marudec->discont) {
1323 GST_DEBUG_OBJECT (marudec, "no buffer but keeping timestamp");
1324 // marudec->clear_ts = FALSE;
1328 GST_ERROR_OBJECT (marudec, "Asked to decode non-audio/video frame!");
1329 g_assert_not_reached ();
1337 if (len < 0 || have_data < 0) {
1338 GST_WARNING_OBJECT (marudec,
1339 "maru_%sdec: decoding error (len: %d, have_data: %d)",
1340 oclass->codec->name, len, have_data);
1343 } else if (len == 0 && have_data == 0) {
1351 GST_LOG_OBJECT (marudec,
1352 "Decoded data, now pushing buffer %p with offset %" G_GINT64_FORMAT
1353 ", timestamp %" GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT,
1354 outbuf, GST_BUFFER_OFFSET (outbuf),
1355 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1356 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
1358 if (marudec->discont) {
1359 /* GST_BUFFER_FLAG_DISCONT :
1360 * the buffer marks a data discontinuity in the stream. This typically
1361 * occurs after a seek or a dropped buffer from a live or network source.
1363 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
1364 marudec->discont = FALSE;
1367 if (marudec->segment.rate > 0.0) {
1369 *ret = gst_pad_push (marudec->srcpad, outbuf);
1372 GST_DEBUG_OBJECT (marudec, "queued frame");
1373 marudec->queued = g_list_prepend (marudec->queued, outbuf);
1377 GST_DEBUG_OBJECT (marudec, "Didn't get a decoded buffer");
1383 static GstFlowReturn
1384 gst_marudec_chain (GstPad *pad, GstBuffer *buffer)
1386 GstMaruDec *marudec;
1387 GstMaruDecClass *oclass;
1389 gint in_size, len, have_data;
1390 GstFlowReturn ret = GST_FLOW_OK;
1391 GstClockTime in_timestamp;
1392 GstClockTime in_duration;
1395 const GstTSInfo *in_info;
1396 const GstTSInfo *dec_info;
1398 marudec = (GstMaruDec *) (GST_PAD_PARENT (pad));
1400 if (G_UNLIKELY (!marudec->opened)) {
1402 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1403 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION, (NULL),
1404 ("maru_%sdec: input format was not set before data start",
1405 oclass->codec->name));
1406 gst_buffer_unref (buffer);
1407 return GST_FLOW_NOT_NEGOTIATED;
1410 discont = GST_BUFFER_IS_DISCONT (buffer);
1413 if (G_UNLIKELY (discont)) {
1414 GST_DEBUG_OBJECT (marudec, "received DISCONT");
1415 gst_marudec_drain (marudec);
1416 // gst_marudec_flush_pcache (marudec);
1417 codec_flush_buffers (marudec->context, marudec->dev);
1418 marudec->discont = TRUE;
1419 gst_marudec_reset_ts (marudec);
1421 // marudec->clear_ts = TRUE;
1423 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1425 if (G_UNLIKELY (marudec->waiting_for_key)) {
1426 if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT) &&
1427 oclass->codec->media_type != AVMEDIA_TYPE_AUDIO) {
1430 marudec->waiting_for_key = FALSE;
1433 if (marudec->pcache) {
1434 GST_LOG_OBJECT (marudec, "join parse cache");
1435 buffer = gst_buffer_join (marudec->pcache, buffer);
1436 marudec->pcache = NULL;
1440 in_timestamp = GST_BUFFER_TIMESTAMP (buffer);
1441 in_duration = GST_BUFFER_DURATION (buffer);
1442 in_offset = GST_BUFFER_OFFSET (buffer);
1444 in_info = gst_ts_info_store (marudec, in_timestamp, in_duration, in_offset);
1447 if (in_timestamp != -1) {
1448 if (!marudec->reordered_in && marudec->last_in != -1) {
1449 if (in_timestamp < marudec->last_in) {
1450 GST_LOG_OBJECT (marudec, "detected reordered input timestamps");
1451 marudec->reordered_in = TRUE;
1452 marudec->last_diff = GST_CLOCK_TIME_NONE;
1453 } else if (in_timestamp > marudec->last_in) {
1455 diff = in_timestamp - marudec->last_in;
1456 if (marudec->last_frames) {
1457 diff /= marudec->last_frames;
1460 GST_LOG_OBJECT (marudec, "estimated duration %" GST_TIME_FORMAT " %u",
1461 GST_TIME_ARGS (diff), marudec->last_frames);
1463 marudec->last_diff = diff;
1466 marudec->last_in = in_timestamp;
1467 marudec->last_frames;
1471 GST_LOG_OBJECT (marudec,
1472 "Received new data of size %u, offset: %" G_GUINT64_FORMAT ", ts:%"
1473 GST_TIME_FORMAT ", dur: %" GST_TIME_FORMAT ", info %d",
1474 GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer),
1475 GST_TIME_ARGS (in_timestamp), GST_TIME_ARGS (in_duration), in_info->idx);
1477 in_buf = GST_BUFFER_DATA (buffer);
1478 in_size = GST_BUFFER_SIZE (buffer);
1483 gst_marudec_frame (marudec, in_buf, in_size, &have_data, dec_info, in_offset, &ret);
1486 if (marudec->clear_ts) {
1487 in_timestamp = GST_CLOCK_TIME_NONE;
1488 in_duration = GST_CLOCK_TIME_NONE;
1489 in_offset = GST_BUFFER_OFFSET_NONE;
1490 in_info = GST_TS_INFO_NONE;
1492 marudec->clear_ts = TRUE;
1496 gst_buffer_unref (buffer);
1501 static GstStateChangeReturn
1502 gst_marudec_change_state (GstElement *element, GstStateChange transition)
1504 GstMaruDec *marudec = (GstMaruDec *) element;
1505 GstStateChangeReturn ret;
1507 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1509 switch (transition) {
1510 case GST_STATE_CHANGE_PAUSED_TO_READY:
1511 GST_OBJECT_LOCK (marudec);
1512 gst_marudec_close (marudec);
1513 GST_OBJECT_UNLOCK (marudec);
1516 clear_queued (marudec);
1526 gst_marudec_register (GstPlugin *plugin, GList *element)
1528 GTypeInfo typeinfo = {
1529 sizeof (GstMaruDecClass),
1530 (GBaseInitFunc) gst_marudec_base_init,
1532 (GClassInitFunc) gst_marudec_class_init,
1535 sizeof (GstMaruDec),
1537 (GInstanceInitFunc) gst_marudec_init,
1542 gint rank = GST_RANK_PRIMARY;
1543 GList *elem = element;
1544 CodecElement *codec = NULL;
1550 /* register element */
1552 codec = (CodecElement *)(elem->data);
1557 if (codec->codec_type != CODEC_TYPE_DECODE) {
1561 type_name = g_strdup_printf ("maru_%sdec", codec->name);
1562 type = g_type_from_name (type_name);
1564 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1565 g_type_set_qdata (type, GST_MARUDEC_PARAMS_QDATA, (gpointer) codec);
1568 if (!gst_element_register (plugin, type_name, rank, type)) {
1573 } while ((elem = elem->next));