view loco.c @ 2530:eace30b70601 libavcodec

go LOCO, courtesy of Kostya Shishkov
author melanson
date Tue, 01 Mar 2005 02:24:58 +0000
parents
children 2b01396ab483
line wrap: on
line source

/*
 * LOCO codec
 * Copyright (c) 2005 Konstantin Shishkov
 *
 * This library 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 of the License, or (at your option) any later version.
 *
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
 
/**
 * @file loco.c
 * LOCO codec.
 */
 
#include "avcodec.h"
#include "common.h"
#include "bitstream.h"
#include "golomb.h"

enum LOCO_MODE {LOCO_UNKN=0, LOCO_CYUY2=-1, LOCO_CRGB=-2, LOCO_CYV12=-3,
 LOCO_YUY2=1, LOCO_UYVY=2, LOCO_RGB=3, LOCO_RGBA=4, LOCO_YV12=5};

typedef struct LOCOContext{
    AVCodecContext *avctx;
    AVFrame pic;
    int lossy;
    int mode;
} LOCOContext;

typedef struct RICEContext{
    GetBitContext gb;
    int save, run, run2; /* internal rice decoder state */
    int sum, count; /* sum and count for getting rice parameter */
}RICEContext;

/* could use get_sr_golomb() but is behaves differently on numbers like Rice(2, -64) */
static inline int get_rice(GetBitContext *gb, int K)
{
    int i;
    int V = 0;
    for(i = 0; !get_bits1(gb); i++);
    V = i;
    for(i = 0; i < K; i++) V = (V << 1) | get_bits1(gb);
    if(V & 1) return (V + 1) >> 1;
    return -(V >> 1);
}

static inline int get_u_rice(GetBitContext *gb, int K)
{
    int i;
    int V = 0;
    for(i = 0; !get_bits1(gb); i++);
    V = i;
    for(i = 0; i < K; i++) V = (V << 1) | get_bits1(gb);
    return V;
}

static int loco_get_rice_param(RICEContext *r)
{
    int cnt = 0;
    int val = r->count;
    
    while(r->sum > val && cnt < 9) {
        val <<= 1;
        cnt++;
    }
    
    return cnt;
}

static inline void loco_update_rice_param(RICEContext *r, int val)
{
    if (val < 0)
        val = -val;
    r->sum += val;
    r->count++;
    
    if(r->count == 16) {
        r->sum >>= 1;
        r->count >>= 1;
    }
}

static inline int loco_get_rice(RICEContext *r)
{
    int v;
    
    if (r->run > 0) { /* we have zero run */
        r->run--;
        return 0;
    }
    v = -get_rice(&r->gb, loco_get_rice_param(r));
    if (!v) {
        if (r->save >= 0) {
            r->run = get_u_rice(&r->gb, 2);
            if(r->run > 1)
                r->save += r->run + 1;
            else
                r->save -= 3;
        }
        else
            r->run2++;
    } else if (r->run2 > 0) {
        if (r->run2 > 2)
            r->save += r->run2;
        else
            r->save -= 3;
        r->run2 = 0;
    }
    
    return v;
}

/* LOCO main predictor - LOCO-I/JPEG-LS predictor */
static inline int loco_predict(uint8_t* data, int stride, int step)
{
    int max_ab, min_ab;
    int a, b, c;
    
    a = data[-stride];
    b = data[-step];
    c = data[-stride - step];
    
    max_ab = (a > b) ? a : b;
    min_ab = (a < b) ? a : b;
    
    if (c >= max_ab) return min_ab;
    if (c <= min_ab) return max_ab;
    return (a + b - c);
}

static int loco_decode_plane(LOCOContext *l, uint8_t *data, int width, int height,
                             int stride, uint8_t *buf, int buf_size, int step)
{
    RICEContext rc;
    int val;
    int i, j;
    
    init_get_bits(&rc.gb, buf, buf_size*8);
    rc.save = 0;
    rc.run = 0;
    rc.run2 = 0;
    
    rc.sum = 8;
    rc.count = 1;
    
    /* restore top left pixel */
    val = loco_get_rice(&rc);
    loco_update_rice_param(&rc, val);
    if (val < 0) val -= l->lossy;
    if (val > 0) val += l->lossy;
    data[0] = 128 + val;
    /* restore top line */
    for (i = 1; i < width; i++) {
        val = loco_get_rice(&rc);
        loco_update_rice_param(&rc, val);
        if (val < 0) val -= l->lossy;
        if (val > 0) val += l->lossy;
        data[i * step] = data[i * step - step] + val;
    }
    data += stride;
    for (j = 1; j < height; j++) {
        /* restore left column */
        val = loco_get_rice(&rc);
        loco_update_rice_param(&rc, val);
        if (val < 0) val -= l->lossy;
        if (val > 0) val += l->lossy;
        data[0] = data[-stride] + val;
        /* restore all other pixels */
        for (i = 1; i < width; i++) {
            val = loco_get_rice(&rc);
            loco_update_rice_param(&rc, val);
            if (val < 0) val -= l->lossy;
            if (val > 0) val += l->lossy;
            data[i * step] = loco_predict(&data[i * step], stride, step) + val;
        }
        data += stride;
    }
    
    return ((get_bits_count(&rc.gb) + 7) >> 3);
}

