• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2014> Sreerenj Balachandran <sreerenjb@gnome.org>
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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include <string.h>
24 #include <stdlib.h>             /* free */
25 
26 #include <gst/video/video.h>
27 #include <gst/video/gstvideometa.h>
28 
29 #include "gstwebpenc.h"
30 
31 #define GST_CAT_DEFAULT webpenc_debug
32 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
33 
34 enum
35 {
36   PROP_0,
37   PROP_LOSSLESS,
38   PROP_QUALITY,
39   PROP_SPEED,
40   PROP_PRESET
41 };
42 
43 #define DEFAULT_LOSSLESS FALSE
44 #define DEFAULT_QUALITY 90
45 #define DEFAULT_SPEED 4
46 #define DEFAULT_PRESET WEBP_PRESET_PHOTO
47 
48 static void gst_webp_enc_set_property (GObject * object, guint prop_id,
49     const GValue * value, GParamSpec * pspec);
50 static void gst_webp_enc_get_property (GObject * object, guint prop_id,
51     GValue * value, GParamSpec * pspec);
52 static gboolean gst_webp_enc_start (GstVideoEncoder * benc);
53 static gboolean gst_webp_enc_stop (GstVideoEncoder * benc);
54 static gboolean gst_webp_enc_set_format (GstVideoEncoder * encoder,
55     GstVideoCodecState * state);
56 static GstFlowReturn gst_webp_enc_handle_frame (GstVideoEncoder * encoder,
57     GstVideoCodecFrame * frame);
58 static gboolean gst_webp_enc_propose_allocation (GstVideoEncoder * encoder,
59     GstQuery * query);
60 
61 static GstStaticPadTemplate webp_enc_sink_factory =
62 GST_STATIC_PAD_TEMPLATE ("sink",
63     GST_PAD_SINK,
64     GST_PAD_ALWAYS,
65     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ I420, YV12, RGB, RGBA}"))
66     );
67 static GstStaticPadTemplate webp_enc_src_factory =
68 GST_STATIC_PAD_TEMPLATE ("src",
69     GST_PAD_SRC,
70     GST_PAD_ALWAYS,
71     GST_STATIC_CAPS ("image/webp, "
72         "framerate = (fraction) [0/1, MAX], "
73         "width = (int) [ 16, 16383 ], " "height = (int) [ 16, 16383 ]")
74     );
75 
76 enum
77 {
78   GST_WEBP_PRESET_DEFAULT,
79   GST_WEBP_PRESET_PICTURE,
80   GST_WEBP_PRESET_PHOTO,
81   GST_WEBP_PRESET_DRAWING,
82   GST_WEBP_PRESET_ICON,
83   GST_WEBP_PREET_TEXT
84 };
85 
86 static const GEnumValue preset_values[] = {
87   {GST_WEBP_PRESET_DEFAULT, "Default", "none"},
88   {GST_WEBP_PRESET_PICTURE, "Digital picture,inner shot", "picture"},
89   {GST_WEBP_PRESET_PHOTO, "Outdoor photo, natural lighting", "photo"},
90   {GST_WEBP_PRESET_DRAWING, "Hand or Line drawing", "drawing"},
91   {GST_WEBP_PRESET_ICON, "Small-sized colorful images", "icon"},
92   {GST_WEBP_PREET_TEXT, "text-like", "text"},
93   {0, NULL, NULL},
94 };
95 
96 #define GST_WEBP_ENC_PRESET_TYPE (gst_webp_enc_preset_get_type())
97 static GType
gst_webp_enc_preset_get_type(void)98 gst_webp_enc_preset_get_type (void)
99 {
100   static GType preset_type = 0;
101 
102   if (!preset_type) {
103     preset_type = g_enum_register_static ("GstWebpEncPreset", preset_values);
104   }
105   return preset_type;
106 }
107 
108 #define gst_webp_enc_parent_class parent_class
109 G_DEFINE_TYPE (GstWebpEnc, gst_webp_enc, GST_TYPE_VIDEO_ENCODER);
110 GST_ELEMENT_REGISTER_DEFINE (webpenc, "webpenc",
111     GST_RANK_PRIMARY, GST_TYPE_WEBP_ENC);
112 
113 static void
gst_webp_enc_class_init(GstWebpEncClass * klass)114 gst_webp_enc_class_init (GstWebpEncClass * klass)
115 {
116   GObjectClass *gobject_class;
117   GstElementClass *element_class;
118   GstVideoEncoderClass *venc_class;
119 
120   gobject_class = (GObjectClass *) klass;
121   element_class = (GstElementClass *) klass;
122   venc_class = (GstVideoEncoderClass *) klass;
123 
124   parent_class = g_type_class_peek_parent (klass);
125 
126   gobject_class->set_property = gst_webp_enc_set_property;
127   gobject_class->get_property = gst_webp_enc_get_property;
128   gst_element_class_add_static_pad_template (element_class,
129       &webp_enc_sink_factory);
130   gst_element_class_add_static_pad_template (element_class,
131       &webp_enc_src_factory);
132   gst_element_class_set_static_metadata (element_class, "WEBP image encoder",
133       "Codec/Encoder/Image", "Encode images in WEBP format",
134       "Sreerenj Balachandran <sreerenjb@gnome.org>");
135 
136   venc_class->start = gst_webp_enc_start;
137   venc_class->stop = gst_webp_enc_stop;
138   venc_class->set_format = gst_webp_enc_set_format;
139   venc_class->handle_frame = gst_webp_enc_handle_frame;
140   venc_class->propose_allocation = gst_webp_enc_propose_allocation;
141 
142   g_object_class_install_property (gobject_class, PROP_LOSSLESS,
143       g_param_spec_boolean ("lossless", "Lossless",
144           "Enable lossless encoding",
145           DEFAULT_LOSSLESS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146   g_object_class_install_property (gobject_class, PROP_QUALITY,
147       g_param_spec_float ("quality", "quality-level",
148           "quality level, between 0 (smallest file) and 100 (biggest)",
149           0, 100, DEFAULT_QUALITY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
150   g_object_class_install_property (gobject_class, PROP_SPEED,
151       g_param_spec_uint ("speed", "Compression Method",
152           "quality/speed trade-off (0=fast, 6=slower-better)",
153           0, 6, DEFAULT_SPEED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
154   g_object_class_install_property (gobject_class, PROP_PRESET,
155       g_param_spec_enum ("preset", "preset tuning",
156           "Preset name for visual tuning",
157           GST_WEBP_ENC_PRESET_TYPE, DEFAULT_PRESET,
158           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
159 
160   GST_DEBUG_CATEGORY_INIT (webpenc_debug, "webpenc", 0,
161       "WEBP encoding element");
162 
163   gst_type_mark_as_plugin_api (GST_WEBP_ENC_PRESET_TYPE, 0);
164 }
165 
166 static void
gst_webp_enc_init(GstWebpEnc * webpenc)167 gst_webp_enc_init (GstWebpEnc * webpenc)
168 {
169   GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_ENCODER_SINK_PAD (webpenc));
170 
171   webpenc->lossless = DEFAULT_LOSSLESS;
172   webpenc->quality = DEFAULT_QUALITY;
173   webpenc->speed = DEFAULT_SPEED;
174   webpenc->preset = DEFAULT_PRESET;
175 
176   webpenc->use_argb = FALSE;
177   webpenc->rgb_format = GST_VIDEO_FORMAT_UNKNOWN;
178 }
179 
180 static gboolean
gst_webp_enc_set_format(GstVideoEncoder * encoder,GstVideoCodecState * state)181 gst_webp_enc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state)
182 {
183   GstWebpEnc *enc = GST_WEBP_ENC (encoder);
184   GstVideoCodecState *output_state;
185   GstVideoInfo *info;
186   GstVideoFormat format;
187 
188   info = &state->info;
189   format = GST_VIDEO_INFO_FORMAT (info);
190 
191   if (GST_VIDEO_INFO_IS_YUV (info)) {
192     switch (format) {
193       case GST_VIDEO_FORMAT_I420:
194       case GST_VIDEO_FORMAT_YV12:
195         enc->webp_color_space = WEBP_YUV420;
196         break;
197       default:
198         break;
199     }
200   } else {
201     if (GST_VIDEO_INFO_IS_RGB (info)) {
202       enc->rgb_format = format;
203       enc->use_argb = 1;
204     }
205   }
206 
207   if (enc->input_state)
208     gst_video_codec_state_unref (enc->input_state);
209   enc->input_state = gst_video_codec_state_ref (state);
210 
211   output_state =
212       gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (enc),
213       gst_caps_new_empty_simple ("image/webp"), enc->input_state);
214   gst_video_codec_state_unref (output_state);
215 
216   return TRUE;
217 }
218 
219 static gboolean
gst_webp_set_picture_params(GstWebpEnc * enc)220 gst_webp_set_picture_params (GstWebpEnc * enc)
221 {
222   GstVideoInfo *info;
223   gboolean ret = TRUE;
224 
225   info = &enc->input_state->info;
226 
227   if (!WebPPictureInit (&enc->webp_picture)) {
228     ret = FALSE;
229     goto failed_pic_init;
230   }
231 
232   enc->webp_picture.use_argb = enc->use_argb;
233   if (!enc->use_argb)
234     enc->webp_picture.colorspace = enc->webp_color_space;
235 
236   enc->webp_picture.width = GST_VIDEO_INFO_WIDTH (info);
237   enc->webp_picture.height = GST_VIDEO_INFO_HEIGHT (info);
238 
239   WebPMemoryWriterInit (&enc->webp_writer);
240   enc->webp_picture.writer = WebPMemoryWrite;
241   enc->webp_picture.custom_ptr = &enc->webp_writer;
242 
243   return ret;
244 
245 failed_pic_init:
246   {
247     GST_ERROR_OBJECT (enc, "Failed to Initialize WebPPicture !");
248     return ret;
249   }
250 }
251 
252 static GstFlowReturn
gst_webp_enc_handle_frame(GstVideoEncoder * encoder,GstVideoCodecFrame * frame)253 gst_webp_enc_handle_frame (GstVideoEncoder * encoder,
254     GstVideoCodecFrame * frame)
255 {
256   GstWebpEnc *enc = GST_WEBP_ENC (encoder);
257   GstBuffer *out_buffer = NULL;
258   GstVideoFrame vframe;
259 
260   GST_LOG_OBJECT (enc, "got new frame");
261 
262   gst_webp_set_picture_params (enc);
263 
264   if (!gst_video_frame_map (&vframe, &enc->input_state->info,
265           frame->input_buffer, GST_MAP_READ))
266     return GST_FLOW_ERROR;
267 
268   if (!enc->use_argb) {
269     enc->webp_picture.y = GST_VIDEO_FRAME_COMP_DATA (&vframe, 0);
270     enc->webp_picture.u = GST_VIDEO_FRAME_COMP_DATA (&vframe, 1);
271     enc->webp_picture.v = GST_VIDEO_FRAME_COMP_DATA (&vframe, 2);
272 
273     enc->webp_picture.y_stride = GST_VIDEO_FRAME_COMP_STRIDE (&vframe, 0);
274     enc->webp_picture.uv_stride = GST_VIDEO_FRAME_COMP_STRIDE (&vframe, 1);
275 
276   } else {
277     switch (enc->rgb_format) {
278       case GST_VIDEO_FORMAT_RGB:
279         WebPPictureImportRGB (&enc->webp_picture,
280             GST_VIDEO_FRAME_COMP_DATA (&vframe, 0),
281             GST_VIDEO_FRAME_COMP_STRIDE (&vframe, 0));
282         break;
283       case GST_VIDEO_FORMAT_RGBA:
284         WebPPictureImportRGBA (&enc->webp_picture,
285             GST_VIDEO_FRAME_COMP_DATA (&vframe, 0),
286             GST_VIDEO_FRAME_COMP_STRIDE (&vframe, 0));
287         break;
288       default:
289         break;
290     }
291   }
292 
293   if (WebPEncode (&enc->webp_config, &enc->webp_picture)) {
294     WebPPictureFree (&enc->webp_picture);
295 
296     out_buffer = gst_buffer_new_allocate (NULL, enc->webp_writer.size, NULL);
297     if (!out_buffer) {
298       GST_ERROR_OBJECT (enc, "Failed to create output buffer");
299       gst_video_frame_unmap (&vframe);
300       return GST_FLOW_ERROR;
301     }
302     gst_buffer_fill (out_buffer, 0, enc->webp_writer.mem,
303         enc->webp_writer.size);
304     free (enc->webp_writer.mem);
305   } else {
306     GST_ERROR_OBJECT (enc, "Failed to encode WebPPicture");
307     gst_video_frame_unmap (&vframe);
308     return GST_FLOW_ERROR;
309   }
310 
311   gst_video_frame_unmap (&vframe);
312   frame->output_buffer = out_buffer;
313   return gst_video_encoder_finish_frame (encoder, frame);
314 }
315 
316 static gboolean
gst_webp_enc_propose_allocation(GstVideoEncoder * encoder,GstQuery * query)317 gst_webp_enc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
318 {
319   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
320   return
321       GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
322       query);
323 }
324 
325 static void
gst_webp_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)326 gst_webp_enc_set_property (GObject * object, guint prop_id,
327     const GValue * value, GParamSpec * pspec)
328 {
329   GstWebpEnc *webpenc = GST_WEBP_ENC (object);
330 
331   switch (prop_id) {
332     case PROP_LOSSLESS:
333       webpenc->lossless = g_value_get_boolean (value);
334       break;
335     case PROP_QUALITY:
336       webpenc->quality = g_value_get_float (value);
337       break;
338     case PROP_SPEED:
339       webpenc->speed = g_value_get_uint (value);
340       break;
341     case PROP_PRESET:
342       webpenc->preset = g_value_get_enum (value);
343       break;
344     default:
345       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346       break;
347   }
348 
349 }
350 
351 static void
gst_webp_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)352 gst_webp_enc_get_property (GObject * object, guint prop_id, GValue * value,
353     GParamSpec * pspec)
354 {
355   GstWebpEnc *webpenc = GST_WEBP_ENC (object);
356 
357   switch (prop_id) {
358     case PROP_LOSSLESS:
359       g_value_set_boolean (value, webpenc->lossless);
360       break;
361     case PROP_QUALITY:
362       g_value_set_float (value, webpenc->quality);
363       break;
364     case PROP_SPEED:
365       g_value_set_uint (value, webpenc->speed);
366       break;
367     case PROP_PRESET:
368       g_value_set_enum (value, webpenc->preset);
369       break;
370     default:
371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372       break;
373   }
374 }
375 
376 static gboolean
gst_webp_enc_start(GstVideoEncoder * benc)377 gst_webp_enc_start (GstVideoEncoder * benc)
378 {
379   GstWebpEnc *enc = (GstWebpEnc *) benc;
380 
381   if (!WebPConfigPreset (&enc->webp_config, enc->preset, enc->quality)) {
382     GST_ERROR_OBJECT (enc, "Failed to Initialize WebPConfig ");
383     return FALSE;
384   }
385 
386   enc->webp_config.lossless = enc->lossless;
387   enc->webp_config.method = enc->speed;
388   if (!WebPValidateConfig (&enc->webp_config)) {
389     GST_ERROR_OBJECT (enc, "Failed to Validate the WebPConfig");
390     return FALSE;
391   }
392   return TRUE;
393 }
394 
395 static gboolean
gst_webp_enc_stop(GstVideoEncoder * benc)396 gst_webp_enc_stop (GstVideoEncoder * benc)
397 {
398   GstWebpEnc *enc = GST_WEBP_ENC (benc);
399   if (enc->input_state)
400     gst_video_codec_state_unref (enc->input_state);
401   return TRUE;
402 }
403