Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / ext / jpeg / gstsmokeenc.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 /**
20  * SECTION:element-smokeenc
21  *
22  * Encodes images in smoke format.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include <string.h>
29
30 #include "gstsmokeenc.h"
31 #include <gst/video/video.h>
32
33 GST_DEBUG_CATEGORY_STATIC (smokeenc_debug);
34 #define GST_CAT_DEFAULT smokeenc_debug
35
36
37 /* SmokeEnc signals and args */
38 enum
39 {
40   FRAME_ENCODED,
41   /* FILL ME */
42   LAST_SIGNAL
43 };
44
45 #define DEFAULT_PROP_MIN_QUALITY 10
46 #define DEFAULT_PROP_MAX_QUALITY 85
47 #define DEFAULT_PROP_THRESHOLD 3000
48 #define DEFAULT_PROP_KEYFRAME 20
49
50 enum
51 {
52   PROP_0,
53   PROP_MIN_QUALITY,
54   PROP_MAX_QUALITY,
55   PROP_THRESHOLD,
56   PROP_KEYFRAME
57       /* FILL ME */
58 };
59
60 static void gst_smokeenc_base_init (gpointer g_class);
61 static void gst_smokeenc_class_init (GstSmokeEnc * klass);
62 static void gst_smokeenc_init (GstSmokeEnc * smokeenc);
63 static void gst_smokeenc_finalize (GObject * object);
64
65 static GstStateChangeReturn
66 gst_smokeenc_change_state (GstElement * element, GstStateChange transition);
67
68 static GstFlowReturn gst_smokeenc_chain (GstPad * pad, GstBuffer * buf);
69 static GstCaps *gst_smokeenc_getcaps (GstPad * pad);
70 static gboolean gst_smokeenc_setcaps (GstPad * pad, GstCaps * caps);
71
72 static gboolean gst_smokeenc_resync (GstSmokeEnc * smokeenc);
73 static void gst_smokeenc_set_property (GObject * object, guint prop_id,
74     const GValue * value, GParamSpec * pspec);
75 static void gst_smokeenc_get_property (GObject * object, guint prop_id,
76     GValue * value, GParamSpec * pspec);
77
78 static GstElementClass *parent_class = NULL;
79
80 GType
81 gst_smokeenc_get_type (void)
82 {
83   static GType smokeenc_type = 0;
84
85   if (!smokeenc_type) {
86     static const GTypeInfo smokeenc_info = {
87       sizeof (GstSmokeEncClass),
88       (GBaseInitFunc) gst_smokeenc_base_init,
89       NULL,
90       (GClassInitFunc) gst_smokeenc_class_init,
91       NULL,
92       NULL,
93       sizeof (GstSmokeEnc),
94       0,
95       (GInstanceInitFunc) gst_smokeenc_init,
96     };
97
98     smokeenc_type =
99         g_type_register_static (GST_TYPE_ELEMENT, "GstSmokeEnc", &smokeenc_info,
100         0);
101   }
102   return smokeenc_type;
103 }
104
105 static GstStaticPadTemplate gst_smokeenc_sink_pad_template =
106 GST_STATIC_PAD_TEMPLATE ("sink",
107     GST_PAD_SINK,
108     GST_PAD_ALWAYS,
109     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
110     );
111
112 static GstStaticPadTemplate gst_smokeenc_src_pad_template =
113 GST_STATIC_PAD_TEMPLATE ("src",
114     GST_PAD_SRC,
115     GST_PAD_ALWAYS,
116     GST_STATIC_CAPS ("video/x-smoke, "
117         "width = (int) [ 16, 4096 ], "
118         "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0/1, MAX ]")
119     );
120
121 static void
122 gst_smokeenc_base_init (gpointer g_class)
123 {
124   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
125
126   gst_element_class_add_static_pad_template (element_class,
127       &gst_smokeenc_sink_pad_template);
128   gst_element_class_add_static_pad_template (element_class,
129       &gst_smokeenc_src_pad_template);
130   gst_element_class_set_details_simple (element_class, "Smoke video encoder",
131       "Codec/Encoder/Video",
132       "Encode images into the Smoke format", "Wim Taymans <wim@fluendo.com>");
133 }
134
135 static void
136 gst_smokeenc_class_init (GstSmokeEnc * klass)
137 {
138   GObjectClass *gobject_class;
139   GstElementClass *gstelement_class;
140
141   gobject_class = (GObjectClass *) klass;
142   gstelement_class = (GstElementClass *) klass;
143
144   parent_class = g_type_class_peek_parent (klass);
145
146   gobject_class->finalize = gst_smokeenc_finalize;
147   gobject_class->set_property = gst_smokeenc_set_property;
148   gobject_class->get_property = gst_smokeenc_get_property;
149
150   g_object_class_install_property (gobject_class, PROP_MIN_QUALITY,
151       g_param_spec_int ("qmin", "Qmin", "Minimum quality",
152           0, 100, DEFAULT_PROP_MIN_QUALITY,
153           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
154   g_object_class_install_property (gobject_class, PROP_MAX_QUALITY,
155       g_param_spec_int ("qmax", "Qmax", "Maximum quality",
156           0, 100, DEFAULT_PROP_MAX_QUALITY,
157           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
158   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
159       g_param_spec_int ("threshold", "Threshold", "Motion estimation threshold",
160           0, 100000000, DEFAULT_PROP_THRESHOLD,
161           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
162   g_object_class_install_property (gobject_class, PROP_KEYFRAME,
163       g_param_spec_int ("keyframe", "Keyframe",
164           "Insert keyframe every N frames", 1, 100000,
165           DEFAULT_PROP_KEYFRAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
166
167   gstelement_class->change_state =
168       GST_DEBUG_FUNCPTR (gst_smokeenc_change_state);
169
170   GST_DEBUG_CATEGORY_INIT (smokeenc_debug, "smokeenc", 0,
171       "Smoke encoding element");
172 }
173
174 static void
175 gst_smokeenc_init (GstSmokeEnc * smokeenc)
176 {
177   /* create the sink and src pads */
178   smokeenc->sinkpad =
179       gst_pad_new_from_static_template (&gst_smokeenc_sink_pad_template,
180       "sink");
181   gst_pad_set_chain_function (smokeenc->sinkpad, gst_smokeenc_chain);
182   gst_pad_set_getcaps_function (smokeenc->sinkpad, gst_smokeenc_getcaps);
183   gst_pad_set_setcaps_function (smokeenc->sinkpad, gst_smokeenc_setcaps);
184   gst_element_add_pad (GST_ELEMENT (smokeenc), smokeenc->sinkpad);
185
186   smokeenc->srcpad =
187       gst_pad_new_from_static_template (&gst_smokeenc_src_pad_template, "src");
188   gst_pad_set_getcaps_function (smokeenc->srcpad, gst_smokeenc_getcaps);
189   gst_pad_use_fixed_caps (smokeenc->srcpad);
190   gst_element_add_pad (GST_ELEMENT (smokeenc), smokeenc->srcpad);
191
192   smokeenc->min_quality = DEFAULT_PROP_MIN_QUALITY;
193   smokeenc->max_quality = DEFAULT_PROP_MAX_QUALITY;
194   smokeenc->threshold = DEFAULT_PROP_THRESHOLD;
195   smokeenc->keyframe = DEFAULT_PROP_KEYFRAME;
196 }
197
198 static void
199 gst_smokeenc_finalize (GObject * object)
200 {
201   GstSmokeEnc *enc = GST_SMOKEENC (object);
202
203   if (enc->info)
204     smokecodec_info_free (enc->info);
205
206   G_OBJECT_CLASS (parent_class)->finalize (object);
207 }
208
209 static GstCaps *
210 gst_smokeenc_getcaps (GstPad * pad)
211 {
212   GstSmokeEnc *smokeenc = GST_SMOKEENC (gst_pad_get_parent (pad));
213   GstPad *otherpad;
214   GstCaps *result, *caps;
215   const GstCaps *tcaps;
216   const char *name;
217   int i;
218   GstStructure *structure = NULL;
219
220   /* we want to proxy properties like width, height and framerate from the
221      other end of the element */
222   otherpad = (pad == smokeenc->srcpad) ? smokeenc->sinkpad : smokeenc->srcpad;
223
224   /* get template caps, we always need this to fiter the peer caps */
225   tcaps = gst_pad_get_pad_template_caps (otherpad);
226
227   /* get any constraints on the peer pad */
228   caps = gst_pad_peer_get_caps (otherpad);
229
230   if (caps == NULL)
231     caps = gst_caps_copy (tcaps);
232   else
233     caps = gst_caps_make_writable (caps);
234
235   /* intersect with the template */
236   result = gst_caps_intersect (caps, tcaps);
237   gst_caps_unref (caps);
238
239   if (pad == smokeenc->srcpad) {
240     name = "video/x-smoke";
241   } else {
242     name = "video/x-raw-yuv";
243   }
244
245   /* we can only copy width, height, framerate from one side to the other */
246   for (i = 0; i < gst_caps_get_size (result); i++) {
247     structure = gst_caps_get_structure (result, i);
248
249     gst_structure_set_name (structure, name);
250     gst_structure_remove_field (structure, "format");
251     /* ... but for the sink pad, we only do I420 anyway, so add that */
252     if (pad == smokeenc->sinkpad) {
253       gst_structure_set (structure, "format", GST_TYPE_FOURCC,
254           GST_STR_FOURCC ("I420"), NULL);
255     }
256   }
257
258   gst_object_unref (smokeenc);
259
260   return result;
261 }
262
263 static gboolean
264 gst_smokeenc_setcaps (GstPad * pad, GstCaps * caps)
265 {
266   GstSmokeEnc *smokeenc;
267   GstStructure *structure;
268   const GValue *framerate;
269   gboolean ret;
270   GstCaps *srccaps;
271
272   smokeenc = GST_SMOKEENC (gst_pad_get_parent (pad));
273
274   structure = gst_caps_get_structure (caps, 0);
275   framerate = gst_structure_get_value (structure, "framerate");
276   if (framerate) {
277     smokeenc->fps_num = gst_value_get_fraction_numerator (framerate);
278     smokeenc->fps_denom = gst_value_get_fraction_denominator (framerate);
279   } else {
280     smokeenc->fps_num = 0;
281     smokeenc->fps_denom = 1;
282   }
283
284   gst_structure_get_int (structure, "width", &smokeenc->width);
285   gst_structure_get_int (structure, "height", &smokeenc->height);
286
287   if ((smokeenc->width & 0x0f) != 0 || (smokeenc->height & 0x0f) != 0)
288     goto width_or_height_notx16;
289
290   if (!gst_smokeenc_resync (smokeenc))
291     goto init_failed;
292
293   srccaps = gst_caps_new_simple ("video/x-smoke",
294       "width", G_TYPE_INT, smokeenc->width,
295       "height", G_TYPE_INT, smokeenc->height,
296       "framerate", GST_TYPE_FRACTION, smokeenc->fps_num, smokeenc->fps_denom,
297       NULL);
298
299   ret = gst_pad_set_caps (smokeenc->srcpad, srccaps);
300   gst_caps_unref (srccaps);
301
302   gst_object_unref (smokeenc);
303
304   return ret;
305
306 width_or_height_notx16:
307   {
308     GST_WARNING_OBJECT (smokeenc, "width and height must be multiples of 16"
309         ", %dx%d not allowed", smokeenc->width, smokeenc->height);
310     gst_object_unref (smokeenc);
311     return FALSE;
312   }
313 init_failed:
314   {
315     GST_WARNING_OBJECT (smokeenc, "could not init decoder");
316     gst_object_unref (smokeenc);
317     return FALSE;
318   }
319 }
320
321 static gboolean
322 gst_smokeenc_resync (GstSmokeEnc * smokeenc)
323 {
324   int ret;
325
326   GST_DEBUG ("resync: %dx%d@%d/%dfps", smokeenc->width, smokeenc->height,
327       smokeenc->fps_num, smokeenc->fps_denom);
328
329   if (smokeenc->info)
330     smokecodec_info_free (smokeenc->info);
331
332   ret = smokecodec_encode_new (&smokeenc->info, smokeenc->width,
333       smokeenc->height, smokeenc->fps_num, smokeenc->fps_denom);
334
335   if (ret != SMOKECODEC_OK)
336     goto init_failed;
337
338   smokecodec_set_quality (smokeenc->info, smokeenc->min_quality,
339       smokeenc->max_quality);
340
341   GST_DEBUG ("resync done");
342   return TRUE;
343
344   /* ERRORS */
345 init_failed:
346   {
347     GST_WARNING_OBJECT (smokeenc, "smokecodec_encode_new() failed: %d", ret);
348     return FALSE;
349   }
350 }
351
352 static GstFlowReturn
353 gst_smokeenc_chain (GstPad * pad, GstBuffer * buf)
354 {
355   GstSmokeEnc *smokeenc;
356   guchar *data, *outdata;
357   gulong size;
358   gint outsize;
359   guint encsize;
360   GstBuffer *outbuf;
361   SmokeCodecFlags flags;
362   GstFlowReturn ret;
363
364   smokeenc = GST_SMOKEENC (GST_OBJECT_PARENT (pad));
365
366   data = GST_BUFFER_DATA (buf);
367   size = GST_BUFFER_SIZE (buf);
368
369   GST_LOG_OBJECT (smokeenc, "got buffer of %lu bytes", size);
370
371   if (smokeenc->need_header) {
372     outbuf = gst_buffer_new_and_alloc (256);
373     outdata = GST_BUFFER_DATA (outbuf);
374
375     GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
376     GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
377
378     smokecodec_encode_id (smokeenc->info, outdata, &encsize);
379
380     GST_BUFFER_SIZE (outbuf) = encsize;
381     gst_buffer_set_caps (outbuf, GST_PAD_CAPS (smokeenc->srcpad));
382
383     ret = gst_pad_push (smokeenc->srcpad, outbuf);
384     if (ret != GST_FLOW_OK)
385       goto done;
386
387     smokeenc->need_header = FALSE;
388   }
389
390   encsize = outsize = smokeenc->width * smokeenc->height * 3;
391   outbuf = gst_buffer_new_and_alloc (outsize);
392   outdata = GST_BUFFER_DATA (outbuf);
393
394   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
395   GST_BUFFER_DURATION (outbuf) =
396       gst_util_uint64_scale_int (GST_SECOND, smokeenc->fps_denom,
397       smokeenc->fps_num);
398   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (smokeenc->srcpad));
399
400   flags = 0;
401   if ((smokeenc->frame % smokeenc->keyframe) == 0) {
402     flags |= SMOKECODEC_KEYFRAME;
403   }
404   smokecodec_set_quality (smokeenc->info, smokeenc->min_quality,
405       smokeenc->max_quality);
406   smokecodec_set_threshold (smokeenc->info, smokeenc->threshold);
407   smokecodec_encode (smokeenc->info, data, flags, outdata, &encsize);
408   gst_buffer_unref (buf);
409
410   GST_BUFFER_SIZE (outbuf) = encsize;
411   GST_BUFFER_OFFSET (outbuf) = smokeenc->frame;
412   GST_BUFFER_OFFSET_END (outbuf) = smokeenc->frame + 1;
413
414   ret = gst_pad_push (smokeenc->srcpad, outbuf);
415
416   smokeenc->frame++;
417
418 done:
419
420   return ret;
421 }
422
423 static void
424 gst_smokeenc_set_property (GObject * object, guint prop_id,
425     const GValue * value, GParamSpec * pspec)
426 {
427   GstSmokeEnc *smokeenc;
428
429   g_return_if_fail (GST_IS_SMOKEENC (object));
430   smokeenc = GST_SMOKEENC (object);
431
432   switch (prop_id) {
433     case PROP_MIN_QUALITY:
434       smokeenc->min_quality = g_value_get_int (value);
435       break;
436     case PROP_MAX_QUALITY:
437       smokeenc->max_quality = g_value_get_int (value);
438       break;
439     case PROP_THRESHOLD:
440       smokeenc->threshold = g_value_get_int (value);
441       break;
442     case PROP_KEYFRAME:
443       smokeenc->keyframe = g_value_get_int (value);
444       break;
445     default:
446       break;
447   }
448 }
449
450 static void
451 gst_smokeenc_get_property (GObject * object, guint prop_id, GValue * value,
452     GParamSpec * pspec)
453 {
454   GstSmokeEnc *smokeenc;
455
456   g_return_if_fail (GST_IS_SMOKEENC (object));
457   smokeenc = GST_SMOKEENC (object);
458
459   switch (prop_id) {
460     case PROP_MIN_QUALITY:
461       g_value_set_int (value, smokeenc->min_quality);
462       break;
463     case PROP_MAX_QUALITY:
464       g_value_set_int (value, smokeenc->max_quality);
465       break;
466     case PROP_THRESHOLD:
467       g_value_set_int (value, smokeenc->threshold);
468       break;
469     case PROP_KEYFRAME:
470       g_value_set_int (value, smokeenc->keyframe);
471       break;
472     default:
473       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
474       break;
475   }
476 }
477
478 static GstStateChangeReturn
479 gst_smokeenc_change_state (GstElement * element, GstStateChange transition)
480 {
481   GstStateChangeReturn ret;
482   GstSmokeEnc *enc;
483
484   enc = GST_SMOKEENC (element);
485
486   switch (transition) {
487     case GST_STATE_CHANGE_READY_TO_PAUSED:
488       /* reset the initial video state */
489       enc->width = 0;
490       enc->height = 0;
491       enc->frame = 0;
492       enc->need_header = TRUE;
493     default:
494       break;
495   }
496
497   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
498   if (ret != GST_STATE_CHANGE_SUCCESS)
499     return ret;
500
501   switch (transition) {
502     case GST_STATE_CHANGE_PAUSED_TO_READY:
503       break;
504     default:
505       break;
506   }
507
508   return ret;
509 }