|
|
|
@ -41,6 +41,9 @@ typedef struct LibWebPContext { |
|
|
|
|
int chroma_warning; // chroma linesize mismatch warning has been printed
|
|
|
|
|
int conversion_warning; // pixel format conversion warning has been printed
|
|
|
|
|
WebPConfig config; // libwebp configuration
|
|
|
|
|
AVFrame *ref; |
|
|
|
|
int cr_size; |
|
|
|
|
int cr_threshold; |
|
|
|
|
} LibWebPContext; |
|
|
|
|
|
|
|
|
|
static int libwebp_error_to_averror(int err) |
|
|
|
@ -144,8 +147,8 @@ static int libwebp_encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
|
|
|
|
pic->argb = (uint32_t *)frame->data[0]; |
|
|
|
|
pic->argb_stride = frame->linesize[0] / 4; |
|
|
|
|
} else { |
|
|
|
|
if (frame->linesize[1] != frame->linesize[2]) { |
|
|
|
|
if (!s->chroma_warning) { |
|
|
|
|
if (frame->linesize[1] != frame->linesize[2] || s->cr_threshold) { |
|
|
|
|
if (!s->chroma_warning && !s->cr_threshold) { |
|
|
|
|
av_log(avctx, AV_LOG_WARNING, |
|
|
|
|
"Copying frame due to differing chroma linesizes.\n"); |
|
|
|
|
s->chroma_warning = 1; |
|
|
|
@ -158,22 +161,80 @@ static int libwebp_encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
|
|
|
|
alt_frame->width = frame->width; |
|
|
|
|
alt_frame->height = frame->height; |
|
|
|
|
alt_frame->format = frame->format; |
|
|
|
|
if (s->cr_threshold) |
|
|
|
|
alt_frame->format = AV_PIX_FMT_YUVA420P; |
|
|
|
|
ret = av_frame_get_buffer(alt_frame, 32); |
|
|
|
|
if (ret < 0) |
|
|
|
|
goto end; |
|
|
|
|
alt_frame->format = frame->format; |
|
|
|
|
av_frame_copy(alt_frame, frame); |
|
|
|
|
frame = alt_frame; |
|
|
|
|
if (s->cr_threshold) { |
|
|
|
|
int x,y, x2, y2, p; |
|
|
|
|
int bs = s->cr_size; |
|
|
|
|
|
|
|
|
|
if (!s->ref) { |
|
|
|
|
s->ref = av_frame_clone(frame); |
|
|
|
|
if (!s->ref) { |
|
|
|
|
ret = AVERROR(ENOMEM); |
|
|
|
|
goto end; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
alt_frame->format = AV_PIX_FMT_YUVA420P; |
|
|
|
|
for (y = 0; y < frame->height; y+= bs) { |
|
|
|
|
for (x = 0; x < frame->width; x+= bs) { |
|
|
|
|
int skip; |
|
|
|
|
int sse = 0; |
|
|
|
|
for (p = 0; p < 3; p++) { |
|
|
|
|
int bs2 = bs >> !!p; |
|
|
|
|
int w = FF_CEIL_RSHIFT(frame->width , !!p); |
|
|
|
|
int h = FF_CEIL_RSHIFT(frame->height, !!p); |
|
|
|
|
int xs = x >> !!p; |
|
|
|
|
int ys = y >> !!p; |
|
|
|
|
for (y2 = ys; y2 < FFMIN(ys + bs2, h); y2++) { |
|
|
|
|
for (x2 = xs; x2 < FFMIN(xs + bs2, w); x2++) { |
|
|
|
|
int diff = frame->data[p][frame->linesize[p] * y2 + x2] |
|
|
|
|
-s->ref->data[p][frame->linesize[p] * y2 + x2]; |
|
|
|
|
sse += diff*diff; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
skip = sse < s->cr_threshold && frame->data[3] != s->ref->data[3]; |
|
|
|
|
if (!skip) |
|
|
|
|
for (p = 0; p < 3; p++) { |
|
|
|
|
int bs2 = bs >> !!p; |
|
|
|
|
int w = FF_CEIL_RSHIFT(frame->width , !!p); |
|
|
|
|
int h = FF_CEIL_RSHIFT(frame->height, !!p); |
|
|
|
|
int xs = x >> !!p; |
|
|
|
|
int ys = y >> !!p; |
|
|
|
|
for (y2 = ys; y2 < FFMIN(ys + bs2, h); y2++) { |
|
|
|
|
memcpy(&s->ref->data[p][frame->linesize[p] * y2 + xs], |
|
|
|
|
& frame->data[p][frame->linesize[p] * y2 + xs], FFMIN(bs2, w-xs)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (y2 = y; y2 < FFMIN(y+bs, frame->height); y2++) { |
|
|
|
|
memset(&frame->data[3][frame->linesize[3] * y2 + x], |
|
|
|
|
skip ? 0 : 255, |
|
|
|
|
FFMIN(bs, frame->width-x)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pic->use_argb = 0; |
|
|
|
|
pic->y = frame->data[0]; |
|
|
|
|
pic->u = frame->data[1]; |
|
|
|
|
pic->v = frame->data[2]; |
|
|
|
|
pic->y_stride = frame->linesize[0]; |
|
|
|
|
pic->uv_stride = frame->linesize[1]; |
|
|
|
|
if (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) { |
|
|
|
|
if (frame->format == AV_PIX_FMT_YUVA420P) { |
|
|
|
|
pic->colorspace = WEBP_YUV420A; |
|
|
|
|
pic->a = frame->data[3]; |
|
|
|
|
pic->a_stride = frame->linesize[3]; |
|
|
|
|
if (alt_frame) |
|
|
|
|
WebPCleanupTransparentArea(pic); |
|
|
|
|
} else { |
|
|
|
|
pic->colorspace = WEBP_YUV420; |
|
|
|
|
} |
|
|
|
@ -243,6 +304,15 @@ end: |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int libwebp_encode_close(AVCodecContext *avctx) |
|
|
|
|
{ |
|
|
|
|
LibWebPContext *s = avctx->priv_data; |
|
|
|
|
|
|
|
|
|
av_frame_free(&s->ref); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#define OFFSET(x) offsetof(LibWebPContext, x) |
|
|
|
|
#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM |
|
|
|
|
static const AVOption options[] = { |
|
|
|
@ -255,9 +325,12 @@ static const AVOption options[] = { |
|
|
|
|
{ "drawing", "hand or line drawing, with high-contrast details", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DRAWING }, 0, 0, VE, "preset" }, |
|
|
|
|
{ "icon", "small-sized colorful images", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_ICON }, 0, 0, VE, "preset" }, |
|
|
|
|
{ "text", "text-like", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_TEXT }, 0, 0, VE, "preset" }, |
|
|
|
|
{ "cr_threshold","Conditional replenishment threshold", OFFSET(cr_threshold), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, |
|
|
|
|
{ "cr_size" ,"Conditional replenishment block size", OFFSET(cr_size) , AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 256, VE }, |
|
|
|
|
{ NULL }, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const AVClass class = { |
|
|
|
|
.class_name = "libwebp", |
|
|
|
|
.item_name = av_default_item_name, |
|
|
|
@ -279,6 +352,7 @@ AVCodec ff_libwebp_encoder = { |
|
|
|
|
.priv_data_size = sizeof(LibWebPContext), |
|
|
|
|
.init = libwebp_encode_init, |
|
|
|
|
.encode2 = libwebp_encode_frame, |
|
|
|
|
.close = libwebp_encode_close, |
|
|
|
|
.pix_fmts = (const enum AVPixelFormat[]) { |
|
|
|
|
AV_PIX_FMT_RGB32, |
|
|
|
|
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, |
|
|
|
|