Chronomaster DFA decoder

Message ID 20110315103956.GA25880@kst-acer
State Superseded
Headers show

Commit Message

Kostya Shishkov March 15, 2011, 10:39 a.m.
On Tue, Mar 15, 2011 at 11:10:01AM +0100, Reinhard Tartler wrote:
> 
> 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?

http://samples.mplayerhq.hu/game-formats/chronomaster-dfa/0002.dfa
should do if one likes to add it

[...] 
> > +
> > +#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?

Well, get_bits.h is not needed here at all (will be dropped) but I don't see
any problems with the rest of includes.

[...]
> > +        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.

D'oh! Of course it should be removed.

[Stray empty line at the end was removed too]

Comments

Reinhard Tartler March 15, 2011, 2:57 p.m. | #1
On Tue, Mar 15, 2011 at 11:39:56 (CET), Kostya wrote:

> On Tue, Mar 15, 2011 at 11:10:01AM +0100, Reinhard Tartler wrote:
>> 
>> 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?
>
> http://samples.mplayerhq.hu/game-formats/chronomaster-dfa/0002.dfa
> should do if one likes to add it

What's the current process to add new media to fate? Bugreport where?
whom to poke? Where to add the file in the code?
Mike Melanson March 15, 2011, 3:25 p.m. | #2
On 03/15/2011 07:57 AM, Reinhard Tartler wrote:
>> http://samples.mplayerhq.hu/game-formats/chronomaster-dfa/0002.dfa
>> should do if one likes to add it

Added:

http://samples.mplayerhq.hu/fate-suite/chronomaster-dfa/

> What's the current process to add new media to fate? Bugreport where?
> whom to poke? Where to add the file in the code?

No formal process yet. Just announce it to this list and someone with 
access to the server will make it happen. Just please make sure that the 
sample decodes to the same result on at least 2 different CPU architectures.
Reinhard Tartler March 15, 2011, 9:21 p.m. | #3
On Tue, Mar 15, 2011 at 16:25:03 (CET), Mike Melanson wrote:

> On 03/15/2011 07:57 AM, Reinhard Tartler wrote:
>>> http://samples.mplayerhq.hu/game-formats/chronomaster-dfa/0002.dfa
>>> should do if one likes to add it
>
> Added:
>
> http://samples.mplayerhq.hu/fate-suite/chronomaster-dfa/

Thanks!

I'm currently uploading an updated fate package to
https://launchpad.net/~motumedia/+archive/ffmpeg-daily/+packages

>> What's the current process to add new media to fate? Bugreport where?
>> whom to poke? Where to add the file in the code?
>
> No formal process yet. Just announce it to this list and someone with
> access to the server will make it happen. Just please make sure that the
> sample decodes to the same result on at least 2 different CPU
> architectures.

Okay. I'm curious, what needs to be changed in the proposed dfa patch so
that it is actually being used on the fate machines?
Mans Rullgard March 15, 2011, 9:26 p.m. | #4
Reinhard Tartler <siretart@tauware.de> writes:

> On Tue, Mar 15, 2011 at 16:25:03 (CET), Mike Melanson wrote:
>
>> On 03/15/2011 07:57 AM, Reinhard Tartler wrote:
>>>> http://samples.mplayerhq.hu/game-formats/chronomaster-dfa/0002.dfa
>>>> should do if one likes to add it
>>
>> Added:
>>
>> http://samples.mplayerhq.hu/fate-suite/chronomaster-dfa/
>
> Thanks!
>
> I'm currently uploading an updated fate package to
> https://launchpad.net/~motumedia/+archive/ffmpeg-daily/+packages
>
>>> What's the current process to add new media to fate? Bugreport where?
>>> whom to poke? Where to add the file in the code?
>>
>> No formal process yet. Just announce it to this list and someone with
>> access to the server will make it happen. Just please make sure that the
>> sample decodes to the same result on at least 2 different CPU
>> architectures.
>
> Okay. I'm curious, what needs to be changed in the proposed dfa patch so
> that it is actually being used on the fate machines?

You need to add a test command and reference output.

Patch

From 233f046d050f4dae193b7c1b47b599c7031202c9 Mon Sep 17 00:00:00 2001
From: Kostya Shishkov <kostya.shishkov@gmail.com>
Date: Tue, 15 Mar 2011 09:37:48 +0100
Subject: [PATCH] Chronomaster DFA decoder

---
 Changelog                |    1 +
 doc/general.texi         |    4 +
 libavcodec/Makefile      |    1 +
 libavcodec/allcodecs.c   |    1 +
 libavcodec/avcodec.h     |    3 +-
 libavcodec/dfa.c         |  406 ++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/Makefile     |    1 +
 libavformat/allformats.c |    1 +
 libavformat/dfa.c        |  122 ++++++++++++++
 libavformat/version.h    |    2 +-
 10 files changed, 540 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..0b82d1e
--- /dev/null
+++ b/libavcodec/dfa.c
@@ -0,0 +1,406 @@ 
+/*
+ * 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"
+// 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;
+        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, \
-- 
1.7.0.4