view tscc.c @ 2601:c31a28f27d9a libavcodec

increasing precission of the quantization parameter this is needed as the quantization stepsize for each subband is also in this precission and insignificant changes to the wavelet like scaling its coefficients slightly differently would lead to wildly variing PSNR and bitrate note, a encoder could also simply choose to leave the least significant bits of the quantization parameters zero which would give the exact previous behaviour except a y very tiny number of bits in the header
author michael
date Sat, 09 Apr 2005 22:15:48 +0000
parents 5e9f8eef19b9
children ef2149182f1c
line wrap: on
line source

/*
 * TechSmith Camtasia decoder
 * Copyright (c) 2004 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 tscc.c
 * TechSmith Camtasia decoder
 *
 * Fourcc: TSCC
 *
 * Codec is very simple:
 *  it codes picture (picture difference, really)
 *  with algorithm almost identical to Windows RLE8,
 *  only without padding and with greater pixel sizes,
 *  then this coded picture is packed with ZLib
 *
 * Supports: BGR8,BGR555,BGR24 - only BGR8 and BGR555 tested
 *
 */

#include <stdio.h>
#include <stdlib.h>

#include "common.h"
#include "avcodec.h"

#ifdef CONFIG_ZLIB
#include <zlib.h>
#endif


/*
 * Decoder context
 */
typedef struct TsccContext {

    AVCodecContext *avctx;
    AVFrame pic;

    // Bits per pixel
    int bpp;
    // Decompressed data size
    unsigned int decomp_size;
    // Decompression buffer
    unsigned char* decomp_buf;
    int height;
#ifdef CONFIG_ZLIB
    z_stream zstream;
#endif
} CamtasiaContext;

/*
 *
 * Decode RLE - almost identical to Windows BMP RLE8
 *              and enhanced to bigger color depths
 *
 */
 
static int decode_rle(CamtasiaContext *c, unsigned int srcsize)
{
    unsigned char *src = c->decomp_buf;
    unsigned char *output, *output_end;
    int p1, p2, line=c->height, pos=0, i;
    
    output = c->pic.data[0] + (c->height - 1) * c->pic.linesize[0];
    output_end = c->pic.data[0] + (c->height) * c->pic.linesize[0];
    while(src < c->decomp_buf + srcsize) {
        p1 = *src++;
        if(p1 == 0) { //Escape code
            p2 = *src++;
            if(p2 == 0) { //End-of-line
                output = c->pic.data[0] + (--line) * c->pic.linesize[0];
                if (line < 0)
                    return -1;
                pos = 0;
                continue;
            } else if(p2 == 1) { //End-of-picture
                return 0;
            } else if(p2 == 2) { //Skip
                p1 = *src++;
                p2 = *src++;
                line -= p2;
                if (line < 0)
                    return -1;
                pos += p1;
                output = c->pic.data[0] + line * c->pic.linesize[0] + pos * (c->bpp / 8);
                continue;
            }
            // Copy data
            if (output + p2 * (c->bpp / 8) > output_end) {
                src += p2 * (c->bpp / 8);
                continue;
            }
            for(i = 0; i < p2 * (c->bpp / 8); i++) {
                *output++ = *src++;
            }
	    // RLE8 copy is actually padded - and runs are not!
	    if(c->bpp == 8 && (p2 & 1)) {
		src++;
	    }
            pos += p2;
        } else { //Run of pixels
            int pix[4]; //original pixel
            switch(c->bpp){
            case  8: pix[0] = *src++;
                     break;
            case 16: pix[0] = *src++;
                     pix[1] = *src++;
                     break;
            case 24: pix[0] = *src++;
                     pix[1] = *src++;
                     pix[2] = *src++;
                     break;
            case 32: pix[0] = *src++;
                     pix[1] = *src++;
                     pix[2] = *src++;
                     pix[3] = *src++;
                     break;
            }
            if (output + p1 * (c->bpp / 8) > output_end)
                continue;
            for(i = 0; i < p1; i++) {
                switch(c->bpp){
                case  8: *output++ = pix[0];
                         break;
                case 16: *output++ = pix[0];
                         *output++ = pix[1];
                         break;
                case 24: *output++ = pix[0];
                         *output++ = pix[1];
                         *output++ = pix[2];
                         break;
                case 32: *output++ = pix[0];
                         *output++ = pix[1];
                         *output++ = pix[2];
                         *output++ = pix[3];
                         break;
                }
            }
            pos += p1;
        }
    }
    
    av_log(c->avctx, AV_LOG_ERROR, "Camtasia warning: no End-of-picture code\n");        
    return 1;
}

/*
 *
 * Decode a frame
 *
 */
