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
111 static void
gst_webp_enc_class_init(GstWebpEncClass * klass)112 gst_webp_enc_class_init (GstWebpEncClass * klass)
113 {
114 GObjectClass *gobject_class;
115 GstElementClass *element_class;
116 GstVideoEncoderClass *venc_class;
117
118 gobject_class = (GObjectClass *) klass;
119 element_class = (GstElementClass *) klass;
120 venc_class = (GstVideoEncoderClass *) klass;
121
122 parent_class = g_type_class_peek_parent (klass);
123
124 gobject_class->set_property = gst_webp_enc_set_property;
125 gobject_class->get_property = gst_webp_enc_get_property;
126 gst_element_class_add_static_pad_template (element_class,
127 &webp_enc_sink_factory);
128 gst_element_class_add_static_pad_template (element_class,
129 &webp_enc_src_factory);
130 gst_element_class_set_static_metadata (element_class, "WEBP image encoder",
131 "Codec/Encoder/Image", "Encode images in WEBP format",
132 "Sreerenj Balachandran <sreerenjb@gnome.org>");
133
134 venc_class->start = gst_webp_enc_start;
135 venc_class->stop = gst_webp_enc_stop;
136 venc_class->set_format = gst_webp_enc_set_format;
137 venc_class->handle_frame = gst_webp_enc_handle_frame;
138 venc_class->propose_allocation = gst_webp_enc_propose_allocation;
139
140 g_object_class_install_property (gobject_class, PROP_LOSSLESS,
141 g_param_spec_boolean ("lossless", "Lossless",
142 "Enable lossless encoding",
143 DEFAULT_LOSSLESS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
144 g_object_class_install_property (gobject_class, PROP_QUALITY,
145 g_param_spec_float ("quality", "quality-level",
146 "quality level, between 0 (smallest file) and 100 (biggest)",
147 0, 100, DEFAULT_QUALITY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
148 g_object_class_install_property (gobject_class, PROP_SPEED,
149 g_param_spec_uint ("speed", "Compression Method",
150 "quality/speed trade-off (0=fast, 6=slower-better)",
151 0, 6, DEFAULT_SPEED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
152 g_object_class_install_property (gobject_class, PROP_PRESET,
153 g_param_spec_enum ("preset", "preset tuning",
154 "Preset name for visual tuning",
155 GST_WEBP_ENC_PRESET_TYPE, DEFAULT_PRESET,
156 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
157
158 GST_DEBUG_CATEGORY_INIT (webpenc_debug, "webpenc", 0,
159 "WEBP encoding element");
160 }
161
162 static void
gst_webp_enc_init(GstWebpEnc * webpenc)163 gst_webp_enc_init (GstWebpEnc * webpenc)
164 {
165 GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_ENCODER_SINK_PAD (webpenc));
166
167 webpenc->lossless = DEFAULT_LOSSLESS;
168 webpenc->quality = DEFAULT_QUALITY;
169 webpenc->speed = DEFAULT_SPEED;
170 webpenc->preset = DEFAULT_PRESET;
171
172 webpenc->use_argb = FALSE;
173 webpenc->rgb_format = GST_VIDEO_FORMAT_UNKNOWN;
174 }
175
176 static gboolean
gst_webp_enc_set_format(GstVideoEncoder * encoder,GstVideoCodecState * state)177 gst_webp_enc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state)
178 {
179 GstWebpEnc *enc = GST_WEBP_ENC (encoder);
180 GstVideoCodecState *output_state;
181 GstVideoInfo *info;
182 GstVideoFormat format;
183
184 info = &state->info;
185 format = GST_VIDEO_INFO_FORMAT (info);
186
187 if (GST_VIDEO_INFO_IS_YUV (info)) {
188 switch (format) {
189 case GST_VIDEO_FORMAT_I420:
190 case GST_VIDEO_FORMAT_YV12:
191 enc->webp_color_space = WEBP_YUV420;
192 break;
193 default:
194 break;
195 }
196 } else {
197 if (GST_VIDEO_INFO_IS_RGB (info)) {
198 enc->rgb_format = format;
199 enc->use_argb = 1;
200 }
201 }
202
203 if (enc->input_state)
204 gst_video_codec_state_unref (enc->input_state);
205 enc->input_state = gst_video_codec_state_ref (state);
206
207 output_state =
208 gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (enc),
209 gst_caps_new_empty_simple ("image/webp"), enc->input_state);
210 gst_video_codec_state_unref (output_state);
211
212 return TRUE;
213 }
214
215 static gboolean
gst_webp_set_picture_params(GstWebpEnc * enc)216 gst_webp_set_picture_params (GstWebpEnc * enc)
217 {
218 GstVideoInfo *info;
219 gboolean ret = TRUE;
220
221 info = &enc->input_state->info;
222
223 if (!WebPPictureInit (&enc->webp_picture)) {
224 ret = FALSE;
225 goto failed_pic_init;
226 }
227
228 enc->webp_picture.use_argb = enc->use_argb;
229 if (!enc->use_argb)
230 enc->webp_picture.colorspace = enc->webp_color_space;
231
232 enc->webp_picture.width = GST_VIDEO_INFO_WIDTH (info);
233 enc->webp_picture.height = GST_VIDEO_INFO_HEIGHT (info);
234
235 WebPMemoryWriterInit (&enc->webp_writer);
236 enc->webp_picture.writer = WebPMemoryWrite;
237 enc->webp_picture.custom_ptr = &enc->webp_writer;
238
239 return ret;
240
241 failed_pic_init:
242 {
243 GST_ERROR_OBJECT (enc, "Failed to Initialize WebPPicture !");
244 return ret;
245 }
246 }
247
248 static GstFlowReturn
gst_webp_enc_handle_frame(GstVideoEncoder * encoder,GstVideoCodecFrame * frame)249 gst_webp_enc_handle_frame (GstVideoEncoder * encoder,
250 GstVideoCodecFrame * frame)
251 {
252 GstWebpEnc *enc = GST_WEBP_ENC (encoder);
253 GstBuffer *out_buffer = NULL;
254 GstVideoFrame vframe;
255
256 GST_LOG_OBJECT (enc, "got new frame");
257
258 gst_webp_set_picture_params (enc);
259
260 if (!gst_video_frame_map (&vframe, &enc->input_state->info,
261 frame->input_buffer, GST_MAP_READ))
262 return GST_FLOW_ERROR;
263
264 if (!enc->use_argb) {
265 enc->webp_picture.y = GST_VIDEO_FRAME_COMP_DATA (&vframe, 0);
266 enc->webp_picture.u = GST_VIDEO_FRAME_COMP_DATA (&vframe, 1);
267 enc->webp_picture.v = GST_VIDEO_FRAME_COMP_DATA (&vframe, 2);
268
269 enc->webp_picture.y_stride = GST_VIDEO_FRAME_COMP_STRIDE (&vframe, 0);
270 enc->webp_picture.uv_stride = GST_VIDEO_FRAME_COMP_STRIDE (&vframe, 1);
271
272 } else {
273 switch (enc->rgb_format) {
274 case GST_VIDEO_FORMAT_RGB:
275 WebPPictureImportRGB (&enc->webp_picture,
276 GST_VIDEO_FRAME_COMP_DATA (&vframe, 0),
277 GST_VIDEO_FRAME_COMP_STRIDE (&vframe, 0));
278 break;
279 case GST_VIDEO_FORMAT_RGBA:
280 WebPPictureImportRGBA (&enc->webp_picture,
281 GST_VIDEO_FRAME_COMP_DATA (&vframe, 0),
282 GST_VIDEO_FRAME_COMP_STRIDE (&vframe, 0));
283 break;
284 default:
285 break;
286 }
287 }
288
289 if (WebPEncode (&enc->webp_config, &enc->webp_picture)) {
290 WebPPictureFree (&enc->webp_picture);
291
292 out_buffer = gst_buffer_new_allocate (NULL, enc->webp_writer.size, NULL);
293 if (!out_buffer) {
294 GST_ERROR_OBJECT (enc, "Failed to create output buffer");
295 gst_video_frame_unmap (&vframe);
296 return GST_FLOW_ERROR;
297 }
298 gst_buffer_fill (out_buffer, 0, enc->webp_writer.mem,
299 enc->webp_writer.size);
300 free (enc->webp_writer.mem);
301 } else {
302 GST_ERROR_OBJECT (enc, "Failed to encode WebPPicture");
303 gst_video_frame_unmap (&vframe);
304 return GST_FLOW_ERROR;
305 }
306
307 gst_video_frame_unmap (&vframe);
308 frame->output_buffer = out_buffer;
309 return gst_video_encoder_finish_frame (encoder, frame);
310 }
311
312 static gboolean
gst_webp_enc_propose_allocation(GstVideoEncoder * encoder,GstQuery * query)313 gst_webp_enc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
314 {
315 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
316 return
317 GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
318 query);
319 }
320
321 static void
gst_webp_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)322 gst_webp_enc_set_property (GObject * object, guint prop_id,
323 const GValue * value, GParamSpec * pspec)
324 {
325 GstWebpEnc *webpenc = GST_WEBP_ENC (object);
326
327 switch (prop_id) {
328 case PROP_LOSSLESS:
329 webpenc->lossless = g_value_get_boolean (value);
330 break;
331 case PROP_QUALITY:
332 webpenc->quality = g_value_get_float (value);
333 break;
334 case PROP_SPEED:
335 webpenc->speed = g_value_get_uint (value);
336 break;
337 case PROP_PRESET:
338 webpenc->preset = g_value_get_enum (value);
339 break;
340 default:
341 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
342 break;
343 }
344
345 }
346
347 static void
gst_webp_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)348 gst_webp_enc_get_property (GObject * object, guint prop_id, GValue * value,
349 GParamSpec * pspec)
350 {
351 GstWebpEnc *webpenc = GST_WEBP_ENC (object);
352
353 switch (prop_id) {
354 case PROP_LOSSLESS:
355 g_value_set_boolean (value, webpenc->lossless);
356 break;
357 case PROP_QUALITY:
358 g_value_set_float (value, webpenc->quality);
359 break;
360 case PROP_SPEED:
361 g_value_set_uint (value, webpenc->speed);
362 break;
363 case PROP_PRESET:
364 g_value_set_enum (value, webpenc->preset);
365 break;
366 default:
367 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
368 break;
369 }
370 }
371
372 static gboolean
gst_webp_enc_start(GstVideoEncoder * benc)373 gst_webp_enc_start (GstVideoEncoder * benc)
374 {
375 GstWebpEnc *enc = (GstWebpEnc *) benc;
376
377 if (!WebPConfigPreset (&enc->webp_config, enc->preset, enc->quality)) {
378 GST_ERROR_OBJECT (enc, "Failed to Initialize WebPConfig ");
379 return FALSE;
380 }
381
382 enc->webp_config.lossless = enc->lossless;
383 enc->webp_config.method = enc->speed;
384 if (!WebPValidateConfig (&enc->webp_config)) {
385 GST_ERROR_OBJECT (enc, "Failed to Validate the WebPConfig");
386 return FALSE;
387 }
388 return TRUE;
389 }
390
391 static gboolean
gst_webp_enc_stop(GstVideoEncoder * benc)392 gst_webp_enc_stop (GstVideoEncoder * benc)
393 {
394 GstWebpEnc *enc = GST_WEBP_ENC (benc);
395 if (enc->input_state)
396 gst_video_codec_state_unref (enc->input_state);
397 return TRUE;
398 }
399
400 gboolean
gst_webp_enc_register(GstPlugin * plugin)401 gst_webp_enc_register (GstPlugin * plugin)
402 {
403 return gst_element_register (plugin, "webpenc",
404 GST_RANK_PRIMARY, GST_TYPE_WEBP_ENC);
405 }
406