diff -uNrp src/Makefile src.new/Makefile --- src/Makefile 2017-05-25 23:24:39 +0300 +++ src.new/Makefile 2017-05-25 23:26:35 +0300 @@ -75,6 +75,10 @@ ifneq ($(findstring HAVE_LSMASH 1, $(CON SRCCLI += output/mp4_lsmash.c endif +ifneq ($(findstring HAVE_AVI_OUTPUT 1, $(CONFIG)),) +SRCCLI += output/avi.c +endif + # MMX/SSE optims ifneq ($(AS),) X86SRC0 = const-a.asm cabac-a.asm dct-a.asm deblock-a.asm mc-a.asm \ diff -uNrp src/configure src.new/configure --- src/configure 2017-05-25 23:24:39 +0300 +++ src.new/configure 2017-05-25 23:28:26 +0300 @@ -53,6 +53,7 @@ External library support: --disable-ffms disable ffmpegsource support --disable-gpac disable gpac support --disable-lsmash disable lsmash support + --disable-avi-output disables avi output (using libavformat) EOF exit 1 @@ -343,6 +344,7 @@ ffms="auto" gpac="auto" lsmash="auto" mp4="no" +avi_output="auto" gpl="yes" thread="auto" swscale="auto" @@ -377,7 +379,7 @@ NL=" # list of all preprocessor HAVE values we can define CONFIG_HAVE="MALLOC_H ALTIVEC ALTIVEC_H MMX ARMV6 ARMV6T2 NEON BEOSTHREAD POSIXTHREAD WIN32THREAD THREAD LOG2F SWSCALE \ LAVF FFMS GPAC AVS GPL VECTOREXT INTERLACED CPU_COUNT OPENCL THP LSMASH X86_INLINE_ASM AS_FUNC INTEL_DISPATCHER \ - MSA MMAP WINRT VSX ARM_INLINE_ASM" + MSA MMAP WINRT VSX ARM_INLINE_ASM AVI_OUTPUT" # parse options @@ -432,6 +434,9 @@ for opt do --disable-lsmash) lsmash="no" ;; + --disable-avi-output) + avi_output="no" + ;; --disable-gpl) gpl="no" ;; @@ -1055,6 +1060,7 @@ if [ "$cli" = "no" ] ; then lsmash="no" mp4="no" swscale="no" + avi_output="no" fi if [ "$swscale" = "auto" ] ; then @@ -1185,6 +1191,32 @@ elif [ "$gpac" = "yes" ] ; then LDFLAGSCLI="$GPAC_LIBS $LDFLAGSCLI" 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" + fi + fi +fi + +if [ "$avi_output" = "yes" ]; then + LDFLAGSCLI="$AVI_LIBS $LDFLAGSCLI" + [ -n "$AVI_CFLAGS" ] && CFLAGS="$CFLAGS $AVI_CFLAGS" + define HAVE_AVI_OUTPUT +fi + if [ "$avs" = "auto" ] ; then avs="no" # cygwin can use avisynth if it can use LoadLibrary @@ -1512,6 +1544,7 @@ avs: $avs lavf: $lavf ffms: $ffms mp4: $mp4 +avi output: $avi_output gpl: $gpl thread: $thread opencl: $opencl diff -uNrp src/output/avi.c src.new/output/avi.c --- src/output/avi.c 1970-01-01 03:00:00 +0300 +++ src.new/output/avi.c 2017-05-25 23:29:03 +0300 @@ -0,0 +1,256 @@ +/***************************************************************************** + * avi.c: avi muxer (using libavformat) + ***************************************************************************** + * Copyright (C) 2010-2014 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 "output.h" +#undef DECLARE_ALIGNED +#include +#include + +typedef struct +{ + cli_output_opt_t opt; + + 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->extradata ); + av_freep( &h->video_stm->codec ); + av_freep( &h->video_stm ); + } + + if( h->mux_fc && h->mux_fc->pb ) + { + avio_close( 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, cli_output_opt_t *opt ) +{ + *p_handle = NULL; + + int b_regular = strcmp( psz_filename, "-" ); + b_regular = b_regular && x264_is_regular_file_path( psz_filename ); + if( b_regular ) + { + FILE *fh = x264_fopen( psz_filename, "wb" ); + if( !fh ) + { + x264_cli_log( "avi", X264_LOG_ERROR, "cannot open output file `%s'.\n", psz_filename ); + return -1; + } + b_regular = x264_is_regular_file( fh ); + fclose( fh ); + } + + if( !b_regular ) + { + x264_cli_log( "avi", X264_LOG_ERROR, "AVI output is incompatible with non-regular file `%s'\n", psz_filename ); + return -1; + } + + avi_hnd_t *h = calloc( 1, sizeof(avi_hnd_t) ); + if( !h ) + return -1; + + memcpy( &h->opt, opt, sizeof(cli_output_opt_t) ); + + av_register_all(); + AVOutputFormat *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; + memset( h->mux_fc->filename, 0, sizeof(h->mux_fc->filename) ); + snprintf( h->mux_fc->filename, sizeof(h->mux_fc->filename) - 1, "%s", psz_filename ); + + if( avio_open( &h->mux_fc->pb, psz_filename, AVIO_FLAG_WRITE ) < 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 = avformat_new_stream( h->mux_fc, NULL ); + if( !h->video_stm ) + return -1; + + c = h->video_stm->codec; + c->flags |= p_param->b_repeat_headers ? 0 : CODEC_FLAG_GLOBAL_HEADER; + c->time_base.num = p_param->i_timebase_num; + c->time_base.den = p_param->i_timebase_den; + c->width = p_param->i_width; + c->height = p_param->i_height; + c->pix_fmt = AV_PIX_FMT_NONE; //not used + c->codec_type = AVMEDIA_TYPE_VIDEO; + c->codec_id = AV_CODEC_ID_H264; + c->codec_tag = MKTAG('H','2','6','4'); + + h->video_stm->time_base.num = p_param->i_timebase_num; + h->video_stm->time_base.den = p_param->i_timebase_den; + + if( !(c->flags & CODEC_FLAG_GLOBAL_HEADER) && avformat_write_header( h->mux_fc, NULL ) ) + 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; + AVCodecContext *c; + int i_size = p_nal[0].i_payload + p_nal[1].i_payload + p_nal[2].i_payload; + + if( !h->mux_fc || !h->video_stm ) + return -1; + + c = h->video_stm->codec; + if( c->flags & CODEC_FLAG_GLOBAL_HEADER ) + { + c->extradata_size = i_size - p_nal[2].i_payload; + av_freep( &c->extradata ); + c->extradata = av_malloc( c->extradata_size ); + if( !c->extradata ) + return -1; + /* Write the SPS/PPS to the extradata */ + memcpy( c->extradata, p_nal[0].p_payload, c->extradata_size ); + /* Write the SEI as part of the first frame */ + if( write_buffer( h, p_nal[2].p_payload, p_nal[2].i_payload ) < 0 ) + return -1; + if( avformat_write_header( h->mux_fc, NULL ) ) + return -1; + } + else + 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 ? AV_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; + } + pkt.pts = av_rescale_q( p_picture->i_pts, h->video_stm->codec->time_base, h->video_stm->time_base ); + pkt.dts = av_rescale_q( p_picture->i_dts, h->video_stm->codec->time_base, h->video_stm->time_base ); + 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 -uNrp src/output/output.h src.new/output/output.h --- src/output/output.h 2017-05-25 23:24:40 +0300 +++ src.new/output/output.h 2017-05-25 23:29:17 +0300 @@ -47,5 +47,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 -uNrp src/x264.c src.new/x264.c --- src/x264.c 2017-05-25 23:24:40 +0300 +++ src.new/x264.c 2017-05-25 23:30:29 +0300 @@ -190,6 +190,9 @@ static const char * const muxer_names[] #if HAVE_GPAC || HAVE_LSMASH "mp4", #endif +#if HAVE_AVI_OUTPUT + "avi", +#endif 0 }; @@ -483,6 +486,7 @@ static void help( x264_param_t *defaults " .mkv -> Matroska\n" " .flv -> Flash Video\n" " .mp4 -> MP4 if compiled with GPAC or L-SMASH support (%s)\n" + " .avi -> AVI if compiled with support (%s)\n" "Output bit depth: %d (configured at compile time)\n" "\n" "Options:\n" @@ -514,6 +518,11 @@ static void help( x264_param_t *defaults #else "no", #endif +#if HAVE_AVI_OUTPUT + "yes", +#else + "no", +#endif x264_bit_depth ); H0( "Example usage:\n" ); @@ -1184,6 +1193,23 @@ static int select_output( const char *mu param->b_annexb = 0; param->b_repeat_headers = 0; } + else if( !strcasecmp( ext, "avi" ) ) + { +#if HAVE_AVI_OUTPUT + cli_output = avi_output; + param->b_annexb = 1; + /* param->b_dts_compress = 0; */ + param->b_repeat_headers = 1; + if( param->b_vfr_input ) + { + x264_cli_log( "x264", X264_LOG_WARNING, "VFR is not compatible with AVI output\n" ); + param->b_vfr_input = 0; + } +#else + x264_cli_log( "x264", X264_LOG_ERROR, "not compiled with AVI output support\n" ); + return -1; +#endif + } else cli_output = raw_output; return 0;