From 55adc04a679391b05f1c10f0db5020904cdbb01e Mon Sep 17 00:00:00 2001
From: Serge GW <tipok@grabber.(none)>
Date: Fri, 16 Apr 2010 19:28:28 +0300
Subject: [PATCH] crusher264 full 16042010

---
 configure                  |    9 +
 doc/general.texi           |    2 +
 libavcodec/Makefile        |    1 +
 libavcodec/allcodecs.c     |    1 +
 libavcodec/libcrusher264.c |  466 ++++++++++++++++++++++++++++++++++++++++++++
 libavdevice/Makefile       |    1 +
 libavdevice/alldevices.c   |    1 +
 libavdevice/crushercap.c   |  391 +++++++++++++++++++++++++++++++++++++
 libavformat/Makefile       |    1 +
 libavformat/allformats.c   |    1 +
 libavformat/qbox.c         |  278 ++++++++++++++++++++++++++
 libavformat/qbox.h         |   69 +++++++
 12 files changed, 1221 insertions(+), 0 deletions(-)
 create mode 100644 libavcodec/libcrusher264.c
 create mode 100644 libavdevice/crushercap.c
 create mode 100644 libavformat/qbox.c
 create mode 100644 libavformat/qbox.h

diff --git a/configure b/configure
index 25e8cef..66be170 100755
--- a/configure
+++ b/configure
@@ -166,6 +166,7 @@ External library support:
   --enable-bzlib           enable bzlib [autodetect]
   --enable-libopencore-amrnb enable AMR-NB de/encoding via libopencore-amrnb [no]
   --enable-libopencore-amrwb enable AMR-WB decoding via libopencore-amrwb [no]
+  --enable-libcrusher264   enable H.264 encoding via crusher264 [no]
   --enable-libdc1394       enable IIDC-1394 grabbing using libdc1394
                            and libraw1394 [no]
   --enable-libdirac        enable Dirac support via libdirac [no]
@@ -910,6 +911,7 @@ CONFIG_LIST="
     gray
     h264dsp
     hardcoded_tables
+    libcrusher264
     libdc1394
     libdirac
     libfaac
@@ -1334,6 +1336,7 @@ h264_parser_select="golomb h264dsp"
 aac_adtstoasc_bsf_select="aac_parser"
 
 # external libraries
+libcrusher264_encoder_deps="libcrusher264 qbox_demuxer"
 libdirac_decoder_deps="libdirac !libschroedinger"
 libdirac_encoder_deps="libdirac"
 libfaac_encoder_deps="libfaac"
@@ -1394,6 +1397,7 @@ audio_beos_indev_extralibs="-lmedia -lbe"
 audio_beos_outdev_deps="audio_beos"
 audio_beos_outdev_extralibs="-lmedia -lbe"
 bktr_indev_deps_any="dev_bktr_ioctl_bt848_h machine_ioctl_bt848_h dev_video_bktr_ioctl_bt848_h dev_ic_bt8xx_h"
+crushercap_indev_deps="libcrusher264 crushercap qbox_demuxer"
 dv1394_indev_deps="dv1394 dv_demuxer"
 jack_indev_deps="jack_jack_h"
 jack_indev_extralibs="-ljack"
@@ -2251,6 +2255,7 @@ case $target_os in
         enable dos_paths
         ;;
     linux)
+        enable crushercap
         enable dv1394
         ;;
     irix*)
@@ -2333,6 +2338,7 @@ die_license_disabled() {
     enabled $1 || { enabled $2 && die "$2 is $1 and --enable-$1 is not specified."; }
 }
 
+die_license_disabled gpl libcrusher264
 die_license_disabled gpl libfaad2
 die_license_disabled gpl libx264
 die_license_disabled gpl libxvid
@@ -2607,6 +2613,8 @@ check_mathfunc truncf
 
 # these are off by default, so fail if requested and not available
 enabled avisynth   && require2 vfw32 "windows.h vfw.h" AVIFileInit -lavifil32
+enabled libcrusher264  && add_cflags $(pkg-config --cflags crusher264) &&
+                      require  libcrusher264 crusher264/crusher.h crusher_encode $(pkg-config --libs crusher264)
 enabled libdirac   && add_cflags $(pkg-config --cflags dirac) &&
                       require  libdirac libdirac_decoder/dirac_parser.h dirac_decoder_init $(pkg-config --libs dirac) &&
                       require  libdirac libdirac_encoder/dirac_encoder.h dirac_encoder_init $(pkg-config --libs dirac)
@@ -2886,6 +2894,7 @@ echo "threading support         ${thread_type-no}"
 echo "SDL support               ${sdl-no}"
 echo "Sun medialib support      ${mlib-no}"
 echo "AVISynth enabled          ${avisynth-no}"
+echo "libcrusher264 enabled     ${libcrusher264-no}"
 echo "libdc1394 support         ${libdc1394-no}"
 echo "libdirac enabled          ${libdirac-no}"
 echo "libfaac enabled           ${libfaac-no}"
diff --git a/doc/general.texi b/doc/general.texi
index 1bfde02..cbcc96b 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -151,6 +151,8 @@ library:
 @item Ogg                       @tab X @tab X
 @item TechnoTrend PVA           @tab   @tab X
     @tab Used by TechnoTrend DVB PCI boards.
+@item QBOX                      @tab   @tab X
+    @tab Used by Maxim codec chips
 @item QCP                       @tab   @tab X
 @item raw ADTS (AAC)            @tab X @tab X
 @item raw AC-3                  @tab X @tab X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 68b42b4..f1d7365 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -502,6 +502,7 @@ OBJS-$(CONFIG_OGG_MUXER)               += xiph.o flacdec.o flacdata.o flac.o
 OBJS-$(CONFIG_RTP_MUXER)               += mpegvideo.o
 
 # external codec libraries
+OBJS-$(CONFIG_LIBCRUSHER264_ENCODER)      += libcrusher264.o
 OBJS-$(CONFIG_LIBDIRAC_DECODER)           += libdiracdec.o
 OBJS-$(CONFIG_LIBDIRAC_ENCODER)           += libdiracenc.o libdirac_libschro.o
 OBJS-$(CONFIG_LIBFAAC_ENCODER)            += libfaac.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 662ae02..12a3a4b 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -331,6 +331,7 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC  (XSUB, xsub);
 
     /* external libraries */
+    REGISTER_ENCODER (LIBCRUSHER264, libcrusher264);
     REGISTER_ENCDEC  (LIBDIRAC, libdirac);
     REGISTER_ENCODER (LIBFAAC, libfaac);
     REGISTER_DECODER (LIBFAAD, libfaad);
