CDXL demuxer and decoder
authorPaul B Mahol <onemda@gmail.com>
Tue, 14 Feb 2012 17:36:20 +0000 (17:36 +0000)
committerDiego Biurrun <diego@biurrun.de>
Tue, 14 Feb 2012 21:32:53 +0000 (22:32 +0100)
Signed-off-by: Paul B Mahol <onemda@gmail.com>
Signed-off-by: Diego Biurrun <diego@biurrun.de>
Changelog
doc/general.texi
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/avcodec.h
libavcodec/cdxl.c [new file with mode: 0644]
libavcodec/version.h
libavformat/Makefile
libavformat/allformats.c
libavformat/cdxl.c [new file with mode: 0644]
libavformat/version.h

index cc7420c..8bcccf4 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -6,6 +6,7 @@ version <next>:
 - XWD encoder and decoder
 - Support for fragmentation in the mov/mp4 muxer
 - ISMV (Smooth Streaming) muxer
+- CDXL demuxer and decoder
 
 
 version 0.8:
index 50ae764..14624bc 100644 (file)
@@ -131,6 +131,8 @@ library:
     @tab Multimedia format used by Delphine Software games.
 @item CD+G                      @tab   @tab X
     @tab Video format used by CD+G karaoke disks
+@item Commodore CDXL            @tab   @tab X
+    @tab Amiga CD video format
 @item Core Audio Format         @tab   @tab X
     @tab Apple Core Audio Format
 @item CRC testing format        @tab X @tab
@@ -435,6 +437,8 @@ following image formats are supported:
     @tab fourcc: CSCD
 @item CD+G                   @tab     @tab  X
     @tab Video codec for CD+G karaoke disks
+@item CDXL                   @tab     @tab  X
+    @tab Amiga CD video codec
 @item Chinese AVS video      @tab  E  @tab  X
     @tab AVS1-P2, JiZhun profile, encoding through external library libxavs
 @item Delphine Software International CIN video  @tab     @tab  X
index a891651..2eaef6b 100644 (file)
@@ -102,6 +102,7 @@ OBJS-$(CONFIG_C93_DECODER)             += c93.o
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
                                           mpeg12data.o mpegvideo.o
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
+OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
 OBJS-$(CONFIG_CINEPAK_DECODER)         += cinepak.o
 OBJS-$(CONFIG_CLJR_DECODER)            += cljr.o
 OBJS-$(CONFIG_CLJR_ENCODER)            += cljr.o
index cda71e0..1c72177 100644 (file)
@@ -86,6 +86,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER (C93, c93);
     REGISTER_DECODER (CAVS, cavs);
     REGISTER_DECODER (CDGRAPHICS, cdgraphics);
+    REGISTER_DECODER (CDXL, cdxl);
     REGISTER_DECODER (CINEPAK, cinepak);
     REGISTER_ENCDEC  (CLJR, cljr);
     REGISTER_DECODER (CSCD, cscd);
