dec263df3b95245da351c1c561b1a5efad29afd4
[platform/adaptation/emulator/gst-plugins-emulator.git] / src / gstmaruenc.c
1 /*
2  * GStreamer codec plugin for Tizen Emulator.
3  *
4  * Copyright (C) 2013 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:
7  * KiTae Kim <kt920.kim@samsung.com>
8  * SeokYeon Hwang <syeon.hwang@samsung.com>
9  * YeongKyoon Lee <yeongkyoon.lee@samsung.com>
10  *
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.
15  *
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.
20  *
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.
25  *
26  * Contributors:
27  * - S-Core Co., Ltd
28  *
29  */
30
31 #include "gstmarudevice.h"
32 #include "gstmaruutils.h"
33 #include "gstmaruinterface.h"
34 #include <gst/base/gstadapter.h>
35
36 #define GST_MARUENC_PARAMS_QDATA g_quark_from_static_string("maruenc-params")
37
38 typedef struct _GstMaruEnc
39 {
40   GstElement element;
41
42   GstPad *srcpad;
43   GstPad *sinkpad;
44
45   CodecContext *context;
46   CodecDevice *dev;
47   gboolean opened;
48   GstClockTime adapter_ts;
49   guint64 adapter_consumed;
50   GstAdapter *adapter;
51   gboolean discont;
52
53   // cache
54   gulong bitrate;
55   gint gop_size;
56   gulong buffer_size;
57
58   guint8 *working_buf;
59   gulong working_buf_size;
60
61   GQueue *delay;
62
63 } GstMaruEnc;
64
65 typedef struct _GstMaruEncClass
66 {
67   GstElementClass parent_class;
68
69   CodecElement *codec;
70   GstPadTemplate *sinktempl;
71   GstPadTemplate *srctempl;
72   GstCaps *sinkcaps;
73 } GstMaruEncClass;
74
75 static GstElementClass *parent_class = NULL;
76
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);
81
82 static gboolean gst_maruenc_setcaps (GstPad *pad, GstCaps *caps);
83 static GstCaps *gst_maruenc_getcaps (GstPad *pad);
84
85 static GstCaps *gst_maruenc_get_possible_sizes (GstMaruEnc *maruenc,
86   GstPad *pad, const GstCaps *caps);
87
88 static GstFlowReturn gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer);
89 static GstFlowReturn gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer);
90
91 static gboolean gst_maruenc_event_video (GstPad *pad, GstEvent *event);
92 static gboolean gst_maruenc_event_src (GstPad *pad, GstEvent *event);
93
94 GstStateChangeReturn gst_maruenc_change_state (GstElement *element, GstStateChange transition);
95
96 #define DEFAULT_VIDEO_BITRATE   300000
97 #define DEFAULT_VIDEO_GOP_SIZE  15
98 #define DEFAULT_AUDIO_BITRATE   128000
99
100 #define DEFAULT_WIDTH 352
101 #define DEFAULT_HEIGHT 288
102
103 /*
104  * Implementation
105  */
106 static void
107 gst_maruenc_base_init (GstMaruEncClass *klass)
108 {
109     GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
110     GstPadTemplate *sinktempl = NULL, *srctempl = NULL;
111     GstCaps *sinkcaps = NULL, *srccaps = NULL;
112     CodecElement *codec;
113     gchar *longname, *classification, *description;
114
115     codec =
116         (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
117          GST_MARUENC_PARAMS_QDATA);
118
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);
123
124     gst_element_class_set_details_simple (element_class,
125             longname,
126             classification,
127             description,
128 //            "accelerated codec for Tizen Emulator",
129             "Kitae Kim <kt920.kim@samsung.com>");
130
131     g_free (longname);
132     g_free (classification);
133
134
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);
138   }
139
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");
143     break;
144   case AVMEDIA_TYPE_AUDIO:
145     sinkcaps = gst_maru_codectype_to_audio_caps (NULL, codec->name, TRUE, codec);
146     break;
147   default:
148     GST_LOG("unknown media type.\n");
149     break;
150   }
151
152   if (!sinkcaps) {
153       GST_DEBUG ("Couldn't get sink caps for encoder '%s'", codec->name);
154       sinkcaps = gst_caps_new_simple ("unknown/unknown", NULL);
155   }
156
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);
161
162   gst_element_class_add_pad_template (element_class, srctempl);
163   gst_element_class_add_pad_template (element_class, sinktempl);
164
165   klass->codec = codec;
166   klass->sinktempl = sinktempl;
167   klass->srctempl = srctempl;
168   klass->sinkcaps = NULL;
169 }
170
171 static void
172 gst_maruenc_class_init (GstMaruEncClass *klass)
173 {
174   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
175   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
176
177   parent_class = g_type_class_peek_parent (klass);
178
179 #if 0
180   gobject_class->set_property = gst_maruenc_set_property
181   gobject_class->get_property = gst_maruenc_get_property
182 #endif
183
184   gstelement_class->change_state = gst_maruenc_change_state;
185
186   gobject_class->finalize = gst_maruenc_finalize;
187 }
188
189 static void
190 gst_maruenc_init (GstMaruEnc *maruenc)
191 {
192   GstMaruEncClass *oclass;
193   oclass = (GstMaruEncClass*) (G_OBJECT_GET_CLASS(maruenc));
194
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));
200
201   maruenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
202   gst_pad_use_fixed_caps (maruenc->srcpad);
203
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);
208
209     maruenc->bitrate = DEFAULT_VIDEO_BITRATE;
210     maruenc->buffer_size = 512 * 1024;
211     maruenc->gop_size = DEFAULT_VIDEO_GOP_SIZE;
212 #if 0
213     maruenc->lmin = 2;
214     maruenc->lmax = 31;
215 #endif
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;
219   }
220
221   gst_element_add_pad (GST_ELEMENT (maruenc), maruenc->sinkpad);
222   gst_element_add_pad (GST_ELEMENT (maruenc), maruenc->srcpad);
223
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;
227
228   maruenc->opened = FALSE;
229
230 #if 0
231   maruenc->file = NULL;
232 #endif
233   maruenc->delay = g_queue_new ();
234
235   maruenc->dev = g_malloc0 (sizeof(CodecDevice));
236   if (!maruenc->dev) {
237     printf("[gst-maru][%d] failed to allocate memory\n", __LINE__);
238   }
239
240   // need to know what adapter does.
241   maruenc->adapter = gst_adapter_new ();
242 }
243
244 static void
245 gst_maruenc_finalize (GObject *object)
246 {
247   // Deinit Decoder
248   GstMaruEnc *maruenc = (GstMaruEnc *) object;
249
250   if (maruenc->opened) {
251     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
252     maruenc->opened = FALSE;
253   }
254
255   if (maruenc->context) {
256     g_free (maruenc->context);
257     maruenc->context = NULL;
258   }
259
260   g_queue_free (maruenc->delay);
261 #if 0
262   g_free (maruenc->filename);
263 #endif
264
265   g_object_unref (maruenc->adapter);
266
267   G_OBJECT_CLASS (parent_class)->finalize (object);
268 }
269
270 static GstCaps *
271 gst_maruenc_get_possible_sizes (GstMaruEnc *maruenc, GstPad *pad,
272   const GstCaps *caps)
273 {
274   GstCaps *othercaps = NULL;
275   GstCaps *tmpcaps = NULL;
276   GstCaps *intersect = NULL;
277   guint i;
278
279   othercaps = gst_pad_peer_get_caps (maruenc->srcpad);
280
281   if (!othercaps) {
282     return gst_caps_copy (caps);
283   }
284
285   intersect = gst_caps_intersect (othercaps,
286     gst_pad_get_pad_template_caps (maruenc->srcpad));
287   gst_caps_unref (othercaps);
288
289   if (gst_caps_is_empty (intersect)) {
290     return intersect;
291   }
292
293   if (gst_caps_is_any (intersect)) {
294     return gst_caps_copy (caps);
295   }
296
297   tmpcaps = gst_caps_new_empty ();
298
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;
304     GstStructure *tmps;
305
306     height = gst_structure_get_value (s, "height");
307     width = gst_structure_get_value (s, "width");
308     framerate = gst_structure_get_value (s, "framerate");
309
310     tmps = gst_structure_new ("video/x-rwa-rgb", NULL);
311     if (width) {
312       gst_structure_set_value (tmps, "width", width);
313     }
314     if (height) {
315       gst_structure_set_value (tmps, "height", height);
316     }
317     if (framerate) {
318       gst_structure_set_value (tmps, "framerate", framerate);
319     }
320     gst_caps_merge_structure (tmpcaps, gst_structure_copy (tmps));
321
322     gst_structure_set_name (tmps, "video/x-raw-yuv");
323     gst_caps_merge_structure (tmpcaps, gst_structure_copy (tmps));
324
325     gst_structure_set_name (tmps, "video/x-raw-gray");
326     gst_caps_merge_structure (tmpcaps, tmps);
327   }
328   gst_caps_unref (intersect);
329
330   intersect = gst_caps_intersect (caps, tmpcaps);
331   gst_caps_unref (tmpcaps);
332
333   return intersect;
334 }
335
336 static GstCaps *
337 gst_maruenc_getcaps (GstPad *pad)
338 {
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;
346   gint i;
347
348   GST_DEBUG_OBJECT (maruenc, "getting caps");
349
350   if (!oclass->codec) {
351     GST_ERROR_OBJECT (maruenc, "codec element is null.");
352     return NULL;
353   }
354
355   if (oclass->codec->media_type == AVMEDIA_TYPE_AUDIO) {
356     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
357
358     GST_DEBUG_OBJECT (maruenc, "audio caps, return template %" GST_PTR_FORMAT,
359       caps);
360     return caps;
361   }
362
363   // cached
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);
367     return caps;
368   }
369
370   GST_DEBUG_OBJECT (maruenc, "probing caps");
371   i = pixfmt = 0;
372
373   for (pixfmt = 0;; pixfmt++) {
374     GstCaps *tmpcaps;
375
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");
379       break;
380     }
381
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);
385     if (tmpcaps) {
386       GST_DEBUG_OBJECT (maruenc, "Got caps, breaking out");
387       if (!caps) {
388         caps = gst_caps_new_empty ();
389       }
390       gst_caps_append (caps, tmpcaps);
391       continue;
392     }
393
394     GST_DEBUG_OBJECT (maruenc,
395         "Couldn't figure out caps without context, trying again with a context");
396
397     GST_DEBUG_OBJECT (maruenc, "pixfmt: %d", pixfmt);
398     if (pixfmt >= PIX_FMT_NB) {
399       GST_WARNING ("Invalid pixfmt, breaking out");
400       break;
401     }
402
403     ctx = g_malloc0 (sizeof(CodecContext));
404     if (!ctx) {
405       GST_DEBUG_OBJECT (maruenc, "no context");
406       break;
407     }
408
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;
415
416 //  ctx->strict_std_compliance = -1;
417     ctx->video.pix_fmt = pixfmt;
418
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;
423       if (!caps) {
424         caps = gst_caps_new_empty ();
425       }
426       tmpcaps = gst_maru_codectype_to_caps (oclass->codec->media_type, ctx,
427           oclass->codec->name, TRUE);
428       if (tmpcaps) {
429         gst_caps_append (caps, tmpcaps);
430       } else {
431         GST_LOG_OBJECT (maruenc,
432             "Couldn't get caps for codec: %s", oclass->codec->name);
433       }
434       gst_maru_avcodec_close (ctx, maruenc->dev);
435     } else {
436       GST_DEBUG_OBJECT (maruenc, "Opening codec failed with pixfmt: %d", pixfmt);
437     }
438
439 #if 0
440     if (ctx->priv_data) {
441       gst_maru_avcodec_close (ctx, maruenc->dev);
442     }
443 #endif
444     g_free (ctx);
445   }
446
447   if (!caps) {
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);
452     return caps;
453   }
454
455   GST_DEBUG_OBJECT (maruenc, "probed caps gave %" GST_PTR_FORMAT, caps);
456   oclass->sinkcaps = gst_caps_copy (caps);
457
458   finalcaps = gst_maruenc_get_possible_sizes (maruenc, pad, caps);
459   gst_caps_unref (caps);
460
461   return finalcaps;
462 }
463
464 static gboolean
465 gst_maruenc_setcaps (GstPad *pad, GstCaps *caps)
466 {
467   GstMaruEnc *maruenc;
468   GstMaruEncClass *oclass;
469   GstCaps *other_caps;
470   GstCaps *allowed_caps;
471   GstCaps *icaps;
472   enum PixelFormat pix_fmt;
473
474   maruenc = (GstMaruEnc *) (gst_pad_get_parent (pad));
475   oclass = (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
476
477   if (maruenc->opened) {
478     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
479     maruenc->opened = FALSE;
480
481     gst_pad_set_caps (maruenc->srcpad, NULL);
482   }
483
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);
487
488 #if 0
489
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);
494
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;
501
502   if (maruenc->interlaced) {
503     maruenc->context->flags |=
504       CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME;
505     maruenc->picture->interlaced_frame = TRUE;
506
507     maruenc->picture->top_field_first = TRUE;
508   }
509 #endif
510
511   gst_maru_caps_with_codectype (oclass->codec->media_type, caps, maruenc->context);
512
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);
524   }
525
526   pix_fmt = maruenc->context->video.pix_fmt;
527
528   // open codec
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);
533     return FALSE;
534   }
535
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);
541     return FALSE;
542   }
543
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);
548     return FALSE;
549   }
550
551   GST_DEBUG_OBJECT (maruenc, "picking an output format.");
552   allowed_caps = gst_pad_get_allowed_caps (maruenc->srcpad);
553   if (!allowed_caps) {
554     GST_DEBUG_OBJECT (maruenc, "but no peer, using template caps");
555     allowed_caps =
556       gst_caps_copy (gst_pad_get_pad_template_caps (maruenc->srcpad));
557   }
558
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);
562
563   other_caps =
564   gst_maru_codecname_to_caps (oclass->codec->name, maruenc->context, TRUE);
565   if (!other_caps) {
566     GST_DEBUG ("Unsupported codec - no caps found");
567     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
568     return FALSE;
569   }
570
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);
576     return FALSE;
577   }
578
579   if (gst_caps_get_size (icaps) > 1) {
580     GstCaps *newcaps;
581
582     newcaps =
583       gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
584               0)), NULL);
585     gst_caps_unref (icaps);
586     icaps = newcaps;
587   }
588
589   if (!gst_pad_set_caps (maruenc->srcpad, icaps)) {
590     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
591     gst_caps_unref (icaps);
592     return FALSE;
593   }
594   gst_object_unref (maruenc);
595
596   maruenc->opened = TRUE;
597
598   return TRUE;
599 }
600
601 static void
602 gst_maruenc_setup_working_buf (GstMaruEnc *maruenc)
603 {
604   guint wanted_size =
605       maruenc->context->video.width * maruenc->context->video.height * 6 +
606       FF_MIN_BUFFER_SIZE;
607
608   if (maruenc->working_buf == NULL ||
609     maruenc->working_buf_size != wanted_size) {
610     if (maruenc->working_buf) {
611       g_free (maruenc->working_buf);
612     }
613     maruenc->working_buf_size = wanted_size;
614     maruenc->working_buf = g_malloc0 (maruenc->working_buf_size);
615   }
616   maruenc->buffer_size = wanted_size;
617 }
618
619 GstFlowReturn
620 gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer)
621 {
622   GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
623   GstBuffer *outbuf = NULL;
624   gint ret_size = 0, frame_size = 0;
625   int ret = 0;
626   uint32_t mem_offset = 0;
627   uint8_t *working_buf = NULL;
628
629   GST_DEBUG_OBJECT (maruenc,
630       "Received buffer of time %" GST_TIME_FORMAT,
631       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
632
633 #if 0
634   GST_OBJECT_LOCK (maruenc);
635   force_keyframe = maruenc->force_keyframe;
636   maruenc->force_keyframe = FALSE;
637   GST_OBJECT_UNLOCK (maruenc);
638
639   if (force_keyframe) {
640     maruenc->picture->pict_type = FF_I_TYPE;
641   }
642 #endif
643
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),
647       GST_FLOW_ERROR);
648
649 #if 0
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);
653 #endif
654
655   // TODO: check whether this func needs or not.
656   gst_maruenc_setup_working_buf (maruenc);
657
658   ret_size =
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),
662                 maruenc->dev);
663
664   if (ret_size < 0) {
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);
670     return GST_FLOW_OK;
671   }
672
673   g_queue_push_tail (maruenc->delay, buffer);
674   if (ret_size) {
675     buffer = g_queue_pop_head (maruenc->delay);
676   } else {
677     return GST_FLOW_OK;
678   }
679
680 #if 0
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),
685         GST_ERROR_SYSTEM);
686     }
687   }
688 #endif
689
690   mem_offset = maruenc->dev->mem_info.offset;
691   working_buf = maruenc->dev->buf + mem_offset;
692
693   CODEC_LOG (DEBUG,
694     "encoded video. mem_offset = 0x%x\n",  mem_offset);
695
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);
701
702 #if 0
703   ret = ioctl(maruenc->dev->fd, CODEC_CMD_RELEASE_BUFFER, &mem_offset);
704   if (ret < 0) {
705     CODEC_LOG (ERR, "failed to release used buffer\n");
706   }
707 #endif
708
709 #if 0
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);
713     }
714   } else {
715     GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
716   }
717 #endif
718   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
719
720   gst_buffer_unref (buffer);
721
722 #if 0
723
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)));
729   }
730 #endif
731
732   return gst_pad_push (maruenc->srcpad, outbuf);
733 }
734
735 GstFlowReturn
736 gst_maruenc_encode_audio (GstMaruEnc *maruenc, guint8 *audio_in,
737   guint in_size, guint max_size, GstClockTime timestamp,
738   GstClockTime duration, gboolean discont)
739 {
740   GstBuffer *outbuf;
741   guint8 *audio_out;
742   gint res;
743   GstFlowReturn ret;
744
745   outbuf = gst_buffer_new_and_alloc (max_size + FF_MIN_BUFFER_SIZE);
746   audio_out = GST_BUFFER_DATA (outbuf);
747
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;
751   }
752
753   res = codec_encode_audio (maruenc->context, audio_out, max_size,
754                                   audio_in, in_size, maruenc->dev);
755
756   if (res < 0) {
757     GST_ERROR_OBJECT (maruenc, "Failed to encode buffer: %d", res);
758     gst_buffer_unref (outbuf);
759     return GST_FLOW_OK;
760   }
761   GST_LOG_OBJECT (maruenc, "got output size %d", res);
762
763   GST_BUFFER_SIZE (outbuf) = res;
764   GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
765   GST_BUFFER_DURATION (outbuf) = duration;
766   if (discont) {
767     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
768   }
769   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
770
771   GST_LOG_OBJECT (maruenc, "pushing size %d, timestamp %",
772       GST_TIME_FORMAT, res, GST_TIME_ARGS (timestamp));
773
774   ret = gst_pad_push (maruenc->srcpad, outbuf);
775
776   return ret;
777 }
778
779 static GstFlowReturn
780 gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer)
781 {
782   GstMaruEnc *maruenc;
783 //  GstMaruEncClass *oclass;
784   GstClockTime timestamp, duration;
785   guint in_size, frame_size;
786   gint osize;
787   GstFlowReturn ret;
788   gint out_size = 0;
789   gboolean discont;
790   guint8 *in_data;
791   CodecContext *ctx;
792
793   maruenc = (GstMaruEnc *) (GST_OBJECT_PARENT (pad));
794 //  oclass = (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
795
796   ctx = maruenc->context;
797
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);
802
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);
806
807   frame_size = ctx->audio.frame_size;
808   osize = ctx->audio.bits_per_sample_fmt;
809
810   if (frame_size > 1) {
811     guint avail, frame_bytes;
812
813     if (discont) {
814       GST_LOG_OBJECT (maruenc, "DISCONT, clear adapter");
815       gst_adapter_clear (maruenc->adapter);
816       maruenc->discont = TRUE;
817     }
818
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;
824     } else {
825       GstClockTime upstream_time;
826       GstClockTime consumed_time;
827       guint64 bytes;
828
829       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));
836
837       upstream_time = gst_adapter_prev_timestamp (maruenc->adapter, &bytes);
838       if (GST_CLOCK_TIME_IS_VALID (upstream_time)) {
839         GstClockTimeDiff diff;
840
841         upstream_time +=
842           gst_util_uint64_scale (bytes, GST_SECOND,
843             ctx->audio.sample_rate * osize * ctx->audio.channels);
844         diff = upstream_time - timestamp;
845
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;
851
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;
857         }
858       }
859     }
860
861     GST_LOG_OBJECT (maruenc, "pushing buffer in adapter");
862     gst_adapter_push (maruenc->adapter, buffer);
863
864     frame_bytes = frame_size * osize * ctx->audio.channels;
865     avail = gst_adapter_available (maruenc->adapter);
866
867     GST_LOG_OBJECT (maruenc, "frame_bytes %u, avail %u", frame_bytes, avail);
868
869     while (avail >= frame_bytes) {
870       GST_LOG_OBJECT (maruenc, "taking %u bytes from the adapter", frame_bytes);
871
872       in_data = (guint8 *) gst_adapter_peek (maruenc->adapter, frame_bytes);
873       maruenc->adapter_consumed += frame_size;
874
875       duration =
876         gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
877           ctx->audio.sample_rate);
878       duration -= (timestamp - maruenc->adapter_ts);
879
880       out_size = frame_bytes * 4;
881
882       ret =
883         gst_maruenc_encode_audio (maruenc, in_data, frame_bytes, out_size,
884           timestamp, duration, maruenc->discont);
885
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));
890       }
891
892       timestamp += duration;
893
894       maruenc->discont = FALSE;
895       avail = gst_adapter_available (maruenc->adapter);
896     }
897     GST_LOG_OBJECT (maruenc, "%u bytes left in the adapter", avail);
898   } else {
899 #if 0
900     int coded_bps = av_get_bits_per_sample (oclass->codec->name);
901
902     GST_LOG_OBJECT (maruenc, "coded bps %d, osize %d", coded_bps, osize);
903
904     out_size = in_size / osize;
905     if (coded_bps) {
906       out_size = (out_size * coded_bps) / 8;
907     }
908 #endif
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));
916     }
917   }
918
919   return GST_FLOW_OK;
920 }
921
922 static void
923 gst_maruenc_flush_buffers (GstMaruEnc *maruenc, gboolean send)
924 {
925 #if 0
926   GstBuffer *outbuf, *inbuf;
927   gint ret_size = 0;
928 #endif
929
930   GST_DEBUG_OBJECT (maruenc, "flushing buffers with sending %d", send);
931
932   if (!maruenc->opened) {
933     while (!g_queue_is_empty (maruenc->delay)) {
934       gst_buffer_unref (g_queue_pop_head (maruenc->delay));
935     }
936   }
937
938 #if 0
939   while (!g_queue_is_empty (maruenc->delay)) {
940     maruenc_setup_working_buf (maruenc);
941
942     ret_size = codec_encode_video (maruenc->context,
943       maruenc->working_buf, maruenc->working_buf_size, NULL, NULL, 0,
944       maruenc->dev);
945
946     if (ret_size < 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);
951       break;
952     }
953
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),
958           GST_ERROR_SYSTEM);
959       }
960     }
961
962     inbuf = g_queue_pop_head (maruenc->delay);
963
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);
968
969     if (!maruenc->context->coded_frame->key_frame) {
970       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
971     }
972     gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
973
974     gst_buffer_unref (inbuf);
975
976     if (send) {
977       gst_pad_push (maruenc->srcpad, outbuf);
978     } else {
979       gst_buffer_unref (outbuf);
980     }
981   }
982
983   while (!g_queue_is_empty (maruenc->delay)) {
984     gst_buffer_unref (g_queue_pop_head (maruenc->delay));
985   }
986 #endif
987 }
988
989 static gboolean
990 gst_maruenc_event_video (GstPad *pad, GstEvent *event)
991 {
992   GstMaruEnc *maruenc;
993   maruenc = (GstMaruEnc *) gst_pad_get_parent (pad);
994
995   switch (GST_EVENT_TYPE (event)) {
996   case GST_EVENT_EOS:
997     gst_maruenc_flush_buffers (maruenc, TRUE);
998     break;
999   case GST_EVENT_CUSTOM_DOWNSTREAM:
1000   {
1001     const GstStructure *s;
1002     s = gst_event_get_structure (event);
1003
1004     if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1005 #if 0
1006       maruenc->picture->pict_type = FF_I_TYPE;
1007 #endif
1008     }
1009   }
1010     break;
1011   default:
1012     break;
1013   }
1014
1015   return gst_pad_push_event (maruenc->srcpad, event);
1016 }
1017
1018 static gboolean
1019 gst_maruenc_event_src (GstPad *pad, GstEvent *event)
1020 {
1021   GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
1022   gboolean forward = TRUE;
1023
1024   switch (GST_EVENT_TYPE (event)) {
1025   case GST_EVENT_CUSTOM_UPSTREAM:
1026   {
1027     const GstStructure *s;
1028     s = gst_event_get_structure (event);
1029
1030     if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1031 #if 0
1032       GST_OBJECT_LOCK (maruenc);
1033       maruenc->force_keyframe = TRUE;
1034       GST_OBJECT_UNLOCK (maruenc);
1035 #endif
1036       forward = FALSE;
1037       gst_event_unref (event);
1038     }
1039   }
1040     break;
1041   default:
1042     break;
1043   }
1044
1045   if (forward) {
1046     return gst_pad_push_event (maruenc->sinkpad, event);
1047   }
1048
1049   return TRUE;
1050 }
1051
1052 GstStateChangeReturn
1053 gst_maruenc_change_state (GstElement *element, GstStateChange transition)
1054 {
1055   GstMaruEnc *maruenc = (GstMaruEnc*)element;
1056   GstStateChangeReturn ret;
1057
1058   switch (transition) {
1059   default:
1060     break;
1061   }
1062
1063   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1064
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;
1072     }
1073     gst_adapter_clear (maruenc->adapter);
1074
1075 #if 0
1076     if (maruenc->flie) {
1077       fclose (maruenc->file);
1078       maruenc->file = NULL;
1079     }
1080 #endif
1081
1082     if (maruenc->working_buf) {
1083       g_free (maruenc->working_buf);
1084       maruenc->working_buf = NULL;
1085     }
1086     break;
1087   default:
1088     break;
1089   }
1090
1091   return ret;
1092 }
1093
1094 gboolean
1095 gst_maruenc_register (GstPlugin *plugin, GList *element)
1096 {
1097   GTypeInfo typeinfo = {
1098       sizeof (GstMaruEncClass),
1099       (GBaseInitFunc) gst_maruenc_base_init,
1100       NULL,
1101       (GClassInitFunc) gst_maruenc_class_init,
1102       NULL,
1103       NULL,
1104       sizeof (GstMaruEnc),
1105       0,
1106       (GInstanceInitFunc) gst_maruenc_init,
1107   };
1108
1109   GType type;
1110   gchar *type_name;
1111   gint rank = GST_RANK_PRIMARY;
1112   GList *elem = element;
1113   CodecElement *codec = NULL;
1114
1115   if (!elem) {
1116     return FALSE;
1117   }
1118
1119   /* register element */
1120   do {
1121     codec = (CodecElement *)(elem->data);
1122     if (!codec) {
1123       return FALSE;
1124     }
1125
1126     if (codec->codec_type != CODEC_TYPE_ENCODE) {
1127       continue;
1128     }
1129
1130     type_name = g_strdup_printf ("maru_%senc", codec->name);
1131     type = g_type_from_name (type_name);
1132     if (!type) {
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);
1135     }
1136
1137     if (!gst_element_register (plugin, type_name, rank, type)) {
1138       g_free (type_name);
1139       return FALSE;
1140     }
1141     g_free (type_name);
1142   } while ((elem = elem->next));
1143
1144   return TRUE;
1145 }