2 * WebP encoding support via libwebp
3 * Copyright (c) 2013 Justin Ruggles <justin.ruggles@gmail.com>
5 * This file is part of FFmpeg.
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.
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.
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
24 * WebP encoder using libwebp
27 #include <webp/encode.h>
29 #include "libavutil/common.h"
30 #include "libavutil/frame.h"
31 #include "libavutil/imgutils.h"
32 #include "libavutil/opt.h"
36 typedef struct LibWebPContext {
37 AVClass *class; // class for AVOptions
38 float quality; // lossy quality 0 - 100
39 int lossless; // use lossless encoding
40 int preset; // configuration preset
41 int chroma_warning; // chroma linesize mismatch warning has been printed
42 int conversion_warning; // pixel format conversion warning has been printed
43 WebPConfig config; // libwebp configuration
46 static int libwebp_error_to_averror(int err)
49 case VP8_ENC_ERROR_OUT_OF_MEMORY:
50 case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
51 return AVERROR(ENOMEM);
52 case VP8_ENC_ERROR_NULL_PARAMETER:
53 case VP8_ENC_ERROR_INVALID_CONFIGURATION:
54 case VP8_ENC_ERROR_BAD_DIMENSION:
55 return AVERROR(EINVAL);
57 return AVERROR_UNKNOWN;
60 static av_cold int libwebp_encode_init(AVCodecContext *avctx)
62 LibWebPContext *s = avctx->priv_data;
65 if (avctx->global_quality < 0)
66 avctx->global_quality = 75 * FF_QP2LAMBDA;
67 s->quality = av_clipf(avctx->global_quality / (float)FF_QP2LAMBDA,
70 if (avctx->compression_level < 0 || avctx->compression_level > 6) {
71 av_log(avctx, AV_LOG_WARNING, "invalid compression level: %d\n",
72 avctx->compression_level);
73 avctx->compression_level = av_clip(avctx->compression_level, 0, 6);
76 if (s->preset >= WEBP_PRESET_DEFAULT) {
77 ret = WebPConfigPreset(&s->config, s->preset, s->quality);
79 return AVERROR_UNKNOWN;
80 s->lossless = s->config.lossless;
81 s->quality = s->config.quality;
82 avctx->compression_level = s->config.method;
84 ret = WebPConfigInit(&s->config);
86 return AVERROR_UNKNOWN;
88 s->config.lossless = s->lossless;
89 s->config.quality = s->quality;
90 s->config.method = avctx->compression_level;
92 ret = WebPValidateConfig(&s->config);
94 return AVERROR(EINVAL);
97 av_log(avctx, AV_LOG_DEBUG, "%s - quality=%.1f method=%d\n",
98 s->lossless ? "Lossless" : "Lossy", s->quality,
99 avctx->compression_level);
104 static int libwebp_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
105 const AVFrame *frame, int *got_packet)
107 LibWebPContext *s = avctx->priv_data;
108 AVFrame *alt_frame = NULL;
109 WebPPicture *pic = NULL;
110 WebPMemoryWriter mw = { 0 };
113 if (avctx->width > WEBP_MAX_DIMENSION || avctx->height > WEBP_MAX_DIMENSION) {
114 av_log(avctx, AV_LOG_ERROR, "Picture size is too large. Max is %dx%d.\n",
115 WEBP_MAX_DIMENSION, WEBP_MAX_DIMENSION);
116 return AVERROR(EINVAL);
119 pic = av_malloc(sizeof(*pic));
121 return AVERROR(ENOMEM);
123 ret = WebPPictureInit(pic);
125 ret = AVERROR_UNKNOWN;
128 pic->width = avctx->width;
129 pic->height = avctx->height;
131 if (avctx->pix_fmt == AV_PIX_FMT_RGB32) {
133 /* libwebp will automatically convert RGB input to YUV when
135 if (!s->conversion_warning) {
136 av_log(avctx, AV_LOG_WARNING,
137 "Using libwebp for RGB-to-YUV conversion. You may want "
138 "to consider passing in YUV instead for lossy "
140 s->conversion_warning = 1;
144 pic->argb = (uint32_t *)frame->data[0];
145 pic->argb_stride = frame->linesize[0] / 4;
147 if (frame->linesize[1] != frame->linesize[2]) {
148 if (!s->chroma_warning) {
149 av_log(avctx, AV_LOG_WARNING,
150 "Copying frame due to differing chroma linesizes.\n");
151 s->chroma_warning = 1;
153 alt_frame = av_frame_alloc();
155 ret = AVERROR(ENOMEM);
158 alt_frame->width = frame->width;
159 alt_frame->height = frame->height;
160 alt_frame->format = frame->format;
161 ret = av_frame_get_buffer(alt_frame, 32);
164 av_frame_copy(alt_frame, frame);
168 pic->y = frame->data[0];
169 pic->u = frame->data[1];
170 pic->v = frame->data[2];
171 pic->y_stride = frame->linesize[0];
172 pic->uv_stride = frame->linesize[1];
173 if (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) {
174 pic->colorspace = WEBP_YUV420A;
175 pic->a = frame->data[3];
176 pic->a_stride = frame->linesize[3];
178 pic->colorspace = WEBP_YUV420;
182 /* We do not have a way to automatically prioritize RGB over YUV
183 in automatic pixel format conversion based on whether we're
184 encoding lossless or lossy, so we do conversion with libwebp as
186 if (!s->conversion_warning) {
187 av_log(avctx, AV_LOG_WARNING,
188 "Using libwebp for YUV-to-RGB conversion. You may want "
189 "to consider passing in RGB instead for lossless "
191 s->conversion_warning = 1;
194 #if (WEBP_ENCODER_ABI_VERSION <= 0x201)
195 /* libwebp should do the conversion automatically, but there is a
196 bug that causes it to return an error instead, so a work-around
198 See https://code.google.com/p/webp/issues/detail?id=178 */
199 pic->memory_ = (void*)1; /* something non-null */
200 ret = WebPPictureYUVAToARGB(pic);
202 av_log(avctx, AV_LOG_ERROR,
203 "WebPPictureYUVAToARGB() failed with error: %d\n",
205 ret = libwebp_error_to_averror(pic->error_code);
208 pic->memory_ = NULL; /* restore pointer */
213 WebPMemoryWriterInit(&mw);
214 pic->custom_ptr = &mw;
215 pic->writer = WebPMemoryWrite;
217 ret = WebPEncode(&s->config, pic);
219 av_log(avctx, AV_LOG_ERROR, "WebPEncode() failed with error: %d\n",
221 ret = libwebp_error_to_averror(pic->error_code);
225 ret = ff_alloc_packet(pkt, mw.size);
228 memcpy(pkt->data, mw.mem, mw.size);
230 pkt->flags |= AV_PKT_FLAG_KEY;
234 #if (WEBP_ENCODER_ABI_VERSION > 0x0203)
235 WebPMemoryWriterClear(&mw);
237 free(mw.mem); /* must use free() according to libwebp documentation */
239 WebPPictureFree(pic);
241 av_frame_free(&alt_frame);
246 #define OFFSET(x) offsetof(LibWebPContext, x)
247 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
248 static const AVOption options[] = {
249 { "lossless", "Use lossless mode", OFFSET(lossless), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE },
250 { "preset", "Configuration preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, WEBP_PRESET_TEXT, VE, "preset" },
251 { "none", "do not use a preset", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, "preset" },
252 { "default", "default preset", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DEFAULT }, 0, 0, VE, "preset" },
253 { "picture", "digital picture, like portrait, inner shot", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PICTURE }, 0, 0, VE, "preset" },
254 { "photo", "outdoor photograph, with natural lighting", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PHOTO }, 0, 0, VE, "preset" },
255 { "drawing", "hand or line drawing, with high-contrast details", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DRAWING }, 0, 0, VE, "preset" },
256 { "icon", "small-sized colorful images", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_ICON }, 0, 0, VE, "preset" },
257 { "text", "text-like", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_TEXT }, 0, 0, VE, "preset" },
261 static const AVClass class = {
262 .class_name = "libwebp",
263 .item_name = av_default_item_name,
265 .version = LIBAVUTIL_VERSION_INT,
268 static const AVCodecDefault libwebp_defaults[] = {
269 { "compression_level", "4" },
270 { "global_quality", "-1" },
274 AVCodec ff_libwebp_encoder = {
276 .long_name = NULL_IF_CONFIG_SMALL("libwebp WebP image"),
277 .type = AVMEDIA_TYPE_VIDEO,
278 .id = AV_CODEC_ID_WEBP,
279 .priv_data_size = sizeof(LibWebPContext),
280 .init = libwebp_encode_init,
281 .encode2 = libwebp_encode_frame,
282 .pix_fmts = (const enum AVPixelFormat[]) {
284 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P,
287 .priv_class = &class,
288 .defaults = libwebp_defaults,