index 6afa140..51b956b 100644 (file)
@@ -244,6 +244,7 @@ enum CodecID {
     CODEC_ID_DXTORY,
     CODEC_ID_V410,
     CODEC_ID_XWD,
+    CODEC_ID_CDXL,
 
     /* various PCM "codecs" */
     CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/cdxl.c b/libavcodec/cdxl.c
new file mode 100644 (file)
index 0000000..a53a001
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * CDXL video decoder
+ * Copyright (c) 2011-2012 Paul B Mahol
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.1 of the License, or (at your option) any later version.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/imgutils.h"
+#include "avcodec.h"
+#include "get_bits.h"
+
+typedef struct {
+    AVCodecContext *avctx;
+    AVFrame        frame;
+    int            bpp;
+    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;
+
+    avcodec_get_frame_defaults(&c->frame);
+    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;
+        AV_WN32(&new_palette[i], (r << 16) | (g << 8) | b);
+    }
+}
+
+static void bitplanar2chunky(CDXLVideoContext *c, int width,
+                             int linesize, uint8_t *out)
+{
+    GetBitContext gb;
+    int x, y, plane;
+
+    init_get_bits(&gb, c->video, c->video_size * 8);
+    memset(out, 0, linesize * c->avctx->height);
+    for (plane = 0; plane < c->bpp; plane++)
+        for (y = 0; y < c->avctx->height; y++)
+            for (x = 0; x < width; x++)
+                out[linesize * y + x] |= get_bits1(&gb) << plane;
+}
+
+static void cdxl_decode_rgb(CDXLVideoContext *c)
+{
+    uint32_t *new_palette = (uint32_t *)c->frame.data[1];
+    int padded_width = FFALIGN(c->avctx->width, 16);
+
+    import_palette(c, new_palette);
+    bitplanar2chunky(c, padded_width, c->frame.linesize[0], c->frame.data[0]);
+}
+
+static void cdxl_decode_ham6(CDXLVideoContext *c)
+{
+    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;
+    out = c->frame.data[0];
+
+    import_palette(c, new_palette);
+    bitplanar2chunky(c, avctx->width, avctx->width, c->new_video);
+
+    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;
+            }
+            AV_WN32(out + x * 3, r | g | b);
+        }
+        out += c->frame.linesize[0];
+    }
+}
+
+static void cdxl_decode_ham8(CDXLVideoContext *c)
+{
+    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;
+    out = c->frame.data[0];
+
+    import_palette(c, new_palette);
+    bitplanar2chunky(c, avctx->width, avctx->width, c->new_video);
+
+    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;
+            }
+            AV_WN32(out + x * 3, r | g | b);
+        }
+        out += c->frame.linesize[0];
+    }
+}
+
+static int cdxl_decode_frame(AVCodecContext *avctx, void *data,
+                             int *data_size, AVPacket *pkt)
+{
+    CDXLVideoContext *c = avctx->priv_data;
+    AVFrame * const p = &c->frame;
+    int ret, w, h, encoding, format, buf_size = pkt->size;
+    const uint8_t *buf = pkt->data;
+
+    if (buf_size < 32)
+        return AVERROR_INVALIDDATA;
+    encoding        = buf[1] & 7;
+    format          = buf[1] & 0xE0;
+    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;
+    if (c->bpp > 8) {
+        av_log_ask_for_sample(avctx, "unsupported pixel size: %d\n", c->bpp);
+        return AVERROR_PATCHWELCOME;
+    }
+    if (format) {
+        av_log_ask_for_sample(avctx, "unsupported pixel format: %d\n", format);
+        return AVERROR_PATCHWELCOME;
+    }
+
+    if ((ret = av_image_check_size(w, h, 0, avctx)) < 0)
+        return ret;
+    if (w != avctx->width || h != avctx->height)
+        avcodec_set_dimensions(avctx, w, h);
+
+    if (encoding == 0) {
+        if (c->video_size < FFALIGN(avctx->width, 16) *
+                            avctx->height * c->bpp / 8)
+            return AVERROR_INVALIDDATA;
+        avctx->pix_fmt = PIX_FMT_PAL8;
+    } else if (encoding == 1 && (c->bpp == 6 || c->bpp == 8)) {
+        if (c->palette_size != (1 << (c->bpp - 1)))
+            return AVERROR_INVALIDDATA;
+        if (c->video_size < avctx->width * avctx->height * c->bpp / 8)
+            return AVERROR_INVALIDDATA;
+        avctx->pix_fmt = PIX_FMT_BGR24;
+    } else {
+        av_log_ask_for_sample(avctx, "unsupported encoding %d and bpp %d\n",
+                              encoding, c->bpp);
+        return AVERROR_PATCHWELCOME;
+    }
+
+    if (p->data[0])
+        avctx->release_buffer(avctx, p);
+
+    p->reference = 0;
+    if ((ret = avctx->get_buffer(avctx, p)) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+        return ret;
+    }
+    p->pict_type = AV_PICTURE_TYPE_I;
+
+    if (encoding) {
+        av_fast_padded_malloc(&c->new_video, &c->new_video_size,
+                              h * w + FF_INPUT_BUFFER_PADDING_SIZE);
+        if (!c->new_video)
+            return AVERROR(ENOMEM);
+        if (c->bpp == 8)
+            cdxl_decode_ham8(c);
+        else
+            cdxl_decode_ham6(c);
+    } else {
+        cdxl_decode_rgb(c);
+    }
+    *data_size      = sizeof(AVFrame);
+    *(AVFrame*)data = c->frame;
+
+    return buf_size;
+}
+
+static av_cold int cdxl_decode_end(AVCodecContext *avctx)
+{
+    CDXLVideoContext *c = avctx->priv_data;
+
+    av_free(c->new_video);
+    if (c->frame.data[0])
+        avctx->release_buffer(avctx, &c->frame);
+
+    return 0;
+}
+
+AVCodec ff_cdxl_decoder = {
+    .name           = "cdxl",
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = CODEC_ID_CDXL,
+    .priv_data_size = sizeof(CDXLVideoContext),
+    .init           = cdxl_decode_init,
+    .close          = cdxl_decode_end,
+    .decode         = cdxl_decode_frame,
+    .capabilities   = CODEC_CAP_DR1,
+    .long_name      = NULL_IF_CONFIG_SMALL("Commodore CDXL video"),
+};
index 485b60e..56aadb2 100644 (file)
@@ -21,7 +21,7 @@
 #define AVCODEC_VERSION_H
 
 #define LIBAVCODEC_VERSION_MAJOR 54
