libavcodec/nuv.c
dfca23e3
 /*
  * NuppelVideo decoder
  * Copyright (c) 2006 Reimar Doeffinger
  *
b78e7197
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
dfca23e3
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
b78e7197
  * version 2.1 of the License, or (at your option) any later version.
dfca23e3
  *
b78e7197
  * FFmpeg is distributed in the hope that it will be useful,
dfca23e3
  * 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
b78e7197
  * License along with FFmpeg; if not, write to the Free Software
dfca23e3
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
cf22705e
 
dfca23e3
 #include <stdio.h>
 #include <stdlib.h>
95e1dfee
 #include <limits.h>
dfca23e3
 
245976da
 #include "libavutil/bswap.h"
1d9c2dc8
 #include "libavutil/common.h"
245976da
 #include "libavutil/lzo.h"
7ffe76e5
 #include "libavutil/imgutils.h"
dfca23e3
 #include "avcodec.h"
759001c5
 #include "internal.h"
dfca23e3
 #include "rtjpeg.h"
 
 typedef struct {
     AVFrame pic;
4f8a91c9
     int codec_frameheader;
1551e8e8
     int quality;
dfca23e3
     int width, height;
     unsigned int decomp_size;
cf22705e
     unsigned char *decomp_buf;
dfca23e3
     uint32_t lq[64], cq[64];
     RTJpegContext rtj;
     DSPContext dsp;
 } NuvContext;
 
4f8a91c9
 static const uint8_t fallback_lquant[] = {
     16,  11,  10,  16,  24,  40,  51,  61,
     12,  12,  14,  19,  26,  58,  60,  55,
     14,  13,  16,  24,  40,  57,  69,  56,
     14,  17,  22,  29,  51,  87,  80,  62,
     18,  22,  37,  56,  68, 109, 103,  77,
     24,  35,  55,  64,  81, 104, 113,  92,
     49,  64,  78,  87, 103, 121, 120, 101,
     72,  92,  95,  98, 112, 100, 103,  99
 };
 
 static const uint8_t fallback_cquant[] = {
     17, 18, 24, 47, 99, 99, 99, 99,
     18, 21, 26, 66, 99, 99, 99, 99,
     24, 26, 56, 99, 99, 99, 99, 99,
     47, 66, 99, 99, 99, 99, 99, 99,
     99, 99, 99, 99, 99, 99, 99, 99,
     99, 99, 99, 99, 99, 99, 99, 99,
     99, 99, 99, 99, 99, 99, 99, 99,
     99, 99, 99, 99, 99, 99, 99, 99
 };
 
dfca23e3
 /**
adbfc605
  * @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
dfca23e3
  */
cf22705e
 static void copy_frame(AVFrame *f, const uint8_t *src, int width, int height)
 {
dfca23e3
     AVPicture pic;
716d413c
     avpicture_fill(&pic, src, AV_PIX_FMT_YUV420P, width, height);
     av_picture_copy((AVPicture *)f, &pic, AV_PIX_FMT_YUV420P, width, height);
dfca23e3
 }
 
 /**
adbfc605
  * @brief extract quantization tables from codec data into our context
dfca23e3
  */
cf22705e
 static int get_quant(AVCodecContext *avctx, NuvContext *c, const uint8_t *buf,
                      int size)
 {
dfca23e3
     int i;
     if (size < 2 * 64 * 4) {
         av_log(avctx, AV_LOG_ERROR, "insufficient rtjpeg quant data\n");
3344f5cb
         return AVERROR_INVALIDDATA;
dfca23e3
     }
     for (i = 0; i < 64; i++, buf += 4)
fead30d4
         c->lq[i] = AV_RL32(buf);
dfca23e3
     for (i = 0; i < 64; i++, buf += 4)
fead30d4
         c->cq[i] = AV_RL32(buf);
dfca23e3
     return 0;
 }
 
4f8a91c9
 /**
adbfc605
  * @brief set quantization tables from a quality value
4f8a91c9
  */
cf22705e
 static void get_quant_quality(NuvContext *c, int quality)
 {
4f8a91c9
     int i;
fd065d05
     quality = FFMAX(quality, 1);
4f8a91c9
     for (i = 0; i < 64; i++) {
         c->lq[i] = (fallback_lquant[i] << 7) / quality;
         c->cq[i] = (fallback_cquant[i] << 7) / quality;
     }
 }
 
