Fix close routine at decoder plugin.
[platform/adaptation/emulator/gst-plugins-emulator.git] / src / gstmaruenc.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 typedef struct _GstMaruEnc
29 {
30   GstElement element;
31
32   GstPad *srcpad;
33   GstPad *sinkpad;
34
35   CodecContext *context;
36   CodecDevice *dev;
37   gboolean opened;
38   GstClockTime adapter_ts;
39   guint64 adapter_consumed;
40   GstAdapter *adapter;
41   gboolean discont;
42
43   // cache
44   gulong bitrate;
45   gint gop_size;
46   gulong buffer_size;
47
48   guint8 *working_buf;
49   gulong working_buf_size;
50
51   GQueue *delay;
52
53 } GstMaruEnc;
54
55 typedef struct _GstMaruEncClass
56 {
57   GstElementClass parent_class;
58
59   CodecElement *codec;
60   GstPadTemplate *sinktempl;
61   GstPadTemplate *srctempl;
62   GstCaps *sinkcaps;
63 } GstMaruEncClass;
64
65 static GstElementClass *parent_class = NULL;
66
67 static void gst_maruenc_base_init (GstMaruEncClass *klass);
68 static void gst_maruenc_class_init (GstMaruEncClass *klass);
69 static void gst_maruenc_init (GstMaruEnc *maruenc);
70 static void gst_maruenc_finalize (GObject *object);
71
72 static gboolean gst_maruenc_setcaps (GstPad *pad, GstCaps *caps);
73 static GstCaps *gst_maruenc_getcaps (GstPad *pad);
74
75 static GstCaps *gst_maruenc_get_possible_sizes (GstMaruEnc *maruenc,
76   GstPad *pad, const GstCaps *caps);
77
78 static GstFlowReturn gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer);
79 static GstFlowReturn gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer);
80
81 static gboolean gst_maruenc_event_video (GstPad *pad, GstEvent *event);
82 static gboolean gst_maruenc_event_src (GstPad *pad, GstEvent *event);
83
84 GstStateChangeReturn gst_maruenc_change_state (GstElement *element, GstStateChange transition);
85
86 #define DEFAULT_VIDEO_BITRATE   300000
87 #define DEFAULT_VIDEO_GOP_SIZE  15
88 #define DEFAULT_AUDIO_BITRATE   128000
89
90 #define DEFAULT_WIDTH 352
91 #define DEFAULT_HEIGHT 288
92
93 /*
94  * Implementation
95  */
96 static void
97 gst_maruenc_base_init (GstMaruEncClass *klass)
98 {
99     GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
100     GstPadTemplate *sinktempl = NULL, *srctempl = NULL;
101     GstCaps *sinkcaps = NULL, *srccaps = NULL;
102     CodecElement *codec;
103     gchar *longname, *classification, *description;
104
105     codec =
106         (CodecElement *)g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
107          GST_MARUENC_PARAMS_QDATA);
108
109     longname = g_strdup_printf ("%s Encoder", codec->longname);
110     classification = g_strdup_printf ("Codec/Encoder/%s",
111             (codec->media_type == AVMEDIA_TYPE_VIDEO) ? "Video" : "Audio");
112     description = g_strdup_printf ("%s Encoder", codec->name);
113
114     gst_element_class_set_details_simple (element_class,
115             longname,
116             classification,
117             description,
118 //            "accelerated codec for Tizen Emulator",
119             "Kitae Kim <kt920.kim@samsung.com>");
120
121     g_free (longname);
122     g_free (classification);
123
124
125   if (!(srccaps = gst_maru_codecname_to_caps (codec->name, NULL, TRUE))) {
126     GST_DEBUG ("Couldn't get source caps for encoder '%s'", codec->name);
127     srccaps = gst_caps_new_simple ("unknown/unknown", NULL);
128   }
129
130   switch (codec->media_type) {
131   case AVMEDIA_TYPE_VIDEO:
132     sinkcaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv; video/x-raw-gray");
133     break;
134   case AVMEDIA_TYPE_AUDIO:
135     sinkcaps = gst_maru_codectype_to_audio_caps (NULL, codec->name, TRUE, codec);
136     break;
137   default:
138     GST_LOG("unknown media type");
139     break;
140   }
141
142   if (!sinkcaps) {
143       GST_DEBUG ("Couldn't get sink caps for encoder '%s'", codec->name);
144       sinkcaps = gst_caps_new_simple ("unknown/unknown", NULL);
145   }
146
147   sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
148           GST_PAD_ALWAYS, sinkcaps);
149   srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
150           GST_PAD_ALWAYS, srccaps);
151
152   gst_element_class_add_pad_template (element_class, srctempl);
153   gst_element_class_add_pad_template (element_class, sinktempl);
154
155   klass->codec = codec;
156   klass->sinktempl = sinktempl;
157   klass->srctempl = srctempl;
158   klass->sinkcaps = NULL;
159 }
160
161 static void
162 gst_maruenc_class_init (GstMaruEncClass *klass)
163 {
164   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
165   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
166
167   parent_class = g_type_class_peek_parent (klass);
168
169 #if 0
170   gobject_class->set_property = gst_maruenc_set_property
171   gobject_class->get_property = gst_maruenc_get_property
172 #endif
173
174   gstelement_class->change_state = gst_maruenc_change_state;
175
176   gobject_class->finalize = gst_maruenc_finalize;
177 }
178
179 static void
180 gst_maruenc_init (GstMaruEnc *maruenc)
181 {
182   GstMaruEncClass *oclass;
183   oclass = (GstMaruEncClass*) (G_OBJECT_GET_CLASS(maruenc));
184
185   maruenc->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
186   gst_pad_set_setcaps_function (maruenc->sinkpad,
187     GST_DEBUG_FUNCPTR(gst_maruenc_setcaps));
188   gst_pad_set_getcaps_function (maruenc->sinkpad,
189     GST_DEBUG_FUNCPTR(gst_maruenc_getcaps));
190
191   maruenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
192   gst_pad_use_fixed_caps (maruenc->srcpad);
193
194 #if 0
195   maruenc->file = NULL;
196 #endif
197   maruenc->delay = g_queue_new ();
198
199   // instead of AVCodecContext
200   maruenc->context = g_malloc0 (sizeof(CodecContext));
201   maruenc->context->video.pix_fmt = PIX_FMT_NONE;
202   maruenc->context->audio.sample_fmt = SAMPLE_FMT_NONE;
203
204   maruenc->opened = FALSE;
205
206   maruenc->dev = g_malloc0 (sizeof(CodecDevice));
207   if (!maruenc->dev) {
208     GST_ERROR_OBJECT (maruenc, "failed to allocate CodecDevice");
209     // TODO: error handling
210   }
211
212   if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO) {
213     gst_pad_set_chain_function (maruenc->sinkpad, gst_maruenc_chain_video);
214     gst_pad_set_event_function (maruenc->sinkpad, gst_maruenc_event_video);
215     gst_pad_set_event_function (maruenc->srcpad, gst_maruenc_event_src);
216
217     maruenc->bitrate = DEFAULT_VIDEO_BITRATE;
218     maruenc->buffer_size = 512 * 1024;
219     maruenc->gop_size = DEFAULT_VIDEO_GOP_SIZE;
220
221   } else if (oclass->codec->media_type == AVMEDIA_TYPE_AUDIO){
222     gst_pad_set_chain_function (maruenc->sinkpad, gst_maruenc_chain_audio);
223     maruenc->bitrate = DEFAULT_AUDIO_BITRATE;
224   }
225
226   gst_element_add_pad (GST_ELEMENT (maruenc), maruenc->sinkpad);
227   gst_element_add_pad (GST_ELEMENT (maruenc), maruenc->srcpad);
228
229   // TODO: need to know what adapter does.
230   maruenc->adapter = gst_adapter_new ();
231 }
232
233 static void
234 gst_maruenc_finalize (GObject *object)
235 {
236   // Deinit Decoder
237   GstMaruEnc *maruenc = (GstMaruEnc *) object;
238
239   if (maruenc->opened) {
240     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
241     maruenc->opened = FALSE;
242   }
243
244   if (maruenc->context) {
245     g_free (maruenc->context);
246     maruenc->context = NULL;
247   }
248
249   if (maruenc->dev) {
250     g_free (maruenc->dev);
251     maruenc->dev = NULL;
252   }
253
254   g_queue_free (maruenc->delay);
255 #if 0
256   g_free (maruenc->filename);
257 #endif
258
259   g_object_unref (maruenc->adapter);
260
261   G_OBJECT_CLASS (parent_class)->finalize (object);
262 }
263
264 static GstCaps *
265 gst_maruenc_get_possible_sizes (GstMaruEnc *maruenc, GstPad *pad,
266   const GstCaps *caps)
267 {
268   GstCaps *othercaps = NULL;
269   GstCaps *tmpcaps = NULL;
270   GstCaps *intersect = NULL;
271   guint i;
272
273   othercaps = gst_pad_peer_get_caps (maruenc->srcpad);
274
275   if (!othercaps) {
276     return gst_caps_copy (caps);
277   }
278
279   intersect = gst_caps_intersect (othercaps,
280     gst_pad_get_pad_template_caps (maruenc->srcpad));
281   gst_caps_unref (othercaps);
282
283   if (gst_caps_is_empty (intersect)) {
284     return intersect;
285   }
286
287   if (gst_caps_is_any (intersect)) {
288     return gst_caps_copy (caps);
289   }
290
291   tmpcaps = gst_caps_new_empty ();
292
293   for (i = 0; i <gst_caps_get_size (intersect); i++) {
294     GstStructure *s = gst_caps_get_structure (intersect, i);
295     const GValue *height = NULL;
296     const GValue *width = NULL;
297     const GValue *framerate = NULL;
298     GstStructure *tmps;
299
300     height = gst_structure_get_value (s, "height");
301     width = gst_structure_get_value (s, "width");
302     framerate = gst_structure_get_value (s, "framerate");
303
304     tmps = gst_structure_new ("video/x-rwa-rgb", NULL);
305     if (width) {
306       gst_structure_set_value (tmps, "width", width);
307     }
308     if (height) {
309       gst_structure_set_value (tmps, "height", height);
310     }
311     if (framerate) {
312       gst_structure_set_value (tmps, "framerate", framerate);
313     }
314     gst_caps_merge_structure (tmpcaps, gst_structure_copy (tmps));
315
316     gst_structure_set_name (tmps, "video/x-raw-yuv");
317     gst_caps_merge_structure (tmpcaps, gst_structure_copy (tmps));
318
319     gst_structure_set_name (tmps, "video/x-raw-gray");
320     gst_caps_merge_structure (tmpcaps, tmps);
321   }
322   gst_caps_unref (intersect);
323
324   intersect = gst_caps_intersect (caps, tmpcaps);
325   gst_caps_unref (tmpcaps);
326
327   return intersect;
328 }
329
330 static GstCaps *
331 gst_maruenc_getcaps (GstPad *pad)
332 {
333   GstMaruEnc *maruenc = (GstMaruEnc *) GST_PAD_PARENT (pad);
334   GstMaruEncClass *oclass =
335     (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
336   CodecContext *ctx = NULL;
337   enum PixelFormat pixfmt;
338   GstCaps *caps = NULL;
339   GstCaps *finalcaps = NULL;
340   gint i;
341
342   GST_DEBUG_OBJECT (maruenc, "getting caps");
343
344   if (!oclass->codec) {
345     GST_ERROR_OBJECT (maruenc, "codec element is null.");
346     return NULL;
347   }
348
349   if (oclass->codec->media_type == AVMEDIA_TYPE_AUDIO) {
350     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
351
352     GST_DEBUG_OBJECT (maruenc, "audio caps, return template %" GST_PTR_FORMAT,
353       caps);
354     return caps;
355   }
356
357   // cached
358   if (oclass->sinkcaps) {
359     caps = gst_maruenc_get_possible_sizes (maruenc, pad, oclass->sinkcaps);
360     GST_DEBUG_OBJECT (maruenc, "return cached caps %" GST_PTR_FORMAT, caps);
361     return caps;
362   }
363
364   GST_DEBUG_OBJECT (maruenc, "probing caps");
365   i = pixfmt = 0;
366
367   for (pixfmt = 0;; pixfmt++) {
368     GstCaps *tmpcaps;
369
370     if ((pixfmt = oclass->codec->pix_fmts[i++]) == PIX_FMT_NONE) {
371       GST_DEBUG_OBJECT (maruenc,
372           "At the end of official pixfmt for this codec, breaking out");
373       break;
374     }
375
376     GST_DEBUG_OBJECT (maruenc,
377         "Got an official pixfmt [%d], attempting to get caps", pixfmt);
378     tmpcaps = gst_maru_pixfmt_to_caps (pixfmt, NULL, oclass->codec->name);
379     if (tmpcaps) {
380       GST_DEBUG_OBJECT (maruenc, "Got caps, breaking out");
381       if (!caps) {
382         caps = gst_caps_new_empty ();
383       }
384       gst_caps_append (caps, tmpcaps);
385       continue;
386     }
387
388     GST_DEBUG_OBJECT (maruenc,
389         "Couldn't figure out caps without context, trying again with a context");
390
391     GST_DEBUG_OBJECT (maruenc, "pixfmt: %d", pixfmt);
392     if (pixfmt >= PIX_FMT_NB) {
393       GST_WARNING ("Invalid pixfmt, breaking out");
394       break;
395     }
396
397     ctx = g_malloc0 (sizeof(CodecContext));
398     if (!ctx) {
399       GST_DEBUG_OBJECT (maruenc, "no context");
400       break;
401     }
402
403     ctx->video.width = DEFAULT_WIDTH;
404     ctx->video.height = DEFAULT_HEIGHT;
405     ctx->video.fps_n = 1;
406     ctx->video.fps_d = 25;
407     ctx->video.ticks_per_frame = 1;
408     ctx->bit_rate = DEFAULT_VIDEO_BITRATE;
409
410     ctx->video.pix_fmt = pixfmt;
411
412     GST_DEBUG ("Attempting to open codec");
413     if (gst_maru_avcodec_open (ctx, oclass->codec, maruenc->dev) >= 0
414         && ctx->video.pix_fmt == pixfmt) {
415       ctx->video.width = -1;
416       if (!caps) {
417         caps = gst_caps_new_empty ();
418       }
419       tmpcaps = gst_maru_codectype_to_caps (oclass->codec->media_type, ctx,
420           oclass->codec->name, TRUE);
421       if (tmpcaps) {
422         gst_caps_append (caps, tmpcaps);
423       } else {
424         GST_LOG_OBJECT (maruenc,
425             "Couldn't get caps for codec: %s", oclass->codec->name);
426       }
427       gst_maru_avcodec_close (ctx, maruenc->dev);
428     } else {
429       GST_DEBUG_OBJECT (maruenc, "Opening codec failed with pixfmt: %d", pixfmt);
430     }
431
432 #if 0
433     if (ctx->priv_data) {
434       gst_maru_avcodec_close (ctx, maruenc->dev);
435     }
436 #endif
437     g_free (ctx);
438   }
439
440   if (!caps) {
441     caps = gst_maruenc_get_possible_sizes (maruenc, pad,
442       gst_pad_get_pad_template_caps (pad));
443     GST_DEBUG_OBJECT (maruenc, "probing gave nothing, "
444       "return template %" GST_PTR_FORMAT, caps);
445     return caps;
446   }
447
448   GST_DEBUG_OBJECT (maruenc, "probed caps gave %" GST_PTR_FORMAT, caps);
449   oclass->sinkcaps = gst_caps_copy (caps);
450
451   finalcaps = gst_maruenc_get_possible_sizes (maruenc, pad, caps);
452   gst_caps_unref (caps);
453
454   return finalcaps;
455 }
456
457 static gboolean
458 gst_maruenc_setcaps (GstPad *pad, GstCaps *caps)
459 {
460   GstMaruEnc *maruenc;
461   GstMaruEncClass *oclass;
462   GstCaps *other_caps;
463   GstCaps *allowed_caps;
464   GstCaps *icaps;
465   enum PixelFormat pix_fmt;
466
467   maruenc = (GstMaruEnc *) (gst_pad_get_parent (pad));
468   oclass = (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
469
470   if (maruenc->opened) {
471     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
472     maruenc->opened = FALSE;
473
474     gst_pad_set_caps (maruenc->srcpad, NULL);
475   }
476
477   maruenc->context->bit_rate = maruenc->bitrate;
478   GST_DEBUG_OBJECT (maruenc, "Setting context to bitrate %lu, gop_size %d",
479       maruenc->bitrate, maruenc->gop_size);
480
481 #if 0
482
483   // user defined properties
484   maruenc->context->gop_size = maruenc->gop_size;
485   maruenc->context->lmin = (maruenc->lmin * FF_QP2LAMBDA + 0.5);
486   maruenc->context->lmax = (maruenc->lmax * FF_QP2LAMBDA + 0.5);
487
488   // some other defaults
489   maruenc->context->b_frame_strategy = 0;
490   maruenc->context->coder_type = 0;
491   maruenc->context->context_model = 0;
492   maruenc->context->scenechange_threshold = 0;
493   maruenc->context->inter_threshold = 0;
494
495   if (maruenc->interlaced) {
496     maruenc->context->flags |=
497       CODEC_FLAG_INTERLACED_DCT | CODEC_FLAG_INTERLACED_ME;
498     maruenc->picture->interlaced_frame = TRUE;
499
500     maruenc->picture->top_field_first = TRUE;
501   }
502 #endif
503
504   gst_maru_caps_with_codectype (oclass->codec->media_type, caps, maruenc->context);
505
506   if (!maruenc->context->video.fps_d) {
507     maruenc->context->video.fps_d = 25;
508     maruenc->context->video.fps_n = 1;
509   } else if (!strcmp(oclass->codec->name ,"mpeg4")
510       && (maruenc->context->video.fps_d > 65535)) {
511       maruenc->context->video.fps_n =
512         (gint) gst_util_uint64_scale_int (maruenc->context->video.fps_n,
513             65535, maruenc->context->video.fps_d);
514       maruenc->context->video.fps_d = 65535;
515       GST_LOG_OBJECT (maruenc, "MPEG4 : scaled down framerate to %d / %d",
516           maruenc->context->video.fps_d, maruenc->context->video.fps_n);
517   }
518
519   pix_fmt = maruenc->context->video.pix_fmt;
520
521   // open codec
522   if (gst_maru_avcodec_open (maruenc->context,
523       oclass->codec, maruenc->dev) < 0) {
524     GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to open codec",
525         oclass->codec->name);
526     return FALSE;
527   }
528
529   if (pix_fmt != maruenc->context->video.pix_fmt) {
530     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
531     GST_DEBUG_OBJECT (maruenc,
532       "maru_%senc: AV wants different colorspace (%d given, %d wanted)",
533       oclass->codec->name, pix_fmt, maruenc->context->video.pix_fmt);
534     return FALSE;
535   }
536
537   if (oclass->codec->media_type == AVMEDIA_TYPE_VIDEO
538     && pix_fmt == PIX_FMT_NONE) {
539     GST_DEBUG_OBJECT (maruenc, "maru_%senc: Failed to determine input format",
540       oclass->codec->name);
541     return FALSE;
542   }
543
544   GST_DEBUG_OBJECT (maruenc, "picking an output format.");
545   allowed_caps = gst_pad_get_allowed_caps (maruenc->srcpad);
546   if (!allowed_caps) {
547     GST_DEBUG_OBJECT (maruenc, "but no peer, using template caps");
548     allowed_caps =
549       gst_caps_copy (gst_pad_get_pad_template_caps (maruenc->srcpad));
550   }
551
552   GST_DEBUG_OBJECT (maruenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
553   gst_maru_caps_with_codecname (oclass->codec->name,
554     oclass->codec->media_type, allowed_caps, maruenc->context);
555
556   other_caps =
557   gst_maru_codecname_to_caps (oclass->codec->name, maruenc->context, TRUE);
558   if (!other_caps) {
559     GST_DEBUG ("Unsupported codec - no caps found");
560     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
561     return FALSE;
562   }
563
564   icaps = gst_caps_intersect (allowed_caps, other_caps);
565   gst_caps_unref (allowed_caps);
566   gst_caps_unref (other_caps);
567   if (gst_caps_is_empty (icaps)) {
568     gst_caps_unref (icaps);
569     return FALSE;
570   }
571
572   if (gst_caps_get_size (icaps) > 1) {
573     GstCaps *newcaps;
574
575     newcaps =
576       gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
577               0)), NULL);
578     gst_caps_unref (icaps);
579     icaps = newcaps;
580   }
581
582   if (!gst_pad_set_caps (maruenc->srcpad, icaps)) {
583     gst_maru_avcodec_close (maruenc->context, maruenc->dev);
584     gst_caps_unref (icaps);
585     return FALSE;
586   }
587   gst_object_unref (maruenc);
588
589   maruenc->opened = TRUE;
590
591   return TRUE;
592 }
593
594 static void
595 gst_maruenc_setup_working_buf (GstMaruEnc *maruenc)
596 {
597   guint wanted_size =
598       maruenc->context->video.width * maruenc->context->video.height * 6 +
599       FF_MIN_BUFFER_SIZE;
600
601   if (maruenc->working_buf == NULL ||
602     maruenc->working_buf_size != wanted_size) {
603     if (maruenc->working_buf) {
604       g_free (maruenc->working_buf);
605     }
606     maruenc->working_buf_size = wanted_size;
607     maruenc->working_buf = g_malloc0 (maruenc->working_buf_size);
608   }
609   maruenc->buffer_size = wanted_size;
610 }
611
612 GstFlowReturn
613 gst_maruenc_chain_video (GstPad *pad, GstBuffer *buffer)
614 {
615   GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
616   GstBuffer *outbuf = NULL;
617   gint ret_size = 0, frame_size = 0;
618   int coded_frame = 0, is_keyframe = 0;
619   uint32_t mem_offset = 0;
620
621   GST_DEBUG_OBJECT (maruenc,
622       "Received buffer of time %" GST_TIME_FORMAT,
623       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
624
625 #if 0
626   GST_OBJECT_LOCK (maruenc);
627   force_keyframe = maruenc->force_keyframe;
628   maruenc->force_keyframe = FALSE;
629   GST_OBJECT_UNLOCK (maruenc);
630
631   if (force_keyframe) {
632     maruenc->picture->pict_type = FF_I_TYPE;
633   }
634 #endif
635
636   frame_size = gst_maru_avpicture_size (maruenc->context->video.pix_fmt,
637       maruenc->context->video.width, maruenc->context->video.height);
638   g_return_val_if_fail (frame_size == GST_BUFFER_SIZE (buffer),
639       GST_FLOW_ERROR);
640
641 #if 0
642   pts = gst_maru_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buffer) /
643     maruenc->context.video.ticks_per_frame,
644     maruenc->context.video.fps_n, maruen->context.video.fps_d);
645 #endif
646
647   gst_maruenc_setup_working_buf (maruenc);
648
649   ret_size =
650     interface->encode_video (maruenc->context, maruenc->working_buf,
651                 maruenc->working_buf_size, GST_BUFFER_DATA (buffer),
652                 GST_BUFFER_SIZE (buffer), GST_BUFFER_TIMESTAMP (buffer),
653                 &coded_frame, &is_keyframe,
654                 maruenc->dev);
655
656   if (ret_size < 0) {
657     GstMaruEncClass *oclass =
658       (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
659     GST_ERROR_OBJECT (maruenc,
660         "maru_%senc: failed to encode buffer", oclass->codec->name);
661     gst_buffer_unref (buffer);
662     return GST_FLOW_OK;
663   }
664
665   g_queue_push_tail (maruenc->delay, buffer);
666   if (ret_size) {
667     buffer = g_queue_pop_head (maruenc->delay);
668   } else {
669     return GST_FLOW_OK;
670   }
671
672 #if 0
673   if (maruenc->file && maruenc->context->stats_out) {
674     if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
675       GST_ELEMENT_ERROR (maruenc, RESOURCE, WRITE,
676         (("Could not write to file \"%s\"."), maruenc->filename),
677         GST_ERROR_SYSTEM);
678     }
679   }
680 #endif
681
682   // mem_offset = maruenc->dev->mem_info.offset;
683   // working_buf = maruenc->dev->buf + mem_offset;
684
685   GST_DEBUG_OBJECT (maruenc, "encoded video. mem_offset = 0x%x",  mem_offset);
686
687   outbuf = gst_buffer_new_and_alloc (ret_size);
688   memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
689   // memcpy (GST_BUFFER_DATA (outbuf), working_buf, ret_size);
690   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
691   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
692
693 #if 0
694   ret = ioctl(maruenc->dev->fd, CODEC_CMD_RELEASE_BUFFER, &mem_offset);
695   if (ret < 0) {
696     GST_ERROR_OBJECT (maruenc, "failed to release used buffer");
697   }
698 #endif
699
700   if (coded_frame) {
701     if (!is_keyframe) {
702       GST_DEBUG_OBJECT (maruenc, "this frame is not a keyframe");
703
704       /* GST_BUFFER_FLAG_DELTA_UNIT
705        * - this unit cannot be decoded independently.
706        */
707       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
708     }
709   } else {
710     GST_WARNING_OBJECT (maruenc, "codec did not provide keyframe info");
711   }
712   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
713
714   gst_buffer_unref (buffer);
715
716 #if 0
717   if (force_keyframe) {
718     gst_pad_push_event (maruenc->srcpad,
719       gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
720       gst_structure_new ("GstForceKeyUnit", "timestamp",
721                          G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), NULL)));
722   }
723 #endif
724
725   return gst_pad_push (maruenc->srcpad, outbuf);
726 }
727
728 GstFlowReturn
729 gst_maruenc_encode_audio (GstMaruEnc *maruenc, guint8 *audio_in,
730   guint in_size, guint max_size, GstClockTime timestamp,
731   GstClockTime duration, gboolean discont)
732 {
733   GstBuffer *outbuf;
734   guint8 *audio_out;
735   gint res;
736   GstFlowReturn ret;
737
738   outbuf = gst_buffer_new_and_alloc (max_size + FF_MIN_BUFFER_SIZE);
739   audio_out = GST_BUFFER_DATA (outbuf);
740
741   GST_LOG_OBJECT (maruenc, "encoding buffer of max size %d", max_size);
742   if (maruenc->buffer_size != max_size) {
743     maruenc->buffer_size = max_size;
744   }
745
746   res = interface->encode_audio (maruenc->context, audio_out, max_size,
747                             audio_in, in_size, timestamp, maruenc->dev);
748
749   if (res < 0) {
750     GST_ERROR_OBJECT (maruenc, "Failed to encode buffer: %d", res);
751     gst_buffer_unref (outbuf);
752     return GST_FLOW_OK;
753   }
754   GST_LOG_OBJECT (maruenc, "got output size %d", res);
755
756   GST_BUFFER_SIZE (outbuf) = res;
757   GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
758   GST_BUFFER_DURATION (outbuf) = duration;
759   if (discont) {
760     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
761   }
762   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
763
764   GST_LOG_OBJECT (maruenc, "pushing size %d, timestamp %",
765       GST_TIME_FORMAT, res, GST_TIME_ARGS (timestamp));
766
767   ret = gst_pad_push (maruenc->srcpad, outbuf);
768
769   return ret;
770 }
771
772 static GstFlowReturn
773 gst_maruenc_chain_audio (GstPad *pad, GstBuffer *buffer)
774 {
775   GstMaruEnc *maruenc;
776   // GstMaruEncClass *oclass;
777   GstClockTime timestamp, duration;
778   guint in_size, frame_size;
779   gint osize;
780   GstFlowReturn ret;
781   gint out_size = 0;
782   gboolean discont;
783   guint8 *in_data;
784   CodecContext *ctx;
785
786   maruenc = (GstMaruEnc *) (GST_OBJECT_PARENT (pad));
787   // oclass = (GstMaruEncClass *) G_OBJECT_GET_CLASS (maruenc);
788
789   ctx = maruenc->context;
790
791   in_size = GST_BUFFER_SIZE (buffer);
792   timestamp = GST_BUFFER_TIMESTAMP (buffer);
793   duration = GST_BUFFER_DURATION (buffer);
794   discont = GST_BUFFER_IS_DISCONT (buffer);
795
796   GST_DEBUG_OBJECT (maruenc,
797     "Received time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
798     ", size %d", GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration), in_size);
799
800   frame_size = ctx->audio.frame_size;
801   osize = ctx->audio.bits_per_sample_fmt;
802
803   if (frame_size > 1) {
804     guint avail, frame_bytes;
805
806     if (discont) {
807       GST_LOG_OBJECT (maruenc, "DISCONT, clear adapter");
808       gst_adapter_clear (maruenc->adapter);
809       maruenc->discont = TRUE;
810     }
811
812     if (gst_adapter_available (maruenc->adapter) == 0) {
813       GST_LOG_OBJECT (maruenc, "taking buffer timestamp %" GST_TIME_FORMAT,
814         GST_TIME_ARGS (timestamp));
815       maruenc->adapter_ts = timestamp;
816       maruenc->adapter_consumed = 0;
817     } else {
818       GstClockTime upstream_time;
819       GstClockTime consumed_time;
820       guint64 bytes;
821
822       consumed_time =
823         gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
824             ctx->audio.sample_rate);
825       timestamp = maruenc->adapter_ts + consumed_time;
826       GST_LOG_OBJECT (maruenc, "taking adapter timestamp %" GST_TIME_FORMAT
827         " and adding consumed time %" GST_TIME_FORMAT,
828         GST_TIME_ARGS (maruenc->adapter_ts), GST_TIME_ARGS (consumed_time));
829
830       upstream_time = gst_adapter_prev_timestamp (maruenc->adapter, &bytes);
831       if (GST_CLOCK_TIME_IS_VALID (upstream_time)) {
832         GstClockTimeDiff diff;
833
834         upstream_time +=
835           gst_util_uint64_scale (bytes, GST_SECOND,
836             ctx->audio.sample_rate * osize * ctx->audio.channels);
837         diff = upstream_time - timestamp;
838
839         if (diff > GST_SECOND / 10 || diff < -GST_SECOND / 10) {
840           GST_DEBUG_OBJECT (maruenc, "adapter timestamp drifting, "
841             "taking upstream timestamp %" GST_TIME_FORMAT,
842             GST_TIME_ARGS (upstream_time));
843           timestamp = upstream_time;
844
845           maruenc->adapter_consumed = bytes / (osize * ctx->audio.channels);
846           maruenc->adapter_ts =
847             upstream_time - gst_util_uint64_scale (maruenc->adapter_consumed,
848                 GST_SECOND, ctx->audio.sample_rate);
849           maruenc->discont = TRUE;
850         }
851       }
852     }
853
854     GST_LOG_OBJECT (maruenc, "pushing buffer in adapter");
855     gst_adapter_push (maruenc->adapter, buffer);
856
857     frame_bytes = frame_size * osize * ctx->audio.channels;
858     avail = gst_adapter_available (maruenc->adapter);
859
860     GST_LOG_OBJECT (maruenc, "frame_bytes %u, avail %u", frame_bytes, avail);
861
862     while (avail >= frame_bytes) {
863       GST_LOG_OBJECT (maruenc, "taking %u bytes from the adapter", frame_bytes);
864
865       in_data = (guint8 *) gst_adapter_peek (maruenc->adapter, frame_bytes);
866       maruenc->adapter_consumed += frame_size;
867
868       duration =
869         gst_util_uint64_scale (maruenc->adapter_consumed, GST_SECOND,
870           ctx->audio.sample_rate);
871       duration -= (timestamp - maruenc->adapter_ts);
872
873       out_size = frame_bytes * 4;
874
875       ret =
876         gst_maruenc_encode_audio (maruenc, in_data, frame_bytes, out_size,
877           timestamp, duration, maruenc->discont);
878
879       gst_adapter_flush (maruenc->adapter, frame_bytes);
880       if (ret != GST_FLOW_OK) {
881         GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
882           gst_flow_get_name (ret));
883       }
884
885       timestamp += duration;
886
887       maruenc->discont = FALSE;
888       avail = gst_adapter_available (maruenc->adapter);
889     }
890     GST_LOG_OBJECT (maruenc, "%u bytes left in the adapter", avail);
891   } else {
892 #if 0
893     int coded_bps = av_get_bits_per_sample (oclass->codec->name);
894
895     GST_LOG_OBJECT (maruenc, "coded bps %d, osize %d", coded_bps, osize);
896
897     out_size = in_size / osize;
898     if (coded_bps) {
899       out_size = (out_size * coded_bps) / 8;
900     }
901 #endif
902     in_data = (guint8 *) GST_BUFFER_DATA (buffer);
903     ret = gst_maruenc_encode_audio (maruenc, in_data, in_size, out_size,
904       timestamp, duration, discont);
905     gst_buffer_unref (buffer);
906     if (ret != GST_FLOW_OK) {
907       GST_DEBUG_OBJECT (maruenc, "Failed to push buffer %d (%s)", ret,
908         gst_flow_get_name (ret));
909     }
910   }
911
912   return GST_FLOW_OK;
913 }
914
915 static void
916 gst_maruenc_flush_buffers (GstMaruEnc *maruenc, gboolean send)
917 {
918 #if 0
919   GstBuffer *outbuf, *inbuf;
920   gint ret_size = 0;
921 #endif
922
923   GST_DEBUG_OBJECT (maruenc, "flushing buffers with sending %d", send);
924
925   if (!maruenc->opened) {
926     while (!g_queue_is_empty (maruenc->delay)) {
927       gst_buffer_unref (g_queue_pop_head (maruenc->delay));
928     }
929   }
930
931 #if 0
932   while (!g_queue_is_empty (maruenc->delay)) {
933     maruenc_setup_working_buf (maruenc);
934
935     ret_size = codec_encode_video (maruenc->context,
936       maruenc->working_buf, maruenc->working_buf_size, NULL, NULL, 0,
937       maruenc->dev);
938
939     if (ret_size < 0) {
940       GstMaruEncClass *oclass =
941         (GstMaruEncClass *) (G_OBJECT_GET_CLASS (maruenc));
942       GST_WARNING_OBJECT (maruenc,
943         "maru_%senc: failed to flush buffer", oclass->codec->name);
944       break;
945     }
946
947     if (maruenc->file && maruenc->context->stats_out) {
948       if (fprintf (maruenc->file, "%s", maruenc->context->stats_out) < 0) {
949         GST_ELEMENT_ERROR (emeulenc, RESOURCE, WRITE,
950           (("Could not write to file \"%s\"."), maruenc->filename),
951           GST_ERROR_SYSTEM);
952       }
953     }
954
955     inbuf = g_queue_pop_head (maruenc->delay);
956
957     outbuf = gst_buffer_new_and_alloc (ret_size);
958     memcpy (GST_BUFFER_DATA (outbuf), maruenc->working_buf, ret_size);
959     GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
960     GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
961
962     if (!maruenc->context->coded_frame->key_frame) {
963       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
964     }
965     gst_buffer_set_caps (outbuf, GST_PAD_CAPS (maruenc->srcpad));
966
967     gst_buffer_unref (inbuf);
968
969     if (send) {
970       gst_pad_push (maruenc->srcpad, outbuf);
971     } else {
972       gst_buffer_unref (outbuf);
973     }
974   }
975
976   while (!g_queue_is_empty (maruenc->delay)) {
977     gst_buffer_unref (g_queue_pop_head (maruenc->delay));
978   }
979 #endif
980 }
981
982 static gboolean
983 gst_maruenc_event_video (GstPad *pad, GstEvent *event)
984 {
985   GstMaruEnc *maruenc;
986   maruenc = (GstMaruEnc *) gst_pad_get_parent (pad);
987
988   switch (GST_EVENT_TYPE (event)) {
989   case GST_EVENT_EOS:
990     gst_maruenc_flush_buffers (maruenc, TRUE);
991     break;
992   case GST_EVENT_CUSTOM_DOWNSTREAM:
993   {
994     const GstStructure *s;
995     s = gst_event_get_structure (event);
996
997     if (gst_structure_has_name (s, "GstForceKeyUnit")) {
998 #if 0
999       maruenc->picture->pict_type = FF_I_TYPE;
1000 #endif
1001     }
1002   }
1003     break;
1004   default:
1005     break;
1006   }
1007
1008   return gst_pad_push_event (maruenc->srcpad, event);
1009 }
1010
1011 static gboolean
1012 gst_maruenc_event_src (GstPad *pad, GstEvent *event)
1013 {
1014   GstMaruEnc *maruenc = (GstMaruEnc *) (GST_PAD_PARENT (pad));
1015   gboolean forward = TRUE;
1016
1017   switch (GST_EVENT_TYPE (event)) {
1018   case GST_EVENT_CUSTOM_UPSTREAM:
1019   {
1020     const GstStructure *s;
1021     s = gst_event_get_structure (event);
1022
1023     if (gst_structure_has_name (s, "GstForceKeyUnit")) {
1024 #if 0
1025       GST_OBJECT_LOCK (maruenc);
1026       maruenc->force_keyframe = TRUE;
1027       GST_OBJECT_UNLOCK (maruenc);
1028 #endif
1029       forward = FALSE;
1030       gst_event_unref (event);
1031     }
1032   }
1033     break;
1034   default:
1035     break;
1036   }
1037
1038   if (forward) {
1039     return gst_pad_push_event (maruenc->sinkpad, event);
1040   }
1041
1042   return TRUE;
1043 }
1044
1045 GstStateChangeReturn
1046 gst_maruenc_change_state (GstElement *element, GstStateChange transition)
1047 {
1048   GstMaruEnc *maruenc = (GstMaruEnc*)element;
1049   GstStateChangeReturn ret;
1050
1051   switch (transition) {
1052   default:
1053     break;
1054   }
1055
1056   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1057
1058   switch (transition) {
1059   case GST_STATE_CHANGE_PAUSED_TO_READY:
1060     gst_maruenc_flush_buffers (maruenc, FALSE);
1061     if (maruenc->opened) {
1062       GST_DEBUG_OBJECT (maruenc, "change_state: PAUSED_TO_READY, close context");
1063       gst_maru_avcodec_close (maruenc->context, maruenc->dev);
1064       maruenc->opened = FALSE;
1065     }
1066     gst_adapter_clear (maruenc->adapter);
1067
1068 #if 0
1069     if (maruenc->flie) {
1070       fclose (maruenc->file);
1071       maruenc->file = NULL;
1072     }
1073 #endif
1074
1075     if (maruenc->working_buf) {
1076       g_free (maruenc->working_buf);
1077       maruenc->working_buf = NULL;
1078     }
1079     break;
1080   default:
1081     break;
1082   }
1083
1084   return ret;
1085 }
1086
1087 gboolean
1088 gst_maruenc_register (GstPlugin *plugin, GList *element)
1089 {
1090   GTypeInfo typeinfo = {
1091       sizeof (GstMaruEncClass),
1092       (GBaseInitFunc) gst_maruenc_base_init,
1093       NULL,
1094       (GClassInitFunc) gst_maruenc_class_init,
1095       NULL,
1096       NULL,
1097       sizeof (GstMaruEnc),
1098       0,
1099       (GInstanceInitFunc) gst_maruenc_init,
1100   };
1101
1102   GType type;
1103   gchar *type_name;
1104   gint rank = GST_RANK_PRIMARY;
1105   GList *elem = element;
1106   CodecElement *codec = NULL;
1107
1108   if (!elem) {
1109     return FALSE;
1110   }
1111
1112   /* register element */
1113   do {
1114     codec = (CodecElement *)(elem->data);
1115     if (!codec) {
1116       return FALSE;
1117     }
1118
1119     if (codec->codec_type != CODEC_TYPE_ENCODE) {
1120       continue;
1121     }
1122
1123     type_name = g_strdup_printf ("maru_%senc", codec->name);
1124     type = g_type_from_name (type_name);
1125     if (!type) {
1126       type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1127       g_type_set_qdata (type, GST_MARUENC_PARAMS_QDATA, (gpointer) codec);
1128     }
1129
1130     if (!gst_element_register (plugin, type_name, rank, type)) {
1131       g_free (type_name);
1132       return FALSE;
1133     }
1134     g_free (type_name);
1135   } while ((elem = elem->next));
1136
1137   return TRUE;
1138 }