-#define LIBAVCODEC_VERSION_MINOR  0
+#define LIBAVCODEC_VERSION_MINOR  1
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
index dd55e42..aa3cf7c 100644 (file)
@@ -55,6 +55,7 @@ OBJS-$(CONFIG_CAF_DEMUXER)               += cafdec.o caf.o mov.o mov_chan.o \
 OBJS-$(CONFIG_CAVSVIDEO_DEMUXER)         += cavsvideodec.o rawdec.o
 OBJS-$(CONFIG_CAVSVIDEO_MUXER)           += rawenc.o
 OBJS-$(CONFIG_CDG_DEMUXER)               += cdg.o
+OBJS-$(CONFIG_CDXL_DEMUXER)              += cdxl.o
 OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
 OBJS-$(CONFIG_DAUD_DEMUXER)              += daud.o
 OBJS-$(CONFIG_DAUD_MUXER)                += daud.o
index a60688c..f5be7aa 100644 (file)
@@ -76,6 +76,7 @@ void av_register_all(void)
     REGISTER_DEMUXER  (CAF, caf);
     REGISTER_MUXDEMUX (CAVSVIDEO, cavsvideo);
     REGISTER_DEMUXER  (CDG, cdg);
+    REGISTER_DEMUXER  (CDXL, cdxl);
     REGISTER_MUXER    (CRC, crc);
     REGISTER_MUXDEMUX (DAUD, daud);
     REGISTER_DEMUXER  (DFA, dfa);
