Chronomaster DFA decoder

Message ID 20110315084238.GA25495@kst-acer
State Superseded
Headers show

Commit Message

Kostya Shishkov March 15, 2011, 8:42 a.m.
$subj, tested on all samples from that game

---
 Changelog                |    1 +
 doc/general.texi         |    4 +
 libavcodec/Makefile      |    1 +
 libavcodec/allcodecs.c   |    1 +
 libavcodec/avcodec.h     |    3 +-
 libavcodec/dfa.c         |  410 ++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/Makefile     |    1 +
 libavformat/allformats.c |    1 +
 libavformat/dfa.c        |  122 ++++++++++++++
 libavformat/version.h    |    2 +-
 10 files changed, 544 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/dfa.c
 create mode 100644 libavformat/dfa.c

Comments

Reinhard Tartler March 15, 2011, 10:10 a.m. | #1
disclaimer: this is no proper technical review and contains nits only.

Are samples for this codec and format in FATE or in the regression
testsuite?

On Tue, Mar 15, 2011 at 09:42:38 (CET), Kostya Shishkov wrote:

> $subj, tested on all samples from that game
>
> ---
>  Changelog                |    1 +
>  doc/general.texi         |    4 +
>  libavcodec/Makefile      |    1 +
>  libavcodec/allcodecs.c   |    1 +
>  libavcodec/avcodec.h     |    3 +-
>  libavcodec/dfa.c         |  410 ++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/Makefile     |    1 +
>  libavformat/allformats.c |    1 +
>  libavformat/dfa.c        |  122 ++++++++++++++
>  libavformat/version.h    |    2 +-
>  10 files changed, 544 insertions(+), 2 deletions(-)
>  create mode 100644 libavcodec/dfa.c
>  create mode 100644 libavformat/dfa.c
>
> diff --git a/Changelog b/Changelog
> index 95c8b65..ace6ded 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -78,6 +78,7 @@ version <next>:
>  - movie source added
>  - Bink version 'b' audio and video decoder
>  - Bitmap Brothers JV playback system
> +- Chronomaster DFA decoder
>  
>  
>  version 0.6:
> diff --git a/doc/general.texi b/doc/general.texi
> index 080357a..b0d5ee1 100644
> --- a/doc/general.texi
> +++ b/doc/general.texi
> @@ -81,6 +81,8 @@ library:
>      @tab Audio format used in some games by CRYO Interactive Entertainment.
>  @item D-Cinema audio            @tab X @tab X
>  @item Deluxe Paint Animation    @tab   @tab X
> +@item DFA                       @tab   @tab X
> +    @tab This format is used in Chronomaster game
>  @item DV video                  @tab X @tab X
>  @item DXA                       @tab   @tab X
>      @tab This format is used in the non-Windows version of the Feeble Files
> @@ -371,6 +373,8 @@ following image formats are supported:
>  @item Cirrus Logic AccuPak   @tab     @tab  X
>      @tab fourcc: CLJR
>  @item Creative YUV (CYUV)    @tab     @tab  X
> +@item DFA                    @tab     @tab  X
> +    @tab Codec used in Chronomaster game.
>  @item Dirac                  @tab  E  @tab  E
>      @tab supported through external libdirac/libschroedinger libraries
>  @item Deluxe Paint Animation @tab     @tab  X
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 21bdbf4..306ab44 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -102,6 +102,7 @@ OBJS-$(CONFIG_COOK_DECODER)            += cook.o
>  OBJS-$(CONFIG_CSCD_DECODER)            += cscd.o
>  OBJS-$(CONFIG_CYUV_DECODER)            += cyuv.o
>  OBJS-$(CONFIG_DCA_DECODER)             += dca.o synth_filter.o dcadsp.o
> +OBJS-$(CONFIG_DFA_DECODER)             += dfa.o
>  OBJS-$(CONFIG_DNXHD_DECODER)           += dnxhddec.o dnxhddata.o
>  OBJS-$(CONFIG_DNXHD_ENCODER)           += dnxhdenc.o dnxhddata.o       \
>                                            mpegvideo_enc.o motion_est.o \
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 2ed49a2..d368bc0 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -88,6 +88,7 @@ void avcodec_register_all(void)
>      REGISTER_DECODER (CLJR, cljr);
>      REGISTER_DECODER (CSCD, cscd);
>      REGISTER_DECODER (CYUV, cyuv);
> +    REGISTER_DECODER (DFA, dfa);
>      REGISTER_ENCDEC  (DNXHD, dnxhd);
>      REGISTER_DECODER (DPX, dpx);
>      REGISTER_DECODER (DSICINVIDEO, dsicinvideo);
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 6491e07..1c0f25c 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -32,7 +32,7 @@
>  #include "libavutil/cpu.h"
>  
>  #define LIBAVCODEC_VERSION_MAJOR 52
> -#define LIBAVCODEC_VERSION_MINOR 114
> +#define LIBAVCODEC_VERSION_MINOR 115
>  #define LIBAVCODEC_VERSION_MICRO  0
>  
>  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
> @@ -262,6 +262,7 @@ enum CodecID {
>      CODEC_ID_LAGARITH,
>      CODEC_ID_PRORES,
>      CODEC_ID_JV,
> +    CODEC_ID_DFA,
>  
>      /* various PCM "codecs" */
>      CODEC_ID_PCM_S16LE= 0x10000,
> diff --git a/libavcodec/dfa.c b/libavcodec/dfa.c
> new file mode 100644
> index 0000000..b2dbef5
> --- /dev/null
> +++ b/libavcodec/dfa.c
> @@ -0,0 +1,410 @@
> +/*
> + * Chronomaster DFA Video Decoder
> + * Copyright (c) 2011 Konstantin Shishkov
> + * based on work by Vladimir "VAG" Gneushev
> + *
> + * 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 "avcodec.h"
> +#include "libavutil/intreadwrite.h"
> +#include "bytestream.h"
> +#define ALT_BITSTREAM_READER_LE
> +#include "get_bits.h"
> +// for av_memcpy_backptr
> +#include "libavutil/lzo.h"

Are the includes in correct order?

> +
> +typedef struct DfaContext {
> +    AVFrame pic;
> +
> +    uint32_t pal[256];
> +    uint8_t *frame_buf;
> +} DfaContext;
> +
> +static av_cold int dfa_decode_init(AVCodecContext *avctx)
> +{
> +    DfaContext *s = avctx->priv_data;
> +
> +    avctx->pix_fmt = PIX_FMT_PAL8;
> +
> +    s->frame_buf = av_mallocz(avctx->width * avctx->height + 16);
> +    if (!s->frame_buf) {
> +        av_log(avctx, AV_LOG_ERROR, "Cannot allocate frame data\n");
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int decode_tsw1(uint8_t *frame, int width, int height,
> +                       const uint8_t *src, const uint8_t *src_end)
> +{
> +    const uint8_t *frame_start = frame;
> +    const uint8_t *frame_end   = frame + width * height;
> +    int mask = 0x10000, bitbuf;
> +    int v, offset, count, segments;
> +
> +    segments = bytestream_get_le32(&src);
> +    frame += bytestream_get_le32(&src);
> +    if (frame > frame_end)
> +        return -1;
> +    while (segments--) {
> +        if (mask == 0x10000) {
> +            if (src >= src_end)
> +                return -1;
> +            bitbuf = bytestream_get_le16(&src);
> +            mask = 1;
> +        }
> +        if (src + 2 > src_end || frame + 2 > frame_end)
> +            return -1;
> +        if (bitbuf & mask) {
> +            v = bytestream_get_le16(&src);
> +            offset = (v & 0x1FFF) << 1;
> +            count = ((v >> 13) + 2) << 1;
> +            if (frame - offset < frame_start || frame_end - frame < count)
> +                return -1;
> +            av_memcpy_backptr(frame, offset, count);
> +            frame += count;
> +        } else {
> +            *frame++ = *src++;
> +            *frame++ = *src++;
> +        }
> +        mask <<= 1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int decode_dsw1(uint8_t *frame, int width, int height,
> +                       const uint8_t *src, const uint8_t *src_end)
> +{
> +    const uint8_t *frame_start = frame;
> +    const uint8_t *frame_end   = frame + width * height;
> +    int mask = 0x10000, bitbuf;
> +    int v, offset, count, segments;
> +
> +    segments = bytestream_get_le16(&src);
> +    while (segments--) {
> +        if (mask == 0x10000) {
> +            if (src >= src_end)
> +                return -1;
> +            bitbuf = bytestream_get_le16(&src);
> +            mask = 1;
> +        }
> +        if (src + 2 > src_end || frame + 2 > frame_end)
> +            return -1;
> +        if (bitbuf & mask) {
> +            v = bytestream_get_le16(&src);
> +            offset = (v & 0x1FFF) << 1;
> +            count = ((v >> 13) + 2) << 1;
> +            if (frame - offset < frame_start || frame_end - frame < count)
> +                return -1;
> +            // can't use av_memcpy_backptr() since it can overwrite following pixels
> +            for (v = 0; v < count; v++)
> +                frame[v] = frame[v - offset];
> +            frame += count;
> +        } else if (bitbuf & (mask << 1)) {
> +            frame += bytestream_get_le16(&src);
> +            if (frame > frame_end)
> +                return -1;
> +        } else {
> +            *frame++ = *src++;
> +            *frame++ = *src++;
> +        }
> +        mask <<= 2;
> +    }
> +
> +    return 0;
> +}
> +
> +static int decode_dds1(uint8_t *frame, int width, int height,
> +                       const uint8_t *src, const uint8_t *src_end)
> +{
> +    const uint8_t *frame_start = frame;
> +    const uint8_t *frame_end   = frame + width * height;
> +    int mask = 0x10000, bitbuf;
> +    int i, v, offset, count, segments;
> +
> +    segments = bytestream_get_le16(&src);
> +    while (segments--) {
> +        if (mask == 0x10000) {
> +            if (src >= src_end)
> +                return -1;
> +            bitbuf = bytestream_get_le16(&src);
> +            mask = 1;
> +        }
> +        if (src + 2 > src_end || frame + 2 > frame_end)
> +            return -1;
> +        if (bitbuf & mask) {
> +            v = bytestream_get_le16(&src);
> +            offset = (v & 0x1FFF) << 2;
> +            count = ((v >> 13) + 2) << 1;
> +            if (frame - offset < frame_start || frame_end - frame < count*2 + width)
> +                return -1;
> +            for (i = 0; i < count; i++) {
> +                frame[0] = frame[1] =
> +                frame[width] = frame[width + 1] = frame[-offset];
> +
> +                frame += 2;
> +            }
> +        } else if (bitbuf & (mask << 1)) {
> +            frame += bytestream_get_le16(&src) * 2;
> +            if (frame > frame_end)
> +                return -1;
> +        } else {
> +            frame[0] = frame[1] =
> +            frame[width] = frame[width + 1] =  *src++;
> +            frame += 2;
> +            frame[0] = frame[1] =
> +            frame[width] = frame[width + 1] =  *src++;
> +            frame += 2;
> +        }
> +        mask <<= 2;
> +    }
> +
> +    return 0;
> +}
> +
> +static int decode_bdlt(uint8_t *frame, int width, int height,
> +                       const uint8_t *src, const uint8_t *src_end)
> +{
> +    const uint8_t *frame_end = frame + width * height;
> +    uint8_t *line_ptr;
> +    int count, lines, segments;
> +
> +    frame += width * bytestream_get_le16(&src);
> +    lines = bytestream_get_le16(&src);
> +    if (frame > frame_end || frame + lines * width > frame_end
> +        || src >= src_end)
> +        return -1;
> +
> +    while (lines--) {
> +        line_ptr = frame;
> +        frame += width;
> +        segments = *src++;
> +        while (segments--) {
> +            if (src_end - src < 3)
> +                return -1;
> +            line_ptr += *src++;
> +            if (line_ptr >= frame)
> +                return -1;
> +            count = (int8_t)*src++;
> +            if (count >= 0) {
> +                if (line_ptr + count > frame || src_end - src < count)
> +                    return -1;
> +                bytestream_get_buffer(&src, line_ptr, count);
> +            } else {
> +                count = -count;
> +                if (line_ptr + count > frame || src >= src_end)
> +                    return -1;
> +                memset(line_ptr, *src++, count);
> +            }
> +            line_ptr += count;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int decode_wdlt(uint8_t *frame, int width, int height,
> +                       const uint8_t *src, const uint8_t *src_end)
> +{
> +    const uint8_t *frame_end   = frame + width * height;
> +    uint8_t *line_ptr;
> +    int count, i, v, lines, segments;
> +
> +    lines = bytestream_get_le16(&src);
> +    if (frame + lines * width > frame_end || src >= src_end)
> +        return -1;
> +
> +    while (lines--) {
> +        segments = bytestream_get_le16(&src);
> +        while ((segments & 0xC000) == 0xC000) {
> +            frame    -= (int16_t)segments * width;
> +            if (frame >= frame_end)
> +                return -1;
> +            segments = bytestream_get_le16(&src);
> +        }
> +        if (segments & 0x8000) {
> +            frame[width - 1] = segments & 0xFF;
> +            segments = bytestream_get_le16(&src);
> +        }
> +        line_ptr = frame;
> +        frame += width;
> +        while (segments--) {
> +            if (src_end - src < 2)
> +                return -1;
> +            line_ptr += *src++;
> +            if (line_ptr >= frame)
> +                return -1;
> +            count = (int8_t)*src++;
> +            if (count >= 0) {
> +                if (line_ptr + count*2 > frame || src_end - src < count*2)
> +                    return -1;
> +                bytestream_get_buffer(&src, line_ptr, count*2);
> +                line_ptr += count * 2;
> +            } else {
> +                count = -count;
> +                if (line_ptr + count*2 > frame || src_end - src < 2)
> +                    return -1;
> +                v = bytestream_get_le16(&src);
> +                for (i = 0; i < count; i++)
> +                    bytestream_put_le16(&line_ptr, v);
> +            }
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int dfa_decode_frame(AVCodecContext *avctx,
> +                            void *data, int *data_size,
> +                            AVPacket *avpkt)
> +{
> +    DfaContext *s = avctx->priv_data;
> +    const uint8_t *buf = avpkt->data;
> +    const uint8_t *buf_end = avpkt->data + avpkt->size;
> +    const uint8_t *tmp_buf;
> +    int chunk_type, chunk_size;
> +    uint8_t *dst;
> +    int ret;
> +    int i, pal_elems;
> +
> +    if (s->pic.data[0])
> +        avctx->release_buffer(avctx, &s->pic);
> +
> +    if ((ret = avctx->get_buffer(avctx, &s->pic))) {
> +        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
> +        return ret;
> +    }
> +
> +    while (buf < buf_end) {
> +        chunk_size = AV_RL32(buf + 4);
> +        chunk_type = AV_RL32(buf + 8);
> +        buf += 12;
> +        if (buf_end - buf < chunk_size) {
> +            av_log(avctx, AV_LOG_ERROR, "Chunk size is too big (%d bytes)\n", chunk_size);
> +            return -1;
> +        }
> +        if (!chunk_type)
> +            break;
> +//av_log(NULL,0,"%c%c%c%c (%d)\n",buf[-12],buf[-11],buf[-10],buf[-9],chunk_type);

I guess this av_log can be removed.

> +        switch (chunk_type) {
> +        case 1:
> +            pal_elems = FFMIN(chunk_size / 3, 256);
> +            tmp_buf = buf;
> +            for (i = 0; i < pal_elems; i++) {
> +                s->pal[i] = bytestream_get_be24(&tmp_buf) << 2;
> +                s->pal[i] |= (s->pal[i] >> 6) & 0x333;
> +            }
> +            s->pic.palette_has_changed = 1;
> +            break;
> +        case 2:
> +            if (chunk_size < avctx->width * avctx->height) {
> +                av_log(avctx, AV_LOG_ERROR, "Too small raw frame chunk, expected %d bytes, got %d bytes\n",
> +                       avctx->width * avctx->height, chunk_size);
> +                return -1;
> +            }
> +            tmp_buf = buf;
> +            bytestream_get_buffer(&tmp_buf, s->frame_buf,
> +                                  avctx->width * avctx->height);
> +            break;
> +        case 3:
> +            if (decode_tsw1(s->frame_buf, avctx->width, avctx->height,
> +                            buf, buf_end)) {
> +                av_log(avctx, AV_LOG_ERROR, "Error decoding TSW1 chunk\n");
> +                return -1;
> +            }
> +            break;
> +        case 4:
> +            if (decode_bdlt(s->frame_buf, avctx->width, avctx->height,
> +                            buf, buf_end)) {
> +                av_log(avctx, AV_LOG_ERROR, "Error decoding BDLT chunk\n");
> +                return -1;
> +            }
> +            break;
> +        case 5:
> +            if (decode_wdlt(s->frame_buf, avctx->width, avctx->height,
> +                            buf, buf_end)) {
> +                av_log(avctx, AV_LOG_ERROR, "Error decoding WDLT chunk\n");
> +                return -1;
> +            }
> +            break;
> +        case 7:
> +            if (decode_dsw1(s->frame_buf, avctx->width, avctx->height,
> +                            buf, buf_end)) {
> +                av_log(avctx, AV_LOG_ERROR, "Error decoding DSW1 chunk\n");
> +                return -1;
> +            }
> +            break;
> +        case 8:
> +            memset(s->pic.data[0], 0, avctx->height * s->pic.linesize[0]);
> +            break;
> +        case 9:
> +            if (decode_dds1(s->frame_buf, avctx->width, avctx->height,
> +                            buf, buf_end)) {
> +                av_log(avctx, AV_LOG_ERROR, "Error decoding DDS1 chunk\n");
> +                return -1;
> +            }
> +            break;
> +        default:
> +            av_log(avctx, AV_LOG_WARNING, "Ignoring unknown chunk type %d\n",
> +                   chunk_type);
> +        }
> +        buf += chunk_size;
> +    }
> +
> +    buf = s->frame_buf;
> +    dst = s->pic.data[0];
> +    for (i = 0; i < avctx->height; i++) {
> +        memcpy(dst, buf, avctx->width);
> +        dst += s->pic.linesize[0];
> +        buf += avctx->width;
> +    }
> +    memcpy(s->pic.data[1], s->pal, sizeof(s->pal));
> +
> +    *data_size = sizeof(AVFrame);
> +    *(AVFrame*)data = s->pic;
> +
> +    return avpkt->size;
> +}
> +
> +static av_cold int dfa_decode_end(AVCodecContext *avctx)
> +{
> +    DfaContext *s = avctx->priv_data;
> +
> +    if (s->pic.data[0])
> +        avctx->release_buffer(avctx, &s->pic);
> +
> +    av_freep(&s->frame_buf);
> +
> +    return 0;
> +}
> +
> +AVCodec ff_dfa_decoder = {
> +    "dfa",
> +    AVMEDIA_TYPE_VIDEO,
> +    CODEC_ID_DFA,
> +    sizeof(DfaContext),
> +    dfa_decode_init,
> +    NULL,
> +    dfa_decode_end,
> +    dfa_decode_frame,
> +    CODEC_CAP_DR1,
> +    .long_name = NULL_IF_CONFIG_SMALL("Chronomaster DFA"),
> +};
> +
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 4eb1620..5f5ed70 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -55,6 +55,7 @@ OBJS-$(CONFIG_CDG_DEMUXER)               += cdg.o
>  OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
>  OBJS-$(CONFIG_DAUD_DEMUXER)              += daud.o
>  OBJS-$(CONFIG_DAUD_MUXER)                += daud.o
> +OBJS-$(CONFIG_DFA_DEMUXER)               += dfa.o
>  OBJS-$(CONFIG_DIRAC_DEMUXER)             += diracdec.o rawdec.o
>  OBJS-$(CONFIG_DIRAC_MUXER)               += rawenc.o
>  OBJS-$(CONFIG_DNXHD_DEMUXER)             += dnxhddec.o rawdec.o
> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> index bca968a..3acaed9 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -75,6 +75,7 @@ void av_register_all(void)
>      REGISTER_DEMUXER  (CDG, cdg);
>      REGISTER_MUXER    (CRC, crc);
>      REGISTER_MUXDEMUX (DAUD, daud);
> +    REGISTER_DEMUXER  (DFA, dfa);
>      REGISTER_MUXDEMUX (DIRAC, dirac);
>      REGISTER_MUXDEMUX (DNXHD, dnxhd);
>      REGISTER_DEMUXER  (DSICIN, dsicin);
> diff --git a/libavformat/dfa.c b/libavformat/dfa.c
> new file mode 100644
> index 0000000..aa7719e
> --- /dev/null
> +++ b/libavformat/dfa.c
> @@ -0,0 +1,122 @@
> +/*
> + * Chronomaster DFA Format Demuxer
> + * Copyright (c) 2011 Konstantin Shishkov
> + *
> + * 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 "avformat.h"
> +
> +static int dfa_probe(AVProbeData *p)
> +{
> +    if (p->buf_size < 4)
> +        return 0;
> +    if (AV_RL32(p->buf) != MKTAG('D', 'F', 'I', 'A'))
> +        return 0;
> +
> +    return AVPROBE_SCORE_MAX;
> +}
> +
> +static int dfa_read_header(AVFormatContext *s,
> +                           AVFormatParameters *ap)
> +{
> +    AVIOContext *pb = s->pb;
> +    AVStream *st;
> +    int frames;
> +    uint32_t fps;
> +
> +    if (avio_rl32(pb) != MKTAG('D', 'F', 'I', 'A')) {
> +        av_log(s, AV_LOG_ERROR, "Invalid magic for DFA\n");
> +        return -1;
> +    }
> +    avio_skip(pb, 2);
> +    frames = avio_rl16(pb);
> +
> +    /* init video codec */
> +    st = av_new_stream(s, 0);
> +    if (!st)
> +        return -1;
> +
> +    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
> +    st->codec->codec_id   = CODEC_ID_DFA;
> +    st->codec->width      = avio_rl16(pb);
> +    st->codec->height     = avio_rl16(pb);
> +    fps = avio_rl32(pb);
> +    if (!fps) {
> +        av_log(s, AV_LOG_WARNING, "Zero FPS reported, defaulting to 10\n");
> +        fps = 100;
> +    }
> +    av_set_pts_info(st, 24, fps, 1000);
> +    avio_skip(pb, 128-16);
> +    st->duration = frames;
> +
> +    return 0;
> +}
> +
> +static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> +    AVIOContext *pb = s->pb;
> +    uint32_t frame_size;
> +    int ret, first = 1;
> +
> +    if(pb->eof_reached)
> +        return AVERROR_EOF;
> +
> +    if (av_get_packet(pb, pkt, 12) != 12)
> +        return AVERROR(EIO);
> +    while (!pb->eof_reached) {
> +        if (!first) {
> +            ret = av_append_packet(pb, pkt, 12);
> +            if (ret < 0) {
> +                av_free_packet(pkt);
> +                return ret;
> +            }
> +        } else
> +            first = 0;
> +        frame_size = AV_RL32(pkt->data + pkt->size - 8);
> +        if (frame_size > INT_MAX - 4) {
> +            av_log(s, AV_LOG_ERROR, "Too large chunk size: %d\n", frame_size);
> +            return AVERROR(EIO);
> +        }
> +        if (AV_RL32(pkt->data + pkt->size - 12) == MKTAG('E', 'O', 'F', 'R')) {
> +            if (frame_size) {
> +                av_log(s, AV_LOG_WARNING, "skipping %d bytes of end-of-frame marker chunk\n",
> +                       frame_size);
> +                avio_skip(pb, frame_size);
> +            }
> +            return 0;
> +        }
> +        ret = av_append_packet(pb, pkt, frame_size);
> +        if (ret < 0) {
> +            av_free_packet(pkt);
> +            return ret;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +AVInputFormat ff_dfa_demuxer = {
> +    "dfa",
> +    NULL_IF_CONFIG_SMALL("Chronomaster DFA"),
> +    0,
> +    dfa_probe,
> +    dfa_read_header,
> +    dfa_read_packet,
> +    .flags = AVFMT_GENERIC_INDEX,
> +};
> diff --git a/libavformat/version.h b/libavformat/version.h
> index aed8908..ae2f580 100644
> --- a/libavformat/version.h
> +++ b/libavformat/version.h
> @@ -24,7 +24,7 @@
>  #include "libavutil/avutil.h"
>  
>  #define LIBAVFORMAT_VERSION_MAJOR 52
> -#define LIBAVFORMAT_VERSION_MINOR 103
> +#define LIBAVFORMAT_VERSION_MINOR 104
>  #define LIBAVFORMAT_VERSION_MICRO  0
>  
>  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \

Patch

diff --git a/Changelog b/Changelog
index 95c8b65..ace6ded 100644
--- a/Changelog
+++ b/Changelog
@@ -78,6 +78,7 @@  version <next>:
 - movie source added
 - Bink version 'b' audio and video decoder
 - Bitmap Brothers JV playback system
+- Chronomaster DFA decoder
 
 
 version 0.6:
diff --git a/doc/general.texi b/doc/general.texi
index 080357a..b0d5ee1 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -81,6 +81,8 @@  library:
     @tab Audio format used in some games by CRYO Interactive Entertainment.
 @item D-Cinema audio            @tab X @tab X
 @item Deluxe Paint Animation    @tab   @tab X
+@item DFA                       @tab   @tab X
+    @tab This format is used in Chronomaster game
 @item DV video                  @tab X @tab X
 @item DXA                       @tab   @tab X
     @tab This format is used in the non-Windows version of the Feeble Files
@@ -371,6 +373,8 @@  following image formats are supported:
 @item Cirrus Logic AccuPak   @tab     @tab  X
     @tab fourcc: CLJR
 @item Creative YUV (CYUV)    @tab     @tab  X
+@item DFA                    @tab     @tab  X
+    @tab Codec used in Chronomaster game.
 @item Dirac                  @tab  E  @tab  E
     @tab supported through external libdirac/libschroedinger libraries
 @item Deluxe Paint Animation @tab     @tab  X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 21bdbf4..306ab44 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -102,6 +102,7 @@  OBJS-$(CONFIG_COOK_DECODER)            += cook.o
 OBJS-$(CONFIG_CSCD_DECODER)            += cscd.o
 OBJS-$(CONFIG_CYUV_DECODER)            += cyuv.o
 OBJS-$(CONFIG_DCA_DECODER)             += dca.o synth_filter.o dcadsp.o
+OBJS-$(CONFIG_DFA_DECODER)             += dfa.o
 OBJS-$(CONFIG_DNXHD_DECODER)           += dnxhddec.o dnxhddata.o
 OBJS-$(CONFIG_DNXHD_ENCODER)           += dnxhdenc.o dnxhddata.o       \
                                           mpegvideo_enc.o motion_est.o \
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 2ed49a2..d368bc0 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -88,6 +88,7 @@  void avcodec_register_all(void)
     REGISTER_DECODER (CLJR, cljr);
     REGISTER_DECODER (CSCD, cscd);
     REGISTER_DECODER (CYUV, cyuv);
+    REGISTER_DECODER (DFA, dfa);
     REGISTER_ENCDEC  (DNXHD, dnxhd);
     REGISTER_DECODER (DPX, dpx);
     REGISTER_DECODER (DSICINVIDEO, dsicinvideo);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 6491e07..1c0f25c 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -32,7 +32,7 @@ 
 #include "libavutil/cpu.h"
 
 #define LIBAVCODEC_VERSION_MAJOR 52
-#define LIBAVCODEC_VERSION_MINOR 114
+#define LIBAVCODEC_VERSION_MINOR 115
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
@@ -262,6 +262,7 @@  enum CodecID {
     CODEC_ID_LAGARITH,
     CODEC_ID_PRORES,
     CODEC_ID_JV,
+    CODEC_ID_DFA,
 
     /* various PCM "codecs" */
     CODEC_ID_PCM_S16LE= 0x10000,
diff --git a/libavcodec/dfa.c b/libavcodec/dfa.c
new file mode 100644
index 0000000..b2dbef5
--- /dev/null
+++ b/libavcodec/dfa.c
@@ -0,0 +1,410 @@ 
+/*
+ * Chronomaster DFA Video Decoder
+ * Copyright (c) 2011 Konstantin Shishkov
+ * based on work by Vladimir "VAG" Gneushev
+ *
+ * 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 "avcodec.h"
+#include "libavutil/intreadwrite.h"
+#include "bytestream.h"
+#define ALT_BITSTREAM_READER_LE
+#include "get_bits.h"
+// for av_memcpy_backptr
+#include "libavutil/lzo.h"
+
+typedef struct DfaContext {
+    AVFrame pic;
+
+    uint32_t pal[256];
+    uint8_t *frame_buf;
+} DfaContext;
+
+static av_cold int dfa_decode_init(AVCodecContext *avctx)
+{
+    DfaContext *s = avctx->priv_data;
+
+    avctx->pix_fmt = PIX_FMT_PAL8;
+
+    s->frame_buf = av_mallocz(avctx->width * avctx->height + 16);
+    if (!s->frame_buf) {
+        av_log(avctx, AV_LOG_ERROR, "Cannot allocate frame data\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int decode_tsw1(uint8_t *frame, int width, int height,
+                       const uint8_t *src, const uint8_t *src_end)
+{
+    const uint8_t *frame_start = frame;
+    const uint8_t *frame_end   = frame + width * height;
+    int mask = 0x10000, bitbuf;
+    int v, offset, count, segments;
+
+    segments = bytestream_get_le32(&src);
+    frame += bytestream_get_le32(&src);
+    if (frame > frame_end)
+        return -1;
+    while (segments--) {
+        if (mask == 0x10000) {
+            if (src >= src_end)
+                return -1;
+            bitbuf = bytestream_get_le16(&src);
+            mask = 1;
+        }
+        if (src + 2 > src_end || frame + 2 > frame_end)
+            return -1;
+        if (bitbuf & mask) {
+            v = bytestream_get_le16(&src);
+            offset = (v & 0x1FFF) << 1;
+            count = ((v >> 13) + 2) << 1;
+            if (frame - offset < frame_start || frame_end - frame < count)
+                return -1;
+            av_memcpy_backptr(frame, offset, count);
+            frame += count;
+        } else {
+            *frame++ = *src++;
+            *frame++ = *src++;
+        }
+        mask <<= 1;
+    }
+
+    return 0;
+}
+
+static int decode_dsw1(uint8_t *frame, int width, int height,
+                       const uint8_t *src, const uint8_t *src_end)
+{
+    const uint8_t *frame_start = frame;
+    const uint8_t *frame_end   = frame + width * height;
+    int mask = 0x10000, bitbuf;
+    int v, offset, count, segments;
+
+    segments = bytestream_get_le16(&src);
+    while (segments--) {
+        if (mask == 0x10000) {
+            if (src >= src_end)
+                return -1;
+            bitbuf = bytestream_get_le16(&src);
+            mask = 1;
+        }
+        if (src + 2 > src_end || frame + 2 > frame_end)
+            return -1;
+        if (bitbuf & mask) {
+            v = bytestream_get_le16(&src);
+            offset = (v & 0x1FFF) << 1;
+            count = ((v >> 13) + 2) << 1;
+            if (frame - offset < frame_start || frame_end - frame < count)
+                return -1;
+            // can't use av_memcpy_backptr() since it can overwrite following pixels
+            for (v = 0; v < count; v++)
+                frame[v] = frame[v - offset];
+            frame += count;
+        } else if (bitbuf & (mask << 1)) {
+            frame += bytestream_get_le16(&src);
+            if (frame > frame_end)
+                return -1;
+        } else {
+            *frame++ = *src++;
+            *frame++ = *src++;
+        }
+        mask <<= 2;
+    }
+
+    return 0;
+}
+
+static int decode_dds1(uint8_t *frame, int width, int height,
+                       const uint8_t *src, const uint8_t *src_end)
+{
+    const uint8_t *frame_start = frame;
+    const uint8_t *frame_end   = frame + width * height;
+    int mask = 0x10000, bitbuf;
+    int i, v, offset, count, segments;
+
+    segments = bytestream_get_le16(&src);
+    while (segments--) {
+        if (mask == 0x10000) {
+            if (src >= src_end)
+                return -1;
+            bitbuf = bytestream_get_le16(&src);
+            mask = 1;
+        }
+        if (src + 2 > src_end || frame + 2 > frame_end)
+            return -1;
+        if (bitbuf & mask) {
+            v = bytestream_get_le16(&src);
+            offset = (v & 0x1FFF) << 2;
+            count = ((v >> 13) + 2) << 1;
+            if (frame - offset < frame_start || frame_end - frame < count*2 + width)
+                return -1;
+            for (i = 0; i < count; i++) {
+                frame[0] = frame[1] =
+                frame[width] = frame[width + 1] = frame[-offset];
+
+                frame += 2;
+            }
+        } else if (bitbuf & (mask << 1)) {
+            frame += bytestream_get_le16(&src) * 2;
+            if (frame > frame_end)
+                return -1;
+        } else {
+            frame[0] = frame[1] =
+            frame[width] = frame[width + 1] =  *src++;
+            frame += 2;
+            frame[0] = frame[1] =
+            frame[width] = frame[width + 1] =  *src++;
+            frame += 2;
+        }
+        mask <<= 2;
+    }
+
+    return 0;
+}
+
+static int decode_bdlt(uint8_t *frame, int width, int height,
+                       const uint8_t *src, const uint8_t *src_end)
+{
+    const uint8_t *frame_end = frame + width * height;
+    uint8_t *line_ptr;
+    int count, lines, segments;
+
+    frame += width * bytestream_get_le16(&src);
+    lines = bytestream_get_le16(&src);
+    if (frame > frame_end || frame + lines * width > frame_end
+        || src >= src_end)
+        return -1;
+
+    while (lines--) {
+        line_ptr = frame;
+        frame += width;
+        segments = *src++;
+        while (segments--) {
+            if (src_end - src < 3)
+                return -1;
+            line_ptr += *src++;
+            if (line_ptr >= frame)
+                return -1;
+            count = (int8_t)*src++;
+            if (count >= 0) {
+                if (line_ptr + count > frame || src_end - src < count)
+                    return -1;
+                bytestream_get_buffer(&src, line_ptr, count);
+            } else {
+                count = -count;
+                if (line_ptr + count > frame || src >= src_end)
+                    return -1;
+                memset(line_ptr, *src++, count);
+            }
+            line_ptr += count;
+        }
+    }
+
+    return 0;
+}
+
+static int decode_wdlt(uint8_t *frame, int width, int height,
+                       const uint8_t *src, const uint8_t *src_end)
+{
+    const uint8_t *frame_end   = frame + width * height;
+    uint8_t *line_ptr;
+    int count, i, v, lines, segments;
+
+    lines = bytestream_get_le16(&src);
+    if (frame + lines * width > frame_end || src >= src_end)
+        return -1;
+
+    while (lines--) {
+        segments = bytestream_get_le16(&src);
+        while ((segments & 0xC000) == 0xC000) {
+            frame    -= (int16_t)segments * width;
+            if (frame >= frame_end)
+                return -1;
+            segments = bytestream_get_le16(&src);
+        }
+        if (segments & 0x8000) {
+            frame[width - 1] = segments & 0xFF;
+            segments = bytestream_get_le16(&src);
+        }
+        line_ptr = frame;
+        frame += width;
+        while (segments--) {
+            if (src_end - src < 2)
+                return -1;
+            line_ptr += *src++;
+            if (line_ptr >= frame)
+                return -1;
+            count = (int8_t)*src++;
+            if (count >= 0) {
+                if (line_ptr + count*2 > frame || src_end - src < count*2)
+                    return -1;
+                bytestream_get_buffer(&src, line_ptr, count*2);
+                line_ptr += count * 2;
+            } else {
+                count = -count;
+                if (line_ptr + count*2 > frame || src_end - src < 2)
+                    return -1;
+                v = bytestream_get_le16(&src);
+                for (i = 0; i < count; i++)
+                    bytestream_put_le16(&line_ptr, v);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int dfa_decode_frame(AVCodecContext *avctx,
+                            void *data, int *data_size,
+                            AVPacket *avpkt)
+{
+    DfaContext *s = avctx->priv_data;
+    const uint8_t *buf = avpkt->data;
+    const uint8_t *buf_end = avpkt->data + avpkt->size;
+    const uint8_t *tmp_buf;
+    int chunk_type, chunk_size;
+    uint8_t *dst;
+    int ret;
+    int i, pal_elems;
+
+    if (s->pic.data[0])
+        avctx->release_buffer(avctx, &s->pic);
+
+    if ((ret = avctx->get_buffer(avctx, &s->pic))) {
+        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+        return ret;
+    }
+
+    while (buf < buf_end) {
+        chunk_size = AV_RL32(buf + 4);
+        chunk_type = AV_RL32(buf + 8);
+        buf += 12;
+        if (buf_end - buf < chunk_size) {
+            av_log(avctx, AV_LOG_ERROR, "Chunk size is too big (%d bytes)\n", chunk_size);
+            return -1;
+        }
+        if (!chunk_type)
+            break;
+//av_log(NULL,0,"%c%c%c%c (%d)\n",buf[-12],buf[-11],buf[-10],buf[-9],chunk_type);
+        switch (chunk_type) {
+        case 1:
+            pal_elems = FFMIN(chunk_size / 3, 256);
+            tmp_buf = buf;
+            for (i = 0; i < pal_elems; i++) {
+                s->pal[i] = bytestream_get_be24(&tmp_buf) << 2;
+                s->pal[i] |= (s->pal[i] >> 6) & 0x333;
+            }
+            s->pic.palette_has_changed = 1;
+            break;
+        case 2:
+            if (chunk_size < avctx->width * avctx->height) {
+                av_log(avctx, AV_LOG_ERROR, "Too small raw frame chunk, expected %d bytes, got %d bytes\n",
+                       avctx->width * avctx->height, chunk_size);
+                return -1;
+            }
+            tmp_buf = buf;
+            bytestream_get_buffer(&tmp_buf, s->frame_buf,
+                                  avctx->width * avctx->height);
+            break;
+        case 3:
+            if (decode_tsw1(s->frame_buf, avctx->width, avctx->height,
+                            buf, buf_end)) {
+                av_log(avctx, AV_LOG_ERROR, "Error decoding TSW1 chunk\n");
+                return -1;
+            }
+            break;
+        case 4:
+            if (decode_bdlt(s->frame_buf, avctx->width, avctx->height,
+                            buf, buf_end)) {
+                av_log(avctx, AV_LOG_ERROR, "Error decoding BDLT chunk\n");
+                return -1;
+            }
+            break;
+        case 5:
+            if (decode_wdlt(s->frame_buf, avctx->width, avctx->height,
+                            buf, buf_end)) {
+                av_log(avctx, AV_LOG_ERROR, "Error decoding WDLT chunk\n");
+                return -1;
+            }
+            break;
+        case 7:
+            if (decode_dsw1(s->frame_buf, avctx->width, avctx->height,
+                            buf, buf_end)) {
+                av_log(avctx, AV_LOG_ERROR, "Error decoding DSW1 chunk\n");
+                return -1;
+            }
+            break;
+        case 8:
+            memset(s->pic.data[0], 0, avctx->height * s->pic.linesize[0]);
+            break;
+        case 9:
+            if (decode_dds1(s->frame_buf, avctx->width, avctx->height,
+                            buf, buf_end)) {
+                av_log(avctx, AV_LOG_ERROR, "Error decoding DDS1 chunk\n");
+                return -1;
+            }
+            break;
+        default:
+            av_log(avctx, AV_LOG_WARNING, "Ignoring unknown chunk type %d\n",
+                   chunk_type);
+        }
+        buf += chunk_size;
+    }
+
+    buf = s->frame_buf;
+    dst = s->pic.data[0];
+    for (i = 0; i < avctx->height; i++) {
+        memcpy(dst, buf, avctx->width);
+        dst += s->pic.linesize[0];
+        buf += avctx->width;
+    }
+    memcpy(s->pic.data[1], s->pal, sizeof(s->pal));
+
+    *data_size = sizeof(AVFrame);
+    *(AVFrame*)data = s->pic;
+
+    return avpkt->size;
+}
+
+static av_cold int dfa_decode_end(AVCodecContext *avctx)
+{
+    DfaContext *s = avctx->priv_data;
+
+    if (s->pic.data[0])
+        avctx->release_buffer(avctx, &s->pic);
+
+    av_freep(&s->frame_buf);
+
+    return 0;
+}
+
+AVCodec ff_dfa_decoder = {
+    "dfa",
+    AVMEDIA_TYPE_VIDEO,
+    CODEC_ID_DFA,
+    sizeof(DfaContext),
+    dfa_decode_init,
+    NULL,
+    dfa_decode_end,
+    dfa_decode_frame,
+    CODEC_CAP_DR1,
+    .long_name = NULL_IF_CONFIG_SMALL("Chronomaster DFA"),
+};
+
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 4eb1620..5f5ed70 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -55,6 +55,7 @@  OBJS-$(CONFIG_CDG_DEMUXER)               += cdg.o
 OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
 OBJS-$(CONFIG_DAUD_DEMUXER)              += daud.o
 OBJS-$(CONFIG_DAUD_MUXER)                += daud.o
+OBJS-$(CONFIG_DFA_DEMUXER)               += dfa.o
 OBJS-$(CONFIG_DIRAC_DEMUXER)             += diracdec.o rawdec.o
 OBJS-$(CONFIG_DIRAC_MUXER)               += rawenc.o
 OBJS-$(CONFIG_DNXHD_DEMUXER)             += dnxhddec.o rawdec.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index bca968a..3acaed9 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -75,6 +75,7 @@  void av_register_all(void)
     REGISTER_DEMUXER  (CDG, cdg);
     REGISTER_MUXER    (CRC, crc);
     REGISTER_MUXDEMUX (DAUD, daud);
+    REGISTER_DEMUXER  (DFA, dfa);
     REGISTER_MUXDEMUX (DIRAC, dirac);
     REGISTER_MUXDEMUX (DNXHD, dnxhd);
     REGISTER_DEMUXER  (DSICIN, dsicin);
diff --git a/libavformat/dfa.c b/libavformat/dfa.c
new file mode 100644
index 0000000..aa7719e
--- /dev/null
+++ b/libavformat/dfa.c
@@ -0,0 +1,122 @@ 
+/*
+ * Chronomaster DFA Format Demuxer
+ * Copyright (c) 2011 Konstantin Shishkov
+ *
+ * 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 "avformat.h"
+
+static int dfa_probe(AVProbeData *p)
+{
+    if (p->buf_size < 4)
+        return 0;
+    if (AV_RL32(p->buf) != MKTAG('D', 'F', 'I', 'A'))
+        return 0;
+
+    return AVPROBE_SCORE_MAX;
+}
+
+static int dfa_read_header(AVFormatContext *s,
+                           AVFormatParameters *ap)
+{
+    AVIOContext *pb = s->pb;
+    AVStream *st;
+    int frames;
+    uint32_t fps;
+
+    if (avio_rl32(pb) != MKTAG('D', 'F', 'I', 'A')) {
+        av_log(s, AV_LOG_ERROR, "Invalid magic for DFA\n");
+        return -1;
+    }
+    avio_skip(pb, 2);
+    frames = avio_rl16(pb);
+
+    /* init video codec */
+    st = av_new_stream(s, 0);
+    if (!st)
+        return -1;
+
+    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+    st->codec->codec_id   = CODEC_ID_DFA;
+    st->codec->width      = avio_rl16(pb);
+    st->codec->height     = avio_rl16(pb);
+    fps = avio_rl32(pb);
+    if (!fps) {
+        av_log(s, AV_LOG_WARNING, "Zero FPS reported, defaulting to 10\n");
+        fps = 100;
+    }
+    av_set_pts_info(st, 24, fps, 1000);
+    avio_skip(pb, 128-16);
+    st->duration = frames;
+
+    return 0;
+}
+
+static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    AVIOContext *pb = s->pb;
+    uint32_t frame_size;
+    int ret, first = 1;
+
+    if(pb->eof_reached)
+        return AVERROR_EOF;
+
+    if (av_get_packet(pb, pkt, 12) != 12)
+        return AVERROR(EIO);
+    while (!pb->eof_reached) {
+        if (!first) {
+            ret = av_append_packet(pb, pkt, 12);
+            if (ret < 0) {
+                av_free_packet(pkt);
+                return ret;
+            }
+        } else
+            first = 0;
+        frame_size = AV_RL32(pkt->data + pkt->size - 8);
+        if (frame_size > INT_MAX - 4) {
+            av_log(s, AV_LOG_ERROR, "Too large chunk size: %d\n", frame_size);
+            return AVERROR(EIO);
+        }
+        if (AV_RL32(pkt->data + pkt->size - 12) == MKTAG('E', 'O', 'F', 'R')) {
+            if (frame_size) {
+                av_log(s, AV_LOG_WARNING, "skipping %d bytes of end-of-frame marker chunk\n",
+                       frame_size);
+                avio_skip(pb, frame_size);
+            }
+            return 0;
+        }
+        ret = av_append_packet(pb, pkt, frame_size);
+        if (ret < 0) {
+            av_free_packet(pkt);
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+AVInputFormat ff_dfa_demuxer = {
+    "dfa",
+    NULL_IF_CONFIG_SMALL("Chronomaster DFA"),
+    0,
+    dfa_probe,
+    dfa_read_header,
+    dfa_read_packet,
+    .flags = AVFMT_GENERIC_INDEX,
+};
diff --git a/libavformat/version.h b/libavformat/version.h
index aed8908..ae2f580 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -24,7 +24,7 @@ 
 #include "libavutil/avutil.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 52
-#define LIBAVFORMAT_VERSION_MINOR 103
+#define LIBAVFORMAT_VERSION_MINOR 104
 #define LIBAVFORMAT_VERSION_MICRO  0
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \