[FFmpeg-cvslog] Add a libwebp encoder

Justin Ruggles git at videolan.org
Sun Dec 22 12:33:16 CET 2013


ffmpeg | branch: master | Justin Ruggles <justin.ruggles at gmail.com> | Fri Nov 22 11:05:13 2013 -0500| [be7c323176e2e5fcf30e3d2ff20975b2f936811b] | committer: Justin Ruggles

Add a libwebp encoder

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=be7c323176e2e5fcf30e3d2ff20975b2f936811b
---

 Changelog               |    1 +
 configure               |    4 +
 doc/encoders.texi       |   61 ++++++++++
 doc/general.texi        |    4 +-
 libavcodec/Makefile     |    1 +
 libavcodec/allcodecs.c  |    1 +
 libavcodec/libwebpenc.c |  287 +++++++++++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h    |    2 +-
 libavformat/img2enc.c   |    2 +-
 9 files changed, 359 insertions(+), 4 deletions(-)

diff --git a/Changelog b/Changelog
index 6ec0357..efd474a 100644
--- a/Changelog
+++ b/Changelog
@@ -52,6 +52,7 @@ version 10:
 - remove mp3_header_(de)compress bitstream filters
 - stereoscopic 3d metadata handling
 - png standalone parser
+- WebP encoding via libwebp
 
 
 version 9:
diff --git a/configure b/configure
index 49f1e73..69bdefc 100755
--- a/configure
+++ b/configure
@@ -202,6 +202,7 @@ External library support:
   --enable-libvorbis       enable Vorbis encoding via libvorbis [no]
   --enable-libvpx          enable VP8 and VP9 de/encoding via libvpx [no]
   --enable-libwavpack      enable wavpack encoding via libwavpack [no]
+  --enable-libwebp         enable WebP encoding via libwebp [no]
   --enable-libx264         enable H.264 encoding via x264 [no]
   --enable-libxavs         enable AVS encoding via xavs [no]
   --enable-libxvid         enable Xvid encoding via xvidcore,
@@ -1116,6 +1117,7 @@ EXTERNAL_LIBRARY_LIST="
     libvorbis
     libvpx
     libwavpack
+    libwebp
     libx264
     libxavs
     libxvid
@@ -1867,6 +1869,7 @@ libvpx_vp8_encoder_deps="libvpx"
 libvpx_vp9_decoder_deps="libvpx"
 libvpx_vp9_encoder_deps="libvpx"
 libwavpack_encoder_deps="libwavpack"
+libwebp_encoder_deps="libwebp"
 libx264_encoder_deps="libx264"
 libxavs_encoder_deps="libxavs"
 libxvid_encoder_deps="libxvid"
@@ -3926,6 +3929,7 @@ enabled libvpx            && {
     enabled libvpx_vp9_decoder && { check_lib2 "vpx/vpx_decoder.h vpx/vp8dx.h" "vpx_codec_vp9_dx" -lvpx || disable libvpx_vp9_decoder; }
     enabled libvpx_vp9_encoder && { check_lib2 "vpx/vpx_encoder.h vpx/vp8cx.h" "vpx_codec_vp9_cx" -lvpx || disable libvpx_vp9_encoder; } }
 enabled libwavpack        && require libwavpack wavpack/wavpack.h WavpackOpenFileOutput  -lwavpack
+enabled libwebp           && require_pkg_config libwebp webp/encode.h WebPGetEncoderVersion
 enabled libx264           && require libx264 x264.h x264_encoder_encode -lx264 &&
                              { check_cpp_condition x264.h "X264_BUILD >= 118" ||
                                die "ERROR: libx264 version must be >= 0.118."; }
diff --git a/doc/encoders.texi b/doc/encoders.texi
index d6f4bce..6068662 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -446,6 +446,67 @@ Same as 3, but with extra processing enabled - corresponding to the wavpack
 @chapter Video Encoders
 @c man begin VIDEO ENCODERS
 
