cdxl.c 9.75 KB
Newer Older
Paul B Mahol's avatar
Paul B Mahol committed
1 2 3 4
/*
 * CDXL video decoder
 * Copyright (c) 2011-2012 Paul B Mahol
 *
5
 * This file is part of FFmpeg.
Paul B Mahol's avatar
Paul B Mahol committed
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
Paul B Mahol's avatar
Paul B Mahol committed
8 9 10 11
 * 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.
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
Paul B Mahol's avatar
Paul B Mahol committed
13 14 15 16 17
 * 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
18
 * License along with FFmpeg; if not, write to the Free Software
Paul B Mahol's avatar
Paul B Mahol committed
19 20 21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

Paul B Mahol's avatar
Paul B Mahol committed
22 23 24 25 26 27
/**
 * @file
 * Commodore CDXL video decoder
 * @author Paul B Mahol
 */

28 29
#define UNCHECKED_BITSTREAM_READER 1

Paul B Mahol's avatar
Paul B Mahol committed
30 31 32
#include "libavutil/intreadwrite.h"
#include "libavutil/imgutils.h"
#include "avcodec.h"
33
#include "bytestream.h"
Paul B Mahol's avatar
Paul B Mahol committed
34
#include "get_bits.h"
35
#include "internal.h"
Paul B Mahol's avatar
Paul B Mahol committed
36

37
#define BIT_PLANAR   0x00
38 39
#define CHUNKY       0x20
#define BYTE_PLANAR  0x40
40 41 42
#define BIT_LINE     0x80
#define BYTE_LINE    0xC0

43
typedef struct CDXLVideoContext {
Paul B Mahol's avatar
Paul B Mahol committed
44 45
    AVCodecContext *avctx;
    int            bpp;
46 47
    int            format;
    int            padded_bits;
Paul B Mahol's avatar
Paul B Mahol committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    const uint8_t  *palette;
    int            palette_size;
    const uint8_t  *video;
    int            video_size;
    uint8_t        *new_video;
    int            new_video_size;
} CDXLVideoContext;

static av_cold int cdxl_decode_init(AVCodecContext *avctx)
{
    CDXLVideoContext *c = avctx->priv_data;

    c->new_video_size = 0;
    c->avctx          = avctx;

    return 0;
}

static void import_palette(CDXLVideoContext *c, uint32_t *new_palette)
{
    int i;

    for (i = 0; i < c->palette_size / 2; i++) {
        unsigned rgb = AV_RB16(&c->palette[i * 2]);
        unsigned r   = ((rgb >> 8) & 0xF) * 0x11;
        unsigned g   = ((rgb >> 4) & 0xF) * 0x11;
        unsigned b   =  (rgb       & 0xF) * 0x11;
75
        AV_WN32(&new_palette[i], (0xFFU << 24) | (r << 16) | (g << 8) | b);
Paul B Mahol's avatar
Paul B Mahol committed
76 77 78
    }
}

79
static void bitplanar2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
Paul B Mahol's avatar
Paul B Mahol committed
80 81 82 83
{
    GetBitContext gb;
    int x, y, plane;

84 85
    if (init_get_bits8(&gb, c->video, c->video_size) < 0)
        return;
86 87 88
    for (plane = 0; plane < c->bpp; plane++) {
        for (y = 0; y < c->avctx->height; y++) {
            for (x = 0; x < c->avctx->width; x++)
Paul B Mahol's avatar
Paul B Mahol committed
89
                out[linesize * y + x] |= get_bits1(&gb) << plane;
90 91 92 93 94 95 96 97 98 99
            skip_bits(&gb, c->padded_bits);
        }
    }
}

static void bitline2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
{
    GetBitContext  gb;
    int x, y, plane;

100 101
    if (init_get_bits8(&gb, c->video, c->video_size) < 0)
        return;
102 103 104 105 106
    for (y = 0; y < c->avctx->height; y++) {
        for (plane = 0; plane < c->bpp; plane++) {
            for (x = 0; x < c->avctx->width; x++)
                out[linesize * y + x] |= get_bits1(&gb) << plane;
            skip_bits(&gb, c->padded_bits);
107 108
        }
    }
Paul B Mahol's avatar
Paul B Mahol committed
109 110
}

111 112 113 114 115 116 117 118 119 120 121
static void chunky2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
{
    GetByteContext gb;
    int y;

    bytestream2_init(&gb, c->video, c->video_size);
    for (y = 0; y < c->avctx->height; y++) {
        bytestream2_get_buffer(&gb, out + linesize * y, c->avctx->width * 3);
    }
}

