move time_base (pts_num/pts_den) from AVFormatContext -> AVStream
[ffmpeg.git] / libavformat / flvenc.c
index b717bbb..a44ebd9 100644 (file)
  */
 #include "avformat.h"
 
+#undef NDEBUG
+#include <assert.h>
+
 #define VIDEO_FIFO_SIZE 512
 
 typedef struct FLVFrame {
     int type;
     int timestamp;
+    int reserved;
     int flags;
     uint8_t *data;
     int size;
@@ -32,26 +36,15 @@ typedef struct FLVFrame {
 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;
+    int reserved;
     FLVFrame *frames;
 } FLVContext;
 
-
 #ifdef CONFIG_MP3LAME
 
-#define AUDIO_FIFO_SIZE 65536
-
 static const int sSampleRates[3][4] = {
     {44100, 48000, 32000, 0},
     {22050, 24000, 16000, 0},
@@ -98,55 +91,53 @@ static int mp3info(void *data, int *byteSize, int *samplesPerFrame, int *sampleR
     }
 
     if ( !isPadded ) {
-        printf("Fatal error: mp3 data is not padded!\n");
-        exit(0);
+//        printf("Fatal error: mp3 data is not padded!\n");
+//        exit(0);
     }
 
     *isMono = ((header >>  6) & 0x03) == 0x03;
 
     if ( (header >> 19 ) & 0x01 ) {
+        //MPEG1
         *sampleRate = sSampleRates[0][sampleRateID];
         bitRate = sBitRates[0][layerID][bitRateID] * 1000;
         *samplesPerFrame = sSamplesPerFrame[0][layerID];
-
     } else {
         if ( (header >> 20) & 0x01 ) {
+            //MPEG2
             *sampleRate = sSampleRates[1][sampleRateID];
             bitRate = sBitRates[1][layerID][bitRateID] * 1000;
             *samplesPerFrame = sSamplesPerFrame[1][layerID];
         } else {
+            //MPEG2.5
             *sampleRate = sSampleRates[2][sampleRateID];
             bitRate = sBitRates[1][layerID][bitRateID] * 1000;
             *samplesPerFrame = sSamplesPerFrame[2][layerID];
         }
     }
-
-    *byteSize = ( ( ( ( *samplesPerFrame * (bitRate / bitsPerSlot) ) / *sampleRate ) + isPadded ) * bitsPerSlot);
-
+    
+    *byteSize = ( ( ( ( *samplesPerFrame * (bitRate / bitsPerSlot) ) / *sampleRate ) + isPadded ) );
     return 1;
 }
 #endif // CONFIG_MP3LAME
 
+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 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;
 
     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;
 
@@ -155,17 +146,23 @@ static int flv_write_header(AVFormatContext *s)
     put_byte(pb,0); // delayed write
     put_be32(pb,9);
     put_be32(pb,0);
+    
+    for(i=0; i<s->nb_streams; i++){
+        AVCodecContext *enc = &s->streams[i]->codec;
+        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;
+        }
+    }
 
     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 ) {
@@ -174,7 +171,7 @@ static void InsertSorted(FLVContext *flv, FLVFrame *frame)
         FLVFrame *trav = flv->frames;
         FLVFrame *prev = 0;
         for (;trav;) {
-            if ( trav->timestamp >= frame->timestamp ) {
+            if ( trav->timestamp > frame->timestamp) {
                 frame->next = trav;
                 if ( prev ) {
                     prev->next = frame;
@@ -194,10 +191,11 @@ static void InsertSorted(FLVContext *flv, FLVFrame *frame)
 
 static void DumpFrame(ByteIOContext *pb, FLVFrame *frame)
 {
+//av_log(NULL, AV_LOG_DEBUG, "T%02X S%d T%d R%d F%02X ... R%08X\n", frame->type, frame->size+1, frame->timestamp, 0, frame->flags, frame->size+1+11);
     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_be32(pb,frame->reserved); // reserved
     put_byte(pb,frame->flags);
     put_buffer(pb, frame->data, frame->size);
     put_be32(pb,frame->size+1+11); // reserved
@@ -224,13 +222,15 @@ static void Dump(FLVContext *flv, ByteIOContext *pb, int count)
 
 static int flv_write_trailer(AVFormatContext *s)
 {
+    int64_t file_size;
+    int flags = 0;
+
     ByteIOContext *pb = &s->pb;
     FLVContext *flv = s->priv_data;
 
     Dump(flv,pb,1);
 
-    int64_t file_size = url_ftell(pb);
-    int flags = 0;
+    file_size = url_ftell(pb);
     flags |= flv->hasAudio ? 4 : 0;
     flags |= flv->hasVideo ? 1 : 0;
     url_fseek(pb, 4, SEEK_SET);
@@ -240,24 +240,28 @@ static int flv_write_trailer(AVFormatContext *s)
 }
 
 static int flv_write_packet(AVFormatContext *s, int stream_index,
-                            uint8_t *buf, int size, int timestamp)
+                            const uint8_t *buf, int size, int64_t timestamp)
 {
     ByteIOContext *pb = &s->pb;
     AVCodecContext *enc = &s->streams[stream_index]->codec;
     FLVContext *flv = s->priv_data;
-
+    FLVFrame *frame = av_malloc(sizeof(FLVFrame));
+
+    frame->next = 0;
+    frame->size = size;
+    frame->data = av_malloc(size);
+    frame->timestamp = timestamp;
+    frame->reserved= flv->reserved;
+    memcpy(frame->data,buf,size);
+    
+//    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);
@@ -265,93 +269,81 @@ static int flv_write_packet(AVFormatContext *s, int stream_index,
         flv->frameCount ++;
     }
     else if (enc->codec_type == CODEC_TYPE_AUDIO) {
-#ifdef CONFIG_MP3LAME
-        if (enc->codec_id == CODEC_ID_MP3LAME ) {
-            int c=0;
-            for (;c<size;c++) {
-                flv->audioFifo[(flv->audioOutPos+c)%AUDIO_FIFO_SIZE] = buf[c];
-            }
-            flv->audioSize += size;
-            flv->audioOutPos += size;
-            flv->audioOutPos %= AUDIO_FIFO_SIZE;
+        int soundFormat = 0x02;
 
-            if ( flv->initDelay == -1 ) {
-                flv->initDelay = timestamp;
-            }
+        switch (enc->sample_rate) {
+            case    44100:
+                soundFormat |= 0x0C;
+                break;
+            case    22050:
+                soundFormat |= 0x08;
+                break;
+            case    11025:
+                soundFormat |= 0x04;
+                break;
+            case     8000: //nellymoser only
+            case     5512: //not mp3
+                soundFormat |= 0x00;
+                break;
+            default:
+                assert(0);
+        }
+
+        if (enc->channels > 1) {
+            soundFormat |= 0x01;
+        }
+        
+        switch(enc->codec_id){
+        case CODEC_ID_MP3:
+            soundFormat |= 0x20;
+            break;
+        case 0:
+            soundFormat |= enc->codec_tag<<4;
+            break;
+        default:
+            assert(0);
+        }
 
-            if ( flv->audioTime == -1 ) {
-                flv->audioTime = timestamp;
+        assert(size);
+        if ( flv->initDelay == -1 ) {
+            flv->initDelay = timestamp;
+        }
+
+        frame->type = 8;
+        frame->flags = soundFormat;
+
+//            if ( flv->audioTime == -1 ) {
 //                flv->audioTime = ( ( ( flv->sampleCount - enc->delay ) * 8000 ) / flv->audioRate ) - flv->initDelay - 250;
 //                if ( flv->audioTime < 0 ) {
 //                    flv->audioTime = 0;
 //                }
-            }
-        }
-        for ( ; flv->audioSize >= 4 ; ) {
+//            }
 
+#ifdef CONFIG_MP3LAME
+        if (enc->codec_id == CODEC_ID_MP3 ) {
             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 (;c<mp3FrameSize;c++) {
-                        frame->data[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;
+            /* copy out mp3 header from ring buffer */
+            if(!mp3info(buf,&mp3FrameSize,&mp3SamplesPerFrame,&mp3SampleRate,&mp3IsMono))
+                assert(0);
+            assert ( size == mp3FrameSize );
+            assert(enc->sample_rate == mp3SampleRate);
+//            assert(enc->frame_size == mp3SamplesPerFrame);
+//av_log(NULL, AV_LOG_DEBUG, "sizes: %d %d\n", enc->frame_size, mp3SamplesPerFrame);
+
+            frame->timestamp = (1000*flv->sampleCount + enc->sample_rate/2)/(enc->sample_rate);
+            flv->sampleCount += mp3SamplesPerFrame;
         }
 #endif
-    }
+
+        // We got audio! Make sure we set this to the global flags on closure
+        flv->hasAudio = 1;
+        InsertSorted(flv,frame);
+    }else
+        assert(0);
     Dump(flv,pb,128);
     put_flush_packet(pb);
     return 0;
@@ -364,7 +356,7 @@ static AVOutputFormat flv_oformat = {
     "flv",
     sizeof(FLVContext),
 #ifdef CONFIG_MP3LAME
-    CODEC_ID_MP3LAME,
+    CODEC_ID_MP3,
 #else // CONFIG_MP3LAME
     CODEC_ID_NONE,
 #endif // CONFIG_MP3LAME