diff --git a/libavcodec/libcrusher264.c b/libavcodec/libcrusher264.c
new file mode 100644
index 0000000..ae39a87
--- /dev/null
+++ b/libavcodec/libcrusher264.c
@@ -0,0 +1,466 @@
+/*
+ * H.264 encoding using Maxim's hardware codec
+ * Copyright (c) 2009 Sergiy Gur'yev ( piratfm gmail com )
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+* @file libavcodec/libcrusher264.c
+* H.264 encoder support via libcrusher264 library and hardware accelerator; more details about the Crusher
+* reference platform can be found at http://tipok.org.ua/node/13.
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+/* FFmpeg includes */
+#include "avcodec.h"
+#include "dsputil.h"
+#include "libavformat/qbox.h"
+
+/* libcrusher264 includes */
+#include <crusher264/crusher.h>
+
+#undef CRUSHER_EXTRA_DEBUG
+
+typedef struct CrusherEncContext {
+    crusher_t       enc;
+    uint8_t        *sps_pps;         ///< sps/pps frame, received from device
+    int             sps_pps_size;
+    int             curr_block;       ///< current qbox (it contains 1 or 2 h264 nals)
+    int             frames_buff_size;
+    int             frames_buff_fill;
+    int             frames_buff_skip;
+    qboxContext    *frames_buff;     ///< output buffer
+    int             frames_delay;
+    int             conv_lines;
+    int             conv_uv_lines;
+    AVPicture       conv_input;
+    AVPicture       conv_output;
+} CrusherEncContext;
+
+
+/** convert part of image with height=32 and width=64
+ *                32       64
+ *ptr_in->+--------+--------+
+ *        |   1    |    2   |
+ *     16 +--------+--------+
+ *        |   3    |    4   |
+ *     32 +--------+--------+
+ */
+static inline void conv_copy4_16x32(uint8_t *ptr_out, uint8_t *ptr_in, uint16_t Stride)
+{
+    int line;
+    for (line=0; line < 16; line++) {
+        memcpy (ptr_out + (line*32),             ptr_in + (line*Stride),                    32);
+        memcpy (ptr_out + (line*32) + (32*16),   ptr_in + (line*Stride) + 32,               32);
+        memcpy (ptr_out + (line*32) + (32*16)*2, ptr_in + (line*Stride) + (16*Stride),      32);
+        memcpy (ptr_out + (line*32) + (32*16)*3, ptr_in + (line*Stride) + (16*Stride) + 32, 32);
+    }
+}
+
+/** convert 32 lines
+ */
+static inline void conv_copy32Lines(uint8_t *ptr_out, uint8_t *ptr_in, uint8_t cols)
+{
+    int fourBlockNum;
+    const uint16_t Stride = cols*(2*32);
+    for(fourBlockNum = 0; fourBlockNum < cols; fourBlockNum++)
+        conv_copy4_16x32(ptr_out + (32*16)*4*fourBlockNum, ptr_in + (32*2)*fourBlockNum, Stride);
+}
+
+/** convert full frame
+ */
+static void conv_frame(CrusherEncContext *ctx)
+{
+    const int cols = ctx->conv_input.linesize[0] >> 6;
+    const int rows = (ctx->conv_lines + ctx->conv_uv_lines) >> 5;
+
+    const int lines_32_size = (32*16) * 2 * 2 * cols;
+    int lines_32_num;
+    for(lines_32_num = 0; lines_32_num < rows; lines_32_num++)
+        conv_copy32Lines(ctx->conv_output.data[0] + lines_32_size * lines_32_num,
+                ctx->conv_input.data[0] + lines_32_size * lines_32_num, cols);
+}
+
+/** parse every qbox, received from codec, when have "flush" event
+ */
+static int add_nals(AVCodecContext *ctx, int alloc_buff)
+{
+    CrusherEncContext *c4 = ctx->priv_data;
+    qboxContext qbox;
+    int i;
+    int oldptr = c4->frames_buff_fill;
+
+    if(!c4->enc.out_blocks)
+        return 0;
+
+    /* in most cases we have 6 qboxes at one event */
+    for(i=c4->curr_block; i < c4->enc.out_blocks; i++) {
+        if(ff_qbox_parse(&qbox, c4->enc.out_data[i].data, c4->enc.out_data[i].len)) {
+#ifdef CRUSHER_EXTRA_DEBUG
+            av_log(ctx, AV_LOG_DEBUG, "QBOX: sst:0x%08x sf:0x%08x l:%d ts:%d\n",
+                    qbox.sample_stream_type, qbox.sample_flags, qbox.qbox_size, qbox.sample_cts);
+#endif
+            if(qbox.sample_stream_type != SAMPLE_TYPE_H264) {
+                av_log(ctx, AV_LOG_ERROR, "Unknown sample flags 0x%08x (maybe audio?)\n", qbox.sample_flags);
+                return -1;
+            }
+
+            if(!ff_qbox_make_startcode(qbox.data, qbox.qbox_size)){
+                av_log(ctx, AV_LOG_ERROR, "can't create H264 startcodes\n");
+                return -1;
+            }
+
+            /* payload started ? */
+            if(qbox.sample_flags & SAMPLE_FLAGS_CONFIGURATION_INFO) {
+                if(alloc_buff) {
+                    c4->sps_pps_size = qbox.qbox_size;
+                    c4->sps_pps = av_malloc(c4->sps_pps_size);
+                    memcpy(c4->sps_pps, qbox.data, c4->sps_pps_size);
+                } else {
+                    av_log(ctx, AV_LOG_ERROR, "SPS/PPS received in the middle of encoding\n");
+                }
+            } else {
+                if(c4->frames_buff_skip) {
+                    c4->frames_buff_skip--;
+                    continue;
+                }
+                if(c4->frames_buff_fill == c4->frames_buff_size) {
+                    if(alloc_buff) {
+                        c4->frames_buff_size++;
+                        c4->frames_buff = av_realloc(c4->frames_buff, sizeof(qboxContext) * c4->frames_buff_size);
+                    } else {
+                        av_log(ctx, AV_LOG_ERROR, "too small buffer (%d frames)\n", c4->frames_buff_size);
+                        return -1;
+                    }
+                }
+
+                memcpy(&c4->frames_buff[c4->frames_buff_fill], &qbox, QBOX_HDR_SIZE);
+                /* add extradata if needed */
+                if (ctx->flags & CODEC_FLAG_GLOBAL_HEADER || !(qbox.sample_flags & SAMPLE_FLAGS_SYNC_POINT)) {
+                    c4->frames_buff[c4->frames_buff_fill].data = av_malloc(qbox.qbox_size);
+                    memcpy(c4->frames_buff[c4->frames_buff_fill].data, qbox.data, qbox.qbox_size);
+                } else { /* sync point and no global header */
+                    c4->frames_buff[c4->frames_buff_fill].qbox_size+=c4->sps_pps_size;
+                    c4->frames_buff[c4->frames_buff_fill].data = av_malloc(c4->frames_buff[c4->frames_buff_fill].qbox_size);
+                    memcpy(c4->frames_buff[c4->frames_buff_fill].data, c4->sps_pps, c4->sps_pps_size);
+                    memcpy(c4->frames_buff[c4->frames_buff_fill].data + c4->sps_pps_size, qbox.data, qbox.qbox_size);
+                }
+
+                c4->frames_buff_fill++;
+            }
+        }
+    }
+    return c4->frames_buff_fill - oldptr;
+}
+
+
+/** encode dummy gop
+ * Huge workaround:
+ * Due sps/pps sent only while encoding started, force sending
+ * gop with empty frames until extradata received from device,
+ * also count frames, buffered inside device
+ */
+static int encode_dummy_gops(AVCodecContext *ctx)
+{
+    CrusherEncContext *c4 = ctx->priv_data;
+    int i, ret, frames2send = 0;
+    int sended_frames = 0;
+
+    c4->frames_buff_size=0;
+    c4->frames_buff_fill=0;
+    c4->frames_buff = av_malloc(sizeof(qboxContext) * c4->frames_buff_size);
+
+    do {
+        do {
+            ret = crusher_encode(&c4->enc, c4->conv_output.data[0], c4->enc.inputFrameLen);
+            if(ret == CODEC_FLUSHED)
+                add_nals(ctx, 1);
+        } while (ret == CODEC_FLUSHED);
+        sended_frames++;
+    } while (ret != CODEC_FAIL && ret != CODEC_FINISHED && !c4->sps_pps_size);
+
+    if(ret == CODEC_FAIL || ret == CODEC_FINISHED || !c4->sps_pps_size)
+        return -1;
+
+    if(sended_frames % c4->enc.gopsize)
+        frames2send = c4->enc.gopsize - (sended_frames % c4->enc.gopsize);
+    c4->frames_delay = sended_frames;// - c4->frames_buff_fill;
+#ifdef CRUSHER_EXTRA_DEBUG
+    av_log(ctx, AV_LOG_DEBUG, "sent: %d, need more %d frames to fill gop %d\n",
+           sended_frames, frames2send, c4->enc.gopsize);
+#endif
+    do {
+        do {
+            ret = crusher_encode(&c4->enc, c4->conv_output.data[0], c4->enc.inputFrameLen);
+            if(ret == CODEC_FLUSHED)
+                add_nals(ctx, 1);
+        } while (ret == CODEC_FLUSHED);
+        frames2send--;
+        sended_frames++;
+    } while (ret != CODEC_FAIL && ret != CODEC_FINISHED && frames2send);
+
+    if(ret == CODEC_FAIL || ret == CODEC_FINISHED || sended_frames % c4->enc.gopsize)
+        return -1;
+
+    c4->frames_buff_skip = sended_frames - c4->frames_buff_fill;
+#ifdef CRUSHER_EXTRA_DEBUG
+    av_log(ctx, AV_LOG_DEBUG, "skipping %d empy frames\n", c4->frames_buff_skip);
+#endif
+
+    /* free allocated data for params detection */
+    for (i=0; i < c4->frames_buff_fill; i++)
+        av_free(c4->frames_buff[i].data);
+
+#ifdef CRUSHER_EXTRA_DEBUG
+    av_log(ctx, AV_LOG_DEBUG, "Codec buffer: size:%d, ptr:%d, sended:%d\n",
+           c4->frames_buff_size, c4->frames_buff_fill, sended_frames);
+#endif
+
+    c4->frames_buff_size = c4->frames_delay;
+    c4->frames_buff_fill = 0;
+    c4->frames_buff = av_realloc(c4->frames_buff, sizeof(qboxContext) * c4->frames_buff_size);
+
+#ifdef CRUSHER_EXTRA_DEBUG
+    av_log(ctx, AV_LOG_DEBUG, "new buff size: %d\n", c4->frames_buff_size);
+#endif
+    return sended_frames;
+}
+
+/** encode 1 frame
+ */
+static int CrusherEnc_frame(AVCodecContext *ctx, uint8_t *buf,
+                                int bufsize, void *data)
+{
+    CrusherEncContext *c4 = ctx->priv_data;
+    AVFrame *frame = data;
+    int ret = -1;
+    int datalen = 0;
+
+    assert(ctx->pix_fmt == PIX_FMT_NV12);
+
+    if(frame) {
+        /* canvas size & copy frame
+         * can't use av_picture_copy due uv width must be equal y width */
+        ff_img_copy_plane(c4->conv_input.data[0], c4->conv_input.linesize[0],
+                frame->data[0], frame->linesize[0], ctx->width, ctx->height);
+        ff_img_copy_plane(c4->conv_input.data[1], c4->conv_input.linesize[1],
+                frame->data[1], frame->linesize[1], ctx->width, ctx->height>>1);
+
+        /* Convert planes to the crusher format (sequence of 32x16 blocks in NV12 format) */
+        conv_frame(c4);
+        do  {
+            ret = crusher_encode(&c4->enc, c4->conv_output.data[0], c4->enc.inputFrameLen);
+            if(ret == CODEC_FLUSHED)
+                add_nals(ctx, 0);
+        } while (ret == CODEC_FLUSHED);
+    } else if(!c4->enc.finished) {
+        av_log(ctx, AV_LOG_DEBUG, "Codec finishing!\n");
+        do {
+            ret = crusher_encode(&c4->enc, NULL, 0);
+            if (ret == CODEC_FLUSHED)
+                add_nals(ctx, 0);
+        } while(ret != CODEC_FAIL && ret != CODEC_FINISHED);
+    }
+
+    if(ret == CODEC_FAIL) {
+        av_log(ctx, AV_LOG_ERROR, "Failed\n");
+        return -1;
+    }
+
+    /* send frame from buffer */
+    if(c4->frames_buff_fill) {
+        assert(bufsize >= c4->frames_buff[0].size);
+#ifdef CRUSHER_EXTRA_DEBUG
+        av_log(ctx, AV_LOG_DEBUG, "Frame out[0/%d/%d]:"
+                                  "len:%d, type:%d, ts:%d\n",
+                c4->frames_buff_fill, c4->frames_buff_size,
+                c4->frames_buff[0].qbox_size,
+                c4->frames_buff[0].sample_stream_type,
+                c4->frames_buff[0].sample_cts);
+#endif
+        datalen = c4->frames_buff[0].qbox_size;
+        memcpy(buf, c4->frames_buff[0].data, datalen);
+        av_freep(&c4->frames_buff[0].data);
+
+        if(c4->frames_buff[0].sample_flags & SAMPLE_FLAGS_SYNC_POINT) {
+            ctx->coded_frame->key_frame = 1;
+            ctx->coded_frame->pict_type = FF_I_TYPE;
+        } else {
+            ctx->coded_frame->key_frame = 0;
+            ctx->coded_frame->pict_type = FF_P_TYPE;
+        }
+        //just return number of frame?
+        if (c4->frames_buff[0].sample_flags & SAMPLE_FLAGS_CTS_PRESENT) {
+            ctx->coded_frame->pts = c4->frames_buff[0].sample_cts/c4->enc.framerate_den - c4->frames_delay;
+        } else {
+            ctx->coded_frame->pts = AV_NOPTS_VALUE;
+        }
+
+        c4->frames_buff_fill--;
+        if(c4->frames_buff_fill)
+            memmove(&c4->frames_buff[0], &c4->frames_buff[1], c4->frames_buff_fill * sizeof(qboxContext));
+    } else if(frame) {
+        av_log(ctx, AV_LOG_DEBUG, "no frames in buffer\n");
+    } else {
+        av_log(ctx, AV_LOG_DEBUG, "no more input and output frames\n");
+    }
+
+    return datalen;
+}
+
+static av_cold int CrusherEnc_close(AVCodecContext *avctx)
+{
+    CrusherEncContext *c4 = avctx->priv_data;
+    int i;
+
+    av_log(avctx, AV_LOG_DEBUG, "Closing encoder\n");
+
+    avpicture_free(&c4->conv_input);
+    avpicture_free(&c4->conv_output);
+    crusher_close(&c4->enc);
+
+    for (i=0; i < c4->frames_buff_fill; i++)
+        av_freep(&c4->frames_buff[i].data);
+    av_freep(&c4->frames_buff);
+    av_freep(&avctx->coded_frame);
+    return 0;
+}
+
+static av_cold int CrusherEnc_init(AVCodecContext *avctx)
+{
+    CrusherEncContext *c4 = avctx->priv_data;
+
+    assert(ctx->avctx == PIX_FMT_NV12);
+
+    crusher_defaults(&c4->enc);
+    /* upload fx2 firmware if not done yet. */
+    if( !crusher_find_device(&c4->enc, 1) ) {
+        av_log(avctx, AV_LOG_ERROR, "device not found");
+        goto CrusherEnc_init_fail;
+    }
+
+    c4->enc.out_format    = OUT_FORMAT_QBOX;
+    c4->enc.width         = avctx->width;
+    c4->enc.height        = avctx->height;
+    c4->enc.bitrate       = avctx->bit_rate;
+    c4->enc.framerate_den = avctx->time_base.num;
+    c4->enc.framerate_num = avctx->time_base.den;
+    c4->enc.gopsize       = avctx->gop_size;
+    if(avctx->rc_buffer_size)
+        c4->enc.rc_size   = avctx->rc_buffer_size;
+
+    c4->enc.scenecut_threshold = avctx->scenechange_threshold;
+    c4->enc.deblock            = (avctx->flags & CODEC_FLAG_LOOP_FILTER) ? 1 : 0;
+    c4->enc.deblock_alpha      = avctx->deblockalpha;
+    c4->enc.deblock_beta       = avctx->deblockbeta;
+#ifdef CRUSHER_EXTRA_DEBUG
+    av_log(avctx, AV_LOG_DEBUG, "fmt=%d, w=%d, h=%d, br=%d, gop_s=%d, rc_s=%d, sc_newgop=%d, db=%d, db_a=%d, db_b=%d\ntb: num=%d, den=%d\n",
+        c4->enc.out_format, c4->enc.width, c4->enc.height, c4->enc.bitrate, c4->enc.gopsize,
+        c4->enc.rc_size, c4->enc.scenecut_threshold, c4->enc.deblock, c4->enc.deblock_alpha, c4->enc.deblock_beta, avctx->time_base.num, avctx->time_base.den);
+#endif
+
+    if( !crusher_init_device(&c4->enc) ) {
+        av_log(avctx, AV_LOG_ERROR, "init failed");
+        goto CrusherEnc_init_fail;
+    }
+
+    /* send default textconfig. */
+    if( !crusher_send_textconfig(&c4->enc) ) {
+        av_log(avctx, AV_LOG_ERROR, "sending textconfig failed");
+        goto CrusherEnc_init_fail;
+    }
+
+    c4->conv_input.linesize[0]  = FFALIGN(avctx->width, 64);
+    c4->conv_lines              = FFALIGN(avctx->height, 32);
+    c4->conv_uv_lines           = FFALIGN(RSHIFT(avctx->height, 1), 32);
+    c4->enc.inputFrameLen       = c4->conv_input.linesize[0] * (c4->conv_lines + c4->conv_uv_lines);
+
+    /* can't use avpicture_alloc due UV height must be aligned by 32 */
+    c4->conv_input.data[0]      = av_malloc(c4->enc.inputFrameLen);
+    c4->conv_input.linesize[1]  = c4->conv_input.linesize[0];
+    c4->conv_input.data[1]      = c4->conv_input.data[0] + c4->conv_input.linesize[0] * c4->conv_lines;
+
+    c4->conv_output.data[0]     = av_malloc(c4->enc.inputFrameLen);
+    c4->conv_output.linesize[0] = c4->conv_input.linesize[0];
+    c4->conv_output.linesize[1] = c4->conv_input.linesize[1];
+    c4->conv_output.data[1]     = c4->conv_output.data[0] + c4->conv_output.linesize[0] * c4->conv_lines;
+
+    /* allocate place for image converter input & output */
+    if(!c4->conv_input.data[0] || !c4->conv_output.data[0]){
+        av_log(avctx, AV_LOG_ERROR, "can't allocate picture converter");
+        goto CrusherEnc_init_fail;
+    }
+
+#ifdef CRUSHER_EXTRA_DEBUG
+    av_log(avctx, AV_LOG_DEBUG, "converter: newsize:%dx%d uv:%dx%d framelen: %d\n",
+            c4->conv_input.linesize[0], c4->conv_lines,
+            c4->conv_input.linesize[0], c4->conv_uv_lines,
+            c4->enc.inputFrameLen);
+#endif
+    avctx->coded_frame    = avcodec_alloc_frame();           ///< Set up the output AVFrame
+    c4->curr_block        = 0;
+
+    if(!crusher_start(&c4->enc)){
+          av_log(avctx, AV_LOG_ERROR, "starting codec device failed");
+          goto CrusherEnc_init_fail;
+    }
+
+    if(encode_dummy_gops(avctx) < 0){
+        av_log(avctx, AV_LOG_ERROR, "encoding dummy GOP failed");
+        goto CrusherEnc_init_fail;
+    }
+
+    if(!c4->sps_pps_size) {
+        av_log(avctx, AV_LOG_ERROR, "codec don't send SPS/PPS, failed");
+        goto CrusherEnc_init_fail;
+    }
+
+    /* extradata received */
+    if (avctx->flags & CODEC_FLAG_GLOBAL_HEADER) {
+        avctx->extradata_size = c4->sps_pps_size;
+        avctx->extradata      = av_malloc(c4->sps_pps_size);
+        memcpy(avctx->extradata, c4->sps_pps, c4->sps_pps_size);
+        av_log(avctx, AV_LOG_DEBUG, "codec extradata: %d bytes\n", c4->sps_pps_size);
+    }
+    return 0;
+
+CrusherEnc_init_fail:
+    crusher_close(&c4->enc);
+    avpicture_free(&c4->conv_input);
+    avpicture_free(&c4->conv_output);
+    return -1;
+}
+
+AVCodec libcrusher264_encoder = {
+    .name           = "libcrusher264",
+    .type           = CODEC_TYPE_VIDEO,
+    .id             = CODEC_ID_H264,
+    .priv_data_size = sizeof(CrusherEncContext),
+    .init           = CrusherEnc_init,
+    .encode         = CrusherEnc_frame,
+    .close          = CrusherEnc_close,
+    .capabilities   = CODEC_CAP_DELAY,
+    .supported_framerates= (const AVRational[]){{2500,100}, {3000,100}, {0,0}},
+    .pix_fmts       = (const enum PixelFormat[]) { PIX_FMT_NV12, PIX_FMT_NONE },
+    .long_name      = NULL_IF_CONFIG_SMALL("libcrusher264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Hardwared)"),
+};
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index a0c3858..c9b757b 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -13,6 +13,7 @@ OBJS-$(CONFIG_ALSA_INDEV)                += alsa-audio-common.o \
 OBJS-$(CONFIG_ALSA_OUTDEV)               += alsa-audio-common.o \
                                             alsa-audio-enc.o
 OBJS-$(CONFIG_BKTR_INDEV)                += bktr.o
