rendered paste bodydiff --git a/Makefile b/Makefileindex 0f4d0cf..8a31a45 100644--- a/Makefile+++ b/Makefile@@ -46,6 +46,20 @@ ifneq ($(findstring HAVE_GPAC, $(CONFIG)),) SRCCLI += output/mp4.c endif +ifneq ($(findstring HAVE_AUDIO 1, $(CONFIG)),)+SRCCLI += audio/audio.c audio/encoders.c \+ filters/audio/audio_filters.c \+ filters/audio/internal.c \+ audio/encoders/enc_raw.c+ifneq ($(findstring HAVE_LAVF, $(CONFIG)),)+SRCCLI += input/audio/lavf.c+endif+endif++ifneq ($(findstring HAVE_LAME 1, $(CONFIG)),)+SRCCLI += audio/encoders/enc_mp3lame.c+endif+ # Visualization sources ifeq ($(VIS),yes) SRCS += common/visualize.c common/display-x11.cdiff --git a/audio/audio.c b/audio/audio.cnew file mode 100644index 0000000..fc115cf--- /dev/null+++ b/audio/audio.c@@ -0,0 +1,24 @@+#include "filters/audio/internal.h"++#include <assert.h>++hnd_t audio_open_from_file( audio_filter_t *preferred_filter, char *path, int trackno )+{+ audio_filter_t *source = preferred_filter ? preferred_filter : af_get_filter( "lavf" );+ if( !source )+ {+ x264_cli_log( "audio", X264_LOG_ERROR, "no decoder / demuxer avilable!\n" );+ return NULL;+ }+ hnd_t h = NULL;+ size_t init_arg_size = strlen( path ) + 10;+ char *init_arg = malloc( init_arg_size );+ assert( snprintf( init_arg, init_arg_size, "%s,%d", path, trackno ) < init_arg_size );+ if( source->init( &h, init_arg ) < 0 || !h )+ {+ x264_cli_log( "audio", X264_LOG_ERROR, "error initializing source filter!\n" );+ return NULL;+ }+ free( init_arg );+ return h;+}diff --git a/audio/audio.h b/audio/audio.hnew file mode 100644index 0000000..de9fa58--- /dev/null+++ b/audio/audio.h@@ -0,0 +1,18 @@+#ifndef AUDIO_AUDIO_H_+#define AUDIO_AUDIO_H_++#include <stdint.h>+#include "x264cli.h"+#include "filters/audio/audio_filters.h"++enum AudioTrack+{+ TRACK_ANY = -1,+ TRACK_NONE = -2+};++hnd_t audio_open_from_file( audio_filter_t *preferred_filter, char *path, int trackno );++#include "audio/encoders.h"++#endif /* AUDIO_AUDIO_H_ */diff --git a/audio/encoders.c b/audio/encoders.cnew file mode 100644index 0000000..b839d13--- /dev/null+++ b/audio/encoders.c@@ -0,0 +1,114 @@+#include "audio/encoders.h"++#include <assert.h>+#include <stdlib.h>++struct aenc_t+{+ const audio_encoder_t *enc;+ hnd_t handle;+};++hnd_t audio_encoder_open( const audio_encoder_t *encoder, hnd_t filter_chain, const char *opts )+{+ assert( encoder && filter_chain );+ struct aenc_t *enc = calloc( 1, sizeof( struct aenc_t ) );+ enc->enc = encoder;+ enc->handle = encoder->init( filter_chain, opts );++ return enc;+}++audio_info_t *audio_encoder_info( hnd_t encoder )+{+ assert( encoder );+ struct aenc_t *enc = encoder;++ return enc->enc->get_info( enc->handle );+}++audio_packet_t *audio_encode_frame( hnd_t encoder )+{+ assert( encoder );+ struct aenc_t *enc = encoder;++ return enc->enc->get_next_packet( enc->handle );+}++void audio_encoder_skip_samples( hnd_t encoder, uint64_t samplecount )+{+ assert( encoder );+ struct aenc_t *enc = encoder;++ return enc->enc->skip_samples( enc->handle, samplecount );+}++audio_packet_t *audio_encoder_finish( hnd_t encoder )+{+ assert( encoder );+ struct aenc_t *enc = encoder;++ return enc->enc->finish( enc->handle );+}++void audio_free_frame( hnd_t encoder, audio_packet_t *frame )+{+ assert( encoder );+ struct aenc_t *enc = encoder;++ return enc->enc->free_packet( enc->handle, frame );+}++void audio_encoder_close( hnd_t encoder )+{+ if( !encoder )+ return;+ struct aenc_t *enc = encoder;++ enc->enc->close( enc->handle );+ free( enc );+}++const audio_encoder_t *encoder_by_name( char *name )+{+#define IFRET( enc ) if( !strcmp( #enc, name ) ) return &audio_encoder_ ## enc;+#if HAVE_LAME+ IFRET( mp3 );+#endif+ IFRET( raw );+#undef IFRET+ return NULL;+}++const audio_encoder_t *select_audio_encoder( char *encoder, char* allowed_list[] )+{+ if( !encoder )+ return NULL;+ if( allowed_list )+ {+ if( !strcmp( encoder, "auto" ) )+ {+ audio_encoder_t *enc;+ for( int i = 0; allowed_list[i] != NULL; i++ )+ {+ enc = encoder_by_name( allowed_list[i] );+ if( enc )+ return enc;+ }+ return NULL;+ }+ else+ {+ int valid = 0;+ for( int i = 0; allowed_list[i] != NULL; i++ )+ if( !strcmp( encoder, allowed_list[i] ) )+ {+ valid = 1;+ break;+ }+ if( !valid )+ return NULL;+ }+ }+ return encoder_by_name( encoder );+}diff --git a/audio/encoders.h b/audio/encoders.hnew file mode 100644index 0000000..daacac2--- /dev/null+++ b/audio/encoders.h@@ -0,0 +1,40 @@+#ifndef AUDIO_ENCODERS_H_+#define AUDIO_ENCODERS_H_++#include "audio/audio.h"+#include "filters/audio/audio_filters.h"++typedef struct audio_encoder_t+{+ hnd_t (*init)( hnd_t filter_chain, const char *opts );+ audio_info_t *(*get_info)( hnd_t handle );+ audio_packet_t *(*get_next_packet)( hnd_t handle );+ void (*skip_samples)( hnd_t handle, uint64_t samplecount );+ audio_packet_t *(*finish)( hnd_t handle );+ void (*free_packet)( hnd_t handle, audio_packet_t *samples );+ void (*close)( hnd_t handle );+} audio_encoder_t;++extern const audio_encoder_t audio_encoder_raw;+#if HAVE_LAME+extern const audio_encoder_t audio_encoder_mp3;+#endif++/* the first available encoder on allowed_list is the prefered encoder if encoder is "auto"+ * allowed_list = NULL means any valid encoder is allowed+ * The 'none' case isn't handled by this function (will return NULL like with any other invalid encoder)+ * If the user wants 'none' to be a default, it must be tested outside of this function+ * If the user wants to allow any encoder, the default case must be tested outside of this function */+const audio_encoder_t *select_audio_encoder( char *encoder, char* allowed_list[] );+const audio_encoder_t *encoder_by_name( char *name );+hnd_t audio_encoder_open( const audio_encoder_t *encoder, hnd_t filter_chain, const char *opts );++audio_info_t *audio_encoder_info( hnd_t encoder );+void audio_encoder_skip_samples( hnd_t encoder, uint64_t samplecount );+audio_packet_t *audio_encode_frame( hnd_t encoder );+audio_packet_t *audio_encoder_finish( hnd_t encoder );+void audio_free_frame( hnd_t encoder, audio_packet_t *frame );++void audio_encoder_close( hnd_t encoder );++#endifdiff --git a/audio/encoders/enc_mp3lame.c b/audio/encoders/enc_mp3lame.cnew file mode 100644index 0000000..7d440a6--- /dev/null+++ b/audio/encoders/enc_mp3lame.c@@ -0,0 +1,175 @@+#include "audio/encoders.h"+#include "filters/audio/internal.h"++#include "lame/lame.h"+#include <assert.h>++typedef struct enc_lame_t+{+ audio_info_t info;+ audio_info_t af_info;+ hnd_t filter_chain;++ int finishing;+ lame_global_flags *lame;+ int64_t last_sample;+ uint8_t *buffer;+ size_t bufsize;+ audio_packet_t *in;+} enc_lame_t;++static hnd_t init( hnd_t filter_chain, const char *opt_str )+{+ assert( filter_chain );+ audio_hnd_t *chain = filter_chain;+ if( chain->info.channels > 2 )+ {+ x264_cli_log( "lame", X264_LOG_ERROR, "only mono or stereo audio is supported\n" );+ return 0;+ }+ enc_lame_t *h = calloc( 1, sizeof( enc_lame_t ) );+ h->filter_chain = chain;+ h->info = h->af_info = chain->info;++ char **opts = x264_split_options( opt_str, (const char*[]){ "bitrate", "vbr", "quality", NULL } );+ assert( opts );++ char *cbr = x264_get_option( "bitrate", opts );+ char *vbr = x264_get_option( "vbr", opts );++ float brval = x264_otof( vbr, 6.0 );+ brval = x264_otof( cbr, brval );+ int quality = x264_otoi( x264_get_option( "quality", opts ), 0 );++ x264_free_string_array( opts );+ if( cbr && vbr ) {+ x264_cli_log( "lame", X264_LOG_ERROR, "both bitrate and quality mode specified" );+ return 0;+ }++ h->info.codec_name = "mp3";+ h->info.extradata = NULL;+ h->info.extradata_size = 0;++ h->lame = lame_init();+ // lame expects floats to be in the same range as shorts, our floats are -1..1 so tell it to scale+ lame_set_scale( h->lame, 32768 );+ lame_set_in_samplerate( h->lame, h->info.samplerate );+ lame_set_num_channels( h->lame, h->info.channels );+ lame_set_quality( h->lame, quality );+ lame_set_VBR( h->lame, vbr_default );++ if( cbr )+ {+ lame_set_VBR( h->lame, vbr_off );+ lame_set_brate( h->lame, (int) brval );+ }+ else+ lame_set_VBR_quality( h->lame, brval );++ lame_init_params( h->lame );++ h->info.framelen = lame_get_framesize( h->lame );+ h->info.framesize = h->info.framelen * 2;+ h->info.chansize = 2;+ h->info.samplesize = 2 * h->info.channels;++ h->bufsize = 125 * h->info.framelen / 100 + 7200; // from lame.h, largest frame that the encoding functions may return++ x264_cli_log( "audio", X264_LOG_INFO, "opened lame mp3 encoder (%s: %g%s)\n",+ ( cbr ? "bitrate" : "VBR" ), brval,+ ( cbr ? "kbps" : "" ) );++ return h;+}++static audio_info_t *get_info( hnd_t handle )+{+ assert( handle );+ enc_lame_t *h = handle;++ return &h->info;+}++static void free_packet( hnd_t handle, audio_packet_t *packet )+{+ packet->owner = NULL;+ af_free_packet( packet );+}++static audio_packet_t *get_next_packet( hnd_t handle )+{+ enc_lame_t *h = handle;+ if( h->finishing )+ return NULL;++ audio_packet_t *out = calloc( 1, sizeof( audio_packet_t ) );+ out->rawdata = malloc( h->bufsize );++ while( !out->size )+ {+ if( h->in && h->in->flags & AUDIO_FLAG_EOF )+ {+ h->finishing = 1;+ goto error; // Not an error here but it'd do the same handling+ }+ af_free_packet( h->in );++ if( !( h->in = af_get_samples( h->filter_chain, h->last_sample, h->last_sample + h->info.framelen ) ) )+ goto error;+ h->last_sample += h->in->samplecount;++ out->size = lame_encode_buffer_float( h->lame, h->in->data[0], h->in->data[1],+ h->in->samplecount, out->rawdata, h->bufsize );+ }++ return out;++error:+ af_free_packet( h->in );+ af_free_packet( out );+ return NULL;+}++static void skip_samples( hnd_t handle, uint64_t samplecount )+{+ ((enc_lame_t*)handle)->last_sample += samplecount;+}++static audio_packet_t *finish( hnd_t encoder )+{+ enc_lame_t *h = encoder;+ h->finishing = 1;++ audio_packet_t *out = calloc( 1, sizeof( audio_packet_t ) );+ out->rawdata = malloc( h->bufsize );+ out->size = lame_encode_flush( h->lame, out->rawdata, h->bufsize );+ if( !out->size )+ goto error;+ return out;++error:+ af_free_packet( out );+ return NULL;+}+++static void mp3_close( hnd_t handle )+{+ enc_lame_t *h = handle;++ lame_close( h->lame );+ free( h );+}++const audio_encoder_t audio_encoder_mp3 =+{+ .init = init,+ .get_info = get_info,+ .get_next_packet = get_next_packet,+ .skip_samples = skip_samples,+ .finish = finish,+ .free_packet = free_packet,+ .close = mp3_close+};+diff --git a/audio/encoders/enc_raw.c b/audio/encoders/enc_raw.cnew file mode 100644index 0000000..c1a890c--- /dev/null+++ b/audio/encoders/enc_raw.c@@ -0,0 +1,92 @@+#include "audio/encoders.h"+#include "filters/audio/internal.h"++#include <assert.h>++typedef struct enc_raw_t+{+ audio_info_t info;+ int finishing;+ hnd_t filter_chain;+ int64_t last_sample;+} enc_raw_t;++static hnd_t init( hnd_t filter_chain, const char *opts )+{+ assert( filter_chain );+ enc_raw_t *h = calloc( 1, sizeof( enc_raw_t ) );+ audio_hnd_t *chain = h->filter_chain = filter_chain;+ h->info = chain->info;++ h->info.codec_name = "raw";+ h->info.extradata = NULL;+ h->info.extradata_size = 0;+ h->info.chansize = 2;+ h->info.samplesize = 2 * h->info.channels;++ x264_cli_log( "audio", X264_LOG_INFO, "opened raw encoder (%dbits, %dch, %dhz)\n",+ h->info.chansize * 8, h->info.channels, h->info.samplerate );+ return h;+}++static audio_info_t *get_info( hnd_t handle )+{+ assert( handle );+ enc_raw_t *h = handle;++ return &h->info;+}++static audio_packet_t *get_next_packet( hnd_t handle )+{+ enc_raw_t *h = handle;+ if( h->finishing )+ return NULL;++ audio_packet_t *smp = af_get_samples( h->filter_chain, h->last_sample, h->last_sample + h->info.framelen );+ if( !smp )+ return NULL;+ h->last_sample += h->info.framelen;++ audio_packet_t *out = calloc( 1, sizeof( audio_packet_t ) );+ memcpy( out, smp, sizeof( audio_packet_t ) );+ out->data = NULL;+ out->size = 0;+ out->rawdata = af_interleave2( SMPFMT_S16, smp->data, smp->channels, smp->samplecount );+ out->size = smp->samplecount * h->info.samplesize;+ af_free_packet( smp );++ return out;+}++static void skip_samples( hnd_t handle, uint64_t samplecount )+{+ ((enc_raw_t*)handle)->last_sample += samplecount;+}++static audio_packet_t *finish( hnd_t handle )+{+ ((enc_raw_t*)handle)->finishing = 1;+ return NULL;+}++static void free_packet( hnd_t handle, audio_packet_t *packet )+{+ af_free_packet( packet );+}++static void raw_close( hnd_t handle )+{+ free( handle );+}++const audio_encoder_t audio_encoder_raw =+{+ .init = init,+ .get_info = get_info,+ .get_next_packet = get_next_packet,+ .skip_samples = skip_samples,+ .finish = finish,+ .free_packet = free_packet,+ .close = raw_close+};diff --git a/configure b/configureindex b6382f0..aab64af 100755--- a/configure+++ b/configure@@ -11,6 +11,7 @@ echo " --disable-avs disables avisynth support (windows only)" echo " --disable-lavf disables libavformat support" echo " --disable-ffms disables ffmpegsource support" echo " --disable-gpac disables gpac support"+echo " --disable-audio disables audio support (requires lavf support)" echo " --disable-pthread disables multithreaded encoding" echo " --disable-swscale disables swscale support" echo " --disable-asm disables platform-specific assembly optimizations"@@ -138,6 +139,7 @@ avs="auto" lavf="auto" ffms="auto" gpac="auto"+audio="auto" pthread="auto" swscale="auto" asm="auto"@@ -148,6 +150,8 @@ vis="no" shared="no" bit_depth="8" +audio_mp3="auto"+ CFLAGS="$CFLAGS -Wall -I." LDFLAGS="$LDFLAGS" LDFLAGSCLI="$LDFLAGSCLI"@@ -192,6 +196,9 @@ for opt do --disable-gpac) gpac="no" ;;+ --disable-audio)+ audio="no"+ ;; --extra-asflags=*) ASFLAGS="$ASFLAGS ${opt#--extra-asflags=}" ;;@@ -625,6 +632,23 @@ elif [ "$swscale" = "yes" ]; then CFLAGS="$CFLAGS $SWSCALE_CFLAGS" fi +if [ "$audio" = "auto" ]; then+ audio="yes"+ audio_encs="raw"+ if [ "$audio_mp3" = "auto" ]; then+ if cc_check lame/lame.h "-lmp3lame" "get_lame_version();"; then+ LDFLAGSCLI="-lmp3lame $LDFLAGSCLI"+ audio_mp3="yes"+ audio_encs="$audio_encs, mp3"+ else+ audio_mp3="no"+ fi+ fi+fi++define HAVE_AUDIO $(test "$audio" = "yes" && echo 1 || echo 0)+define HAVE_LAME $(test "$audio_mp3" = "yes" && echo 1 || echo 0)+ GPAC_LIBS="-lgpac_static" if [ $SYS = MINGW ]; then GPAC_LIBS="$GPAC_LIBS -lwinmm"@@ -779,6 +803,7 @@ asm: $asm avs: $avs lavf: $lavf ffms: $ffms+audio: $audio$(test $audio = "yes" && echo " ($audio_encs)") gpac: $gpac pthread: $pthread filters: $filtersdiff --git a/filters/audio/audio_filters.c b/filters/audio/audio_filters.cnew file mode 100644index 0000000..0bff822--- /dev/null+++ b/filters/audio/audio_filters.c@@ -0,0 +1,61 @@+#include "filters/audio/internal.h"++#include <assert.h>++audio_info_t *af_get_info( hnd_t handle )+{+ return &((audio_hnd_t*)handle)->info;+}++audio_filter_t *af_get_filter( char *name )+{+#define CHECK( filter ) \+ extern audio_filter_t audio_filter_##filter; \+ if ( !strcmp( name, audio_filter_##filter.name ) ) \+ return &audio_filter_##filter+#if HAVE_LAVF+ CHECK( lavf );+#endif+#undef CHECKFLT+#undef CHECK+ return NULL;+}++audio_packet_t *af_get_samples( hnd_t handle, int64_t first_sample, int64_t last_sample )+{+ audio_hnd_t *h = handle;+ audio_packet_t *out = h->self->get_samples( h, first_sample, last_sample );+ if( out )+ {+ out->owner = h;+ return out;+ }+ return 0;+}++void af_free_packet( audio_packet_t *pkt )+{+ if( !pkt )+ return;+ audio_hnd_t *owner = pkt->owner;+ if( owner )+ owner->self->free_packet( owner, pkt );+ else+ {+ if( pkt->priv )+ free( pkt->priv );+ if( pkt->rawdata )+ free( pkt->rawdata );+ if( pkt->data && pkt->channels )+ af_free_buffer( pkt->data, pkt->channels );+ free( pkt );+ }+}++void af_close( hnd_t chain )+{+ audio_hnd_t *h = chain;+ if( h->prev )+ af_close( h->prev );+ h->self->close( h );+}diff --git a/filters/audio/audio_filters.h b/filters/audio/audio_filters.hnew file mode 100644index 0000000..cdd9bf2--- /dev/null+++ b/filters/audio/audio_filters.h@@ -0,0 +1,85 @@+#ifndef FILTERS_AUDIO_AUDIO_FILTERS_H_+#define FILTERS_AUDIO_AUDIO_FILTERS_H_++#include <stdint.h>+#include "x264cli.h"+#include "filters/filters.h"++// Ripped from ffmpeg's avcodec.h+#ifndef CH_FRONT_LEFT+#define CH_FRONT_LEFT 0x00000001+#define CH_FRONT_RIGHT 0x00000002+#define CH_FRONT_CENTER 0x00000004+#define CH_LOW_FREQUENCY 0x00000008+#define CH_BACK_LEFT 0x00000010+#define CH_BACK_RIGHT 0x00000020+#define CH_FRONT_LEFT_OF_CENTER 0x00000040+#define CH_FRONT_RIGHT_OF_CENTER 0x00000080+#define CH_BACK_CENTER 0x00000100+#define CH_SIDE_LEFT 0x00000200+#define CH_SIDE_RIGHT 0x00000400+#define CH_TOP_CENTER 0x00000800+#define CH_TOP_FRONT_LEFT 0x00001000+#define CH_TOP_FRONT_CENTER 0x00002000+#define CH_TOP_FRONT_RIGHT 0x00004000+#define CH_TOP_BACK_LEFT 0x00008000+#define CH_TOP_BACK_CENTER 0x00010000+#define CH_TOP_BACK_RIGHT 0x00020000+#define CH_STEREO_LEFT 0x20000000 ///< Stereo downmix.+#define CH_STEREO_RIGHT 0x40000000 ///< See CH_STEREO_LEFT.+#endif++enum AudioFlags+{+ AUDIO_FLAG_NONE = 0,+ AUDIO_FLAG_EOF = 1+};++typedef struct audio_packet_t {+ int64_t dts;+ float **data;+ int size;+ unsigned channels;+ unsigned samplecount;+ uint8_t *rawdata;+ int rawsize;+ int64_t pos;+ enum AudioFlags flags;+ hnd_t priv;+ hnd_t owner;+} audio_packet_t;++typedef struct audio_filter_t+{+ int (*init)( hnd_t *handle, const char *opts );+ struct audio_packet_t *(*get_samples)( hnd_t handle, int64_t first_sample, int64_t last_sample );+ void (*free_packet)( hnd_t self, struct audio_packet_t *frame );+ void (*close)( hnd_t handle );+ char *name, *longname, *description, *help;+ void (*help_callback)( int longhelp );+} audio_filter_t;++typedef struct audio_info_t+{+ char *codec_name;+ int samplerate; // Sample Rate in Hz+ int channels; // How many channels+ int64_t chanlayout; // Channel layout (CH_*)+ int framelen; // Frame length in samples+ size_t framesize; // Frame size in bytes+ int chansize; // Bytes per channel per sample (from the encoded audio)+ int samplesize; // Bytes per sample (from the encoded audio)+ int64_t time_base_num, time_base_den;+ uint8_t *extradata;+ int extradata_size;+} audio_info_t;++#include "audio/audio.h"++audio_info_t *af_get_info( hnd_t handle );+audio_filter_t *af_get_filter( char *name );+audio_packet_t *af_get_samples( hnd_t handle, int64_t first_sample, int64_t last_sample );+void af_free_packet( audio_packet_t *pkt );+void af_close( hnd_t chain );++#endif /* AUDIO_H_ */diff --git a/filters/audio/internal.c b/filters/audio/internal.cnew file mode 100644index 0000000..d41b124--- /dev/null+++ b/filters/audio/internal.c@@ -0,0 +1,160 @@+#include "filters/audio/internal.h"+#include <stdint.h>+#include <math.h>++float **af_get_buffer( unsigned channels, unsigned samplecount )+{+ float **samples = malloc( sizeof( float* ) * channels );+ for( int i = 0; i < channels; i++ ) {+ samples[i] = malloc( sizeof( float ) * samplecount );+ }+ return samples;+}++int af_resize_buffer( float **buffer, unsigned channels, unsigned samplecount )+{+ for( int c = 0; c < channels; c++ )+ {+ if( !(buffer[c] = realloc( buffer[c], sizeof( float ) * samplecount )) )+ return -1;+ }+ return 0;+}++float **af_dup_buffer( float **buffer, unsigned channels, unsigned samplecount )+{+ float **buf = af_get_buffer( channels, samplecount );+ for( int c = 0; c < channels; c++ )+ memcpy( buf[c], buffer[c], samplecount );+ return buf;+}++void af_free_buffer( float **buffer, unsigned channels )+{+ if( !buffer )+ return;+ for( int c = 0; c < channels; c++ )+ free( buffer[c] );+ free( buffer );+}++int af_cat_buffer( float **buf, unsigned bufsamples, float **in, unsigned insamples, unsigned channels )+{+ if( af_resize_buffer( buf, channels, bufsamples + insamples ) < 0 )+ return -1;+ for( int c = 0; c < channels; c++ )+ for( int s = 0; s < insamples; s++ )+ buf[c][bufsamples+s] = in[c][s];+ return 0;+}++float **af_deinterleave ( float *samples, unsigned channels, unsigned samplecount )+{+ float **deint = af_get_buffer( channels, samplecount );+ for( int s = 0; s < samplecount; s++ )+ for( int c = 0; c < channels; c++ )+ deint[c][s] = samples[s*channels + c];+ return deint;+}++float *af_interleave ( float **in, unsigned channels, unsigned samplecount )+{+ float *inter = malloc( sizeof( float ) * channels * samplecount );+ for( int c = 0; c < channels; c++ )+ for( int s = 0; s < samplecount; s++ )+ inter[s*channels + c] = in[c][s];+ return inter;+}++float **af_deinterleave2( uint8_t *samples, enum SampleFmt fmt, unsigned channels, unsigned samplecount )+{+ float *in = (float*) af_convert( SMPFMT_FLT, samples, fmt, channels, samplecount );+ float **out = af_deinterleave( in, channels, samplecount );+ free( in );+ return out;+}++uint8_t *af_interleave2( enum SampleFmt outfmt, float **in, unsigned channels, unsigned samplecount )+{+ float *tmp = af_interleave( in, channels, samplecount );+ uint8_t *out = af_convert( outfmt, (uint8_t*) tmp, SMPFMT_FLT, channels, samplecount );+ free( tmp );+ return out;+}++static inline int samplesize( enum SampleFmt fmt )+{+ switch( fmt )+ {+ case SMPFMT_U8:+ return 1;+ case SMPFMT_S16:+ return 2;+ case SMPFMT_S32:+ case SMPFMT_FLT:+ return 4;+ case SMPFMT_DBL:+ return 8;+ default:+ return 0;+ }+}++#define CLIPFUN( num, type, min, max ) \+ static inline type clip##num( int64_t i ) { \+ return (type)( ( i > max ) ? max : ( ( i < min ) ? min : i ) ); \+ }+CLIPFUN( 8, uint8_t, 0, UINT8_MAX )+CLIPFUN( 16, int16_t, INT16_MIN, INT16_MAX )+CLIPFUN( 32, int32_t, INT32_MIN, INT32_MAX )+#undef CLIPFUN++uint8_t *af_convert( enum SampleFmt outfmt, uint8_t *in, enum SampleFmt fmt, unsigned channels, unsigned samplecount )+{+ int totalsamples = channels * samplecount;+ int sz = samplesize( outfmt ) * totalsamples;+ uint8_t *out = malloc( sz );+ if( !out )+ return NULL;++ if( fmt == outfmt )+ {+ memcpy( out, in, sz );+ return out;+ }++#define CONVERT( ifmt, ofmt, otype, expr ) \+ if( ifmt == fmt && ofmt == outfmt ) { \+ for( int i = 0; i < totalsamples; i++ ) \+ { \+ ((otype*)out)[i] = (otype)expr; \+ } \+ return out; \+ }+#define IN( itype ) (((itype*)in)[i])++ CONVERT( SMPFMT_U8, SMPFMT_S16, int16_t, (IN( uint8_t ) - 0x80) << 8 );+ CONVERT( SMPFMT_U8, SMPFMT_S32, int32_t, (IN( uint8_t ) - 0x80) << 24 );+ CONVERT( SMPFMT_U8, SMPFMT_FLT, float, (IN( uint8_t ) - 0x80) * (1.0 / (1<<7)) );+ CONVERT( SMPFMT_U8, SMPFMT_DBL, double, (IN( uint8_t ) - 0x80) * (1.0 / (1<<7)) );+ CONVERT( SMPFMT_S16, SMPFMT_U8, uint8_t, (IN( int16_t ) >> 8) + 0x80 );+ CONVERT( SMPFMT_S16, SMPFMT_S32, int32_t, IN( int16_t ) << 16 );+ CONVERT( SMPFMT_S16, SMPFMT_FLT, float, IN( int16_t ) * (1.0 / (1<<15)) );+ CONVERT( SMPFMT_S16, SMPFMT_DBL, double, IN( int16_t ) * (1.0 / (1<<15)) );+ CONVERT( SMPFMT_S32, SMPFMT_U8, uint8_t, (IN( int32_t ) >> 24) + 0x80 );+ CONVERT( SMPFMT_S32, SMPFMT_S16, int16_t, IN( int32_t ) >> 16 );+ CONVERT( SMPFMT_S32, SMPFMT_FLT, float, IN( int32_t ) * (1.0 / (1<<31)) );+ CONVERT( SMPFMT_S32, SMPFMT_DBL, double, IN( int32_t ) * (1.0 / (1<<31)) );+ CONVERT( SMPFMT_FLT, SMPFMT_U8, uint8_t, clip8( lrintf( IN( float ) * (1<<7) ) + 0x80 ) );+ CONVERT( SMPFMT_FLT, SMPFMT_S16, int16_t, clip16( lrintf( IN( float ) * (1<<15) ) ) );+ CONVERT( SMPFMT_FLT, SMPFMT_S32, int32_t, clip32( llrintf( IN( float ) * (1U<<31) ) ) );+ CONVERT( SMPFMT_FLT, SMPFMT_DBL, double, IN( float ) );+ CONVERT( SMPFMT_FLT, SMPFMT_U8, uint8_t, clip8( lrintf( IN( double ) * (1<<7) ) + 0x80 ) );+ CONVERT( SMPFMT_FLT, SMPFMT_S16, int16_t, clip16( lrintf( IN( double ) * (1<<15) ) ) );+ CONVERT( SMPFMT_FLT, SMPFMT_S32, int32_t, clip32( llrintf( IN( double ) * (1U<<31) ) ) );+ CONVERT( SMPFMT_FLT, SMPFMT_DBL, double, IN( double ) );+#undef IN+#undef CONVERT+ free( out );+ return NULL;+}diff --git a/filters/audio/internal.h b/filters/audio/internal.hnew file mode 100644index 0000000..56a833a--- /dev/null+++ b/filters/audio/internal.h@@ -0,0 +1,58 @@+#ifndef FILTERS_AUDIO_INTERNAL_H_+#define FILTERS_AUDIO_INTERNAL_H_++#include "filters/audio/audio_filters.h"++#define AUDIO_FILTER_COMMON \+ const audio_filter_t *self; \+ audio_info_t info; \+ struct audio_hnd_t *prev;++#define INIT_FILTER_STRUCT(filterstruct, structname) \+ structname *h; \+ do \+ { \+ h = calloc( 1, sizeof( structname ) ); \+ if( !h ) \+ goto fail; \+ h->self = &filterstruct; \+ h->prev = *handle; \+ if( h->prev ) \+ h->info = h->prev->info; \+ *handle = h; \+ } while( 0 )++// Generic audio handle (used to access fields from AUDIO_FILTER_COMMON)+typedef struct audio_hnd_t+{+ AUDIO_FILTER_COMMON+} audio_hnd_t;++#define AF_LOG( handle, level, ... ) do { x264_cli_log( ((audio_hnd_t*)handle)->self->name, (level), __VA_ARGS__ ); } while (0)++#define AF_LOG_ERR( handle, ... ) AF_LOG( (handle), X264_LOG_ERROR , __VA_ARGS__ )+#define AF_LOG_WARN( handle, ... ) AF_LOG( (handle), X264_LOG_WARNING, __VA_ARGS__ )++enum SampleFmt {+ SMPFMT_NONE = -1,+ SMPFMT_U8,+ SMPFMT_S16,+ SMPFMT_S32,+ SMPFMT_FLT,+ SMPFMT_DBL+};++float **af_get_buffer ( unsigned channels, unsigned samplecount );+int af_resize_buffer( float **buffer, unsigned channels, unsigned samplecount );+void af_free_buffer ( float **buffer, unsigned channels );+float **af_dup_buffer ( float **buffer, unsigned channels, unsigned samplecount );+int af_cat_buffer ( float **buf, unsigned bufsamples, float **in, unsigned insamples, unsigned channels );++float **af_deinterleave ( float *samples, unsigned channels, unsigned samplecount );+float *af_interleave ( float **in, unsigned channels, unsigned samplecount );++float **af_deinterleave2( uint8_t *samples, enum SampleFmt fmt, unsigned channels, unsigned samplecount );+uint8_t *af_interleave2 ( enum SampleFmt outfmt, float **in, unsigned channels, unsigned samplecount );+uint8_t *af_convert ( enum SampleFmt outfmt, uint8_t *in, enum SampleFmt fmt, unsigned channels, unsigned samplecount );++#endif /* FILTERS_AUDIO_INTERNAL_H_ */diff --git a/input/audio/lavf.c b/input/audio/lavf.cnew file mode 100644index 0000000..c6d9ca9--- /dev/null+++ b/input/audio/lavf.c@@ -0,0 +1,400 @@+#include "filters/audio/internal.h"+#undef DECLARE_ALIGNED+#include "libavformat/avformat.h"+#include "libavcodec/avcodec.h"+#include <assert.h>+#include <stdio.h>+#include <inttypes.h>++typedef struct lavf_source_t+{+ AUDIO_FILTER_COMMON+ AVFormatContext *lavf;+ AVCodecContext *ctx;+ AVCodec *codec;++ int samplefmt;+ unsigned track;+ uint8_t *buffer;+ intptr_t bufsize;+ intptr_t surplus;+ intptr_t len;+ uint64_t bytepos;++ AVPacket *pkt;+} lavf_source_t;++#define DEFAULT_BUFSIZE AVCODEC_MAX_AUDIO_FRAME_SIZE * 2++static int buffer_next_frame( lavf_source_t *h );++const audio_filter_t audio_filter_lavf;++static int init( hnd_t *handle, const char *opt_str )+{+ assert( opt_str );+ assert( !(*handle) ); // This must be the first filter+ char **opts = x264_split_options( opt_str, (const char*[]){ "filename", "track", NULL } );++ if( !opts )+ return -1;++ char *filename = x264_get_option( "filename", opts );+ char *trackstr = x264_otos( x264_get_option( "track", opts ), "any" );++ if( !filename )+ {+ x264_cli_log( "lavf", X264_LOG_ERROR, "no filename given" );+ goto fail2;+ }++ int track;+ if ( !strcmp( trackstr, "any" ) )+ track = TRACK_ANY;+ else+ track = x264_otoi( trackstr, TRACK_NONE );++ if( track == TRACK_NONE )+ {+ x264_cli_log( "lavf", X264_LOG_ERROR, "no valid track requested ('any', 0 or a positive integer)\n" );+ goto fail2;+ }++ INIT_FILTER_STRUCT( audio_filter_lavf, lavf_source_t );++ av_register_all();+ if( !strcmp( filename, "-" ) )+ filename = "pipe:";++ if( av_open_input_file( &h->lavf, filename, NULL, 0, NULL ) )+ {+ AF_LOG_ERR( h, "could not open audio file\n" );+ goto fail;+ }++ if( av_find_stream_info( h->lavf ) < 0 )+ {+ AF_LOG_ERR( h, "could not find stream info\n" );+ goto fail;+ }++ unsigned tid = TRACK_NONE;+ if( track >= 0 )+ {+ if( track < h->lavf->nb_streams &&+ h->lavf->streams[track]->codec->codec_type == CODEC_TYPE_AUDIO )+ tid = track;+ else+ AF_LOG_ERR( h, "requested track %d is unavailable "+ "or is not an audio track\n", track );+ }+ else // TRACK_ANY (pick first)+ {+ for( track = 0;+ track < h->lavf->nb_streams &&+ h->lavf->streams[track]->codec->codec_type != CODEC_TYPE_AUDIO; )+ ++track;+ if( track < h->lavf->nb_streams )+ tid = track;+ else+ AF_LOG_ERR( h, "could not find any audio track\n" );+ }++ if( tid == TRACK_NONE )+ goto fail;++ h->track = tid;++ h->ctx = h->lavf->streams[tid]->codec;+ h->codec = avcodec_find_decoder( h->ctx->codec_id );+ if( avcodec_open( h->ctx, h->codec ) )+ goto codecfail;++ h->samplefmt = h->ctx->sample_fmt;+ h->info = (audio_info_t)+ {+ .samplerate = h->ctx->sample_rate,+ .channels = h->ctx->channels,+ .chanlayout = h->ctx->channel_layout,+ .framelen = h->ctx->frame_size,+ .framesize = h->ctx->frame_size * sizeof( float ),+ .chansize = av_get_bits_per_sample_format( h->samplefmt ) / 8,+ .samplesize = av_get_bits_per_sample_format( h->samplefmt ) * h->ctx->channels / 8,+ .time_base_num = h->ctx->time_base.num,+ .time_base_den = h->ctx->time_base.den,+ .extradata = h->ctx->extradata,+ .extradata_size = h->ctx->extradata_size+ };++ h->bufsize = DEFAULT_BUFSIZE;+ h->surplus = h->info.framesize * 3 / 2;+ assert( h->bufsize > h->surplus * 2 );+ h->buffer = av_malloc( h->bufsize );++ if( !buffer_next_frame( h ) )+ goto codecfail;++ x264_free_string_array( opts );+ return 0;++codecfail:+ AF_LOG_ERR( h, "error opening the %s decoder for track %d\n", h->codec->name, h->track );+fail:+ if( h->lavf )+ av_close_input_file( h->lavf );+ if( h )+ free( h );+ *handle = NULL;+fail2:+ x264_free_string_array( opts );+ return -1;+}++static inline void free_avpacket( AVPacket *pkt )+{+ av_free_packet( pkt );+ free( pkt );+}++static void free_packet( hnd_t handle, audio_packet_t *pkt )+{+ pkt->owner = NULL;+ af_free_packet( pkt );+}++static struct AVPacket *next_packet( lavf_source_t *h )+{+ AVPacket *pkt = calloc( 1, sizeof( AVPacket ) );++ int ret;+ do+ {+ if( pkt->data )+ av_free_packet( pkt );+ if( (ret = av_read_frame( h->lavf, pkt )) )+ {+ if( ret != AVERROR_EOF )+ AF_LOG_ERR( h, "read error: %s\n", strerror( -ret ) );+ else+ AF_LOG( h, X264_LOG_INFO, "end of file reached\n" );+ free_avpacket( pkt );+ return NULL;+ }+ }+ while( pkt->stream_index != h->track );++ return pkt;+}++static int low_decode_audio( lavf_source_t *h, uint8_t *buf, intptr_t buflen )+{+ static AVPacket pkt_temp;+ static uint8_t desync_warn = 0;++ int len = 0, datalen = 0;++ while( h->pkt && pkt_temp.size > 0 )+ {+ datalen = buflen;+ len = avcodec_decode_audio3( h->ctx, (int16_t*) buf, &datalen, &pkt_temp );++ if( len < 0 ) {+ // Broken frame, drop+ if( !desync_warn++ ) // repeat the warning every 256 errors+ AF_LOG_WARN( h, "Decoding errors may cause audio desync\n" );+ pkt_temp.size = 0;+ break;+ }++ pkt_temp.data += len;+ pkt_temp.size -= len;++ if( datalen < 0 )+ continue;++ return datalen;+ }++ free_avpacket( h->pkt );+ h->pkt = next_packet( h );++ if( !h->pkt )+ return -1;++ pkt_temp.data = h->pkt->data;+ pkt_temp.size = h->pkt->size;++ return 0;+}++static struct AVPacket *decode_next_frame( lavf_source_t *h )+{+ AVPacket *dst = calloc( 1, sizeof( AVPacket ) );+ assert( !av_new_packet( dst, AVCODEC_MAX_AUDIO_FRAME_SIZE ) );++ int len = 0;+ while( ( len = low_decode_audio( h, dst->data, dst->size ) ) == 0 )+ {+ // Read more+ }+ if( len < 0 ) // EOF or demuxing error+ {+ free_avpacket( dst );+ return NULL;+ }++ dst->size = len;++ return dst;+}++static int buffer_next_frame( lavf_source_t *h )+{+ AVPacket *dec = decode_next_frame( h );+ if( !dec )+ return 0;++ if( h->len + dec->size > h->bufsize )+ {+ memmove( h->buffer, h->buffer + dec->size, h->bufsize - dec->size );+ h->len -= dec->size;+ h->bytepos += dec->size;+ }+ memcpy( h->buffer + h->len, dec->data, dec->size );+ h->len += dec->size;++ free_avpacket( dec );++ return 1;+}++static inline int not_in_cache( lavf_source_t *h, int64_t sample )+{+ int64_t samplebyte = sample * h->info.samplesize;+ if( samplebyte < h->bytepos )+ return -1; // before+ else if( samplebyte < h->bytepos + h->len )+ return 0; // in cache+ return 1; // after+}++static int64_t fill_buffer_until( lavf_source_t *h, int64_t lastsample )+{+ static int errored = 0;+ if( errored )+ return -1;+ if( not_in_cache( h, lastsample ) < 0 )+ {+ AF_LOG_ERR( h, "backwards seeking not supported yet "+ "(requested sample %"PRIu64", first available is %"PRIu64")\n",+ lastsample, h->bytepos / h->info.samplesize );+ return -1;+ }+ int ret;+ while( ( ret = not_in_cache( h, lastsample ) ) > 0 )+ {+ if( !buffer_next_frame( h ) )+ {+ // libavcodec already warns for us+ errored = 1;+ break;+ }+ }+ assert( ret >= 0 );+ return h->bytepos + h->len;+}+++static struct audio_packet_t *get_samples( hnd_t handle, int64_t first_sample, int64_t last_sample )+{+ lavf_source_t *h = handle;+ assert( first_sample >= 0 && last_sample > first_sample );++ if( fill_buffer_until( h, first_sample ) < 0 )+ return NULL;++ audio_packet_t *pkt = calloc( 1, sizeof( audio_packet_t ) );+ pkt->channels = h->info.channels;+ pkt->samplecount = last_sample - first_sample;+ pkt->size = pkt->samplecount * h->info.samplesize;++ if( pkt->size + h->surplus > h->bufsize )+ {+ int64_t pivot = first_sample + ( h->bufsize - h->surplus * 2 ) / h->info.samplesize;+ int64_t expected_size = ( pivot - first_sample ) * h->info.samplesize;++ audio_packet_t *prev = get_samples( h, first_sample, pivot );+ if( !prev )+ goto fail;++ if( prev->size < expected_size ) // EOF+ {+ af_free_packet( pkt );+ prev->flags |= AUDIO_FLAG_EOF;+ return prev;+ }+ assert( prev->size == expected_size );++ audio_packet_t *next = get_samples( h, pivot, last_sample );+ if( !next )+ {+ af_free_packet( prev );+ goto fail;+ }++ pkt->data = af_dup_buffer( prev->data, prev->channels, prev->samplecount );+ af_cat_buffer( pkt->data, pkt->samplecount, next->data, next->samplecount, pkt->channels );++ pkt->samplecount = prev->samplecount + next->samplecount;+ pkt->size = prev->size + next->size;++ af_free_packet( prev );+ af_free_packet( next );+ }+ else+ {+ int64_t lastreq = last_sample * h->info.samplesize;+ int64_t lastavail = fill_buffer_until( h, last_sample );+ if( lastavail < 0 )+ goto fail;++ intptr_t start = ( first_sample * h->info.samplesize ) - h->bytepos;++ if( lastavail < lastreq )+ {+ pkt->size = lastavail - h->bytepos - start;+ pkt->samplecount = pkt->size / h->info.samplesize;+ pkt->flags = AUDIO_FLAG_EOF;+ }+ assert( start + pkt->size <= h->bufsize );+ pkt->data = af_deinterleave2( h->buffer + start, h->samplefmt, pkt->channels, pkt->samplecount );+ }++ return pkt;++fail:+ af_free_packet( pkt );+ return NULL;+}++static void lavf_close( hnd_t handle )+{+ assert( handle );+ lavf_source_t *h = handle;+ av_free( h->buffer );+ free_avpacket( h->pkt );+ avcodec_close( h->ctx );+ av_close_input_file( h->lavf );+ free( h );+}++const audio_filter_t audio_filter_lavf =+{+ .name = "lavf",+ .description = "Demuxes and decodes audio files using libavformat + libavcodec",+ .help = "Arguments: filename[:track]",+ .init = init,+ .get_samples = get_samples,+ .free_packet = free_packet,+ .close = lavf_close+};diff --git a/input/ffms.c b/input/ffms.cindex 84118d5..861cddc 100644--- a/input/ffms.c+++ b/input/ffms.c@@ -35,8 +35,13 @@ #define SetConsoleTitle(t) #endif +#if HAVE_AUDIO+#include "audio/audio.h"+#endif+ typedef struct {+ char *filename; FFMS_VideoSource *video_source; FFMS_Track *track; int reduce_pts;@@ -85,6 +90,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c int trackno = FFMS_GetFirstTrackOfType( idx, FFMS_TYPE_VIDEO, &e ); FAIL_IF_ERROR( trackno < 0, "could not find video track\n" ) + h->filename = strdup( psz_filename ); h->video_source = FFMS_CreateVideoSource( psz_filename, trackno, idx, 1, seekmode, &e ); FAIL_IF_ERROR( !h->video_source, "could not create video source\n" ) @@ -173,8 +179,19 @@ static int close_file( hnd_t handle ) { ffms_hnd_t *h = handle; FFMS_DestroyVideoSource( h->video_source );+ free( h->filename ); free( h ); return 0; } +#if HAVE_AUDIO+static hnd_t open_audio( hnd_t handle, int track )+{+ ffms_hnd_t *h = handle;+ return audio_open_from_file( NULL, h->filename, track );+}++const cli_input_t ffms_input = { open_file, picture_alloc, read_frame, NULL, picture_clean, close_file, open_audio };+#else const cli_input_t ffms_input = { open_file, picture_alloc, read_frame, NULL, picture_clean, close_file };+#endifdiff --git a/input/input.h b/input/input.hindex bb1bfb7..18f0a40 100644--- a/input/input.h+++ b/input/input.h@@ -83,6 +83,9 @@ typedef struct int (*release_frame)( cli_pic_t *pic, hnd_t handle ); void (*picture_clean)( cli_pic_t *pic ); int (*close_file)( hnd_t handle );+#if HAVE_AUDIO+ hnd_t (*open_audio)( hnd_t handle, int track );+#endif } cli_input_t; extern const cli_input_t raw_input;diff --git a/input/lavf.c b/input/lavf.cindex e1dd352..38fd405 100644--- a/input/lavf.c+++ b/input/lavf.c@@ -27,8 +27,13 @@ #include <libavformat/avformat.h> #include <libavutil/pixdesc.h> +#if HAVE_AUDIO+#include "audio/audio.h"+#endif+ typedef struct {+ char *filename; AVFormatContext *lavf; int stream_id; int next_frame;@@ -134,6 +139,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c if( param ) free( param ); FAIL_IF_ERROR( av_find_stream_info( h->lavf ) < 0, "could not find input stream info\n" )+ h->filename = strdup( psz_filename ); int i = 0; while( i < h->lavf->nb_streams && h->lavf->streams[i]->codec->codec_type != CODEC_TYPE_VIDEO )@@ -211,8 +217,24 @@ static int close_file( hnd_t handle ) lavf_hnd_t *h = handle; avcodec_close( h->lavf->streams[h->stream_id]->codec ); av_close_input_file( h->lavf );+ free( h->filename ); free( h ); return 0; } +#if HAVE_AUDIO+static hnd_t open_audio( hnd_t handle, int track )+{+ lavf_hnd_t *h = handle;+ if ( !x264_is_regular_file_path( h->filename ) )+ {+ x264_cli_log( "lavf", X264_LOG_WARNING, "reading audio from non-regular files is not implemented yet.\n" );+ return 0;+ }+ return audio_open_from_file( NULL, h->filename, track );+}++const cli_input_t lavf_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file, open_audio };+#else const cli_input_t lavf_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file };+#endifdiff --git a/output/flv.c b/output/flv.cindex 9831a5b..084ed69 100644--- a/output/flv.c+++ b/output/flv.c@@ -27,6 +27,20 @@ do {\ return -1;\ } while( 0 ) +#if HAVE_AUDIO+typedef struct+{+ audio_info_t *info;+ hnd_t encoder;+ int header;+ int codecid;+ int stereo;+ int64_t lastdts;+ int64_t step_num;+ int64_t step_den;+} flv_audio_hnd_t;+#endif+ typedef struct { flv_buffer *c;@@ -52,22 +66,116 @@ typedef struct int b_vfr_input; unsigned start;++#if HAVE_AUDIO+ flv_audio_hnd_t *a_flv;+#endif } flv_hnd_t; -static int write_header( flv_buffer *c )+#if HAVE_AUDIO+static int audio_init( hnd_t handle, hnd_t filters, char *audio_enc, char *audio_parameters ) {- x264_put_tag( c, "FLV" ); // Signature- x264_put_byte( c, 1 ); // Version- x264_put_byte( c, 1 ); // Video Only- x264_put_be32( c, 9 ); // DataOffset- x264_put_be32( c, 0 ); // PreviousTagSize0+ if( !strcmp( audio_enc, "none" ) || !filters )+ return 0;++ // TODO: support adpcm_swf, pcm and aac+ const audio_encoder_t *encoder = select_audio_encoder( audio_enc, (char*[]){ "mp3", "raw", NULL } );+ FAIL_IF_ERR( !encoder, "flv", "unable to select audio encoder\n" );++ hnd_t enc;+ FAIL_IF_ERR( !(enc = audio_encoder_open( encoder, filters, audio_parameters )), "flv", "error opening audio encoder" );+ flv_hnd_t *p_flv = handle;+ flv_audio_hnd_t *a_flv = p_flv->a_flv = calloc( 1, sizeof( flv_audio_hnd_t ) );+ a_flv->lastdts = INT64_MIN;+ audio_info_t *info = a_flv->info = audio_encoder_info( enc );++ int header = 0;+ if ( !strcmp( info->codec_name, "raw" ) )+ a_flv->codecid = FLV_CODECID_RAW;+ else if( !strcmp( info->codec_name, "mp3" ) )+ a_flv->codecid = FLV_CODECID_MP3;++ header |= a_flv->codecid;+ a_flv->stereo = info->channels == 2;++ switch( info->samplerate )+ {+ case 5512:+ case 8000:+ header |= FLV_SAMPLERATE_SPECIAL;+ break;+ case 11025:+ header |= FLV_SAMPLERATE_11025HZ;+ break;+ case 22050:+ header |= FLV_SAMPLERATE_22050HZ;+ break;+ case 44100:+ header |= FLV_SAMPLERATE_44100HZ;+ break;+ default:+ x264_cli_log( "flv", X264_LOG_ERROR, "unsupported %dhz sample rate\n", info->samplerate );+ goto error;+ }++ switch( info->chansize )+ {+ case 1:+ header |= FLV_SAMPLESSIZE_8BIT;+ break;+ case 2:+ header |= FLV_SAMPLESSIZE_16BIT;+ break;+ default:+ x264_cli_log( "flv", X264_LOG_ERROR, "%d-bit audio not supported\n", (int) info->chansize * 8 );+ goto error;+ }++ switch( info->channels )+ {+ case 1:+ header |= FLV_MONO;+ break;+ case 2:+ header |= FLV_STEREO;+ break;+ default:+ x264_cli_log( "flv", X264_LOG_ERROR, "%d-channel audio not supported\n", info->channels );+ goto error;+ }++ a_flv->header = header;+ a_flv->step_num = a_flv->info->framelen * 1000;+ a_flv->step_den = a_flv->info->samplerate;++ a_flv->encoder = enc;++ return 1;++ error:+ audio_encoder_close( enc );+ free( p_flv->a_flv );+ p_flv->a_flv = NULL;++ return -1;+}+#endif++static int write_header( flv_buffer *c, int audio )+{+ x264_put_tag( c, "FLV" ); // Signature+ x264_put_byte( c, 1 ); // Version+ x264_put_byte( c, 1 | (audio ? 4 : 0) ); // Video + Audio (if requested)+ x264_put_be32( c, 9 ); // DataOffset+ x264_put_be32( c, 0 ); // PreviousTagSize 0 return flv_flush_data( c ); } -static int open_file( char *psz_filename, hnd_t *p_handle )+static int open_file( char *psz_filename, hnd_t *p_handle, hnd_t audio_filters, char *audio_enc, char *audio_params ) { flv_hnd_t *p_flv = malloc( sizeof(*p_flv) );+ *p_handle = NULL; if( !p_flv ) return -1;@@ -77,10 +185,19 @@ static int open_file( char *psz_filename, hnd_t *p_handle ) if( !p_flv->c ) return -1; - CHECK( write_header( p_flv->c ) );+ int ret = 0;+#if HAVE_AUDIO+ ret = audio_init( p_flv, audio_filters, audio_enc, audio_params );+ FAIL_IF_ERR( ret < 0, "flv", "unable to init audio output" );+#endif+ CHECK( write_header( p_flv->c, ret ) ); *p_handle = p_flv; +#if HAVE_AUDIO+ return ret;+#else return 0;+#endif } static int set_param( hnd_t handle, x264_param_t *p_param )@@ -132,6 +249,21 @@ static int set_param( hnd_t handle, x264_param_t *p_param ) p_flv->i_bitrate_pos = c->d_cur + c->d_total + 1; x264_put_amf_double( c, 0 ); // written at end of encoding +#if HAVE_AUDIO+ if( p_flv->a_flv )+ {+ flv_audio_hnd_t *a_flv = p_flv->a_flv;+ x264_put_amf_string( c, "audiocodecid" );+ x264_put_amf_double( c, a_flv->codecid >> FLV_AUDIO_CODECID_OFFSET );+ x264_put_amf_string( c, "audiosamplesize" );+ x264_put_amf_double( c, a_flv->info->chansize );+ x264_put_amf_string( c, "audiosamplerate" );+ x264_put_amf_double( c, a_flv->info->samplerate );+ x264_put_amf_string( c, "stereo" );+ x264_put_amf_bool ( c, a_flv->stereo );+ }+#endif+ x264_put_amf_string( c, "" ); x264_put_byte( c, AMF_END_OF_OBJECT ); @@ -152,6 +284,9 @@ static int set_param( hnd_t handle, x264_param_t *p_param ) static int write_headers( hnd_t handle, x264_nal_t *p_nal ) { flv_hnd_t *p_flv = handle;+#if HAVE_AUDIO+ flv_audio_hnd_t *a_flv = p_flv->a_flv;+#endif flv_buffer *c = p_flv->c; int sps_size = p_nal[0].i_payload;@@ -202,11 +337,82 @@ static int write_headers( hnd_t handle, x264_nal_t *p_nal ) unsigned length = c->d_cur - p_flv->start; rewrite_amf_be24( c, length, p_flv->start - 10 ); x264_put_be32( c, length + 11 ); // Last tag size++#if HAVE_AUDIO+ if( a_flv && a_flv->codecid == FLV_CODECID_AAC )+ {+ FAIL_IF_ERR( !a_flv->info->extradata, "flv", "audio codec is AAC but extradata is NULL\n" );+ x264_put_byte( c, FLV_TAG_TYPE_AUDIO );+ x264_put_be24( c, 2 + a_flv->info->extradata_size );+ x264_put_be24( c, 0 );+ x264_put_byte( c, 0 );+ x264_put_be24( c, 0 );++ x264_put_byte( c, a_flv->header );+ x264_put_byte( c, 0 );+ flv_append_data( c, a_flv->info->extradata, a_flv->info->extradata_size );+ x264_put_be32( c, 11 + 2 + a_flv->info->extradata_size );+ }+#endif+ CHECK( flv_flush_data( c ) ); return sei_size + sps_size + pps_size; } +#if HAVE_AUDIO+static int write_audio( flv_hnd_t *p_flv, int64_t video_dts, int finish )+{+ flv_audio_hnd_t *a_flv = p_flv->a_flv;+ flv_buffer *c = p_flv->c;++ assert( a_flv );++ int aac = a_flv->codecid == FLV_CODECID_AAC;+ if( a_flv->lastdts == INT64_MIN )+ {+ if( video_dts > 0 )+ audio_encoder_skip_samples( a_flv->encoder, video_dts * a_flv->info->samplerate / 1000 );+ a_flv->lastdts = video_dts; // first frame (nonzero if --seek is used)+ }+ audio_packet_t *frame;+ int frames = 0;+ while( a_flv->lastdts <= video_dts || video_dts < 0 )+ {+ if( finish )+ frame = audio_encoder_finish( a_flv->encoder );+ else if( !(frame = audio_encode_frame( a_flv->encoder )) )+ {+ finish = 1;+ continue;+ }++ if( !frame ) break;++ a_flv->lastdts += a_flv->step_num / a_flv->step_den;++ x264_put_byte( c, FLV_TAG_TYPE_AUDIO );+ x264_put_be24( c, 1 + aac + frame->size );+ x264_put_be24( c, (int32_t) a_flv->lastdts );+ x264_put_byte( c, (int32_t) a_flv->lastdts >> 24 );+ x264_put_be24( c, 0 );++ x264_put_byte( c, a_flv->header );+ if( aac )+ x264_put_byte( c, 1 );+ flv_append_data( c, frame->rawdata, frame->size );++ x264_put_be32( c, 11 + 1 + aac + frame->size );++ audio_free_frame( a_flv->encoder, frame );++ CHECK( flv_flush_data( c ) );+ ++frames;+ }+ return frames;+}+#endif+ static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture ) { flv_hnd_t *p_flv = handle;@@ -236,6 +442,10 @@ static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_ p_flv->i_prev_dts = p_picture->i_dts; p_flv->i_prev_pts = p_picture->i_pts; +#if HAVE_AUDIO+ FAIL_IF_ERR( p_flv->a_flv && write_audio( p_flv, dts, 0 ) < 0, "flv", "error writing audio\n" );+#endif+ // A new frame - write packet header x264_put_byte( c, FLV_TAG_TYPE_VIDEO ); x264_put_be24( c, 0 ); // calculated later@@ -278,6 +488,14 @@ static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest flv_hnd_t *p_flv = handle; flv_buffer *c = p_flv->c; +#if HAVE_AUDIO+ if( p_flv->a_flv )+ {+ FAIL_IF_ERR( p_flv->a_flv && write_audio( p_flv, -1, 1 ) < 0, "flv", "error flushing audio\n" );+ audio_encoder_close( p_flv->a_flv->encoder );+ }+#endif+ CHECK( flv_flush_data( c ) ); double total_duration = (double)(2 * largest_pts - second_largest_pts) * p_flv->i_timebase_num / p_flv->i_timebase_den;@@ -299,6 +517,11 @@ static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest } fclose( c->fp );++#if HAVE_AUDIO+ if( p_flv->a_flv )+ free( p_flv->a_flv );+#endif free( p_flv ); free( c ); diff --git a/output/flv_bytestream.c b/output/flv_bytestream.cindex e02476c..2176786 100644--- a/output/flv_bytestream.c+++ b/output/flv_bytestream.c@@ -78,6 +78,12 @@ void x264_put_amf_double( flv_buffer *c, double d ) x264_put_be64( c, dbl2int( d ) ); } +void x264_put_amf_bool( flv_buffer *c, int i )+{+ x264_put_byte( c, AMF_DATA_TYPE_BOOL );+ x264_put_byte( c, !!i );+}+ /* flv writing functions */ flv_buffer *flv_create_writer( const char *filename )diff --git a/output/flv_bytestream.h b/output/flv_bytestream.hindex 00f37fe..db06b17 100644--- a/output/flv_bytestream.h+++ b/output/flv_bytestream.h@@ -74,8 +74,11 @@ enum enum {- FLV_CODECID_MP3 = 2 << FLV_AUDIO_CODECID_OFFSET,- FLV_CODECID_AAC = 10<< FLV_AUDIO_CODECID_OFFSET,+ FLV_CODECID_RAW = 0 << FLV_AUDIO_CODECID_OFFSET,+ FLV_CODECID_ADPCM = 1 << FLV_AUDIO_CODECID_OFFSET,+ FLV_CODECID_MP3 = 2 << FLV_AUDIO_CODECID_OFFSET,+ FLV_CODECID_PCM = 3 << FLV_AUDIO_CODECID_OFFSET,+ FLV_CODECID_AAC = 10 << FLV_AUDIO_CODECID_OFFSET, }; enum@@ -131,5 +134,6 @@ void x264_put_be24( flv_buffer *c, uint32_t val ); void x264_put_tag( flv_buffer *c, const char *tag ); void x264_put_amf_string( flv_buffer *c, const char *str ); void x264_put_amf_double( flv_buffer *c, double d );+void x264_put_amf_bool( flv_buffer *c, int b ); #endifdiff --git a/output/matroska.c b/output/matroska.cindex a1219d0..d077162 100644--- a/output/matroska.c+++ b/output/matroska.c@@ -35,10 +35,13 @@ typedef struct } mkv_hnd_t; -static int open_file( char *psz_filename, hnd_t *p_handle )+static int open_file( char *psz_filename, hnd_t *p_handle, hnd_t audio_filters, char *audio_enc, char *audio_params ) { mkv_hnd_t *p_mkv; + FAIL_IF_ERR( audio_enc && ( strcmp( audio_enc, "none" ) && strcmp( audio_enc, "auto" ) ), "matroska",+ "audio is not yet supported on this muxer\n" );+ *p_handle = NULL; p_mkv = malloc( sizeof(*p_mkv) );diff --git a/output/mp4.c b/output/mp4.cindex f2ff5be..b97adcd 100644--- a/output/mp4.c+++ b/output/mp4.c@@ -155,10 +155,14 @@ static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest return 0; } -static int open_file( char *psz_filename, hnd_t *p_handle )+static int open_file( char *psz_filename, hnd_t *p_handle, hnd_t audio_filters, char *audio_enc, char *audio_params ) { mp4_hnd_t *p_mp4; ++ FAIL_IF_ERR( audio_enc && ( strcmp( audio_enc, "none" ) && strcmp( audio_enc, "auto" ) ), "mp4",+ "audio is not yet supported on this muxer\n" );+ *p_handle = NULL; FILE *fh = fopen( psz_filename, "w" ); if( !fh )diff --git a/output/output.h b/output/output.hindex 094fefc..64c53fd 100644--- a/output/output.h+++ b/output/output.h@@ -25,10 +25,13 @@ #define X264_OUTPUT_H #include "x264cli.h"+#if HAVE_AUDIO+#include "audio/encoders.h"+#endif typedef struct {- int (*open_file)( char *psz_filename, hnd_t *p_handle );+ int (*open_file)( char *psz_filename, hnd_t *p_handle, hnd_t audio_filters, char *audio_encoder, char *audio_parameters ); int (*set_param)( hnd_t handle, x264_param_t *p_param ); int (*write_headers)( hnd_t handle, x264_nal_t *p_nal ); int (*write_frame)( hnd_t handle, uint8_t *p_nal, int i_size, x264_picture_t *p_picture );diff --git a/output/raw.c b/output/raw.cindex fc418fb..d7f627a 100644--- a/output/raw.c+++ b/output/raw.c@@ -23,8 +23,11 @@ #include "output.h" -static int open_file( char *psz_filename, hnd_t *p_handle )+static int open_file( char *psz_filename, hnd_t *p_handle, hnd_t audio_filters, char *audio_enc, char *audio_params ) {+ FAIL_IF_ERR( audio_enc && ( strcmp( audio_enc, "none" ) && strcmp( audio_enc, "auto" ) ), "raw",+ "audio is not supported on this muxer\n" );+ if( !strcmp( psz_filename, "-" ) ) *p_handle = stdout; else if( !(*p_handle = fopen( psz_filename, "w+b" )) )diff --git a/x264.c b/x264.cindex 2f4263e..d1c450e 100644--- a/x264.c+++ b/x264.c@@ -32,6 +32,9 @@ #include "common/common.h" #include "x264cli.h"+#if HAVE_AUDIO+#include "audio/audio.h"+#endif #include "input/input.h" #include "output/output.h" #include "filters/filters.h"@@ -64,6 +67,10 @@ typedef struct { int i_seek; hnd_t hin; hnd_t hout;+#if HAVE_AUDIO+ hnd_t haud;+ hnd_t haenc;+#endif FILE *qpfile; FILE *tcfile_out; double timebase_convert_multiplier;@@ -652,6 +659,26 @@ static void Help( x264_param_t *defaults, int longhelp ) H2( " --pic-struct Force pic_struct in Picture Timing SEI\n" ); H0( "\n" );+ H0( "Audio:\n" );+#if HAVE_AUDIO+ H0( "Audio is automatically opened from the input file if supported by the demuxer.\n" );+ H0( " --audiofile <filename> Uses audio from the specified file\n" );+ H0( " --acodec <string> Specifies the audio codec [auto].");+ H1( " Supported codecs:\n" );+#define CODEC( test, name ) if( test ) \+ H1( " - " name "\n" )+ CODEC( 1 , "auto" );+ CODEC( 1 , "none" );+ CODEC( 1 , "raw" );+ CODEC( HAVE_LAME, "mp3" );+#undef CODEC+ H0( "\n" );+ H0( " --abitrate <integer> Enable bitrate mode and specifies bitrate\n" );+ H0( " --aquality <float> Specifies audio quality [6]\n" );+#else /* HAVE_AUDIO */+ H0( "Audio support was not compiled in.\n" );+#endif+ H0( "\n" ); H0( "Input/Output:\n" ); H0( "\n" ); H0( " -o, --output Specify output file\n" );@@ -730,7 +757,11 @@ enum { OPT_LOG_LEVEL, OPT_VIDEO_FILTER, OPT_INPUT_RES,- OPT_INPUT_CSP+ OPT_INPUT_CSP,+ OPT_AUDIOFILE,+ OPT_AUDIOCODEC,+ OPT_AUDIOBITRATE,+ OPT_AUDIOQUALITY } OptionsOPT; static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw";@@ -882,6 +913,10 @@ static struct option long_options[] = { "video-filter", required_argument, NULL, OPT_VIDEO_FILTER }, { "input-res", required_argument, NULL, OPT_INPUT_RES }, { "input-csp", required_argument, NULL, OPT_INPUT_CSP },+ { "audiofile", required_argument, NULL, OPT_AUDIOFILE },+ { "acodec", required_argument, NULL, OPT_AUDIOCODEC },+ { "abitrate", required_argument, NULL, OPT_AUDIOBITRATE },+ { "aquality", required_argument, NULL, OPT_AUDIOQUALITY }, {0, 0, 0, 0} }; @@ -1094,6 +1129,14 @@ static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) char *preset = NULL; char *tune = NULL; +#if HAVE_AUDIO+ char *audio_enc = "auto";+ char *audio_filename = NULL;+ int audio_bitrate = -1;+ float audio_quality = NAN;+ int audio_enable = 1;+#endif+ x264_param_default( &defaults ); cli_log_level = defaults.i_log_level; @@ -1255,6 +1298,35 @@ static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) case OPT_INPUT_CSP: input_opt.colorspace = optarg; break;+ case OPT_AUDIOCODEC:+#if HAVE_AUDIO+ audio_enc = optarg;+ if( !strcmp( audio_enc, "none" ) )+ audio_enable = 0;+ else FAIL_IF_ERROR( !strcmp( audio_enc, "auto" ) || !encoder_by_name( audio_enc ),+ "audio encoder %s not supported or not compiled in\n" )+ break;+#else+ if( !strcmp( optarg, "none" ) || !strcmp( optarg, "auto" ) )+ break;+#endif+ case OPT_AUDIOFILE:+#if HAVE_AUDIO+ audio_filename = optarg;+ break;+#endif+ case OPT_AUDIOBITRATE:+#if HAVE_AUDIO+ audio_bitrate = atoi( optarg );+ FAIL_IF_ERROR( audio_bitrate <= 0, "bitrate must be > 0.\n" );+ break;+#endif+ case OPT_AUDIOQUALITY:+ FAIL_IF_ERROR( !HAVE_AUDIO, "audio support was not compiled in.\n" );+#if HAVE_AUDIO+ audio_quality = (float) atof( optarg );+ break;+#endif default: generic_option: {@@ -1299,7 +1371,6 @@ generic_option: if( select_output( muxer, output_filename, param ) ) return -1;- FAIL_IF_ERROR( output.open_file( output_filename, &opt->hout ), "could not open output file `%s'\n", output_filename ) input_filename = argv[optind++]; video_info_t info = {0};@@ -1321,12 +1392,49 @@ generic_option: FAIL_IF_ERROR( !opt->hin && input.open_file( input_filename, &opt->hin, &info, &input_opt ), "could not open input file `%s'\n", input_filename ) +#if HAVE_AUDIO+ if( audio_enable )+ {+ if ( audio_filename )+ opt->haud = audio_open_from_file( NULL, audio_filename, TRACK_ANY );+ else if ( input.open_audio )+ opt->haud = input.open_audio( opt->hin, TRACK_ANY );+ else+ {+ x264_cli_log( "x264", X264_LOG_WARNING, "the used input does not support audio and --audiofile was not given, disabling audio.\n" );+ audio_enable = 0;+ }++ if ( audio_filename && !opt->haud )+ return -1;+ }+#endif+ x264_reduce_fraction( &info.sar_width, &info.sar_height ); x264_reduce_fraction( &info.fps_num, &info.fps_den ); x264_cli_log( demuxername, X264_LOG_INFO, "%dx%d%c %d:%d @ %d/%d fps (%cfr)\n", info.width, info.height, info.interlaced ? 'i' : 'p', info.sar_width, info.sar_height, info.fps_num, info.fps_den, info.vfr ? 'v' : 'c' ); +#if HAVE_AUDIO+ char arg[30] = { 0 };+ if( audio_enable )+ {+ if( audio_bitrate > 0 )+ snprintf( arg, 30, "bitrate=%d", audio_bitrate );+ else if( audio_quality == audio_quality ) // not NaN+ snprintf( arg, 30, "vbr=%f", audio_quality );+ }+#endif++ FAIL_IF_ERROR(+#if HAVE_AUDIO+ output.open_file( output_filename, &opt->hout, opt->haud, audio_enc, arg )+#else+ output.open_file( output_filename, &opt->hout, NULL, NULL, NULL )+#endif+ < 0, "could not open output file `%s'\n", output_filename )+ if( tcfile_name ) { FAIL_IF_ERROR( b_user_fps, "--fps + --tcfile-in is incompatible.\n" )@@ -1725,6 +1833,12 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) opt->tcfile_out = NULL; } +#if HAVE_AUDIO+ if( opt->haenc )+ audio_encoder_close( opt->haenc );+ if( opt->haud )+ af_close( opt->haud );+#endif filter.free( opt->hin ); output.close_file( opt->hout, largest_pts, second_largest_pts );