Skip to content

Instantly share code, notes, and snippets.

@dhpollack
Created October 21, 2010 21:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save dhpollack/639426 to your computer and use it in GitHub Desktop.
Save dhpollack/639426 to your computer and use it in GitHub Desktop.
Updated crusher264 patch for FFmpeg 0.6.1 (tested on Dockstar)
diff --git a/configure b/configure
index d725942..6e95687 100755
--- a/configure
+++ b/configure
@@ -166,6 +166,7 @@ External library support:
--enable-libopencore-amrnb enable AMR-NB de/encoding via libopencore-amrnb [no]
--enable-libopencore-amrwb enable AMR-WB decoding via libopencore-amrwb [no]
--enable-libopencv enable video filtering via libopencv [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]
@@ -882,6 +883,7 @@ CONFIG_LIST="
h264pred
hardcoded_tables
huffman
+ libcrusher264
libdc1394
libdirac
libfaac
@@ -1320,6 +1322,7 @@ vdpau_deps="vdpau_vdpau_h vdpau_vdpau_x11_h"
h264_parser_select="golomb h264dsp h264pred"
# external libraries
+libcrusher264_encoder_deps="libcrusher264 qbox_demuxer"
libdirac_decoder_deps="libdirac !libschroedinger"
libdirac_encoder_deps="libdirac"
libfaac_encoder_deps="libfaac"
@@ -1376,6 +1379,7 @@ w64_demuxer_deps="wav_demuxer"
alsa_indev_deps="alsa_asoundlib_h snd_pcm_htimestamp"
alsa_outdev_deps="alsa_asoundlib_h"
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"
libdc1394_indev_deps="libdc1394"
@@ -2382,6 +2386,7 @@ case $target_os in
enable dos_paths
;;
linux)
+ enable crushercap
enable dv1394
;;
irix*)
@@ -2445,6 +2450,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 libx264
die_license_disabled gpl libxavs
die_license_disabled gpl libxvid
@@ -2741,6 +2747,8 @@ check_mathfunc roundf
check_mathfunc truncf
# these are off by default, so fail if requested and not available
+enabled libcrusher264 && add_cflags $(pkg-config --cflags crusher264) &&
+ require libcrusher264 crusher264/crusher.h crusher_encode $(pkg-config --libs crusher264)
enabled avisynth && require2 vfw32 "windows.h vfw.h" AVIFileInit -lavifil32
enabled frei0r && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; }
enabled libdirac && add_cflags $(pkg-config --cflags dirac) &&
@@ -3036,6 +3044,7 @@ echo "SDL support ${sdl-no}"
echo "Sun medialib support ${mlib-no}"
echo "AVISynth enabled ${avisynth-no}"
echo "frei0r enabled ${frei0r-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 f59258c..6a080b3 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -156,6 +156,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 385ae02..10b89e5 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -530,6 +530,7 @@ OBJS-$(CONFIG_WEBM_MUXER) += xiph.o mpeg4audio.o \
flacdec.o flacdata.o flac.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 89614ab..6626c06 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -347,6 +347,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_ENCDEC (LIBGSM, libgsm);
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 1c0630b..23f886f 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 de3bc82..9f6c0c1 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -41,6 +41,7 @@ void avdevice_register_all(void)
/* devices */
REGISTER_INOUTDEV (ALSA, alsa);
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 2ba8b09..1f8a9f9 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -207,6 +207,7 @@ OBJS-$(CONFIG_PCM_U32LE_MUXER) += pcmenc.o rawenc.o
OBJS-$(CONFIG_PCM_U8_DEMUXER) += pcmdec.o pcm.o rawdec.o
OBJS-$(CONFIG_PCM_U8_MUXER) += pcmenc.o rawenc.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) += rawvideodec.o rawdec.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 9f33e31..f654c88 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -171,6 +171,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 = av_be2ne32(*(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_ */
Index: crusher264-config.in
===================================================================
--- crusher264-config.in (revision 69)
+++ crusher264-config.in (working copy)
@@ -44,6 +44,7 @@
--bindir) echo_bindir="yes" ;;
--sbindir) echo_sbindir="yes" ;;
--libexecdir) echo_libexecdir="yes" ;;
+ --datarootdir) echo_datarootdir="yes" ;;
--datadir) echo_datadir="yes" ;;
--sysconfdir) echo_sysconfdir="yes" ;;
--sharedstatedir) echo_sharedstatedir="yes" ;;
@@ -80,6 +81,9 @@
if test -z "$libexecdir" ; then
libexecdir="@libexecdir@"
fi
+if test -z "$datarootdir" ; then
+ datarootdir="@datarootdir@"
+fi
if test -z "$datadir" ; then
datadir="@datadir@"
fi
@@ -160,6 +164,9 @@
if test x$echo_libexecdir = xyes ; then
o=" $libexecdir"
fi
+if test x$echo_datarootdir = xyes ; then
+ o=" $datarootdir"
+fi
if test x$echo_datadir = xyes ; then
o=" $datadir"
fi
@@ -190,6 +197,9 @@
if test x$echo_build = xyes ; then
o=" $build"
fi
+if test x$echo_pkgdatarootdir = xyes ; then
+ o=" ${datarootdir}/${package}"
+fi
if test x$echo_pkgdatadir = xyes ; then
o=" ${datadir}/${package}"
fi
@@ -236,6 +246,7 @@
--bindir \$bindir $bindir
--sbindir \$sbindir $sbindir
--libexecdir \$libexecdir $libexecdir
+ --datarootdir \$datarootdir $datarootdir
--datadir \$datadir $datadir
--sysconfdir \$sysconfdir $sysconfdir
--sharedstatedir \$sharedstatedir$sharedstatedir
@@ -246,10 +257,11 @@
--target \$target $target
--host \$host $host
--build \$build $build
- --pkgdatadir \$datadir/\$package ${datadir}/${package}
- --pkglibdir \$libdir/\$package ${libdir}/${package}
- --pkgincludedir \$includedir/\$package ${includedir}/${package}
- --template-version \$template_version $template_version
+ --pkgdatarootdir \$datarootdir/\$package ${datarootdir}/${package}
+ --pkgdatadir \$datadir/\$package ${datadir}/${package}
+ --pkglibdir \$libdir/\$package ${libdir}/${package}
+ --pkgincludedir \$includedir/\$package ${includedir}/${package}
+ --template-version \$template_version $template_version
--help
EOF
fi
Index: configure.ac
===================================================================
--- configure.ac (revision 69)
+++ configure.ac (working copy)
@@ -242,8 +242,10 @@
AC_DEFINE(HAVE_BSWAP)
;;
armv5*e*|arm[79]*e*|arm9[24]6*|arm96*|arm102[26])
- CRUSHER264_CFLAGS+="-mv=5e"
- AC_DEFINE(ARCH_ARM)
+ #CRUSHER264_CFLAGS+="-mv=5e"
+ #For some reason this breaks ffmpeg 0.6.1 on the Dockstar
+ CRUSHER264_CFLAGS+=""
+ AC_DEFINE(ARCH_ARM)
;;
armv4*|arm7*|arm9[24]*)
CRUSHER264_CFLAGS+="-mv=4"
Index: src/crusher.c
===================================================================
--- src/crusher.c (revision 69)
+++ src/crusher.c (working copy)
@@ -270,7 +270,7 @@
switch(crusher->devtype) {
case DEV_TYPE_ENCODER:
- crusher->mg1264fw = MG1264_Firmware;
+ crusher->mg1264fw = MG1264Firmware;
crusher->framerate_num = 2500;
crusher->framerate_den = 100;
break;
@@ -297,7 +297,7 @@
switch(devices[dev_type_id].devType) {
case DEV_TYPE_ENCODER:
- crusher->fx2fw = FX2_Firmware;
+ crusher->fx2fw = FX2Firmware;
break;
case DEV_TYPE_CAPTURE:
crusher->fx2fw = FX2_CaptureFirmware;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment