1. 程式人生 > >Dump Rtmp Audio Stream To AAC Formate File (從Rtmp流提取並儲存AAC音訊檔案)

Dump Rtmp Audio Stream To AAC Formate File (從Rtmp流提取並儲存AAC音訊檔案)

一、準備工作

  參考:https://www.cnblogs.com/doudouyoutang/p/10220599.html

  搭建本地rtmp服務:

  https://www.cnblogs.com/doudouyoutang/p/6602430.html

  獲取使用到的庫,openssl 和 librtmp

  參考:

  https://www.jianshu.com/p/b38656443e71
  https://github.com/x2on/OpenSSL-for-iPhone

  也可以從我的工程中直接拿 https://github.com/liqiushui/RtmpDumpAsAAC

 

二、關鍵解釋:

  RTMP的Message音訊和視訊分開發送的,音訊和視訊的傳送類似,第一次會收到一個AAC Sequence Header,這裡麵包含音訊格式的描述資訊

  Message 判斷為音訊之後,通過判斷前兩個位元組可以得到後面是 AAC Sequence Header 還是 AAC裸資料

 if(packet.m_packetType == RTMP_PACKET_TYPE_AUDIO)
        {
            //Audio Packet
            //FLV Audio Tag 原始資料,包含Tag Header, 音訊Tag Header一般由一個位元組定義(AAC用兩個位元組)
            //第一個位元組的定義如下:音訊格式 4bits | 取樣率 2bits | 取樣精度 1bits | 聲道數 1bits|
            /*
             看第2個位元組,如果音訊格式AAC(0x0A),AudioTagHeader中會多出1個位元組的資料AACPacketType,這個欄位來表示AACAUDIODATA的型別:
             0x00 = AAC sequence header,類似h.264的sps,pps,在FLV的檔案頭部出現一次。
             0x01 = AAC raw,AAC資料
             */
            
            //FLV Audio Tag, 完整格式,結構為:【0x08, 3位元組包長度,4位元組時間戳,00 00 00】,AF 01 N位元組AAC資料 | 前包長度
            //其中編碼後AAC純資料長度為N,3位元組包長度 = N + 2
            //前包長度 = 11 + 3位元組包長度 = 11 + N + 2 = 13 + N。
            //如果要儲存AAC為流資料,需要 【ADTS頭 + AACRaw資料】【ADTS頭 + AACRaw資料】【ADTS頭 + AACRaw資料】 寫入檔案
            
            if(packet.m_nBodySize >= 2 && packet.m_body[1] == 0x00)
            {
                //AAC sequence header
                this->p = new FLVAudioTagHeader((const unsigned char *)(packet.m_body));
                this->p->parse();
                this->p->dumpHeaderInfo();
                //FFMpeg 解析參考
                //https://github.com/herocodemaster/rtmp-cpp/blob/3ec35590675560ac4fa9557ca7a5917c617d9999/RTMP/projects/ffmpeg/src_completo/libavcodec/mpeg4audio.c
                //用bit操作類https://blog.csdn.net/qll125596718/article/details/6901935
                this->p->parseAudioConfig((const char *)(packet.m_body + 2), packet.m_nBodySize-2);
            }
            
            if(packet.m_nBodySize >= 2 && packet.m_body[1] == 0x01)
            {
                //AAC Raw Data
                unsigned char adts[7] = {0};
                this->p->aac_set_adts_head(adts, packet.m_nBodySize - 2);
                this->dumpBytesToFlv(adts, 7);
                this->dumpBytesToFlv((const unsigned char *)(packet.m_body+2), packet.m_nBodySize-2);
            }
            
            RTMPPacket_Free(&packet);
        }

  RTMP的AAC Payload是 2個位元組的頭 + 【音訊裸資料 | 或者 AAC Sequence Header】,

  關於AAC Sequence Header的解析,可以參考FFMPEG

  如果不解析AAC Sequence Header也是可以的,因為後面每段音訊的前兩個位元組的頭,也包含音訊格式、取樣率、幀率的資訊

  得到音訊的裸資料後,如果需要播放,需要在每段資料前面加上ADTS頭

  感謝: https://blog.csdn.net/lichen18848950451/article/details/78266054

    int aac_set_adts_head(unsigned char *buf, int size)
    {
        ADTSContext *acfg = &this->ctx;
        unsigned char byte;
        if (size < ADTS_HEADER_SIZE)
        {
            return -1;
        }
        buf[0] = 0xff;
        buf[1] = 0xf1;
        byte = 0;
        byte |= (acfg->objecttype & 0x03) << 6;
        byte |= (acfg->sample_rate_index & 0x0f) << 2;
        byte |= (acfg->channel_conf & 0x07) >> 2;
        buf[2] = byte;
        byte = 0;
        byte |= (acfg->channel_conf & 0x07) << 6;
        byte |= (ADTS_HEADER_SIZE + size) >> 11;
        buf[3] = byte;
        byte = 0;
        byte |= (ADTS_HEADER_SIZE + size) >> 3;
        buf[4] = byte;
        byte = 0;
        byte |= ((ADTS_HEADER_SIZE + size) & 0x7) << 5;
        byte |= (0x7ff >> 6) & 0x1f;
        buf[5] = byte;
        byte = 0;
        byte |= (0x7ff & 0x3f) << 2;
        buf[6] = byte;
        
        return 0;
        
    }

  在追加了ADTS頭,aac流就可以匯入播放器進行播放了