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 void 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++;
161 GstClockTime stream_time, jitter;
166 gst_segment_to_stream_time (&marudec->segment, GST_FORMAT_TIME,
168 jitter = GST_CLOCK_DIFF (qostime, earliest_time);
170 gst_message_new_qos (GST_OBJECT_CAST (marudec), FALSE, qostime,
171 stream_time, timestamp, GST_CLOCK_TIME_NONE);
172 gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
173 gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS,
174 marudec->processed, marudec->dropped);
175 gst_element_post_message (GST_ELEMENT_CAST (marudec), qos_msg);
181 marudec->processed++;
186 clear_queued (GstMaruDec *marudec)
188 g_list_foreach (marudec->queued, (GFunc) gst_mini_object_unref, NULL);
189 g_list_free (marudec->queued);
190 marudec->queued = NULL;
194 flush_queued (GstMaruDec *marudec)
196 GstFlowReturn res = GST_FLOW_OK;
198 GST_DEBUG_OBJECT (marudec, "flush queued");
200 while (marudec->queued) {
201 GstBuffer *buf = GST_BUFFER_CAST (marudec->queued->data);
203 GST_LOG_OBJECT (marudec, "pushing buffer %p, offset %"
204 G_GUINT64_FORMAT ", timestamp %"
205 GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, buf,
206 GST_BUFFER_OFFSET (buf),
207 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
208 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
210 res = gst_pad_push (marudec->srcpad, buf);
213 g_list_delete_link (marudec->queued, marudec->queued);
220 gst_marudec_drain (GstMaruDec *marudec)
223 GstMaruDecClass *oclass;
224 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
227 GST_DEBUG_OBJECT (marudec, "drain frame");
229 gint have_data, len, try = 0;
235 gst_marudec_frame (marudec, NULL, 0, &have_data, &ts_info_none, 0, &ret);
237 if (len < 0 || have_data == 0) {
240 } while (try++ < 10);
243 if (marudec->segment.rate < 0.0) {
244 GST_DEBUG_OBJECT (marudec, "reverse playback");
245 flush_queued (marudec);
253 gst_marudec_base_init (GstMaruDecClass *klass)
255 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
256 GstCaps *sinkcaps = NULL, *srccaps = NULL;
257 GstPadTemplate *sinktempl, *srctempl;
259 gchar *longname, *classification, *description;
262 (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
263 GST_MARUDEC_PARAMS_QDATA);
265 longname = g_strdup_printf ("%s Decoder", codec->longname);
266 classification = g_strdup_printf ("Codec/Decoder/%s",
267 (codec->media_type == AVMEDIA_TYPE_VIDEO) ?
269 description = g_strdup_printf("%s Decoder", codec->name);
271 gst_element_class_set_details_simple (element_class,
275 "Kitae Kim <kt920.kim@samsung.com>");
278 g_free (classification);
279 g_free (description);
281 sinkcaps = gst_maru_codecname_to_caps (codec->name, NULL, FALSE);
283 sinkcaps = gst_caps_from_string ("unknown/unknown");
286 switch (codec->media_type) {
287 case AVMEDIA_TYPE_VIDEO:
288 srccaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv");
290 case AVMEDIA_TYPE_AUDIO:
291 srccaps = gst_maru_codectype_to_audio_caps (NULL, codec->name, FALSE, codec);
294 GST_LOG("unknown media type");
299 srccaps = gst_caps_from_string ("unknown/unknown");
302 sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
303 GST_PAD_ALWAYS, sinkcaps);
304 srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
305 GST_PAD_ALWAYS, srccaps);
307 gst_element_class_add_pad_template (element_class, srctempl);
308 gst_element_class_add_pad_template (element_class, sinktempl);
310 klass->codec = codec;
311 klass->sinktempl = sinktempl;
312 klass->srctempl = srctempl;
316 gst_marudec_class_init (GstMaruDecClass *klass)
318 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
319 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
321 parent_class = g_type_class_peek_parent (klass);
324 gobject_class->set_property = gst_marudec_set_property
325 gobject_class->get_property = gst_marudec_get_property
328 gobject_class->finalize = gst_marudec_finalize;
329 gstelement_class->change_state = gst_marudec_change_state;
333 gst_marudec_init (GstMaruDec *marudec)
335 GstMaruDecClass *oclass;
337 oclass = (GstMaruDecClass*) (G_OBJECT_GET_CLASS(marudec));
339 marudec->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
340 gst_pad_set_setcaps_function (marudec->sinkpad,
341 GST_DEBUG_FUNCPTR(gst_marudec_setcaps));
342 gst_pad_set_event_function (marudec->sinkpad,
343 GST_DEBUG_FUNCPTR(gst_marudec_sink_event));
344 gst_pad_set_chain_function (marudec->sinkpad,
345 GST_DEBUG_FUNCPTR(gst_marudec_chain));
347 marudec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src") ;
348 gst_pad_use_fixed_caps (marudec->srcpad);
349 gst_pad_set_event_function (marudec->srcpad,
350 GST_DEBUG_FUNCPTR(gst_marudec_src_event));
352 gst_element_add_pad (GST_ELEMENT(marudec), marudec->sinkpad);
353 gst_element_add_pad (GST_ELEMENT(marudec), marudec->srcpad);
355 marudec->context = g_malloc0 (sizeof(CodecContext));
356 marudec->context->video.pix_fmt = PIX_FMT_NONE;
357 marudec->context->audio.sample_fmt = SAMPLE_FMT_NONE;
359 marudec->opened = FALSE;
360 marudec->format.video.par_n = -1;
361 marudec->format.video.fps_n = -1;
362 marudec->format.video.old_fps_n = -1;
364 marudec->queued = NULL;
365 gst_segment_init (&marudec->segment, GST_FORMAT_TIME);
369 gst_marudec_finalize (GObject *object)
371 GstMaruDec *marudec = (GstMaruDec *) object;
373 GST_DEBUG_OBJECT (marudec, "finalize object and release context");
374 g_free (marudec->context);
375 marudec->context = NULL;
377 G_OBJECT_CLASS (parent_class)->finalize (object);
381 gst_marudec_src_event (GstPad *pad, GstEvent *event)
386 marudec = (GstMaruDec *) gst_pad_get_parent (pad);
388 switch (GST_EVENT_TYPE (event)) {
389 /* Quality Of Service (QOS) event contains a report
390 about the current real-time performance of the stream.*/
394 GstClockTimeDiff diff;
395 GstClockTime timestamp;
397 gst_event_parse_qos (event, &proportion, &diff, ×tamp);
399 /* update our QoS values */
400 gst_marudec_update_qos (marudec, proportion, timestamp + diff);
407 /* forward upstream */
408 res = gst_pad_push_event (marudec->sinkpad, event);
410 gst_object_unref (marudec);
416 gst_marudec_sink_event (GstPad *pad, GstEvent *event)
419 gboolean ret = FALSE;
421 marudec = (GstMaruDec *) gst_pad_get_parent (pad);
423 GST_DEBUG_OBJECT (marudec, "Handling %s event",
424 GST_EVENT_TYPE_NAME (event));
426 switch (GST_EVENT_TYPE (event)) {
428 gst_marudec_drain (marudec);
430 case GST_EVENT_FLUSH_STOP:
432 if (marudec->opened) {
433 GST_DEBUG_OBJECT (marudec, "flush decoded buffers");
434 interface->flush_buffers (marudec->context, marudec->dev);
437 gst_marudec_reset_ts (marudec);
438 gst_marudec_reset_qos (marudec);
439 gst_segment_init (&marudec->segment, GST_FORMAT_TIME);
440 clear_queued (marudec);
443 case GST_EVENT_NEWSEGMENT:
447 gint64 start, stop, time;
450 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
451 &start, &stop, &time);
454 case GST_FORMAT_TIME:
456 case GST_FORMAT_BYTES:
459 bit_rate = marudec->context->bit_rate;
462 GST_WARNING_OBJECT (marudec, "no bitrate to convert BYTES to TIME");
463 gst_event_unref (event);
464 gst_object_unref (marudec);
468 GST_DEBUG_OBJECT (marudec, "bitrate: %d", bit_rate);
471 start = gst_util_uint64_scale_int (start, GST_SECOND, bit_rate);
474 stop = gst_util_uint64_scale_int (stop, GST_SECOND, bit_rate);
477 time = gst_util_uint64_scale_int (time, GST_SECOND, bit_rate);
480 gst_event_unref (event);
482 format = GST_FORMAT_TIME;
485 event = gst_event_new_new_segment (update, rate, format,
490 GST_WARNING_OBJECT (marudec, "unknown format received in NEWSEGMENT");
491 gst_event_unref (event);
492 gst_object_unref (marudec);
496 if (marudec->opened) {
497 gst_marudec_drain (marudec);
500 GST_DEBUG_OBJECT (marudec,
501 "NEWSEGMENT in time start %" GST_TIME_FORMAT " -- stop %"
502 GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
504 gst_segment_set_newsegment_full (&marudec->segment, update,
505 rate, arate, format, start, stop, time);
512 ret = gst_pad_push_event (marudec->srcpad, event);
514 gst_object_unref (marudec);
522 gst_marudec_setcaps (GstPad *pad, GstCaps *caps)
525 GstMaruDecClass *oclass;
526 GstStructure *structure;
531 GST_DEBUG_OBJECT (pad, "setcaps called.");
533 marudec = (GstMaruDec *) (gst_pad_get_parent (pad));
534 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
536 GST_OBJECT_LOCK (marudec);
538 if (marudec->opened) {
539 GST_OBJECT_UNLOCK (marudec);
540 gst_marudec_drain (marudec);
541 GST_OBJECT_LOCK (marudec);
543 gst_marudec_close (marudec);
545 GST_LOG_OBJECT (marudec, "size %dx%d", marudec->context->video.width,
546 marudec->context->video.height);
548 if (!strcmp(oclass->codec->name, "wmv3") ||
549 !strcmp(oclass->codec->name, "vc1")) {
550 gst_maru_caps_to_codecname (caps, oclass->codec->name, NULL);
553 gst_maru_caps_with_codecname (oclass->codec->name, oclass->codec->media_type,
554 caps, marudec->context);
556 GST_LOG_OBJECT (marudec, "size after %dx%d", marudec->context->video.width,
557 marudec->context->video.height);
559 if (!marudec->context->video.fps_d || !marudec->context->video.fps_n) {
560 GST_DEBUG_OBJECT (marudec, "forcing 25/1 framerate");
561 marudec->context->video.fps_n = 1;
562 marudec->context->video.fps_d = 25;
565 structure = gst_caps_get_structure (caps, 0);
567 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
569 GST_DEBUG_OBJECT (marudec, "sink caps have pixel-aspect-ratio of %d:%d",
570 gst_value_get_fraction_numerator (par),
571 gst_value_get_fraction_denominator (par));
574 fps = gst_structure_get_value (structure, "framerate");
575 if (fps != NULL && GST_VALUE_HOLDS_FRACTION (fps)) {
576 marudec->format.video.fps_n = gst_value_get_fraction_numerator (fps);
577 marudec->format.video.fps_d = gst_value_get_fraction_denominator (fps);
578 GST_DEBUG_OBJECT (marudec, "Using framerate %d/%d from incoming",
579 marudec->format.video.fps_n, marudec->format.video.fps_d);
581 marudec->format.video.fps_n = -1;
582 GST_DEBUG_OBJECT (marudec, "Using framerate from codec");
586 if (strcmp (oclass->codec->name, "aac") == 0) {
587 const gchar *format = gst_structure_get_string (structure, "stream-format");
588 if (format == NULL || strcmp ("format", "raw") == 0) {
589 marudec->turnoff_parser = TRUE;
594 if (!gst_marudec_open (marudec)) {
595 GST_DEBUG_OBJECT (marudec, "Failed to open");
596 GST_OBJECT_UNLOCK (marudec);
597 gst_object_unref (marudec);
602 gst_structure_get_int (structure, "width",
603 &marudec->format.video.clip_width);
604 gst_structure_get_int (structure, "height",
605 &marudec->format.video.clip_height);
607 GST_DEBUG_OBJECT (pad, "clipping to %dx%d",
608 marudec->format.video.clip_width, marudec->format.video.clip_height);
610 GST_OBJECT_UNLOCK (marudec);
611 gst_object_unref (marudec);
617 gst_marudec_open (GstMaruDec *marudec)
619 GstMaruDecClass *oclass;
621 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
623 marudec->dev = g_try_malloc0 (sizeof(CodecDevice));
625 GST_ERROR_OBJECT (marudec, "failed to allocate memory for CodecDevice");
629 if (gst_maru_avcodec_open (marudec->context, oclass->codec, marudec->dev) < 0) {
630 g_free(marudec->dev);
632 GST_ERROR_OBJECT (marudec,
633 "maru_%sdec: Failed to open codec", oclass->codec->name);
637 marudec->opened = TRUE;
638 GST_LOG_OBJECT (marudec, "Opened codec %s", oclass->codec->name);
640 switch (oclass->codec->media_type) {
641 case AVMEDIA_TYPE_VIDEO:
642 marudec->format.video.width = 0;
643 marudec->format.video.height = 0;
644 marudec->format.video.clip_width = -1;
645 marudec->format.video.clip_height = -1;
646 marudec->format.video.pix_fmt = PIX_FMT_NB;
647 marudec->format.video.interlaced = FALSE;
649 case AVMEDIA_TYPE_AUDIO:
650 marudec->format.audio.samplerate = 0;
651 marudec->format.audio.channels = 0;
652 marudec->format.audio.depth = 0;
658 gst_marudec_reset_ts (marudec);
660 marudec->proportion = 0.0;
661 marudec->earliest_time = -1;
667 gst_marudec_close (GstMaruDec *marudec)
669 if (!marudec->opened) {
670 GST_DEBUG_OBJECT (marudec, "not opened yet");
674 if (marudec->context) {
675 g_free(marudec->context->codecdata);
676 marudec->context->codecdata = NULL;
683 gst_maru_avcodec_close (marudec->context, marudec->dev);
684 marudec->opened = FALSE;
687 g_free(marudec->dev);
694 gst_marudec_negotiate (GstMaruDec *marudec, gboolean force)
696 GstMaruDecClass *oclass;
699 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
701 switch (oclass->codec->media_type) {
702 case AVMEDIA_TYPE_VIDEO:
703 if (!force && marudec->format.video.width == marudec->context->video.width
704 && marudec->format.video.height == marudec->context->video.height
705 && marudec->format.video.fps_n == marudec->format.video.old_fps_n
706 && marudec->format.video.fps_d == marudec->format.video.old_fps_d
707 && marudec->format.video.pix_fmt == marudec->context->video.pix_fmt
708 && marudec->format.video.par_n == marudec->context->video.par_n
709 && marudec->format.video.par_d == marudec->context->video.par_d) {
712 marudec->format.video.width = marudec->context->video.width;
713 marudec->format.video.height = marudec->context->video.height;
714 marudec->format.video.old_fps_n = marudec->format.video.fps_n;
715 marudec->format.video.old_fps_d = marudec->format.video.fps_d;
716 marudec->format.video.pix_fmt = marudec->context->video.pix_fmt;
717 marudec->format.video.par_n = marudec->context->video.par_n;
718 marudec->format.video.par_d = marudec->context->video.par_d;
720 case AVMEDIA_TYPE_AUDIO:
722 gint depth = gst_maru_smpfmt_depth (marudec->context->audio.sample_fmt);
723 if (!force && marudec->format.audio.samplerate ==
724 marudec->context->audio.sample_rate &&
725 marudec->format.audio.channels == marudec->context->audio.channels &&
726 marudec->format.audio.depth == depth) {
729 marudec->format.audio.samplerate = marudec->context->audio.sample_rate;
730 marudec->format.audio.channels = marudec->context->audio.channels;
731 marudec->format.audio.depth = depth;
739 gst_maru_codectype_to_caps (oclass->codec->media_type, marudec->context,
740 oclass->codec->name, FALSE);
743 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION,
744 ("Could not find GStreamer caps mapping for codec '%s'.",
745 oclass->codec->name), (NULL));
749 switch (oclass->codec->media_type) {
750 case AVMEDIA_TYPE_VIDEO:
755 width = marudec->format.video.clip_width;
756 height = marudec->format.video.clip_height;
757 interlaced = marudec->format.video.interlaced;
759 if (width != -1 && height != -1) {
760 if (width < marudec->context->video.width) {
761 gst_caps_set_simple (caps, "width", G_TYPE_INT, width, NULL);
763 if (height < marudec->context->video.height) {
764 gst_caps_set_simple (caps, "height", G_TYPE_INT, height, NULL);
766 gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced,
769 if (marudec->format.video.fps_n != -1) {
770 gst_caps_set_simple (caps, "framerate",
771 GST_TYPE_FRACTION, marudec->format.video.fps_n,
772 marudec->format.video.fps_d, NULL);
777 case AVMEDIA_TYPE_AUDIO:
783 if (!gst_pad_set_caps (marudec->srcpad, caps)) {
784 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION, (NULL),
785 ("Could not set caps for decoder (%s), not fixed?",
786 oclass->codec->name));
787 gst_caps_unref (caps);
791 gst_caps_unref (caps);
797 new_aligned_buffer (gint size, GstCaps *caps)
801 buf = gst_buffer_new ();
802 GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = g_malloc0 (size);
803 GST_BUFFER_SIZE (buf) = size;
804 GST_BUFFER_FREE_FUNC (buf) = g_free;
807 gst_buffer_set_caps (buf, caps);
814 get_output_buffer (GstMaruDec *marudec, GstBuffer **outbuf)
823 if (G_UNLIKELY (!gst_marudec_negotiate (marudec, FALSE))) {
824 GST_DEBUG_OBJECT (marudec, "negotiate failed");
825 return GST_FLOW_NOT_NEGOTIATED;
828 pict_size = gst_maru_avpicture_size (marudec->context->video.pix_fmt,
829 marudec->context->video.width, marudec->context->video.height);
831 GST_DEBUG_OBJECT (marudec, "size of a picture is negative. "
832 "pixel format: %d, width: %d, height: %d",
833 marudec->context->video.pix_fmt, marudec->context->video.width,
834 marudec->context->video.height);
835 return GST_FLOW_ERROR;
838 GST_DEBUG_OBJECT (marudec, "outbuf size of decoded video %d", pict_size);
840 gst_pad_set_element_private(GST_PAD_PEER(marudec->srcpad), (gpointer)marudec);
842 /* GstPadBufferAllocFunction is mostly overridden by elements that can
843 * provide a hardware buffer in order to avoid additional memcpy operations.
845 gst_pad_set_bufferalloc_function(
846 GST_PAD_PEER(marudec->srcpad),
847 (GstPadBufferAllocFunction) interface->buffer_alloc_and_copy);
849 ret = gst_pad_alloc_buffer_and_set_caps (marudec->srcpad,
850 GST_BUFFER_OFFSET_NONE, pict_size,
851 GST_PAD_CAPS (marudec->srcpad), outbuf);
852 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
853 GST_DEBUG_OBJECT (marudec, "pad_alloc failed %d (%s)", ret,
854 gst_flow_get_name (ret));
862 clip_video_buffer (GstMaruDec *dec, GstBuffer *buf,
863 GstClockTime in_ts, GstClockTime in_dur)
871 clip_audio_buffer (GstMaruDec *dec, GstBuffer *buf,
872 GstClockTime in_ts, GstClockTime in_dur)
875 gint64 diff, cstart, cstop;
878 if (G_UNLIKELY (dec->segment.format != GST_FORMAT_TIME)) {
879 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
883 // in_ts: in_timestamp. check a start time.
884 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts))) {
885 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
890 GST_CLOCK_TIME_IS_VALID (in_dur) ? (in_ts + in_dur) : GST_CLOCK_TIME_NONE;
892 res = gst_segment_clip (&dec->segment, GST_FORMAT_TIME, in_ts,
893 stop, &cstart, &cstop);
894 if (G_UNLIKELY (!res)) {
895 GST_LOG_OBJECT (dec, "out of segment");
896 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
900 if (G_UNLIKELY ((diff = cstart - in_ts) > 0)) {
902 gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, GST_SECOND) *
903 (dec->format.audio.depth * dec->format.audio.channels);
905 GST_DEBUG_OBJECT (dec, "clipping start to %" GST_TIME_FORMAT " %"
906 G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstart), diff);
908 GST_BUFFER_SIZE (buf) -= diff;
909 GST_BUFFER_DATA (buf) += diff;
913 if (G_UNLIKELY ((diff = stop - cstop) > 0)) {
915 gst_util_uint64_scale_int (diff, dec->format.audio.samplerate, GST_SECOND) *
916 (dec->format.audio.depth * dec->format.audio.channels);
918 GST_DEBUG_OBJECT (dec, "clipping stop to %" GST_TIME_FORMAT " %"
919 G_GINT64_FORMAT " bytes", GST_TIME_ARGS (cstop), diff);
921 GST_BUFFER_SIZE (buf) -= diff;
924 GST_BUFFER_TIMESTAMP (buf) = cstart;
925 GST_BUFFER_DURATION (buf) = cstop - cstart;
927 GST_LOG_OBJECT (dec, "%sdropping", (res ? "not " : ""));
932 gst_marudec_video_frame (GstMaruDec *marudec, guint8 *data, guint size,
933 const GstTSInfo *dec_info, gint64 in_offset, GstBuffer **outbuf,
937 gboolean mode_switch;
939 GstClockTime out_timestamp, out_duration, out_pts;
941 const GstTSInfo *out_info;
944 decode = gst_marudec_do_qos (marudec, dec_info->timestamp, &mode_switch);
946 GST_DEBUG_OBJECT (marudec, "decode video: input buffer size %d", size);
948 len = interface->decode_video (marudec, data, size,
949 dec_info->idx, in_offset, outbuf, &have_data);
950 if (len < 0 || !have_data) {
954 *ret = get_output_buffer (marudec, outbuf);
955 if (G_UNLIKELY (*ret != GST_FLOW_OK)) {
956 GST_DEBUG_OBJECT (marudec, "no output buffer");
958 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
963 out_info = gst_ts_info_get (marudec, dec_info->idx);
964 out_pts = out_info->timestamp;
965 out_duration = out_info->duration;
966 out_offset = out_info->offset;
971 out_timestamp = (GstClockTime) out_pts;
972 GST_LOG_OBJECT (marudec, "using timestamp %" GST_TIME_FORMAT
973 " returned by ffmpeg", GST_TIME_ARGS (out_timestamp));
976 if (!GST_CLOCK_TIME_IS_VALID (out_timestamp) && marudec->next_out != -1) {
977 out_timestamp = marudec->next_out;
978 GST_LOG_OBJECT (marudec, "using next timestamp %" GST_TIME_FORMAT,
979 GST_TIME_ARGS (out_timestamp));
982 if (!GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
983 out_timestamp = dec_info->timestamp;
984 GST_LOG_OBJECT (marudec, "using in timestamp %" GST_TIME_FORMAT,
985 GST_TIME_ARGS (out_timestamp));
987 GST_BUFFER_TIMESTAMP (*outbuf) = out_timestamp;
990 if (out_offset != GST_BUFFER_OFFSET_NONE) {
991 GST_LOG_OBJECT (marudec, "Using offset returned by ffmpeg");
992 } else if (out_timestamp != GST_CLOCK_TIME_NONE) {
993 GstFormat out_fmt = GST_FORMAT_DEFAULT;
994 GST_LOG_OBJECT (marudec, "Using offset converted from timestamp");
996 gst_pad_query_peer_convert (marudec->sinkpad,
997 GST_FORMAT_TIME, out_timestamp, &out_fmt, &out_offset);
998 } else if (dec_info->offset != GST_BUFFER_OFFSET_NONE) {
999 GST_LOG_OBJECT (marudec, "using in_offset %" G_GINT64_FORMAT,
1001 out_offset = dec_info->offset;
1003 GST_LOG_OBJECT (marudec, "no valid offset found");
1004 out_offset = GST_BUFFER_OFFSET_NONE;
1006 GST_BUFFER_OFFSET (*outbuf) = out_offset;
1009 if (GST_CLOCK_TIME_IS_VALID (out_duration)) {
1010 GST_LOG_OBJECT (marudec, "Using duration returned by ffmpeg");
1011 } else if (GST_CLOCK_TIME_IS_VALID (dec_info->duration)) {
1012 GST_LOG_OBJECT (marudec, "Using in_duration");
1013 out_duration = dec_info->duration;
1015 if (marudec->format.video.fps_n != -1 &&
1016 (marudec->format.video.fps_n != 1000 &&
1017 marudec->format.video.fps_d != 1)) {
1018 GST_LOG_OBJECT (marudec, "using input framerate for duration");
1019 out_duration = gst_util_uint64_scale_int (GST_SECOND,
1020 marudec->format.video.fps_d, marudec->format.video.fps_n);
1022 if (marudec->context->video.fps_n != 0 &&
1023 (marudec->context->video.fps_d > 0 &&
1024 marudec->context->video.fps_d < 1000)) {
1025 GST_LOG_OBJECT (marudec, "using decoder's framerate for duration");
1026 out_duration = gst_util_uint64_scale_int (GST_SECOND,
1027 marudec->context->video.fps_n * 1,
1028 marudec->context->video.fps_d);
1030 GST_LOG_OBJECT (marudec, "no valid duration found");
1035 if (G_UNLIKELY (!clip_video_buffer (marudec, *outbuf, out_timestamp,
1037 GST_DEBUG_OBJECT (marudec, "buffer clipped");
1038 gst_buffer_unref (*outbuf);
1040 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1041 *ret, *outbuf, len);
1045 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1046 *ret, *outbuf, len);
1051 gst_marudec_audio_frame (GstMaruDec *marudec, CodecElement *codec,
1052 guint8 *data, guint size,
1053 const GstTSInfo *dec_info, GstBuffer **outbuf,
1057 gint have_data = FF_MAX_AUDIO_FRAME_SIZE;
1058 GstClockTime out_timestamp, out_duration;
1062 new_aligned_buffer (FF_MAX_AUDIO_FRAME_SIZE,
1063 GST_PAD_CAPS (marudec->srcpad));
1065 GST_DEBUG_OBJECT (marudec, "decode audio, input buffer size %d", size);
1067 len = interface->decode_audio (marudec->context,
1068 (int16_t *) GST_BUFFER_DATA (*outbuf), &have_data,
1069 data, size, marudec->dev);
1071 GST_DEBUG_OBJECT (marudec,
1072 "Decode audio: len=%d, have_data=%d", len, have_data);
1074 if (len >= 0 && have_data > 0) {
1075 GST_DEBUG_OBJECT (marudec, "Creating output buffer");
1076 if (!gst_marudec_negotiate (marudec, FALSE)) {
1077 gst_buffer_unref (*outbuf);
1080 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1081 *ret, *outbuf, len);
1085 GST_BUFFER_SIZE (*outbuf) = len;
1087 if (GST_CLOCK_TIME_IS_VALID (dec_info->timestamp)) {
1088 out_timestamp = dec_info->timestamp;
1090 out_timestamp = marudec->next_out;
1093 /* calculate based on number of samples */
1094 out_duration = gst_util_uint64_scale (have_data, GST_SECOND,
1095 marudec->format.audio.depth * marudec->format.audio.channels *
1096 marudec->format.audio.samplerate);
1098 out_offset = dec_info->offset;
1100 GST_DEBUG_OBJECT (marudec,
1101 "Buffer created. Size: %d, timestamp: %" GST_TIME_FORMAT
1102 ", duration: %" GST_TIME_FORMAT, have_data,
1103 GST_TIME_ARGS (out_timestamp), GST_TIME_ARGS (out_duration));
1105 GST_BUFFER_TIMESTAMP (*outbuf) = out_timestamp;
1106 GST_BUFFER_DURATION (*outbuf) = out_duration;
1107 GST_BUFFER_OFFSET (*outbuf) = out_offset;
1108 gst_buffer_set_caps (*outbuf, GST_PAD_CAPS (marudec->srcpad));
1110 if (GST_CLOCK_TIME_IS_VALID (out_timestamp)) {
1111 marudec->next_out = out_timestamp + out_duration;
1114 if (G_UNLIKELY (!clip_audio_buffer (marudec, *outbuf,
1115 out_timestamp, out_duration))) {
1116 GST_DEBUG_OBJECT (marudec, "buffer_clipped");
1117 gst_buffer_unref (*outbuf);
1119 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d", *ret, *outbuf, len);
1123 gst_buffer_unref (*outbuf);
1127 if (len == -1 && !strcmp(codec->name, "aac")) {
1128 GST_ELEMENT_ERROR (marudec, STREAM, DECODE, (NULL),
1129 ("Decoding of AAC stream by FFMPEG failed."));
1130 *ret = GST_FLOW_ERROR;
1133 GST_DEBUG_OBJECT (marudec, "return flow %d, out %p, len %d",
1134 *ret, *outbuf, len);
1139 gst_marudec_frame (GstMaruDec *marudec, guint8 *data, guint size,
1140 gint *got_data, const GstTSInfo *dec_info, gint64 in_offset, GstFlowReturn *ret)
1142 GstMaruDecClass *oclass;
1143 GstBuffer *outbuf = NULL;
1144 gint have_data = 0, len = 0;
1146 if (G_UNLIKELY (marudec->context->codec == NULL)) {
1147 GST_ERROR_OBJECT (marudec, "no codec context");
1152 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1154 switch (oclass->codec->media_type) {
1155 case AVMEDIA_TYPE_VIDEO:
1156 len = gst_marudec_video_frame (marudec, data, size,
1157 dec_info, in_offset, &outbuf, ret);
1159 case AVMEDIA_TYPE_AUDIO:
1160 len = gst_marudec_audio_frame (marudec, oclass->codec, data, size,
1161 dec_info, &outbuf, ret);
1162 if (outbuf == NULL && marudec->discont) {
1163 GST_DEBUG_OBJECT (marudec, "no buffer but keeping timestamp");
1167 GST_ERROR_OBJECT (marudec, "Asked to decode non-audio/video frame!");
1168 g_assert_not_reached ();
1176 if (len < 0 || have_data < 0) {
1177 GST_WARNING_OBJECT (marudec,
1178 "maru_%sdec: decoding error (len: %d, have_data: %d)",
1179 oclass->codec->name, len, have_data);
1182 } else if (len == 0 && have_data == 0) {
1190 GST_LOG_OBJECT (marudec,
1191 "Decoded data, now pushing buffer %p with offset %" G_GINT64_FORMAT
1192 ", timestamp %" GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT,
1193 outbuf, GST_BUFFER_OFFSET (outbuf),
1194 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1195 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
1197 if (marudec->discont) {
1198 /* GST_BUFFER_FLAG_DISCONT :
1199 * the buffer marks a data discontinuity in the stream. This typically
1200 * occurs after a seek or a dropped buffer from a live or network source.
1202 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
1203 marudec->discont = FALSE;
1206 if (marudec->segment.rate > 0.0) {
1208 *ret = gst_pad_push (marudec->srcpad, outbuf);
1211 GST_DEBUG_OBJECT (marudec, "queued frame");
1212 marudec->queued = g_list_prepend (marudec->queued, outbuf);
1216 GST_DEBUG_OBJECT (marudec, "Didn't get a decoded buffer");
1222 static GstFlowReturn
1223 gst_marudec_chain (GstPad *pad, GstBuffer *buffer)
1225 GstMaruDec *marudec;
1226 GstMaruDecClass *oclass;
1228 gint in_size, have_data;
1229 GstFlowReturn ret = GST_FLOW_OK;
1230 GstClockTime in_timestamp;
1231 GstClockTime in_duration;
1234 const GstTSInfo *in_info;
1235 const GstTSInfo *dec_info;
1237 marudec = (GstMaruDec *) (GST_PAD_PARENT (pad));
1239 if (G_UNLIKELY (!marudec->opened)) {
1241 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1242 GST_ELEMENT_ERROR (marudec, CORE, NEGOTIATION, (NULL),
1243 ("maru_%sdec: input format was not set before data start",
1244 oclass->codec->name));
1245 gst_buffer_unref (buffer);
1246 return GST_FLOW_NOT_NEGOTIATED;
1249 discont = GST_BUFFER_IS_DISCONT (buffer);
1250 if (G_UNLIKELY (discont)) {
1251 GST_DEBUG_OBJECT (marudec, "received DISCONT");
1252 gst_marudec_drain (marudec);
1253 interface->flush_buffers (marudec->context, marudec->dev);
1254 marudec->discont = TRUE;
1255 gst_marudec_reset_ts (marudec);
1258 oclass = (GstMaruDecClass *) (G_OBJECT_GET_CLASS (marudec));
1260 in_timestamp = GST_BUFFER_TIMESTAMP (buffer);
1261 in_duration = GST_BUFFER_DURATION (buffer);
1262 in_offset = GST_BUFFER_OFFSET (buffer);
1264 in_info = gst_ts_info_store (marudec, in_timestamp, in_duration, in_offset);
1265 GST_LOG_OBJECT (marudec,
1266 "Received new data of size %u, offset: %" G_GUINT64_FORMAT ", ts:%"
1267 GST_TIME_FORMAT ", dur: %" GST_TIME_FORMAT ", info %d",
1268 GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer),
1269 GST_TIME_ARGS (in_timestamp), GST_TIME_ARGS (in_duration), in_info->idx);
1271 in_buf = GST_BUFFER_DATA (buffer);
1272 in_size = GST_BUFFER_SIZE (buffer);
1276 gst_marudec_frame (marudec, in_buf, in_size, &have_data, dec_info, in_offset, &ret);
1278 gst_buffer_unref (buffer);
1283 static GstStateChangeReturn
1284 gst_marudec_change_state (GstElement *element, GstStateChange transition)
1286 GstMaruDec *marudec = (GstMaruDec *) element;
1287 GstStateChangeReturn ret;
1289 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1291 switch (transition) {
1292 case GST_STATE_CHANGE_PAUSED_TO_READY:
1293 GST_OBJECT_LOCK (marudec);
1294 gst_marudec_close (marudec);
1295 GST_OBJECT_UNLOCK (marudec);
1298 clear_queued (marudec);
1308 gst_marudec_register (GstPlugin *plugin, GList *element)
1310 GTypeInfo typeinfo = {
1311 sizeof (GstMaruDecClass),
1312 (GBaseInitFunc) gst_marudec_base_init,
1314 (GClassInitFunc) gst_marudec_class_init,
1317 sizeof (GstMaruDec),
1319 (GInstanceInitFunc) gst_marudec_init,
1324 gint rank = GST_RANK_PRIMARY;
1325 GList *elem = element;
1326 CodecElement *codec = NULL;
1332 /* register element */
1334 codec = (CodecElement *)(elem->data);
1339 if (codec->codec_type != CODEC_TYPE_DECODE) {
1343 type_name = g_strdup_printf ("maru_%sdec", codec->name);
1344 type = g_type_from_name (type_name);
1346 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1347 g_type_set_qdata (type, GST_MARUDEC_PARAMS_QDATA, (gpointer) codec);
1350 if (!gst_element_register (plugin, type_name, rank, type)) {
1355 } while ((elem = elem->next));