cf22705e
 static int codec_reinit(AVCodecContext *avctx, int width, int height,
                         int quality)
 {
1551e8e8
     NuvContext *c = avctx->priv_data;
3344f5cb
     int ret;
 
7f6c828f
     width  = FFALIGN(width,  2);
     height = FFALIGN(height, 2);
1551e8e8
     if (quality >= 0)
         get_quant_quality(c, quality);
     if (width != c->width || height != c->height) {
95e1dfee
         // also reserve space for a possible additional header
         int buf_size = 24 + height * width * 3 / 2 + AV_LZO_OUTPUT_PADDING;
8943925d
         if (buf_size > INT_MAX/8)
95e1dfee
             return -1;
3344f5cb
         if ((ret = av_image_check_size(height, width, 0, avctx)) < 0)
             return ret;
cf22705e
         avctx->width  = c->width  = width;
1551e8e8
         avctx->height = c->height = height;
cf22705e
         av_fast_malloc(&c->decomp_buf, &c->decomp_size,
7ae473e8
                        buf_size);
1551e8e8
         if (!c->decomp_buf) {
cf22705e
             av_log(avctx, AV_LOG_ERROR,
                    "Can't allocate decompression buffer.\n");
95e1dfee
             return AVERROR(ENOMEM);
1551e8e8
         }
cf22705e
         ff_rtjpeg_decode_init(&c->rtj, &c->dsp, c->width, c->height,
                               c->lq, c->cq);
95e1dfee
         return 1;
1551e8e8
     } else if (quality != c->quality)
cf22705e
         ff_rtjpeg_decode_init(&c->rtj, &c->dsp, c->width, c->height,
                               c->lq, c->cq);
 
95e1dfee
     return 0;
1551e8e8
 }
 
df9b9567
 static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
cf22705e
                         AVPacket *avpkt)
 {
7a00bbad
     const uint8_t *buf = avpkt->data;
cf22705e
     int buf_size       = avpkt->size;
     NuvContext *c      = avctx->priv_data;
     AVFrame *picture   = data;
     int orig_size      = buf_size;
2881a656
     int keyframe;
95e1dfee
     int size_change = 0;
dff6197d
     int result, init_frame = !avctx->frame_number;
cf22705e
     enum {
         NUV_UNCOMPRESSED  = '0',
         NUV_RTJPEG        = '1',
         NUV_RTJPEG_IN_LZO = '2',
         NUV_LZO           = '3',
         NUV_BLACK         = 'N',
         NUV_COPY_LAST     = 'L'
     } comptype;
dfca23e3
 
     if (buf_size < 12) {
         av_log(avctx, AV_LOG_ERROR, "coded frame too small\n");
3344f5cb
         return AVERROR_INVALIDDATA;
dfca23e3
     }
 
     // codec data (rtjpeg quant tables)
     if (buf[0] == 'D' && buf[1] == 'R') {
         int ret;
         // skip rest of the frameheader.
cf22705e
         buf       = &buf[12];
dfca23e3
         buf_size -= 12;
cf22705e
         ret       = get_quant(avctx, c, buf, buf_size);
dfca23e3
         if (ret < 0)
             return ret;
cf22705e
         ff_rtjpeg_decode_init(&c->rtj, &c->dsp, c->width, c->height, c->lq,
                               c->cq);
dfca23e3
         return orig_size;
     }
 
e1bc0171
     if (buf_size < 12 || buf[0] != 'V') {
dfca23e3
         av_log(avctx, AV_LOG_ERROR, "not a nuv video frame\n");
3344f5cb
         return AVERROR_INVALIDDATA;
dfca23e3
     }
     comptype = buf[1];
2881a656
     switch (comptype) {
cf22705e
     case NUV_RTJPEG_IN_LZO:
     case NUV_RTJPEG:
         keyframe = !buf[2];
         break;
     case NUV_COPY_LAST:
         keyframe = 0;
         break;
     default:
         keyframe = 1;
         break;
2881a656
     }
95e1dfee
 retry:
dfca23e3
     // skip rest of the frameheader.
cf22705e
     buf       = &buf[12];
dfca23e3
     buf_size -= 12;
c612b00d
     if (comptype == NUV_RTJPEG_IN_LZO || comptype == NUV_LZO) {
95e1dfee
         int outlen = c->decomp_size - AV_LZO_OUTPUT_PADDING, inlen = buf_size;
0b178e56
         if (av_lzo1x_decode(c->decomp_buf, &outlen, buf, &inlen))
c612b00d
             av_log(avctx, AV_LOG_ERROR, "error during lzo decompression\n");
cf22705e
         buf      = c->decomp_buf;
266f6eef
         buf_size = c->decomp_size - AV_LZO_OUTPUT_PADDING - outlen;
c612b00d
     }
4f8a91c9
     if (c->codec_frameheader) {
1551e8e8
         int w, h, q;
7ae473e8
         if (buf_size < RTJPEG_HEADER_SIZE) {
98b01206
             av_log(avctx, AV_LOG_ERROR, "Too small NUV video frame\n");
             return AVERROR_INVALIDDATA;
         }
         // There seem to exist two variants of this header: one starts with 'V'
         // and 5 bytes unknown, the other matches current MythTV and is 4 bytes size,
         // 1 byte header size (== 12), 1 byte version (== 0)
         if (buf[0] != 'V' && AV_RL16(&buf[4]) != 0x000c) {
             av_log(avctx, AV_LOG_ERROR, "Unknown secondary frame header (wrong codec_tag?)\n");
f6afacdb
             return AVERROR_INVALIDDATA;
048905a4
         }
1551e8e8
         w = AV_RL16(&buf[6]);
         h = AV_RL16(&buf[8]);
         q = buf[10];
3344f5cb
         if ((result = codec_reinit(avctx, w, h, q)) < 0)
             return result;
8943925d
         if (result) {
95e1dfee
             buf = avpkt->data;
             buf_size = avpkt->size;
             size_change = 1;
             goto retry;
         }
cf22705e
         buf       = &buf[RTJPEG_HEADER_SIZE];
859a579e
         buf_size -= RTJPEG_HEADER_SIZE;
4f8a91c9
     }
dfca23e3
 
80e9e63c
     if (size_change || keyframe) {
759001c5
         av_frame_unref(&c->pic);
dff6197d
         init_frame = 1;
     }
759001c5
 
1ec94b0f
     if ((result = ff_reget_buffer(avctx, &c->pic)) < 0)
3344f5cb
         return result;
dff6197d
     if (init_frame) {
         memset(c->pic.data[0], 0,    avctx->height * c->pic.linesize[0]);
         memset(c->pic.data[1], 0x80, avctx->height * c->pic.linesize[1] / 2);
         memset(c->pic.data[2], 0x80, avctx->height * c->pic.linesize[2] / 2);
     }
1551e8e8
 
ce5e49b0
     c->pic.pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
2881a656
     c->pic.key_frame = keyframe;
dfca23e3
     // decompress/copy/whatever data
     switch (comptype) {
cf22705e
     case NUV_LZO:
     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;
dfca23e3
         }
56d09250
         if(height > 0)
             copy_frame(&c->pic, buf, c->width, height);
cf22705e
         break;
     }
     case NUV_RTJPEG_IN_LZO:
     case NUV_RTJPEG:
         ff_rtjpeg_decode_frame_yuv420(&c->rtj, &c->pic, buf, buf_size);
         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:
         /* nothing more to do here */
         break;
     default:
         av_log(avctx, AV_LOG_ERROR, "unknown compression\n");
