blob: 21d7adaf56ffee5b88daaa6f242df75eda26bbf2
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: common structs and methods. |
25 | */ |
26 | |
27 | #include "libwebpenc_common.h" |
28 | |
29 | int ff_libwebp_error_to_averror(int err) |
30 | { |
31 | switch (err) { |
32 | case VP8_ENC_ERROR_OUT_OF_MEMORY: |
33 | case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY: |
34 | return AVERROR(ENOMEM); |
35 | case VP8_ENC_ERROR_NULL_PARAMETER: |
36 | case VP8_ENC_ERROR_INVALID_CONFIGURATION: |
37 | case VP8_ENC_ERROR_BAD_DIMENSION: |
38 | return AVERROR(EINVAL); |
39 | } |
40 | return AVERROR_UNKNOWN; |
41 | } |
42 | |
43 | av_cold int ff_libwebp_encode_init_common(AVCodecContext *avctx) |
44 | { |
45 | LibWebPContextCommon *s = avctx->priv_data; |
46 | int ret; |
47 | |
48 | if (avctx->global_quality >= 0) |
49 | s->quality = av_clipf(avctx->global_quality / (float)FF_QP2LAMBDA, |
50 | 0.0f, 100.0f); |
51 | |
52 | if (avctx->compression_level < 0 || avctx->compression_level > 6) { |
53 | av_log(avctx, AV_LOG_WARNING, "invalid compression level: %d\n", |
54 | avctx->compression_level); |
55 | avctx->compression_level = av_clip(avctx->compression_level, 0, 6); |
56 | } |
57 | |
58 | if (s->preset >= WEBP_PRESET_DEFAULT) { |
59 | ret = WebPConfigPreset(&s->config, s->preset, s->quality); |
60 | if (!ret) |
61 | return AVERROR_UNKNOWN; |
62 | s->lossless = s->config.lossless; |
63 | s->quality = s->config.quality; |
64 | avctx->compression_level = s->config.method; |
65 | } else { |
66 | ret = WebPConfigInit(&s->config); |
67 | if (!ret) |
68 | return AVERROR_UNKNOWN; |
69 | |
70 | s->config.lossless = s->lossless; |
71 | s->config.quality = s->quality; |
72 | s->config.method = avctx->compression_level; |
73 | |
74 | ret = WebPValidateConfig(&s->config); |
75 | if (!ret) |
76 | return AVERROR(EINVAL); |
77 | } |
78 | |
79 | av_log(avctx, AV_LOG_DEBUG, "%s - quality=%.1f method=%d\n", |
80 | s->lossless ? "Lossless" : "Lossy", s->quality, |
81 | avctx->compression_level); |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | int ff_libwebp_get_frame(AVCodecContext *avctx, LibWebPContextCommon *s, |
87 | const AVFrame *frame, AVFrame **alt_frame_ptr, |
88 | WebPPicture **pic_ptr) { |
89 | int ret; |
90 | WebPPicture *pic = NULL; |
91 | AVFrame *alt_frame = NULL; |
92 | |
93 | if (avctx->width > WEBP_MAX_DIMENSION || avctx->height > WEBP_MAX_DIMENSION) { |
94 | av_log(avctx, AV_LOG_ERROR, "Picture size is too large. Max is %dx%d.\n", |
95 | WEBP_MAX_DIMENSION, WEBP_MAX_DIMENSION); |
96 | return AVERROR(EINVAL); |
97 | } |
98 | |
99 | *pic_ptr = av_malloc(sizeof(*pic)); |
100 | pic = *pic_ptr; |
101 | if (!pic) |
102 | return AVERROR(ENOMEM); |
103 | |
104 | ret = WebPPictureInit(pic); |
105 | if (!ret) { |
106 | ret = AVERROR_UNKNOWN; |
107 | goto end; |
108 | } |
109 | pic->width = avctx->width; |
110 | pic->height = avctx->height; |
111 | |
112 | if (avctx->pix_fmt == AV_PIX_FMT_RGB32) { |
113 | if (!s->lossless) { |
114 | /* libwebp will automatically convert RGB input to YUV when |
115 | encoding lossy. */ |
116 | if (!s->conversion_warning) { |
117 | av_log(avctx, AV_LOG_WARNING, |
118 | "Using libwebp for RGB-to-YUV conversion. You may want " |
119 | "to consider passing in YUV instead for lossy " |
120 | "encoding.\n"); |
121 | s->conversion_warning = 1; |
122 | } |
123 | } |
124 | pic->use_argb = 1; |
125 | pic->argb = (uint32_t *)frame->data[0]; |
126 | pic->argb_stride = frame->linesize[0] / 4; |
127 | } else { |
128 | if (frame->linesize[1] != frame->linesize[2] || s->cr_threshold) { |
129 | if (!s->chroma_warning && !s->cr_threshold) { |
130 | av_log(avctx, AV_LOG_WARNING, |
131 | "Copying frame due to differing chroma linesizes.\n"); |
132 | s->chroma_warning = 1; |
133 | } |
134 | *alt_frame_ptr = av_frame_alloc(); |
135 | alt_frame = *alt_frame_ptr; |
136 | if (!alt_frame) { |
137 | ret = AVERROR(ENOMEM); |
138 | goto end; |
139 | } |
140 | alt_frame->width = frame->width; |
141 | alt_frame->height = frame->height; |
142 | alt_frame->format = frame->format; |
143 | if (s->cr_threshold) |
144 | alt_frame->format = AV_PIX_FMT_YUVA420P; |
145 | ret = av_frame_get_buffer(alt_frame, 32); |
146 | if (ret < 0) |
147 | goto end; |
148 | alt_frame->format = frame->format; |
149 | av_frame_copy(alt_frame, frame); |
150 | frame = alt_frame; |
151 | if (s->cr_threshold) { |
152 | int x,y, x2, y2, p; |
153 | int bs = s->cr_size; |
154 | |
155 | if (!s->ref) { |
156 | s->ref = av_frame_clone(frame); |
157 | if (!s->ref) { |
158 | ret = AVERROR(ENOMEM); |
159 | goto end; |
160 | } |
161 | } |
162 | |
163 | alt_frame->format = AV_PIX_FMT_YUVA420P; |
164 | for (y = 0; y < frame->height; y+= bs) { |
165 | for (x = 0; x < frame->width; x+= bs) { |
166 | int skip; |
167 | int sse = 0; |
168 | for (p = 0; p < 3; p++) { |
169 | int bs2 = bs >> !!p; |
170 | int w = AV_CEIL_RSHIFT(frame->width , !!p); |
171 | int h = AV_CEIL_RSHIFT(frame->height, !!p); |
172 | int xs = x >> !!p; |
173 | int ys = y >> !!p; |
174 | for (y2 = ys; y2 < FFMIN(ys + bs2, h); y2++) { |
175 | for (x2 = xs; x2 < FFMIN(xs + bs2, w); x2++) { |
176 | int diff = frame->data[p][frame->linesize[p] * y2 + x2] |
177 | -s->ref->data[p][frame->linesize[p] * y2 + x2]; |
178 | sse += diff*diff; |
179 | } |
180 | } |
181 | } |
182 | skip = sse < s->cr_threshold && frame->data[3] != s->ref->data[3]; |
183 | if (!skip) |
184 | for (p = 0; p < 3; p++) { |
185 | int bs2 = bs >> !!p; |
186 | int w = AV_CEIL_RSHIFT(frame->width , !!p); |
187 | int h = AV_CEIL_RSHIFT(frame->height, !!p); |
188 | int xs = x >> !!p; |
189 | int ys = y >> !!p; |
190 | for (y2 = ys; y2 < FFMIN(ys + bs2, h); y2++) { |
191 | memcpy(&s->ref->data[p][frame->linesize[p] * y2 + xs], |
192 | & frame->data[p][frame->linesize[p] * y2 + xs], FFMIN(bs2, w-xs)); |
193 | } |
194 | } |
195 | for (y2 = y; y2 < FFMIN(y+bs, frame->height); y2++) { |
196 | memset(&frame->data[3][frame->linesize[3] * y2 + x], |
197 | skip ? 0 : 255, |
198 | FFMIN(bs, frame->width-x)); |
199 | } |
200 | } |
201 | } |
202 | } |
203 | } |
204 | |
205 | pic->use_argb = 0; |
206 | pic->y = frame->data[0]; |
207 | pic->u = frame->data[1]; |
208 | pic->v = frame->data[2]; |
209 | pic->y_stride = frame->linesize[0]; |
210 | pic->uv_stride = frame->linesize[1]; |
211 | if (frame->format == AV_PIX_FMT_YUVA420P) { |
212 | pic->colorspace = WEBP_YUV420A; |
213 | pic->a = frame->data[3]; |
214 | pic->a_stride = frame->linesize[3]; |
215 | if (alt_frame) |
216 | WebPCleanupTransparentArea(pic); |
217 | } else { |
218 | pic->colorspace = WEBP_YUV420; |
219 | } |
220 | |
221 | if (s->lossless) { |
222 | /* We do not have a way to automatically prioritize RGB over YUV |
223 | in automatic pixel format conversion based on whether we're |
224 | encoding lossless or lossy, so we do conversion with libwebp as |
225 | a convenience. */ |
226 | if (!s->conversion_warning) { |
227 | av_log(avctx, AV_LOG_WARNING, |
228 | "Using libwebp for YUV-to-RGB conversion. You may want " |
229 | "to consider passing in RGB instead for lossless " |
230 | "encoding.\n"); |
231 | s->conversion_warning = 1; |
232 | } |
233 | |
234 | #if (WEBP_ENCODER_ABI_VERSION <= 0x201) |
235 | /* libwebp should do the conversion automatically, but there is a |
236 | bug that causes it to return an error instead, so a work-around |
237 | is required. |
238 | See https://code.google.com/p/webp/issues/detail?id=178 */ |
239 | pic->memory_ = (void*)1; /* something non-null */ |
240 | ret = WebPPictureYUVAToARGB(pic); |
241 | if (!ret) { |
242 | av_log(avctx, AV_LOG_ERROR, |
243 | "WebPPictureYUVAToARGB() failed with error: %d\n", |
244 | pic->error_code); |
245 | ret = libwebp_error_to_averror(pic->error_code); |
246 | goto end; |
247 | } |
248 | pic->memory_ = NULL; /* restore pointer */ |
249 | #endif |
250 | } |
251 | } |
252 | end: |
253 | return ret; |
254 | } |
255 |