+ at section libwebp
+
+libwebp WebP Image encoder wrapper
+
+libwebp is Google's official encoder for WebP images. It can encode in either
+lossy or lossless mode. Lossy images are essentially a wrapper around a VP8
+frame. Lossless images are a separate codec developed by Google.
+
+ at subsection Pixel Format
+
+Currently, libwebp only supports YUV420 for lossy and RGB for lossless due
+to limitations of the format and libwebp. Alpha is supported for either mode.
+Because of API limitations, if RGB is passed in when encoding lossy or YUV is
+passed in for encoding lossless, the pixel format will automatically be
+converted using functions from libwebp. This is not ideal and is done only for
+convenience.
+
+ at subsection Options
+
+ at table @option
+
+ at item -lossless @var{boolean}
+Enables/Disables use of lossless mode. Default is 0.
+
+ at item -compression_level @var{integer}
+For lossy, this is a quality/speed tradeoff. Higher values give better quality
+for a given size at the cost of increased encoding time. For lossless, this is
+a size/speed tradeoff. Higher values give smaller size at the cost of increased
+encoding time. More specifically, it controls the number of extra algorithms
+and compression tools used, and varies the combination of these tools. This
+maps to the @var{method} option in libwebp. The valid range is 0 to 6.
+Default is 4.
+
+ at item -qscale @var{float}
+For lossy encoding, this controls image quality, 0 to 100. For lossless
+encoding, this controls the effort and time spent at compressing more. The
+default value is 75. Note that for usage via libavcodec, this option is called
+ at var{global_quality} and must be multiplied by @var{FF_QP2LAMBDA}.
+
+ at item -preset @var{type}
+Configuration preset. This does some automatic settings based on the general
+type of the image.
+ at table @option
+ at item none
+Do not use a preset.
+ at item default
+Use the encoder default.
+ at item picture
+Digital picture, like portrait, inner shot
+ at item photo
+Outdoor photograph, with natural lighting
+ at item drawing
+Hand or line drawing, with high-contrast details
+ at item icon
+Small-sized colorful images
+ at item text
+Text-like
+ at end table
+
+ at end table
+
 @section libx264
 
 x264 H.264/MPEG-4 AVC encoder wrapper
diff --git a/doc/general.texi b/doc/general.texi
index e34fabd..a528280 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -415,8 +415,8 @@ following image formats are supported:
     @tab YUV, JPEG and some extension is not supported yet.
 @item Truevision Targa  @tab X @tab X
     @tab Targa (.TGA) image format
- at item WebP         @tab @tab X
-    @tab WebP image format
+ at item WebP         @tab E @tab X
+    @tab WebP image format, encoding supported through external library libwebp
 @item XBM  @tab X @tab
     @tab X BitMap image format
 @item XWD  @tab X @tab X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index fb79c32..fd7808e 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -612,6 +612,7 @@ OBJS-$(CONFIG_LIBVPX_VP8_ENCODER)         += libvpxenc.o
 OBJS-$(CONFIG_LIBVPX_VP9_DECODER)         += libvpxdec.o libvpx.o
 OBJS-$(CONFIG_LIBVPX_VP9_ENCODER)         += libvpxenc.o libvpx.o
 OBJS-$(CONFIG_LIBWAVPACK_ENCODER)         += libwavpackenc.o
+OBJS-$(CONFIG_LIBWEBP_ENCODER)            += libwebpenc.o
 OBJS-$(CONFIG_LIBX264_ENCODER)            += libx264.o
 OBJS-$(CONFIG_LIBXAVS_ENCODER)            += libxavs.o
 OBJS-$(CONFIG_LIBXVID_ENCODER)            += libxvid.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 8b2bdc1..5a6c914 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -437,6 +437,7 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC (LIBVPX_VP8,        libvpx_vp8);
     REGISTER_ENCDEC (LIBVPX_VP9,        libvpx_vp9);
     REGISTER_ENCODER(LIBWAVPACK,        libwavpack);
+    REGISTER_ENCODER(LIBWEBP,           libwebp);
     REGISTER_ENCODER(LIBX264,           libx264);
     REGISTER_ENCODER(LIBXAVS,           libxavs);
     REGISTER_ENCODER(LIBXVID,           libxvid);