+OBJS-$(CONFIG_CRUSHERCAP_INDEV)          += crushercap.o
 OBJS-$(CONFIG_DV1394_INDEV)              += dv1394.o
 OBJS-$(CONFIG_JACK_INDEV)                += jack_audio.o
 OBJS-$(CONFIG_OSS_INDEV)                 += oss_audio.o
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index e7a9a5e..6336565 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -42,6 +42,7 @@ void avdevice_register_all(void)
     REGISTER_INOUTDEV (ALSA, alsa);
     REGISTER_INOUTDEV (AUDIO_BEOS, audio_beos);
     REGISTER_INDEV    (BKTR, bktr);
+    REGISTER_INDEV    (CRUSHERCAP, crushercap);
     REGISTER_INDEV    (DV1394, dv1394);
     REGISTER_INDEV    (JACK, jack);
     REGISTER_INOUTDEV (OSS, oss);
diff --git a/libavdevice/crushercap.c b/libavdevice/crushercap.c
new file mode 100644
index 0000000..1466b69
--- /dev/null
+++ b/libavdevice/crushercap.c
@@ -0,0 +1,391 @@
+/*
+ * Linux CrusherCapture (MG1264) interface
+ * Copyright (c) 2010 piratfm@gmail.com
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <time.h>
+#include <strings.h>
+
+#include <crusher264/crusher.h>
+#include "libavformat/avformat.h"
+#include "libavformat/qbox.h"
+#include "libavcodec/mpeg4audio.h"
+
+#undef CRUSHERCAP_EXTRA_DEBUG
+//#define CRUSHERCAP_EXTRA_DEBUG
+
+/* wrapping issue workaround:
+ * originally pts have only 32 bits, but due fast (~20 hours) filling
+ * additional counter used */
+#define CRUSHERCAP_EXTRA_PTS
+
+
+struct crusherCap_data {
+    crusher_t   crusher;
+    int qboxes_used;
+    qboxContext qbox; ///< qbox demuxing context
+    MPEG4AudioConfig cfg;
+#ifdef CRUSHERCAP_EXTRA_PTS
+    int64_t extra_pts;
+    uint32_t last_sample_cts;
+#endif
+};
+
+/** device initialization:
+ * - start device
+ * - read additional parameters from input string, and set up them
+ * - read extradata for h264/aac streams
+ * - parse aac-asc and set audio parameters
+ * - set time_base for a/v streams
+ */
+static int crushercap_read_header(AVFormatContext * s, AVFormatParameters * ap)
+{
+    struct crusherCap_data *ccap = s->priv_data;
+    AVStream *st;
+    int ret = CODEC_FAIL;
+    char *inparams_ptr, *str_ptr, *inparams = NULL;
+    int got_audio=0;
+    int got_video=0;
+
+#ifdef CRUSHERCAP_EXTRA_DEBUG
+    av_log(s, AV_LOG_DEBUG, "loading defaults\n");
+#endif
+    crusher_defaults(&ccap->crusher);
+
+
+    if( !crusher_find_device(&ccap->crusher, 1) ) {
+        av_log(s, AV_LOG_ERROR, "device not found\n");
+        goto failed;
+    }
+
+    if (ccap->crusher.devtype != DEV_TYPE_CAPTURE) {
+        av_log(s, AV_LOG_ERROR, "it's not capture device\n");
+        goto failed;
+    }
+
+    if (ap->channels <= 0) {
+        av_log(s, AV_LOG_ERROR, "Bad channels number %d\n", ap->channels);
+        goto failed;
+    }
+
+    if (ap->sample_rate <= 0) {
+        av_log(s, AV_LOG_ERROR, "Bad channels samplerate %d\n", ap->sample_rate);
+        goto failed;
+    }
+
+    ccap->crusher.framerate_num = 90000;
+    if (ap->standard && !strcasecmp(ap->standard, "ntsc")) {
+        ccap->crusher.framerate_den = 3003;
+    } else {
+        ccap->crusher.framerate_den = 3600;
+    }
+
+    if (ap->channel && ap->channel < 3) {
+        ccap->crusher.video_input = ap->channel;
+    } else {
+        ccap->crusher.video_input = CAPTURE_INPUT_COMPOSITE;
+    }
+
+    if(ap->width && ap->height) {
+        ccap->crusher.width = ap->width;
+        ccap->crusher.height = ap->height;
+    }
+
+    ccap->crusher.audio_samplerate = ap->sample_rate;
+    ccap->crusher.audio_channels = ap->channels;
+    ccap->crusher.devmode = DEV_TYPE_CAPTURE;
+    ccap->crusher.out_format = OUT_FORMAT_QBOX;
+    ccap->crusher.rc_size = 35000000;
+    ccap->crusher.scenecut_threshold = 0;
+    ccap->crusher.deblock = 0;
+    ccap->crusher.bitrate_mode = BITRATE_MODE_MEDIUM;
+
+    /* get additional parameters from "-i" argument */
+    inparams = av_strdup(s->filename);
+    inparams_ptr = strtok (inparams, ":");
+    while (inparams_ptr) {
+        str_ptr = strchr(inparams_ptr, '=');
+        if(str_ptr && strlen(inparams_ptr) > strlen(str_ptr)) {
+            *str_ptr='\0';
+            str_ptr++;
+            av_log(s, AV_LOG_DEBUG, "inparams: param:\"%s\", value:\"%s\"\n", inparams_ptr, str_ptr);
+            if(!strcmp(inparams_ptr, "devnum") || !strcmp(inparams_ptr, "d")) {
+                ccap->crusher.devnum = atoi(str_ptr);
+            } else if (!strcmp(inparams_ptr, "bitrate") || !strcmp(inparams_ptr, "b")) {
+                ccap->crusher.bitrate = atoi(str_ptr);
+                if(ccap->crusher.bitrate >= 2000000)
+                    ccap->crusher.bitrate_mode = BITRATE_MODE_HIGH;
+            } else if (!strcmp(inparams_ptr, "abitrate") || !strcmp(inparams_ptr, "ab")) {
+                ccap->crusher.audio_bitrate = atoi(str_ptr);
+
+            /* audio codec selector */
+            } else if (!strcmp(inparams_ptr, "acodec") && !strcmp(str_ptr, "pcm")) {
+                ccap->crusher.audio_codec = AUDIO_CODEC_PCM;
+            } else if (!strcmp(inparams_ptr, "acodec") && !strcmp(str_ptr, "aac")) {
+                ccap->crusher.audio_codec = AUDIO_CODEC_AAC;
+            } else if (!strcmp(inparams_ptr, "acodec") && !strcmp(str_ptr, "mp2")) {
+                ccap->crusher.audio_codec = AUDIO_CODEC_MP2;
+                av_log(s, AV_LOG_WARNING, "crushercap mp2 still not working\n");
+            } else {
+                av_log(s, AV_LOG_ERROR, "Bad input parameter: %s\n"
+                                        "Avaliable parameters:\n"
+                                        "devnum [d] - device number\n"
+                                        "bitrate [b] - video bitrate\n"
+                                        "abitrate [ab] - audio bitrate (no matter for pcm)\n"
+                                        "acodec - audio codec (aac,pcm,mp2 default:aac)\n", inparams_ptr);
+                av_free(inparams);
+                goto failed;
+            }
+
+        }
+        inparams_ptr = strtok (NULL, ":");
+    }
+    av_free(inparams);
+
+#ifdef CRUSHERCAP_EXTRA_DEBUG
+    av_log(s, AV_LOG_DEBUG, "i:%d ac:%d, ab:%d, ar:%d, b:%d, s:%dx%d r:%d/%d\n",
+            ccap->crusher.video_input, ccap->crusher.audio_channels, ccap->crusher.audio_bitrate,
+            ccap->crusher.audio_samplerate,
+            ccap->crusher.bitrate,
+            ccap->crusher.width, ccap->crusher.height, ccap->crusher.framerate_num, ccap->crusher.framerate_den);
+#endif
+
+    if( !crusher_init_device(&ccap->crusher) ) {
+        av_log(s, AV_LOG_ERROR, "init failed");
+        goto failed;
+    }
+
+    if( !crusher_send_textconfig(&ccap->crusher) ) {
+        av_log(s, AV_LOG_ERROR, "sending textconfig failed");
+        goto failed;
+    }
+
+    if( !crusher_start(&ccap->crusher)){
+        av_log(s, AV_LOG_ERROR, "starting device failed");
+        goto failed;
+    }
+
+    /* get extradata for a/v */
+    do {
+        ret = crusher_encode(&ccap->crusher, NULL, 0);
+        if (ret == CODEC_FLUSHED && ccap->crusher.out_blocks) {
+
+            for(ccap->qboxes_used=0; ccap->qboxes_used < ccap->crusher.out_blocks; ccap->qboxes_used++) {
+                if(ff_qbox_parse(&ccap->qbox, ccap->crusher.out_data[ccap->qboxes_used].data,
+                        ccap->crusher.out_data[ccap->qboxes_used].len)) {
+
+                    /* if no more configs, return */
+                    if(got_video && got_audio)
+                        return 0;
+
+                    switch(ccap->qbox.sample_stream_type){
+                        case SAMPLE_TYPE_AAC:
+                            if (!(st = av_new_stream(s, 1)))
+                                return AVERROR(ENOMEM);
+
+                            st->codec->codec_type = CODEC_TYPE_AUDIO;
+                            st->codec->codec_id   = CODEC_ID_AAC;
+                            st->codec->extradata_size = ccap->qbox.qbox_size;
+                            st->codec->extradata = av_malloc(st->codec->extradata_size);
+                            memcpy(st->codec->extradata, ccap->qbox.data, ccap->qbox.qbox_size);
+                            ff_mpeg4audio_get_config(&ccap->cfg, st->codec->extradata,
+                                                     st->codec->extradata_size);
+                            st->codec->channels = ccap->cfg.channels;
+                            st->codec->sample_rate = ccap->cfg.sample_rate;
+                            ap->sample_rate = ccap->cfg.sample_rate;
+                            st->codec->bit_rate = ccap->crusher.audio_bitrate;
+                            st->codec->sample_fmt = SAMPLE_FMT_S16;
+                            st->codec->frame_size = 1024;
+                            got_audio=1;
+                            break;
+
+                        case SAMPLE_TYPE_PCM:
+                            if (!(st = av_new_stream(s, 1)))
+                                return AVERROR(ENOMEM);
+
+                            st->codec->codec_type = CODEC_TYPE_AUDIO;
+                            st->codec->codec_id   = CODEC_ID_PCM_S16BE;
+                            st->codec->channels = ccap->crusher.audio_channels;
+                            st->codec->sample_rate = ccap->crusher.audio_samplerate;
+                            ap->sample_rate = ccap->crusher.audio_samplerate;
+                            st->codec->sample_fmt = SAMPLE_FMT_S16;
+                            got_audio=1;
+                            break;
+
+                        case SAMPLE_TYPE_QMA:
+                            if (!(st = av_new_stream(s, 1)))
+                                return AVERROR(ENOMEM);
+
+                            st->codec->codec_type = CODEC_TYPE_AUDIO;
+                            st->codec->codec_id   = CODEC_ID_MP2;
+                            st->codec->channels = ccap->crusher.audio_channels;
+                            st->codec->sample_rate = ccap->crusher.audio_samplerate;
+                            ap->sample_rate = ccap->crusher.audio_samplerate;
+                            st->codec->sample_fmt = SAMPLE_FMT_S16;
+                            got_audio=1;
+                            break;
+
+                        case SAMPLE_TYPE_H264:
+                            if (!(st = av_new_stream(s, 0)))
+                                return AVERROR(ENOMEM);
+                            st->codec->codec_type = CODEC_TYPE_VIDEO;
+                            st->codec->codec_id   = CODEC_ID_H264;
+                            st->codec->extradata_size = ccap->qbox.qbox_size;
+                            st->codec->extradata = av_malloc(st->codec->extradata_size);
+                            st->codec->bit_rate = ccap->crusher.bitrate;
+                            ff_qbox_make_startcode(ccap->qbox.data, ccap->qbox.qbox_size);
+                            memcpy(st->codec->extradata, ccap->qbox.data, ccap->qbox.qbox_size);
+                            got_video=1;
+                            /* TODO: set aspect ratio */
+                            break;
+
+                        default:
+                            av_log(s, AV_LOG_ERROR, "unknown qbox\n");
+                            goto failed;
+                            break;
+                    }
+
+#ifdef CRUSHERCAP_EXTRA_PTS
+                    av_set_pts_info(st, 64, 1, ccap->crusher.framerate_num);
+#else
+                    av_set_pts_info(st, 32, 1, ccap->crusher.framerate_num);
+#endif
+                } else {
+                    av_log(s, AV_LOG_ERROR, "bad qbox");
+                    goto failed;
+                }
+            }
+        }
+    } while ((ret == CODEC_FLUSHED ||ret == CODEC_FRAME_SENT) && ret != CODEC_FAIL);
+    return 0;
+
+failed:
+    crusher_close(&ccap->crusher);
+    return AVERROR(EIO);
+}
+
+/** reading qboxes from device (each event have 0-6 qboxes),
+ * if no qboxes left in buffer, then crusher_encode until get some event
+ */
+static int crushercap_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    struct crusherCap_data *ccap = s->priv_data;
+    int ret = CODEC_FAIL;
+
+    if(ccap->qboxes_used >= ccap->crusher.out_blocks) {
+        do {
+            ret = crusher_encode(&ccap->crusher, NULL, 0);
+        } while (ret != CODEC_FAIL && (ret != CODEC_FLUSHED || !ccap->crusher.out_blocks));
+
+        if (ret != CODEC_FLUSHED)
+            return -1;
+        ccap->qboxes_used=0;
+    }
+
+#ifdef CRUSHERCAP_EXTRA_DEBUG
+    av_log(s, AV_LOG_DEBUG, "parsing qbox %d/%d\n", ccap->qboxes_used, ccap->crusher.out_blocks);
+#endif
+    if(ff_qbox_parse(&ccap->qbox, ccap->crusher.out_data[ccap->qboxes_used].data,
+            ccap->crusher.out_data[ccap->qboxes_used].len)) {
+#ifdef CRUSHERCAP_EXTRA_DEBUG
+        av_log(s, AV_LOG_DEBUG, "QBOX: sst:0x%08x sf:0x%08x pl:%u ts:%u\n",
+                ccap->qbox.sample_stream_type, ccap->qbox.sample_flags, ccap->qbox.qbox_size, ccap->qbox.sample_cts);
+#endif
+        /* if no more configs, return */
+        if((ccap->qbox.sample_flags & SAMPLE_FLAGS_CONFIGURATION_INFO) || !(ccap->qbox.sample_flags & SAMPLE_FLAGS_CTS_PRESENT)) {
+            av_log(s, AV_LOG_ERROR, "stream config in the middle?\n");
+            return -1;
+        }
+
+        switch(ccap->qbox.sample_stream_type) {
+            case SAMPLE_TYPE_H264:
+                ff_qbox_make_startcode(ccap->qbox.data, ccap->qbox.qbox_size);
+                /* if no global header: add SPS/PPS to IDR */
+                if (!(s->flags & CODEC_FLAG_GLOBAL_HEADER) && (ccap->qbox.sample_flags & SAMPLE_FLAGS_SYNC_POINT)) {
+                    memmove(ccap->qbox.data + s->streams[0]->codec->extradata_size,
+                            ccap->qbox.data, ccap->qbox.qbox_size);
+                    memcpy(ccap->qbox.data, s->streams[0]->codec->extradata, s->streams[0]->codec->extradata_size);
+                    ccap->qbox.qbox_size += s->streams[0]->codec->extradata_size;
+                }
+                av_init_packet(pkt);
+                pkt->stream_index = 0;
+                break;
+            case SAMPLE_TYPE_AAC:
+            case SAMPLE_TYPE_PCM:
+            case SAMPLE_TYPE_QMA:
+                av_init_packet(pkt);
+                pkt->stream_index = 1;
+                break;
+            default:
+                av_log(s, AV_LOG_ERROR, "unknown qbox : 0x%08x", ccap->qbox.sample_stream_type);
+                return -1;
+                break;
+        }
+
+        pkt->data         = ccap->qbox.data;
+        pkt->size         = ccap->qbox.qbox_size;
+        pkt->flags       |= ccap->qbox.sample_flags & SAMPLE_FLAGS_SYNC_POINT ? PKT_FLAG_KEY : 0;
+
+        if(ccap->qbox.sample_flags & SAMPLE_FLAGS_CTS_PRESENT) {
+            pkt->pts = ccap->qbox.sample_cts;
+#ifdef CRUSHERCAP_EXTRA_PTS
+            if(ccap->qbox.sample_cts < ccap->last_sample_cts)
+                ccap->extra_pts += 0x100000000;
+            ccap->last_sample_cts = ccap->qbox.sample_cts;
+            pkt->pts += ccap->extra_pts;
+#endif
+        } else {
+            pkt->pts = AV_NOPTS_VALUE;
+        }
+
+        ccap->qboxes_used++;
+        return pkt->size;
+    } else {
+        av_log(s, AV_LOG_ERROR, "bad qbox");
+        return -1;
+    }
+    ccap->qboxes_used++;
+    return 0;
+}
+
+
+static int crushercap_close(AVFormatContext * context)
+{
+    struct crusherCap_data *ccap = context->priv_data;
+    crusher_close(&ccap->crusher);
+    return 0;
+}
+
+AVInputFormat crushercap_demuxer = {
+    .name           = "crushercap",
+    .long_name      = NULL_IF_CONFIG_SMALL("MG1264 CrusherCapture A/V grab"),
+    .priv_data_size = sizeof(struct crusherCap_data),
+    .read_header    = crushercap_read_header,
+    .read_packet    = crushercap_read_packet,
+    .read_close     = crushercap_close,
+    .flags          = AVFMT_NOFILE
+};
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 43a4a2e..59c56a8 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -197,6 +197,7 @@ OBJS-$(CONFIG_PCM_U32LE_MUXER)           += raw.o
 OBJS-$(CONFIG_PCM_U8_DEMUXER)            += raw.o
 OBJS-$(CONFIG_PCM_U8_MUXER)              += raw.o
 OBJS-$(CONFIG_PVA_DEMUXER)               += pva.o
