AAC與g711音頻RTMP推流實踐

 

      前面完成了視頻RTMP推流實踐,本文介紹RTMP的音頻推流,包括AACg711a,g711u三種場景音頻推流。基於前面的視頻推流實踐,我們新增了推流AAC,g711a,g711u的三個接口。分別爲SendAAcData(),sendg711a_audio(),sendg711u_audio(),對外提供API調用。接口類對外定義如下:   

class Wrapper_RtmpLib
{
public:
    Wrapper_RtmpLib(char * url);
    ~Wrapper_RtmpLib();
    int Open();
    int SendVideoData(char * data,int dataLength, unsigned int timeStamp);   
    int SendAAcData(char * data,int dataLength, unsigned int timeStamp);  
    int sendg711a_audio(unsigned char *buf,int len,unsigned int timeStamp);  
    int sendg711u_audio(unsigned char *buf,int len,unsigned int timeStamp);
    int IsConnect();
    int Close();
private:
         ………由於篇幅所限,這裏省略
}; 
  1. AAC推流實踐:

SendAAcData 接收上層傳來的AAC幀數據,長度和時間戳。其中AAC幀數據包括ADTS頭和RAW的AAC數據。該函數從上層接收一幀數據,將ADTS頭組裝一個packet 通過sendaac_headerconfig發送出去,然後再將raw的AAC數據打包通過sendaac_rawaudio發送出去。

int Wrapper_RtmpLib::SendAAcData(char * data,int dataLength, unsigned int timeStamp)
{
    int ret = -1;
    uint8_t audioSpecificConfig[2] = { 0 };
    AdtsKeyHeader tAdtsKeyHeader;
    GetAdtsKeyConfig(data, &tAdtsKeyHeader);
    GetAAcSpecificConfig(tAdtsKeyHeader.nProfile+1, tAdtsKeyHeader.nSfIndex, tAdtsKeyHeader.nChannelConfiguration, audioSpecificConfig);
    printf("nProfile %d nSfIndex %d nChannelConfiguration %d \n", tAdtsKeyHeader.nProfile, tAdtsKeyHeader.nSfIndex, tAdtsKeyHeader.nChannelConfiguration);
    ret = sendaac_headerconfig(audioSpecificConfig, 2, 0);
    if(ret==-1)
     {
        printf("send key frame is failed\n");
     }
      else
     {
         printf("send key frame now len 7\n");
     }
     ret = sendaac_rawaudio((unsigned char *)data+7,dataLength-7,timeStamp);
     if(ret==-1)
     {
          printf("send raw frame is failed\n");
     }
     else
     {
          printf("send raw frame now len %d \n", tAdtsKeyHeader.nAacFrameLength-7);
     }
}

AAC 推流的Demo使用如下:

#include <cstdio>
#include"Wrapper_RtmpLib.hpp"
#include <unistd.h>
#include<string.h>
#include <signal.h>
#include<time.h>
#include <errno.h>
uint8_t tag0 = 0xff;
uint8_t tag1 = 0xf0;
void help(char *p)
{
    printf("Use:");
    printf("%s AudioFile RTMP_URL \n",p);
}
int main(int argc,char *argv[])
{
    if(argc<3)
   {
       help(argv[0]);
       return 1;
    }
    uint8_t FrameBuffer[4096];
    signal(SIGPIPE, SIG_IGN);
    Wrapper_RtmpLib test(argv[2]);
    if (test.Open() < 0)
    {
        printf("open is failed\n");
        return 0;
    }
    if (test.IsConnect() < 0)
     {
        printf("connect is failed\n");
        return 0;
    }
    else
        printf("connect is ok\n");
    //uint8_t adtsheader[7];
    FILE *fd = fopen(argv[1], "rb+");
    if (fd == NULL)
    {
        printf("fopen is failed,err %d\n", errno);
        return 0;
    }
    void *pStart = NULL;
    void *pEnd = NULL;
    int count = 0;
    unsigned int timestamp = 0;
    while (1)
    {
        int ret = fread(FrameBuffer, 7, 1, fd);
        if (ret != 1)
        {
           printf("fread header is failed,err %d\n", errno);
           return 0;
        }

        if ((FrameBuffer[0] == tag0) && (FrameBuffer[1] & 0xf0 == tag1))
            //  if ((FrameBuffer[0] == '\xFF') && (FrameBuffer[1] & 0xf0 == '\xF0'))
        {
            //分析頭數據,讀裸露數據
            bs_s bitbuffer_Robj;
            bs_init(&bitbuffer_Robj, FrameBuffer, 7);

            bs_skip(&bitbuffer_Robj, 30);//調過30bit,找到len字段
            int len = bs_read(&bitbuffer_Robj, 13);
            printf("raw_len = %d\n", len);

            int ret = fread(FrameBuffer+7, len-7, 1, fd);
            if (ret != 1)
            {
                printf("fread raw block is failed,err %d\n", errno);
                return 0;
            }
                  test.SendAAcData((char *)FrameBuffer, len, timestamp);
                  timestamp += 22;
                    usleep(1000*22);
        }
    }
    printf("data is endof now");
    getchar();
}

