2003-07-10 01:10:59 +02:00
/*
2006-10-23 10:57:54 +02:00
* FLV muxer
2009-01-19 16:46:40 +01:00
* Copyright ( c ) 2003 The FFmpeg Project
2003-07-10 01:10:59 +02:00
*
2006-10-07 17:30:46 +02:00
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
2003-07-10 01:10:59 +02:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 17:30:46 +02:00
* version 2.1 of the License , or ( at your option ) any later version .
2003-07-10 01:10:59 +02:00
*
2006-10-07 17:30:46 +02:00
* FFmpeg is distributed in the hope that it will be useful ,
2003-07-10 01:10:59 +02:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2006-10-07 17:30:46 +02:00
* License along with FFmpeg ; if not , write to the Free Software
2006-01-12 23:43:26 +01:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2003-07-10 01:10:59 +02:00
*/
2011-01-29 06:32:09 +01:00
# include "libavutil/intreadwrite.h"
2012-06-27 10:16:18 +02:00
# include "libavutil/dict.h"
2011-11-27 15:04:16 +01:00
# include "libavutil/intfloat.h"
2012-06-28 23:57:31 +02:00
# include "libavutil/avassert.h"
2012-06-27 10:16:18 +02:00
# include "avc.h"
2003-07-10 01:10:59 +02:00
# include "avformat.h"
2006-12-06 01:23:04 +01:00
# include "flv.h"
2010-05-22 18:01:32 +02:00
# include "internal.h"
2010-09-11 01:29:07 +02:00
# include "metadata.h"
2015-11-18 22:44:08 +01:00
# include "libavutil/opt.h"
# include "libavcodec/put_bits.h"
# include "libavcodec/aacenctab.h"
2003-07-10 01:10:59 +02:00
2004-04-17 14:04:59 +02:00
2007-01-21 02:39:17 +01:00
static const AVCodecTag flv_video_codec_ids [ ] = {
2012-08-05 11:11:04 +02:00
{ AV_CODEC_ID_FLV1 , FLV_CODECID_H263 } ,
2012-08-07 22:45:46 +02:00
{ AV_CODEC_ID_H263 , FLV_CODECID_REALH263 } ,
{ AV_CODEC_ID_MPEG4 , FLV_CODECID_MPEG4 } ,
2012-08-05 11:11:04 +02:00
{ AV_CODEC_ID_FLASHSV , FLV_CODECID_SCREEN } ,
{ AV_CODEC_ID_FLASHSV2 , FLV_CODECID_SCREEN2 } ,
{ AV_CODEC_ID_VP6F , FLV_CODECID_VP6 } ,
2013-10-31 01:24:07 +01:00
{ AV_CODEC_ID_VP6 , FLV_CODECID_VP6 } ,
2012-08-07 22:45:46 +02:00
{ AV_CODEC_ID_VP6A , FLV_CODECID_VP6A } ,
2012-08-05 11:11:04 +02:00
{ AV_CODEC_ID_H264 , FLV_CODECID_H264 } ,
{ AV_CODEC_ID_NONE , 0 }
2006-12-28 13:35:22 +01:00
} ;
2007-01-21 02:39:17 +01:00
static const AVCodecTag flv_audio_codec_ids [ ] = {
2012-08-05 11:11:04 +02:00
{ AV_CODEC_ID_MP3 , FLV_CODECID_MP3 > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_PCM_U8 , FLV_CODECID_PCM > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_PCM_S16BE , FLV_CODECID_PCM > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_PCM_S16LE , FLV_CODECID_PCM_LE > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_ADPCM_SWF , FLV_CODECID_ADPCM > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_AAC , FLV_CODECID_AAC > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_NELLYMOSER , FLV_CODECID_NELLYMOSER > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_PCM_MULAW , FLV_CODECID_PCM_MULAW > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_PCM_ALAW , FLV_CODECID_PCM_ALAW > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_SPEEX , FLV_CODECID_SPEEX > > FLV_AUDIO_CODECID_OFFSET } ,
{ AV_CODEC_ID_NONE , 0 }
2006-12-28 13:35:22 +01:00
} ;
2003-07-10 01:10:59 +02:00
typedef struct FLVContext {
2015-11-18 22:44:08 +01:00
AVClass * av_class ;
2012-06-27 10:16:18 +02:00
int reserved ;
2008-10-03 12:16:29 +02:00
int64_t duration_offset ;
int64_t filesize_offset ;
2006-08-06 17:29:50 +02:00
int64_t duration ;
2011-10-26 18:43:18 +02:00
int64_t delay ; ///< first dts delay (needed for AVC & Speex)
2014-11-07 22:09:09 +01:00
AVCodecContext * audio_enc ;
AVCodecContext * video_enc ;
double framerate ;
AVCodecContext * data_enc ;
2015-11-18 22:44:08 +01:00
int flags ;
2003-07-10 01:10:59 +02:00
} FLVContext ;
2011-10-20 21:08:48 +02:00
typedef struct FLVStreamContext {
int64_t last_ts ; ///< last timestamp for each stream
} FLVStreamContext ;
2012-05-07 16:09:30 +02:00
static int get_audio_flags ( AVFormatContext * s , AVCodecContext * enc )
{
2012-06-27 10:16:18 +02:00
int flags = ( enc - > bits_per_coded_sample = = 16 ) ? FLV_SAMPLESSIZE_16BIT
: FLV_SAMPLESSIZE_8BIT ;
2004-07-02 21:26:51 +02:00
2012-08-05 11:11:04 +02:00
if ( enc - > codec_id = = AV_CODEC_ID_AAC ) // specs force these parameters
2012-06-27 10:16:18 +02:00
return FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ |
FLV_SAMPLESSIZE_16BIT | FLV_STEREO ;
2012-08-05 11:11:04 +02:00
else if ( enc - > codec_id = = AV_CODEC_ID_SPEEX ) {
2009-10-16 05:02:25 +02:00
if ( enc - > sample_rate ! = 16000 ) {
2012-06-27 10:16:18 +02:00
av_log ( s , AV_LOG_ERROR ,
2012-09-06 15:10:21 +02:00
" FLV only supports wideband (16kHz) Speex audio \n " ) ;
2012-09-06 14:54:57 +02:00
return AVERROR ( EINVAL ) ;
2009-10-16 05:02:25 +02:00
}
if ( enc - > channels ! = 1 ) {
2012-09-06 15:10:21 +02:00
av_log ( s , AV_LOG_ERROR , " FLV only supports mono Speex audio \n " ) ;
2012-09-06 14:54:57 +02:00
return AVERROR ( EINVAL ) ;
2009-10-16 05:02:25 +02:00
}
return FLV_CODECID_SPEEX | FLV_SAMPLERATE_11025HZ | FLV_SAMPLESSIZE_16BIT ;
} else {
2012-06-27 10:16:18 +02:00
switch ( enc - > sample_rate ) {
case 44100 :
2006-12-06 01:23:04 +01:00
flags | = FLV_SAMPLERATE_44100HZ ;
2004-07-02 21:26:51 +02:00
break ;
2012-06-27 10:16:18 +02:00
case 22050 :
2006-12-06 01:23:04 +01:00
flags | = FLV_SAMPLERATE_22050HZ ;
2004-07-02 21:26:51 +02:00
break ;
2012-06-27 10:16:18 +02:00
case 11025 :
2006-12-06 01:23:04 +01:00
flags | = FLV_SAMPLERATE_11025HZ ;
2004-07-02 21:26:51 +02:00
break ;
2012-06-27 10:16:18 +02:00
case 16000 : // nellymoser only
case 8000 : // nellymoser only
case 5512 : // not MP3
2012-08-05 11:11:04 +02:00
if ( enc - > codec_id ! = AV_CODEC_ID_MP3 ) {
2007-10-09 03:12:27 +02:00
flags | = FLV_SAMPLERATE_SPECIAL ;
break ;
2007-10-09 03:01:07 +02:00
}
2004-07-02 21:26:51 +02:00
default :
2012-06-27 10:16:18 +02:00
av_log ( s , AV_LOG_ERROR ,
2012-09-06 15:10:21 +02:00
" FLV does not support sample rate %d, "
" choose from (44100, 22050, 11025) \n " , enc - > sample_rate ) ;
2012-09-06 14:54:57 +02:00
return AVERROR ( EINVAL ) ;
2012-06-27 10:16:18 +02:00
}
2008-05-27 00:00:35 +02:00
}
2004-07-02 21:26:51 +02:00
2012-06-27 10:16:18 +02:00
if ( enc - > channels > 1 )
2006-12-06 01:23:04 +01:00
flags | = FLV_STEREO ;
2005-12-17 19:14:38 +01:00
2012-06-27 10:16:18 +02:00
switch ( enc - > codec_id ) {
2012-08-05 11:11:04 +02:00
case AV_CODEC_ID_MP3 :
2006-12-06 01:23:04 +01:00
flags | = FLV_CODECID_MP3 | FLV_SAMPLESSIZE_16BIT ;
2004-07-02 21:26:51 +02:00
break ;
2012-08-05 11:11:04 +02:00
case AV_CODEC_ID_PCM_U8 :
2008-02-24 01:57:15 +01:00
flags | = FLV_CODECID_PCM | FLV_SAMPLESSIZE_8BIT ;
2005-12-22 02:10:11 +01:00
break ;
2012-08-05 11:11:04 +02:00
case AV_CODEC_ID_PCM_S16BE :
2008-02-24 01:57:15 +01:00
flags | = FLV_CODECID_PCM | FLV_SAMPLESSIZE_16BIT ;
2005-12-22 02:10:11 +01:00
break ;
2012-08-05 11:11:04 +02:00
case AV_CODEC_ID_PCM_S16LE :
2006-12-06 01:23:04 +01:00
flags | = FLV_CODECID_PCM_LE | FLV_SAMPLESSIZE_16BIT ;
2005-12-22 02:10:11 +01:00
break ;
2012-08-05 11:11:04 +02:00
case AV_CODEC_ID_ADPCM_SWF :
2012-06-27 10:16:18 +02:00
flags | = FLV_CODECID_ADPCM | FLV_SAMPLESSIZE_16BIT ;
2005-12-22 02:10:11 +01:00
break ;
2012-08-05 11:11:04 +02:00
case AV_CODEC_ID_NELLYMOSER :
2012-06-27 10:16:18 +02:00
if ( enc - > sample_rate = = 8000 )
flags | = FLV_CODECID_NELLYMOSER_8KHZ_MONO | FLV_SAMPLESSIZE_16BIT ;
else if ( enc - > sample_rate = = 16000 )
2011-12-15 14:10:57 +01:00
flags | = FLV_CODECID_NELLYMOSER_16KHZ_MONO | FLV_SAMPLESSIZE_16BIT ;
2012-06-27 10:16:18 +02:00
else
flags | = FLV_CODECID_NELLYMOSER | FLV_SAMPLESSIZE_16BIT ;
2008-05-02 21:35:31 +02:00
break ;
2012-08-05 11:11:04 +02:00
case AV_CODEC_ID_PCM_MULAW :
2012-06-28 16:28:56 +02:00
flags = FLV_CODECID_PCM_MULAW | FLV_SAMPLERATE_SPECIAL | FLV_SAMPLESSIZE_16BIT ;
break ;
2012-08-05 11:11:04 +02:00
case AV_CODEC_ID_PCM_ALAW :
2012-06-28 16:28:56 +02:00
flags = FLV_CODECID_PCM_ALAW | FLV_SAMPLERATE_SPECIAL | FLV_SAMPLESSIZE_16BIT ;
2008-05-02 21:35:31 +02:00
break ;
2004-07-02 21:26:51 +02:00
case 0 :
2012-06-27 10:16:18 +02:00
flags | = enc - > codec_tag < < 4 ;
2004-07-02 21:26:51 +02:00
break ;
default :
2012-09-06 15:10:21 +02:00
av_log ( s , AV_LOG_ERROR , " Audio codec '%s' not compatible with FLV \n " ,
avcodec_get_name ( enc - > codec_id ) ) ;
2012-09-06 14:54:57 +02:00
return AVERROR ( EINVAL ) ;
2004-07-02 21:26:51 +02:00
}
2005-12-17 19:14:38 +01:00
2004-07-02 21:26:51 +02:00
return flags ;
}
2011-02-20 11:04:12 +01:00
static void put_amf_string ( AVIOContext * pb , const char * str )
2006-07-17 23:51:21 +02:00
{
size_t len = strlen ( str ) ;
2011-02-21 19:28:17 +01:00
avio_wb16 ( pb , len ) ;
avio_write ( pb , str , len ) ;
2006-07-17 23:51:21 +02:00
}
2012-06-27 10:16:18 +02:00
static void put_avc_eos_tag ( AVIOContext * pb , unsigned ts )
{
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , FLV_TAG_TYPE_VIDEO ) ;
2012-06-27 10:16:18 +02:00
avio_wb24 ( pb , 5 ) ; /* Tag Data Size */
avio_wb24 ( pb , ts ) ; /* lower 24 bits of timestamp in ms */
avio_w8 ( pb , ( ts > > 24 ) & 0x7F ) ; /* MSB of ts in ms */
avio_wb24 ( pb , 0 ) ; /* StreamId = 0 */
avio_w8 ( pb , 23 ) ; /* ub[4] FrameType = 1, ub[4] CodecId = 7 */
avio_w8 ( pb , 2 ) ; /* AVC end of sequence */
avio_wb24 ( pb , 0 ) ; /* Always 0 for AVC EOS. */
avio_wb32 ( pb , 16 ) ; /* Size of FLV tag */
2010-08-18 11:39:21 +02:00
}
2011-02-20 11:04:12 +01:00
static void put_amf_double ( AVIOContext * pb , double d )
2006-07-17 23:51:21 +02:00
{
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , AMF_DATA_TYPE_NUMBER ) ;
2011-11-27 15:04:16 +01:00
avio_wb64 ( pb , av_double2int ( d ) ) ;
2006-07-17 23:51:21 +02:00
}
2012-06-27 10:16:18 +02:00
static void put_amf_bool ( AVIOContext * pb , int b )
{
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , AMF_DATA_TYPE_BOOL ) ;
avio_w8 ( pb , ! ! b ) ;
2006-12-28 13:35:22 +01:00
}
2014-11-07 22:09:10 +01:00
static void write_metadata ( AVFormatContext * s , unsigned int ts )
2003-07-10 01:10:59 +02:00
{
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s - > pb ;
2003-07-10 01:10:59 +02:00
FLVContext * flv = s - > priv_data ;
2014-11-17 13:08:05 +01:00
int metadata_count = 0 ;
2011-09-21 15:58:07 +02:00
int64_t metadata_size_pos , data_size , metadata_count_pos ;
2011-05-22 12:46:29 +02:00
AVDictionaryEntry * tag = NULL ;
2003-07-10 01:10:59 +02:00
2006-07-17 23:51:21 +02:00
/* write meta_tag */
2012-06-27 10:16:18 +02:00
avio_w8 ( pb , 18 ) ; // tag type META
metadata_size_pos = avio_tell ( pb ) ;
avio_wb24 ( pb , 0 ) ; // size of data part (sum of all parts below)
2014-11-07 22:09:10 +01:00
avio_wb24 ( pb , ts ) ; // timestamp
2012-06-27 10:16:18 +02:00
avio_wb32 ( pb , 0 ) ; // reserved
2006-07-17 23:51:21 +02:00
/* now data of data_size size */
/* first event name as a string */
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , AMF_DATA_TYPE_STRING ) ;
2006-07-17 23:51:21 +02:00
put_amf_string ( pb , " onMetaData " ) ; // 12 bytes
/* mixed array (hash) with size and string/type/data tuples */
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , AMF_DATA_TYPE_MIXEDARRAY ) ;
2011-09-21 15:58:07 +02:00
metadata_count_pos = avio_tell ( pb ) ;
2014-11-07 22:09:09 +01:00
metadata_count = 4 * ! ! flv - > video_enc +
5 * ! ! flv - > audio_enc +
1 * ! ! flv - > data_enc +
2012-05-09 23:35:58 +02:00
2 ; // +2 for duration and file size
2011-09-21 15:58:07 +02:00
avio_wb32 ( pb , metadata_count ) ;
2006-07-17 23:51:21 +02:00
2006-08-06 17:29:50 +02:00
put_amf_string ( pb , " duration " ) ;
2014-11-07 22:09:09 +01:00
flv - > duration_offset = avio_tell ( pb ) ;
2006-07-17 23:51:21 +02:00
2012-06-27 10:16:18 +02:00
// fill in the guessed duration, it'll be corrected later if incorrect
put_amf_double ( pb , s - > duration / AV_TIME_BASE ) ;
2014-11-07 22:09:09 +01:00
if ( flv - > video_enc ) {
2006-07-17 23:51:21 +02:00
put_amf_string ( pb , " width " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > video_enc - > width ) ;
2006-07-17 23:51:21 +02:00
put_amf_string ( pb , " height " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > video_enc - > height ) ;
2006-07-17 23:51:21 +02:00
put_amf_string ( pb , " videodatarate " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > video_enc - > bit_rate / 1024.0 ) ;
2006-07-17 23:51:21 +02:00
2014-11-07 22:09:09 +01:00
if ( flv - > framerate ! = 0.0 ) {
2014-05-27 09:49:29 +02:00
put_amf_string ( pb , " framerate " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > framerate ) ;
2014-06-11 03:45:07 +02:00
metadata_count + + ;
2014-05-27 09:49:29 +02:00
}
2006-12-28 13:35:22 +01:00
put_amf_string ( pb , " videocodecid " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > video_enc - > codec_tag ) ;
2006-07-17 23:51:21 +02:00
}
2014-11-07 22:09:09 +01:00
if ( flv - > audio_enc ) {
2009-02-02 17:10:46 +01:00
put_amf_string ( pb , " audiodatarate " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > audio_enc - > bit_rate / 1024.0 ) ;
2009-02-02 17:10:46 +01:00
2006-07-17 23:51:21 +02:00
put_amf_string ( pb , " audiosamplerate " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > audio_enc - > sample_rate ) ;
2006-12-28 13:35:22 +01:00
put_amf_string ( pb , " audiosamplesize " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > audio_enc - > codec_id = = AV_CODEC_ID_PCM_U8 ? 8 : 16 ) ;
2006-12-28 13:35:22 +01:00
put_amf_string ( pb , " stereo " ) ;
2014-11-07 22:09:09 +01:00
put_amf_bool ( pb , flv - > audio_enc - > channels = = 2 ) ;
2006-12-28 13:35:22 +01:00
put_amf_string ( pb , " audiocodecid " ) ;
2014-11-07 22:09:09 +01:00
put_amf_double ( pb , flv - > audio_enc - > codec_tag ) ;
2006-07-17 23:51:21 +02:00
}
2014-11-07 22:09:09 +01:00
if ( flv - > data_enc ) {
2012-05-09 23:35:58 +02:00
put_amf_string ( pb , " datastream " ) ;
put_amf_double ( pb , 0.0 ) ;
}
2011-05-22 12:46:29 +02:00
while ( ( tag = av_dict_get ( s - > metadata , " " , tag , AV_DICT_IGNORE_SUFFIX ) ) ) {
2011-10-06 03:20:57 +02:00
if ( ! strcmp ( tag - > key , " width " )
| | ! strcmp ( tag - > key , " height " )
| | ! strcmp ( tag - > key , " videodatarate " )
| | ! strcmp ( tag - > key , " framerate " )
| | ! strcmp ( tag - > key , " videocodecid " )
| | ! strcmp ( tag - > key , " audiodatarate " )
| | ! strcmp ( tag - > key , " audiosamplerate " )
| | ! strcmp ( tag - > key , " audiosamplesize " )
| | ! strcmp ( tag - > key , " stereo " )
| | ! strcmp ( tag - > key , " audiocodecid " )
| | ! strcmp ( tag - > key , " duration " )
| | ! strcmp ( tag - > key , " onMetaData " )
2015-01-03 23:03:55 +01:00
| | ! strcmp ( tag - > key , " datasize " )
| | ! strcmp ( tag - > key , " lasttimestamp " )
| | ! strcmp ( tag - > key , " totalframes " )
| | ! strcmp ( tag - > key , " hasAudio " )
| | ! strcmp ( tag - > key , " hasVideo " )
| | ! strcmp ( tag - > key , " hasCuePoints " )
| | ! strcmp ( tag - > key , " hasMetadata " )
| | ! strcmp ( tag - > key , " hasKeyframes " )
2011-10-06 03:20:57 +02:00
) {
2012-09-06 15:10:21 +02:00
av_log ( s , AV_LOG_DEBUG , " Ignoring metadata for %s \n " , tag - > key ) ;
2011-10-06 03:20:57 +02:00
continue ;
}
2010-09-11 01:29:07 +02:00
put_amf_string ( pb , tag - > key ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , AMF_DATA_TYPE_STRING ) ;
2010-09-11 01:29:07 +02:00
put_amf_string ( pb , tag - > value ) ;
2011-09-21 15:58:07 +02:00
metadata_count + + ;
2010-09-11 01:29:07 +02:00
}
2006-08-06 17:29:50 +02:00
put_amf_string ( pb , " filesize " ) ;
2012-06-27 10:16:18 +02:00
flv - > filesize_offset = avio_tell ( pb ) ;
2006-08-06 17:29:50 +02:00
put_amf_double ( pb , 0 ) ; // delayed write
2006-07-17 23:51:21 +02:00
put_amf_string ( pb , " " ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , AMF_END_OF_OBJECT ) ;
2006-07-17 23:51:21 +02:00
/* write total size of tag */
2012-06-27 10:16:18 +02:00
data_size = avio_tell ( pb ) - metadata_size_pos - 10 ;
2011-09-21 15:58:07 +02:00
avio_seek ( pb , metadata_count_pos , SEEK_SET ) ;
avio_wb32 ( pb , metadata_count ) ;
2011-02-28 14:57:54 +01:00
avio_seek ( pb , metadata_size_pos , SEEK_SET ) ;
2011-02-21 19:28:17 +01:00
avio_wb24 ( pb , data_size ) ;
2011-03-15 09:14:38 +01:00
avio_skip ( pb , data_size + 10 - 3 ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , data_size + 11 ) ;
2014-11-07 22:09:09 +01:00
}
2015-03-03 14:39:26 +01:00
static int unsupported_codec ( AVFormatContext * s ,
const char * type , int codec_id )
{
const AVCodecDescriptor * desc = avcodec_descriptor_get ( codec_id ) ;
av_log ( s , AV_LOG_ERROR ,
" %s codec %s not compatible with flv \n " ,
type ,
desc ? desc - > name : " unknown " ) ;
return AVERROR ( ENOSYS ) ;
}
2014-11-07 22:09:09 +01:00
static int flv_write_header ( AVFormatContext * s )
{
int i ;
AVIOContext * pb = s - > pb ;
FLVContext * flv = s - > priv_data ;
int64_t data_size ;
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVCodecContext * enc = s - > streams [ i ] - > codec ;
FLVStreamContext * sc ;
switch ( enc - > codec_type ) {
case AVMEDIA_TYPE_VIDEO :
if ( s - > streams [ i ] - > avg_frame_rate . den & &
s - > streams [ i ] - > avg_frame_rate . num ) {
flv - > framerate = av_q2d ( s - > streams [ i ] - > avg_frame_rate ) ;
}
if ( flv - > video_enc ) {
av_log ( s , AV_LOG_ERROR ,
" at most one video stream is supported in flv \n " ) ;
return AVERROR ( EINVAL ) ;
}
flv - > video_enc = enc ;
2015-03-22 17:19:25 +01:00
if ( ! ff_codec_get_tag ( flv_video_codec_ids , enc - > codec_id ) )
2015-03-03 14:39:26 +01:00
return unsupported_codec ( s , " Video " , enc - > codec_id ) ;
2015-03-04 20:42:33 +01:00
2014-11-17 12:43:55 +01:00
if ( enc - > codec_id = = AV_CODEC_ID_MPEG4 | |
enc - > codec_id = = AV_CODEC_ID_H263 ) {
int error = s - > strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL ;
av_log ( s , error ? AV_LOG_ERROR : AV_LOG_WARNING ,
" Codec %s is not supported in the official FLV specification, \n " , avcodec_get_name ( enc - > codec_id ) ) ;
if ( error ) {
av_log ( s , AV_LOG_ERROR ,
" use vstrict=-1 / -strict -1 to use it anyway. \n " ) ;
return AVERROR ( EINVAL ) ;
}
2014-11-23 13:22:27 +01:00
} else if ( enc - > codec_id = = AV_CODEC_ID_VP6 ) {
av_log ( s , AV_LOG_WARNING ,
" Muxing VP6 in flv will produce flipped video on playback. \n " ) ;
2014-11-07 22:09:09 +01:00
}
break ;
case AVMEDIA_TYPE_AUDIO :
if ( flv - > audio_enc ) {
av_log ( s , AV_LOG_ERROR ,
" at most one audio stream is supported in flv \n " ) ;
return AVERROR ( EINVAL ) ;
}
flv - > audio_enc = enc ;
if ( get_audio_flags ( s , enc ) < 0 )
2015-03-03 14:39:26 +01:00
return unsupported_codec ( s , " Audio " , enc - > codec_id ) ;
2014-11-17 12:43:55 +01:00
if ( enc - > codec_id = = AV_CODEC_ID_PCM_S16BE )
av_log ( s , AV_LOG_WARNING ,
" 16-bit big-endian audio in flv is valid but most likely unplayable (hardware dependent); use s16le \n " ) ;
2014-11-07 22:09:09 +01:00
break ;
case AVMEDIA_TYPE_DATA :
2015-03-04 20:42:33 +01:00
if ( enc - > codec_id ! = AV_CODEC_ID_TEXT & & enc - > codec_id ! = AV_CODEC_ID_NONE )
2015-03-03 14:39:26 +01:00
return unsupported_codec ( s , " Data " , enc - > codec_id ) ;
2014-11-07 22:09:09 +01:00
flv - > data_enc = enc ;
break ;
2015-01-20 23:55:09 +01:00
case AVMEDIA_TYPE_SUBTITLE :
if ( enc - > codec_id ! = AV_CODEC_ID_TEXT ) {
av_log ( s , AV_LOG_ERROR , " Subtitle codec '%s' for stream %d is not compatible with FLV \n " ,
avcodec_get_name ( enc - > codec_id ) , i ) ;
return AVERROR_INVALIDDATA ;
}
flv - > data_enc = enc ;
break ;
2014-11-07 22:09:09 +01:00
default :
2014-11-17 12:43:55 +01:00
av_log ( s , AV_LOG_ERROR , " Codec type '%s' for stream %d is not compatible with FLV \n " ,
av_get_media_type_string ( enc - > codec_type ) , i ) ;
return AVERROR ( EINVAL ) ;
2014-11-07 22:09:09 +01:00
}
avpriv_set_pts_info ( s - > streams [ i ] , 32 , 1 , 1000 ) ; /* 32 bit pts in ms */
sc = av_mallocz ( sizeof ( FLVStreamContext ) ) ;
if ( ! sc )
return AVERROR ( ENOMEM ) ;
s - > streams [ i ] - > priv_data = sc ;
sc - > last_ts = - 1 ;
}
flv - > delay = AV_NOPTS_VALUE ;
avio_write ( pb , " FLV " , 3 ) ;
avio_w8 ( pb , 1 ) ;
avio_w8 ( pb , FLV_HEADER_FLAG_HASAUDIO * ! ! flv - > audio_enc +
FLV_HEADER_FLAG_HASVIDEO * ! ! flv - > video_enc ) ;
avio_wb32 ( pb , 9 ) ;
avio_wb32 ( pb , 0 ) ;
for ( i = 0 ; i < s - > nb_streams ; i + + )
if ( s - > streams [ i ] - > codec - > codec_tag = = 5 ) {
avio_w8 ( pb , 8 ) ; // message type
avio_wb24 ( pb , 0 ) ; // include flags
avio_wb24 ( pb , 0 ) ; // time stamp
avio_wb32 ( pb , 0 ) ; // reserved
avio_wb32 ( pb , 11 ) ; // size
flv - > reserved = 5 ;
}
2014-11-07 22:09:10 +01:00
write_metadata ( s , 0 ) ;
2006-07-17 23:51:21 +02:00
2008-05-27 00:00:35 +02:00
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVCodecContext * enc = s - > streams [ i ] - > codec ;
2012-08-07 22:45:46 +02:00
if ( enc - > codec_id = = AV_CODEC_ID_AAC | | enc - > codec_id = = AV_CODEC_ID_H264 | | enc - > codec_id = = AV_CODEC_ID_MPEG4 ) {
2008-10-03 12:16:29 +02:00
int64_t pos ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , enc - > codec_type = = AVMEDIA_TYPE_VIDEO ?
2012-06-27 10:16:18 +02:00
FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO ) ;
2011-02-21 19:28:17 +01:00
avio_wb24 ( pb , 0 ) ; // size patched later
avio_wb24 ( pb , 0 ) ; // ts
2012-06-27 10:16:18 +02:00
avio_w8 ( pb , 0 ) ; // ts ext
2011-02-21 19:28:17 +01:00
avio_wb24 ( pb , 0 ) ; // streamid
2011-03-03 20:11:45 +01:00
pos = avio_tell ( pb ) ;
2012-08-05 11:11:04 +02:00
if ( enc - > codec_id = = AV_CODEC_ID_AAC ) {
2012-05-07 16:09:30 +02:00
avio_w8 ( pb , get_audio_flags ( s , enc ) ) ;
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , 0 ) ; // AAC sequence header
2015-11-18 22:44:08 +01:00
if ( ! enc - > extradata_size & & flv - > flags & 1 ) {
PutBitContext pbc ;
int samplerate_index ;
int channels = flv - > audio_enc - > channels - ( flv - > audio_enc - > channels = = 8 ? 1 : 0 ) ;
uint8_t data [ 2 ] ;
for ( samplerate_index = 0 ; samplerate_index < 16 ; samplerate_index + + )
if ( flv - > audio_enc - > sample_rate = = mpeg4audio_sample_rates [ samplerate_index ] )
break ;
init_put_bits ( & pbc , data , sizeof ( data ) ) ;
put_bits ( & pbc , 5 , flv - > audio_enc - > profile + 1 ) ; //profile
put_bits ( & pbc , 4 , samplerate_index ) ; //sample rate index
put_bits ( & pbc , 4 , channels ) ;
put_bits ( & pbc , 1 , 0 ) ; //frame length - 1024 samples
put_bits ( & pbc , 1 , 0 ) ; //does not depend on core coder
put_bits ( & pbc , 1 , 0 ) ; //is not extension
flush_put_bits ( & pbc ) ;
avio_w8 ( pb , data [ 0 ] ) ;
avio_w8 ( pb , data [ 1 ] ) ;
av_log ( s , AV_LOG_WARNING , " AAC sequence header: %02x %02x. \n " , data [ 0 ] , data [ 1 ] ) ;
}
2011-02-21 19:28:17 +01:00
avio_write ( pb , enc - > extradata , enc - > extradata_size ) ;
2008-05-27 00:00:35 +02:00
} else {
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , enc - > codec_tag | FLV_FRAME_KEY ) ; // flags
avio_w8 ( pb , 0 ) ; // AVC sequence header
avio_wb24 ( pb , 0 ) ; // composition time
2008-05-27 00:00:35 +02:00
ff_isom_write_avcc ( pb , enc - > extradata , enc - > extradata_size ) ;
}
2011-03-03 20:11:45 +01:00
data_size = avio_tell ( pb ) - pos ;
2011-02-28 14:57:54 +01:00
avio_seek ( pb , - data_size - 10 , SEEK_CUR ) ;
2011-02-21 19:28:17 +01:00
avio_wb24 ( pb , data_size ) ;
2011-03-15 09:14:38 +01:00
avio_skip ( pb , data_size + 10 - 3 ) ;
2011-02-21 19:28:17 +01:00
avio_wb32 ( pb , data_size + 11 ) ; // previous tag size
2008-05-27 00:00:35 +02:00
}
}
2003-07-10 01:10:59 +02:00
return 0 ;
}
static int flv_write_trailer ( AVFormatContext * s )
{
2003-07-11 01:18:09 +02:00
int64_t file_size ;
2011-02-20 11:04:12 +01:00
AVIOContext * pb = s - > pb ;
2003-07-10 01:10:59 +02:00
FLVContext * flv = s - > priv_data ;
2010-08-18 11:39:21 +02:00
int i ;
/* Add EOS tag */
for ( i = 0 ; i < s - > nb_streams ; i + + ) {
AVCodecContext * enc = s - > streams [ i ] - > codec ;
2011-10-20 21:08:48 +02:00
FLVStreamContext * sc = s - > streams [ i ] - > priv_data ;
2010-09-26 01:27:16 +02:00
if ( enc - > codec_type = = AVMEDIA_TYPE_VIDEO & &
2012-08-07 22:45:46 +02:00
( enc - > codec_id = = AV_CODEC_ID_H264 | | enc - > codec_id = = AV_CODEC_ID_MPEG4 ) )
2011-10-20 21:08:48 +02:00
put_avc_eos_tag ( pb , sc - > last_ts ) ;
2010-08-18 11:39:21 +02:00
}
2003-07-10 01:10:59 +02:00
2011-03-03 20:11:45 +01:00
file_size = avio_tell ( pb ) ;
2006-08-06 17:29:50 +02:00
2011-10-05 14:12:42 +02:00
/* update information */
2012-12-13 14:48:25 +01:00
if ( avio_seek ( pb , flv - > duration_offset , SEEK_SET ) < 0 )
2012-12-13 14:48:25 +01:00
av_log ( s , AV_LOG_WARNING , " Failed to update header with correct duration. \n " ) ;
else
put_amf_double ( pb , flv - > duration / ( double ) 1000 ) ;
2012-12-13 14:48:25 +01:00
if ( avio_seek ( pb , flv - > filesize_offset , SEEK_SET ) < 0 )
2012-12-13 14:48:25 +01:00
av_log ( s , AV_LOG_WARNING , " Failed to update header with correct filesize. \n " ) ;
else
put_amf_double ( pb , file_size ) ;
2006-08-06 17:29:50 +02:00
2011-02-28 14:57:54 +01:00
avio_seek ( pb , file_size , SEEK_SET ) ;
2003-07-10 01:10:59 +02:00
return 0 ;
}
2004-05-29 04:06:32 +02:00
static int flv_write_packet ( AVFormatContext * s , AVPacket * pkt )
2003-07-10 01:10:59 +02:00
{
2012-06-27 10:16:18 +02:00
AVIOContext * pb = s - > pb ;
AVCodecContext * enc = s - > streams [ pkt - > stream_index ] - > codec ;
FLVContext * flv = s - > priv_data ;
2011-10-20 21:08:48 +02:00
FLVStreamContext * sc = s - > streams [ pkt - > stream_index ] - > priv_data ;
2008-05-27 00:00:35 +02:00
unsigned ts ;
2012-06-27 10:16:18 +02:00
int size = pkt - > size ;
uint8_t * data = NULL ;
2012-09-06 14:54:57 +02:00
int flags = - 1 , flags_size , ret ;
2003-07-10 01:10:59 +02:00
2013-08-15 10:04:07 +02:00
if ( enc - > codec_id = = AV_CODEC_ID_VP6F | | enc - > codec_id = = AV_CODEC_ID_VP6A | |
2013-10-31 01:24:07 +01:00
enc - > codec_id = = AV_CODEC_ID_VP6 | | enc - > codec_id = = AV_CODEC_ID_AAC )
2012-06-27 10:16:18 +02:00
flags_size = 2 ;
2012-08-07 22:45:46 +02:00
else if ( enc - > codec_id = = AV_CODEC_ID_H264 | | enc - > codec_id = = AV_CODEC_ID_MPEG4 )
2012-06-27 10:16:18 +02:00
flags_size = 5 ;
2007-08-05 01:03:17 +02:00
else
2012-06-27 10:16:18 +02:00
flags_size = 1 ;
2007-08-05 01:03:17 +02:00
2014-11-07 22:09:10 +01:00
if ( flv - > delay = = AV_NOPTS_VALUE )
flv - > delay = - pkt - > dts ;
if ( pkt - > dts < - flv - > delay ) {
av_log ( s , AV_LOG_WARNING ,
" Packets are not in the proper order with respect to DTS \n " ) ;
return AVERROR ( EINVAL ) ;
}
ts = pkt - > dts + flv - > delay ; // add delay to force positive dts
if ( s - > event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED ) {
write_metadata ( s , ts ) ;
s - > event_flags & = ~ AVSTREAM_EVENT_FLAG_METADATA_UPDATED ;
}
2012-05-09 23:35:58 +02:00
switch ( enc - > codec_type ) {
case AVMEDIA_TYPE_VIDEO :
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , FLV_TAG_TYPE_VIDEO ) ;
2007-01-01 23:52:22 +01:00
2015-03-22 17:19:25 +01:00
flags = ff_codec_get_tag ( flv_video_codec_ids , enc - > codec_id ) ;
2007-01-01 23:52:22 +01:00
2010-03-31 14:29:58 +02:00
flags | = pkt - > flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER ;
2012-05-09 23:35:58 +02:00
break ;
case AVMEDIA_TYPE_AUDIO :
2012-05-07 16:09:30 +02:00
flags = get_audio_flags ( s , enc ) ;
2005-12-17 19:14:38 +01:00
2012-06-11 20:14:22 +02:00
av_assert0 ( size ) ;
2004-04-17 14:04:59 +02:00
2011-02-21 19:28:17 +01:00
avio_w8 ( pb , FLV_TAG_TYPE_AUDIO ) ;
2012-05-09 23:35:58 +02:00
break ;
2015-01-20 23:55:09 +01:00
case AVMEDIA_TYPE_SUBTITLE :
2012-05-09 23:35:58 +02:00
case AVMEDIA_TYPE_DATA :
2011-08-27 02:42:22 +02:00
avio_w8 ( pb , FLV_TAG_TYPE_META ) ;
2012-05-09 23:35:58 +02:00
break ;
default :
return AVERROR ( EINVAL ) ;
2004-06-25 22:03:51 +02:00
}
2012-06-27 10:16:18 +02:00
2012-08-07 22:45:46 +02:00
if ( enc - > codec_id = = AV_CODEC_ID_H264 | | enc - > codec_id = = AV_CODEC_ID_MPEG4 ) {
2015-06-13 21:34:40 +02:00
/* check if extradata looks like mp4 formatted */
2012-06-27 10:16:18 +02:00
if ( enc - > extradata_size > 0 & & * ( uint8_t * ) enc - > extradata ! = 1 )
2012-09-06 14:54:57 +02:00
if ( ( ret = ff_avc_parse_nal_units_buf ( pkt - > data , & data , & size ) ) < 0 )
return ret ;
2012-08-07 22:45:46 +02:00
} else if ( enc - > codec_id = = AV_CODEC_ID_AAC & & pkt - > size > 2 & &
2012-03-12 20:36:51 +01:00
( AV_RB16 ( pkt - > data ) & 0xfff0 ) = = 0xfff0 ) {
2013-03-26 15:16:07 +01:00
if ( ! s - > streams [ pkt - > stream_index ] - > nb_frames ) {
2012-09-06 15:10:21 +02:00
av_log ( s , AV_LOG_ERROR , " Malformed AAC bitstream detected: "
2014-08-19 14:28:35 +02:00
" use the audio bitstream filter 'aac_adtstoasc' to fix it "
2012-09-06 15:10:21 +02:00
" ('-bsf:a aac_adtstoasc' option with ffmpeg) \n " ) ;
2012-09-06 14:54:57 +02:00
return AVERROR_INVALIDDATA ;
2013-03-26 15:16:07 +01:00
}
av_log ( s , AV_LOG_WARNING , " aac bitstream error \n " ) ;
2008-05-27 00:00:35 +02:00
}
2012-06-27 10:16:18 +02:00
2011-10-19 19:20:15 +02:00
/* check Speex packet duration */
2012-08-05 11:11:04 +02:00
if ( enc - > codec_id = = AV_CODEC_ID_SPEEX & & ts - sc - > last_ts > 160 )
2011-10-19 19:20:15 +02:00
av_log ( s , AV_LOG_WARNING , " Warning: Speex stream has more than "
" 8 frames per packet. Adobe Flash "
" Player cannot handle this! \n " ) ;
2011-10-20 21:08:48 +02:00
if ( sc - > last_ts < ts )
sc - > last_ts = ts ;
2011-10-19 19:20:15 +02:00
2014-11-03 00:28:06 +01:00
if ( size + flags_size > = 1 < < 24 ) {
av_log ( s , AV_LOG_ERROR , " Too large packet with size %u >= %u \n " ,
size + flags_size , 1 < < 24 ) ;
return AVERROR ( EINVAL ) ;
}
2012-06-27 10:16:18 +02:00
avio_wb24 ( pb , size + flags_size ) ;
2014-05-31 01:43:41 +02:00
avio_wb24 ( pb , ts & 0xFFFFFF ) ;
2012-06-27 10:16:18 +02:00
avio_w8 ( pb , ( ts > > 24 ) & 0x7F ) ; // timestamps are 32 bits _signed_
avio_wb24 ( pb , flv - > reserved ) ;
2011-08-27 02:42:22 +02:00
2015-01-20 23:55:09 +01:00
if ( enc - > codec_type = = AVMEDIA_TYPE_DATA | |
enc - > codec_type = = AVMEDIA_TYPE_SUBTITLE ) {
2012-05-09 23:35:58 +02:00
int data_size ;
2013-12-19 07:38:15 +01:00
int64_t metadata_size_pos = avio_tell ( pb ) ;
2014-10-02 03:57:50 +02:00
if ( enc - > codec_id = = AV_CODEC_ID_TEXT ) {
2014-09-30 06:06:38 +02:00
// legacy FFmpeg magic?
avio_w8 ( pb , AMF_DATA_TYPE_STRING ) ;
put_amf_string ( pb , " onTextData " ) ;
avio_w8 ( pb , AMF_DATA_TYPE_MIXEDARRAY ) ;
avio_wb32 ( pb , 2 ) ;
put_amf_string ( pb , " type " ) ;
avio_w8 ( pb , AMF_DATA_TYPE_STRING ) ;
put_amf_string ( pb , " Text " ) ;
put_amf_string ( pb , " text " ) ;
avio_w8 ( pb , AMF_DATA_TYPE_STRING ) ;
put_amf_string ( pb , pkt - > data ) ;
put_amf_string ( pb , " " ) ;
avio_w8 ( pb , AMF_END_OF_OBJECT ) ;
} else {
// just pass the metadata through
avio_write ( pb , data ? data : pkt - > data , size ) ;
}
2012-05-09 23:35:58 +02:00
/* write total size of tag */
data_size = avio_tell ( pb ) - metadata_size_pos ;
avio_seek ( pb , metadata_size_pos - 10 , SEEK_SET ) ;
avio_wb24 ( pb , data_size ) ;
avio_seek ( pb , data_size + 10 - 3 , SEEK_CUR ) ;
avio_wb32 ( pb , data_size + 11 ) ;
} else {
2012-08-30 05:20:51 +02:00
av_assert1 ( flags > = 0 ) ;
2012-06-27 10:16:18 +02:00
avio_w8 ( pb , flags ) ;
2013-10-31 01:24:07 +01:00
if ( enc - > codec_id = = AV_CODEC_ID_VP6 )
avio_w8 ( pb , 0 ) ;
2013-08-15 10:07:30 +02:00
if ( enc - > codec_id = = AV_CODEC_ID_VP6F | | enc - > codec_id = = AV_CODEC_ID_VP6A ) {
if ( enc - > extradata_size )
avio_w8 ( pb , enc - > extradata [ 0 ] ) ;
else
avio_w8 ( pb , ( ( FFALIGN ( enc - > width , 16 ) - enc - > width ) < < 4 ) |
( FFALIGN ( enc - > height , 16 ) - enc - > height ) ) ;
} else if ( enc - > codec_id = = AV_CODEC_ID_AAC )
2012-06-27 10:16:18 +02:00
avio_w8 ( pb , 1 ) ; // AAC raw
2012-08-07 22:45:46 +02:00
else if ( enc - > codec_id = = AV_CODEC_ID_H264 | | enc - > codec_id = = AV_CODEC_ID_MPEG4 ) {
2012-06-27 10:16:18 +02:00
avio_w8 ( pb , 1 ) ; // AVC NALU
avio_wb24 ( pb , pkt - > pts - pkt - > dts ) ;
}
2009-01-16 02:22:30 +01:00
2012-06-27 10:16:18 +02:00
avio_write ( pb , data ? data : pkt - > data , size ) ;
2009-01-16 02:22:30 +01:00
2012-06-27 10:16:18 +02:00
avio_wb32 ( pb , size + flags_size + 11 ) ; // previous tag size
flv - > duration = FFMAX ( flv - > duration ,
pkt - > pts + flv - > delay + pkt - > duration ) ;
2012-05-09 23:35:58 +02:00
}
2009-01-16 02:22:30 +01:00
av_free ( data ) ;
2011-06-06 16:13:05 +02:00
return pb - > error ;
2003-07-10 01:10:59 +02:00
}
2015-11-18 22:44:08 +01:00
static const AVOption options [ ] = {
{ " flvflags " , " FLV muxer flags " , offsetof ( FLVContext , flags ) , AV_OPT_TYPE_FLAGS , { . i64 = 0 } , INT_MIN , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , " flvflags " } ,
{ " aac_seq_header_detect " , " Put AAC sequence header based on stream data " , 0 , AV_OPT_TYPE_CONST , { . i64 = 1 } , INT_MIN , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM , " flvflags " } ,
{ NULL } ,
} ;
static const AVClass flv_muxer_class = {
. class_name = " flv muxer " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2011-01-25 23:03:28 +01:00
AVOutputFormat ff_flv_muxer = {
2011-07-16 22:18:12 +02:00
. name = " flv " ,
2012-07-24 03:23:48 +02:00
. long_name = NULL_IF_CONFIG_SMALL ( " FLV (Flash Video) " ) ,
2011-07-16 22:18:12 +02:00
. mime_type = " video/x-flv " ,
. extensions = " flv " ,
. priv_data_size = sizeof ( FLVContext ) ,
2012-08-05 11:11:04 +02:00
. audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF ,
. video_codec = AV_CODEC_ID_FLV1 ,
2011-07-16 22:18:12 +02:00
. write_header = flv_write_header ,
. write_packet = flv_write_packet ,
. write_trailer = flv_write_trailer ,
2012-06-27 10:16:18 +02:00
. codec_tag = ( const AVCodecTag * const [ ] ) {
flv_video_codec_ids , flv_audio_codec_ids , 0
} ,
2011-05-26 20:19:04 +02:00
. flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
AVFMT_TS_NONSTRICT ,
2015-11-18 22:44:08 +01:00
. priv_class = & flv_muxer_class ,
2003-07-10 01:10:59 +02:00
} ;