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 #include "gstmarudevice.h"
22 #include "gstmaruutils.h"
23 #include "gstmaruinterface.h"
24 #include <gst/base/gstadapter.h>
26 #define GST_MARUENC_PARAMS_QDATA g_quark_from_static_string("maruenc-params")
28 typedef struct _GstMaruEnc
35 CodecContext *context;
38 GstClockTime adapter_ts;
39 guint64 adapter_consumed;
49 gulong working_buf_size;
55 typedef struct _GstMaruEncClass
57 GstElementClass parent_class;
60 GstPadTemplate *sinktempl;
61 GstPadTemplate *srctempl;
65 static GstElementClass *parent_class = NULL;
67 static void gst_maruenc_base_init (GstMaruEncClass *klass);
68 static void gst_maruenc_class_init (GstMaruEncClass *klass);
69 static void gst_maruenc_init (GstMaruEnc *maruenc);
70 static void gst_maruenc_finalize (GObject *object);
72 static gboolean gst_maruenc_setcaps (GstPad *pad, GstCaps *caps);
73 static GstCaps *gst_maruenc_getcaps (GstPad *pad);
75 static GstCaps *gst_maruenc_get_possible_sizes (GstMaruEnc *maruenc,
76 GstPad *pad, const GstCaps *caps);
78 static GstFlowReturn gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer);
79 static GstFlowReturn gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer);
81 static gboolean gst_maruenc_event_video (GstPad *pad, GstEvent *event);
82 static gboolean gst_maruenc_event_src (GstPad *pad, GstEvent *event);
84 GstStateChangeReturn gst_maruenc_change_state (GstElement *element, GstStateChange transition);
86 #define DEFAULT_VIDEO_BITRATE 300000
87 #define DEFAULT_VIDEO_GOP_SIZE 15
88 #define DEFAULT_AUDIO_BITRATE 128000
90 #define DEFAULT_WIDTH 352
91 #define DEFAULT_HEIGHT 288
97 gst_maruenc_base_init (GstMaruEncClass *klass)
99 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
100 GstPadTemplate *sinktempl = NULL, *srctempl = NULL;
101 GstCaps *sinkcaps = NULL, *srccaps = NULL;
103 gchar *longname, *classification, *description;
106 (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
107 GST_MARUENC_PARAMS_QDATA);
109 longname = g_strdup_printf ("%s Encoder", codec->longname);
110 classification = g_strdup_printf ("Codec/Encoder/%s",
111 (codec->media_type == AVMEDIA_TYPE_VIDEO) ? "Video" : "Audio");
112 description = g_strdup_printf ("%s Encoder", codec->name);
114 gst_element_class_set_details_simple (element_class,
118 // "accelerated codec for Tizen Emulator",
119 "Kitae Kim <kt920.kim@samsung.com>");
122 g_free (classification);
125 if (!(srccaps = gst_maru_codecname_to_caps (codec->name, NULL, TRUE))) {
126 GST_DEBUG ("Couldn't get source caps for encoder '%s'", codec->name);
127 srccaps = gst_caps_new_simple ("unknown/unknown", NULL);
130 switch (codec->media_type) {
131 case AVMEDIA_TYPE_VIDEO:
132 sinkcaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv; video/x-raw-gray");
134 case AVMEDIA_TYPE_AUDIO:
135 sinkcaps = gst_maru_codectype_to_audio_caps (NULL, codec->name, TRUE, codec);
138 GST_LOG("unknown media type");
143 GST_DEBUG ("Couldn't get sink caps for encoder '%s'", codec->name);
144 sinkcaps = gst_caps_new_simple ("unknown/unknown", NULL);
147 sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
148 GST_PAD_ALWAYS, sinkcaps);
149 srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
150 GST_PAD_ALWAYS, srccaps);
152 gst_element_class_add_pad_template (element_class, srctempl);
153 gst_element_class_add_pad_template (element_class, sinktempl);
155 klass->codec = codec;
156 klass->sinktempl = sinktempl;
157 klass->srctempl = srctempl;
158 klass->sinkcaps = NULL;
162 gst_maruenc_class_init (GstMaruEncClass *klass)
164 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
165 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
167 parent_class = g_type_class_peek_parent (klass);
170 gobject_class->set_property = gst_maruenc_set_property
171 gobject_class->get_property = gst_maruenc_get_property
174 gstelement_class->change_state = gst_maruenc_change_state;
176 gobject_class->finalize = gst_maruenc_finalize;
180 gst_maruenc_init (GstMaruEnc *maruenc)
182 GstMaruEncClass *oclass;
183 oclass = (GstMaruEncClass*) (G_OBJECT_GET_CLASS(maruenc));
185 maruenc->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
186 gst_pad_set_setcaps_function (maruenc->sinkpad,
187 GST_DEBUG_FUNCPTR(gst_maruenc_setcaps));
188 gst_pad_set_getcaps_function (maruenc->sinkpad,
189 GST_DEBUG_FUNCPTR(gst_maruenc_getcaps));
191 maruenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
192 gst_pad_use_fixed_caps (maruenc->srcpad);
195 maruenc->file = NULL;
197 maruenc->delay = g_queue_new ();
199 // instead of AVCodecContext
200 maruenc->context = g_malloc0 (sizeof(CodecContext));
201 maruenc->context->video.pix_fmt = PIX_FMT_NONE;
202 maruenc->context->audio.sample_fmt = SAMPLE_FMT_NONE;
204 maruenc->opened = FALSE;
206 maruenc->dev = g_malloc0 (sizeof(CodecDevice));
208 GST_ERROR_OBJECT (maruenc, "failed to allocate CodecDevice");
209 // TODO: error handling
212 if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO) {
213 gst_pad_set_chain_function (maruenc->sinkpad, gst_maruenc_chain_video);
214 gst_pad_set_event_function (maruenc->sinkpad, gst_maruenc_event_video);
215 gst_pad_set_event_function (maruenc->srcpad, gst_maruenc_event_src);
217 maruenc->bitrate = DEFAULT_VIDEO_BITRATE;
218 maruenc->buffer_size = 512 * 1024;
219 maruenc->gop_size = DEFAULT_VIDEO_GOP_SIZE;
221 } else if (oclass->codec->media_type == AVMEDIA_TYPE_AUDIO){
222 gst_pad_set_chain_function (maruenc->sinkpad, gst_maruenc_chain_audio);
223 maruenc->bitrate = DEFAULT_AUDIO_BITRATE;
226 gst_element_add_pad (GST_ELEMENT (maruenc), maruenc->sinkpad);
227 gst_element_add_pad (GST_ELEMENT (maruenc), maruenc->srcpad);
229 // TODO: need to know what adapter does.
230 maruenc->adapter = gst_adapter_new ();
234 gst_maruenc_finalize (GObject *object)
237 GstMaruEnc *maruenc = (GstMaruEnc *) object;
239 if (maruenc->opened) {
240 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
241 maruenc->opened = FALSE;
244 if (maruenc->context) {
245 g_free (maruenc->context);
246 maruenc->context = NULL;
250 g_free (maruenc->dev);
254 g_queue_free (maruenc->delay);
256 g_free (maruenc->filename);
259 g_object_unref (maruenc->adapter);
261 G_OBJECT_CLASS (parent_class)->finalize (object);
265 gst_maruenc_get_possible_sizes (GstMaruEnc *maruenc, GstPad *pad,
268 GstCaps *othercaps = NULL;
269 GstCaps *tmpcaps = NULL;
270 GstCaps *intersect = NULL;
273 othercaps = gst_pad_peer_get_caps (maruenc->srcpad);
276 return gst_caps_copy (caps);
279 intersect = gst_caps_intersect (othercaps,
280 gst_pad_get_pad_template_caps (maruenc->srcpad));
281 gst_caps_unref (othercaps);
283 if (gst_caps_is_empty (intersect)) {
287 if (gst_caps_is_any (intersect)) {
288 return gst_caps_copy (caps);
291 tmpcaps = gst_caps_new_empty ();
293 for (i = 0; i <gst_caps_get_size (intersect); i++) {
294 GstStructure *s = gst_caps_get_structure (intersect, i);
295 const GValue *height = NULL;
296 const GValue *width = NULL;
297 const GValue *framerate = NULL;
300 height = gst_structure_get_value (s, "height");
301 width = gst_structure_get_value (s, "width");
302 framerate = gst_structure_get_value (s, "framerate");
304 tmps = gst_structure_new ("video/x-rwa-rgb", NULL);
306 gst_structure_set_value (tmps, "width", width);
309 gst_structure_set_value (tmps, "height", height);
312 gst_structure_set_value (tmps, "framerate", framerate);
314 gst_caps_merge_structure (tmpcaps, gst_structure_copy (tmps));
316 gst_structure_set_name (tmps, "video/x-raw-yuv");
317 gst_caps_merge_structure (tmpcaps, gst_structure_copy (tmps));
319 gst_structure_set_name (tmps, "video/x-raw-gray");
320 gst_caps_merge_structure (tmpcaps, tmps);
322 gst_caps_unref (intersect);
324 intersect = gst_caps_intersect (caps, tmpcaps);
325 gst_caps_unref (tmpcaps);
331 gst_maruenc_getcaps (GstPad *pad)
333 GstMaruEnc *maruenc = (GstMaruEnc *) GST_PAD_PARENT (pad);
334 GstMaruEncClass *oclass =
335 (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
336 CodecContext *ctx = NULL;
337 enum PixelFormat pixfmt;
338 GstCaps *caps = NULL;
339 GstCaps *finalcaps = NULL;
342 GST_DEBUG_OBJECT (maruenc, "getting caps");
344 if (!oclass->codec) {
345 GST_ERROR_OBJECT (maruenc, "codec element is null.");
349 if (oclass->codec->media_type == AVMEDIA_TYPE_AUDIO) {
350 caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
352 GST_DEBUG_OBJECT (maruenc, "audio caps, return template %" GST_PTR_FORMAT,
358 if (oclass->sinkcaps) {
359 caps = gst_maruenc_get_possible_sizes (maruenc, pad, oclass->sinkcaps);
360 GST_DEBUG_OBJECT (maruenc, "return cached caps %" GST_PTR_FORMAT, caps);
364 GST_DEBUG_OBJECT (maruenc, "probing caps");
367 for (pixfmt = 0;; pixfmt++) {
370 if ((pixfmt = oclass->codec->pix_fmts[i++]) == PIX_FMT_NONE) {
371 GST_DEBUG_OBJECT (maruenc,
372 "At the end of official pixfmt for this codec, breaking out");
376 GST_DEBUG_OBJECT (maruenc,
377 "Got an official pixfmt [%d], attempting to get caps", pixfmt);
378 tmpcaps = gst_maru_pixfmt_to_caps (pixfmt, NULL, oclass->codec->name);
380 GST_DEBUG_OBJECT (maruenc, "Got caps, breaking out");
382 caps = gst_caps_new_empty ();
384 gst_caps_append (caps, tmpcaps);
388 GST_DEBUG_OBJECT (maruenc,
389 "Couldn't figure out caps without context, trying again with a context");
391 GST_DEBUG_OBJECT (maruenc, "pixfmt: %d", pixfmt);
392 if (pixfmt >= PIX_FMT_NB) {
393 GST_WARNING ("Invalid pixfmt, breaking out");
397 ctx = g_malloc0 (sizeof(CodecContext));
399 GST_DEBUG_OBJECT (maruenc, "no context");
403 ctx->video.width = DEFAULT_WIDTH;
404 ctx->video.height = DEFAULT_HEIGHT;
405 ctx->video.fps_n = 1;
406 ctx->video.fps_d = 25;
407 ctx->video.ticks_per_frame = 1;
408 ctx->bit_rate = DEFAULT_VIDEO_BITRATE;
410 ctx->video.pix_fmt = pixfmt;
412 GST_DEBUG ("Attempting to open codec");
413 if (gst_maru_avcodec_open (ctx, oclass->codec, maruenc->dev) >= 0
414 && ctx->video.pix_fmt == pixfmt) {
415 ctx->video.width = -1;
417 caps = gst_caps_new_empty ();
419 tmpcaps = gst_maru_codectype_to_caps (oclass->codec->media_type, ctx,
420 oclass->codec->name, TRUE);
422 gst_caps_append (caps, tmpcaps);
424 GST_LOG_OBJECT (maruenc,
425 "Couldn't get caps for codec: %s", oclass->codec->name);
427 gst_maru_avcodec_close (ctx, maruenc->dev);
429 GST_DEBUG_OBJECT (maruenc, "Opening codec failed with pixfmt: %d", pixfmt);
433 if (ctx->priv_data) {
434 gst_maru_avcodec_close (ctx, maruenc->dev);
441 caps = gst_maruenc_get_possible_sizes (maruenc, pad,
442 gst_pad_get_pad_template_caps (pad));
443 GST_DEBUG_OBJECT (maruenc, "probing gave nothing, "
444 "return template %" GST_PTR_FORMAT, caps);
448 GST_DEBUG_OBJECT (maruenc, "probed caps gave %" GST_PTR_FORMAT, caps);
449 oclass->sinkcaps = gst_caps_copy (caps);
451 finalcaps = gst_maruenc_get_possible_sizes (maruenc, pad, caps);
452 gst_caps_unref (caps);
458 gst_maruenc_setcaps (GstPad *pad, GstCaps *caps)
461 GstMaruEncClass *oclass;
463 GstCaps *allowed_caps;
465 enum PixelFormat pix_fmt;
467 maruenc = (GstMaruEnc *) (gst_pad_get_parent (pad));
468 oclass = (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
470 if (maruenc->opened) {
471 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
472 maruenc->opened = FALSE;
474 gst_pad_set_caps (maruenc->srcpad, NULL);
477 maruenc->context->bit_rate = maruenc->bitrate;
478 GST_DEBUG_OBJECT (maruenc, "Setting context to bitrate %lu, gop_size %d",
479 maruenc->bitrate, maruenc->gop_size);
483 // user defined properties
484 maruenc->context->gop_size = maruenc->gop_size;
485 maruenc->context->lmin = (maruenc->lmin * FF_QP2LAMBDA + 0.5);
486 maruenc->context->lmax = (maruenc->lmax * FF_QP2LAMBDA + 0.5);
488 // some other defaults
489 maruenc->context->b_frame_strategy = 0;
490 maruenc->context->coder_type = 0;
491 maruenc->context->context_model = 0;
492 maruenc->context->scenechange_threshold = 0;
493 maruenc->context->inter_threshold = 0;
495 if (maruenc->interlaced) {
496 maruenc->context->flags |=
497 CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME;
498 maruenc->picture->interlaced_frame = TRUE;
500 maruenc->picture->top_field_first = TRUE;
504 gst_maru_caps_with_codectype (oclass->codec->media_type, caps, maruenc->context);
506 if (!maruenc->context->video.fps_d) {
507 maruenc->context->video.fps_d = 25;
508 maruenc->context->video.fps_n = 1;
509 } else if (!strcmp(oclass->codec->name ,"mpeg4")
510 && (maruenc->context->video.fps_d > 65535)) {
511 maruenc->context->video.fps_n =
512 (gint) gst_util_uint64_scale_int (maruenc->context->video.fps_n,
513 65535, maruenc->context->video.fps_d);
514 maruenc->context->video.fps_d = 65535;
515 GST_LOG_OBJECT (maruenc, "MPEG4 : scaled down framerate to %d / %d",
516 maruenc->context->video.fps_d, maruenc->context->video.fps_n);
519 pix_fmt = maruenc->context->video.pix_fmt;
522 if (gst_maru_avcodec_open (maruenc->context,
523 oclass->codec, maruenc->dev) < 0) {
524 GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to open codec",
525 oclass->codec->name);
529 if (pix_fmt != maruenc->context->video.pix_fmt) {
530 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
531 GST_DEBUG_OBJECT (maruenc,
532 "maru_%senc: AV wants different colorspace (%d given, %d wanted)",
533 oclass->codec->name, pix_fmt, maruenc->context->video.pix_fmt);
537 if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO
538 && pix_fmt == PIX_FMT_NONE) {
539 GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to determine input format",
540 oclass->codec->name);
544 GST_DEBUG_OBJECT (maruenc, "picking an output format.");
545 allowed_caps = gst_pad_get_allowed_caps (maruenc->srcpad);
547 GST_DEBUG_OBJECT (maruenc, "but no peer, using template caps");
549 gst_caps_copy (gst_pad_get_pad_template_caps (maruenc->srcpad));
552 GST_DEBUG_OBJECT (maruenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
553 gst_maru_caps_with_codecname (oclass->codec->name,
554 oclass->codec->media_type, allowed_caps, maruenc->context);
557 gst_maru_codecname_to_caps (oclass->codec->name, maruenc->context, TRUE);
559 GST_DEBUG ("Unsupported codec - no caps found");
560 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
564 icaps = gst_caps_intersect (allowed_caps, other_caps);
565 gst_caps_unref (allowed_caps);
566 gst_caps_unref (other_caps);
567 if (gst_caps_is_empty (icaps)) {
568 gst_caps_unref (icaps);
572 if (gst_caps_get_size (icaps) > 1) {
576 gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
578 gst_caps_unref (icaps);
582 if (!gst_pad_set_caps (maruenc->srcpad, icaps)) {
583 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
584 gst_caps_unref (icaps);
587 gst_object_unref (maruenc);
589 maruenc->opened = TRUE;
595 gst_maruenc_setup_working_buf (GstMaruEnc *maruenc)
598 maruenc->context->video.width * maruenc->context->video.height * 6 +
601 if (maruenc->working_buf == NULL ||
602 maruenc->working_buf_size != wanted_size) {
603 if (maruenc->working_buf) {
604 g_free (maruenc->working_buf);
606 maruenc->working_buf_size = wanted_size;
607 maruenc->working_buf = g_malloc0 (maruenc->working_buf_size);
609 maruenc->buffer_size = wanted_size;
613 gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer)
615 GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
616 GstBuffer *outbuf = NULL;
617 gint ret_size = 0, frame_size = 0;
618 int coded_frame = 0, is_keyframe = 0;
619 uint32_t mem_offset = 0;
621 GST_DEBUG_OBJECT (maruenc,
622 "Received buffer of time %" GST_TIME_FORMAT,
623 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
626 GST_OBJECT_LOCK (maruenc);
627 force_keyframe = maruenc->force_keyframe;
628 maruenc->force_keyframe = FALSE;
629 GST_OBJECT_UNLOCK (maruenc);
631 if (force_keyframe) {
632 maruenc->picture->pict_type = FF_I_TYPE;
636 frame_size = gst_maru_avpicture_size (maruenc->context->video.pix_fmt,
637 maruenc->context->video.width, maruenc->context->video.height);
638 g_return_val_if_fail (frame_size == GST_BUFFER_SIZE (buffer),
642 pts = gst_maru_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buffer) /
643 maruenc->context.video.ticks_per_frame,
644 maruenc->context.video.fps_n, maruen->context.video.fps_d);
647 gst_maruenc_setup_working_buf (maruenc);
650 interface->encode_video (maruenc->context, maruenc->working_buf,
651 maruenc->working_buf_size, GST_BUFFER_DATA (buffer),
652 GST_BUFFER_SIZE (buffer), GST_BUFFER_TIMESTAMP (buffer),
653 &coded_frame, &is_keyframe,
657 GstMaruEncClass *oclass =
658 (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
659 GST_ERROR_OBJECT (maruenc,
660 "maru_%senc: failed to encode buffer", oclass->codec->name);
661 gst_buffer_unref (buffer);
665 g_queue_push_tail (maruenc->delay, buffer);
667 buffer = g_queue_pop_head (maruenc->delay);
673 if (maruenc->file && maruenc->context->stats_out) {
674 if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
675 GST_ELEMENT_ERROR (maruenc, RESOURCE, WRITE,
676 (("Could not write to file \"%s\"."), maruenc->filename),
682 // mem_offset = maruenc->dev->mem_info.offset;
683 // working_buf = maruenc->dev->buf + mem_offset;
685 GST_DEBUG_OBJECT (maruenc, "encoded video. mem_offset = 0x%x", mem_offset);
687 outbuf = gst_buffer_new_and_alloc (ret_size);
688 memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
689 // memcpy (GST_BUFFER_DATA (outbuf), working_buf, ret_size);
690 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
691 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
694 ret = ioctl(maruenc->dev->fd, CODEC_CMD_RELEASE_BUFFER, &mem_offset);
696 GST_ERROR_OBJECT (maruenc, "failed to release used buffer");
702 GST_DEBUG_OBJECT (maruenc, "this frame is not a keyframe");
704 /* GST_BUFFER_FLAG_DELTA_UNIT
705 * - this unit cannot be decoded independently.
707 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
710 GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
712 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
714 gst_buffer_unref (buffer);
717 if (force_keyframe) {
718 gst_pad_push_event (maruenc->srcpad,
719 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
720 gst_structure_new ("GstForceKeyUnit", "timestamp",
721 G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), NULL)));
725 return gst_pad_push (maruenc->srcpad, outbuf);
729 gst_maruenc_encode_audio (GstMaruEnc *maruenc, guint8 *audio_in,
730 guint in_size, guint max_size, GstClockTime timestamp,
731 GstClockTime duration, gboolean discont)
738 outbuf = gst_buffer_new_and_alloc (max_size + FF_MIN_BUFFER_SIZE);
739 audio_out = GST_BUFFER_DATA (outbuf);
741 GST_LOG_OBJECT (maruenc, "encoding buffer of max size %d", max_size);
742 if (maruenc->buffer_size != max_size) {
743 maruenc->buffer_size = max_size;
746 res = interface->encode_audio (maruenc->context, audio_out, max_size,
747 audio_in, in_size, timestamp, maruenc->dev);
750 GST_ERROR_OBJECT (maruenc, "Failed to encode buffer: %d", res);
751 gst_buffer_unref (outbuf);
754 GST_LOG_OBJECT (maruenc, "got output size %d", res);
756 GST_BUFFER_SIZE (outbuf) = res;
757 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
758 GST_BUFFER_DURATION (outbuf) = duration;
760 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
762 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
764 GST_LOG_OBJECT (maruenc, "pushing size %d, timestamp %",
765 GST_TIME_FORMAT, res, GST_TIME_ARGS (timestamp));
767 ret = gst_pad_push (maruenc->srcpad, outbuf);
773 gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer)
776 // GstMaruEncClass *oclass;
777 GstClockTime timestamp, duration;
778 guint in_size, frame_size;
786 maruenc = (GstMaruEnc *) (GST_OBJECT_PARENT (pad));
787 // oclass = (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
789 ctx = maruenc->context;
791 in_size = GST_BUFFER_SIZE (buffer);
792 timestamp = GST_BUFFER_TIMESTAMP (buffer);
793 duration = GST_BUFFER_DURATION (buffer);
794 discont = GST_BUFFER_IS_DISCONT (buffer);
796 GST_DEBUG_OBJECT (maruenc,
797 "Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
798 ", size %d", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), in_size);
800 frame_size = ctx->audio.frame_size;
801 osize = ctx->audio.bits_per_sample_fmt;
803 if (frame_size > 1) {
804 guint avail, frame_bytes;
807 GST_LOG_OBJECT (maruenc, "DISCONT, clear adapter");
808 gst_adapter_clear (maruenc->adapter);
809 maruenc->discont = TRUE;
812 if (gst_adapter_available (maruenc->adapter) == 0) {
813 GST_LOG_OBJECT (maruenc, "taking buffer timestamp %" GST_TIME_FORMAT,
814 GST_TIME_ARGS (timestamp));
815 maruenc->adapter_ts = timestamp;
816 maruenc->adapter_consumed = 0;
818 GstClockTime upstream_time;
819 GstClockTime consumed_time;
823 gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
824 ctx->audio.sample_rate);
825 timestamp = maruenc->adapter_ts + consumed_time;
826 GST_LOG_OBJECT (maruenc, "taking adapter timestamp %" GST_TIME_FORMAT
827 " and adding consumed time %" GST_TIME_FORMAT,
828 GST_TIME_ARGS (maruenc->adapter_ts), GST_TIME_ARGS (consumed_time));
830 upstream_time = gst_adapter_prev_timestamp (maruenc->adapter, &bytes);
831 if (GST_CLOCK_TIME_IS_VALID (upstream_time)) {
832 GstClockTimeDiff diff;
835 gst_util_uint64_scale (bytes, GST_SECOND,
836 ctx->audio.sample_rate * osize * ctx->audio.channels);
837 diff = upstream_time - timestamp;
839 if (diff > GST_SECOND / 10 || diff < -GST_SECOND / 10) {
840 GST_DEBUG_OBJECT (maruenc, "adapter timestamp drifting, "
841 "taking upstream timestamp %" GST_TIME_FORMAT,
842 GST_TIME_ARGS (upstream_time));
843 timestamp = upstream_time;
845 maruenc->adapter_consumed = bytes / (osize * ctx->audio.channels);
846 maruenc->adapter_ts =
847 upstream_time - gst_util_uint64_scale (maruenc->adapter_consumed,
848 GST_SECOND, ctx->audio.sample_rate);
849 maruenc->discont = TRUE;
854 GST_LOG_OBJECT (maruenc, "pushing buffer in adapter");
855 gst_adapter_push (maruenc->adapter, buffer);
857 frame_bytes = frame_size * osize * ctx->audio.channels;
858 avail = gst_adapter_available (maruenc->adapter);
860 GST_LOG_OBJECT (maruenc, "frame_bytes %u, avail %u", frame_bytes, avail);
862 while (avail >= frame_bytes) {
863 GST_LOG_OBJECT (maruenc, "taking %u bytes from the adapter", frame_bytes);
865 in_data = (guint8 *) gst_adapter_peek (maruenc->adapter, frame_bytes);
866 maruenc->adapter_consumed += frame_size;
869 gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
870 ctx->audio.sample_rate);
871 duration -= (timestamp - maruenc->adapter_ts);
873 out_size = frame_bytes * 4;
876 gst_maruenc_encode_audio (maruenc, in_data, frame_bytes, out_size,
877 timestamp, duration, maruenc->discont);
879 gst_adapter_flush (maruenc->adapter, frame_bytes);
880 if (ret != GST_FLOW_OK) {
881 GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
882 gst_flow_get_name (ret));
885 timestamp += duration;
887 maruenc->discont = FALSE;
888 avail = gst_adapter_available (maruenc->adapter);
890 GST_LOG_OBJECT (maruenc, "%u bytes left in the adapter", avail);
893 int coded_bps = av_get_bits_per_sample (oclass->codec->name);
895 GST_LOG_OBJECT (maruenc, "coded bps %d, osize %d", coded_bps, osize);
897 out_size = in_size / osize;
899 out_size = (out_size * coded_bps) / 8;
902 in_data = (guint8 *) GST_BUFFER_DATA (buffer);
903 ret = gst_maruenc_encode_audio (maruenc, in_data, in_size, out_size,
904 timestamp, duration, discont);
905 gst_buffer_unref (buffer);
906 if (ret != GST_FLOW_OK) {
907 GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
908 gst_flow_get_name (ret));
916 gst_maruenc_flush_buffers (GstMaruEnc *maruenc, gboolean send)
919 GstBuffer *outbuf, *inbuf;
923 GST_DEBUG_OBJECT (maruenc, "flushing buffers with sending %d", send);
925 if (!maruenc->opened) {
926 while (!g_queue_is_empty (maruenc->delay)) {
927 gst_buffer_unref (g_queue_pop_head (maruenc->delay));
932 while (!g_queue_is_empty (maruenc->delay)) {
933 maruenc_setup_working_buf (maruenc);
935 ret_size = codec_encode_video (maruenc->context,
936 maruenc->working_buf, maruenc->working_buf_size, NULL, NULL, 0,
940 GstMaruEncClass *oclass =
941 (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
942 GST_WARNING_OBJECT (maruenc,
943 "maru_%senc: failed to flush buffer", oclass->codec->name);
947 if (maruenc->file && maruenc->context->stats_out) {
948 if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
949 GST_ELEMENT_ERROR (emeulenc, RESOURCE, WRITE,
950 (("Could not write to file \"%s\"."), maruenc->filename),
955 inbuf = g_queue_pop_head (maruenc->delay);
957 outbuf = gst_buffer_new_and_alloc (ret_size);
958 memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
959 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
960 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
962 if (!maruenc->context->coded_frame->key_frame) {
963 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
965 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
967 gst_buffer_unref (inbuf);
970 gst_pad_push (maruenc->srcpad, outbuf);
972 gst_buffer_unref (outbuf);
976 while (!g_queue_is_empty (maruenc->delay)) {
977 gst_buffer_unref (g_queue_pop_head (maruenc->delay));
983 gst_maruenc_event_video (GstPad *pad, GstEvent *event)
986 maruenc = (GstMaruEnc *) gst_pad_get_parent (pad);
988 switch (GST_EVENT_TYPE (event)) {
990 gst_maruenc_flush_buffers (maruenc, TRUE);
992 case GST_EVENT_CUSTOM_DOWNSTREAM:
994 const GstStructure *s;
995 s = gst_event_get_structure (event);
997 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
999 maruenc->picture->pict_type = FF_I_TYPE;
1008 return gst_pad_push_event (maruenc->srcpad, event);
1012 gst_maruenc_event_src (GstPad *pad, GstEvent *event)
1014 GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
1015 gboolean forward = TRUE;
1017 switch (GST_EVENT_TYPE (event)) {
1018 case GST_EVENT_CUSTOM_UPSTREAM:
1020 const GstStructure *s;
1021 s = gst_event_get_structure (event);
1023 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1025 GST_OBJECT_LOCK (maruenc);
1026 maruenc->force_keyframe = TRUE;
1027 GST_OBJECT_UNLOCK (maruenc);
1030 gst_event_unref (event);
1039 return gst_pad_push_event (maruenc->sinkpad, event);
1045 GstStateChangeReturn
1046 gst_maruenc_change_state (GstElement *element, GstStateChange transition)
1048 GstMaruEnc *maruenc = (GstMaruEnc*)element;
1049 GstStateChangeReturn ret;
1051 switch (transition) {
1056 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1058 switch (transition) {
1059 case GST_STATE_CHANGE_PAUSED_TO_READY:
1060 gst_maruenc_flush_buffers (maruenc, FALSE);
1061 if (maruenc->opened) {
1062 GST_DEBUG_OBJECT (maruenc, "change_state: PAUSED_TO_READY, close context");
1063 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
1064 maruenc->opened = FALSE;
1066 gst_adapter_clear (maruenc->adapter);
1069 if (maruenc->flie) {
1070 fclose (maruenc->file);
1071 maruenc->file = NULL;
1075 if (maruenc->working_buf) {
1076 g_free (maruenc->working_buf);
1077 maruenc->working_buf = NULL;
1088 gst_maruenc_register (GstPlugin *plugin, GList *element)
1090 GTypeInfo typeinfo = {
1091 sizeof (GstMaruEncClass),
1092 (GBaseInitFunc) gst_maruenc_base_init,
1094 (GClassInitFunc) gst_maruenc_class_init,
1097 sizeof (GstMaruEnc),
1099 (GInstanceInitFunc) gst_maruenc_init,
1104 gint rank = GST_RANK_PRIMARY;
1105 GList *elem = element;
1106 CodecElement *codec = NULL;
1112 /* register element */
1114 codec = (CodecElement *)(elem->data);
1119 if (codec->codec_type != CODEC_TYPE_ENCODE) {
1123 type_name = g_strdup_printf ("maru_%senc", codec->name);
1124 type = g_type_from_name (type_name);
1126 type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1127 g_type_set_qdata (type, GST_MARUENC_PARAMS_QDATA, (gpointer) codec);
1130 if (!gst_element_register (plugin, type_name, rank, type)) {
1135 } while ((elem = elem->next));