close #333784 unref the result of gst_pad_get_parent() by: Christophe Fergeau.
[platform/upstream/gstreamer.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
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include <string.h>
25
26 #include "gstsmokeenc.h"
27 #include <gst/video/video.h>
28
29 /* elementfactory information */
30 GstElementDetails gst_smokeenc_details = {
31   "Smoke image encoder",
32   "Codec/Encoder/Image",
33   "Encode images in the Smoke format",
34   "Wim Taymans <wim@fluendo.com>",
35 };
36
37 GST_DEBUG_CATEGORY (smokeenc_debug);
38 #define GST_CAT_DEFAULT smokeenc_debug
39
40 #define SMOKEENC_DEFAULT_MIN_QUALITY 10
41 #define SMOKEENC_DEFAULT_MAX_QUALITY 85
42 #define SMOKEENC_DEFAULT_THRESHOLD 3000
43 #define SMOKEENC_DEFAULT_KEYFRAME 20
44
45 /* SmokeEnc signals and args */
46 enum
47 {
48   FRAME_ENCODED,
49   /* FILL ME */
50   LAST_SIGNAL
51 };
52
53 enum
54 {
55   ARG_0,
56   ARG_MIN_QUALITY,
57   ARG_MAX_QUALITY,
58   ARG_THRESHOLD,
59   ARG_KEYFRAME
60       /* FILL ME */
61 };
62
63 static void gst_smokeenc_base_init (gpointer g_class);
64 static void gst_smokeenc_class_init (GstSmokeEnc * klass);
65 static void gst_smokeenc_init (GstSmokeEnc * smokeenc);
66
67 static GstFlowReturn gst_smokeenc_chain (GstPad * pad, GstBuffer * buf);
68 static gboolean gst_smokeenc_setcaps (GstPad * pad, GstCaps * caps);
69 static GstCaps *gst_smokeenc_getcaps (GstPad * pad);
70
71 static void gst_smokeenc_resync (GstSmokeEnc * smokeenc);
72 static void gst_smokeenc_set_property (GObject * object, guint prop_id,
73     const GValue * value, GParamSpec * pspec);
74 static void gst_smokeenc_get_property (GObject * object, guint prop_id,
75     GValue * value, GParamSpec * pspec);
76
77 static GstElementClass *parent_class = NULL;
78
79 //static guint gst_smokeenc_signals[LAST_SIGNAL] = { 0 };
80
81 GType
82 gst_smokeenc_get_type (void)
83 {
84   static GType smokeenc_type = 0;
85
86   if (!smokeenc_type) {
87     static const GTypeInfo smokeenc_info = {
88       sizeof (GstSmokeEncClass),
89       (GBaseInitFunc) gst_smokeenc_base_init,
90       NULL,
91       (GClassInitFunc) gst_smokeenc_class_init,
92       NULL,
93       NULL,
94       sizeof (GstSmokeEnc),
95       0,
96       (GInstanceInitFunc) gst_smokeenc_init,
97     };
98
99     smokeenc_type =
100         g_type_register_static (GST_TYPE_ELEMENT, "GstSmokeEnc", &smokeenc_info,
101         0);
102   }
103   return smokeenc_type;
104 }
105
106 static GstStaticPadTemplate gst_smokeenc_sink_pad_template =
107 GST_STATIC_PAD_TEMPLATE ("sink",
108     GST_PAD_SINK,
109     GST_PAD_ALWAYS,
110     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
111     );
112
113 static GstStaticPadTemplate gst_smokeenc_src_pad_template =
114 GST_STATIC_PAD_TEMPLATE ("src",
115     GST_PAD_SRC,
116     GST_PAD_ALWAYS,
117     GST_STATIC_CAPS ("video/x-smoke, "
118         "width = (int) [ 16, 4096 ], "
119         "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0/1, MAX ]")
120     );
121
122 static void
123 gst_smokeenc_base_init (gpointer g_class)
124 {
125   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
126
127   gst_element_class_add_pad_template (element_class,
128       gst_static_pad_template_get (&gst_smokeenc_sink_pad_template));
129   gst_element_class_add_pad_template (element_class,
130       gst_static_pad_template_get (&gst_smokeenc_src_pad_template));
131   gst_element_class_set_details (element_class, &gst_smokeenc_details);
132 }
133
134 static void
135 gst_smokeenc_class_init (GstSmokeEnc * klass)
136 {
137   GObjectClass *gobject_class;
138   GstElementClass *gstelement_class;
139
140   gobject_class = (GObjectClass *) klass;
141   gstelement_class = (GstElementClass *) klass;
142
143   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
144
145   gobject_class->set_property = gst_smokeenc_set_property;
146   gobject_class->get_property = gst_smokeenc_get_property;
147
148   g_object_class_install_property (gobject_class, ARG_MIN_QUALITY,
149       g_param_spec_int ("qmin", "Qmin", "Minimum quality",
150           0, 100, SMOKEENC_DEFAULT_MIN_QUALITY, G_PARAM_READWRITE));
151   g_object_class_install_property (gobject_class, ARG_MAX_QUALITY,
152       g_param_spec_int ("qmax", "Qmax", "Maximum quality",
153           0, 100, SMOKEENC_DEFAULT_MAX_QUALITY, G_PARAM_READWRITE));
154   g_object_class_install_property (gobject_class, ARG_THRESHOLD,
155       g_param_spec_int ("threshold", "Threshold", "Motion estimation threshold",
156           0, 100000000, SMOKEENC_DEFAULT_THRESHOLD, G_PARAM_READWRITE));
157   g_object_class_install_property (gobject_class, ARG_KEYFRAME,
158       g_param_spec_int ("keyframe", "Keyframe",
159           "Insert keyframe every N frames", 1, 100000,
160           SMOKEENC_DEFAULT_KEYFRAME, G_PARAM_READWRITE));
161
162   GST_DEBUG_CATEGORY_INIT (smokeenc_debug, "smokeenc", 0,
163       "Smoke encoding element");
164 }
165
166 static void
167 gst_smokeenc_init (GstSmokeEnc * smokeenc)
168 {
169   /* create the sink and src pads */
170   smokeenc->sinkpad =
171       gst_pad_new_from_template (gst_static_pad_template_get
172       (&gst_smokeenc_sink_pad_template), "sink");
173   gst_pad_set_chain_function (smokeenc->sinkpad, gst_smokeenc_chain);
174   gst_pad_set_getcaps_function (smokeenc->sinkpad, gst_smokeenc_getcaps);
175   gst_pad_set_setcaps_function (smokeenc->sinkpad, gst_smokeenc_setcaps);
176   gst_element_add_pad (GST_ELEMENT (smokeenc), smokeenc->sinkpad);
177
178   smokeenc->srcpad =
179       gst_pad_new_from_template (gst_static_pad_template_get
180       (&gst_smokeenc_src_pad_template), "src");
181   gst_pad_set_getcaps_function (smokeenc->sinkpad, gst_smokeenc_getcaps);
182   gst_pad_use_fixed_caps (smokeenc->sinkpad);
183   /*gst_pad_set_link_function (smokeenc->sinkpad, gst_smokeenc_link); */
184   gst_element_add_pad (GST_ELEMENT (smokeenc), smokeenc->srcpad);
185
186   /* reset the initial video state */
187   smokeenc->width = 0;
188   smokeenc->height = 0;
189   smokeenc->frame = 0;
190   smokeenc->need_header = TRUE;
191
192   gst_smokeenc_resync (smokeenc);
193
194   smokeenc->min_quality = SMOKEENC_DEFAULT_MIN_QUALITY;
195   smokeenc->max_quality = SMOKEENC_DEFAULT_MAX_QUALITY;
196   smokeenc->threshold = SMOKEENC_DEFAULT_THRESHOLD;
197   smokeenc->keyframe = SMOKEENC_DEFAULT_KEYFRAME;
198 }
199
200 static GstCaps *
201 gst_smokeenc_getcaps (GstPad * pad)
202 {
203   GstSmokeEnc *smokeenc = GST_SMOKEENC (gst_pad_get_parent (pad));
204   GstPad *otherpad;
205   GstCaps *caps;
206   const char *name;
207   int i;
208   GstStructure *structure = NULL;
209
210   /* we want to proxy properties like width, height and framerate from the
211      other end of the element */
212   otherpad = (pad == smokeenc->srcpad) ? smokeenc->sinkpad : smokeenc->srcpad;
213   caps = gst_pad_get_allowed_caps (otherpad);
214   if (pad == smokeenc->srcpad) {
215     name = "image/x-smoke";
216   } else {
217     name = "video/x-raw-yuv";
218   }
219   for (i = 0; i < gst_caps_get_size (caps); i++) {
220     structure = gst_caps_get_structure (caps, i);
221
222     gst_structure_set_name (structure, name);
223     gst_structure_remove_field (structure, "format");
224     /* ... but for the sink pad, we only do I420 anyway, so add that */
225     if (pad == smokeenc->sinkpad) {
226       gst_structure_set (structure, "format", GST_TYPE_FOURCC,
227           GST_STR_FOURCC ("I420"), NULL);
228     }
229   }
230
231   gst_object_unref (smokeenc);
232
233   return caps;
234 }
235
236 static gboolean
237 gst_smokeenc_setcaps (GstPad * pad, GstCaps * caps)
238 {
239   GstSmokeEnc *smokeenc = GST_SMOKEENC (gst_pad_get_parent (pad));
240   GstStructure *structure;
241   gboolean ret = TRUE;
242   GstCaps *othercaps;
243   GstPad *otherpad;
244   const GValue *framerate;
245
246   otherpad = (pad == smokeenc->srcpad) ? smokeenc->sinkpad : smokeenc->srcpad;
247
248   structure = gst_caps_get_structure (caps, 0);
249   framerate = gst_structure_get_value (structure, "framerate");
250   if (framerate) {
251     smokeenc->fps_num = gst_value_get_fraction_numerator (framerate);
252     smokeenc->fps_denom = gst_value_get_fraction_denominator (framerate);
253   } else {
254     smokeenc->fps_num = 0;
255     smokeenc->fps_denom = 1;
256   }
257
258   gst_structure_get_int (structure, "width", &smokeenc->width);
259   gst_structure_get_int (structure, "height", &smokeenc->height);
260
261   othercaps = gst_caps_copy (gst_pad_get_pad_template_caps (otherpad));
262   gst_caps_set_simple (othercaps,
263       "width", G_TYPE_INT, smokeenc->width,
264       "height", G_TYPE_INT, smokeenc->height,
265       "framerate", GST_TYPE_FRACTION, smokeenc->fps_num, smokeenc->fps_denom,
266       NULL);
267
268   ret = gst_pad_set_caps (smokeenc->srcpad, othercaps);
269   gst_caps_unref (othercaps);
270
271   if (GST_PAD_LINK_SUCCESSFUL (ret)) {
272     gst_smokeenc_resync (smokeenc);
273   }
274
275   gst_object_unref (smokeenc);
276
277   return ret;
278 }
279
280 static void
281 gst_smokeenc_resync (GstSmokeEnc * smokeenc)
282 {
283   GST_DEBUG ("gst_smokeenc_resync: resync");
284
285   smokecodec_encode_new (&smokeenc->info, smokeenc->width, smokeenc->height,
286       smokeenc->fps_num, smokeenc->fps_denom);
287   smokecodec_set_quality (smokeenc->info, smokeenc->min_quality,
288       smokeenc->max_quality);
289
290   GST_DEBUG ("gst_smokeenc_resync: resync done");
291 }
292
293 static GstFlowReturn
294 gst_smokeenc_chain (GstPad * pad, GstBuffer * buf)
295 {
296   GstSmokeEnc *smokeenc;
297   guchar *data, *outdata;
298   gulong size;
299   gint outsize;
300   guint encsize;
301   GstBuffer *outbuf;
302   SmokeCodecFlags flags;
303   GstFlowReturn ret;
304
305   smokeenc = GST_SMOKEENC (GST_OBJECT_PARENT (pad));
306
307   data = GST_BUFFER_DATA (buf);
308   size = GST_BUFFER_SIZE (buf);
309
310   GST_DEBUG ("gst_smokeenc_chain: got buffer of %ld bytes in '%s'", size,
311       GST_OBJECT_NAME (smokeenc));
312
313   if (smokeenc->need_header) {
314     outbuf = gst_buffer_new ();
315     outsize = 256;
316     outdata = g_malloc (outsize);
317     GST_BUFFER_DATA (outbuf) = outdata;
318     GST_BUFFER_MALLOCDATA (outbuf) = outdata;
319     GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
320     GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
321
322     smokecodec_encode_id (smokeenc->info, outdata, &encsize);
323
324     GST_BUFFER_SIZE (outbuf) = encsize;
325
326     ret = gst_pad_push (smokeenc->srcpad, outbuf);
327
328     smokeenc->need_header = FALSE;
329   }
330
331   outbuf = gst_buffer_new ();
332   outsize = smokeenc->width * smokeenc->height * 3;
333   outdata = g_malloc (outsize);
334   GST_BUFFER_DATA (outbuf) = outdata;
335   GST_BUFFER_MALLOCDATA (outbuf) = outdata;
336   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
337   GST_BUFFER_DURATION (outbuf) =
338       smokeenc->fps_denom * GST_SECOND / smokeenc->fps_num;
339
340   flags = 0;
341   if ((smokeenc->frame % smokeenc->keyframe) == 0) {
342     flags |= SMOKECODEC_KEYFRAME;
343   }
344   smokecodec_set_quality (smokeenc->info, smokeenc->min_quality,
345       smokeenc->max_quality);
346   smokecodec_set_threshold (smokeenc->info, smokeenc->threshold);
347   smokecodec_encode (smokeenc->info, data, flags, outdata, &encsize);
348   gst_buffer_unref (buf);
349
350   GST_BUFFER_SIZE (outbuf) = encsize;
351   GST_BUFFER_OFFSET (outbuf) = smokeenc->frame;
352   GST_BUFFER_OFFSET_END (outbuf) = smokeenc->frame + 1;
353
354   ret = gst_pad_push (smokeenc->srcpad, outbuf);
355
356   smokeenc->frame++;
357
358   return ret;
359 }
360
361 static void
362 gst_smokeenc_set_property (GObject * object, guint prop_id,
363     const GValue * value, GParamSpec * pspec)
364 {
365   GstSmokeEnc *smokeenc;
366
367   g_return_if_fail (GST_IS_SMOKEENC (object));
368   smokeenc = GST_SMOKEENC (object);
369
370   switch (prop_id) {
371     case ARG_MIN_QUALITY:
372       smokeenc->min_quality = g_value_get_int (value);
373       break;
374     case ARG_MAX_QUALITY:
375       smokeenc->max_quality = g_value_get_int (value);
376       break;
377     case ARG_THRESHOLD:
378       smokeenc->threshold = g_value_get_int (value);
379       break;
380     case ARG_KEYFRAME:
381       smokeenc->keyframe = g_value_get_int (value);
382       break;
383     default:
384       break;
385   }
386 }
387
388 static void
389 gst_smokeenc_get_property (GObject * object, guint prop_id, GValue * value,
390     GParamSpec * pspec)
391 {
392   GstSmokeEnc *smokeenc;
393
394   g_return_if_fail (GST_IS_SMOKEENC (object));
395   smokeenc = GST_SMOKEENC (object);
396
397   switch (prop_id) {
398     case ARG_MIN_QUALITY:
399       g_value_set_int (value, smokeenc->min_quality);
400       break;
401     case ARG_MAX_QUALITY:
402       g_value_set_int (value, smokeenc->max_quality);
403       break;
404     case ARG_THRESHOLD:
405       g_value_set_int (value, smokeenc->threshold);
406       break;
407     case ARG_KEYFRAME:
408       g_value_set_int (value, smokeenc->keyframe);
409       break;
410     default:
411       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
412       break;
413   }
414 }