static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, uint8_t *buf, int buf_size)
{
    CamtasiaContext * const c = (CamtasiaContext *)avctx->priv_data;
    unsigned char *encoded = (unsigned char *)buf;
    unsigned char *outptr;
#ifdef CONFIG_ZLIB
    int zret; // Zlib return code
#endif
    int len = buf_size;

    if(c->pic.data[0])
            avctx->release_buffer(avctx, &c->pic);

    c->pic.reference = 1;
    c->pic.buffer_hints = FF_BUFFER_HINTS_VALID;
    if(avctx->get_buffer(avctx, &c->pic) < 0){
        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
        return -1;
    }

    outptr = c->pic.data[0]; // Output image pointer

#ifdef CONFIG_ZLIB
    zret = inflateReset(&(c->zstream));
    if (zret != Z_OK) {
        av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", zret);
        return -1;
    }
    c->zstream.next_in = encoded;
    c->zstream.avail_in = len;
    c->zstream.next_out = c->decomp_buf;
    c->zstream.avail_out = c->decomp_size;
    zret = inflate(&(c->zstream), Z_FINISH);
    // Z_DATA_ERROR means empty picture
    if ((zret != Z_OK) && (zret != Z_STREAM_END) && (zret != Z_DATA_ERROR)) {
        av_log(avctx, AV_LOG_ERROR, "Inflate error: %d\n", zret);
        return -1;
    }


    if(zret != Z_DATA_ERROR)
        decode_rle(c, c->zstream.avail_out);
    
    /* make the palette available on the way out */
    if (c->avctx->pix_fmt == PIX_FMT_PAL8) {
        memcpy(c->pic.data[1], c->avctx->palctrl->palette, AVPALETTE_SIZE);
        if (c->avctx->palctrl->palette_changed) {
            c->pic.palette_has_changed = 1;
            c->avctx->palctrl->palette_changed = 0;
        }
    }

#else
    av_log(avctx, AV_LOG_ERROR, "BUG! Zlib support not compiled in frame decoder.\n");
    return -1;
#endif

    *data_size = sizeof(AVFrame);
    *(AVFrame*)data = c->pic;

    /* always report that the buffer was completely consumed */
    return buf_size;
}



/*
 *
 * Init tscc decoder
 *
 */
static int decode_init(AVCodecContext *avctx)
{
    CamtasiaContext * const c = (CamtasiaContext *)avctx->priv_data;
    int zret; // Zlib return code

    c->avctx = avctx;
    avctx->has_b_frames = 0;

    c->pic.data[0] = NULL;
    c->height = avctx->height;

    if (avcodec_check_dimensions(avctx, avctx->height, avctx->width) < 0) {
        return 1;
    }

#ifdef CONFIG_ZLIB
    // Needed if zlib unused or init aborted before inflateInit
    memset(&(c->zstream), 0, sizeof(z_stream)); 
#else
    av_log(avctx, AV_LOG_ERROR, "Zlib support not compiled.\n");
    return 1;
#endif
    switch(avctx->bits_per_sample){
    case  8: avctx->pix_fmt = PIX_FMT_PAL8; break;
    case 16: avctx->pix_fmt = PIX_FMT_RGB555; break;
    case 24:
             avctx->pix_fmt = PIX_FMT_BGR24;
             break;
    case 32: avctx->pix_fmt = PIX_FMT_RGBA32; break;
    default: av_log(avctx, AV_LOG_ERROR, "Camtasia error: unknown depth %i bpp\n", avctx->bits_per_sample);
             return -1;             
    }
    c->bpp = avctx->bits_per_sample;
    c->decomp_size = (avctx->width * c->bpp + (avctx->width + 254) / 255 + 2) * avctx->height + 2;//RLE in the 'best' case

    /* Allocate decompression buffer */
    if (c->decomp_size) {
        if ((c->decomp_buf = av_malloc(c->decomp_size)) == NULL) {
            av_log(avctx, AV_LOG_ERROR, "Can't allocate decompression buffer.\n");
            return 1;
        }
    }
  
#ifdef CONFIG_ZLIB
    c->zstream.zalloc = Z_NULL;
    c->zstream.zfree = Z_NULL;
    c->zstream.opaque = Z_NULL;
    zret = inflateInit(&(c->zstream));
    if (zret != Z_OK) {
        av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret);
        return 1;
    }
#endif

    return 0;
}



/*
 *
 * Uninit tscc decoder
 *
 */
static int decode_end(AVCodecContext *avctx)
{
    CamtasiaContext * const c = (CamtasiaContext *)avctx->priv_data;

    av_freep(&c->decomp_buf);

    if (c->pic.data[0])
        avctx->release_buffer(avctx, &c->pic);
#ifdef CONFIG_ZLIB
    inflateEnd(&(c->zstream));
#endif

    return 0;
}

AVCodec tscc_decoder = {
        "camtasia",
        CODEC_TYPE_VIDEO,
        CODEC_ID_TSCC,
        sizeof(CamtasiaContext),
        decode_init,
        NULL,
        decode_end,
        decode_frame,
        CODEC_CAP_DR1,
};