X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fflvenc.c;h=de1dd0801bc21a6b5525b1f095eb0eb207ec0356;hb=dd15f9bf48e74289a16f14a7de54fd55c3f09b76;hp=72abb657b174d8f73b2b2f5d99452b481b2e2df4;hpb=14b3225377998175b0ce1026432d179d34d179c0;p=ffmpeg diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c index 72abb657b17..de1dd0801bc 100644 --- a/libavformat/flvenc.c +++ b/libavformat/flvenc.c @@ -14,212 +14,194 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" -#define VIDEO_FIFO_SIZE 512 - -typedef struct FLVFrame { - int type; - int timestamp; - int flags; - uint8_t *data; - int size; - struct FLVFrame *next; -} FLVFrame; +#undef NDEBUG +#include typedef struct FLVContext { int hasAudio; int hasVideo; -#ifdef CONFIG_MP3LAME - int audioTime; - int audioInPos; - int audioOutPos; - int audioSize; - int audioRate; - int initDelay; - int soundDelay; - uint8_t *audioFifo; - int64_t sampleCount; -#endif // CONFIG_MP3LAME - int64_t frameCount; - FLVFrame *frames; + int reserved; } FLVContext; - -#ifdef CONFIG_MP3LAME - -#define AUDIO_FIFO_SIZE 65536 - -static const int sSampleRates[3][4] = { - {44100, 48000, 32000, 0}, - {22050, 24000, 16000, 0}, - {11025, 12000, 8000, 0}, -}; - -static const int sBitRates[2][3][15] = { - { { 0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448}, - { 0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384}, - { 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320} - }, - { { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160} - }, -}; - -static const int sSamplesPerFrame[3][3] = -{ - { 384, 1152, 1152 }, - { 384, 1152, 576 }, - { 384, 1152, 576 } -}; - -static const int sBitsPerSlot[3] = { - 32, - 8, - 8 -}; - -static int mp3info(void *data, int *byteSize, int *samplesPerFrame, int *sampleRate, int *isMono ) -{ - uint8_t *dataTmp = (uint8_t *)data; - uint32_t header = ( (uint32_t)dataTmp[0] << 24 ) | ( (uint32_t)dataTmp[1] << 16 ) | ( (uint32_t)dataTmp[2] << 8 ) | (uint32_t)dataTmp[3]; - int layerID = 3 - ((header >> 17) & 0x03); - int bitRateID = ((header >> 12) & 0x0f); - int sampleRateID = ((header >> 10) & 0x03); - int bitRate = 0; - int bitsPerSlot = sBitsPerSlot[layerID]; - int isPadded = ((header >> 9) & 0x01); - - if ( (( header >> 21 ) & 0x7ff) != 0x7ff ) { - return 0; +static int get_audio_flags(AVCodecContext *enc){ + int flags = (enc->bits_per_sample == 16) ? 0x2 : 0x0; + + switch (enc->sample_rate) { + case 44100: + flags |= 0x0C; + break; + case 22050: + flags |= 0x08; + break; + case 11025: + flags |= 0x04; + break; + case 8000: //nellymoser only + case 5512: //not mp3 + flags |= 0x00; + break; + default: + av_log(enc, AV_LOG_ERROR, "flv doesnt support that sample rate, choose from (44100, 22050, 11025)\n"); + return -1; } - if ( !isPadded ) { - printf("Fatal error: mp3 data is not padded!\n"); - exit(0); + if (enc->channels > 1) { + flags |= 0x01; } - *isMono = ((header >> 6) & 0x03) == 0x03; + switch(enc->codec_id){ + case CODEC_ID_MP3: + flags |= 0x20 | 0x2; + break; + case CODEC_ID_PCM_S8: + break; + case CODEC_ID_PCM_S16BE: + flags |= 0x60 | 0x2; + break; + case CODEC_ID_PCM_S16LE: + flags |= 0x2; + break; + case CODEC_ID_ADPCM_SWF: + flags |= 0x10; + break; + case 0: + flags |= enc->codec_tag<<4; + break; + default: + av_log(enc, AV_LOG_ERROR, "codec not compatible with flv\n"); + return -1; + } - if ( (header >> 19 ) & 0x01 ) { - *sampleRate = sSampleRates[0][sampleRateID]; - bitRate = sBitRates[0][layerID][bitRateID] * 1000; - *samplesPerFrame = sSamplesPerFrame[0][layerID]; + return flags; +} - } else { - if ( (header >> 20) & 0x01 ) { - *sampleRate = sSampleRates[1][sampleRateID]; - bitRate = sBitRates[1][layerID][bitRateID] * 1000; - *samplesPerFrame = sSamplesPerFrame[1][layerID]; - } else { - *sampleRate = sSampleRates[2][sampleRateID]; - bitRate = sBitRates[1][layerID][bitRateID] * 1000; - *samplesPerFrame = sSamplesPerFrame[2][layerID]; - } - } +#define AMF_DOUBLE 0 +#define AMF_BOOLEAN 1 +#define AMF_STRING 2 +#define AMF_OBJECT 3 +#define AMF_MIXED_ARRAY 8 +#define AMF_ARRAY 10 +#define AMF_DATE 11 - *byteSize = ( ( ( ( *samplesPerFrame * (bitRate / bitsPerSlot) ) / *sampleRate ) + isPadded ) * bitsPerSlot); +static void put_amf_string(ByteIOContext *pb, const char *str) +{ + size_t len = strlen(str); + put_be16(pb, len); + put_buffer(pb, str, len); +} - return 1; +static void put_amf_double(ByteIOContext *pb, double d) +{ + put_byte(pb, AMF_DOUBLE); + put_be64(pb, av_dbl2int(d)); } -#endif // CONFIG_MP3LAME static int flv_write_header(AVFormatContext *s) { ByteIOContext *pb = &s->pb; FLVContext *flv = s->priv_data; - - av_set_pts_info(s, 24, 1, 1000); /* 24 bit pts in ms */ + int i, width, height, samplerate; + double framerate = 0.0; + int metadata_size_pos, data_size; flv->hasAudio = 0; flv->hasVideo = 0; -#ifdef CONFIG_MP3LAME - flv->audioTime = -1; - flv->audioFifo = av_malloc(AUDIO_FIFO_SIZE); - flv->audioInPos = 0; - flv->audioOutPos = 0; - flv->audioSize = 0; - flv->audioRate = 44100; - flv->initDelay = -1; - flv->soundDelay = 0; -#endif // CONFIG_MP3LAME - - flv->frames = 0; - put_tag(pb,"FLV"); put_byte(pb,1); put_byte(pb,0); // delayed write put_be32(pb,9); put_be32(pb,0); - return 0; -} - -static void put_be24(ByteIOContext *pb, int value) -{ - put_byte(pb, (value>>16) & 0xFF ); - put_byte(pb, (value>> 8) & 0xFF ); - put_byte(pb, (value>> 0) & 0xFF ); -} - -static void InsertSorted(FLVContext *flv, FLVFrame *frame) -{ - if ( !flv->frames ) { - flv->frames = frame; - } else { - FLVFrame *trav = flv->frames; - FLVFrame *prev = 0; - for (;trav;) { - if ( trav->timestamp >= frame->timestamp ) { - frame->next = trav; - if ( prev ) { - prev->next = frame; - } else { - flv->frames = frame; - } - break; + for(i=0; inb_streams; i++){ + AVCodecContext *enc = s->streams[i]->codec; + if (enc->codec_type == CODEC_TYPE_VIDEO) { + width = enc->width; + height = enc->height; + if (s->streams[i]->r_frame_rate.den && s->streams[i]->r_frame_rate.num) { + framerate = av_q2d(s->streams[i]->r_frame_rate); + } else { + framerate = 1/av_q2d(s->streams[i]->codec->time_base); } - prev = trav; - trav = trav->next; + flv->hasVideo=1; + } else { + flv->hasAudio=1; + samplerate = enc->sample_rate; } - if ( !trav ) { - prev->next = frame; + av_set_pts_info(s->streams[i], 24, 1, 1000); /* 24 bit pts in ms */ + if(enc->codec_tag == 5){ + put_byte(pb,8); // message type + put_be24(pb,0); // include flags + put_be24(pb,0); // time stamp + put_be32(pb,0); // reserved + put_be32(pb,11); // size + flv->reserved=5; } + if(enc->codec_type == CODEC_TYPE_AUDIO && get_audio_flags(enc)<0) + return -1; } -} -static void DumpFrame(ByteIOContext *pb, FLVFrame *frame) -{ - put_byte(pb,frame->type); // message type - put_be24(pb,frame->size+1); // include flags - put_be24(pb,frame->timestamp); // time stamp - put_be32(pb,0); // reserved - put_byte(pb,frame->flags); - put_buffer(pb, frame->data, frame->size); - put_be32(pb,frame->size+1+11); // reserved - av_free(frame->data); -} + /* write meta_tag */ + put_byte(pb, 18); // tag type META + metadata_size_pos= url_ftell(pb); + put_be24(pb, 0); // size of data part (sum of all parts below) + put_be24(pb, 0); // time stamp + put_be32(pb, 0); // reserved -static void Dump(FLVContext *flv, ByteIOContext *pb, int count) -{ - int c=0; - FLVFrame *trav = flv->frames; - FLVFrame *prev = 0; - for (;trav;c++) { - trav = trav->next; + /* now data of data_size size */ + + /* first event name as a string */ + put_byte(pb, AMF_STRING); // 1 byte + put_amf_string(pb, "onMetaData"); // 12 bytes + + /* mixed array (hash) with size and string/type/data tuples */ + put_byte(pb, AMF_MIXED_ARRAY); + put_be32(pb, 4*flv->hasVideo + flv->hasAudio + (!!s->file_size) + (s->duration != AV_NOPTS_VALUE && s->duration)); + + if (s->duration != AV_NOPTS_VALUE && s->duration) { + put_amf_string(pb, "duration"); + put_amf_double(pb, s->duration / (double)AV_TIME_BASE); + } + + if(flv->hasVideo){ + put_amf_string(pb, "width"); + put_amf_double(pb, width); + + put_amf_string(pb, "height"); + put_amf_double(pb, height); + + put_amf_string(pb, "videodatarate"); + put_amf_double(pb, s->bit_rate / 1024.0); + + put_amf_string(pb, "framerate"); + put_amf_double(pb, framerate); + } + + if(flv->hasAudio){ + put_amf_string(pb, "audiosamplerate"); + put_amf_double(pb, samplerate); } - trav = flv->frames; - for ( ; c >= count; c-- ) { - DumpFrame(pb,trav); - prev = trav; - trav = trav->next; - av_free(prev); + + if(s->file_size){ + put_amf_string(pb, "filesize"); + put_amf_double(pb, s->file_size); } - flv->frames = trav; + + put_amf_string(pb, ""); + put_byte(pb, 9); // end marker 1 byte + + /* write total size of tag */ + data_size= url_ftell(pb) - metadata_size_pos - 10; + url_fseek(pb, metadata_size_pos, SEEK_SET); + put_be24(pb, data_size); + url_fseek(pb, data_size + 10 - 3, SEEK_CUR); + put_be32(pb, data_size + 11); + + return 0; } static int flv_write_trailer(AVFormatContext *s) @@ -230,8 +212,6 @@ static int flv_write_trailer(AVFormatContext *s) ByteIOContext *pb = &s->pb; FLVContext *flv = s->priv_data; - Dump(flv,pb,1); - file_size = url_ftell(pb); flags |= flv->hasAudio ? 4 : 0; flags |= flv->hasVideo ? 1 : 0; @@ -241,132 +221,48 @@ static int flv_write_trailer(AVFormatContext *s) return 0; } -static int flv_write_packet(AVFormatContext *s, int stream_index, - uint8_t *buf, int size, int timestamp) +static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) { ByteIOContext *pb = &s->pb; - AVCodecContext *enc = &s->streams[stream_index]->codec; + AVCodecContext *enc = s->streams[pkt->stream_index]->codec; FLVContext *flv = s->priv_data; + int size= pkt->size; + int flags; + +// av_log(s, AV_LOG_DEBUG, "type:%d pts: %lld size:%d\n", enc->codec_type, timestamp, size); if (enc->codec_type == CODEC_TYPE_VIDEO) { - FLVFrame *frame = av_malloc(sizeof(FLVFrame)); - frame->next = 0; - frame->type = 9; - frame->flags = 2; // choose h263 - frame->flags |= enc->coded_frame->key_frame ? 0x10 : 0x20; // add keyframe indicator - frame->timestamp = timestamp; - //frame->timestamp = ( ( flv->frameCount * (int64_t)FRAME_RATE_BASE * (int64_t)1000 ) / (int64_t)enc->frame_rate ); - //printf("%08x %f %f\n",frame->timestamp,(double)enc->frame_rate/(double)FRAME_RATE_BASE,1000*(double)FRAME_RATE_BASE/(double)enc->frame_rate); - frame->size = size; - frame->data = av_malloc(size); - memcpy(frame->data,buf,size); - flv->hasVideo = 1; - - InsertSorted(flv,frame); - - flv->frameCount ++; - } - else if (enc->codec_type == CODEC_TYPE_AUDIO) { -#ifdef CONFIG_MP3LAME - if (enc->codec_id == CODEC_ID_MP3LAME ) { - int c=0; - for (;caudioFifo[(flv->audioOutPos+c)%AUDIO_FIFO_SIZE] = buf[c]; - } - flv->audioSize += size; - flv->audioOutPos += size; - flv->audioOutPos %= AUDIO_FIFO_SIZE; + put_byte(pb, 9); + flags = 2; // choose h263 + flags |= pkt->flags & PKT_FLAG_KEY ? 0x10 : 0x20; // add keyframe indicator + } else { + assert(enc->codec_type == CODEC_TYPE_AUDIO); + flags = get_audio_flags(enc); - if ( flv->initDelay == -1 ) { - flv->initDelay = timestamp; - } + assert(size); - if ( flv->audioTime == -1 ) { - flv->audioTime = timestamp; -// flv->audioTime = ( ( ( flv->sampleCount - enc->delay ) * 8000 ) / flv->audioRate ) - flv->initDelay - 250; -// if ( flv->audioTime < 0 ) { -// flv->audioTime = 0; -// } - } - } - for ( ; flv->audioSize >= 4 ; ) { - - int mp3FrameSize = 0; - int mp3SampleRate = 0; - int mp3IsMono = 0; - int mp3SamplesPerFrame = 0; - - if ( mp3info(&flv->audioFifo[flv->audioInPos],&mp3FrameSize,&mp3SamplesPerFrame,&mp3SampleRate,&mp3IsMono) ) { - if ( flv->audioSize >= mp3FrameSize ) { - - int soundFormat = 0x22; - int c=0; - FLVFrame *frame = av_malloc(sizeof(FLVFrame)); - - flv->audioRate = mp3SampleRate; - - switch (mp3SampleRate) { - case 44100: - soundFormat |= 0x0C; - break; - case 22050: - soundFormat |= 0x08; - break; - case 11025: - soundFormat |= 0x04; - break; - } - - if ( !mp3IsMono ) { - soundFormat |= 0x01; - } - - frame->next = 0; - frame->type = 8; - frame->flags = soundFormat; - frame->timestamp = flv->audioTime; - frame->size = mp3FrameSize; - frame->data = av_malloc(mp3FrameSize); - - for (;cdata[c] = flv->audioFifo[(flv->audioInPos+c)%AUDIO_FIFO_SIZE]; - } - - flv->audioInPos += mp3FrameSize; - flv->audioSize -= mp3FrameSize; - flv->audioInPos %= AUDIO_FIFO_SIZE; - flv->sampleCount += mp3SamplesPerFrame; - - // Reset audio for next round - flv->audioTime = -1; - // We got audio! Make sure we set this to the global flags on closure - flv->hasAudio = 1; - - InsertSorted(flv,frame); - } - break; - } - flv->audioInPos ++; - flv->audioSize --; - flv->audioInPos %= AUDIO_FIFO_SIZE; - // no audio in here! - flv->audioTime = -1; - } -#endif + put_byte(pb, 8); } - Dump(flv,pb,128); + + put_be24(pb,size+1); // include flags + put_be24(pb,pkt->pts); + put_be32(pb,flv->reserved); + put_byte(pb,flags); + put_buffer(pb, pkt->data, size); + put_be32(pb,size+1+11); // previous tag size + put_flush_packet(pb); return 0; } -static AVOutputFormat flv_oformat = { +AVOutputFormat flv_muxer = { "flv", "flv format", - "video/x-flashvideo", + "video/x-flv", "flv", sizeof(FLVContext), #ifdef CONFIG_MP3LAME - CODEC_ID_MP3LAME, + CODEC_ID_MP3, #else // CONFIG_MP3LAME CODEC_ID_NONE, #endif // CONFIG_MP3LAME @@ -375,9 +271,3 @@ static AVOutputFormat flv_oformat = { flv_write_packet, flv_write_trailer, }; - -int flvenc_init(void) -{ - av_register_output_format(&flv_oformat); - return 0; -}