3344f5cb
         return AVERROR_INVALIDDATA;
dfca23e3
     }
 
759001c5
     if ((result = av_frame_ref(picture, &c->pic)) < 0)
         return result;
 
df9b9567
     *got_frame = 1;
dfca23e3
     return orig_size;
 }
 
cf22705e
 static av_cold int decode_init(AVCodecContext *avctx)
 {
     NuvContext *c  = avctx->priv_data;
3344f5cb
     int ret;
 
716d413c
     avctx->pix_fmt = AV_PIX_FMT_YUV420P;
dfca23e3
     c->pic.data[0] = NULL;
cf22705e
     c->decomp_buf  = NULL;
     c->quality     = -1;
     c->width       = 0;
     c->height      = 0;
 
4f8a91c9
     c->codec_frameheader = avctx->codec_tag == MKTAG('R', 'J', 'P', 'G');
cf22705e
 
dfca23e3
     if (avctx->extradata_size)
         get_quant(avctx, c, avctx->extradata, avctx->extradata_size);
cf22705e
 
9cf0841e
     ff_dsputil_init(&c->dsp, avctx);
cf22705e
 
3344f5cb
     if ((ret = codec_reinit(avctx, avctx->width, avctx->height, -1)) < 0)
         return ret;
cf22705e
 
dfca23e3
     return 0;
 }
 
cf22705e
 static av_cold int decode_end(AVCodecContext *avctx)
 {
e4141433
     NuvContext *c = avctx->priv_data;
cf22705e
 
dfca23e3
     av_freep(&c->decomp_buf);
759001c5
     av_frame_unref(&c->pic);
cf22705e
 
dfca23e3
     return 0;
 }
 
e7e2df27
 AVCodec ff_nuv_decoder = {
ec6402b7
     .name           = "nuv",
     .type           = AVMEDIA_TYPE_VIDEO,
36ef5369
     .id             = AV_CODEC_ID_NUV,
ec6402b7
     .priv_data_size = sizeof(NuvContext),
     .init           = decode_init,
     .close          = decode_end,
     .decode         = decode_frame,
     .capabilities   = CODEC_CAP_DR1,
00c3b67b
     .long_name      = NULL_IF_CONFIG_SMALL("NuppelVideo/RTJPEG"),
dfca23e3
 };