一、準備工作
參考: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流就可以導入播放器進行播放了