# HG changeset patch # User reimar # Date 1143498170 0 # Node ID 28aaf0a0135eed45f15862afce22e570082d1ff1 # Parent 8f048c3295ff23356953089d50cd1f60743009ef NuppelVideo/MythTVVideo support, including rtjpeg decoder diff -r 8f048c3295ff -r 28aaf0a0135e Makefile --- a/Makefile Mon Mar 27 12:51:19 2006 +0000 +++ b/Makefile Mon Mar 27 22:22:50 2006 +0000 @@ -180,6 +180,11 @@ OBJS+= cscd.o OBJS+= lzo.o endif +ifeq ($(CONFIG_NUV_DECODER),yes) + OBJS+= nuv.o + OBJS+= rtjpeg.o + OBJS+= lzo.o +endif ifeq ($(CONFIG_ULTI_DECODER),yes) OBJS+= ulti.o endif diff -r 8f048c3295ff -r 28aaf0a0135e allcodecs.c --- a/allcodecs.c Mon Mar 27 12:51:19 2006 +0000 +++ b/allcodecs.c Mon Mar 27 22:22:50 2006 +0000 @@ -267,6 +267,9 @@ #ifdef CONFIG_CSCD_DECODER register_avcodec(&cscd_decoder); #endif //CONFIG_CSCD_DECODER +#ifdef CONFIG_NUV_DECODER + register_avcodec(&nuv_decoder); +#endif //CONFIG_NUV_DECODER #ifdef CONFIG_ULTI_DECODER register_avcodec(&ulti_decoder); #endif //CONFIG_ULTI_DECODER diff -r 8f048c3295ff -r 28aaf0a0135e avcodec.h --- a/avcodec.h Mon Mar 27 12:51:19 2006 +0000 +++ b/avcodec.h Mon Mar 27 22:22:50 2006 +0000 @@ -119,6 +119,7 @@ CODEC_ID_ZMBV, CODEC_ID_AVS, CODEC_ID_SMACKVIDEO, + CODEC_ID_NUV, /* various pcm "codecs" */ CODEC_ID_PCM_S16LE= 0x10000, @@ -2221,6 +2222,7 @@ extern AVCodec flac_decoder; extern AVCodec tscc_decoder; extern AVCodec cscd_decoder; +extern AVCodec nuv_decoder; extern AVCodec ulti_decoder; extern AVCodec qdraw_decoder; extern AVCodec xl_decoder; diff -r 8f048c3295ff -r 28aaf0a0135e nuv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nuv.c Mon Mar 27 22:22:50 2006 +0000 @@ -0,0 +1,214 @@ +/* + * NuppelVideo decoder + * Copyright (c) 2006 Reimar Doeffinger + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +#include "common.h" +#include "avcodec.h" + +#include "bswap.h" +#include "dsputil.h" +#include "lzo.h" +#include "rtjpeg.h" + +typedef struct { + AVFrame pic; + int width, height; + unsigned int decomp_size; + unsigned char* decomp_buf; + uint32_t lq[64], cq[64]; + RTJpegContext rtj; + DSPContext dsp; +} NuvContext; + +/** + * \brief copy frame data from buffer to AVFrame, handling stride. + * \param f destination AVFrame + * \param src source buffer, does not use any line-stride + * \param width width of the video frame + * \param height height of the video frame + */ +static void copy_frame(AVFrame *f, uint8_t *src, + int width, int height) { + AVPicture pic; + avpicture_fill(&pic, src, PIX_FMT_YUV420P, width, height); + img_copy((AVPicture *)f, &pic, PIX_FMT_YUV420P, width, height); +} + +/** + * \brief extract quantization tables from codec data into our context + */ +static int get_quant(AVCodecContext *avctx, NuvContext *c, + uint8_t *buf, int size) { + int i; + if (size < 2 * 64 * 4) { + av_log(avctx, AV_LOG_ERROR, "insufficient rtjpeg quant data\n"); + return -1; + } + for (i = 0; i < 64; i++, buf += 4) + c->lq[i] = LE_32(buf); + for (i = 0; i < 64; i++, buf += 4) + c->cq[i] = LE_32(buf); + return 0; +} + +static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, + uint8_t *buf, int buf_size) { + NuvContext *c = (NuvContext *)avctx->priv_data; + AVFrame *picture = data; + int orig_size = buf_size; + enum {NUV_UNCOMPRESSED = '0', NUV_RTJPEG = '1', + NUV_RTJPEG_IN_LZO = '2', NUV_LZO = '3', + NUV_BLACK = 'N', NUV_COPY_LAST = 'L'} comptype; + + if (buf_size < 12) { + av_log(avctx, AV_LOG_ERROR, "coded frame too small\n"); + return -1; + } + + if (c->pic.data[0]) + avctx->release_buffer(avctx, &c->pic); + c->pic.reference = 1; + c->pic.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_READABLE | + FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE; + if (avctx->get_buffer(avctx, &c->pic) < 0) { + av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); + return -1; + } + + // codec data (rtjpeg quant tables) + if (buf[0] == 'D' && buf[1] == 'R') { + int ret; + // skip rest of the frameheader. + buf = &buf[12]; + buf_size -= 12; + ret = get_quant(avctx, c, buf, buf_size); + if (ret < 0) + return ret; + rtjpeg_decode_init(&c->rtj, &c->dsp, c->width, c->height, c->lq, c->cq); + return orig_size; + } + + if (buf[0] != 'V' || buf_size < 12) { + av_log(avctx, AV_LOG_ERROR, "not a nuv video frame\n"); + return -1; + } + comptype = buf[1]; + // skip rest of the frameheader. + buf = &buf[12]; + buf_size -= 12; + + c->pic.pict_type = FF_I_TYPE; + c->pic.key_frame = 1; + // decompress/copy/whatever data + switch (comptype) { + case NUV_UNCOMPRESSED: { + int height = c->height; + if (buf_size < c->width * height * 3 / 2) { + av_log(avctx, AV_LOG_ERROR, "uncompressed frame too short\n"); + height = buf_size / c->width / 3 * 2; + } + copy_frame(&c->pic, buf, c->width, height); + break; + } + case NUV_RTJPEG: { + rtjpeg_decode_frame_yuv420(&c->rtj, &c->pic, buf, buf_size); + break; + } + case NUV_RTJPEG_IN_LZO: { + int outlen = c->decomp_size, inlen = buf_size; + if (lzo1x_decode(c->decomp_buf, &outlen, buf, &inlen)) + av_log(avctx, AV_LOG_ERROR, "error during lzo decompression\n"); + rtjpeg_decode_frame_yuv420(&c->rtj, &c->pic, c->decomp_buf, c->decomp_size); + break; + } + case NUV_LZO: { + int outlen = c->decomp_size, inlen = buf_size; + if (lzo1x_decode(c->decomp_buf, &outlen, buf, &inlen)) + av_log(avctx, AV_LOG_ERROR, "error during lzo decompression\n"); + copy_frame(&c->pic, c->decomp_buf, c->width, c->height); + break; + } + case NUV_BLACK: { + memset(c->pic.data[0], 0, c->width * c->height); + memset(c->pic.data[1], 128, c->width * c->height / 4); + memset(c->pic.data[2], 128, c->width * c->height / 4); + break; + } + case NUV_COPY_LAST: { + c->pic.pict_type = FF_P_TYPE; + c->pic.key_frame = 0; + /* nothing more to do here */ + break; + } + default: + av_log(avctx, AV_LOG_ERROR, "unknown compression\n"); + return -1; + } + + *picture = c->pic; + *data_size = sizeof(AVFrame); + return orig_size; +} + +static int decode_init(AVCodecContext *avctx) { + NuvContext *c = (NuvContext *)avctx->priv_data; + avctx->width = (avctx->width + 1) & ~1; + avctx->height = (avctx->height + 1) & ~1; + if (avcodec_check_dimensions(avctx, avctx->height, avctx->width) < 0) { + return 1; + } + avctx->has_b_frames = 0; + avctx->pix_fmt = PIX_FMT_YUV420P; + c->pic.data[0] = NULL; + c->width = avctx->width; + c->height = avctx->height; + c->decomp_size = c->height * c->width * 3 / 2; + c->decomp_buf = av_malloc(c->decomp_size + LZO_OUTPUT_PADDING); + if (!c->decomp_buf) { + av_log(avctx, AV_LOG_ERROR, "Can't allocate decompression buffer.\n"); + return 1; + } + dsputil_init(&c->dsp, avctx); + if (avctx->extradata_size) + get_quant(avctx, c, avctx->extradata, avctx->extradata_size); + rtjpeg_decode_init(&c->rtj, &c->dsp, c->width, c->height, c->lq, c->cq); + return 0; +} + +static int decode_end(AVCodecContext *avctx) { + NuvContext *c = (NuvContext *)avctx->priv_data; + av_freep(&c->decomp_buf); + if (c->pic.data[0]) + avctx->release_buffer(avctx, &c->pic); + return 0; +} + +AVCodec nuv_decoder = { + "nuv", + CODEC_TYPE_VIDEO, + CODEC_ID_NUV, + sizeof(NuvContext), + decode_init, + NULL, + decode_end, + decode_frame, + CODEC_CAP_DR1, +}; + diff -r 8f048c3295ff -r 28aaf0a0135e rtjpeg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtjpeg.c Mon Mar 27 22:22:50 2006 +0000 @@ -0,0 +1,162 @@ +/* + * RTJpeg decoding functions + * Copyright (c) 2006 Reimar Doeffinger + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "common.h" +#include "bitstream.h" +#include "dsputil.h" +#include "rtjpeg.h" + +#define PUT_COEFF(c) \ + i = scan[coeff--]; \ + block[i] = (c) * quant[i]; + +//! aligns the bitstream to the give power of two +#define ALIGN(a) \ + n = (-get_bits_count(gb)) & (a - 1); \ + if (n) {skip_bits(gb, n);} + +/** + * \brief read one block from stream + * \param gb contains stream data + * \param block where data is written to + * \param scan array containing the mapping stream address -> block position + * \param quant quantization factors + * + * Note: GetBitContext is used to make the code simpler, since all data is + * aligned this could be done faster in a different way, e.g. as it is done + * in MPlayer libmpcodecs/native/RTjpegN.c + */ +static inline int get_block(GetBitContext *gb, DCTELEM *block, uint8_t *scan, + uint32_t *quant) { + int coeff, i, n; + int8_t ac; + uint8_t dc = get_bits(gb, 8); + + // block not coded + if (dc == 255) + return 0; + + // number of non-zero coefficients + coeff = get_bits(gb, 6); + // normally we would only need to clear the (63 - coeff) last values, + // but since we do not know where they are we just clear the whole block + memset(block, 0, 64 * sizeof(DCTELEM)); + + // 2 bits per coefficient + while (coeff) { + ac = get_sbits(gb, 2); + if (ac == -2) + break; // continue with more bits + PUT_COEFF(ac); + } + + // 4 bits per coefficient + ALIGN(4); + while (coeff) { + ac = get_sbits(gb, 4); + if (ac == -8) + break; // continue with more bits + PUT_COEFF(ac); + } + + // 8 bits per coefficient + ALIGN(8); + while (coeff) { + ac = get_sbits(gb, 8); + PUT_COEFF(ac); + } + + PUT_COEFF(dc); + return 1; +} + +/** + * \brief decode one rtjpeg YUV420 frame + * \param c context, must be initialized via rtjpeg_decode_init + * \param f AVFrame to place decoded frame into. If parts of the frame + * are not coded they are left unchanged, so consider initializing it + * \param buf buffer containing input data + * \param buf_size length of input data in bytes + * \return number of bytes consumed from the input buffer + */ +int rtjpeg_decode_frame_yuv420(RTJpegContext *c, AVFrame *f, + uint8_t *buf, int buf_size) { + GetBitContext gb; + int w = c->w / 16, h = c->h / 16; + int x, y; + void *y1 = f->data[0], *y2 = f->data[0] + 8 * f->linesize[0]; + void *u = f->data[1], *v = f->data[2]; + init_get_bits(&gb, buf, buf_size * 8); + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + if (get_block(&gb, c->block, c->scan, c->lquant)) + c->dsp->idct_put(y1, f->linesize[0], c->block); + y1 += 8; + if (get_block(&gb, c->block, c->scan, c->lquant)) + c->dsp->idct_put(y1, f->linesize[0], c->block); + y1 += 8; + if (get_block(&gb, c->block, c->scan, c->lquant)) + c->dsp->idct_put(y2, f->linesize[0], c->block); + y2 += 8; + if (get_block(&gb, c->block, c->scan, c->lquant)) + c->dsp->idct_put(y2, f->linesize[0], c->block); + y2 += 8; + if (get_block(&gb, c->block, c->scan, c->cquant)) + c->dsp->idct_put(u, f->linesize[1], c->block); + u += 8; + if (get_block(&gb, c->block, c->scan, c->cquant)) + c->dsp->idct_put(v, f->linesize[2], c->block); + v += 8; + } + y1 += 2 * 8 * (f->linesize[0] - w); + y2 += 2 * 8 * (f->linesize[0] - w); + u += 8 * (f->linesize[1] - w); + v += 8 * (f->linesize[2] - w); + } + return get_bits_count(&gb) / 8; +} + +/** + * \brief initialize an RTJpegContext, may be called multiple times + * \param c context to initialize + * \param dsp specifies the idct to use for decoding + * \param width width of image, will be rounded down to the nearest multiple + * of 16 for decoding + * \param height height of image, will be rounded down to the nearest multiple + * of 16 for decoding + * \param lquant luma quantization table to use + * \param cquant chroma quantization table to use + */ +void rtjpeg_decode_init(RTJpegContext *c, DSPContext *dsp, + int width, int height, + uint32_t *lquant, uint32_t *cquant) { + int i; + c->dsp = dsp; + for (i = 0; i < 64; i++) { + int z = ff_zigzag_direct[i]; + int p = c->dsp->idct_permutation[i]; + z = ((z << 3) | (z >> 3)) & 63; // rtjpeg uses a transposed variant + + // permute the scan and quantization tables for the chosen idct + c->scan[i] = c->dsp->idct_permutation[z]; + c->lquant[p] = lquant[i]; + c->cquant[p] = cquant[i]; + } + c->w = width; + c->h = height; +} diff -r 8f048c3295ff -r 28aaf0a0135e rtjpeg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtjpeg.h Mon Mar 27 22:22:50 2006 +0000 @@ -0,0 +1,19 @@ +#ifndef RTJPEG_H +#define RTJPEG_H + +typedef struct { + int w, h; + DSPContext *dsp; + DCTELEM block[64]; + uint8_t scan[64]; + uint32_t lquant[64]; + uint32_t cquant[64]; +} RTJpegContext; + +void rtjpeg_decode_init(RTJpegContext *c, DSPContext *dsp, + int width, int height, + uint32_t *lquant, uint32_t *cquant); + +int rtjpeg_decode_frame_yuv420(RTJpegContext *c, AVFrame *f, + uint8_t *buf, int buf_size); +#endif