From c1df694f45c25d621c4ae6f90fd86fd5db1e015a Mon Sep 17 00:00:00 2001 From: Anton Mitrofanov Date: Wed, 7 Apr 2010 13:01:16 +0300 Subject: [PATCH] Support AVI-output --- Makefile | 4 + configure | 35 +++++++++ output/avi.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ output/output.h | 1 + x264.c | 26 +++++++ 5 files changed, 283 insertions(+), 0 deletions(-) create mode 100644 output/avi.c diff --git a/Makefile b/Makefile index 24cf324..9deeb09 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,10 @@ ifneq ($(findstring MP4_OUTPUT, $(CONFIG)),) SRCCLI += output/mp4.c endif +ifneq ($(findstring AVI_OUTPUT, $(CONFIG)),) +SRCCLI += output/avi.c +endif + # Visualization sources ifeq ($(VIS),yes) SRCS += common/visualize.c common/display-x11.c diff --git a/configure b/configure index 0a2eaa7..c398fe5 100755 --- a/configure +++ b/configure @@ -11,6 +11,7 @@ echo " --disable-avs-input disables avisynth input (win32 only)" echo " --disable-lavf-input disables libavformat input" echo " --disable-ffms-input disables ffmpegsource input" echo " --disable-mp4-output disables mp4 output (using gpac)" +echo " --disable-avi-output disables avi output (using libavformat)" echo " --disable-pthread disables multithreaded encoding" echo " --disable-asm disables platform-specific assembly optimizations" echo " --enable-debug adds -g, doesn't strip" @@ -117,6 +118,7 @@ avs_input="auto" lavf_input="auto" ffms_input="auto" mp4_output="auto" +avi_output="auto" pthread="auto" asm="yes" debug="no" @@ -184,6 +186,12 @@ for opt do --disable-mp4-output) mp4_output="no" ;; + --enable-avi-output) + avi_output="auto" + ;; + --disable-avi-output) + avi_output="no" + ;; --extra-asflags=*) ASFLAGS="$ASFLAGS ${opt#--extra-asflags=}" ;; @@ -606,6 +614,32 @@ if [ "$mp4_output" = "yes" ] ; then LDFLAGSCLI="$LDFLAGSCLI $MP4_LDFLAGS" fi +if [ "$avi_output" = "auto" ] ; then + avi_output="no" + if ${cross_prefix}pkg-config --exists libavformat 2>$DEVNULL; then + AVI_LIBS="$AVI_LIBS $(${cross_prefix}pkg-config --libs libavformat)" + AVI_CFLAGS="$AVI_CFLAGS $(${cross_prefix}pkg-config --cflags libavformat)" + fi + if [ -z "$AVI_LIBS" -a -z "$AVI_CFLAGS" ]; then + AVI_LIBS="-lavformat" + for lib in -lavcodec -lavutil -lm -lz -lbz2 $libpthread -lavifil32; do + cc_check "" $lib && AVI_LIBS="$AVI_LIBS $lib" + done + fi + AVI_LIBS="-L. $AVI_LIBS" + if cc_check libavformat/avformat.h "$AVI_CFLAGS $AVI_LIBS" ; then + if cc_check libavformat/avformat.h "$AVI_CFLAGS $AVI_LIBS" 'av_register_all(); av_guess_format( "avi", NULL, NULL );' ; then + avi_output="yes" + define AVI_OUTPUT + fi + fi +fi + +if [ "$avi_output" = "yes" ]; then + LDFLAGSCLI="$AVI_LIBS $LDFLAGSCLI" + [ -n "$AVI_CFLAGS" ] && CFLAGS="$CFLAGS $AVI_CFLAGS" +fi + if [ "$avs_input" = "auto" ] ; then avs_input=no if [ $SYS = MINGW ] && cc_check avisynth_c.h ; then @@ -722,6 +756,7 @@ avs input: $avs_input lavf input: $lavf_input ffms input: $ffms_input mp4 output: $mp4_output +avi output: $avi_output pthread: $pthread debug: $debug gprof: $gprof diff --git a/output/avi.c b/output/avi.c new file mode 100644 index 0000000..f88291f --- /dev/null +++ b/output/avi.c @@ -0,0 +1,217 @@ +/***************************************************************************** + * avi.c: x264 avi output module + ***************************************************************************** + * Copyright (C) 2010 x264 project + * + * Authors: Anton Mitrofanov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" +#undef DECLARE_ALIGNED +#include + +typedef struct +{ + AVFormatContext *mux_fc; + AVStream *video_stm; + uint8_t *data; + unsigned d_max; + unsigned d_cur; +} avi_hnd_t; + +static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts ) +{ + avi_hnd_t *h = handle; + + if( !h ) + return 0; + + if( h->data ) + { + free( h->data ); + h->data = NULL; + } + + if( h->mux_fc && h->video_stm ) + { + av_write_trailer( h->mux_fc ); + av_freep( &h->video_stm->codec ); + av_freep( &h->video_stm ); + } + + if( h->mux_fc && h->mux_fc->pb ) + { + url_fclose( h->mux_fc->pb ); + h->mux_fc->pb = NULL; + } + + if( h->mux_fc ) + av_freep( &h->mux_fc ); + + free( h ); + + return 0; +} + +static int open_file( char *psz_filename, hnd_t *p_handle ) +{ + avi_hnd_t *h; + AVOutputFormat *mux_fmt; + + *p_handle = NULL; + + FILE *fh = fopen( psz_filename, "w" ); + if( !fh ) + return -1; + else if( !x264_is_regular_file( fh ) ) + { + fprintf( stderr, "avi [error]: AVI output is incompatible with non-regular file `%s'\n", psz_filename ); + return -1; + } + fclose( fh ); + + if( !(h = malloc( sizeof(avi_hnd_t) )) ) + return -1; + memset( h, 0, sizeof(avi_hnd_t) ); + + av_register_all(); + mux_fmt = av_guess_format( "avi", NULL, NULL ); + if( !mux_fmt ) + { + close_file( h, 0, 0 ); + return -1; + } + + h->mux_fc = avformat_alloc_context(); + if( !h->mux_fc ) + { + close_file( h, 0, 0 ); + return -1; + } + h->mux_fc->oformat = mux_fmt; + snprintf( h->mux_fc->filename, sizeof(h->mux_fc->filename), "%s", psz_filename ); + + if( url_fopen( &h->mux_fc->pb, psz_filename, URL_WRONLY ) < 0 ) + { + close_file( h, 0, 0 ); + return -1; + } + + *p_handle = h; + + return 0; +} + +static int set_param( hnd_t handle, x264_param_t *p_param ) +{ + avi_hnd_t *h = handle; + AVCodecContext *c; + + if( !h->mux_fc || h->video_stm ) + return -1; + + h->video_stm = av_new_stream( h->mux_fc, 0 ); + if( !h->video_stm ) + return -1; + + c = h->video_stm->codec; + c->time_base.num = p_param->i_fps_den; + c->time_base.den = p_param->i_fps_num; + c->width = p_param->i_width; + c->height = p_param->i_height; + c->pix_fmt = PIX_FMT_YUV420P; + c->codec_type = CODEC_TYPE_VIDEO; + c->codec_id = CODEC_ID_H264; + c->codec_tag = MKTAG('H','2','6','4'); + + if( av_set_parameters( h->mux_fc, NULL ) < 0 ) + return -1; + if( av_write_header( h->mux_fc ) ) + return -1; + + return 0; +} + +static int write_buffer( avi_hnd_t *h, uint8_t *p_nalu, int i_size ) +{ + unsigned ns = h->d_cur + i_size; + + if( !h->data || ns > h->d_max ) + { + void *dp; + unsigned dn = 16; + + while( ns > dn ) + dn <<= 1; + + dp = realloc( h->data, dn ); + if( !dp ) + return -1; + + h->data = dp; + h->d_max = dn; + } + + memcpy( h->data + h->d_cur, p_nalu, i_size ); + h->d_cur = ns; + + return i_size; +} + +static int write_headers( hnd_t handle, x264_nal_t *p_nal ) +{ + avi_hnd_t *h = handle; + int i_size = p_nal[0].i_payload + p_nal[1].i_payload + p_nal[2].i_payload; + + if( write_buffer( h, p_nal[0].p_payload, i_size ) < 0 ) + return -1; + + return i_size; +} + +static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture ) +{ + avi_hnd_t *h = handle; + AVPacket pkt; + + if( !h->mux_fc || !h->video_stm ) + return -1; + + av_init_packet(&pkt); + pkt.stream_index = h->video_stm->index; + pkt.flags |= p_picture->b_keyframe ? PKT_FLAG_KEY : 0; + if( h->d_cur ) + { + if( write_buffer( h, p_nalu, i_size ) < 0 ) + return -1; + pkt.data = h->data; + pkt.size = h->d_cur; + } + else + { + pkt.data = p_nalu; + pkt.size = i_size; + } + if( av_interleaved_write_frame( h->mux_fc, &pkt ) ) + return -1; + + h->d_cur = 0; + + return i_size; +} + +const cli_output_t avi_output = { open_file, set_param, write_headers, write_frame, close_file }; diff --git a/output/output.h b/output/output.h index c79b48e..8346fec 100644 --- a/output/output.h +++ b/output/output.h @@ -37,5 +37,6 @@ extern const cli_output_t raw_output; extern const cli_output_t mkv_output; extern const cli_output_t mp4_output; extern const cli_output_t flv_output; +extern const cli_output_t avi_output; #endif diff --git a/x264.c b/x264.c index bb3707c..3c12f3c 100644 --- a/x264.c +++ b/x264.c @@ -92,6 +92,9 @@ static const char * const muxer_names[] = #ifdef MP4_OUTPUT "mp4", #endif +#ifdef AVI_OUTPUT + "avi", +#endif 0 }; @@ -217,6 +220,7 @@ static void Help( x264_param_t *defaults, int longhelp ) " .mkv -> Matroska\n" " .flv -> Flash Video\n" " .mp4 -> MP4 if compiled with GPAC support (%s)\n" + " .avi -> AVI if compiled with support (%s)\n" "\n" "Options:\n" "\n" @@ -241,6 +245,11 @@ static void Help( x264_param_t *defaults, int longhelp ) "no", #endif #ifdef MP4_OUTPUT + "yes", +#else + "no", +#endif +#ifdef AVI_OUTPUT "yes" #else "no" @@ -789,6 +798,23 @@ static int select_output( const char *muxer, char *filename, x264_param_t *param param->b_dts_compress = 1; param->b_repeat_headers = 0; } + else if( !strcasecmp( ext, "avi" ) ) + { +#ifdef AVI_OUTPUT + output = avi_output; + param->b_annexb = 1; + param->b_dts_compress = 0; + param->b_repeat_headers = 1; + if( param->b_vfr_input ) + { + fprintf( stderr, "x264 [warning]: VFR is not compatible with AVI output\n" ); + param->b_vfr_input = 0; + } +#else + fprintf( stderr, "x264 [error]: not compiled with AVI output support\n" ); + return -1; +#endif + } else output = raw_output; return 0; -- 1.7.0.2.msysgit.0