122 123 124 125 126 127 128 129 130 131 132
static void import_format(CDXLVideoContext *c, int linesize, uint8_t *out)
{
    memset(out, 0, linesize * c->avctx->height);

    switch (c->format) {
    case BIT_PLANAR:
        bitplanar2chunky(c, linesize, out);
        break;
    case BIT_LINE:
        bitline2chunky(c, linesize, out);
        break;
133 134 135
    case CHUNKY:
        chunky2chunky(c, linesize, out);
        break;
136 137 138
    }
}

139
static void cdxl_decode_rgb(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
140
{
141
    uint32_t *new_palette = (uint32_t *)frame->data[1];
Paul B Mahol's avatar
Paul B Mahol committed
142

143
    memset(frame->data[1], 0, AVPALETTE_SIZE);
Paul B Mahol's avatar
Paul B Mahol committed
144
    import_palette(c, new_palette);
145
    import_format(c, frame->linesize[0], frame->data[0]);
Paul B Mahol's avatar
Paul B Mahol committed
146 147
}

148 149 150 151 152
static void cdxl_decode_raw(CDXLVideoContext *c, AVFrame *frame)
{
    import_format(c, frame->linesize[0], frame->data[0]);
}

153
static void cdxl_decode_ham6(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
154 155 156 157 158 159 160
{
    AVCodecContext *avctx = c->avctx;
    uint32_t new_palette[16], r, g, b;
    uint8_t *ptr, *out, index, op;
    int x, y;

    ptr = c->new_video;
161
    out = frame->data[0];
Paul B Mahol's avatar
Paul B Mahol committed
162 163

    import_palette(c, new_palette);
164
    import_format(c, avctx->width, c->new_video);
Paul B Mahol's avatar
Paul B Mahol committed
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189

    for (y = 0; y < avctx->height; y++) {
        r = new_palette[0] & 0xFF0000;
        g = new_palette[0] & 0xFF00;
        b = new_palette[0] & 0xFF;
        for (x = 0; x < avctx->width; x++) {
            index  = *ptr++;
            op     = index >> 4;
            index &= 15;
            switch (op) {
            case 0:
                r = new_palette[index] & 0xFF0000;
                g = new_palette[index] & 0xFF00;
                b = new_palette[index] & 0xFF;
                break;
            case 1:
                b = index * 0x11;
                break;
            case 2:
                r = index * 0x11 << 16;
                break;
            case 3:
                g = index * 0x11 << 8;
                break;
            }
190
            AV_WL24(out + x * 3, r | g | b);
Paul B Mahol's avatar
Paul B Mahol committed
191
        }
192
        out += frame->linesize[0];
Paul B Mahol's avatar
Paul B Mahol committed
193 194 195
    }
}

196
static void cdxl_decode_ham8(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
197 198 199 200 201 202 203
{
    AVCodecContext *avctx = c->avctx;
    uint32_t new_palette[64], r, g, b;
    uint8_t *ptr, *out, index, op;
    int x, y;

    ptr = c->new_video;
204
    out = frame->data[0];
Paul B Mahol's avatar
Paul B Mahol committed
205 206

    import_palette(c, new_palette);
207
    import_format(c, avctx->width, c->new_video);
Paul B Mahol's avatar
Paul B Mahol committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

    for (y = 0; y < avctx->height; y++) {
        r = new_palette[0] & 0xFF0000;
        g = new_palette[0] & 0xFF00;
        b = new_palette[0] & 0xFF;
        for (x = 0; x < avctx->width; x++) {
            index  = *ptr++;
            op     = index >> 6;
            index &= 63;
            switch (op) {
            case 0:
                r = new_palette[index] & 0xFF0000;
                g = new_palette[index] & 0xFF00;
                b = new_palette[index] & 0xFF;
                break;
            case 1:
                b = (index <<  2) | (b & 3);
                break;
            case 2:
                r = (index << 18) | (r & (3 << 16));
                break;
            case 3:
                g = (index << 10) | (g & (3 << 8));
                break;
            }
233
            AV_WL24(out + x * 3, r | g | b);
Paul B Mahol's avatar
Paul B Mahol committed
234
        }
235
        out += frame->linesize[0];
Paul B Mahol's avatar
Paul B Mahol committed
236 237 238 239
    }
}

static int cdxl_decode_frame(AVCodecContext *avctx, void *data,
240
                             int *got_frame, AVPacket *pkt)
Paul B Mahol's avatar
Paul B Mahol committed
241 242
{
    CDXLVideoContext *c = avctx->priv_data;
243
    AVFrame * const p = data;
244
    int ret, w, h, encoding, aligned_width, buf_size = pkt->size;
Paul B Mahol's avatar
Paul B Mahol committed
245 246 247 248 249
    const uint8_t *buf = pkt->data;

    if (buf_size < 32)
        return AVERROR_INVALIDDATA;
    encoding        = buf[1] & 7;
250
    c->format       = buf[1] & 0xE0;
Paul B Mahol's avatar
Paul B Mahol committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264
    w               = AV_RB16(&buf[14]);
    h               = AV_RB16(&buf[16]);
    c->bpp          = buf[19];
    c->palette_size = AV_RB16(&buf[20]);
    c->palette      = buf + 32;
    c->video        = c->palette + c->palette_size;
    c->video_size   = buf_size - c->palette_size - 32;

    if (c->palette_size > 512)
        return AVERROR_INVALIDDATA;
    if (buf_size < c->palette_size + 32)
        return AVERROR_INVALIDDATA;
    if (c->bpp < 1)
        return AVERROR_INVALIDDATA;
265
    if (c->format != BIT_PLANAR && c->format != BIT_LINE && c->format != CHUNKY) {
266
        avpriv_request_sample(avctx, "Pixel format 0x%0x", c->format);
Paul B Mahol's avatar
Paul B Mahol committed
267 268 269
        return AVERROR_PATCHWELCOME;
    }

270
    if ((ret = ff_set_dimensions(avctx, w, h)) < 0)
Paul B Mahol's avatar
Paul B Mahol committed
271 272
        return ret;

273 274 275 276
    if (c->format == CHUNKY)
        aligned_width = avctx->width;
    else
        aligned_width = FFALIGN(c->avctx->width, 16);
277
    c->padded_bits  = aligned_width - c->avctx->width;
278
    if (c->video_size < aligned_width * avctx->height * (int64_t)c->bpp / 8)
279
        return AVERROR_INVALIDDATA;
280
    if (!encoding && c->palette_size && c->bpp <= 8 && c->format != CHUNKY) {
281
        avctx->pix_fmt = AV_PIX_FMT_PAL8;
282
    } else if (encoding == 1 && (c->bpp == 6 || c->bpp == 8) && c->format != CHUNKY) {
Paul B Mahol's avatar
Paul B Mahol committed
283 284
        if (c->palette_size != (1 << (c->bpp - 1)))
            return AVERROR_INVALIDDATA;
285
        avctx->pix_fmt = AV_PIX_FMT_BGR24;
286 287 288
    } else if (!encoding && c->bpp == 24 && c->format == CHUNKY &&
               !c->palette_size) {
        avctx->pix_fmt = AV_PIX_FMT_RGB24;
Paul B Mahol's avatar
Paul B Mahol committed
289
    } else {
290 291
        avpriv_request_sample(avctx, "Encoding %d, bpp %d and format 0x%x",
                              encoding, c->bpp, c->format);
Paul B Mahol's avatar
Paul B Mahol committed
292 293 294
        return AVERROR_PATCHWELCOME;
    }

295
    if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
Paul B Mahol's avatar
Paul B Mahol committed
296 297 298 299 300
        return ret;
    p->pict_type = AV_PICTURE_TYPE_I;

    if (encoding) {
        av_fast_padded_malloc(&c->new_video, &c->new_video_size,
301
                              h * w + AV_INPUT_BUFFER_PADDING_SIZE);
Paul B Mahol's avatar
Paul B Mahol committed
302 303 304
        if (!c->new_video)
            return AVERROR(ENOMEM);
        if (c->bpp == 8)
305
            cdxl_decode_ham8(c, p);
Paul B Mahol's avatar
Paul B Mahol committed
306
        else
307
            cdxl_decode_ham6(c, p);
308
    } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
309
        cdxl_decode_rgb(c, p);
310 311
    } else {
        cdxl_decode_raw(c, p);
Paul B Mahol's avatar
Paul B Mahol committed
312
    }
313
    *got_frame = 1;
Paul B Mahol's avatar
Paul B Mahol committed
314 315 316 317 318 319 320 321

    return buf_size;
}

static av_cold int cdxl_decode_end(AVCodecContext *avctx)
{
    CDXLVideoContext *c = avctx->priv_data;

322
    av_freep(&c->new_video);
Paul B Mahol's avatar
Paul B Mahol committed
323 324 325 326 327 328

    return 0;
}

AVCodec ff_cdxl_decoder = {
    .name           = "cdxl",
329
    .long_name      = NULL_IF_CONFIG_SMALL("Commodore CDXL video"),
Paul B Mahol's avatar
Paul B Mahol committed
330
    .type           = AVMEDIA_TYPE_VIDEO,
331
    .id             = AV_CODEC_ID_CDXL,
Paul B Mahol's avatar
Paul B Mahol committed
332 333 334 335
    .priv_data_size = sizeof(CDXLVideoContext),
    .init           = cdxl_decode_init,
    .close          = cdxl_decode_end,
    .decode         = cdxl_decode_frame,
336
    .capabilities   = AV_CODEC_CAP_DR1,
Paul B Mahol's avatar
Paul B Mahol committed
337
};