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")
34 typedef struct _GstMaruVidEnc
37 GstVideoEncoder parent;
39 GstVideoCodecState *input_state;
41 CodecContext *context;
52 gulong working_buf_size;
58 typedef struct _GstMaruEnc
65 CodecContext *context;
68 GstClockTime adapter_ts;
69 guint64 adapter_consumed;
79 gulong working_buf_size;
85 typedef struct _GstMaruVidEncClass
87 GstVideoEncoderClass parent_class;
90 GstPadTemplate *sinktempl;
91 GstPadTemplate *srctempl;
94 typedef struct _GstMaruEncClass
96 GstElementClass parent_class;
99 GstPadTemplate *sinktempl;
100 GstPadTemplate *srctempl;
104 static GstElementClass *parent_class = NULL;
106 static void gst_maruvidenc_base_init (GstMaruVidEncClass *klass);
107 static void gst_maruvidenc_class_init (GstMaruVidEncClass *klass);
108 static void gst_maruvidenc_init (GstMaruVidEnc *maruenc);
109 static void gst_maruvidenc_finalize (GObject *object);
111 static gboolean gst_maruvidenc_set_format (GstVideoEncoder * encoder,
112 GstVideoCodecState * state);
113 static gboolean gst_maruvidenc_propose_allocation (GstVideoEncoder * encoder,
116 static GstCaps *gst_maruvidenc_getcaps (GstVideoEncoder * encoder, GstCaps * filter);
117 static GstFlowReturn gst_maruvidenc_handle_frame (GstVideoEncoder * encoder,
118 GstVideoCodecFrame * frame);
120 static void gst_maruvidenc_set_property (GObject * object,
121 guint prop_id, const GValue * value, GParamSpec * pspec);
122 static void gst_maruvidenc_get_property (GObject * object,
123 guint prop_id, GValue * value, GParamSpec * pspec);
125 #define DEFAULT_VIDEO_BITRATE 300000
126 #define DEFAULT_VIDEO_GOP_SIZE 15
128 #define DEFAULT_WIDTH 352
129 #define DEFAULT_HEIGHT 288
135 gst_maruvidenc_base_init (GstMaruVidEncClass *klass)
137 GST_DEBUG (" >> ENTER");
138 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
140 GstPadTemplate *sinktempl = NULL, *srctempl = NULL;
141 GstCaps *sinkcaps = NULL, *srccaps = NULL;
142 gchar *longname, *description;
145 (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
146 GST_MARUENC_PARAMS_QDATA);
147 g_assert (codec != NULL);
149 /* construct the element details struct */
150 longname = g_strdup_printf ("%s Encoder", codec->longname);
151 description = g_strdup_printf ("%s Encoder", codec->name);
153 gst_element_class_set_metadata (element_class,
155 "Codec/Encoder/Video",
157 "Sooyoung Ha <yoosah.ha@samsung.com>");
159 g_free (description);
161 if (!(srccaps = gst_maru_codecname_to_caps (codec->name, NULL, TRUE))) {
162 GST_DEBUG ("Couldn't get source caps for encoder '%s'", codec->name);
163 srccaps = gst_caps_new_empty_simple ("unknown/unknown");
166 sinkcaps = gst_maru_codectype_to_video_caps (NULL, codec->name, FALSE, codec);
169 GST_DEBUG ("Couldn't get sink caps for encoder '%s'", codec->name);
170 sinkcaps = gst_caps_new_empty_simple ("unknown/unknown");
174 sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
175 GST_PAD_ALWAYS, sinkcaps);
176 srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
177 GST_PAD_ALWAYS, srccaps);
179 gst_element_class_add_pad_template (element_class, srctempl);
180 gst_element_class_add_pad_template (element_class, sinktempl);
182 klass->codec = codec;
183 klass->sinktempl = sinktempl;
184 klass->srctempl = srctempl;
188 gst_maruvidenc_class_init (GstMaruVidEncClass *klass)
190 GST_DEBUG (" >> ENTER");
191 GObjectClass *gobject_class = (GObjectClass *) klass;
192 GstVideoEncoderClass *venc_class = (GstVideoEncoderClass *) klass;
194 parent_class = g_type_class_peek_parent (klass);
196 gobject_class->set_property = gst_maruvidenc_set_property;
197 gobject_class->get_property = gst_maruvidenc_get_property;
199 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE,
200 g_param_spec_ulong ("bitrate", "Bit Rate",
201 "Target VIDEO Bitrate", 0, G_MAXULONG, DEFAULT_VIDEO_BITRATE,
202 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204 venc_class->handle_frame = gst_maruvidenc_handle_frame;
205 venc_class->getcaps = gst_maruvidenc_getcaps;
206 venc_class->set_format = gst_maruvidenc_set_format;
207 venc_class->propose_allocation = gst_maruvidenc_propose_allocation;
209 gobject_class->finalize = gst_maruvidenc_finalize;
213 gst_maruvidenc_init (GstMaruVidEnc *maruenc)
215 GST_DEBUG (" >> ENTER");
216 // instead of AVCodecContext
217 maruenc->context = g_malloc0 (sizeof(CodecContext));
218 maruenc->context->video.pix_fmt = PIX_FMT_NONE;
219 maruenc->context->audio.sample_fmt = SAMPLE_FMT_NONE;
221 maruenc->opened = FALSE;
223 maruenc->dev = g_malloc0 (sizeof(CodecDevice));
225 maruenc->bitrate = DEFAULT_VIDEO_BITRATE;
226 maruenc->buffer_size = 512 * 1024;
227 maruenc->gop_size = DEFAULT_VIDEO_GOP_SIZE;
231 gst_maruvidenc_finalize (GObject *object)
233 GST_DEBUG (" >> ENTER");
235 GstMaruVidEnc *maruenc = (GstMaruVidEnc *) object;
237 if (maruenc->opened) {
238 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
239 maruenc->opened = FALSE;
242 if (maruenc->context) {
243 g_free (maruenc->context);
244 maruenc->context = NULL;
248 g_free (maruenc->dev);
252 G_OBJECT_CLASS (parent_class)->finalize (object);
256 gst_maruvidenc_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
258 GST_DEBUG (" >> ENTER");
259 GstMaruVidEnc *maruenc = (GstMaruVidEnc *) encoder;
260 GstCaps *caps = NULL;
262 GST_DEBUG_OBJECT (maruenc, "getting caps");
264 caps = gst_video_encoder_proxy_getcaps (encoder, NULL, filter);
265 GST_DEBUG_OBJECT (maruenc, "return caps %" GST_PTR_FORMAT, caps);
270 gst_maruvidenc_set_format (GstVideoEncoder * encoder,
271 GstVideoCodecState * state)
273 GST_DEBUG (" >> ENTER");
275 GstCaps *allowed_caps;
277 GstVideoCodecState *output_format;
278 enum PixelFormat pix_fmt;
279 GstMaruVidEnc *maruenc = (GstMaruVidEnc *) encoder;
280 GstMaruVidEncClass *oclass =
281 (GstMaruVidEncClass *) G_OBJECT_GET_CLASS (maruenc);
283 /* close old session */
284 if (maruenc->opened) {
285 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
286 maruenc->opened = FALSE;
289 /* user defined properties */
290 maruenc->context->bit_rate = maruenc->bitrate;
291 GST_DEBUG_OBJECT (maruenc, "Setting avcontext to bitrate %lu, gop_size %d",
292 maruenc->bitrate, maruenc->gop_size);
294 GST_DEBUG_OBJECT (maruenc, "Extracting common video information");
295 /* fetch pix_fmt, fps, par, width, height... */
296 gst_maru_videoinfo_to_context (&state->info, maruenc->context);
298 if (!strcmp(oclass->codec->name ,"mpeg4")
299 && (maruenc->context->video.fps_d > 65535)) {
300 /* MPEG4 Standards do not support time_base denominator greater than
301 * (1<<16) - 1 . We therefore scale them down.
302 * Agreed, it will not be the exact framerate... but the difference
303 * shouldn't be that noticeable */
304 maruenc->context->video.fps_n =
305 (gint) gst_util_uint64_scale_int (maruenc->context->video.fps_n,
306 65535, maruenc->context->video.fps_d);
307 maruenc->context->video.fps_d = 65535;
308 GST_DEBUG_OBJECT (maruenc, "MPEG4 : scaled down framerate to %d / %d",
309 maruenc->context->video.fps_d, maruenc->context->video.fps_n);
312 pix_fmt = maruenc->context->video.pix_fmt;
315 if (gst_maru_avcodec_open (maruenc->context,
316 oclass->codec, maruenc->dev) < 0) {
317 GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to open codec",
318 oclass->codec->name);
322 /* is the colourspace correct? */
323 if (pix_fmt != maruenc->context->video.pix_fmt) {
324 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
325 GST_DEBUG_OBJECT (maruenc,
326 "maru_%senc: AV wants different colorspace (%d given, %d wanted)",
327 oclass->codec->name, pix_fmt, maruenc->context->video.pix_fmt);
331 /* we may have failed mapping caps to a pixfmt,
332 * and quite some codecs do not make up their own mind about that
333 * in any case, _NONE can never work out later on */
334 if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO
335 && pix_fmt == PIX_FMT_NONE) {
336 GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to determine input format",
337 oclass->codec->name);
341 /* some codecs support more than one format, first auto-choose one */
342 GST_DEBUG_OBJECT (maruenc, "picking an output format ...");
343 allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
345 GST_DEBUG_OBJECT (maruenc, "... but no peer, using template caps");
346 /* we need to copy because get_allowed_caps returns a ref, and
347 * get_pad_template_caps doesn't */
349 gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
351 GST_DEBUG_OBJECT (maruenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
352 gst_maru_caps_with_codecname (oclass->codec->name,
353 oclass->codec->media_type, allowed_caps, maruenc->context);
355 /* try to set this caps on the other side */
357 gst_maru_codecname_to_caps (oclass->codec->name, maruenc->context, TRUE);
359 GST_DEBUG ("Unsupported codec - no caps found");
360 gst_maru_avcodec_close (maruenc->context, maruenc->dev);
364 icaps = gst_caps_intersect (allowed_caps, other_caps);
365 gst_caps_unref (allowed_caps);
366 gst_caps_unref (other_caps);
367 if (gst_caps_is_empty (icaps)) {
368 gst_caps_unref (icaps);
371 icaps = gst_caps_truncate (icaps);
373 /* Store input state and set output state */
374 if (maruenc->input_state)
375 gst_video_codec_state_unref (maruenc->input_state);
376 maruenc->input_state = gst_video_codec_state_ref (state);
378 output_format = gst_video_encoder_set_output_state (encoder, icaps, state);
379 gst_video_codec_state_unref (output_format);
382 maruenc->opened = TRUE;
388 gst_maruvidenc_propose_allocation (GstVideoEncoder * encoder,
391 GST_DEBUG (" >> ENTER");
392 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
394 return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
399 gst_maruenc_setup_working_buf (GstMaruVidEnc *maruenc)
401 GST_DEBUG (" >> ENTER");
403 maruenc->context->video.width * maruenc->context->video.height * 6 +
406 if (maruenc->working_buf == NULL ||
407 maruenc->working_buf_size != wanted_size) {
408 if (maruenc->working_buf) {
409 g_free (maruenc->working_buf);
411 maruenc->working_buf_size = wanted_size;
412 maruenc->working_buf = g_malloc0 (maruenc->working_buf_size);
414 maruenc->buffer_size = wanted_size;
418 gst_maruvidenc_handle_frame (GstVideoEncoder * encoder,
419 GstVideoCodecFrame * frame)
421 GST_DEBUG (" >> ENTER");
422 GstMaruVidEnc *maruenc = (GstMaruVidEnc *) encoder;
425 int coded_frame = 0, is_keyframe = 0;
428 gst_buffer_map (frame->input_buffer, &mapinfo, GST_MAP_READ);
430 gst_maruenc_setup_working_buf (maruenc);
433 interface->encode_video (maruenc->context, maruenc->working_buf,
434 maruenc->working_buf_size, mapinfo.data,
435 mapinfo.size, GST_BUFFER_TIMESTAMP (frame->input_buffer),
436 &coded_frame, &is_keyframe, maruenc->dev);
437 gst_buffer_unmap (frame->input_buffer, &mapinfo);
440 GstMaruVidEncClass *oclass =
441 (GstMaruVidEncClass *) (G_OBJECT_GET_CLASS (maruenc));
442 GST_ERROR_OBJECT (maruenc,
443 "maru_%senc: failed to encode buffer", oclass->codec->name);
447 /* Encoder needs more data */
452 gst_video_codec_frame_unref (frame);
454 /* Get oldest frame */
455 frame = gst_video_encoder_get_oldest_frame (encoder);
457 /* Allocate output buffer */
458 if (gst_video_encoder_allocate_output_frame (encoder, frame,
459 ret_size) != GST_FLOW_OK) {
460 gst_video_codec_frame_unref (frame);
461 GstMaruVidEncClass *oclass =
462 (GstMaruVidEncClass *) (G_OBJECT_GET_CLASS (maruenc));
463 GST_ERROR_OBJECT (maruenc,
464 "maru_%senc: failed to alloc buffer", oclass->codec->name);
465 return GST_FLOW_ERROR;
468 outbuf = frame->output_buffer;
469 gst_buffer_fill (outbuf, 0, maruenc->working_buf, ret_size);
471 /* buggy codec may not set coded_frame */
474 GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
476 GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
478 return gst_video_encoder_finish_frame (encoder, frame);
483 gst_maruvidenc_set_property (GObject *object,
484 guint prop_id, const GValue *value, GParamSpec *pspec)
486 GST_DEBUG (" >> ENTER");
487 GstMaruVidEnc *maruenc;
489 maruenc = (GstMaruVidEnc *) (object);
491 if (maruenc->opened) {
492 GST_WARNING_OBJECT (maruenc,
493 "Can't change properties one decoder is setup !");
499 maruenc->bitrate = g_value_get_ulong (value);
507 gst_maruvidenc_get_property (GObject *object,
508 guint prop_id, GValue *value, GParamSpec *pspec)
510 GST_DEBUG (" >> ENTER");
511 GstMaruVidEnc *maruenc;
513 maruenc = (GstMaruVidEnc *) (object);
517 g_value_set_ulong (value, maruenc->bitrate);
525 gst_maruvidenc_register (GstPlugin *plugin, GList *element)
527 GTypeInfo typeinfo = {
528 sizeof (GstMaruVidEncClass),
529 (GBaseInitFunc) gst_maruvidenc_base_init,
531 (GClassInitFunc) gst_maruvidenc_class_init,
534 sizeof (GstMaruVidEnc),
536 (GInstanceInitFunc) gst_maruvidenc_init,
541 gint rank = GST_RANK_PRIMARY;
542 GList *elem = element;
543 CodecElement *codec = NULL;
549 /* register element */
551 codec = (CodecElement *)(elem->data);
556 if (codec->codec_type != CODEC_TYPE_ENCODE || codec->media_type != AVMEDIA_TYPE_VIDEO) {
560 type_name = g_strdup_printf ("maru_%senc", codec->name);
561 type = g_type_from_name (type_name);
563 type = g_type_register_static (GST_TYPE_VIDEO_ENCODER, type_name, &typeinfo, 0);
564 g_type_set_qdata (type, GST_MARUENC_PARAMS_QDATA, (gpointer) codec);
567 if (!gst_element_register (plugin, type_name, rank, type)) {
572 } while ((elem = elem->next));