static int decode_frame(AVCodecContext *avctx, 
                        void *data, int *data_size,
                        uint8_t *buf, int buf_size)
{
    LOCOContext * const l = avctx->priv_data;
    AVFrame * const p= (AVFrame*)&l->pic;
    int decoded;

    if(p->data[0])
        avctx->release_buffer(avctx, p);

    p->reference = 0;
    if(avctx->get_buffer(avctx, p) < 0){
        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
        return -1;
    }
    p->key_frame = 1;

    switch(l->mode) {
    case LOCO_CYUY2: case LOCO_YUY2: case LOCO_UYVY:
        decoded = loco_decode_plane(l, p->data[0], avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 1);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[1], avctx->width / 2, avctx->height,
                                    p->linesize[1], buf, buf_size, 1);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[2], avctx->width / 2, avctx->height,
                                    p->linesize[2], buf, buf_size, 1);
        break;
    case LOCO_CYV12: case LOCO_YV12:
        decoded = loco_decode_plane(l, p->data[0], avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 1);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[1], avctx->width / 2, avctx->height / 2,
                                    p->linesize[1], buf, buf_size, 1);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[2], avctx->width / 2, avctx->height / 2,
                                    p->linesize[2], buf, buf_size, 1);
        break;
    case LOCO_CRGB: case LOCO_RGB:
        decoded = loco_decode_plane(l, p->data[0], avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 3);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[0] + 1, avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 3);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[0] + 2, avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 3);
        break;
    case LOCO_RGBA:
        decoded = loco_decode_plane(l, p->data[0], avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 4);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[0] + 1, avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 4);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[0] + 2, avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 4);
        buf += decoded; buf_size -= decoded;
        decoded = loco_decode_plane(l, p->data[0] + 3, avctx->width, avctx->height,
                                    p->linesize[0], buf, buf_size, 4);
        break;
    }

    *data_size = sizeof(AVFrame);
    *(AVFrame*)data = l->pic;
    
    return buf_size;
}

static int decode_init(AVCodecContext *avctx){
    LOCOContext * const l = avctx->priv_data;
    int version;

    l->avctx = avctx;
    if (avctx->extradata_size < 12) {
        av_log(avctx, AV_LOG_ERROR, "Extradata size must be >= 12 instead of %i\n",
               avctx->extradata_size);
        return -1;
    }
    version = LE_32(avctx->extradata);
    switch(version) {
    case 1:
        l->lossy = 0;
        break;
    case 2:
        l->lossy = LE_32(avctx->extradata + 8);
        break;
    default:
        l->lossy = LE_32(avctx->extradata + 8);
        av_log(avctx, AV_LOG_INFO, "This is LOCO codec version %i, please upload file for study\n", version);
    }
    
    l->mode = LE_32(avctx->extradata + 4);
    switch(l->mode) {
    case LOCO_CYUY2: case LOCO_YUY2: case LOCO_UYVY:
        avctx->pix_fmt = PIX_FMT_YUV422P;
        break;
    case LOCO_CRGB: case LOCO_RGB:
        avctx->pix_fmt = PIX_FMT_BGR24;
        break;
    case LOCO_CYV12: case LOCO_YV12:
        avctx->pix_fmt = PIX_FMT_YUV420P;
        break;
    case LOCO_RGBA:
        avctx->pix_fmt = PIX_FMT_RGBA32;
        break;
    default:
        av_log(avctx, AV_LOG_INFO, "Unknown colorspace, index = %i\n", l->mode);
        return -1;
    }

    return 0;
}

AVCodec loco_decoder = {
    "loco",
    CODEC_TYPE_VIDEO,
    CODEC_ID_LOCO,
    sizeof(LOCOContext),
    decode_init,
    NULL,
    NULL,
    decode_frame,
    CODEC_CAP_DR1,
};