1 /*
2 * WebP encoding support via libwebp
3 * Copyright (c) 2013 Justin Ruggles <justin.ruggles@gmail.com>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * WebP encoder using libwebp
25 */
26
27 #include <webp/encode.h>
28
35
43 WebPConfig
config;
// libwebp configuration
48
50 {
51 switch (err) {
52 case VP8_ENC_ERROR_OUT_OF_MEMORY:
53 case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
55 case VP8_ENC_ERROR_NULL_PARAMETER:
56 case VP8_ENC_ERROR_INVALID_CONFIGURATION:
57 case VP8_ENC_ERROR_BAD_DIMENSION:
59 }
61 }
62
64 {
67
70 0.0f, 100.0f);
71
76 }
77
78 if (s->
preset >= WEBP_PRESET_DEFAULT) {
80 if (!ret)
85 } else {
86 ret = WebPConfigInit(&s->
config);
87 if (!ret)
89
93
94 ret = WebPValidateConfig(&s->
config);
95 if (!ret)
97 }
98
102
103 return 0;
104 }
105
108 {
111 WebPPicture *pic =
NULL;
112 WebPMemoryWriter mw = { 0 };
114
115 if (avctx->
width > WEBP_MAX_DIMENSION || avctx->
height > WEBP_MAX_DIMENSION) {
117 WEBP_MAX_DIMENSION, WEBP_MAX_DIMENSION);
119 }
120
122 if (!pic)
124
125 ret = WebPPictureInit(pic);
126 if (!ret) {
129 }
130 pic->width = avctx->
width;
131 pic->height = avctx->
height;
132
135 /* libwebp will automatically convert RGB input to YUV when
136 encoding lossy. */
139 "Using libwebp for RGB-to-YUV conversion. You may want "
140 "to consider passing in YUV instead for lossy "
141 "encoding.\n");
143 }
144 }
145 pic->use_argb = 1;
146 pic->argb = (uint32_t *)frame->
data[0];
147 pic->argb_stride = frame->
linesize[0] / 4;
148 } else {
152 "Copying frame due to differing chroma linesizes.\n");
154 }
156 if (!alt_frame) {
159 }
166 if (ret < 0)
170 frame = alt_frame;
174
180 }
181 }
182
184 for (y = 0; y < frame->
height; y+= bs) {
185 for (x = 0; x < frame->
width; x+= bs) {
186 int skip;
188 for (p = 0; p < 3; p++) {
189 int bs2 = bs >> !!p;
192 int xs = x >> !!p;
193 int ys = y >> !!p;
194 for (y2 = ys; y2 <
FFMIN(ys + bs2, h); y2++) {
195 for (x2 = xs; x2 <
FFMIN(xs + bs2, w); x2++) {
199 }
200 }
201 }
203 if (!skip)
204 for (p = 0; p < 3; p++) {
205 int bs2 = bs >> !!p;
208 int xs = x >> !!p;
209 int ys = y >> !!p;
210 for (y2 = ys; y2 <
FFMIN(ys + bs2, h); y2++) {
212 & frame->
data[p][frame->
linesize[p] * y2 + xs], FFMIN(bs2, w-xs));
213 }
214 }
215 for (y2 = y; y2 <
FFMIN(y+bs, frame->
height); y2++) {
217 skip ? 0 : 255,
218 FFMIN(bs, frame->
width-x));
219 }
220 }
221 }
222 }
223 }
224
225 pic->use_argb = 0;
226 pic->y = frame->
data[0];
227 pic->u = frame->
data[1];
228 pic->v = frame->
data[2];
230 pic->uv_stride = frame->
linesize[1];
232 pic->colorspace = WEBP_YUV420A;
233 pic->a = frame->
data[3];
235 if (alt_frame)
236 WebPCleanupTransparentArea(pic);
237 } else {
238 pic->colorspace = WEBP_YUV420;
239 }
240
242 /* We do not have a way to automatically prioritize RGB over YUV
243 in automatic pixel format conversion based on whether we're
244 encoding lossless or lossy, so we do conversion with libwebp as
245 a convenience. */
248 "Using libwebp for YUV-to-RGB conversion. You may want "
249 "to consider passing in RGB instead for lossless "
250 "encoding.\n");
252 }
253
254 #if (WEBP_ENCODER_ABI_VERSION <= 0x201)
255 /* libwebp should do the conversion automatically, but there is a
256 bug that causes it to return an error instead, so a work-around
257 is required.
258 See https://code.google.com/p/webp/issues/detail?id=178 */
259 pic->memory_ = (void*)1; /* something non-null */
260 ret = WebPPictureYUVAToARGB(pic);
261 if (!ret) {
263 "WebPPictureYUVAToARGB() failed with error: %d\n",
264 pic->error_code);
267 }
268 pic->memory_ =
NULL;
/* restore pointer */
269 #endif
270 }
271 }
272
273 WebPMemoryWriterInit(&mw);
274 pic->custom_ptr = &mw;
275 pic->writer = WebPMemoryWrite;
276
277 ret = WebPEncode(&s->
config, pic);
278 if (!ret) {
280 pic->error_code);
283 }
284
286 if (ret < 0)
288 memcpy(pkt->
data, mw.mem, mw.size);
289
291 *got_packet = 1;
292
294 #if (WEBP_ENCODER_ABI_VERSION > 0x0203)
295 WebPMemoryWriterClear(&mw);
296 #else
297 free(mw.mem); /* must use free() according to libwebp documentation */
298 #endif
299 WebPPictureFree(pic);
302
304 }
305
307 {
309
311
312 return 0;
313 }
314
315 #define OFFSET(x) offsetof(LibWebPContext, x)
316 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
320 {
"none",
"do not use a preset", 0,
AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0,
VE,
"preset" },
321 {
"default",
"default preset", 0,
AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DEFAULT }, 0, 0,
VE,
"preset" },
322 {
"picture",
"digital picture, like portrait, inner shot", 0,
AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PICTURE }, 0, 0,
VE,
"preset" },
323 {
"photo",
"outdoor photograph, with natural lighting", 0,
AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PHOTO }, 0, 0,
VE,
"preset" },
324 {
"drawing",
"hand or line drawing, with high-contrast details", 0,
AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DRAWING }, 0, 0,
VE,
"preset" },
325 {
"icon",
"small-sized colorful images", 0,
AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_ICON }, 0, 0,
VE,
"preset" },
326 {
"text",
"text-like", 0,
AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_TEXT }, 0, 0,
VE,
"preset" },
327 {
"cr_threshold",
"Conditional replenishment threshold",
OFFSET(cr_threshold),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX,
VE },
328 {
"cr_size" ,
"Conditional replenishment block size",
OFFSET(cr_size) ,
AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 256,
VE },
331 };
332
333
339 };
340
342 { "compression_level", "4" },
343 { "global_quality", "-1" },
345 };
346
360 },
361 .priv_class = &class,
363 };