avutil: Rename FF_CEIL_COMPAT to AV_CEIL_COMPAT
[ffmpeg.git] / libavcodec / libwebpenc_common.c
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 }