image_writer: add WebP support (lossy or lossless)

This commit is contained in:
sfan5 2019-09-14 22:18:02 +02:00
parent 8925f10962
commit 0f79444c6d
4 changed files with 55 additions and 7 deletions

View File

@ -3319,6 +3319,7 @@ Screenshot
:png: PNG :png: PNG
:jpg: JPEG (default) :jpg: JPEG (default)
:jpeg: JPEG (alias for jpg) :jpeg: JPEG (alias for jpg)
:webp: WebP
``--screenshot-tag-colorspace=<yes|no>`` ``--screenshot-tag-colorspace=<yes|no>``
Tag screenshots with the appropriate colorspace. Tag screenshots with the appropriate colorspace.
@ -3452,6 +3453,13 @@ Screenshot
of compression that can be achieved. For most images, "mixed" achieves the of compression that can be achieved. For most images, "mixed" achieves the
best compression ratio, hence it is the default. best compression ratio, hence it is the default.
``--screenshot-webp-lossless=<yes|no>``
Write lossless WebP files. ``--screenshot-webp-quality`` is ignored if this
is set. The default is no.
``--screenshot-webp-quality=<0-100>``
Set the WebP quality level. Higher means better quality. The default is 75.
Software Scaler Software Scaler
--------------- ---------------

View File

@ -415,6 +415,8 @@ Available video output drivers are:
JPEG files, extension .jpeg. JPEG files, extension .jpeg.
png png
PNG files. PNG files.
webp
WebP files.
``--vo-image-png-compression=<0-9>`` ``--vo-image-png-compression=<0-9>``
PNG compression factor (speed vs. file size tradeoff) (default: 7) PNG compression factor (speed vs. file size tradeoff) (default: 7)
@ -425,6 +427,10 @@ Available video output drivers are:
JPEG quality factor (default: 90) JPEG quality factor (default: 90)
``--vo-image-jpeg-optimize=<0-100>`` ``--vo-image-jpeg-optimize=<0-100>``
JPEG optimization factor (default: 100) JPEG optimization factor (default: 100)
``--vo-image-webp-lossless=<yes|no>``
Enable writing lossless WebP files (default: no)
``--vo-image-webp-quality=<0-100>``
WebP quality (default: 75)
``--vo-image-outdir=<dirname>`` ``--vo-image-outdir=<dirname>``
Specify the directory to save the image files to (default: ``./``). Specify the directory to save the image files to (default: ``./``).

View File

