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