+OBJS-$(CONFIG_QBOX_DEMUXER)              += qbox.o
 OBJS-$(CONFIG_QCP_DEMUXER)               += qcp.o
 OBJS-$(CONFIG_R3D_DEMUXER)               += r3d.o
 OBJS-$(CONFIG_RAWVIDEO_DEMUXER)          += raw.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 27dba10..fa1ea7e 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -164,6 +164,7 @@ void av_register_all(void)
     REGISTER_MUXDEMUX (PCM_U8,    pcm_u8);
     REGISTER_MUXER    (PSP, psp);
     REGISTER_DEMUXER  (PVA, pva);
+    REGISTER_DEMUXER  (QBOX, qbox);
     REGISTER_DEMUXER  (QCP, qcp);
     REGISTER_DEMUXER  (R3D, r3d);
     REGISTER_MUXDEMUX (RAWVIDEO, rawvideo);
diff --git a/libavformat/qbox.c b/libavformat/qbox.c
new file mode 100644
index 0000000..b4f8033
--- /dev/null
+++ b/libavformat/qbox.c
@@ -0,0 +1,278 @@
+/*
+ * Mobilygen/Maxim QBOX demuxer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; 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"
+#include "qbox.h"
+#include "libavcodec/bytestream.h"
+#include "libavutil/bswap.h"
+#include "libavcodec/bytestream.h"
+#include "libavcodec/mpeg4audio.h"
+
+#undef QBOX_EXTRA_DEBUG
+
+typedef struct qboxDemux {
+    qboxContext qbox;
+    int fr_num;
+    int fr_den;
+    int from_header;
+    int got_audio;
+    int got_video;
+} qboxDemux;
+
+/*
+ * The following functions constitute our interface to the world
+ */
+
+/* parse first 6x4 bytes of header
+ * TODO: parse headers with extensions (may be longer)
+ *
+ */
+int ff_qbox_parse(qboxContext *qbox, uint8_t *input_data, int data_size)
+{
+    const uint8_t *src = input_data;
+
+    if(data_size < QBOX_HDR_SIZE)
+        return 0;
+
+
+    qbox->qbox_size = bytestream_get_be32(&src);
+    if(qbox->qbox_size < QBOX_HDR_SIZE)
+        return 0;
+
+    qbox->qbox_size-=QBOX_HDR_SIZE;
+    bytestream_get_be32(&src); //qbox
+    qbox->version = bytestream_get_byte(&src);
+    qbox->boxflags = bytestream_get_be24(&src);
+    qbox->sample_stream_type = bytestream_get_be16(&src);
+    qbox->sample_stream_id = bytestream_get_be16(&src);
+    qbox->sample_flags = bytestream_get_be32(&src);
+    qbox->sample_cts = bytestream_get_be32(&src);
+    qbox->data = input_data + QBOX_HDR_SIZE;
+    return qbox->qbox_size;
+}
+
+/** create h264 startcodes (by default there's nalu lengths in uint32_be format)
+ */
+int ff_qbox_make_startcode(uint8_t *ptr, int input_data)
+{
+    uint32_t size;
+    uint8_t *ptr_tmp = ptr;
+    while (ptr_tmp - ptr < input_data - 4) {
+        size = be2me_32(*(uint32_t *) ptr_tmp);
+        ptr_tmp[0] = 0x00;
+        ptr_tmp[1] = 0x00;
+        ptr_tmp[2] = 0x00;
+        ptr_tmp[3] = 0x01;
+        ptr_tmp+=4+size;
+    }
+    return 1;
+}
+
+
+/*
+ * demuxer probe:
+ * first 4 bytes of every qbox - is full qbox length
+ * next 4 bytes - chars "qbox", try find them
+ */
+static int qbox_probe(AVProbeData * p) {
+    const uint8_t *d;
+    d = p->buf;
+    if (d[4] == 'q' && d[5] == 'b' && d[6] == 'o'  && d[7] == 'x' ) {
+        return AVPROBE_SCORE_MAX;
+    }
+    return 0;
+}
+
+/*
+ * probably there's no header, just stream with qboxes,
+ * if qbox flagged with CONFIGURATION_INFO, there's chance to get AudioSpecificConfig or SPS/PPS
+ * frames skipped until both audio & video seen
+ * */
+static int qbox_read_header(AVFormatContext *s, AVFormatParameters *ap) {
+    qboxDemux *d = s->priv_data;
+    qboxContext *c = &d->qbox;
+    ByteIOContext *pb = s->pb;
+    MPEG4AudioConfig cfg;
+    AVStream *st;
+    uint8_t qbox_header[QBOX_HDR_SIZE];
+
+
+    do {
+        if(d->got_audio && d->got_video)
+            return 0;
+
+        if(!get_buffer(pb, qbox_header, QBOX_HDR_SIZE))
+            return AVERROR(EIO);
+
+        if(!ff_qbox_parse(c, qbox_header, QBOX_HDR_SIZE))
+            return AVERROR(EIO);
+
+        switch (c->sample_stream_type) {
+            case SAMPLE_TYPE_AAC:
+                if (c->sample_flags & SAMPLE_FLAGS_CONFIGURATION_INFO) {
+                    if (!(st = av_new_stream(s, 1)))
+                        return AVERROR(ENOMEM);
+                    st->codec->codec_type = CODEC_TYPE_AUDIO;
+                    st->codec->codec_id   = CODEC_ID_AAC;
+                    st->codec->extradata_size = c->qbox_size;
+                    st->codec->extradata = av_malloc(st->codec->extradata_size);
+                    get_buffer(pb, st->codec->extradata, st->codec->extradata_size);
+                    ff_mpeg4audio_get_config(&cfg, st->codec->extradata,
+                                             st->codec->extradata_size);
+                    st->codec->channels = cfg.channels;
+                    st->codec->sample_rate = cfg.sample_rate;
+
+                    /* need to set form external */
+                    if(ap->sample_rate == 25) {
+                        av_set_pts_info(st, 32, 3600, 90000);
+                    } else if(ap->sample_rate == 30) {
+                        av_set_pts_info(st, 32, 3001, 90000);
+                    } else {
+                        av_set_pts_info(st, 32, 3600, 90000); // default
+                    }
+                }
+                d->got_audio=1;
+                break;
+            case SAMPLE_TYPE_PCM:
+                if (!(st = av_new_stream(s, 1)))
+                    return AVERROR(ENOMEM);
+                st->codec->codec_type = CODEC_TYPE_AUDIO;
+                st->codec->codec_id   = CODEC_ID_PCM_S16BE;
+                /* this 3 parameters have to be set up by higher level functions */
+                st->codec->channels = 2;
+                st->codec->sample_rate = 48000;
+                if(ap->sample_rate == 25) {
+                    av_set_pts_info(st, 32, 3600, 90000);
+                } else if(ap->sample_rate == 30) {
+                    av_set_pts_info(st, 32, 3001, 90000);
+                } else {
+                    av_set_pts_info(st, 32, 3600, 90000); // default
+                }
+                url_fskip(pb, c->qbox_size); //TODO: use it too
+                d->got_audio=1;
+#ifdef QBOX_EXTRA_DEBUG
+                av_log(s, AV_LOG_DEBUG, "got_audio\n");
+#endif
+                break;
+            case SAMPLE_TYPE_H264:
+                if (c->sample_flags & SAMPLE_FLAGS_CONFIGURATION_INFO) {
+                    if (!(st = av_new_stream(s, 0)))
+                        return AVERROR(ENOMEM);
+                    st->codec->codec_type = CODEC_TYPE_VIDEO;
+                    st->codec->codec_id   = CODEC_ID_H264;
+                    st->codec->extradata_size = c->qbox_size;
+                    st->codec->extradata = av_malloc(st->codec->extradata_size);
+                    get_buffer(pb, st->codec->extradata, st->codec->extradata_size);
+                    ff_qbox_make_startcode(st->codec->extradata, st->codec->extradata_size);
+                    /* TODO: set aspect ratio */
+                    if(ap->sample_rate == 25) {
+                        av_set_pts_info(st, 32, 3600, 90000);
+                    } else if(ap->sample_rate == 30) {
+                        av_set_pts_info(st, 32, 3001, 90000);
+                    } else {
+                        av_set_pts_info(st, 32, 3600, 90000); ///< default
+                    }
+                }
+                if(d->got_video==1) {
+                    /* looks like qboxes without sound
+                     * change pts info */
+                    av_set_pts_info(s->streams[0], 32, 2500, 100);
+                    d->got_audio=1;
+                    d->from_header=1;
+                }
+                /* audio detecting given chance, but got 2 frames of video, seems no audio... */
+
+                d->got_video=1;
+#ifdef QBOX_EXTRA_DEBUG
+                av_log(s, AV_LOG_DEBUG, "got_video\n");
+#endif
+                break;
+            default:
+                av_log(s, AV_LOG_ERROR, "unknown qbox: s:%d t:0x%08x id:%08x f:%08x cts:%d\n",
+            c->qbox_size, c->sample_stream_type, c->sample_stream_id, c->sample_flags, c->sample_cts);
+                url_fskip(pb, c->qbox_size);
+                break;
+        }
+    } while(c->sample_flags & SAMPLE_FLAGS_CONFIGURATION_INFO);
+    return 0;
+}
+
+
+
+/*
+ * extradata set, create packets from qboxes
+ * */
+static int qbox_read_packet(AVFormatContext *s, AVPacket *pkt) {
+    qboxDemux *d = s->priv_data;
+    qboxContext *c = &d->qbox;
+    ByteIOContext *pb = s->pb;
+    uint8_t qbox_header[QBOX_HDR_SIZE];
+    int ret = 0;
+
+    if(!d->from_header) {
+        if(!get_buffer(pb, qbox_header, QBOX_HDR_SIZE))
+            return AVERROR(EIO);
+
+        if(!ff_qbox_parse(c, qbox_header, QBOX_HDR_SIZE))
+            return AVERROR(EIO);
+    } else {
+        d->from_header=0;
+    }
+#ifdef QBOX_EXTRA_DEBUG
+    av_log(s, AV_LOG_DEBUG, "qbox: s:%d t:0x%08x id:%08x f:%08x cts:%d\n",
+            c->qbox_size, c->sample_stream_type, c->sample_stream_id, c->sample_flags, c->sample_cts);
+#endif
+
+    if ((ret = av_get_packet(pb, pkt, c->qbox_size)) <= 0)
+        return AVERROR(EIO);
+
+    pkt->dts = c->sample_cts;
+    if(c->sample_flags & SAMPLE_FLAGS_SYNC_POINT)
+        pkt->flags |= PKT_FLAG_KEY;
+
+
+    switch (c->sample_stream_type) {
+        case SAMPLE_TYPE_QMA:
+        case SAMPLE_TYPE_PCM:
+        case SAMPLE_TYPE_AAC:
+            pkt->stream_index = 1;
+            break;
+        case SAMPLE_TYPE_H264:
+            pkt->stream_index = 0;
+            ff_qbox_make_startcode(pkt->data, pkt->size);
+            break;
+        default:
+            av_log(s, AV_LOG_ERROR, "unknown qbox\n");
+            return 0;
+            break;
+    }
+    return ret;
+}
+
+
+AVInputFormat qbox_demuxer = {
+    "qbox",
+    NULL_IF_CONFIG_SMALL("qbox raw format"),
+    sizeof(qboxDemux),
+    qbox_probe,
+    qbox_read_header,
+    qbox_read_packet,
+};
diff --git a/libavformat/qbox.h b/libavformat/qbox.h
new file mode 100644
index 0000000..45dc190
--- /dev/null
+++ b/libavformat/qbox.h
@@ -0,0 +1,69 @@
+/*
+ * Mobilygen/Maxim QBOX demuxer
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QBOX_H_
+#define QBOX_H_
+
+#define QBOX_HDR_SIZE (6*4)
+
+enum {
+    SAMPLE_TYPE_AAC = 0x1,
+    SAMPLE_TYPE_H264,
+    SAMPLE_TYPE_PCM,
+    SAMPLE_TYPE_DEBUG,
+    SAMPLE_TYPE_H264_SLICE,
+    SAMPLE_TYPE_QMA,
+    SAMPLE_TYPE_VIN_STATS_GLOBAL,
+    SAMPLE_TYPE_VIN_STATS_MB,
+    SAMPLE_TYPE_Q711,
+    SAMPLE_TYPE_Q728,
+    SAMPLE_TYPE_MAX,
+};
+
+
+
+#define SAMPLE_FLAGS_CONFIGURATION_INFO 0x01
+#define SAMPLE_FLAGS_CTS_PRESENT 0x02
+#define SAMPLE_FLAGS_SYNC_POINT 0x04
+#define SAMPLE_FLAGS_DISPOSABLE 0x08
+#define SAMPLE_FLAGS_MUTE 0x10
+#define SAMPLE_FLAGS_BASE_CTS_INCREMENT 0x20
+#define SAMPLE_FLAGS_META_INFO 0x40
+#define SAMPLE_FLAGS_END_OF_SEQUENCE 0x80
+#define SAMPLE_FLAGS_END_OF_STREAM 0x100
+#define SAMPLE_FLAGS_PADDING_MASK 0xFF000000
+
+typedef struct qboxContext {
+    uint32_t qbox_size; ///< will be decreased
+    uint32_t boxflags;
+    uint8_t  version;
+    uint16_t sample_stream_type;
+    uint16_t sample_stream_id;
+    uint32_t sample_flags;
+    uint32_t sample_cts;
+    uint8_t  *data;
+} qboxContext;
+
+int ff_qbox_parse(qboxContext *qbox, uint8_t *input_data, int data_size);
+int ff_qbox_make_startcode(uint8_t *ptr, int input_data);
+
+
+
+#endif /* QBOX_H_ */
-- 
1.5.4.3