@ -48,6 +48,8 @@ const struct image_writer_opts image_writer_opts_defaults = {
.png_filter = 5, .png_filter = 5,
.jpeg_quality = 90, .jpeg_quality = 90,
.jpeg_source_chroma = 1, .jpeg_source_chroma = 1,
.webp_lossless = 0,
.webp_quality = 75,
.tag_csp = 0, .tag_csp = 0,
}; };
@ -55,6 +57,7 @@ const struct m_opt_choice_alternatives mp_image_writer_formats[] = {
{"jpg", AV_CODEC_ID_MJPEG}, {"jpg", AV_CODEC_ID_MJPEG},
{"jpeg", AV_CODEC_ID_MJPEG}, {"jpeg", AV_CODEC_ID_MJPEG},
{"png", AV_CODEC_ID_PNG}, {"png", AV_CODEC_ID_PNG},
{"webp", AV_CODEC_ID_WEBP},
{0} {0}
}; };
@ -66,6 +69,8 @@ const struct m_option image_writer_opts[] = {
OPT_FLAG("jpeg-source-chroma", jpeg_source_chroma, 0), OPT_FLAG("jpeg-source-chroma", jpeg_source_chroma, 0),
OPT_INTRANGE("png-compression", png_compression, 0, 0, 9), OPT_INTRANGE("png-compression", png_compression, 0, 0, 9),
OPT_INTRANGE("png-filter", png_filter, 0, 0, 5), OPT_INTRANGE("png-filter", png_filter, 0, 0, 5),
OPT_FLAG("webp-lossless", webp_lossless, 0),
OPT_INTRANGE("webp-quality", webp_quality, 0, 0, 100),
OPT_FLAG("high-bit-depth", high_bit_depth, 0), OPT_FLAG("high-bit-depth", high_bit_depth, 0),
OPT_FLAG("tag-colorspace", tag_csp, 0), OPT_FLAG("tag-colorspace", tag_csp, 0),
{0}, {0},
@ -96,7 +101,13 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
av_init_packet(&pkt); av_init_packet(&pkt);
struct AVCodec *codec = avcodec_find_encoder(ctx->opts->format); struct AVCodec *codec;
if (ctx->opts->format == AV_CODEC_ID_WEBP) {
codec = avcodec_find_encoder_by_name("libwebp"); // non-animated encoder
} else {
codec = avcodec_find_encoder(ctx->opts->format);
}
AVCodecContext *avctx = NULL; AVCodecContext *avctx = NULL;
if (!codec) if (!codec)
goto print_open_fail; goto print_open_fail;
@ -109,9 +120,11 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
avctx->height = image->h; avctx->height = image->h;
avctx->color_range = mp_csp_levels_to_avcol_range(image->params.color.levels); avctx->color_range = mp_csp_levels_to_avcol_range(image->params.color.levels);
avctx->pix_fmt = imgfmt2pixfmt(image->imgfmt); avctx->pix_fmt = imgfmt2pixfmt(image->imgfmt);
// Annoying deprecated garbage for the jpg encoder. if (codec->id == AV_CODEC_ID_MJPEG) {
if (image->params.color.levels == MP_CSP_LEVELS_PC) // Annoying deprecated garbage for the jpg encoder.
avctx->pix_fmt = replace_j_format(avctx->pix_fmt); if (image->params.color.levels == MP_CSP_LEVELS_PC)
avctx->pix_fmt = replace_j_format(avctx->pix_fmt);
}
if (avctx->pix_fmt == AV_PIX_FMT_NONE) { if (avctx->pix_fmt == AV_PIX_FMT_NONE) {
MP_ERR(ctx, "Image format %s not supported by lavc.\n", MP_ERR(ctx, "Image format %s not supported by lavc.\n",
mp_imgfmt_to_name(image->imgfmt)); mp_imgfmt_to_name(image->imgfmt));
@ -121,6 +134,11 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
avctx->compression_level = ctx->opts->png_compression; avctx->compression_level = ctx->opts->png_compression;
av_opt_set_int(avctx, "pred", ctx->opts->png_filter, av_opt_set_int(avctx, "pred", ctx->opts->png_filter,
AV_OPT_SEARCH_CHILDREN); AV_OPT_SEARCH_CHILDREN);
} else if (codec->id == AV_CODEC_ID_WEBP) {
av_opt_set_int(avctx, "lossless", ctx->opts->webp_lossless,
AV_OPT_SEARCH_CHILDREN);
av_opt_set_int(avctx, "quality", ctx->opts->webp_quality,
AV_OPT_SEARCH_CHILDREN);
} }
if (avcodec_open2(avctx, codec, NULL) < 0) { if (avcodec_open2(avctx, codec, NULL) < 0) {
@ -292,6 +310,7 @@ int image_writer_format_from_ext(const char *ext)
} }
static struct mp_image *convert_image(struct mp_image *image, int destfmt, static struct mp_image *convert_image(struct mp_image *image, int destfmt,
enum mp_csp_levels yuv_levels,
struct mp_log *log) struct mp_log *log)
{ {
int d_w, d_h; int d_w, d_h;
@ -308,9 +327,9 @@ static struct mp_image *convert_image(struct mp_image *image, int destfmt,
// If RGB, just assume everything is correct. // If RGB, just assume everything is correct.
if (p.color.space != MP_CSP_RGB) { if (p.color.space != MP_CSP_RGB) {
// Currently, assume what FFmpeg's jpg encoder needs. // Currently, assume what FFmpeg's jpg encoder or libwebp needs.
// Of course this works only for non-HDR (no HDR support in libswscale). // Of course this works only for non-HDR (no HDR support in libswscale).
p.color.levels = MP_CSP_LEVELS_PC; p.color.levels = yuv_levels;
p.color.space = MP_CSP_BT_601; p.color.space = MP_CSP_BT_601;
p.chroma_location = MP_CHROMA_CENTER; p.chroma_location = MP_CHROMA_CENTER;
mp_image_params_guess_csp(&p); mp_image_params_guess_csp(&p);
@ -354,11 +373,24 @@ bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
destfmt = IMGFMT_RGB24; destfmt = IMGFMT_RGB24;
} }
#endif #endif
if (opts->format == AV_CODEC_ID_WEBP && !opts->webp_lossless) {
// For lossy images, libwebp has its own RGB->YUV conversion.
// We don't want that, so force YUV/YUVA here.
int alpha = image->fmt.flags & MP_IMGFLAG_ALPHA;
destfmt = alpha ? pixfmt2imgfmt(AV_PIX_FMT_YUVA420P) : IMGFMT_420P;
}
if (!destfmt) if (!destfmt)
destfmt = get_target_format(&ctx); destfmt = get_target_format(&ctx);
struct mp_image *dst = convert_image(image, destfmt, log); enum mp_csp_levels levels; // Ignored if destfmt is a RGB format
if (opts->format == AV_CODEC_ID_WEBP) {
levels = MP_CSP_LEVELS_TV;
} else {
levels = MP_CSP_LEVELS_PC;
}
struct mp_image *dst = convert_image(image, destfmt, levels, log);
if (!dst) if (!dst)
return false; return false;

View File

@ -32,6 +32,8 @@ struct image_writer_opts {
int jpeg_progressive; int jpeg_progressive;
int jpeg_baseline; int jpeg_baseline;
int jpeg_source_chroma; int jpeg_source_chroma;
int webp_lossless;
int webp_quality;
int tag_csp; int tag_csp;
}; };