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 "gstmarudevice.h"
32 #include "gstmaruutils.h"
33 #include "gstmaruinterface.h"
34 #include <gst/base/gstadapter.h>
36 #define GST_MARUENC_PARAMS_QDATA g_quark_from_static_string("maruenc-params")
38 typedef struct _GstMaruEnc
45 CodecContext *context;
48 GstClockTime adapter_ts;
49 guint64 adapter_consumed;
59 gulong working_buf_size;
65 typedef struct _GstMaruEncClass
67 GstElementClass parent_class;
70 GstPadTemplate *sinktempl;
71 GstPadTemplate *srctempl;
75 static GstElementClass *parent_class = NULL;
77 static void gst_maruenc_base_init (GstMaruEncClass *klass);
78 static void gst_maruenc_class_init (GstMaruEncClass *klass);
79 static void gst_maruenc_init (GstMaruEnc *maruenc);
80 static void gst_maruenc_finalize (GObject *object);
82 static gboolean gst_maruenc_setcaps (GstPad *pad, GstCaps *caps);
83 static GstCaps *gst_maruenc_getcaps (GstPad *pad);
85 static GstCaps *gst_maruenc_get_possible_sizes (GstMaruEnc *maruenc,
86 GstPad *pad, const GstCaps *caps);
88 static GstFlowReturn gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer);
89 static GstFlowReturn gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer);
91 static gboolean gst_maruenc_event_video (GstPad *pad, GstEvent *event);
92 static gboolean gst_maruenc_event_src (GstPad *pad, GstEvent *event);
94 GstStateChangeReturn gst_maruenc_change_state (GstElement *element, GstStateChange transition);
96 #define DEFAULT_VIDEO_BITRATE 300000
97 #define DEFAULT_VIDEO_GOP_SIZE 15
98 #define DEFAULT_AUDIO_BITRATE 128000
100 #define DEFAULT_WIDTH 352
101 #define DEFAULT_HEIGHT 288
107 gst_maruenc_base_init (GstMaruEncClass *klass)
109 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
110 GstPadTemplate *sinktempl = NULL, *srctempl = NULL;
111 GstCaps *sinkcaps = NULL, *srccaps = NULL;
113 gchar *longname, *classification, *description;
116 (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
117 GST_MARUENC_PARAMS_QDATA);
119 longname = g_strdup_printf ("%s Encoder", codec->longname);
120 classification = g_strdup_printf ("Codec/Encoder/%s",
121 (codec->media_type == AVMEDIA_TYPE_VIDEO) ? "Video" : "Audio");
122 description = g_strdup_printf ("%s Encoder", codec->name);
124 gst_element_class_set_details_simple (element_class,
128 // "accelerated codec for Tizen Emulator",
129 "Kitae Kim <kt920.kim@samsung.com>");
132 g_free (classification);
135 if (!(srccaps = gst_maru_codecname_to_caps (codec->name, NULL, TRUE))) {
136 GST_DEBUG ("Couldn't get source caps for encoder '%s'", codec->name);
137 srccaps = gst_caps_new_simple ("unknown/unknown", NULL);
140 switch (codec->media_type) {
141 case AVMEDIA_TYPE_VIDEO:
142 sinkcaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv; video/x-raw-gray");
144 case AVMEDIA_TYPE_AUDIO:
145 sinkcaps = gst_maru_codectype_to_audio_caps (NULL, codec->name, TRUE, codec);
148 GST_LOG("unknown media type.\n");
153 GST_DEBUG ("Couldn't get sink caps for encoder '%s'", codec->name);
154 sinkcaps = gst_caps_new_simple ("unknown/unknown", NULL);
157 sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
158 GST_PAD_ALWAYS, sinkcaps);
159 srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
160 GST_PAD_ALWAYS, srccaps);
162 gst_element_class_add_pad_template (element_class, srctempl);
163 gst_element_class_add_pad_template (element_class, sinktempl);
165 klass->codec = codec;
166 klass->sinktempl = sinktempl;
167 klass->srctempl = srctempl;
168 klass->sinkcaps = NULL;
172 gst_maruenc_class_init (GstMaruEncClass *klass)
174 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
175 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
177 parent_class = g_type_class_peek_parent (klass);
180 gobject_class->set_property = gst_maruenc_set_property
181 gobject_class->get_property = gst_maruenc_get_property
184 gstelement_class->change_state = gst_maruenc_change_state;
186 gobject_class->finalize = gst_maruenc_finalize;
190 gst_maruenc_init (GstMaruEnc *maruenc)
192 GstMaruEncClass *oclass;
193 oclass = (GstMaruEncClass*) (G_OBJECT_GET_CLASS(maruenc));
195 maruenc->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
196 gst_pad_set_setcaps_function (maruenc->sinkpad,
197 GST_DEBUG_FUNCPTR(gst_maruenc_setcaps));
198 gst_pad_set_getcaps_function (maruenc->sinkpad,
199 GST_DEBUG_FUNCPTR(gst_maruenc_getcaps));
201 maruenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
202 gst_pad_use_fixed_caps (maruenc->srcpad);
204 if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO) {
205 gst_pad_set_chain_function (maruenc->sinkpad, gst_maruenc_chain_video);
206 gst_pad_set_event_function (maruenc->sinkpad, gst_maruenc_event_video);
207 gst_pad_set_event_function (maruenc->srcpad, gst_maruenc_event_src);
209 maruenc->bitrate = DEFAULT_VIDEO_BITRATE;
210 maruenc->buffer_size = 512 * 1024;
211 maruenc->gop_size = DEFAULT_VIDEO_GOP_SIZE;
216 } else if (oclass->codec->media_type == AVMEDIA_TYPE_AUDIO){
217 gst_pad_set_chain_function (maruenc->sinkpad, gst_maruenc_chain_audio);
218 maruenc->bitrate = DEFAULT_AUDIO_BITRATE;
221 gst_element_add_pad (GST_ELEMENT (maruenc), maruenc->sinkpad);
222 gst_element_add_pad (GST_ELEMENT (maruenc), maruenc->srcpad);
224 maruenc->context = g_malloc0 (sizeof(CodecContext));
225 maruenc->context->video.pix_fmt = PIX_FMT_NONE;
226 maruenc->context->audio.sample_fmt = SAMPLE_FMT_NONE;
228 maruenc->opened = FALSE;
231 maruenc->file = NULL;
233 maruenc->delay = g_queue_new ();
235 maruenc->dev = g_malloc0 (sizeof(CodecDevice));
237 printf("[gst-maru][%d] failed to allocate memory\n", __LINE__);
240 // need to know what adapter does.
241 maruenc->adapter = gst_adapter_new ();
245 gst_maruenc_finalize (GObject *object)
248 GstMaruEnc *maruenc = (GstMaruEnc *) object;
250 if (maruenc->opened) {
251 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
252 maruenc->opened = FALSE;
255 if (maruenc->context) {
256 g_free (maruenc->context);
257 maruenc->context = NULL;
260 g_queue_free (maruenc->delay);
262 g_free (maruenc->filename);
265 g_object_unref (maruenc->adapter);
267 G_OBJECT_CLASS (parent_class)->finalize (object);
271 gst_maruenc_get_possible_sizes (GstMaruEnc *maruenc, GstPad *pad,
274 GstCaps *othercaps = NULL;
275 GstCaps *tmpcaps = NULL;
276 GstCaps *intersect = NULL;
279 othercaps = gst_pad_peer_get_caps (maruenc->srcpad);
282 return gst_caps_copy (caps);
285 intersect = gst_caps_intersect (othercaps,
286 gst_pad_get_pad_template_caps (maruenc->srcpad));
287 gst_caps_unref (othercaps);
289 if (gst_caps_is_empty (intersect)) {
293 if (gst_caps_is_any (intersect)) {
294 return gst_caps_copy (caps);
297 tmpcaps = gst_caps_new_empty ();
299 for (i = 0; i <gst_caps_get_size (intersect); i++) {
300 GstStructure *s = gst_caps_get_structure (intersect, i);
301 const GValue *height = NULL;
302 const GValue *width = NULL;
303 const GValue *framerate = NULL;
306 height = gst_structure_get_value (s, "height");
307 width = gst_structure_get_value (s, "width");
308 framerate = gst_structure_get_value (s, "framerate");
310 tmps = gst_structure_new ("video/x-rwa-rgb", NULL);
312 gst_structure_set_value (tmps, "width", width);
315 gst_structure_set_value (tmps, "height", height);
318 gst_structure_set_value (tmps, "framerate", framerate);
320 gst_caps_merge_structure (tmpcaps, gst_structure_copy (tmps));
322 gst_structure_set_name (tmps, "video/x-raw-yuv");
323 gst_caps_merge_structure (tmpcaps, gst_structure_copy (tmps));
325 gst_structure_set_name (tmps, "video/x-raw-gray");
326 gst_caps_merge_structure (tmpcaps, tmps);
328 gst_caps_unref (intersect);
330 intersect = gst_caps_intersect (caps, tmpcaps);
331 gst_caps_unref (tmpcaps);
337 gst_maruenc_getcaps (GstPad *pad)
339 GstMaruEnc *maruenc = (GstMaruEnc *) GST_PAD_PARENT (pad);
340 GstMaruEncClass *oclass =
341 (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
342 CodecContext *ctx = NULL;
343 enum PixelFormat pixfmt;
344 GstCaps *caps = NULL;
345 GstCaps *finalcaps = NULL;
348 GST_DEBUG_OBJECT (maruenc, "getting caps");
350 if (!oclass->codec) {
351 GST_ERROR_OBJECT (maruenc, "codec element is null.");
355 if (oclass->codec->media_type == AVMEDIA_TYPE_AUDIO) {
356 caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
358 GST_DEBUG_OBJECT (maruenc, "audio caps, return template %" GST_PTR_FORMAT,
364 if (oclass->sinkcaps) {
365 caps = gst_maruenc_get_possible_sizes (maruenc, pad, oclass->sinkcaps);
366 GST_DEBUG_OBJECT (maruenc, "return cached caps %" GST_PTR_FORMAT, caps);
370 GST_DEBUG_OBJECT (maruenc, "probing caps");
373 for (pixfmt = 0;; pixfmt++) {
376 if ((pixfmt = oclass->codec->pix_fmts[i++]) == PIX_FMT_NONE) {
377 GST_DEBUG_OBJECT (maruenc,
378 "At the end of official pixfmt for this codec, breaking out");
382 GST_DEBUG_OBJECT (maruenc,
383 "Got an official pixfmt [%d], attempting to get caps", pixfmt);
384 tmpcaps = gst_maru_pixfmt_to_caps (pixfmt, NULL, oclass->codec->name);
386 GST_DEBUG_OBJECT (maruenc, "Got caps, breaking out");
388 caps = gst_caps_new_empty ();
390 gst_caps_append (caps, tmpcaps);
394 GST_DEBUG_OBJECT (maruenc,
395 "Couldn't figure out caps without context, trying again with a context");
397 GST_DEBUG_OBJECT (maruenc, "pixfmt: %d", pixfmt);
398 if (pixfmt >= PIX_FMT_NB) {
399 GST_WARNING ("Invalid pixfmt, breaking out");
403 ctx = g_malloc0 (sizeof(CodecContext));
405 GST_DEBUG_OBJECT (maruenc, "no context");
409 ctx->video.width = DEFAULT_WIDTH;
410 ctx->video.height = DEFAULT_HEIGHT;
411 ctx->video.fps_n = 1;
412 ctx->video.fps_d = 25;
413 ctx->video.ticks_per_frame = 1;
414 ctx->bit_rate = DEFAULT_VIDEO_BITRATE;
416 // ctx->strict_std_compliance = -1;
417 ctx->video.pix_fmt = pixfmt;
419 GST_DEBUG ("Attempting to open codec");
420 if (gst_maru_avcodec_open (ctx, oclass->codec, maruenc->dev) >= 0
421 && ctx->video.pix_fmt == pixfmt) {
422 ctx->video.width = -1;
424 caps = gst_caps_new_empty ();
426 tmpcaps = gst_maru_codectype_to_caps (oclass->codec->media_type, ctx,
427 oclass->codec->name, TRUE);
429 gst_caps_append (caps, tmpcaps);
431 GST_LOG_OBJECT (maruenc,
432 "Couldn't get caps for codec: %s", oclass->codec->name);
434 gst_maru_avcodec_close (ctx, maruenc->dev);
436 GST_DEBUG_OBJECT (maruenc, "Opening codec failed with pixfmt: %d", pixfmt);
439 gst_maru_avcodec_close (ctx, maruenc->dev);
441 if (ctx->priv_data) {
442 gst_maru_avcodec_close (ctx, maruenc->dev);
449 caps = gst_maruenc_get_possible_sizes (maruenc, pad,
450 gst_pad_get_pad_template_caps (pad));
451 GST_DEBUG_OBJECT (maruenc, "probing gave nothing, "
452 "return template %" GST_PTR_FORMAT, caps);
456 GST_DEBUG_OBJECT (maruenc, "probed caps gave %" GST_PTR_FORMAT, caps);
457 oclass->sinkcaps = gst_caps_copy (caps);
459 finalcaps = gst_maruenc_get_possible_sizes (maruenc, pad, caps);
460 gst_caps_unref (caps);
466 gst_maruenc_setcaps (GstPad *pad, GstCaps *caps)
469 GstMaruEncClass *oclass;
471 GstCaps *allowed_caps;
473 enum PixelFormat pix_fmt;
476 maruenc = (GstMaruEnc *) (gst_pad_get_parent (pad));
477 oclass = (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
479 if (maruenc->opened) {
480 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
481 maruenc->opened = FALSE;
483 gst_pad_set_caps (maruenc->srcpad, NULL);
486 maruenc->context->bit_rate = maruenc->bitrate;
487 GST_DEBUG_OBJECT (maruenc, "Setting context to bitrate %lu, gop_size %d",
488 maruenc->bitrate, maruenc->gop_size);
492 // user defined properties
493 maruenc->context->gop_size = maruenc->gop_size;
494 maruenc->context->lmin = (maruenc->lmin * FF_QP2LAMBDA + 0.5);
495 maruenc->context->lmax = (maruenc->lmax * FF_QP2LAMBDA + 0.5);
497 // some other defaults
498 maruenc->context->b_frame_strategy = 0;
499 maruenc->context->coder_type = 0;
500 maruenc->context->context_model = 0;
501 maruenc->context->scenechange_threshold = 0;
502 maruenc->context->inter_threshold = 0;
504 if (maruenc->interlaced) {
505 maruenc->context->flags |=
506 CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME;
507 maruenc->picture->interlaced_frame = TRUE;
509 maruenc->picture->top_field_first = TRUE;
513 gst_maru_caps_with_codectype (oclass->codec->media_type, caps, maruenc->context);
515 if (!maruenc->context->video.fps_d) {
516 maruenc->context->video.fps_d = 25;
517 maruenc->context->video.fps_n = 1;
518 } else if (!strcmp(oclass->codec->name ,"mpeg4")
519 && (maruenc->context->video.fps_d > 65535)) {
520 maruenc->context->video.fps_n =
521 (gint) gst_util_uint64_scale_int (maruenc->context->video.fps_n,
522 65535, maruenc->context->video.fps_d);
523 maruenc->context->video.fps_d = 65535;
524 GST_LOG_OBJECT (maruenc, "MPEG4 : scaled down framerate to %d / %d",
525 maruenc->context->video.fps_d, maruenc->context->video.fps_n);
528 pix_fmt = maruenc->context->video.pix_fmt;
531 switch (oclass->codec->media_type) {
532 case AVMEDIA_TYPE_VIDEO:
536 width = maruenc->context->video.width;
537 height = maruenc->context->video.height;
538 buf_size = width * height * 6 + FF_MIN_BUFFER_SIZE + 100;
541 case AVMEDIA_TYPE_AUDIO:
542 buf_size = FF_MAX_AUDIO_FRAME_SIZE + 100;
550 maruenc->dev->buf_size = gst_maru_align_size(buf_size);
553 if (gst_maru_avcodec_open (maruenc->context,
554 oclass->codec, maruenc->dev) < 0) {
555 GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to open codec",
556 oclass->codec->name);
560 if (pix_fmt != maruenc->context->video.pix_fmt) {
561 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
562 GST_DEBUG_OBJECT (maruenc,
563 "maru_%senc: AV wants different colorspace (%d given, %d wanted)",
564 oclass->codec->name, pix_fmt, maruenc->context->video.pix_fmt);
568 if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO
569 && pix_fmt == PIX_FMT_NONE) {
570 GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to determine input format",
571 oclass->codec->name);
575 GST_DEBUG_OBJECT (maruenc, "picking an output format.");
576 allowed_caps = gst_pad_get_allowed_caps (maruenc->srcpad);
578 GST_DEBUG_OBJECT (maruenc, "but no peer, using template caps");
580 gst_caps_copy (gst_pad_get_pad_template_caps (maruenc->srcpad));
583 GST_DEBUG_OBJECT (maruenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
584 gst_maru_caps_with_codecname (oclass->codec->name,
585 oclass->codec->media_type, allowed_caps, maruenc->context);
588 gst_maru_codecname_to_caps (oclass->codec->name, maruenc->context, TRUE);
590 GST_DEBUG("Unsupported codec - no caps found");
591 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
595 icaps = gst_caps_intersect (allowed_caps, other_caps);
596 gst_caps_unref (allowed_caps);
597 gst_caps_unref (other_caps);
598 if (gst_caps_is_empty (icaps)) {
599 gst_caps_unref (icaps);
603 if (gst_caps_get_size (icaps) > 1) {
607 gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
609 gst_caps_unref (icaps);
613 if (!gst_pad_set_caps (maruenc->srcpad, icaps)) {
614 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
615 gst_caps_unref (icaps);
618 gst_object_unref (maruenc);
620 maruenc->opened = TRUE;
626 gst_maruenc_setup_working_buf (GstMaruEnc *maruenc)
629 maruenc->context->video.width * maruenc->context->video.height * 6 +
632 if (maruenc->working_buf == NULL ||
633 maruenc->working_buf_size != wanted_size) {
634 if (maruenc->working_buf) {
635 g_free (maruenc->working_buf);
637 maruenc->working_buf_size = wanted_size;
638 maruenc->working_buf = g_malloc0 (maruenc->working_buf_size);
640 maruenc->buffer_size = wanted_size;
644 gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer)
646 GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
647 GstBuffer *outbuf = NULL;
648 gint ret_size = 0, frame_size = 0;
650 uint32_t mem_offset = 0;
651 uint8_t *working_buf = NULL;
653 GST_DEBUG_OBJECT (maruenc,
654 "Received buffer of time %" GST_TIME_FORMAT,
655 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
658 GST_OBJECT_LOCK (maruenc);
659 force_keyframe = maruenc->force_keyframe;
660 maruenc->force_keyframe = FALSE;
661 GST_OBJECT_UNLOCK (maruenc);
663 if (force_keyframe) {
664 maruenc->picture->pict_type = FF_I_TYPE;
668 frame_size = gst_maru_avpicture_size (maruenc->context->video.pix_fmt,
669 maruenc->context->video.width, maruenc->context->video.height);
670 g_return_val_if_fail (frame_size == GST_BUFFER_SIZE (buffer),
674 pts = gst_maru_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buffer) /
675 maruenc->context.video.ticks_per_frame,
676 maruenc->context.video.fps_n, maruen->context.video.fps_d);
679 // TODO: check whether this func needs or not.
680 gst_maruenc_setup_working_buf (maruenc);
683 codec_encode_video (maruenc->context, maruenc->working_buf,
684 maruenc->working_buf_size, GST_BUFFER_DATA (buffer),
685 GST_BUFFER_SIZE (buffer), GST_BUFFER_TIMESTAMP (buffer),
689 GstMaruEncClass *oclass =
690 (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
691 GST_ERROR_OBJECT (maruenc,
692 "maru_%senc: failed to encode buffer", oclass->codec->name);
693 gst_buffer_unref (buffer);
697 g_queue_push_tail (maruenc->delay, buffer);
699 buffer = g_queue_pop_head (maruenc->delay);
705 if (maruenc->file && maruenc->context->stats_out) {
706 if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
707 GST_ELEMENT_ERROR (maruenc, RESOURCE, WRITE,
708 (("Could not write to file \"%s\"."), maruenc->filename),
714 mem_offset = maruenc->dev->mem_info.offset;
715 working_buf = maruenc->dev->buf + mem_offset;
718 "encoded video. mem_offset = 0x%x\n", mem_offset);
720 outbuf = gst_buffer_new_and_alloc (ret_size);
721 memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
722 // memcpy (GST_BUFFER_DATA (outbuf), working_buf, ret_size);
723 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
724 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
727 ret = ioctl(maruenc->dev->fd, CODEC_CMD_RELEASE_BUFFER, &mem_offset);
729 CODEC_LOG (ERR, "failed to release used buffer\n");
734 if (maruenc->context->coded_frame) {
735 if (!maruenc->context->coded_frame->key_frame) {
736 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
739 GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
742 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
744 gst_buffer_unref (buffer);
748 if (force_keyframe) {
749 gst_pad_push_event (maruenc->srcpad,
750 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
751 gst_structure_new ("GstForceKeyUnit", "timestamp",
752 G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), NULL)));
756 return gst_pad_push (maruenc->srcpad, outbuf);
760 gst_maruenc_encode_audio (GstMaruEnc *maruenc, guint8 *audio_in,
761 guint in_size, guint max_size, GstClockTime timestamp,
762 GstClockTime duration, gboolean discont)
769 outbuf = gst_buffer_new_and_alloc (max_size + FF_MIN_BUFFER_SIZE);
770 audio_out = GST_BUFFER_DATA (outbuf);
772 GST_LOG_OBJECT (maruenc, "encoding buffer of max size %d", max_size);
773 if (maruenc->buffer_size != max_size) {
774 maruenc->buffer_size = max_size;
777 res = codec_encode_audio (maruenc->context, audio_out, max_size,
778 audio_in, in_size, maruenc->dev);
781 GST_ERROR_OBJECT (maruenc, "Failed to encode buffer: %d", res);
782 gst_buffer_unref (outbuf);
785 GST_LOG_OBJECT (maruenc, "got output size %d", res);
787 GST_BUFFER_SIZE (outbuf) = res;
788 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
789 GST_BUFFER_DURATION (outbuf) = duration;
791 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
793 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
795 GST_LOG_OBJECT (maruenc, "pushing size %d, timestamp %",
796 GST_TIME_FORMAT, res, GST_TIME_ARGS (timestamp));
798 ret = gst_pad_push (maruenc->srcpad, outbuf);
804 gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer)
807 // GstMaruEncClass *oclass;
808 GstClockTime timestamp, duration;
809 guint in_size, frame_size;
817 maruenc = (GstMaruEnc *) (GST_OBJECT_PARENT (pad));
818 // oclass = (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
820 ctx = maruenc->context;
822 in_size = GST_BUFFER_SIZE (buffer);
823 timestamp = GST_BUFFER_TIMESTAMP (buffer);
824 duration = GST_BUFFER_DURATION (buffer);
825 discont = GST_BUFFER_IS_DISCONT (buffer);
827 GST_DEBUG_OBJECT (maruenc,
828 "Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
829 ", size %d", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), in_size);
831 frame_size = ctx->audio.frame_size;
832 osize = ctx->audio.bits_per_sample_fmt;
834 if (frame_size > 1) {
835 guint avail, frame_bytes;
838 GST_LOG_OBJECT (maruenc, "DISCONT, clear adapter");
839 gst_adapter_clear (maruenc->adapter);
840 maruenc->discont = TRUE;
843 if (gst_adapter_available (maruenc->adapter) == 0) {
844 GST_LOG_OBJECT (maruenc, "taking buffer timestamp %" GST_TIME_FORMAT,
845 GST_TIME_ARGS (timestamp));
846 maruenc->adapter_ts = timestamp;
847 maruenc->adapter_consumed = 0;
849 GstClockTime upstream_time;
850 GstClockTime consumed_time;
854 gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
855 ctx->audio.sample_rate);
856 timestamp = maruenc->adapter_ts + consumed_time;
857 GST_LOG_OBJECT (maruenc, "taking adapter timestamp %" GST_TIME_FORMAT
858 " and adding consumed time %" GST_TIME_FORMAT,
859 GST_TIME_ARGS (maruenc->adapter_ts), GST_TIME_ARGS (consumed_time));
861 upstream_time = gst_adapter_prev_timestamp (maruenc->adapter, &bytes);
862 if (GST_CLOCK_TIME_IS_VALID (upstream_time)) {
863 GstClockTimeDiff diff;
866 gst_util_uint64_scale (bytes, GST_SECOND,
867 ctx->audio.sample_rate * osize * ctx->audio.channels);
868 diff = upstream_time - timestamp;
870 if (diff > GST_SECOND / 10 || diff < -GST_SECOND / 10) {
871 GST_DEBUG_OBJECT (maruenc, "adapter timestamp drifting, "
872 "taking upstream timestamp %" GST_TIME_FORMAT,
873 GST_TIME_ARGS (upstream_time));
874 timestamp = upstream_time;
876 maruenc->adapter_consumed = bytes / (osize * ctx->audio.channels);
877 maruenc->adapter_ts =
878 upstream_time - gst_util_uint64_scale (maruenc->adapter_consumed,
879 GST_SECOND, ctx->audio.sample_rate);
880 maruenc->discont = TRUE;
885 GST_LOG_OBJECT (maruenc, "pushing buffer in adapter");
886 gst_adapter_push (maruenc->adapter, buffer);
888 frame_bytes = frame_size * osize * ctx->audio.channels;
889 avail = gst_adapter_available (maruenc->adapter);
891 GST_LOG_OBJECT (maruenc, "frame_bytes %u, avail %u", frame_bytes, avail);
893 while (avail >= frame_bytes) {
894 GST_LOG_OBJECT (maruenc, "taking %u bytes from the adapter", frame_bytes);
896 in_data = (guint8 *) gst_adapter_peek (maruenc->adapter, frame_bytes);
897 maruenc->adapter_consumed += frame_size;
900 gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
901 ctx->audio.sample_rate);
902 duration -= (timestamp - maruenc->adapter_ts);
904 out_size = frame_bytes * 4;
907 gst_maruenc_encode_audio (maruenc, in_data, frame_bytes, out_size,
908 timestamp, duration, maruenc->discont);
910 gst_adapter_flush (maruenc->adapter, frame_bytes);
911 if (ret != GST_FLOW_OK) {
912 GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
913 gst_flow_get_name (ret));
916 timestamp += duration;
918 maruenc->discont = FALSE;
919 avail = gst_adapter_available (maruenc->adapter);
921 GST_LOG_OBJECT (maruenc, "%u bytes left in the adapter", avail);
924 int coded_bps = av_get_bits_per_sample (oclass->codec->name);
926 GST_LOG_OBJECT (maruenc, "coded bps %d, osize %d", coded_bps, osize);
928 out_size = in_size / osize;
930 out_size = (out_size * coded_bps) / 8;
933 in_data = (guint8 *) GST_BUFFER_DATA (buffer);
934 ret = gst_maruenc_encode_audio (maruenc, in_data, in_size, out_size,
935 timestamp, duration, discont);
936 gst_buffer_unref (buffer);
937 if (ret != GST_FLOW_OK) {
938 GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
939 gst_flow_get_name (ret));
947 gst_maruenc_flush_buffers (GstMaruEnc *maruenc, gboolean send)
950 GstBuffer *outbuf, *inbuf;
954 GST_DEBUG_OBJECT (maruenc, "flushing buffers with sending %d", send);
956 if (!maruenc->opened) {
957 while (!g_queue_is_empty (maruenc->delay)) {
958 gst_buffer_unref (g_queue_pop_head (maruenc->delay));
963 while (!g_queue_is_empty (maruenc->delay)) {
964 maruenc_setup_working_buf (maruenc);
966 ret_size = codec_encode_video (maruenc->context,
967 maruenc->working_buf, maruenc->working_buf_size, NULL, NULL, 0,
971 GstMaruEncClass *oclass =
972 (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
973 GST_WARNING_OBJECT (maruenc,
974 "maru_%senc: failed to flush buffer", oclass->codec->name);
978 if (maruenc->file && maruenc->context->stats_out) {
979 if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
980 GST_ELEMENT_ERROR (emeulenc, RESOURCE, WRITE,
981 (("Could not write to file \"%s\"."), maruenc->filename),
986 inbuf = g_queue_pop_head (maruenc->delay);
988 outbuf = gst_buffer_new_and_alloc (ret_size);
989 memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
990 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
991 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
993 if (!maruenc->context->coded_frame->key_frame) {
994 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
996 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
998 gst_buffer_unref (inbuf);
1001 gst_pad_push (maruenc->srcpad, outbuf);
1003 gst_buffer_unref (outbuf);
1007 while (!g_queue_is_empty (maruenc->delay)) {
1008 gst_buffer_unref (g_queue_pop_head (maruenc->delay));
1014 gst_maruenc_event_video (GstPad *pad, GstEvent *event)
1016 GstMaruEnc *maruenc;
1017 maruenc = (GstMaruEnc *) gst_pad_get_parent (pad);
1019 switch (GST_EVENT_TYPE (event)) {
1021 gst_maruenc_flush_buffers (maruenc, TRUE);
1023 case GST_EVENT_CUSTOM_DOWNSTREAM:
1025 const GstStructure *s;
1026 s = gst_event_get_structure (event);
1028 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1030 maruenc->picture->pict_type = FF_I_TYPE;
1039 return gst_pad_push_event (maruenc->srcpad, event);
1043 gst_maruenc_event_src (GstPad *pad, GstEvent *event)
1045 GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
1046 gboolean forward = TRUE;
1048 switch (GST_EVENT_TYPE (event)) {
1049 case GST_EVENT_CUSTOM_UPSTREAM:
1051 const GstStructure *s;
1052 s = gst_event_get_structure (event);
1054 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1056 GST_OBJECT_LOCK (maruenc);
1057 maruenc->force_keyframe = TRUE;
1058 GST_OBJECT_UNLOCK (maruenc);
1061 gst_event_unref (event);
1070 return gst_pad_push_event (maruenc->sinkpad, event);
1076 GstStateChangeReturn
1077 gst_maruenc_change_state (GstElement *element, GstStateChange transition)
1079 GstMaruEnc *maruenc = (GstMaruEnc*)element;
1080 GstStateChangeReturn ret;
1082 switch (transition) {
1087 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1089 switch (transition) {
1090 case GST_STATE_CHANGE_PAUSED_TO_READY:
1091 gst_maruenc_flush_buffers (maruenc, FALSE);
1092 if (maruenc->opened) {
1093 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
1094 maruenc->opened = FALSE;
1096 gst_adapter_clear (maruenc->adapter);
1099 if (maruenc->flie) {
1100 fclose (maruenc->file);
1101 maruenc->file = NULL;
1105 if (maruenc->working_buf) {
1106 g_free (maruenc->working_buf);
1107 maruenc->working_buf = NULL;
1118 gst_maruenc_register (GstPlugin *plugin, GList *element)
1120 GTypeInfo typeinfo = {
1121 sizeof (GstMaruEncClass),
1122 (GBaseInitFunc) gst_maruenc_base_init,
1124 (GClassInitFunc) gst_maruenc_class_init,
1127 sizeof (GstMaruEnc),
1129 (GInstanceInitFunc) gst_maruenc_init,
1134 gint rank = GST_RANK_PRIMARY;
1135 GList *elem = element;
1136 CodecElement *codec = NULL;
1142 /* register element */
1144 codec = (CodecElement *)(elem->data);
1149 if (codec->codec_type != CODEC_TYPE_ENCODE) {
1153 type_name = g_strdup_printf ("maru_%senc", codec->name);
1154 type = g_type_from_name (type_name);
1156 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1157 g_type_set_qdata (type, GST_MARUENC_PARAMS_QDATA, (gpointer) codec);
1160 if (!gst_element_register (plugin, type_name, rank, type)) {
1165 } while ((elem = elem->next));