On Tue, Nov 08, 2011 at 11:13:48PM -0500, Derek Buitenhuis wrote:
> Add a decoder for the VBLE Lossless Codec, which
> still has a cult following. Used to be popular
> years ago on doom9.

I think you mean "more than half a decade ago for a short time". I remember
hearing about it but didn't bother to RE it even then. Still, nice to have it.
Can you also provide samples?
 
> Signed-off-by: Derek Buitenhuis <[email protected]>
> ---
>  Changelog              |    1 +
>  doc/general.texi       |    1 +
>  libavcodec/Makefile    |    1 +
>  libavcodec/allcodecs.c |    1 +
>  libavcodec/avcodec.h   |    1 +
>  libavcodec/vble.c      |  245 
> ++++++++++++++++++++++++++++++++++++++++++++++++
>  libavcodec/version.h   |    2 +-
>  libavformat/riff.c     |    1 +
>  8 files changed, 252 insertions(+), 1 deletions(-)
>  create mode 100644 libavcodec/vble.c
> 
> diff --git a/Changelog b/Changelog
> index 5387a65..a322c9d 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -100,6 +100,7 @@ easier to use. The changes are:
>  - Properly working defaults in libx264 wrapper, support for native presets.
>  - Encrypted OMA files support
>  - Discworld II BMV decoding support
> +- VBLE Decoder
>  
>  
>  version 0.7:
> diff --git a/doc/general.texi b/doc/general.texi
> index 3187f2c..d2747c0 100644
> --- a/doc/general.texi
> +++ b/doc/general.texi
> @@ -520,6 +520,7 @@ following image formats are supported:
>      @tab Codec used in DOS CD-ROM FlashBack game.
>  @item Ut Video               @tab     @tab  X
>  @item V210 Quicktime Uncompressed 4:2:2 10-bit     @tab  X  @tab  X
> +@item VBLE Lossless Codec    @tab     @tab  X
>  @item VMware Screen Codec / VMware Video  @tab     @tab  X
>      @tab Codec used in videos captured by VMware.
>  @item Westwood Studios VQA (Vector Quantized Animation) video  @tab     @tab 
>  X
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 413b04d..ee68f68 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -383,6 +383,7 @@ OBJS-$(CONFIG_V210_DECODER)            += v210dec.o
>  OBJS-$(CONFIG_V210_ENCODER)            += v210enc.o
>  OBJS-$(CONFIG_V210X_DECODER)           += v210x.o
>  OBJS-$(CONFIG_VB_DECODER)              += vb.o
> +OBJS-$(CONFIG_VBLE_DECODER)            += vble.o
>  OBJS-$(CONFIG_VC1_DECODER)             += vc1dec.o vc1.o vc1data.o vc1dsp.o \
>                                            msmpeg4.o msmpeg4data.o           \
>                                            intrax8.o intrax8dsp.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 2beb81c..7244d34 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -203,6 +203,7 @@ void avcodec_register_all(void)
>      REGISTER_ENCDEC  (V210,  v210);
>      REGISTER_DECODER (V210X, v210x);
>      REGISTER_DECODER (VB, vb);
> +    REGISTER_DECODER (VBLE, vble);
>      REGISTER_DECODER (VC1, vc1);
>      REGISTER_DECODER (VC1_VDPAU, vc1_vdpau);
>      REGISTER_DECODER (VC1IMAGE, vc1image);
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index f169bec..85ea665 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -220,6 +220,7 @@ enum CodecID {
>  #endif
>      CODEC_ID_UTVIDEO,
>      CODEC_ID_BMV_VIDEO,
> +    CODEC_ID_VBLE,
>  
>      /* various PCM "codecs" */
>      CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the 
> start of audio codecs
> diff --git a/libavcodec/vble.c b/libavcodec/vble.c
> new file mode 100644
> index 0000000..6f7ed32
> --- /dev/null
> +++ b/libavcodec/vble.c
> @@ -0,0 +1,245 @@
> +/*
> + * VBLE Decoder
> + * Copyright (c) 2011 Derek Buitenhuis
> + *
> + * 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
> + * VBLE Decoder
> + */
> +
> +/* Little endian bitstream */
> +#define ALT_BITSTREAM_READER_LE
> +
> +#include "avcodec.h"
> +#include "get_bits.h"
> +
> +typedef struct {
> +    AVCodecContext *avctx;
> +    
> +    int size;
> +    uint8_t *len;
> +    uint32_t *val;
> +} VBLEContext;
> +
> +static uint8_t vble_bitscan(GetBitContext *gb)
> +{
> +    uint32_t src = get_bits_long(gb, 32);
> +
> +    skip_bits_long(gb, -32);

src = show_bits_long(gb, 32);

> +    for(int i = 0; i < 32; i++) {
> +        if(src & (1 << i)) {
> +            skip_bits_long(gb, i + 1);
> +            return i;
> +        }
> +    }
> +
> +    return -1;
> +}

Also the function is strange IMO, numbers like

00000000010 and
11111111110 will be treated the same

Also please declare loop variables once and don't use for(int i=...

> +static void vble_unpack(VBLEContext *ctx, GetBitContext *gb)
> +{
> +    uint8_t *lengths = ctx->len;
> +    int sum;
> +
> +    /* Read all the lengths in first */
> +    for (int i = 0; i < ctx->size * 4; i++)
> +        lengths[i] = vble_bitscan(gb);
> +
> +    /* Pixels are encoded in groups of 4 */
> +    for (int i = 0; i < ctx->size; i++) {
> +        lengths = ctx->len + i * 4;
> +
> +        sum = lengths[0] + lengths[1] + lengths[2] + lengths[3];
> +
> +        if(sum != 0)
> +            ctx->val[i] = get_bits_long(gb, sum);
> +        else
> +            ctx->val[i] = 0;

a check for sum > 32 wouldn't hurt

> +    }
> +}
> +
> +static void vble_decode(VBLEContext *ctx)
> +{
> +    AVFrame *pic = ctx->avctx->coded_frame;
> +    uint8_t *out;
> +    uint8_t *lengths;
> +    uint8_t pix, len_sum;
> +    int pos;
> +    int h = ctx->avctx->height;
> +
> +    for (int i = 0; i < ctx->size; i++) {
> +        len_sum = 0;
> +        lengths = ctx->len + i * 4;
> +
> +        for (int p = 0; p < 4; p++) {
> +            pos = i * 4 + p; 
> +
> +            if (pos < pic->linesize[0] * h) {
> +                /* Y */
> +                out = pic->data[0];
> +            }
> +            else if (pos < pic->linesize[0] * h + pic->linesize[1] * h / 2) {
> +                /* U */
> +                out = pic->data[1];
> +                pos -= pic->linesize[0] * h;
> +            }
> +            else {
> +                /* V */
> +                out = pic->data[2];
> +                pos -= pic->linesize[0] * h + pic->linesize[1] * h / 2;
> +            }
> +
> +            pix = (uint8_t)(ctx->val[i] >> len_sum);
> +            pix &= (1 << (lengths[p])) - 1;
> +            pix += (1 << (lengths[p])) - 1;
> +
> +            out[pos] = pix & 1 ? 255 - (pix >> 1) : pix >> 1;
> +
> +            len_sum += lengths[p];
> +        }
> +    }
> +}
> +
> +static void vble_median_restore(uint8_t *plane, int width, int height)
> +{
> +    uint8_t *dst = plane;
> +    uint8_t a, b, c;
> +
> +    for(int i = 1; i < width; i++)
> +        dst[i] += dst[i - 1];
> +
> +    dst += width;
> +
> +    for(int i = 1; i < height; i++) { 
> +        for(int j = 1; j < width; j++) {
> +            a = dst[j - 1];
> +            b = dst[j - width];
> +            c = a + b - dst[j - 1 - width];
> +            dst[j] += mid_pred(a, b, c);
> +        }
> +        dst += width;
> +    }
> +}
> +
> +static int vble_decode_frame(AVCodecContext *avctx, void *data, int 
> *data_size,
> +                             AVPacket *avpkt)
> +{
> +    VBLEContext *ctx = avctx->priv_data;
> +    AVFrame *pic = avctx->coded_frame;
> +    GetBitContext gb;
> +    const uint8_t *src = avpkt->data;
> +    int version;
> +    int w = avctx->width, h = avctx->height;
> +
> +    /* Clear buffer if need be */
> +    if(pic->data[0])
> +        avctx->release_buffer(avctx, pic);
> +
> +    /* Allocate buffer */
> +    if(avctx->get_buffer(avctx, pic) < 0) {
> +        av_log(avctx, AV_LOG_ERROR, "Could not allocate buffer.\n");
> +        return -1;
> +    }
> +    
> +    /* Set flags */
> +    pic->reference = 0;
> +    pic->key_frame = 1;
> +    pic->pict_type = FF_I_TYPE;
> +    
> +    /* Codec is YV12-only */
> +    pic->linesize[0] = w;
> +    pic->linesize[1] = pic->linesize[2] = w / 2;

That's abuse, you shouldn't touch linesizes. If you want continuous buffer
then allocate it and copy data into pic later.

> +    /* Version should always be 1 */
> +    version = AV_RL32(src);
> +    av_log(avctx, AV_LOG_DEBUG, "Version: %d\n", version);
> +
> +    if (version != 1) {
> +        av_log(avctx, AV_LOG_ERROR, "Unsupported VBLE Version: %d\n", 
> version);
> +        return -1;
> +    }
> +    
> +    /* Move past version */
> +    src += 4;
> +
> +    init_get_bits(&gb, src, (avpkt->size - 4) * 8);
> +
> +    /* Unpack and decode */
> +    vble_unpack(ctx, &gb);
> +    vble_decode(ctx);
> +
> +    /* Restore planes. Should be almost identical to Huffyuv's. */
> +    vble_median_restore(pic->data[0], pic->linesize[0], h);
> +    vble_median_restore(pic->data[1], pic->linesize[1], h / 2);
> +    vble_median_restore(pic->data[2], pic->linesize[2], h / 2);
> +
> +    *data_size = sizeof(AVFrame);
> +    *(AVFrame *)data = *pic;
> +
> +    return avpkt->size;
> +}
> +
> +static av_cold int vble_decode_init(AVCodecContext *avctx)
> +{
> +    VBLEContext *ctx = avctx->priv_data;
> +
> +    ctx->avctx = avctx;
> +    
> +    /* Number of samples divided by 4 */
> +    ctx->size = (avctx->width * avctx->height * 3) / 8;
> +
> +    ctx->len = (uint8_t *)av_malloc(ctx->size * 4 * sizeof(uint8_t));
> +    ctx->val = (uint32_t *)av_malloc(ctx->size * sizeof(uint32_t));
> +
> +    avctx->pix_fmt = PIX_FMT_YUV420P;
> +    avctx->bits_per_raw_sample = 8;
> +    avctx->coded_frame = avcodec_alloc_frame();
> +
> +    return 0;
> +}
> +
> +static av_cold int vble_decode_close(AVCodecContext *avctx)
> +{
> +    VBLEContext *ctx = avctx->priv_data;
> +    AVFrame *pic = avctx->coded_frame;
> +
> +    if(pic->data[0])
> +        avctx->release_buffer(avctx, pic);
> +
> +    av_freep(&pic);
> +    av_freep(&ctx->len);
> +    av_freep(&ctx->val);
> +
> +    return 0;
> +}
> +
> +AVCodec ff_vble_decoder = {
> +    .name           = "vble",
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = CODEC_ID_VBLE,
> +    .priv_data_size = sizeof(VBLEContext),
> +    .init           = vble_decode_init,
> +    .close          = vble_decode_close,
> +    .decode         = vble_decode_frame,
> +    .capabilities   = CODEC_CAP_DR1,
> +    .long_name      = NULL_IF_CONFIG_SMALL("VBLE Video Codec"),
> +};
> +
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index 5e07ec2..c11f09c 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -21,7 +21,7 @@
>  #define AVCODEC_VERSION_H
>  
>  #define LIBAVCODEC_VERSION_MAJOR 53
> -#define LIBAVCODEC_VERSION_MINOR 17
> +#define LIBAVCODEC_VERSION_MINOR 18
>  #define LIBAVCODEC_VERSION_MICRO  0
>  
>  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
> diff --git a/libavformat/riff.c b/libavformat/riff.c
> index 8eed7ce..29c3803 100644
> --- a/libavformat/riff.c
> +++ b/libavformat/riff.c
> @@ -279,6 +279,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
>      { CODEC_ID_UTVIDEO,      MKTAG('U', 'L', 'R', 'G') },
>      { CODEC_ID_UTVIDEO,      MKTAG('U', 'L', 'Y', '0') },
>      { CODEC_ID_UTVIDEO,      MKTAG('U', 'L', 'Y', '2') },
> +    { CODEC_ID_VBLE,         MKTAG('V', 'B', 'L', 'E') },
>      { CODEC_ID_NONE,         0 }
>  };
>  
> -- 
> 1.7.7.1
> 
> _______________________________________________
> libav-devel mailing list
> [email protected]
> https://lists.libav.org/mailman/listinfo/libav-devel
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to