diff --git a/libavcodec/libwebpenc.c b/libavcodec/libwebpenc.c
new file mode 100644
index 0000000..74e73dc
--- /dev/null
+++ b/libavcodec/libwebpenc.c
@@ -0,0 +1,287 @@
+/*
+ * WebP encoding support via libwebp
+ * Copyright (c) 2013 Justin Ruggles <justin.ruggles at gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Libav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * WebP encoder using libwebp
+ */
+
+#include <webp/encode.h>
+
+#include "libavutil/common.h"
+#include "libavutil/frame.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "avcodec.h"
+#include "internal.h"
+
+typedef struct LibWebPContext {
+    AVClass *class;         // class for AVOptions
+    float quality;          // lossy quality 0 - 100
+    int lossless;           // use lossless encoding
+    int preset;             // configuration preset
+    int chroma_warning;     // chroma linesize mismatch warning has been printed
+    int conversion_warning; // pixel format conversion warning has been printed
+    WebPConfig config;      // libwebp configuration
+} LibWebPContext;
+
+static int libwebp_error_to_averror(int err)
+{
+    switch (err) {
+    case VP8_ENC_ERROR_OUT_OF_MEMORY:
+    case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
+        return AVERROR(ENOMEM);
+    case VP8_ENC_ERROR_NULL_PARAMETER:
+    case VP8_ENC_ERROR_INVALID_CONFIGURATION:
+    case VP8_ENC_ERROR_BAD_DIMENSION:
+        return AVERROR(EINVAL);
+    }
+    return AVERROR_UNKNOWN;
+}
+
+static av_cold int libwebp_encode_init(AVCodecContext *avctx)
+{
+    LibWebPContext *s = avctx->priv_data;
+    int ret;
+
+    if (avctx->global_quality < 0)
+        avctx->global_quality = 75 * FF_QP2LAMBDA;
+    s->quality = av_clipf(avctx->global_quality / (float)FF_QP2LAMBDA,
+                          0.0f, 100.0f);
+
+    if (avctx->compression_level < 0 || avctx->compression_level > 6) {
+        av_log(avctx, AV_LOG_WARNING, "invalid compression level: %d\n",
+               avctx->compression_level);
+        avctx->compression_level = av_clip(avctx->compression_level, 0, 6);
+    }
+
+    if (s->preset >= WEBP_PRESET_DEFAULT) {
+        ret = WebPConfigPreset(&s->config, s->preset, s->quality);
+        if (!ret)
+            return AVERROR_UNKNOWN;
+        s->lossless              = s->config.lossless;
+        s->quality               = s->config.quality;
+        avctx->compression_level = s->config.method;
+    } else {
+        ret = WebPConfigInit(&s->config);
+        if (!ret)
+            return AVERROR_UNKNOWN;
+
+        s->config.lossless = s->lossless;
+        s->config.quality  = s->quality;
+        s->config.method   = avctx->compression_level;
+
+        ret = WebPValidateConfig(&s->config);
+        if (!ret)
+            return AVERROR(EINVAL);
+    }
+
+    av_log(avctx, AV_LOG_DEBUG, "%s - quality=%.1f method=%d\n",
+           s->lossless ? "Lossless" : "Lossy", s->quality,
+           avctx->compression_level);
+
+    return 0;
+}
+
+static int libwebp_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
+                                const AVFrame *frame, int *got_packet)
+{
+    LibWebPContext *s  = avctx->priv_data;
+    AVFrame *alt_frame = NULL;
+    WebPPicture *pic = NULL;
+    WebPMemoryWriter mw = { 0 };
+    int ret;
+
+    if (avctx->width > WEBP_MAX_DIMENSION || avctx->height > WEBP_MAX_DIMENSION) {
+        av_log(avctx, AV_LOG_ERROR, "Picture size is too large. Max is %dx%d.\n",
+               WEBP_MAX_DIMENSION, WEBP_MAX_DIMENSION);
+        return AVERROR(EINVAL);
+    }
+
+    pic = av_malloc(sizeof(*pic));
+    if (!pic)
+        return AVERROR(ENOMEM);
+
+    ret = WebPPictureInit(pic);
+    if (!ret) {
+        ret = AVERROR_UNKNOWN;
+        goto end;
+    }
+    pic->width  = avctx->width;
+    pic->height = avctx->height;
+
+    if (avctx->pix_fmt == AV_PIX_FMT_RGB32) {
+        if (!s->lossless) {
+            /* libwebp will automatically convert RGB input to YUV when
+               encoding lossy. */
+            if (!s->conversion_warning) {
+                av_log(avctx, AV_LOG_WARNING,
+                       "Using libwebp for RGB-to-YUV conversion. You may want "
+                       "to consider passing in YUV instead for lossy "
+                       "encoding.\n");
+                s->conversion_warning = 1;
+            }
+        }
+        pic->use_argb    = 1;
+        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) {
+                av_log(avctx, AV_LOG_WARNING,
+                       "Copying frame due to differing chroma linesizes.\n");
+                s->chroma_warning = 1;
+            }
+            alt_frame = av_frame_alloc();
+            if (!alt_frame) {
+                ret = AVERROR(ENOMEM);
+                goto end;
+            }
+            alt_frame->width  = frame->width;
+            alt_frame->height = frame->height;
+            alt_frame->format = frame->format;
+            ret = av_frame_get_buffer(alt_frame, 32);
+            if (ret < 0)
+                goto end;
+            av_image_copy(alt_frame->data, alt_frame->linesize,
+                          frame->data, frame->linesize,
+                          avctx->pix_fmt, frame->width, frame->height);
+            frame = alt_frame;
+        }
+        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) {
+            pic->colorspace = WEBP_YUV420A;
+            pic->a          = frame->data[3];
+            pic->a_stride   = frame->linesize[3];
+        } else {
+            pic->colorspace = WEBP_YUV420;
+        }
+
+        if (s->lossless) {
+            /* We do not have a way to automatically prioritize RGB over YUV
+               in automatic pixel format conversion based on whether we're
+               encoding lossless or lossy, so we do conversion with libwebp as
+               a convenience. */
+            if (!s->conversion_warning) {
+                av_log(avctx, AV_LOG_WARNING,
+                       "Using libwebp for YUV-to-RGB conversion. You may want "
+                       "to consider passing in RGB instead for lossless "
+                       "encoding.\n");
+                s->conversion_warning = 1;
+            }
+
+#if (WEBP_ENCODER_ABI_VERSION <= 0x201)
+            /* libwebp should do the conversion automatically, but there is a
+               bug that causes it to return an error instead, so a work-around
+               is required.
+               See https://code.google.com/p/webp/issues/detail?id=178 */
+            pic->memory_ = (void*)1;  /* something non-null */
+            ret = WebPPictureYUVAToARGB(pic);
+            if (!ret) {
+                av_log(avctx, AV_LOG_ERROR,
+                       "WebPPictureYUVAToARGB() failed with error: %d\n",
+                       pic->error_code);
+                ret = libwebp_error_to_averror(pic->error_code);
+                goto end;
+            }
+            pic->memory_ = NULL;  /* restore pointer */
+#endif
+        }
+    }
+
+    WebPMemoryWriterInit(&mw);
+    pic->custom_ptr = &mw;
+    pic->writer     = WebPMemoryWrite;
+
+    ret = WebPEncode(&s->config, pic);
+    if (!ret) {
+        av_log(avctx, AV_LOG_ERROR, "WebPEncode() failed with error: %d\n",
+               pic->error_code);
+        ret = libwebp_error_to_averror(pic->error_code);
+        goto end;
+    }
+
+    ret = ff_alloc_packet(pkt, mw.size);
+    if (ret < 0)
+        goto end;
+    memcpy(pkt->data, mw.mem, mw.size);
+
+    pkt->flags |= AV_PKT_FLAG_KEY;
+    *got_packet = 1;
+
+end:
+    free(mw.mem); /* must use free() according to libwebp documentation */
+    WebPPictureFree(pic);
+    av_freep(&pic);
+    av_frame_free(&alt_frame);
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(LibWebPContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+    { "lossless",   "Use lossless mode",       OFFSET(lossless), AV_OPT_TYPE_INT,   { .i64 =  0 },  0, 1,                           VE           },
+    { "preset",     "Configuration preset",    OFFSET(preset),   AV_OPT_TYPE_INT,   { .i64 = -1 }, -1, WEBP_PRESET_TEXT,            VE, "preset" },
+    { "none",       "do not use a preset",                              0, AV_OPT_TYPE_CONST, { .i64 = -1                  }, 0, 0, VE, "preset" },
+    { "default",    "default preset",                                   0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DEFAULT }, 0, 0, VE, "preset" },
+    { "picture",    "digital picture, like portrait, inner shot",       0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PICTURE }, 0, 0, VE, "preset" },
+    { "photo",      "outdoor photograph, with natural lighting",        0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PHOTO   }, 0, 0, VE, "preset" },
+    { "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" },
+    { NULL },
+};
+
+static const AVClass class = {
+    .class_name = "libwebp",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static const AVCodecDefault libwebp_defaults[] = {
+    { "compression_level",  "4"  },
+    { "global_quality",     "-1" },
+    { NULL },
+};
+
+AVCodec ff_libwebp_encoder = {
+    .name           = "libwebp",
+    .long_name      = NULL_IF_CONFIG_SMALL("libwebp WebP image"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_WEBP,
+    .priv_data_size = sizeof(LibWebPContext),
+    .init           = libwebp_encode_init,
+    .encode2        = libwebp_encode_frame,
+    .pix_fmts       = (const enum AVPixelFormat[]) {
+        AV_PIX_FMT_RGB32,
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P,
+        AV_PIX_FMT_NONE
+    },
+    .priv_class     = &class,
+    .defaults       = libwebp_defaults,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 25c22df..9a43e82 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -27,7 +27,7 @@
  */
 
 #define LIBAVCODEC_VERSION_MAJOR 55
-#define LIBAVCODEC_VERSION_MINOR 30
+#define LIBAVCODEC_VERSION_MINOR 31
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c
index 7b94869..4cc5c3f 100644
--- a/libavformat/img2enc.c
+++ b/libavformat/img2enc.c
@@ -148,7 +148,7 @@ AVOutputFormat ff_image2_muxer = {
     .long_name      = NULL_IF_CONFIG_SMALL("image2 sequence"),
     .extensions     = "bmp,dpx,jpeg,jpg,ljpg,pam,pbm,pcx,pgm,pgmyuv,png,"
                       "ppm,sgi,tga,tif,tiff,jp2,xwd,sun,ras,rs,im1,im8,im24,"
-                      "sunras,xbm",
+                      "sunras,webp,xbm",
     .priv_data_size = sizeof(VideoMuxData),
     .video_codec    = AV_CODEC_ID_MJPEG,
     .write_header   = write_header,



More information about the ffmpeg-cvslog mailing list