77d577d4d9179f81afd50b1e771a30ee6ce698a7
[platform/adaptation/emulator/gst-plugins-emulator.git] / src / gstmaruvidenc.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #include "gstmarudevice.h"
22 #include "gstmaruutils.h"
23 #include "gstmaruinterface.h"
24 #include <gst/base/gstadapter.h>
25
26 #define GST_MARUENC_PARAMS_QDATA g_quark_from_static_string("maruenc-params")
27
28 enum
29 {
30   ARG_0,
31   ARG_BIT_RATE
32 };
33
34 typedef struct _GstMaruVidEnc
35 {
36
37   GstVideoEncoder parent;
38
39   GstVideoCodecState *input_state;
40
41   CodecContext *context;
42   CodecDevice *dev;
43   gboolean opened;
44   gboolean discont;
45
46   /* cache */
47   gulong bitrate;
48   gint gop_size;
49   gulong buffer_size;
50
51   guint8 *working_buf;
52   gulong working_buf_size;
53
54   GQueue *delay;
55
56 } GstMaruVidEnc;
57
58 typedef struct _GstMaruEnc
59 {
60   GstElement element;
61
62   GstPad *srcpad;
63   GstPad *sinkpad;
64
65   CodecContext *context;
66   CodecDevice *dev;
67   gboolean opened;
68   GstClockTime adapter_ts;
69   guint64 adapter_consumed;
70   GstAdapter *adapter;
71   gboolean discont;
72
73   // cache
74   gulong bitrate;
75   gint gop_size;
76   gulong buffer_size;
77
78   guint8 *working_buf;
79   gulong working_buf_size;
80
81   GQueue *delay;
82
83 } GstMaruEnc;
84
85 typedef struct _GstMaruVidEncClass
86 {
87   GstVideoEncoderClass parent_class;
88
89   CodecElement *codec;
90   GstPadTemplate *sinktempl;
91   GstPadTemplate *srctempl;
92 } GstMaruVidEncClass;
93
94 typedef struct _GstMaruEncClass
95 {
96   GstElementClass parent_class;
97
98   CodecElement *codec;
99   GstPadTemplate *sinktempl;
100   GstPadTemplate *srctempl;
101   GstCaps *sinkcaps;
102 } GstMaruEncClass;
103
104 static GstElementClass *parent_class = NULL;
105
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);
110
111 static gboolean gst_maruvidenc_set_format (GstVideoEncoder * encoder,
112     GstVideoCodecState * state);
113 static gboolean gst_maruvidenc_propose_allocation (GstVideoEncoder * encoder,
114     GstQuery * query);
115
116 static GstCaps *gst_maruvidenc_getcaps (GstVideoEncoder * encoder, GstCaps * filter);
117 static GstFlowReturn gst_maruvidenc_handle_frame (GstVideoEncoder * encoder,
118     GstVideoCodecFrame * frame);
119
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);
124
125 #define DEFAULT_VIDEO_BITRATE   300000
126 #define DEFAULT_VIDEO_GOP_SIZE  15
127
128 #define DEFAULT_WIDTH 352
129 #define DEFAULT_HEIGHT 288
130
131 /*
132  * Implementation
133  */
134 static void
135 gst_maruvidenc_base_init (GstMaruVidEncClass *klass)
136 {
137   GST_DEBUG (" >> ENTER");
138   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
139   CodecElement *codec;
140   GstPadTemplate *sinktempl = NULL, *srctempl = NULL;
141   GstCaps *sinkcaps = NULL, *srccaps = NULL;
142   gchar *longname, *description;
143
144   codec =
145     (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
146         GST_MARUENC_PARAMS_QDATA);
147   g_assert (codec != NULL);
148
149   /* construct the element details struct */
150   longname = g_strdup_printf ("%s Encoder", codec->longname);
151   description = g_strdup_printf ("%s Encoder", codec->name);
152
153   gst_element_class_set_metadata (element_class,
154       longname,
155       "Codec/Encoder/Video",
156       description,
157       "Sooyoung Ha <yoosah.ha@samsung.com>");
158   g_free (longname);
159   g_free (description);
160
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");
164   }
165
166   sinkcaps = gst_maru_codectype_to_video_caps (NULL, codec->name, FALSE, codec);
167
168   if (!sinkcaps) {
169       GST_DEBUG ("Couldn't get sink caps for encoder '%s'", codec->name);
170       sinkcaps = gst_caps_new_empty_simple ("unknown/unknown");
171   }
172
173   /* pad templates */
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);
178
179   gst_element_class_add_pad_template (element_class, srctempl);
180   gst_element_class_add_pad_template (element_class, sinktempl);
181
182   klass->codec = codec;
183   klass->sinktempl = sinktempl;
184   klass->srctempl = srctempl;
185 }
186
187 static void
188 gst_maruvidenc_class_init (GstMaruVidEncClass *klass)
189 {
190   GST_DEBUG (" >> ENTER");
191   GObjectClass *gobject_class = (GObjectClass *) klass;
192   GstVideoEncoderClass *venc_class = (GstVideoEncoderClass *) klass;
193
194   parent_class = g_type_class_peek_parent (klass);
195
196   gobject_class->set_property = gst_maruvidenc_set_property;
197   gobject_class->get_property = gst_maruvidenc_get_property;
198
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));
203
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;
208
209   gobject_class->finalize = gst_maruvidenc_finalize;
210 }
211
212 static void
213 gst_maruvidenc_init (GstMaruVidEnc *maruenc)
214 {
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;
220
221   maruenc->opened = FALSE;
222
223   maruenc->dev = g_malloc0 (sizeof(CodecDevice));
224
225   maruenc->bitrate = DEFAULT_VIDEO_BITRATE;
226   maruenc->buffer_size = 512 * 1024;
227   maruenc->gop_size = DEFAULT_VIDEO_GOP_SIZE;
228 }
229
230 static void
231 gst_maruvidenc_finalize (GObject *object)
232 {
233   GST_DEBUG (" >> ENTER");
234   // Deinit Decoder
235   GstMaruVidEnc *maruenc = (GstMaruVidEnc *) object;
236
237   if (maruenc->opened) {
238     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
239     maruenc->opened = FALSE;
240   }
241
242   if (maruenc->context) {
243     g_free (maruenc->context);
244     maruenc->context = NULL;
245   }
246
247   if (maruenc->dev) {
248     g_free (maruenc->dev);
249     maruenc->dev = NULL;
250   }
251
252   G_OBJECT_CLASS (parent_class)->finalize (object);
253 }
254
255 static GstCaps *
256 gst_maruvidenc_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
257 {
258   GST_DEBUG (" >> ENTER");
259   GstMaruVidEnc *maruenc = (GstMaruVidEnc *) encoder;
260   GstCaps *caps = NULL;
261
262   GST_DEBUG_OBJECT (maruenc, "getting caps");
263
264   caps = gst_video_encoder_proxy_getcaps (encoder, NULL, filter);
265   GST_DEBUG_OBJECT (maruenc, "return caps %" GST_PTR_FORMAT, caps);
266   return caps;
267 }
268
269 static gboolean
270 gst_maruvidenc_set_format (GstVideoEncoder * encoder,
271     GstVideoCodecState * state)
272 {
273   GST_DEBUG (" >> ENTER");
274   GstCaps *other_caps;
275   GstCaps *allowed_caps;
276   GstCaps *icaps;
277   GstVideoCodecState *output_format;
278   enum PixelFormat pix_fmt;
279   GstMaruVidEnc *maruenc = (GstMaruVidEnc *) encoder;
280   GstMaruVidEncClass *oclass =
281       (GstMaruVidEncClass *) G_OBJECT_GET_CLASS (maruenc);
282
283   /* close old session */
284   if (maruenc->opened) {
285     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
286     maruenc->opened = FALSE;
287   }
288
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);
293
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);
297
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);
310   }
311
312   pix_fmt = maruenc->context->video.pix_fmt;
313
314   /* open codec */
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);
319     return FALSE;
320   }
321
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);
328     return FALSE;
329   }
330
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);
338     return FALSE;
339   }
340
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));
344   if (!allowed_caps) {
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 */
348     allowed_caps =
349         gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
350   }
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);
354
355   /* try to set this caps on the other side */
356   other_caps =
357   gst_maru_codecname_to_caps (oclass->codec->name, maruenc->context, TRUE);
358   if (!other_caps) {
359     GST_DEBUG ("Unsupported codec - no caps found");
360     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
361     return FALSE;
362   }
363
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);
369     return FALSE;
370   }
371   icaps = gst_caps_truncate (icaps);
372
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);
377
378   output_format = gst_video_encoder_set_output_state (encoder, icaps, state);
379   gst_video_codec_state_unref (output_format);
380
381   /* success! */
382   maruenc->opened = TRUE;
383
384   return TRUE;
385 }
386
387 static gboolean
388 gst_maruvidenc_propose_allocation (GstVideoEncoder * encoder,
389     GstQuery * query)
390 {
391   GST_DEBUG (" >> ENTER");
392   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
393
394   return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
395       query);
396 }
397
398 static void
399 gst_maruenc_setup_working_buf (GstMaruVidEnc *maruenc)
400 {
401   GST_DEBUG (" >> ENTER");
402   guint wanted_size =
403       maruenc->context->video.width * maruenc->context->video.height * 6 +
404       FF_MIN_BUFFER_SIZE;
405
406   if (maruenc->working_buf == NULL ||
407     maruenc->working_buf_size != wanted_size) {
408     if (maruenc->working_buf) {
409       g_free (maruenc->working_buf);
410     }
411     maruenc->working_buf_size = wanted_size;
412     maruenc->working_buf = g_malloc0 (maruenc->working_buf_size);
413   }
414   maruenc->buffer_size = wanted_size;
415 }
416
417 static GstFlowReturn
418 gst_maruvidenc_handle_frame (GstVideoEncoder * encoder,
419     GstVideoCodecFrame * frame)
420 {
421   GST_DEBUG (" >> ENTER");
422   GstMaruVidEnc *maruenc = (GstMaruVidEnc *) encoder;
423   GstBuffer *outbuf;
424   gint ret_size = 0;
425   int coded_frame = 0, is_keyframe = 0;
426   GstMapInfo mapinfo;
427
428   gst_buffer_map (frame->input_buffer, &mapinfo, GST_MAP_READ);
429
430   gst_maruenc_setup_working_buf (maruenc);
431
432   ret_size =
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);
438
439   if (ret_size < 0) {
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);
444     return GST_FLOW_OK;
445   }
446
447   /* Encoder needs more data */
448   if (!ret_size) {
449     return GST_FLOW_OK;
450   }
451
452   gst_video_codec_frame_unref (frame);
453
454   /* Get oldest frame */
455   frame = gst_video_encoder_get_oldest_frame (encoder);
456
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;
466   }
467
468   outbuf = frame->output_buffer;
469   gst_buffer_fill (outbuf, 0, maruenc->working_buf, ret_size);
470
471   /* buggy codec may not set coded_frame */
472   if (coded_frame) {
473     if (is_keyframe)
474       GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
475   } else
476     GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
477
478   return gst_video_encoder_finish_frame (encoder, frame);
479 }
480
481
482 static void
483 gst_maruvidenc_set_property (GObject *object,
484   guint prop_id, const GValue *value, GParamSpec *pspec)
485 {
486   GST_DEBUG (" >> ENTER");
487   GstMaruVidEnc *maruenc;
488
489   maruenc = (GstMaruVidEnc *) (object);
490
491   if (maruenc->opened) {
492     GST_WARNING_OBJECT (maruenc,
493       "Can't change properties one decoder is setup !");
494     return;
495   }
496
497   switch (prop_id) {
498     case ARG_BIT_RATE:
499       maruenc->bitrate = g_value_get_ulong (value);
500       break;
501     default:
502       break;
503   }
504 }
505
506 static void
507 gst_maruvidenc_get_property (GObject *object,
508   guint prop_id, GValue *value, GParamSpec *pspec)
509 {
510   GST_DEBUG (" >> ENTER");
511   GstMaruVidEnc *maruenc;
512
513   maruenc = (GstMaruVidEnc *) (object);
514
515   switch (prop_id) {
516     case ARG_BIT_RATE:
517       g_value_set_ulong (value, maruenc->bitrate);
518       break;
519     default:
520       break;
521   }
522 }
523
524 gboolean
525 gst_maruvidenc_register (GstPlugin *plugin, GList *element)
526 {
527   GTypeInfo typeinfo = {
528       sizeof (GstMaruVidEncClass),
529       (GBaseInitFunc) gst_maruvidenc_base_init,
530       NULL,
531       (GClassInitFunc) gst_maruvidenc_class_init,
532       NULL,
533       NULL,
534       sizeof (GstMaruVidEnc),
535       0,
536       (GInstanceInitFunc) gst_maruvidenc_init,
537   };
538
539   GType type;
540   gchar *type_name;
541   gint rank = GST_RANK_PRIMARY;
542   GList *elem = element;
543   CodecElement *codec = NULL;
544
545   if (!elem) {
546     return FALSE;
547   }
548
549   /* register element */
550   do {
551     codec = (CodecElement *)(elem->data);
552     if (!codec) {
553       return FALSE;
554     }
555
556     if (codec->codec_type != CODEC_TYPE_ENCODE || codec->media_type != AVMEDIA_TYPE_VIDEO) {
557       continue;
558     }
559
560     type_name = g_strdup_printf ("maru_%senc", codec->name);
561     type = g_type_from_name (type_name);
562     if (!type) {
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);
565     }
566
567     if (!gst_element_register (plugin, type_name, rank, type)) {
568       g_free (type_name);
569       return FALSE;
570     }
571     g_free (type_name);
572   } while ((elem = elem->next));
573
574   return TRUE;
575 }