FLV muxing support
authorKieran Kunhya <kieran@kunhya.com>
Sun, 15 Nov 2009 03:01:09 +0000 (19:01 -0800)
committerJason Garrett-Glaser <darkshikari@gmail.com>
Sun, 15 Nov 2009 03:43:40 +0000 (19:43 -0800)
Makefile
common/osdep.h
configure
output/flv.c [new file with mode: 0644]
output/flv_bytestream.c [new file with mode: 0644]
output/flv_bytestream.h [new file with mode: 0644]
output/matroska.c
output/mp4.c
output/output.h
output/raw.c
x264.c

index 2fe3361..5a6d358 100644 (file)
--- 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)
 
index 2f9e1af..eb48dcc 100644 (file)
@@ -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);
index 5e89395..d08c548 100755 (executable)
--- 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 (file)
index 0000000..30b609b
--- /dev/null
@@ -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 (file)
index 0000000..683946d
--- /dev/null
@@ -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 <stdlib.h>
+#include <string.h>
+#include <math.h>
+#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 (file)
index 0000000..741dfbb
--- /dev/null
@@ -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
index e986a75..8c9d29f 100644 (file)
@@ -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;
index dcac72e..e0d35fc 100644 (file)
@@ -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;
index 5409f15..e448041 100644 (file)
@@ -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
index 1bfc356..42a707f 100644 (file)
@@ -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 115e66a..6bf75e0 100644 (file)
--- 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;