diff --git a/libavformat/cdxl.c b/libavformat/cdxl.c
new file mode 100644 (file)
index 0000000..f2956dd
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * CDXL demuxer
+ * Copyright (c) 2011-2012 Paul B Mahol
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.1 of the License, or (at your option) any later version.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+
+#define CDXL_HEADER_SIZE 32
+
+typedef struct CDXLDemuxContext {
+    AVClass     *class;
+    int         sample_rate;
+    char        *framerate;
+    AVRational  fps;
+    int         read_chunk;
+    uint8_t     header[CDXL_HEADER_SIZE];
+    int         video_stream_index;
+    int         audio_stream_index;
+} CDXLDemuxContext;
+
+static int cdxl_read_header(AVFormatContext *s)
+{
+    CDXLDemuxContext *cdxl = s->priv_data;
+    int ret;
+
+    if ((ret = av_parse_video_rate(&cdxl->fps, cdxl->framerate)) < 0) {
+        av_log(s, AV_LOG_ERROR,
+               "Could not parse framerate: %s.\n", cdxl->framerate);
+        return ret;
+    }
+
+    cdxl->read_chunk         =  0;
+    cdxl->video_stream_index = -1;
+    cdxl->audio_stream_index = -1;
+
+    s->ctx_flags |= AVFMTCTX_NOHEADER;
+
+    return 0;
+}
+
+static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    CDXLDemuxContext *cdxl = s->priv_data;
+    AVIOContext *pb = s->pb;
+    uint32_t current_size;
+    uint16_t audio_size, palette_size;
+    int32_t  video_size;
+    int64_t  pos;
+    int      ret;
+
+    if (pb->eof_reached)
+        return AVERROR_EOF;
+
+    pos = avio_tell(pb);
+    if (!cdxl->read_chunk &&
+        avio_read(pb, cdxl->header, CDXL_HEADER_SIZE) != CDXL_HEADER_SIZE)
+        return AVERROR_EOF;
+    if (cdxl->header[0] != 1) {
+        av_log(s, AV_LOG_ERROR, "non-standard cdxl file\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    current_size = AV_RB32(&cdxl->header[2]);
+    palette_size = AV_RB16(&cdxl->header[20]);
+    audio_size   = AV_RB16(&cdxl->header[22]);
+
+    if (palette_size > 512)
+        return AVERROR_INVALIDDATA;
+    if (current_size < audio_size + palette_size + CDXL_HEADER_SIZE)
+        return AVERROR_INVALIDDATA;
+    video_size   = current_size - audio_size - CDXL_HEADER_SIZE;
+
+    if (cdxl->read_chunk && audio_size) {
+        if (cdxl->audio_stream_index == -1) {
+            AVStream *st = avformat_new_stream(s, NULL);
+            if (!st)
+                return AVERROR(ENOMEM);
+
+            st->codec->codec_type    = AVMEDIA_TYPE_AUDIO;
+            st->codec->codec_tag     = 0;
+            st->codec->codec_id      = CODEC_ID_PCM_S8;
+            st->codec->channels      = cdxl->header[1] & 0x10 ? 2 : 1;
+            st->codec->sample_rate   = cdxl->sample_rate;
+            cdxl->audio_stream_index = st->index;
+            avpriv_set_pts_info(st, 32, 1, cdxl->sample_rate);
+        }
+
+        ret = av_get_packet(pb, pkt, audio_size);
+        if (ret < 0)
+            return ret;
+        pkt->stream_index = cdxl->audio_stream_index;
+        pkt->pos          = pos;
+        pkt->duration     = audio_size;
+        cdxl->read_chunk  = 0;
+    } else {
+        if (cdxl->video_stream_index == -1) {
+            AVStream *st = avformat_new_stream(s, NULL);
+            if (!st)
+                return AVERROR(ENOMEM);
+
+            st->codec->codec_type    = AVMEDIA_TYPE_VIDEO;
+            st->codec->codec_tag     = 0;
+            st->codec->codec_id      = CODEC_ID_CDXL;
+            st->codec->width         = AV_RB16(&cdxl->header[14]);
+            st->codec->height        = AV_RB16(&cdxl->header[16]);
+            cdxl->video_stream_index = st->index;
+            avpriv_set_pts_info(st, 63, cdxl->fps.den, cdxl->fps.num);
+        }
+
+        if (av_new_packet(pkt, video_size + CDXL_HEADER_SIZE) < 0)
+            return AVERROR(ENOMEM);
+        memcpy(pkt->data, cdxl->header, CDXL_HEADER_SIZE);
+        ret = avio_read(pb, pkt->data + CDXL_HEADER_SIZE, video_size);
+        if (ret < 0) {
+            av_free_packet(pkt);
+            return ret;
+        }
+        pkt->stream_index  = cdxl->video_stream_index;
+        pkt->flags        |= AV_PKT_FLAG_KEY;
+        pkt->pos           = pos;
+        cdxl->read_chunk   = audio_size;
+    }
+
+    return ret;
+}
+
+#define OFFSET(x) offsetof(CDXLDemuxContext, x)
+static const AVOption cdxl_options[] = {
+    { "sample_rate", "", OFFSET(sample_rate), AV_OPT_TYPE_INT,    { .dbl = 11025 }, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
+    { "framerate",   "", OFFSET(framerate),   AV_OPT_TYPE_STRING, { .str = "10" },  0, 0,       AV_OPT_FLAG_DECODING_PARAM },
+    { NULL },
+};
+
+static const AVClass cdxl_demuxer_class = {
+    .class_name = "CDXL demuxer",
+    .item_name  = av_default_item_name,
+    .option     = cdxl_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_cdxl_demuxer = {
+    .name           = "cdxl",
+    .long_name      = NULL_IF_CONFIG_SMALL("Commodore CDXL video format"),
+    .priv_data_size = sizeof(CDXLDemuxContext),
+    .read_header    = cdxl_read_header,
+    .read_packet    = cdxl_read_packet,
+    .extensions     = "cdxl,xl",
+    .flags          = AVFMT_GENERIC_INDEX,
+    .priv_class     = &cdxl_demuxer_class,
+};
index 443d752..b944589 100644 (file)
@@ -30,7 +30,7 @@
 #include "libavutil/avutil.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 54
-#define LIBAVFORMAT_VERSION_MINOR  0
+#define LIBAVFORMAT_VERSION_MINOR  1
 #define LIBAVFORMAT_VERSION_MICRO  0
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \