diff --git a/libavcodec/libwebpenc.c b/libavcodec/libwebpenc.c index 4cb8dc384e..c4c880fab0 100644 --- a/libavcodec/libwebpenc.c +++ b/libavcodec/libwebpenc.c @@ -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, diff --git a/libavcodec/version.h b/libavcodec/version.h index 98ed86971e..ea9207a63a 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -30,7 +30,7 @@ #define LIBAVCODEC_VERSION_MAJOR 56 #define LIBAVCODEC_VERSION_MINOR 10 -#define LIBAVCODEC_VERSION_MICRO 100 +#define LIBAVCODEC_VERSION_MICRO 101 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \