All pastes #1901430 Raw Edit

Anonymous

public diff v1 · immutable
#1901430 ·published 2010-07-15 23:50 UTC
rendered paste body
diff --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 );