1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * WebP encoding support via libwebp 3cabdff1aSopenharmony_ci * Copyright (c) 2013 Justin Ruggles <justin.ruggles@gmail.com> 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci/** 23cabdff1aSopenharmony_ci * @file 24cabdff1aSopenharmony_ci * WebP encoder using libwebp: common structs and methods. 25cabdff1aSopenharmony_ci */ 26cabdff1aSopenharmony_ci 27cabdff1aSopenharmony_ci#include "libavutil/opt.h" 28cabdff1aSopenharmony_ci#include "libwebpenc_common.h" 29cabdff1aSopenharmony_ci 30cabdff1aSopenharmony_ciconst FFCodecDefault ff_libwebp_defaults[] = { 31cabdff1aSopenharmony_ci { "compression_level", "4" }, 32cabdff1aSopenharmony_ci { "global_quality", "-1" }, 33cabdff1aSopenharmony_ci { NULL }, 34cabdff1aSopenharmony_ci}; 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(LibWebPContextCommon, x) 37cabdff1aSopenharmony_ci#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM 38cabdff1aSopenharmony_cistatic const AVOption options[] = { 39cabdff1aSopenharmony_ci { "lossless", "Use lossless mode", OFFSET(lossless), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, 40cabdff1aSopenharmony_ci { "preset", "Configuration preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, WEBP_PRESET_TEXT, VE, "preset" }, 41cabdff1aSopenharmony_ci { "none", "do not use a preset", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, "preset" }, 42cabdff1aSopenharmony_ci { "default", "default preset", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DEFAULT }, 0, 0, VE, "preset" }, 43cabdff1aSopenharmony_ci { "picture", "digital picture, like portrait, inner shot", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PICTURE }, 0, 0, VE, "preset" }, 44cabdff1aSopenharmony_ci { "photo", "outdoor photograph, with natural lighting", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PHOTO }, 0, 0, VE, "preset" }, 45cabdff1aSopenharmony_ci { "drawing", "hand or line drawing, with high-contrast details", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DRAWING }, 0, 0, VE, "preset" }, 46cabdff1aSopenharmony_ci { "icon", "small-sized colorful images", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_ICON }, 0, 0, VE, "preset" }, 47cabdff1aSopenharmony_ci { "text", "text-like", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_TEXT }, 0, 0, VE, "preset" }, 48cabdff1aSopenharmony_ci { "cr_threshold","Conditional replenishment threshold", OFFSET(cr_threshold), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, 49cabdff1aSopenharmony_ci { "cr_size" ,"Conditional replenishment block size", OFFSET(cr_size) , AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 256, VE }, 50cabdff1aSopenharmony_ci { "quality" ,"Quality", OFFSET(quality), AV_OPT_TYPE_FLOAT, { .dbl = 75 }, 0, 100, VE }, 51cabdff1aSopenharmony_ci { NULL }, 52cabdff1aSopenharmony_ci}; 53cabdff1aSopenharmony_ci 54cabdff1aSopenharmony_ciconst AVClass ff_libwebpenc_class = { 55cabdff1aSopenharmony_ci .class_name = "libwebp encoder", 56cabdff1aSopenharmony_ci .item_name = av_default_item_name, 57cabdff1aSopenharmony_ci .option = options, 58cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 59cabdff1aSopenharmony_ci}; 60cabdff1aSopenharmony_ci 61cabdff1aSopenharmony_ciconst enum AVPixelFormat ff_libwebpenc_pix_fmts[] = { 62cabdff1aSopenharmony_ci AV_PIX_FMT_RGB32, 63cabdff1aSopenharmony_ci AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, 64cabdff1aSopenharmony_ci AV_PIX_FMT_NONE 65cabdff1aSopenharmony_ci}; 66cabdff1aSopenharmony_ci 67cabdff1aSopenharmony_ciint ff_libwebp_error_to_averror(int err) 68cabdff1aSopenharmony_ci{ 69cabdff1aSopenharmony_ci switch (err) { 70cabdff1aSopenharmony_ci case VP8_ENC_ERROR_OUT_OF_MEMORY: 71cabdff1aSopenharmony_ci case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY: 72cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 73cabdff1aSopenharmony_ci case VP8_ENC_ERROR_NULL_PARAMETER: 74cabdff1aSopenharmony_ci case VP8_ENC_ERROR_INVALID_CONFIGURATION: 75cabdff1aSopenharmony_ci case VP8_ENC_ERROR_BAD_DIMENSION: 76cabdff1aSopenharmony_ci return AVERROR(EINVAL); 77cabdff1aSopenharmony_ci } 78cabdff1aSopenharmony_ci return AVERROR_UNKNOWN; 79cabdff1aSopenharmony_ci} 80cabdff1aSopenharmony_ci 81cabdff1aSopenharmony_ciav_cold int ff_libwebp_encode_init_common(AVCodecContext *avctx) 82cabdff1aSopenharmony_ci{ 83cabdff1aSopenharmony_ci LibWebPContextCommon *s = avctx->priv_data; 84cabdff1aSopenharmony_ci int ret; 85cabdff1aSopenharmony_ci 86cabdff1aSopenharmony_ci if (avctx->global_quality >= 0) 87cabdff1aSopenharmony_ci s->quality = av_clipf(avctx->global_quality / (float)FF_QP2LAMBDA, 88cabdff1aSopenharmony_ci 0.0f, 100.0f); 89cabdff1aSopenharmony_ci 90cabdff1aSopenharmony_ci if (avctx->compression_level < 0 || avctx->compression_level > 6) { 91cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "invalid compression level: %d\n", 92cabdff1aSopenharmony_ci avctx->compression_level); 93cabdff1aSopenharmony_ci avctx->compression_level = av_clip(avctx->compression_level, 0, 6); 94cabdff1aSopenharmony_ci } 95cabdff1aSopenharmony_ci 96cabdff1aSopenharmony_ci if (s->preset >= WEBP_PRESET_DEFAULT) { 97cabdff1aSopenharmony_ci ret = WebPConfigPreset(&s->config, s->preset, s->quality); 98cabdff1aSopenharmony_ci if (!ret) 99cabdff1aSopenharmony_ci return AVERROR_UNKNOWN; 100cabdff1aSopenharmony_ci s->lossless = s->config.lossless; 101cabdff1aSopenharmony_ci s->quality = s->config.quality; 102cabdff1aSopenharmony_ci avctx->compression_level = s->config.method; 103cabdff1aSopenharmony_ci } else { 104cabdff1aSopenharmony_ci ret = WebPConfigInit(&s->config); 105cabdff1aSopenharmony_ci if (!ret) 106cabdff1aSopenharmony_ci return AVERROR_UNKNOWN; 107cabdff1aSopenharmony_ci 108cabdff1aSopenharmony_ci s->config.lossless = s->lossless; 109cabdff1aSopenharmony_ci s->config.quality = s->quality; 110cabdff1aSopenharmony_ci s->config.method = avctx->compression_level; 111cabdff1aSopenharmony_ci 112cabdff1aSopenharmony_ci ret = WebPValidateConfig(&s->config); 113cabdff1aSopenharmony_ci if (!ret) 114cabdff1aSopenharmony_ci return AVERROR(EINVAL); 115cabdff1aSopenharmony_ci } 116cabdff1aSopenharmony_ci 117cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_DEBUG, "%s - quality=%.1f method=%d\n", 118cabdff1aSopenharmony_ci s->lossless ? "Lossless" : "Lossy", s->quality, 119cabdff1aSopenharmony_ci avctx->compression_level); 120cabdff1aSopenharmony_ci 121cabdff1aSopenharmony_ci return 0; 122cabdff1aSopenharmony_ci} 123cabdff1aSopenharmony_ci 124cabdff1aSopenharmony_ciint ff_libwebp_get_frame(AVCodecContext *avctx, LibWebPContextCommon *s, 125cabdff1aSopenharmony_ci const AVFrame *frame, AVFrame **alt_frame_ptr, 126cabdff1aSopenharmony_ci WebPPicture **pic_ptr) { 127cabdff1aSopenharmony_ci int ret; 128cabdff1aSopenharmony_ci WebPPicture *pic = NULL; 129cabdff1aSopenharmony_ci AVFrame *alt_frame = NULL; 130cabdff1aSopenharmony_ci 131cabdff1aSopenharmony_ci if (avctx->width > WEBP_MAX_DIMENSION || avctx->height > WEBP_MAX_DIMENSION) { 132cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Picture size is too large. Max is %dx%d.\n", 133cabdff1aSopenharmony_ci WEBP_MAX_DIMENSION, WEBP_MAX_DIMENSION); 134cabdff1aSopenharmony_ci return AVERROR(EINVAL); 135cabdff1aSopenharmony_ci } 136cabdff1aSopenharmony_ci 137cabdff1aSopenharmony_ci *pic_ptr = av_malloc(sizeof(*pic)); 138cabdff1aSopenharmony_ci pic = *pic_ptr; 139cabdff1aSopenharmony_ci if (!pic) 140cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 141cabdff1aSopenharmony_ci 142cabdff1aSopenharmony_ci ret = WebPPictureInit(pic); 143cabdff1aSopenharmony_ci if (!ret) { 144cabdff1aSopenharmony_ci ret = AVERROR_UNKNOWN; 145cabdff1aSopenharmony_ci goto end; 146cabdff1aSopenharmony_ci } 147cabdff1aSopenharmony_ci pic->width = avctx->width; 148cabdff1aSopenharmony_ci pic->height = avctx->height; 149cabdff1aSopenharmony_ci 150cabdff1aSopenharmony_ci if (avctx->pix_fmt == AV_PIX_FMT_RGB32) { 151cabdff1aSopenharmony_ci if (!s->lossless) { 152cabdff1aSopenharmony_ci /* libwebp will automatically convert RGB input to YUV when 153cabdff1aSopenharmony_ci encoding lossy. */ 154cabdff1aSopenharmony_ci if (!s->conversion_warning) { 155cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, 156cabdff1aSopenharmony_ci "Using libwebp for RGB-to-YUV conversion. You may want " 157cabdff1aSopenharmony_ci "to consider passing in YUV instead for lossy " 158cabdff1aSopenharmony_ci "encoding.\n"); 159cabdff1aSopenharmony_ci s->conversion_warning = 1; 160cabdff1aSopenharmony_ci } 161cabdff1aSopenharmony_ci } 162cabdff1aSopenharmony_ci pic->use_argb = 1; 163cabdff1aSopenharmony_ci pic->argb = (uint32_t *)frame->data[0]; 164cabdff1aSopenharmony_ci pic->argb_stride = frame->linesize[0] / 4; 165cabdff1aSopenharmony_ci } else { 166cabdff1aSopenharmony_ci if (frame->linesize[1] != frame->linesize[2] || s->cr_threshold) { 167cabdff1aSopenharmony_ci if (!s->chroma_warning && !s->cr_threshold) { 168cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, 169cabdff1aSopenharmony_ci "Copying frame due to differing chroma linesizes.\n"); 170cabdff1aSopenharmony_ci s->chroma_warning = 1; 171cabdff1aSopenharmony_ci } 172cabdff1aSopenharmony_ci *alt_frame_ptr = av_frame_alloc(); 173cabdff1aSopenharmony_ci alt_frame = *alt_frame_ptr; 174cabdff1aSopenharmony_ci if (!alt_frame) { 175cabdff1aSopenharmony_ci ret = AVERROR(ENOMEM); 176cabdff1aSopenharmony_ci goto end; 177cabdff1aSopenharmony_ci } 178cabdff1aSopenharmony_ci alt_frame->width = frame->width; 179cabdff1aSopenharmony_ci alt_frame->height = frame->height; 180cabdff1aSopenharmony_ci alt_frame->format = frame->format; 181cabdff1aSopenharmony_ci if (s->cr_threshold) 182cabdff1aSopenharmony_ci alt_frame->format = AV_PIX_FMT_YUVA420P; 183cabdff1aSopenharmony_ci ret = av_frame_get_buffer(alt_frame, 0); 184cabdff1aSopenharmony_ci if (ret < 0) 185cabdff1aSopenharmony_ci goto end; 186cabdff1aSopenharmony_ci alt_frame->format = frame->format; 187cabdff1aSopenharmony_ci av_frame_copy(alt_frame, frame); 188cabdff1aSopenharmony_ci frame = alt_frame; 189cabdff1aSopenharmony_ci if (s->cr_threshold) { 190cabdff1aSopenharmony_ci int x,y, x2, y2, p; 191cabdff1aSopenharmony_ci int bs = s->cr_size; 192cabdff1aSopenharmony_ci 193cabdff1aSopenharmony_ci if (!s->ref) { 194cabdff1aSopenharmony_ci s->ref = av_frame_clone(frame); 195cabdff1aSopenharmony_ci if (!s->ref) { 196cabdff1aSopenharmony_ci ret = AVERROR(ENOMEM); 197cabdff1aSopenharmony_ci goto end; 198cabdff1aSopenharmony_ci } 199cabdff1aSopenharmony_ci } 200cabdff1aSopenharmony_ci 201cabdff1aSopenharmony_ci alt_frame->format = AV_PIX_FMT_YUVA420P; 202cabdff1aSopenharmony_ci for (y = 0; y < frame->height; y+= bs) { 203cabdff1aSopenharmony_ci for (x = 0; x < frame->width; x+= bs) { 204cabdff1aSopenharmony_ci int skip; 205cabdff1aSopenharmony_ci int sse = 0; 206cabdff1aSopenharmony_ci for (p = 0; p < 3; p++) { 207cabdff1aSopenharmony_ci int bs2 = bs >> !!p; 208cabdff1aSopenharmony_ci int w = AV_CEIL_RSHIFT(frame->width , !!p); 209cabdff1aSopenharmony_ci int h = AV_CEIL_RSHIFT(frame->height, !!p); 210cabdff1aSopenharmony_ci int xs = x >> !!p; 211cabdff1aSopenharmony_ci int ys = y >> !!p; 212cabdff1aSopenharmony_ci for (y2 = ys; y2 < FFMIN(ys + bs2, h); y2++) { 213cabdff1aSopenharmony_ci for (x2 = xs; x2 < FFMIN(xs + bs2, w); x2++) { 214cabdff1aSopenharmony_ci int diff = frame->data[p][frame->linesize[p] * y2 + x2] 215cabdff1aSopenharmony_ci -s->ref->data[p][frame->linesize[p] * y2 + x2]; 216cabdff1aSopenharmony_ci sse += diff*diff; 217cabdff1aSopenharmony_ci } 218cabdff1aSopenharmony_ci } 219cabdff1aSopenharmony_ci } 220cabdff1aSopenharmony_ci skip = sse < s->cr_threshold && frame->data[3] != s->ref->data[3]; 221cabdff1aSopenharmony_ci if (!skip) 222cabdff1aSopenharmony_ci for (p = 0; p < 3; p++) { 223cabdff1aSopenharmony_ci int bs2 = bs >> !!p; 224cabdff1aSopenharmony_ci int w = AV_CEIL_RSHIFT(frame->width , !!p); 225cabdff1aSopenharmony_ci int h = AV_CEIL_RSHIFT(frame->height, !!p); 226cabdff1aSopenharmony_ci int xs = x >> !!p; 227cabdff1aSopenharmony_ci int ys = y >> !!p; 228cabdff1aSopenharmony_ci for (y2 = ys; y2 < FFMIN(ys + bs2, h); y2++) { 229cabdff1aSopenharmony_ci memcpy(&s->ref->data[p][frame->linesize[p] * y2 + xs], 230cabdff1aSopenharmony_ci & frame->data[p][frame->linesize[p] * y2 + xs], FFMIN(bs2, w-xs)); 231cabdff1aSopenharmony_ci } 232cabdff1aSopenharmony_ci } 233cabdff1aSopenharmony_ci for (y2 = y; y2 < FFMIN(y+bs, frame->height); y2++) { 234cabdff1aSopenharmony_ci memset(&frame->data[3][frame->linesize[3] * y2 + x], 235cabdff1aSopenharmony_ci skip ? 0 : 255, 236cabdff1aSopenharmony_ci FFMIN(bs, frame->width-x)); 237cabdff1aSopenharmony_ci } 238cabdff1aSopenharmony_ci } 239cabdff1aSopenharmony_ci } 240cabdff1aSopenharmony_ci } 241cabdff1aSopenharmony_ci } 242cabdff1aSopenharmony_ci 243cabdff1aSopenharmony_ci pic->use_argb = 0; 244cabdff1aSopenharmony_ci pic->y = frame->data[0]; 245cabdff1aSopenharmony_ci pic->u = frame->data[1]; 246cabdff1aSopenharmony_ci pic->v = frame->data[2]; 247cabdff1aSopenharmony_ci pic->y_stride = frame->linesize[0]; 248cabdff1aSopenharmony_ci pic->uv_stride = frame->linesize[1]; 249cabdff1aSopenharmony_ci if (frame->format == AV_PIX_FMT_YUVA420P) { 250cabdff1aSopenharmony_ci pic->colorspace = WEBP_YUV420A; 251cabdff1aSopenharmony_ci pic->a = frame->data[3]; 252cabdff1aSopenharmony_ci pic->a_stride = frame->linesize[3]; 253cabdff1aSopenharmony_ci if (alt_frame) 254cabdff1aSopenharmony_ci WebPCleanupTransparentArea(pic); 255cabdff1aSopenharmony_ci } else { 256cabdff1aSopenharmony_ci pic->colorspace = WEBP_YUV420; 257cabdff1aSopenharmony_ci } 258cabdff1aSopenharmony_ci 259cabdff1aSopenharmony_ci if (s->lossless) { 260cabdff1aSopenharmony_ci /* We do not have a way to automatically prioritize RGB over YUV 261cabdff1aSopenharmony_ci in automatic pixel format conversion based on whether we're 262cabdff1aSopenharmony_ci encoding lossless or lossy, so we do conversion with libwebp as 263cabdff1aSopenharmony_ci a convenience. */ 264cabdff1aSopenharmony_ci if (!s->conversion_warning) { 265cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, 266cabdff1aSopenharmony_ci "Using libwebp for YUV-to-RGB conversion. You may want " 267cabdff1aSopenharmony_ci "to consider passing in RGB instead for lossless " 268cabdff1aSopenharmony_ci "encoding.\n"); 269cabdff1aSopenharmony_ci s->conversion_warning = 1; 270cabdff1aSopenharmony_ci } 271cabdff1aSopenharmony_ci 272cabdff1aSopenharmony_ci#if (WEBP_ENCODER_ABI_VERSION <= 0x201) 273cabdff1aSopenharmony_ci /* libwebp should do the conversion automatically, but there is a 274cabdff1aSopenharmony_ci bug that causes it to return an error instead, so a work-around 275cabdff1aSopenharmony_ci is required. 276cabdff1aSopenharmony_ci See https://code.google.com/p/webp/issues/detail?id=178 */ 277cabdff1aSopenharmony_ci pic->memory_ = (void*)1; /* something non-null */ 278cabdff1aSopenharmony_ci ret = WebPPictureYUVAToARGB(pic); 279cabdff1aSopenharmony_ci if (!ret) { 280cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 281cabdff1aSopenharmony_ci "WebPPictureYUVAToARGB() failed with error: %d\n", 282cabdff1aSopenharmony_ci pic->error_code); 283cabdff1aSopenharmony_ci ret = libwebp_error_to_averror(pic->error_code); 284cabdff1aSopenharmony_ci goto end; 285cabdff1aSopenharmony_ci } 286cabdff1aSopenharmony_ci pic->memory_ = NULL; /* restore pointer */ 287cabdff1aSopenharmony_ci#endif 288cabdff1aSopenharmony_ci } 289cabdff1aSopenharmony_ci } 290cabdff1aSopenharmony_ciend: 291cabdff1aSopenharmony_ci return ret; 292cabdff1aSopenharmony_ci} 293