fix broken image problem when encoding video.
[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     gst_maru_avcodec_close (ctx, maruenc->dev);
440 #if 0
441     if (ctx->priv_data) {
442       gst_maru_avcodec_close (ctx, maruenc->dev);
443     }
444 #endif
445     g_free (ctx);
446   }
447
448   if (!caps) {
449     caps = gst_maruenc_get_possible_sizes (maruenc, pad,
450       gst_pad_get_pad_template_caps (pad));
451     GST_DEBUG_OBJECT (maruenc, "probing gave nothing, "
452       "return template %" GST_PTR_FORMAT, caps);
453     return caps;
454   }
455
456   GST_DEBUG_OBJECT (maruenc, "probed caps gave %" GST_PTR_FORMAT, caps);
457   oclass->sinkcaps = gst_caps_copy (caps);
458
459   finalcaps = gst_maruenc_get_possible_sizes (maruenc, pad, caps);
460   gst_caps_unref (caps);
461
462   return finalcaps;
463 }
464
465 static gboolean
466 gst_maruenc_setcaps (GstPad *pad, GstCaps *caps)
467 {
468   GstMaruEnc *maruenc;
469   GstMaruEncClass *oclass;
470   GstCaps *other_caps;
471   GstCaps *allowed_caps;
472   GstCaps *icaps;
473   enum PixelFormat pix_fmt;
474   int32_t buf_size;
475
476   maruenc = (GstMaruEnc *) (gst_pad_get_parent (pad));
477   oclass = (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
478
479   if (maruenc->opened) {
480     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
481     maruenc->opened = FALSE;
482
483     gst_pad_set_caps (maruenc->srcpad, NULL);
484   }
485
486   maruenc->context->bit_rate = maruenc->bitrate;
487   GST_DEBUG_OBJECT (maruenc, "Setting context to bitrate %lu, gop_size %d",
488       maruenc->bitrate, maruenc->gop_size);
489
490 #if 0
491
492   // user defined properties
493   maruenc->context->gop_size = maruenc->gop_size;
494   maruenc->context->lmin = (maruenc->lmin * FF_QP2LAMBDA + 0.5);
495   maruenc->context->lmax = (maruenc->lmax * FF_QP2LAMBDA + 0.5);
496
497   // some other defaults
498   maruenc->context->b_frame_strategy = 0;
499   maruenc->context->coder_type = 0;
500   maruenc->context->context_model = 0;
501   maruenc->context->scenechange_threshold = 0;
502   maruenc->context->inter_threshold = 0;
503
504   if (maruenc->interlaced) {
505     maruenc->context->flags |=
506       CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME;
507     maruenc->picture->interlaced_frame = TRUE;
508
509     maruenc->picture->top_field_first = TRUE;
510   }
511 #endif
512
513   gst_maru_caps_with_codectype (oclass->codec->media_type, caps, maruenc->context);
514
515   if (!maruenc->context->video.fps_d) {
516     maruenc->context->video.fps_d = 25;
517     maruenc->context->video.fps_n = 1;
518   } else if (!strcmp(oclass->codec->name ,"mpeg4")
519       && (maruenc->context->video.fps_d > 65535)) {
520       maruenc->context->video.fps_n =
521         (gint) gst_util_uint64_scale_int (maruenc->context->video.fps_n,
522             65535, maruenc->context->video.fps_d);
523       maruenc->context->video.fps_d = 65535;
524       GST_LOG_OBJECT (maruenc, "MPEG4 : scaled down framerate to %d / %d",
525           maruenc->context->video.fps_d, maruenc->context->video.fps_n);
526   }
527
528   pix_fmt = maruenc->context->video.pix_fmt;
529
530   {
531     switch (oclass->codec->media_type) {
532     case AVMEDIA_TYPE_VIDEO:
533     {
534       int width, height;
535
536       width = maruenc->context->video.width;
537       height = maruenc->context->video.height;
538       buf_size = width * height * 6 + FF_MIN_BUFFER_SIZE + 100;
539       break;
540     }
541     case AVMEDIA_TYPE_AUDIO:
542         buf_size = FF_MAX_AUDIO_FRAME_SIZE + 100;
543         break;
544     default:
545         buf_size = -1;
546         break;
547     }
548   }
549
550   maruenc->dev->buf_size = gst_maru_align_size(buf_size);
551
552   // open codec
553   if (gst_maru_avcodec_open (maruenc->context,
554       oclass->codec, maruenc->dev) < 0) {
555     GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to open codec",
556         oclass->codec->name);
557     return FALSE;
558   }
559
560   if (pix_fmt != maruenc->context->video.pix_fmt) {
561     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
562     GST_DEBUG_OBJECT (maruenc,
563       "maru_%senc: AV wants different colorspace (%d given, %d wanted)",
564       oclass->codec->name, pix_fmt, maruenc->context->video.pix_fmt);
565     return FALSE;
566   }
567
568   if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO
569     && pix_fmt == PIX_FMT_NONE) {
570     GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to determine input format",
571       oclass->codec->name);
572     return FALSE;
573   }
574
575   GST_DEBUG_OBJECT (maruenc, "picking an output format.");
576   allowed_caps = gst_pad_get_allowed_caps (maruenc->srcpad);
577   if (!allowed_caps) {
578     GST_DEBUG_OBJECT (maruenc, "but no peer, using template caps");
579     allowed_caps =
580       gst_caps_copy (gst_pad_get_pad_template_caps (maruenc->srcpad));
581   }
582
583   GST_DEBUG_OBJECT (maruenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
584   gst_maru_caps_with_codecname (oclass->codec->name,
585     oclass->codec->media_type, allowed_caps, maruenc->context);
586
587   other_caps =
588   gst_maru_codecname_to_caps (oclass->codec->name, maruenc->context, TRUE);
589   if (!other_caps) {
590   GST_DEBUG("Unsupported codec - no caps found");
591     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
592     return FALSE;
593   }
594
595   icaps = gst_caps_intersect (allowed_caps, other_caps);
596   gst_caps_unref (allowed_caps);
597   gst_caps_unref (other_caps);
598   if (gst_caps_is_empty (icaps)) {
599     gst_caps_unref (icaps);
600     return FALSE;
601   }
602
603   if (gst_caps_get_size (icaps) > 1) {
604     GstCaps *newcaps;
605
606     newcaps =
607       gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
608               0)), NULL);
609     gst_caps_unref (icaps);
610     icaps = newcaps;
611   }
612
613   if (!gst_pad_set_caps (maruenc->srcpad, icaps)) {
614     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
615     gst_caps_unref (icaps);
616     return FALSE;
617   }
618   gst_object_unref (maruenc);
619
620   maruenc->opened = TRUE;
621
622   return TRUE;
623 }
624
625 static void
626 gst_maruenc_setup_working_buf (GstMaruEnc *maruenc)
627 {
628   guint wanted_size =
629       maruenc->context->video.width * maruenc->context->video.height * 6 +
630       FF_MIN_BUFFER_SIZE;
631
632   if (maruenc->working_buf == NULL ||
633     maruenc->working_buf_size != wanted_size) {
634     if (maruenc->working_buf) {
635       g_free (maruenc->working_buf);
636     }
637     maruenc->working_buf_size = wanted_size;
638     maruenc->working_buf = g_malloc0 (maruenc->working_buf_size);
639   }
640   maruenc->buffer_size = wanted_size;
641 }
642
643 GstFlowReturn
644 gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer)
645 {
646   GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
647   GstBuffer *outbuf = NULL;
648   gint ret_size = 0, frame_size = 0;
649   int ret = 0;
650   uint32_t mem_offset = 0;
651   uint8_t *working_buf = NULL;
652
653   GST_DEBUG_OBJECT (maruenc,
654       "Received buffer of time %" GST_TIME_FORMAT,
655       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
656
657 #if 0
658   GST_OBJECT_LOCK (maruenc);
659   force_keyframe = maruenc->force_keyframe;
660   maruenc->force_keyframe = FALSE;
661   GST_OBJECT_UNLOCK (maruenc);
662
663   if (force_keyframe) {
664     maruenc->picture->pict_type = FF_I_TYPE;
665   }
666 #endif
667
668   frame_size = gst_maru_avpicture_size (maruenc->context->video.pix_fmt,
669       maruenc->context->video.width, maruenc->context->video.height);
670   g_return_val_if_fail (frame_size == GST_BUFFER_SIZE (buffer),
671       GST_FLOW_ERROR);
672
673 #if 0
674   pts = gst_maru_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buffer) /
675     maruenc->context.video.ticks_per_frame,
676     maruenc->context.video.fps_n, maruen->context.video.fps_d);
677 #endif
678
679   // TODO: check whether this func needs or not.
680   gst_maruenc_setup_working_buf (maruenc);
681
682   ret_size =
683     codec_encode_video (maruenc->context, maruenc->working_buf,
684                 maruenc->working_buf_size, GST_BUFFER_DATA (buffer),
685                 GST_BUFFER_SIZE (buffer), GST_BUFFER_TIMESTAMP (buffer),
686                 maruenc->dev);
687
688   if (ret_size < 0) {
689     GstMaruEncClass *oclass =
690       (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
691     GST_ERROR_OBJECT (maruenc,
692         "maru_%senc: failed to encode buffer", oclass->codec->name);
693     gst_buffer_unref (buffer);
694     return GST_FLOW_OK;
695   }
696
697   g_queue_push_tail (maruenc->delay, buffer);
698   if (ret_size) {
699     buffer = g_queue_pop_head (maruenc->delay);
700   } else {
701     return GST_FLOW_OK;
702   }
703
704 #if 0
705   if (maruenc->file && maruenc->context->stats_out) {
706     if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
707       GST_ELEMENT_ERROR (maruenc, RESOURCE, WRITE,
708         (("Could not write to file \"%s\"."), maruenc->filename),
709         GST_ERROR_SYSTEM);
710     }
711   }
712 #endif
713
714   mem_offset = maruenc->dev->mem_info.offset;
715   working_buf = maruenc->dev->buf + mem_offset;
716
717   CODEC_LOG (DEBUG,
718     "encoded video. mem_offset = 0x%x\n",  mem_offset);
719
720   outbuf = gst_buffer_new_and_alloc (ret_size);
721   memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
722   // memcpy (GST_BUFFER_DATA (outbuf), working_buf, ret_size);
723   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
724   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
725
726 #if 0
727   ret = ioctl(maruenc->dev->fd, CODEC_CMD_RELEASE_BUFFER, &mem_offset);
728   if (ret < 0) {
729     CODEC_LOG (ERR, "failed to release used buffer\n");
730   }
731 #endif
732
733 #if 0
734   if (maruenc->context->coded_frame) {
735     if (!maruenc->context->coded_frame->key_frame) {
736       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
737     }
738   } else {
739     GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
740   }
741 #endif
742   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
743
744   gst_buffer_unref (buffer);
745
746 #if 0
747
748   if (force_keyframe) {
749     gst_pad_push_event (maruenc->srcpad,
750       gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
751       gst_structure_new ("GstForceKeyUnit", "timestamp",
752                          G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), NULL)));
753   }
754 #endif
755
756   return gst_pad_push (maruenc->srcpad, outbuf);
757 }
758
759 GstFlowReturn
760 gst_maruenc_encode_audio (GstMaruEnc *maruenc, guint8 *audio_in,
761   guint in_size, guint max_size, GstClockTime timestamp,
762   GstClockTime duration, gboolean discont)
763 {
764   GstBuffer *outbuf;
765   guint8 *audio_out;
766   gint res;
767   GstFlowReturn ret;
768
769   outbuf = gst_buffer_new_and_alloc (max_size + FF_MIN_BUFFER_SIZE);
770   audio_out = GST_BUFFER_DATA (outbuf);
771
772   GST_LOG_OBJECT (maruenc, "encoding buffer of max size %d", max_size);
773   if (maruenc->buffer_size != max_size) {
774     maruenc->buffer_size = max_size;
775   }
776
777   res = codec_encode_audio (maruenc->context, audio_out, max_size,
778                                   audio_in, in_size, maruenc->dev);
779
780   if (res < 0) {
781     GST_ERROR_OBJECT (maruenc, "Failed to encode buffer: %d", res);
782     gst_buffer_unref (outbuf);
783     return GST_FLOW_OK;
784   }
785   GST_LOG_OBJECT (maruenc, "got output size %d", res);
786
787   GST_BUFFER_SIZE (outbuf) = res;
788   GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
789   GST_BUFFER_DURATION (outbuf) = duration;
790   if (discont) {
791     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
792   }
793   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
794
795   GST_LOG_OBJECT (maruenc, "pushing size %d, timestamp %",
796       GST_TIME_FORMAT, res, GST_TIME_ARGS (timestamp));
797
798   ret = gst_pad_push (maruenc->srcpad, outbuf);
799
800   return ret;
801 }
802
803 static GstFlowReturn
804 gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer)
805 {
806   GstMaruEnc *maruenc;
807 //  GstMaruEncClass *oclass;
808   GstClockTime timestamp, duration;
809   guint in_size, frame_size;
810   gint osize;
811   GstFlowReturn ret;
812   gint out_size = 0;
813   gboolean discont;
814   guint8 *in_data;
815   CodecContext *ctx;
816
817   maruenc = (GstMaruEnc *) (GST_OBJECT_PARENT (pad));
818 //  oclass = (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
819
820   ctx = maruenc->context;
821
822   in_size = GST_BUFFER_SIZE (buffer);
823   timestamp = GST_BUFFER_TIMESTAMP (buffer);
824   duration = GST_BUFFER_DURATION (buffer);
825   discont = GST_BUFFER_IS_DISCONT (buffer);
826
827   GST_DEBUG_OBJECT (maruenc,
828     "Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
829     ", size %d", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), in_size);
830
831   frame_size = ctx->audio.frame_size;
832   osize = ctx->audio.bits_per_sample_fmt;
833
834   if (frame_size > 1) {
835     guint avail, frame_bytes;
836
837     if (discont) {
838       GST_LOG_OBJECT (maruenc, "DISCONT, clear adapter");
839       gst_adapter_clear (maruenc->adapter);
840       maruenc->discont = TRUE;
841     }
842
843     if (gst_adapter_available (maruenc->adapter) == 0) {
844       GST_LOG_OBJECT (maruenc, "taking buffer timestamp %" GST_TIME_FORMAT,
845         GST_TIME_ARGS (timestamp));
846       maruenc->adapter_ts = timestamp;
847       maruenc->adapter_consumed = 0;
848     } else {
849       GstClockTime upstream_time;
850       GstClockTime consumed_time;
851       guint64 bytes;
852
853       consumed_time =
854         gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
855             ctx->audio.sample_rate);
856       timestamp = maruenc->adapter_ts + consumed_time;
857       GST_LOG_OBJECT (maruenc, "taking adapter timestamp %" GST_TIME_FORMAT
858         " and adding consumed time %" GST_TIME_FORMAT,
859         GST_TIME_ARGS (maruenc->adapter_ts), GST_TIME_ARGS (consumed_time));
860
861       upstream_time = gst_adapter_prev_timestamp (maruenc->adapter, &bytes);
862       if (GST_CLOCK_TIME_IS_VALID (upstream_time)) {
863         GstClockTimeDiff diff;
864
865         upstream_time +=
866           gst_util_uint64_scale (bytes, GST_SECOND,
867             ctx->audio.sample_rate * osize * ctx->audio.channels);
868         diff = upstream_time - timestamp;
869
870         if (diff > GST_SECOND / 10 || diff < -GST_SECOND / 10) {
871           GST_DEBUG_OBJECT (maruenc, "adapter timestamp drifting, "
872             "taking upstream timestamp %" GST_TIME_FORMAT,
873             GST_TIME_ARGS (upstream_time));
874           timestamp = upstream_time;
875
876           maruenc->adapter_consumed = bytes / (osize * ctx->audio.channels);
877           maruenc->adapter_ts =
878             upstream_time - gst_util_uint64_scale (maruenc->adapter_consumed,
879                 GST_SECOND, ctx->audio.sample_rate);
880           maruenc->discont = TRUE;
881         }
882       }
883     }
884
885     GST_LOG_OBJECT (maruenc, "pushing buffer in adapter");
886     gst_adapter_push (maruenc->adapter, buffer);
887
888     frame_bytes = frame_size * osize * ctx->audio.channels;
889     avail = gst_adapter_available (maruenc->adapter);
890
891     GST_LOG_OBJECT (maruenc, "frame_bytes %u, avail %u", frame_bytes, avail);
892
893     while (avail >= frame_bytes) {
894       GST_LOG_OBJECT (maruenc, "taking %u bytes from the adapter", frame_bytes);
895
896       in_data = (guint8 *) gst_adapter_peek (maruenc->adapter, frame_bytes);
897       maruenc->adapter_consumed += frame_size;
898
899       duration =
900         gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
901           ctx->audio.sample_rate);
902       duration -= (timestamp - maruenc->adapter_ts);
903
904       out_size = frame_bytes * 4;
905
906       ret =
907         gst_maruenc_encode_audio (maruenc, in_data, frame_bytes, out_size,
908           timestamp, duration, maruenc->discont);
909
910       gst_adapter_flush (maruenc->adapter, frame_bytes);
911       if (ret != GST_FLOW_OK) {
912         GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
913           gst_flow_get_name (ret));
914       }
915
916       timestamp += duration;
917
918       maruenc->discont = FALSE;
919       avail = gst_adapter_available (maruenc->adapter);
920     }
921     GST_LOG_OBJECT (maruenc, "%u bytes left in the adapter", avail);
922   } else {
923 #if 0
924     int coded_bps = av_get_bits_per_sample (oclass->codec->name);
925
926     GST_LOG_OBJECT (maruenc, "coded bps %d, osize %d", coded_bps, osize);
927
928     out_size = in_size / osize;
929     if (coded_bps) {
930       out_size = (out_size * coded_bps) / 8;
931     }
932 #endif
933     in_data = (guint8 *) GST_BUFFER_DATA (buffer);
934     ret = gst_maruenc_encode_audio (maruenc, in_data, in_size, out_size,
935       timestamp, duration, discont);
936     gst_buffer_unref (buffer);
937     if (ret != GST_FLOW_OK) {
938       GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
939         gst_flow_get_name (ret));
940     }
941   }
942
943   return GST_FLOW_OK;
944 }
945
946 static void
947 gst_maruenc_flush_buffers (GstMaruEnc *maruenc, gboolean send)
948 {
949 #if 0
950   GstBuffer *outbuf, *inbuf;
951   gint ret_size = 0;
952 #endif
953
954   GST_DEBUG_OBJECT (maruenc, "flushing buffers with sending %d", send);
955
956   if (!maruenc->opened) {
957     while (!g_queue_is_empty (maruenc->delay)) {
958       gst_buffer_unref (g_queue_pop_head (maruenc->delay));
959     }
960   }
961
962 #if 0
963   while (!g_queue_is_empty (maruenc->delay)) {
964     maruenc_setup_working_buf (maruenc);
965
966     ret_size = codec_encode_video (maruenc->context,
967       maruenc->working_buf, maruenc->working_buf_size, NULL, NULL, 0,
968       maruenc->dev);
969
970     if (ret_size < 0) {
971       GstMaruEncClass *oclass =
972         (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
973       GST_WARNING_OBJECT (maruenc,
974         "maru_%senc: failed to flush buffer", oclass->codec->name);
975       break;
976     }
977
978     if (maruenc->file && maruenc->context->stats_out) {
979       if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
980         GST_ELEMENT_ERROR (emeulenc, RESOURCE, WRITE,
981           (("Could not write to file \"%s\"."), maruenc->filename),
982           GST_ERROR_SYSTEM);
983       }
984     }
985
986     inbuf = g_queue_pop_head (maruenc->delay);
987
988     outbuf = gst_buffer_new_and_alloc (ret_size);
989     memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
990     GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
991     GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
992
993     if (!maruenc->context->coded_frame->key_frame) {
994       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
995     }
996     gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
997
998     gst_buffer_unref (inbuf);
999
1000     if (send) {
1001       gst_pad_push (maruenc->srcpad, outbuf);
1002     } else {
1003       gst_buffer_unref (outbuf);
1004     }
1005   }
1006
1007   while (!g_queue_is_empty (maruenc->delay)) {
1008     gst_buffer_unref (g_queue_pop_head (maruenc->delay));
1009   }
1010 #endif
1011 }
1012
1013 static gboolean
1014 gst_maruenc_event_video (GstPad *pad, GstEvent *event)
1015 {
1016   GstMaruEnc *maruenc;
1017   maruenc = (GstMaruEnc *) gst_pad_get_parent (pad);
1018
1019   switch (GST_EVENT_TYPE (event)) {
1020   case GST_EVENT_EOS:
1021     gst_maruenc_flush_buffers (maruenc, TRUE);
1022     break;
1023   case GST_EVENT_CUSTOM_DOWNSTREAM:
1024   {
1025     const GstStructure *s;
1026     s = gst_event_get_structure (event);
1027
1028     if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1029 #if 0
1030       maruenc->picture->pict_type = FF_I_TYPE;
1031 #endif
1032     }
1033   }
1034     break;
1035   default:
1036     break;
1037   }
1038
1039   return gst_pad_push_event (maruenc->srcpad, event);
1040 }
1041
1042 static gboolean
1043 gst_maruenc_event_src (GstPad *pad, GstEvent *event)
1044 {
1045   GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
1046   gboolean forward = TRUE;
1047
1048   switch (GST_EVENT_TYPE (event)) {
1049   case GST_EVENT_CUSTOM_UPSTREAM:
1050   {
1051     const GstStructure *s;
1052     s = gst_event_get_structure (event);
1053
1054     if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1055 #if 0
1056       GST_OBJECT_LOCK (maruenc);
1057       maruenc->force_keyframe = TRUE;
1058       GST_OBJECT_UNLOCK (maruenc);
1059 #endif
1060       forward = FALSE;
1061       gst_event_unref (event);
1062     }
1063   }
1064     break;
1065   default:
1066     break;
1067   }
1068
1069   if (forward) {
1070     return gst_pad_push_event (maruenc->sinkpad, event);
1071   }
1072
1073   return TRUE;
1074 }
1075
1076 GstStateChangeReturn
1077 gst_maruenc_change_state (GstElement *element, GstStateChange transition)
1078 {
1079   GstMaruEnc *maruenc = (GstMaruEnc*)element;
1080   GstStateChangeReturn ret;
1081
1082   switch (transition) {
1083   default:
1084     break;
1085   }
1086
1087   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1088
1089   switch (transition) {
1090   case GST_STATE_CHANGE_PAUSED_TO_READY:
1091     gst_maruenc_flush_buffers (maruenc, FALSE);
1092     if (maruenc->opened) {
1093       gst_maru_avcodec_close (maruenc->context, maruenc->dev);
1094       maruenc->opened = FALSE;
1095     }
1096     gst_adapter_clear (maruenc->adapter);
1097
1098 #if 0
1099     if (maruenc->flie) {
1100       fclose (maruenc->file);
1101       maruenc->file = NULL;
1102     }
1103 #endif
1104
1105     if (maruenc->working_buf) {
1106       g_free (maruenc->working_buf);
1107       maruenc->working_buf = NULL;
1108     }
1109     break;
1110   default:
1111     break;
1112   }
1113
1114   return ret;
1115 }
1116
1117 gboolean
1118 gst_maruenc_register (GstPlugin *plugin, GList *element)
1119 {
1120   GTypeInfo typeinfo = {
1121       sizeof (GstMaruEncClass),
1122       (GBaseInitFunc) gst_maruenc_base_init,
1123       NULL,
1124       (GClassInitFunc) gst_maruenc_class_init,
1125       NULL,
1126       NULL,
1127       sizeof (GstMaruEnc),
1128       0,
1129       (GInstanceInitFunc) gst_maruenc_init,
1130   };
1131
1132   GType type;
1133   gchar *type_name;
1134   gint rank = GST_RANK_PRIMARY;
1135   GList *elem = element;
1136   CodecElement *codec = NULL;
1137
1138   if (!elem) {
1139     return FALSE;
1140   }
1141
1142   /* register element */
1143   do {
1144     codec = (CodecElement *)(elem->data);
1145     if (!codec) {
1146       return FALSE;
1147     }
1148
1149     if (codec->codec_type != CODEC_TYPE_ENCODE) {
1150       continue;
1151     }
1152
1153     type_name = g_strdup_printf ("maru_%senc", codec->name);
1154     type = g_type_from_name (type_name);
1155     if (!type) {
1156       type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1157       g_type_set_qdata (type, GST_MARUENC_PARAMS_QDATA, (gpointer) codec);
1158     }
1159
1160     if (!gst_element_register (plugin, type_name, rank, type)) {
1161       g_free (type_name);
1162       return FALSE;
1163     }
1164     g_free (type_name);
1165   } while ((elem = elem->next));
1166
1167   return TRUE;
1168 }