Skip to content

Commit

Permalink
AVI output
Browse files Browse the repository at this point in the history
  • Loading branch information
msg7086 committed Feb 22, 2015
1 parent 11a53da commit c36b9ea
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Makefile
Expand Up @@ -75,6 +75,10 @@ ifneq ($(findstring HAVE_LSMASH 1, $(CONFIG)),)
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 \
Expand Down
34 changes: 33 additions & 1 deletion configure
Expand Up @@ -50,6 +50,7 @@ External library support:
--disable-swscale disable swscale support
--disable-lavf disable libavformat support
--disable-ffms disable ffmpegsource support
--disable-avi-output disables avi output (using libavformat)
--disable-gpac disable gpac support
--disable-lsmash disable lsmash support
Expand Down Expand Up @@ -277,6 +278,7 @@ static="no"
avs="auto"
lavf="auto"
ffms="auto"
avi_output="auto"
gpac="auto"
lsmash="auto"
mp4="no"
Expand Down Expand Up @@ -311,7 +313,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"
LAVF FFMS GPAC AVS GPL VECTOREXT INTERLACED CPU_COUNT OPENCL THP LSMASH AVI_OUTPUT X86_INLINE_ASM AS_FUNC INTEL_DISPATCHER"

# parse options

Expand Down Expand Up @@ -360,6 +362,9 @@ for opt do
--disable-ffms)
ffms="no"
;;
--disable-avi-output)
avi_output="no"
;;
--disable-gpac)
gpac="no"
;;
Expand Down Expand Up @@ -1024,6 +1029,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
Expand Down Expand Up @@ -1338,6 +1369,7 @@ interlaced: $interlaced
avs: $avs
lavf: $lavf
ffms: $ffms
avi output: $avi_output
mp4: $mp4
gpl: $gpl
thread: $thread
Expand Down
237 changes: 237 additions & 0 deletions output/avi.c
@@ -0,0 +1,237 @@
/*****************************************************************************
* avi.c: x264 avi output module (using libavformat)
*****************************************************************************
* Copyright (C) 2003-2014 x264 project
*
* Authors: Anton Mitrofanov <BugMaster@narod.ru>
*
* 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 <libavformat/avformat.h>

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->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 )
{
avi_hnd_t *h;
AVOutputFormat *mux_fmt;

*p_handle = NULL;

FILE *fh = x264_fopen( psz_filename, "w" );
if( !fh )
return -1;
int b_regular = x264_is_regular_file( fh );
fclose( fh );
FAIL_IF_ERR( !b_regular, "avi", "AVI output is incompatible with non-regular file `%s'\n", psz_filename )

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( 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, 0 );
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 = PIX_FMT_YUV420P;
c->codec_type = AVMEDIA_TYPE_VIDEO;
c->codec_id = CODEC_ID_H264;
c->codec_tag = MKTAG('H','2','6','4');

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_NOPTS_VALUE; //av_rescale_q( p_picture->i_pts, h->video_stm->codec->time_base, h->video_stm->time_base );
pkt.dts = AV_NOPTS_VALUE; //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 };
1 change: 1 addition & 0 deletions output/output.h
Expand Up @@ -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
37 changes: 37 additions & 0 deletions x264.c
Expand Up @@ -190,6 +190,15 @@ static const char * const muxer_names[] =
"flv",
#if HAVE_GPAC || HAVE_LSMASH
"mp4",
#endif
#if HAVE_LSMASH
"3gp",
"3g2",
"mov",
"qt",
#endif
#if HAVE_AVI_OUTPUT
"avi",
#endif
0
};
Expand Down Expand Up @@ -476,6 +485,12 @@ static void help( x264_param_t *defaults, int longhelp )
" .mkv -> Matroska\n"
" .flv -> Flash Video\n"
" .mp4 -> MP4 if compiled with GPAC or L-SMASH support (%s)\n"
#if HAVE_LSMASH
" .3gp -> MP4 (branded '3gp6')\n"
" .3g2 -> MP4 (branded '3gp6' and '3g2a')\n"
" .mov or .qt -> QuickTime File Format\n"
#endif
" .avi -> AVI if compiled with support (%s)\n"
"Output bit depth: %d (configured at compile time)\n"
"\n"
"Options:\n"
Expand Down Expand Up @@ -506,6 +521,11 @@ static void help( x264_param_t *defaults, int longhelp )
"lsmash",
#else
"no",
#endif
#if HAVE_AVI_OUTPUT
"yes",
#else
"no",
#endif
x264_bit_depth
);
Expand Down Expand Up @@ -1217,6 +1237,23 @@ static int select_output( const char *muxer, char *filename, x264_param_t *param
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;
Expand Down

0 comments on commit c36b9ea

Please sign in to comment.