From: Kieran Kunhya Date: Sun, 15 Nov 2009 03:01:09 +0000 (-0800) Subject: FLV muxing support X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=25a029458f70ac1bee8369bee321c8fdcf166f18;p=libx264 FLV muxing support --- diff --git a/Makefile b/Makefile index 2fe33616..5a6d358d 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,8 @@ SRCS = common/mc.c common/predict.c common/pixel.c common/macroblock.c \ encoder/cavlc.c encoder/encoder.c encoder/lookahead.c SRCCLI = x264.c input/yuv.c input/y4m.c output/raw.c \ - output/matroska.c output/matroska_ebml.c + output/matroska.c output/matroska_ebml.c \ + output/flv.c output/flv_bytestream.c MUXERS := $(shell grep -E "(IN|OUT)PUT" config.h) diff --git a/common/osdep.h b/common/osdep.h index 2f9e1af0..eb48dcc8 100644 --- a/common/osdep.h +++ b/common/osdep.h @@ -151,6 +151,7 @@ static inline int x264_pthread_create( x264_pthread_t *t, void *a, void *(*f)(vo #ifdef WORDS_BIGENDIAN #define endian_fix(x) (x) +#define endian_fix64(x) (x) #define endian_fix32(x) (x) #define endian_fix16(x) (x) #else @@ -160,31 +161,34 @@ static ALWAYS_INLINE uint32_t endian_fix32( uint32_t x ) asm("bswap %0":"+r"(x)); return x; } -static ALWAYS_INLINE intptr_t endian_fix( intptr_t x ) -{ - asm("bswap %0":"+r"(x)); - return x; -} #elif defined(__GNUC__) && defined(HAVE_ARMV6) -static ALWAYS_INLINE intptr_t endian_fix( intptr_t x ) +static ALWAYS_INLINE uint32_t endian_fix32( uint32_t x ) { asm("rev %0, %0":"+r"(x)); return x; } -#define endian_fix32 endian_fix #else static ALWAYS_INLINE uint32_t endian_fix32( uint32_t x ) { return (x<<24) + ((x<<8)&0xff0000) + ((x>>8)&0xff00) + (x>>24); } -static ALWAYS_INLINE intptr_t endian_fix( intptr_t x ) +#endif +#if defined(__GNUC__) && defined(ARCH_X86_64) +static ALWAYS_INLINE uint64_t endian_fix64( uint64_t x ) { - if( WORD_SIZE == 8 ) - return endian_fix32(x>>32) + ((uint64_t)endian_fix32(x)<<32); - else - return endian_fix32(x); + asm("bswap %0":"+r"(x)); + return x; +} +#else +static ALWAYS_INLINE uint64_t endian_fix64( uint64_t x ) +{ + return endian_fix32(x>>32) + ((uint64_t)endian_fix32(x)<<32); } #endif +static ALWAYS_INLINE intptr_t endian_fix( intptr_t x ) +{ + return WORD_SIZE == 8 ? endian_fix64(x) : endian_fix32(x); +} static ALWAYS_INLINE uint16_t endian_fix16( uint16_t x ) { return (x<<8)|(x>>8); diff --git a/configure b/configure index 5e893958..d08c548b 100755 --- a/configure +++ b/configure @@ -348,9 +348,13 @@ fi CFLAGS="$CFLAGS -DARCH_$ARCH -DSYS_$SYS" -echo "unsigned int endian = 'B' << 24 | 'I' << 16 | 'G' << 8 | 'E';" > conftest.c +echo "int i = 0x42494745; double f = 0x1.0656e6469616ep+102;" > conftest.c $CC $CFLAGS conftest.c -c -o conftest.o 2>$DEVNULL || die "endian test failed" -grep -q BIGE conftest.o && CFLAGS="$CFLAGS -DWORDS_BIGENDIAN" +if grep -q BIGE conftest.o && grep -q FPendian conftest.o ; then + CFLAGS="$CFLAGS -DWORDS_BIGENDIAN" +elif !(grep -q EGIB conftest.o && grep -q naidnePF conftest.o) ; then + die "endian test failed" +fi # autodetect options that weren't forced nor disabled diff --git a/output/flv.c b/output/flv.c new file mode 100644 index 00000000..30b609b6 --- /dev/null +++ b/output/flv.c @@ -0,0 +1,294 @@ +/***************************************************************************** + * flv.c: + ***************************************************************************** + * Copyright (C) 2009 Kieran Kunhya + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" +#include "flv_bytestream.h" +#define CHECK(x)\ +do {\ + if( (x) < 0 )\ + return -1;\ +} while( 0 ) + +typedef struct +{ + flv_buffer *c; + + uint8_t b_sps; + uint8_t b_pps; + uint8_t *sei; + int sei_len; + + int64_t i_fps_num; + int64_t i_fps_den; + int64_t i_init_delay; + int64_t i_framenum; + int64_t i_mspf; + + uint64_t i_duration_pos; + uint64_t i_filesize_pos; + uint64_t i_bitrate_pos; + + uint8_t b_write_length; + + unsigned start; +} flv_hnd_t; + +static int write_header( flv_buffer *c ) +{ + put_tag( c, "FLV" ); // Signature + put_byte( c, 1 ); // Version + put_byte( c, 1 ); // Video Only + put_be32( c, 9 ); // DataOffset + put_be32( c, 0 ); // PreviousTagSize0 + + return flv_flush_data( c ); +} + +static int open_file( char *psz_filename, hnd_t *p_handle ) +{ + flv_hnd_t *p_flv = malloc( sizeof(*p_flv) ); + *p_handle = NULL; + if( !p_flv ) + return -1; + memset( p_flv, 0, sizeof(*p_flv) ); + + p_flv->c = flv_create_writer( psz_filename ); + if( !p_flv->c ) + return -1; + + CHECK( write_header( p_flv->c ) ); + *p_handle = p_flv; + + return 0; +} + +static int set_param( hnd_t handle, x264_param_t *p_param ) +{ + flv_hnd_t *p_flv = handle; + flv_buffer *c = p_flv->c; + + put_byte( c, FLV_TAG_TYPE_META ); // Tag Type "script data" + + int start = c->d_cur; + put_be24( c, 0 ); // data length + put_be24( c, 0 ); // timestamp + put_be32( c, 0 ); // reserved + + put_byte( c, AMF_DATA_TYPE_STRING ); + put_amf_string( c, "onMetaData" ); + + put_byte( c, AMF_DATA_TYPE_MIXEDARRAY ); + put_be32( c, 7 ); + + put_amf_string( c, "width" ); + put_amf_double( c, p_param->i_width ); + + put_amf_string( c, "height" ); + put_amf_double( c, p_param->i_height ); + + put_amf_string( c, "framerate" ); + put_amf_double( c, p_param->i_fps_num / p_param->i_fps_den ); + + put_amf_string( c, "videocodecid" ); + put_amf_double( c, FLV_CODECID_H264 ); + + put_amf_string( c, "duration" ); + p_flv->i_duration_pos = c->d_cur + c->d_total + 1; // + 1 because of the following AMF_DATA_TYPE_NUMBER byte + put_amf_double( c, 0 ); // written at end of encoding + + put_amf_string( c, "filesize" ); + p_flv->i_filesize_pos = c->d_cur + c->d_total + 1; + put_amf_double( c, 0 ); // written at end of encoding + + put_amf_string( c, "videodatarate" ); + p_flv->i_bitrate_pos = c->d_cur + c->d_total + 1; + put_amf_double( c, 0 ); // written at end of encoding + + put_amf_string( c, "" ); + put_byte( c, AMF_END_OF_OBJECT ); + + unsigned length = c->d_cur - start; + rewrite_amf_be24( c, length - 10, start ); + + put_be32( c, length + 1 ); // tag length + + p_flv->i_fps_num = p_param->i_fps_num; + p_flv->i_fps_den = p_param->i_fps_den; + p_flv->i_init_delay = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0; + p_flv->i_mspf = 1000 * p_flv->i_fps_den / p_flv->i_fps_num; + + fprintf( stderr, "flv [info]: initial delay %i frames\n", + (int)p_flv->i_init_delay ); + + return 0; +} + +static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture ) +{ + flv_hnd_t *p_flv = handle; + flv_buffer *c = p_flv->c; + uint64_t dts = (uint64_t)p_flv->i_framenum * p_flv->i_mspf; + uint64_t pts = (uint64_t)p_picture->i_pts * p_flv->i_mspf / p_flv->i_fps_den; + uint64_t timestamp = dts + p_flv->i_init_delay * p_flv->i_mspf; + uint64_t offset = p_flv->i_init_delay * p_flv->i_mspf + pts - dts; + uint8_t type = p_nalu[4] & 0x1f; + + switch( type ) + { + // sps + case 0x07: + if( !p_flv->b_sps ) + { + uint8_t *sps = p_nalu + 4; + + put_byte( c, FLV_TAG_TYPE_VIDEO ); + put_be24( c, 0 ); // rewrite later, pps size unknown + put_be24( c, 0 ); // timestamp + put_byte( c, 0 ); // timestamp extended + put_be24( c, 0 ); // StreamID - Always 0 + p_flv->start = c->d_cur; // needed for overwriting length + + put_byte( c, 7 | FLV_FRAME_KEY ); // Frametype and CodecID + put_byte( c, 0 ); // AVC sequence header + put_be24( c, 0 ); // composition time + + put_byte( c, 1 ); // version + put_byte( c, sps[1] ); // profile + put_byte( c, sps[2] ); // profile + put_byte( c, sps[3] ); // level + put_byte( c, 0xff ); // 6 bits reserved (111111) + 2 bits nal size length - 1 (11) + put_byte( c, 0xe1 ); // 3 bits reserved (111) + 5 bits number of sps (00001) + + put_be16( c, i_size - 4 ); + flv_append_data( c, sps, i_size - 4 ); + + p_flv->b_sps = 1; + } + break; + + // pps + case 0x08: + if( !p_flv->b_pps ) + { + put_byte( c, 1 ); // number of pps + put_be16( c, i_size - 4 ); + flv_append_data( c, p_nalu + 4, i_size - 4 ); + + // rewrite data length info + unsigned length = c->d_cur - p_flv->start; + rewrite_amf_be24( c, length, p_flv->start - 10 ); + put_be32( c, length + 11 ); // Last tag size + + p_flv->b_pps = 1; + } + break; + + // slice + case 0x1: + case 0x5: + if( !p_flv->b_write_length ) + { + // A new frame - write packet header + put_byte( c, FLV_TAG_TYPE_VIDEO ); + put_be24( c, 0 ); // calculated later + put_be24( c, timestamp ); + put_byte( c, timestamp >> 24 ); + put_be24( c, 0 ); + + p_flv->start = c->d_cur; + put_byte( c, p_picture->i_type == X264_TYPE_IDR ? FLV_FRAME_KEY : FLV_FRAME_INTER ); + put_byte( c, 1 ); // AVC NALU + put_be24( c, offset ); + + p_flv->b_write_length = 1; + } + if( p_flv->sei ) + { + flv_append_data( c, p_flv->sei, p_flv->sei_len ); + free( p_flv->sei ); + p_flv->sei = NULL; + } + flv_append_data( c, p_nalu, i_size ); + break; + // sei + case 0x6: + /* It is within the spec to write this as-is but for + * mplayer/ffmpeg playback this is deferred until before the first frame */ + + p_flv->sei = malloc( i_size ); + if( !p_flv->sei ) + return -1; + p_flv->sei_len = i_size; + + memcpy( p_flv->sei, p_nalu, i_size ); + break; + } + return i_size; +} + +static int set_eop( hnd_t handle, x264_picture_t *p_picture ) +{ + flv_hnd_t *p_flv = handle; + flv_buffer *c = p_flv->c; + + if( p_flv->b_write_length ) + { + unsigned length = c->d_cur - p_flv->start; + rewrite_amf_be24( c, length, p_flv->start - 10 ); + put_be32( c, 11 + length ); // Last tag size + CHECK( flv_flush_data( c ) ); + p_flv->b_write_length = 0; + } + p_flv->i_framenum++; + + return 0; +} + +static void rewrite_amf_double( FILE *fp, uint64_t position, double value ) +{ + uint64_t x = endian_fix64( dbl2int( value ) ); + fseek( fp, position, SEEK_SET ); + fwrite( &x, 8, 1, fp ); +} + +static int close_file( hnd_t handle ) +{ + flv_hnd_t *p_flv = handle; + flv_buffer *c = p_flv->c; + + CHECK( flv_flush_data( c ) ); + + if( x264_is_regular_file( c->fp ) ) + { + double duration = p_flv->i_fps_den * p_flv->i_framenum / p_flv->i_fps_num; + uint64_t filesize = ftell( c->fp ); + rewrite_amf_double( c->fp, p_flv->i_duration_pos, duration ); + rewrite_amf_double( c->fp, p_flv->i_filesize_pos, filesize ); + rewrite_amf_double( c->fp, p_flv->i_bitrate_pos, filesize * 8 / ( duration * 1000 ) ); + } + + fclose( c->fp ); + free( p_flv ); + free( c ); + + return 0; +} + +cli_output_t flv_output = { open_file, set_param, write_nalu, set_eop, close_file }; diff --git a/output/flv_bytestream.c b/output/flv_bytestream.c new file mode 100644 index 00000000..683946df --- /dev/null +++ b/output/flv_bytestream.c @@ -0,0 +1,153 @@ +/***************************************************************************** + * flv_bytestream.c: + ***************************************************************************** + * Copyright (C) 2009 Kieran Kunhya + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include +#include +#include +#include "common/common.h" +#include "flv_bytestream.h" + +uint64_t dbl2int( double value ) +{ + return (union {double f; uint64_t i;}){value}.i; +} + +/* Put functions */ + +void put_byte( flv_buffer *c, uint8_t b ) +{ + flv_append_data( c, &b, 1 ); +} + +void put_be32( flv_buffer *c, uint32_t val ) +{ + put_byte( c, val >> 24 ); + put_byte( c, val >> 16 ); + put_byte( c, val >> 8 ); + put_byte( c, val ); +} + +void put_be64( flv_buffer *c, uint64_t val ) +{ + put_be32( c, val >> 32 ); + put_be32( c, val ); +} + +void put_be16( flv_buffer *c, uint16_t val ) +{ + put_byte( c, val >> 8 ); + put_byte( c, val ); +} + +void put_be24( flv_buffer *c, uint32_t val ) +{ + put_be16( c, val >> 8 ); + put_byte( c, val ); +} + +void put_tag( flv_buffer *c, const char *tag ) +{ + while( *tag ) + put_byte( c, *tag++ ); +} + +void put_amf_string( flv_buffer *c, const char *str ) +{ + uint16_t len = strlen( str ); + put_be16( c, len ); + flv_append_data( c, (uint8_t*)str, len ); +} + +void put_amf_double( flv_buffer *c, double d ) +{ + put_byte( c, AMF_DATA_TYPE_NUMBER ); + put_be64( c, dbl2int( d ) ); +} + +/* flv writing functions */ + +flv_buffer *flv_create_writer( const char *filename ) +{ + flv_buffer *c = malloc( sizeof(*c) ); + + if( !c ) + return NULL; + memset( c, 0, sizeof(*c) ); + + if( !strcmp( filename, "-" ) ) + c->fp = stdout; + else + c->fp = fopen( filename, "wb" ); + if( !c->fp ) + { + free( c ); + return NULL; + } + + return c; +} + +int flv_append_data( flv_buffer *c, uint8_t *data, unsigned size ) +{ + unsigned ns = c->d_cur + size; + + if( ns > c->d_max ) + { + void *dp; + unsigned dn = 16; + while( ns > dn ) + dn <<= 1; + + dp = realloc( c->data, dn ); + if( !dp ) + return -1; + + c->data = dp; + c->d_max = dn; + } + + memcpy( c->data + c->d_cur, data, size ); + + c->d_cur = ns; + + return 0; +} + +void rewrite_amf_be24( flv_buffer *c, unsigned length, unsigned start ) +{ + *(c->data + start + 0) = length >> 16; + *(c->data + start + 1) = length >> 8; + *(c->data + start + 2) = length >> 0; +} + +int flv_flush_data( flv_buffer *c ) +{ + if( !c->d_cur ) + return 0; + + if( fwrite( c->data, c->d_cur, 1, c->fp ) != 1 ) + return -1; + + c->d_total += c->d_cur; + + c->d_cur = 0; + + return 0; +} diff --git a/output/flv_bytestream.h b/output/flv_bytestream.h new file mode 100644 index 00000000..741dfbb4 --- /dev/null +++ b/output/flv_bytestream.h @@ -0,0 +1,135 @@ +/***************************************************************************** + * flv_bytestream.h: + ***************************************************************************** + * Copyright (C) 2009 Kieran Kunhya + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef X264_FLV_BYTESTREAM_H +#define X264_FLV_BYTESTREAM_H + +/* offsets for packed values */ +#define FLV_AUDIO_SAMPLESSIZE_OFFSET 1 +#define FLV_AUDIO_SAMPLERATE_OFFSET 2 +#define FLV_AUDIO_CODECID_OFFSET 4 + +#define FLV_VIDEO_FRAMETYPE_OFFSET 4 + +/* bitmasks to isolate specific values */ +#define FLV_AUDIO_CHANNEL_MASK 0x01 +#define FLV_AUDIO_SAMPLESIZE_MASK 0x02 +#define FLV_AUDIO_SAMPLERATE_MASK 0x0c +#define FLV_AUDIO_CODECID_MASK 0xf0 + +#define FLV_VIDEO_CODECID_MASK 0x0f +#define FLV_VIDEO_FRAMETYPE_MASK 0xf0 + +#define AMF_END_OF_OBJECT 0x09 + +enum +{ + FLV_HEADER_FLAG_HASVIDEO = 1, + FLV_HEADER_FLAG_HASAUDIO = 4, +}; + +enum +{ + FLV_TAG_TYPE_AUDIO = 0x08, + FLV_TAG_TYPE_VIDEO = 0x09, + FLV_TAG_TYPE_META = 0x12, +}; + +enum +{ + FLV_MONO = 0, + FLV_STEREO = 1, +}; + +enum +{ + FLV_SAMPLESSIZE_8BIT = 0, + FLV_SAMPLESSIZE_16BIT = 1 << FLV_AUDIO_SAMPLESSIZE_OFFSET, +}; + +enum +{ + FLV_SAMPLERATE_SPECIAL = 0, /**< signifies 5512Hz and 8000Hz in the case of NELLYMOSER */ + FLV_SAMPLERATE_11025HZ = 1 << FLV_AUDIO_SAMPLERATE_OFFSET, + FLV_SAMPLERATE_22050HZ = 2 << FLV_AUDIO_SAMPLERATE_OFFSET, + FLV_SAMPLERATE_44100HZ = 3 << FLV_AUDIO_SAMPLERATE_OFFSET, +}; + +enum +{ + FLV_CODECID_MP3 = 2 << FLV_AUDIO_CODECID_OFFSET, + FLV_CODECID_AAC = 10<< FLV_AUDIO_CODECID_OFFSET, +}; + +enum +{ + FLV_CODECID_H264 = 7, +}; + +enum +{ + FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET | 7, + FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET | 7, +}; + +typedef enum +{ + AMF_DATA_TYPE_NUMBER = 0x00, + AMF_DATA_TYPE_BOOL = 0x01, + AMF_DATA_TYPE_STRING = 0x02, + AMF_DATA_TYPE_OBJECT = 0x03, + AMF_DATA_TYPE_NULL = 0x05, + AMF_DATA_TYPE_UNDEFINED = 0x06, + AMF_DATA_TYPE_REFERENCE = 0x07, + AMF_DATA_TYPE_MIXEDARRAY = 0x08, + AMF_DATA_TYPE_OBJECT_END = 0x09, + AMF_DATA_TYPE_ARRAY = 0x0a, + AMF_DATA_TYPE_DATE = 0x0b, + AMF_DATA_TYPE_LONG_STRING = 0x0c, + AMF_DATA_TYPE_UNSUPPORTED = 0x0d, +} AMFDataType; + +typedef struct flv_buffer +{ + uint8_t *data; + unsigned d_cur; + unsigned d_max; + FILE *fp; + uint64_t d_total; +} flv_buffer; + +flv_buffer *flv_create_writer( const char *filename ); +int flv_append_data( flv_buffer *c, uint8_t *data, unsigned size ); +int flv_write_byte( flv_buffer *c, uint8_t *byte ); +int flv_flush_data( flv_buffer *c ); +void rewrite_amf_be24( flv_buffer *c, unsigned length, unsigned start ); + +uint64_t dbl2int( double value ); +uint64_t get_amf_double( double value ); +void put_byte( flv_buffer *c, uint8_t b ); +void put_be32( flv_buffer *c, uint32_t val ); +void put_be64( flv_buffer *c, uint64_t val ); +void put_be16( flv_buffer *c, uint16_t val ); +void put_be24( flv_buffer *c, uint32_t val ); +void put_tag( flv_buffer *c, const char *tag ); +void put_amf_string( flv_buffer *c, const char *str ); +void put_amf_double( flv_buffer *c, double d ); + +#endif diff --git a/output/matroska.c b/output/matroska.c index e986a753..8c9d29fe 100644 --- a/output/matroska.c +++ b/output/matroska.c @@ -151,7 +151,7 @@ static int set_param( hnd_t handle, x264_param_t *p_param ) return 0; } -static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size ) +static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture ) { mkv_hnd_t *p_mkv = handle; uint8_t type = p_nalu[4] & 0x1f; diff --git a/output/mp4.c b/output/mp4.c index dcac72e9..e0d35fc9 100644 --- a/output/mp4.c +++ b/output/mp4.c @@ -197,7 +197,7 @@ static int set_param( hnd_t handle, x264_param_t *p_param ) return 0; } -static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size ) +static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture ) { mp4_hnd_t *p_mp4 = handle; GF_AVCConfigSlot *p_slot; diff --git a/output/output.h b/output/output.h index 5409f152..e4480414 100644 --- a/output/output.h +++ b/output/output.h @@ -28,7 +28,7 @@ typedef struct { int (*open_file)( char *psz_filename, hnd_t *p_handle ); int (*set_param)( hnd_t handle, x264_param_t *p_param ); - int (*write_nalu)( hnd_t handle, uint8_t *p_nal, int i_size ); + int (*write_nalu)( hnd_t handle, uint8_t *p_nal, int i_size, x264_picture_t *p_picture ); int (*set_eop)( hnd_t handle, x264_picture_t *p_picture ); int (*close_file)( hnd_t handle ); } cli_output_t; @@ -36,5 +36,6 @@ typedef struct extern cli_output_t raw_output; extern cli_output_t mkv_output; extern cli_output_t mp4_output; +extern cli_output_t flv_output; #endif diff --git a/output/raw.c b/output/raw.c index 1bfc3562..42a707f6 100644 --- a/output/raw.c +++ b/output/raw.c @@ -38,7 +38,7 @@ static int set_param( hnd_t handle, x264_param_t *p_param ) return 0; } -static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size ) +static int write_nalu( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture ) { if( fwrite( p_nalu, i_size, 1, (FILE*)handle ) > 0 ) return i_size; diff --git a/x264.c b/x264.c index 115e66aa..6bf75e05 100644 --- a/x264.c +++ b/x264.c @@ -66,7 +66,7 @@ static cli_output_t output; /* i/o modules that work with pipes (and fifos) */ static const char * const stdin_format_names[] = { "yuv", "y4m", 0 }; -static const char * const stdout_format_names[] = { "raw", "mkv", 0 }; +static const char * const stdout_format_names[] = { "raw", "mkv", "flv", 0 }; static void Help( x264_param_t *defaults, int longhelp ); static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ); @@ -134,6 +134,7 @@ static void Help( x264_param_t *defaults, int longhelp ) "Outfile type is selected by filename:\n" " .264 -> Raw bytestream\n" " .mkv -> Matroska\n" + " .flv -> Flash Video\n" " .mp4 -> MP4 if compiled with GPAC support (%s)\n" "\n" "Options:\n" @@ -364,7 +365,7 @@ static void Help( x264_param_t *defaults, int longhelp ) H0( "\n" ); H0( " -o, --output Specify output file\n" ); H1( " --stdout Specify stdout format [\"%s\"]\n" - " - raw, mkv\n", stdout_format_names[0] ); + " - raw, mkv, flv\n", stdout_format_names[0] ); H1( " --stdin Specify stdin format [\"%s\"]\n" " - yuv, y4m\n", stdin_format_names[0] ); H0( " --sar width:height Specify Sample Aspect Ratio\n" ); @@ -536,7 +537,7 @@ static struct option long_options[] = {0, 0, 0, 0} }; -static int select_output( char *filename, const char *pipe_format ) +static int select_output( char *filename, const char *pipe_format, x264_param_t *param ) { char *ext = filename + strlen( filename ) - 1; while( *ext != '.' && ext > filename ) @@ -545,14 +546,19 @@ static int select_output( char *filename, const char *pipe_format ) if( !strcasecmp( ext, ".mp4" ) ) { #ifdef MP4_OUTPUT - output = mp4_output; + output = mp4_output; // FIXME use b_annexb=0 #else fprintf( stderr, "x264 [error]: not compiled with MP4 output support\n" ); return -1; #endif } else if( !strcasecmp( ext, ".mkv" ) || (!strcmp( filename, "-" ) && !strcasecmp( pipe_format, "mkv" )) ) - output = mkv_output; + output = mkv_output; // FIXME use b_annexb=0 + else if( !strcasecmp( ext, ".flv" ) || (!strcmp( filename, "-" ) && !strcasecmp( pipe_format, "flv" )) ) + { + output = flv_output; + param->b_annexb = 0; + } else output = raw_output; return 0; @@ -1027,7 +1033,7 @@ generic_option: } input_filename = argv[optind++]; - if( select_output( output_filename, stdout_format ) ) + if( select_output( output_filename, stdout_format, param ) ) return -1; if( output.open_file( output_filename, &opt->hout ) ) { @@ -1148,7 +1154,7 @@ static int Encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic ) for( i = 0; i < i_nal; i++ ) { - i_nalu_size = output.write_nalu( hout, nal[i].p_payload, nal[i].i_payload ); + i_nalu_size = output.write_nalu( hout, nal[i].p_payload, nal[i].i_payload, &pic_out ); if( i_nalu_size < 0 ) return -1; i_file += i_nalu_size;