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);
726 ret = ioctl(maruenc->dev->fd, CODEC_CMD_RELEASE_BUFFER, &mem_offset);
728 CODEC_LOG (ERR, "failed to release used buffer\n");
732 if (maruenc->context->coded_frame) {
733 if (!maruenc->context->coded_frame->key_frame) {
734 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
737 GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
740 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
742 gst_buffer_unref (buffer);
746 if (force_keyframe) {
747 gst_pad_push_event (maruenc->srcpad,
748 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
749 gst_structure_new ("GstForceKeyUnit", "timestamp",
750 G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), NULL)));
754 return gst_pad_push (maruenc->srcpad, outbuf);
758 gst_maruenc_encode_audio (GstMaruEnc *maruenc, guint8 *audio_in,
759 guint in_size, guint max_size, GstClockTime timestamp,
760 GstClockTime duration, gboolean discont)
767 outbuf = gst_buffer_new_and_alloc (max_size + FF_MIN_BUFFER_SIZE);
768 audio_out = GST_BUFFER_DATA (outbuf);
770 GST_LOG_OBJECT (maruenc, "encoding buffer of max size %d", max_size);
771 if (maruenc->buffer_size != max_size) {
772 maruenc->buffer_size = max_size;
775 res = codec_encode_audio (maruenc->context, audio_out, max_size,
776 audio_in, in_size, maruenc->dev);
779 GST_ERROR_OBJECT (maruenc, "Failed to encode buffer: %d", res);
780 gst_buffer_unref (outbuf);
783 GST_LOG_OBJECT (maruenc, "got output size %d", res);
785 GST_BUFFER_SIZE (outbuf) = res;
786 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
787 GST_BUFFER_DURATION (outbuf) = duration;
789 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
791 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
793 GST_LOG_OBJECT (maruenc, "pushing size %d, timestamp %",
794 GST_TIME_FORMAT, res, GST_TIME_ARGS (timestamp));
796 ret = gst_pad_push (maruenc->srcpad, outbuf);
802 gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer)
805 GstMaruEncClass *oclass;
806 GstClockTime timestamp, duration;
807 guint in_size, frame_size;
815 maruenc = (GstMaruEnc *) (GST_OBJECT_PARENT (pad));
816 oclass = (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
818 ctx = maruenc->context;
820 in_size = GST_BUFFER_SIZE (buffer);
821 timestamp = GST_BUFFER_TIMESTAMP (buffer);
822 duration = GST_BUFFER_DURATION (buffer);
823 discont = GST_BUFFER_IS_DISCONT (buffer);
825 GST_DEBUG_OBJECT (maruenc,
826 "Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
827 ", size %d", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), in_size);
829 frame_size = ctx->audio.frame_size;
830 osize = ctx->audio.bits_per_sample_fmt;
832 if (frame_size > 1) {
833 guint avail, frame_bytes;
836 GST_LOG_OBJECT (maruenc, "DISCONT, clear adapter");
837 gst_adapter_clear (maruenc->adapter);
838 maruenc->discont = TRUE;
841 if (gst_adapter_available (maruenc->adapter) == 0) {
842 GST_LOG_OBJECT (maruenc, "taking buffer timestamp %" GST_TIME_FORMAT,
843 GST_TIME_ARGS (timestamp));
844 maruenc->adapter_ts = timestamp;
845 maruenc->adapter_consumed = 0;
847 GstClockTime upstream_time;
848 GstClockTime consumed_time;
852 gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
853 ctx->audio.sample_rate);
854 timestamp = maruenc->adapter_ts + consumed_time;
855 GST_LOG_OBJECT (maruenc, "taking adapter timestamp %" GST_TIME_FORMAT
856 " and adding consumed time %" GST_TIME_FORMAT,
857 GST_TIME_ARGS (maruenc->adapter_ts), GST_TIME_ARGS (consumed_time));
859 upstream_time = gst_adapter_prev_timestamp (maruenc->adapter, &bytes);
860 if (GST_CLOCK_TIME_IS_VALID (upstream_time)) {
861 GstClockTimeDiff diff;
864 gst_util_uint64_scale (bytes, GST_SECOND,
865 ctx->audio.sample_rate * osize * ctx->audio.channels);
866 diff = upstream_time - timestamp;
868 if (diff > GST_SECOND / 10 || diff < -GST_SECOND / 10) {
869 GST_DEBUG_OBJECT (maruenc, "adapter timestamp drifting, "
870 "taking upstream timestamp %" GST_TIME_FORMAT,
871 GST_TIME_ARGS (upstream_time));
872 timestamp = upstream_time;
874 maruenc->adapter_consumed = bytes / (osize * ctx->audio.channels);
875 maruenc->adapter_ts =
876 upstream_time - gst_util_uint64_scale (maruenc->adapter_consumed,
877 GST_SECOND, ctx->audio.sample_rate);
878 maruenc->discont = TRUE;
883 GST_LOG_OBJECT (maruenc, "pushing buffer in adapter");
884 gst_adapter_push (maruenc->adapter, buffer);
886 frame_bytes = frame_size * osize * ctx->audio.channels;
887 avail = gst_adapter_available (maruenc->adapter);
889 GST_LOG_OBJECT (maruenc, "frame_bytes %u, avail %u", frame_bytes, avail);
891 while (avail >= frame_bytes) {
892 GST_LOG_OBJECT (maruenc, "taking %u bytes from the adapter", frame_bytes);
894 in_data = (guint8 *) gst_adapter_peek (maruenc->adapter, frame_bytes);
895 maruenc->adapter_consumed += frame_size;
898 gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
899 ctx->audio.sample_rate);
900 duration -= (timestamp - maruenc->adapter_ts);
902 out_size = frame_bytes * 4;
905 gst_maruenc_encode_audio (maruenc, in_data, frame_bytes, out_size,
906 timestamp, duration, maruenc->discont);
908 gst_adapter_flush (maruenc->adapter, frame_bytes);
909 if (ret != GST_FLOW_OK) {
910 GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
911 gst_flow_get_name (ret));
914 timestamp += duration;
916 maruenc->discont = FALSE;
917 avail = gst_adapter_available (maruenc->adapter);
919 GST_LOG_OBJECT (maruenc, "%u bytes left in the adapter", avail);
922 int coded_bps = av_get_bits_per_sample (oclass->codec->name);
924 GST_LOG_OBJECT (maruenc, "coded bps %d, osize %d", coded_bps, osize);
926 out_size = in_size / osize;
928 out_size = (out_size * coded_bps) / 8;
931 in_data = (guint8 *) GST_BUFFER_DATA (buffer);
932 ret = gst_maruenc_encode_audio (maruenc, in_data, in_size, out_size,
933 timestamp, duration, discont);
934 gst_buffer_unref (buffer);
935 if (ret != GST_FLOW_OK) {
936 GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
937 gst_flow_get_name (ret));
945 gst_maruenc_flush_buffers (GstMaruEnc *maruenc, gboolean send)
948 GstBuffer *outbuf, *inbuf;
952 GST_DEBUG_OBJECT (maruenc, "flushing buffers with sending %d", send);
954 if (!maruenc->opened) {
955 while (!g_queue_is_empty (maruenc->delay)) {
956 gst_buffer_unref (g_queue_pop_head (maruenc->delay));
961 while (!g_queue_is_empty (maruenc->delay)) {
962 maruenc_setup_working_buf (maruenc);
964 ret_size = codec_encode_video (maruenc->context,
965 maruenc->working_buf, maruenc->working_buf_size, NULL, NULL, 0,
969 GstMaruEncClass *oclass =
970 (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
971 GST_WARNING_OBJECT (maruenc,
972 "maru_%senc: failed to flush buffer", oclass->codec->name);
976 if (maruenc->file && maruenc->context->stats_out) {
977 if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
978 GST_ELEMENT_ERROR (emeulenc, RESOURCE, WRITE,
979 (("Could not write to file \"%s\"."), maruenc->filename),
984 inbuf = g_queue_pop_head (maruenc->delay);
986 outbuf = gst_buffer_new_and_alloc (ret_size);
987 memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
988 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
989 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
991 if (!maruenc->context->coded_frame->key_frame) {
992 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
994 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
996 gst_buffer_unref (inbuf);
999 gst_pad_push (maruenc->srcpad, outbuf);
1001 gst_buffer_unref (outbuf);
1005 while (!g_queue_is_empty (maruenc->delay)) {
1006 gst_buffer_unref (g_queue_pop_head (maruenc->delay));
1012 gst_maruenc_event_video (GstPad *pad, GstEvent *event)
1014 GstMaruEnc *maruenc;
1015 maruenc = (GstMaruEnc *) gst_pad_get_parent (pad);
1017 switch (GST_EVENT_TYPE (event)) {
1019 gst_maruenc_flush_buffers (maruenc, TRUE);
1021 case GST_EVENT_CUSTOM_DOWNSTREAM:
1023 const GstStructure *s;
1024 s = gst_event_get_structure (event);
1026 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1028 maruenc->picture->pict_type = FF_I_TYPE;
1037 return gst_pad_push_event (maruenc->srcpad, event);
1041 gst_maruenc_event_src (GstPad *pad, GstEvent *event)
1043 GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
1044 gboolean forward = TRUE;
1046 switch (GST_EVENT_TYPE (event)) {
1047 case GST_EVENT_CUSTOM_UPSTREAM:
1049 const GstStructure *s;
1050 s = gst_event_get_structure (event);
1052 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1054 GST_OBJECT_LOCK (maruenc);
1055 maruenc->force_keyframe = TRUE;
1056 GST_OBJECT_UNLOCK (maruenc);
1059 gst_event_unref (event);
1068 return gst_pad_push_event (maruenc->sinkpad, event);
1074 GstStateChangeReturn
1075 gst_maruenc_change_state (GstElement *element, GstStateChange transition)
1077 GstMaruEnc *maruenc = (GstMaruEnc*)element;
1078 GstStateChangeReturn ret;
1080 switch (transition) {
1085 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1087 switch (transition) {
1088 case GST_STATE_CHANGE_PAUSED_TO_READY:
1089 gst_maruenc_flush_buffers (maruenc, FALSE);
1090 if (maruenc->opened) {
1091 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
1092 maruenc->opened = FALSE;
1094 gst_adapter_clear (maruenc->adapter);
1097 if (maruenc->flie) {
1098 fclose (maruenc->file);
1099 maruenc->file = NULL;
1103 if (maruenc->working_buf) {
1104 g_free (maruenc->working_buf);
1105 maruenc->working_buf = NULL;
1116 gst_maruenc_register (GstPlugin *plugin, GList *element)
1118 GTypeInfo typeinfo = {
1119 sizeof (GstMaruEncClass),
1120 (GBaseInitFunc) gst_maruenc_base_init,
1122 (GClassInitFunc) gst_maruenc_class_init,
1125 sizeof (GstMaruEnc),
1127 (GInstanceInitFunc) gst_maruenc_init,
1132 gint rank = GST_RANK_PRIMARY;
1133 GList *elem = element;
1134 CodecElement *codec = NULL;
1140 /* register element */
1142 codec = (CodecElement *)(elem->data);
1147 if (codec->codec_type != CODEC_TYPE_ENCODE) {
1151 type_name = g_strdup_printf ("maru_%senc", codec->name);
1152 type = g_type_from_name (type_name);
1154 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1155 g_type_set_qdata (type, GST_MARUENC_PARAMS_QDATA, (gpointer) codec);
1158 if (!gst_element_register (plugin, type_name, rank, type)) {
1163 } while ((elem = elem->next));