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);
440 if (ctx->priv_data) {
441 gst_maru_avcodec_close (ctx, maruenc->dev);
448 caps = gst_maruenc_get_possible_sizes (maruenc, pad,
449 gst_pad_get_pad_template_caps (pad));
450 GST_DEBUG_OBJECT (maruenc, "probing gave nothing, "
451 "return template %" GST_PTR_FORMAT, caps);
455 GST_DEBUG_OBJECT (maruenc, "probed caps gave %" GST_PTR_FORMAT, caps);
456 oclass->sinkcaps = gst_caps_copy (caps);
458 finalcaps = gst_maruenc_get_possible_sizes (maruenc, pad, caps);
459 gst_caps_unref (caps);
465 gst_maruenc_setcaps (GstPad *pad, GstCaps *caps)
468 GstMaruEncClass *oclass;
470 GstCaps *allowed_caps;
472 enum PixelFormat pix_fmt;
474 maruenc = (GstMaruEnc *) (gst_pad_get_parent (pad));
475 oclass = (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
477 if (maruenc->opened) {
478 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
479 maruenc->opened = FALSE;
481 gst_pad_set_caps (maruenc->srcpad, NULL);
484 maruenc->context->bit_rate = maruenc->bitrate;
485 GST_DEBUG_OBJECT (maruenc, "Setting context to bitrate %lu, gop_size %d",
486 maruenc->bitrate, maruenc->gop_size);
490 // user defined properties
491 maruenc->context->gop_size = maruenc->gop_size;
492 maruenc->context->lmin = (maruenc->lmin * FF_QP2LAMBDA + 0.5);
493 maruenc->context->lmax = (maruenc->lmax * FF_QP2LAMBDA + 0.5);
495 // some other defaults
496 maruenc->context->b_frame_strategy = 0;
497 maruenc->context->coder_type = 0;
498 maruenc->context->context_model = 0;
499 maruenc->context->scenechange_threshold = 0;
500 maruenc->context->inter_threshold = 0;
502 if (maruenc->interlaced) {
503 maruenc->context->flags |=
504 CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME;
505 maruenc->picture->interlaced_frame = TRUE;
507 maruenc->picture->top_field_first = TRUE;
511 gst_maru_caps_with_codectype (oclass->codec->media_type, caps, maruenc->context);
513 if (!maruenc->context->video.fps_d) {
514 maruenc->context->video.fps_d = 25;
515 maruenc->context->video.fps_n = 1;
516 } else if (!strcmp(oclass->codec->name ,"mpeg4")
517 && (maruenc->context->video.fps_d > 65535)) {
518 maruenc->context->video.fps_n =
519 (gint) gst_util_uint64_scale_int (maruenc->context->video.fps_n,
520 65535, maruenc->context->video.fps_d);
521 maruenc->context->video.fps_d = 65535;
522 GST_LOG_OBJECT (maruenc, "MPEG4 : scaled down framerate to %d / %d",
523 maruenc->context->video.fps_d, maruenc->context->video.fps_n);
526 pix_fmt = maruenc->context->video.pix_fmt;
529 if (gst_maru_avcodec_open (maruenc->context,
530 oclass->codec, maruenc->dev) < 0) {
531 GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to open codec",
532 oclass->codec->name);
536 if (pix_fmt != maruenc->context->video.pix_fmt) {
537 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
538 GST_DEBUG_OBJECT (maruenc,
539 "maru_%senc: AV wants different colorspace (%d given, %d wanted)",
540 oclass->codec->name, pix_fmt, maruenc->context->video.pix_fmt);
544 if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO
545 && pix_fmt == PIX_FMT_NONE) {
546 GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to determine input format",
547 oclass->codec->name);
551 GST_DEBUG_OBJECT (maruenc, "picking an output format.");
552 allowed_caps = gst_pad_get_allowed_caps (maruenc->srcpad);
554 GST_DEBUG_OBJECT (maruenc, "but no peer, using template caps");
556 gst_caps_copy (gst_pad_get_pad_template_caps (maruenc->srcpad));
559 GST_DEBUG_OBJECT (maruenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
560 gst_maru_caps_with_codecname (oclass->codec->name,
561 oclass->codec->media_type, allowed_caps, maruenc->context);
564 gst_maru_codecname_to_caps (oclass->codec->name, maruenc->context, TRUE);
566 GST_DEBUG ("Unsupported codec - no caps found");
567 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
571 icaps = gst_caps_intersect (allowed_caps, other_caps);
572 gst_caps_unref (allowed_caps);
573 gst_caps_unref (other_caps);
574 if (gst_caps_is_empty (icaps)) {
575 gst_caps_unref (icaps);
579 if (gst_caps_get_size (icaps) > 1) {
583 gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
585 gst_caps_unref (icaps);
589 if (!gst_pad_set_caps (maruenc->srcpad, icaps)) {
590 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
591 gst_caps_unref (icaps);
594 gst_object_unref (maruenc);
596 maruenc->opened = TRUE;
602 gst_maruenc_setup_working_buf (GstMaruEnc *maruenc)
605 maruenc->context->video.width * maruenc->context->video.height * 6 +
608 if (maruenc->working_buf == NULL ||
609 maruenc->working_buf_size != wanted_size) {
610 if (maruenc->working_buf) {
611 g_free (maruenc->working_buf);
613 maruenc->working_buf_size = wanted_size;
614 maruenc->working_buf = g_malloc0 (maruenc->working_buf_size);
616 maruenc->buffer_size = wanted_size;
620 gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer)
622 GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
623 GstBuffer *outbuf = NULL;
624 gint ret_size = 0, frame_size = 0;
626 uint32_t mem_offset = 0;
627 uint8_t *working_buf = NULL;
629 GST_DEBUG_OBJECT (maruenc,
630 "Received buffer of time %" GST_TIME_FORMAT,
631 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
634 GST_OBJECT_LOCK (maruenc);
635 force_keyframe = maruenc->force_keyframe;
636 maruenc->force_keyframe = FALSE;
637 GST_OBJECT_UNLOCK (maruenc);
639 if (force_keyframe) {
640 maruenc->picture->pict_type = FF_I_TYPE;
644 frame_size = gst_maru_avpicture_size (maruenc->context->video.pix_fmt,
645 maruenc->context->video.width, maruenc->context->video.height);
646 g_return_val_if_fail (frame_size == GST_BUFFER_SIZE (buffer),
650 pts = gst_maru_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buffer) /
651 maruenc->context.video.ticks_per_frame,
652 maruenc->context.video.fps_n, maruen->context.video.fps_d);
655 // TODO: check whether this func needs or not.
656 gst_maruenc_setup_working_buf (maruenc);
659 codec_encode_video (maruenc->context, maruenc->working_buf,
660 maruenc->working_buf_size, GST_BUFFER_DATA (buffer),
661 GST_BUFFER_SIZE (buffer), GST_BUFFER_TIMESTAMP (buffer),
665 GstMaruEncClass *oclass =
666 (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
667 GST_ERROR_OBJECT (maruenc,
668 "maru_%senc: failed to encode buffer", oclass->codec->name);
669 gst_buffer_unref (buffer);
673 g_queue_push_tail (maruenc->delay, buffer);
675 buffer = g_queue_pop_head (maruenc->delay);
681 if (maruenc->file && maruenc->context->stats_out) {
682 if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
683 GST_ELEMENT_ERROR (maruenc, RESOURCE, WRITE,
684 (("Could not write to file \"%s\"."), maruenc->filename),
690 mem_offset = maruenc->dev->mem_info.offset;
691 working_buf = maruenc->dev->buf + mem_offset;
694 "encoded video. mem_offset = 0x%x\n", mem_offset);
696 outbuf = gst_buffer_new_and_alloc (ret_size);
697 memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
698 // memcpy (GST_BUFFER_DATA (outbuf), working_buf, ret_size);
699 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
700 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
703 ret = ioctl(maruenc->dev->fd, CODEC_CMD_RELEASE_BUFFER, &mem_offset);
705 CODEC_LOG (ERR, "failed to release used buffer\n");
710 if (maruenc->context->coded_frame) {
711 if (!maruenc->context->coded_frame->key_frame) {
712 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
715 GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
718 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
720 gst_buffer_unref (buffer);
724 if (force_keyframe) {
725 gst_pad_push_event (maruenc->srcpad,
726 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
727 gst_structure_new ("GstForceKeyUnit", "timestamp",
728 G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), NULL)));
732 return gst_pad_push (maruenc->srcpad, outbuf);
736 gst_maruenc_encode_audio (GstMaruEnc *maruenc, guint8 *audio_in,
737 guint in_size, guint max_size, GstClockTime timestamp,
738 GstClockTime duration, gboolean discont)
745 outbuf = gst_buffer_new_and_alloc (max_size + FF_MIN_BUFFER_SIZE);
746 audio_out = GST_BUFFER_DATA (outbuf);
748 GST_LOG_OBJECT (maruenc, "encoding buffer of max size %d", max_size);
749 if (maruenc->buffer_size != max_size) {
750 maruenc->buffer_size = max_size;
753 res = codec_encode_audio (maruenc->context, audio_out, max_size,
754 audio_in, in_size, maruenc->dev);
757 GST_ERROR_OBJECT (maruenc, "Failed to encode buffer: %d", res);
758 gst_buffer_unref (outbuf);
761 GST_LOG_OBJECT (maruenc, "got output size %d", res);
763 GST_BUFFER_SIZE (outbuf) = res;
764 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
765 GST_BUFFER_DURATION (outbuf) = duration;
767 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
769 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
771 GST_LOG_OBJECT (maruenc, "pushing size %d, timestamp %",
772 GST_TIME_FORMAT, res, GST_TIME_ARGS (timestamp));
774 ret = gst_pad_push (maruenc->srcpad, outbuf);
780 gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer)
783 // GstMaruEncClass *oclass;
784 GstClockTime timestamp, duration;
785 guint in_size, frame_size;
793 maruenc = (GstMaruEnc *) (GST_OBJECT_PARENT (pad));
794 // oclass = (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
796 ctx = maruenc->context;
798 in_size = GST_BUFFER_SIZE (buffer);
799 timestamp = GST_BUFFER_TIMESTAMP (buffer);
800 duration = GST_BUFFER_DURATION (buffer);
801 discont = GST_BUFFER_IS_DISCONT (buffer);
803 GST_DEBUG_OBJECT (maruenc,
804 "Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
805 ", size %d", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), in_size);
807 frame_size = ctx->audio.frame_size;
808 osize = ctx->audio.bits_per_sample_fmt;
810 if (frame_size > 1) {
811 guint avail, frame_bytes;
814 GST_LOG_OBJECT (maruenc, "DISCONT, clear adapter");
815 gst_adapter_clear (maruenc->adapter);
816 maruenc->discont = TRUE;
819 if (gst_adapter_available (maruenc->adapter) == 0) {
820 GST_LOG_OBJECT (maruenc, "taking buffer timestamp %" GST_TIME_FORMAT,
821 GST_TIME_ARGS (timestamp));
822 maruenc->adapter_ts = timestamp;
823 maruenc->adapter_consumed = 0;
825 GstClockTime upstream_time;
826 GstClockTime consumed_time;
830 gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
831 ctx->audio.sample_rate);
832 timestamp = maruenc->adapter_ts + consumed_time;
833 GST_LOG_OBJECT (maruenc, "taking adapter timestamp %" GST_TIME_FORMAT
834 " and adding consumed time %" GST_TIME_FORMAT,
835 GST_TIME_ARGS (maruenc->adapter_ts), GST_TIME_ARGS (consumed_time));
837 upstream_time = gst_adapter_prev_timestamp (maruenc->adapter, &bytes);
838 if (GST_CLOCK_TIME_IS_VALID (upstream_time)) {
839 GstClockTimeDiff diff;
842 gst_util_uint64_scale (bytes, GST_SECOND,
843 ctx->audio.sample_rate * osize * ctx->audio.channels);
844 diff = upstream_time - timestamp;
846 if (diff > GST_SECOND / 10 || diff < -GST_SECOND / 10) {
847 GST_DEBUG_OBJECT (maruenc, "adapter timestamp drifting, "
848 "taking upstream timestamp %" GST_TIME_FORMAT,
849 GST_TIME_ARGS (upstream_time));
850 timestamp = upstream_time;
852 maruenc->adapter_consumed = bytes / (osize * ctx->audio.channels);
853 maruenc->adapter_ts =
854 upstream_time - gst_util_uint64_scale (maruenc->adapter_consumed,
855 GST_SECOND, ctx->audio.sample_rate);
856 maruenc->discont = TRUE;
861 GST_LOG_OBJECT (maruenc, "pushing buffer in adapter");
862 gst_adapter_push (maruenc->adapter, buffer);
864 frame_bytes = frame_size * osize * ctx->audio.channels;
865 avail = gst_adapter_available (maruenc->adapter);
867 GST_LOG_OBJECT (maruenc, "frame_bytes %u, avail %u", frame_bytes, avail);
869 while (avail >= frame_bytes) {
870 GST_LOG_OBJECT (maruenc, "taking %u bytes from the adapter", frame_bytes);
872 in_data = (guint8 *) gst_adapter_peek (maruenc->adapter, frame_bytes);
873 maruenc->adapter_consumed += frame_size;
876 gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
877 ctx->audio.sample_rate);
878 duration -= (timestamp - maruenc->adapter_ts);
880 out_size = frame_bytes * 4;
883 gst_maruenc_encode_audio (maruenc, in_data, frame_bytes, out_size,
884 timestamp, duration, maruenc->discont);
886 gst_adapter_flush (maruenc->adapter, frame_bytes);
887 if (ret != GST_FLOW_OK) {
888 GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
889 gst_flow_get_name (ret));
892 timestamp += duration;
894 maruenc->discont = FALSE;
895 avail = gst_adapter_available (maruenc->adapter);
897 GST_LOG_OBJECT (maruenc, "%u bytes left in the adapter", avail);
900 int coded_bps = av_get_bits_per_sample (oclass->codec->name);
902 GST_LOG_OBJECT (maruenc, "coded bps %d, osize %d", coded_bps, osize);
904 out_size = in_size / osize;
906 out_size = (out_size * coded_bps) / 8;
909 in_data = (guint8 *) GST_BUFFER_DATA (buffer);
910 ret = gst_maruenc_encode_audio (maruenc, in_data, in_size, out_size,
911 timestamp, duration, discont);
912 gst_buffer_unref (buffer);
913 if (ret != GST_FLOW_OK) {
914 GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
915 gst_flow_get_name (ret));
923 gst_maruenc_flush_buffers (GstMaruEnc *maruenc, gboolean send)
926 GstBuffer *outbuf, *inbuf;
930 GST_DEBUG_OBJECT (maruenc, "flushing buffers with sending %d", send);
932 if (!maruenc->opened) {
933 while (!g_queue_is_empty (maruenc->delay)) {
934 gst_buffer_unref (g_queue_pop_head (maruenc->delay));
939 while (!g_queue_is_empty (maruenc->delay)) {
940 maruenc_setup_working_buf (maruenc);
942 ret_size = codec_encode_video (maruenc->context,
943 maruenc->working_buf, maruenc->working_buf_size, NULL, NULL, 0,
947 GstMaruEncClass *oclass =
948 (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
949 GST_WARNING_OBJECT (maruenc,
950 "maru_%senc: failed to flush buffer", oclass->codec->name);
954 if (maruenc->file && maruenc->context->stats_out) {
955 if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
956 GST_ELEMENT_ERROR (emeulenc, RESOURCE, WRITE,
957 (("Could not write to file \"%s\"."), maruenc->filename),
962 inbuf = g_queue_pop_head (maruenc->delay);
964 outbuf = gst_buffer_new_and_alloc (ret_size);
965 memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
966 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
967 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
969 if (!maruenc->context->coded_frame->key_frame) {
970 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
972 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
974 gst_buffer_unref (inbuf);
977 gst_pad_push (maruenc->srcpad, outbuf);
979 gst_buffer_unref (outbuf);
983 while (!g_queue_is_empty (maruenc->delay)) {
984 gst_buffer_unref (g_queue_pop_head (maruenc->delay));
990 gst_maruenc_event_video (GstPad *pad, GstEvent *event)
993 maruenc = (GstMaruEnc *) gst_pad_get_parent (pad);
995 switch (GST_EVENT_TYPE (event)) {
997 gst_maruenc_flush_buffers (maruenc, TRUE);
999 case GST_EVENT_CUSTOM_DOWNSTREAM:
1001 const GstStructure *s;
1002 s = gst_event_get_structure (event);
1004 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1006 maruenc->picture->pict_type = FF_I_TYPE;
1015 return gst_pad_push_event (maruenc->srcpad, event);
1019 gst_maruenc_event_src (GstPad *pad, GstEvent *event)
1021 GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
1022 gboolean forward = TRUE;
1024 switch (GST_EVENT_TYPE (event)) {
1025 case GST_EVENT_CUSTOM_UPSTREAM:
1027 const GstStructure *s;
1028 s = gst_event_get_structure (event);
1030 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1032 GST_OBJECT_LOCK (maruenc);
1033 maruenc->force_keyframe = TRUE;
1034 GST_OBJECT_UNLOCK (maruenc);
1037 gst_event_unref (event);
1046 return gst_pad_push_event (maruenc->sinkpad, event);
1052 GstStateChangeReturn
1053 gst_maruenc_change_state (GstElement *element, GstStateChange transition)
1055 GstMaruEnc *maruenc = (GstMaruEnc*)element;
1056 GstStateChangeReturn ret;
1058 switch (transition) {
1063 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1065 switch (transition) {
1066 case GST_STATE_CHANGE_PAUSED_TO_READY:
1067 gst_maruenc_flush_buffers (maruenc, FALSE);
1068 if (maruenc->opened) {
1069 GST_DEBUG_OBJECT (maruenc, "change_state: PAUSED_TO_READY, close context");
1070 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
1071 maruenc->opened = FALSE;
1073 gst_adapter_clear (maruenc->adapter);
1076 if (maruenc->flie) {
1077 fclose (maruenc->file);
1078 maruenc->file = NULL;
1082 if (maruenc->working_buf) {
1083 g_free (maruenc->working_buf);
1084 maruenc->working_buf = NULL;
1095 gst_maruenc_register (GstPlugin *plugin, GList *element)
1097 GTypeInfo typeinfo = {
1098 sizeof (GstMaruEncClass),
1099 (GBaseInitFunc) gst_maruenc_base_init,
1101 (GClassInitFunc) gst_maruenc_class_init,
1104 sizeof (GstMaruEnc),
1106 (GInstanceInitFunc) gst_maruenc_init,
1111 gint rank = GST_RANK_PRIMARY;
1112 GList *elem = element;
1113 CodecElement *codec = NULL;
1119 /* register element */
1121 codec = (CodecElement *)(elem->data);
1126 if (codec->codec_type != CODEC_TYPE_ENCODE) {
1130 type_name = g_strdup_printf ("maru_%senc", codec->name);
1131 type = g_type_from_name (type_name);
1133 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1134 g_type_set_qdata (type, GST_MARUENC_PARAMS_QDATA, (gpointer) codec);
1137 if (!gst_element_register (plugin, type_name, rank, type)) {
1142 } while ((elem = elem->next));