這裏需要說明的是:因爲AAC每一幀是1024個採樣,所以一幀的時間間隔是:1026/48Khz=21.33ms,故設置時間戳間隔爲22ms(注意我的測試程序AAC採用頻率爲48K hz)。

2)g711推流實踐

 g711a和g711u推流接口爲sendg711a_audio()和sendg711u_audio()。這個2個接口實現原理是一樣。就是直接將raw數據推送出去即可。但畢竟是兩個不同格式,rtmp的數據包的tag頭不一樣。後面我們會講到rtmp的音頻數據tag頭定義。

#include <cstdio>
#include"Wrapper_RtmpLib.hpp"
#include <unistd.h>
#include<string.h>
#include <signal.h>
#include<time.h>
#include <errno.h>
uint8_t tag0 = 0xff;
uint8_t tag1 = 0xf0;
void help(char *p)
{
    printf("Use:");
    printf("%s AudioFile RTMP_URL \n",p);
}
int main(int argc,char *argv[])
{
    if(argc<3)
    {
        help(argv[0]);
        return 1;
    }
    uint8_t FrameBuffer[4096];
    signal(SIGPIPE, SIG_IGN);
    Wrapper_RtmpLib test(argv[2]);
    if (test.Open() < 0)
    {
        printf("open is failed\n");
        return 0;
    }
    if (test.IsConnect() < 0)
    {
        printf("connect is failed\n");
        return 0;
    }
    else
        printf("connect is ok\n");
    //uint8_t adtsheader[7];
    FILE *fd = fopen(argv[1], "rb+");
    if (fd == NULL)
    {
        printf("fopen is failed,err %d\n", errno);
        return 0;
    }
    int count = 0;
    unsigned int timestamp = 0;
    while (1)
    {
        int ret = fread(FrameBuffer, 640, 1, fd);
        if (ret != 1)
        {
           printf("fread header is failed,err %d\n", errno);
           return 0;
        }
        test.sendg711a_audio((unsigned char *)FrameBuffer, 640, timestamp);
        printf("send g711a now 80\n");
        timestamp += 80;
        usleep(1000*40);    
    }
    printf("data is endof now");
    getchar();
}

這裏注意:因爲測試g711a是大華IPC抓下來的包。大華IPC的g711是80ms打一個包(RTP時間戳增量爲640,真實時間間隔爲640/8000=80ms),所以時間戳增量爲80ms.但考慮網絡延時和處理開銷,這裏每個包只延時了40ms就發送下一個包(這個根據實際情況決定)。因爲80ms打一個包,包的大小恰好爲640個字節(80ms*8000hz*2/2=640,g711壓縮率爲2),所以每次拷貝640個字節(即一個RTP包的音頻負載)。由於g711a和g711u算法類似,推流方式也類似,g711u這裏不再贅述。

RTMP的音頻tag頭

 在RTMP發送音頻數據包,包必須包括tag頭+音頻數據。類似flv的tag+data的數據格式。但由於flv和RTMP格式是兼容的,所以tag頭=RTMP的body頭。

具體定義如下:

總結起來如下:

body第一個字節:

Bit7 bit6 bit5 bit4     |bit3 bit2    |bit1          | bit0

format               rate        bit depth       soudtype

body第二個字節

Bit7~bit0

if format=10

0:AAC config 1:aac raw

根據以上規範

AAC :

body第一個字節a|11|1|1=0xAF

body第二個字節

AAC config =0

AAC rawdata = 1

實測AAC的 body第一個字節低4bit影響不大。解碼器主要還是根據發送的config數據來解碼的。

g711a

body第一個字節7|01|1|1 =0x77 

//雙通道,16bit採樣精度,8K採樣頻率

g711u:

body 第一個字節 8|01|1|1 =0x87

//雙通道,16bit採樣精度,8K採樣頻率

更多更詳細資源請關注公衆號:AV_Chat

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章