使用librtmp推h264、aac实时流
生活随笔
收集整理的這篇文章主要介紹了
使用librtmp推h264、aac实时流
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 前言
- 一、推視頻流
- 1.sps、pps
- 2.視頻幀
- 二、推音頻流
- 1.音頻幀
- 三、完整推流
- 1.實時流
- 總結
前言
librtmp可以用于推rtmp流,有時候我們需要將采集的攝像頭或桌面的視頻數據以及麥克風的音頻數據推流出去,這時候就需要使用librtmp的推流功能了,其推流流程比較簡單,只是一些細節需要注意即可。
一、推視頻流
1.sps、pps
在推送idr前需要發送一個sps、pps數據包,代碼如下:
/// <summary> /// 推送sps、pps,在每個idr前需要推送這些數據 /// </summary> /// <param name="rtmp">rtmp對象</param> /// <param name="sps">sps數據</param> /// <param name="spsLen">sps數據長度</param> /// <param name="pps">pps數據</param> /// <param name="ppsLen">pps數據長度</param> void PushSPSPPS(RTMP* rtmp, const unsigned char* sps, int spsLen, const unsigned char* pps, int ppsLen) {int bodySize = spsLen + ppsLen + 16;RTMPPacket rtmpPacket;RTMPPacket_Alloc(&rtmpPacket, bodySize);RTMPPacket_Reset(&rtmpPacket);char* body = rtmpPacket.m_body;int i = 0;//frame type(4bit)和CodecId(4bit)合成一個字節(byte)//frame type 關鍵幀1 非關鍵幀2//CodecId 7表示avcbody[i++] = 0x17;//fixed 4bytebody[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00;//configurationVersion: 版本 1bytebody[i++] = 0x01;//AVCProfileIndication:Profile 1byte sps[1]body[i++] = sps[1];//compatibility: 兼容性 1byte sps[2]body[i++] = sps[2];//AVCLevelIndication: ProfileLevel 1byte sps[3]body[i++] = sps[3];//lengthSizeMinusOne: 包長數據所使用的字節數 1bytebody[i++] = 0xff;//sps個數 1bytebody[i++] = 0xe1;//sps長度 2bytebody[i++] = (spsLen >> 8) & 0xff;body[i++] = spsLen & 0xff;//sps data 內容memcpy(&body[i], sps, spsLen);i += spsLen;//pps個數 1bytebody[i++] = 0x01;//pps長度 2bytebody[i++] = (ppsLen >> 8) & 0xff;body[i++] = ppsLen & 0xff;//pps data 內容memcpy(&body[i], pps, ppsLen);rtmpPacket.m_packetType = RTMP_PACKET_TYPE_VIDEO;rtmpPacket.m_nBodySize = bodySize;rtmpPacket.m_nTimeStamp = 0;rtmpPacket.m_hasAbsTimestamp = 1;rtmpPacket.m_nChannel = 0x04;//音頻或者視頻rtmpPacket.m_headerType = RTMP_PACKET_SIZE_MEDIUM;rtmpPacket.m_nInfoField2 = rtmp->m_stream_id;int nRet = RTMP_SendPacket(rtmp, &rtmpPacket, TRUE);RTMPPacket_Free(&rtmpPacket); }2.視頻幀
推送視頻需要區分是否為idr,然后需要設置時間戳,一般使用絕對時間戳較容易實現同步。
/// <summary> /// 推h264數據 /// </summary> /// <param name="rtmp">rtmp對象</param> /// <param name="data">h264數據</param> /// <param name="dataLen">h264數據長度</param> /// <param name="isIdr">是否為gop首幀</param> /// <param name="timestamp">時間戳,單位毫秒,絕對時間戳</param> void PushH264Data(RTMP* rtmp, const unsigned char* data, int dataLen, bool isIdr, int timestamp) {int bodySize = dataLen + 9;RTMPPacket rtmpPacket;RTMPPacket_Alloc(&rtmpPacket, bodySize);RTMPPacket_Reset(&rtmpPacket);char* body = rtmpPacket.m_body;int i = 0;//frame type(4bit)和CodecId(4bit)合成一個字節(byte)//frame type 關鍵幀1 非關鍵幀2//CodecId 7表示avcif (isIdr) {body[i++] = 0x17;}else {body[i++] = 0x27;}//fixed 4byte 0x01表示NALU單元body[i++] = 0x01;body[i++] = 0x00;body[i++] = 0x00;body[i++] = 0x00;//dataLen 4bytebody[i++] = (dataLen >> 24) & 0xff;body[i++] = (dataLen >> 16) & 0xff;body[i++] = (dataLen >> 8) & 0xff;body[i++] = dataLen & 0xff;//datamemcpy(&body[i], data, dataLen);rtmpPacket.m_packetType = RTMP_PACKET_TYPE_VIDEO;rtmpPacket.m_nBodySize = bodySize;//持續播放時間rtmpPacket.m_nTimeStamp = timestamp;//使用絕對時間戳rtmpPacket.m_hasAbsTimestamp = 1;rtmpPacket.m_nChannel = 0x04;//音頻或者視頻rtmpPacket.m_headerType = RTMP_PACKET_SIZE_LARGE;rtmpPacket.m_nInfoField2 = rtmp->m_stream_id;int nRet = RTMP_SendPacket(rtmp, &rtmpPacket, TRUE);RTMPPacket_Free(&rtmpPacket); }二、推音頻流
1.音頻幀
推音頻幀,需要設置采樣率以及聲道,時間戳采用絕對時間戳。
/// <summary> /// 推流aac數據 /// </summary> /// <param name="rtmp">rtmp對象</param> /// <param name="samplerateIndex">采樣率下標,0 = 5.5 kHz,1 = 11 kHz,2 = 22 kHz,3 = 44 kHz</param> /// <param name="isStereo">是否立體聲</param> /// <param name="data">aac數據</param> /// <param name="dataLen">aac數據長度</param> /// <param name="timestamp">時間戳,單位毫秒,絕對時間戳</param> void PushAacData(RTMP* rtmp, int samplerateIndex, bool isStereo, const unsigned char* data, int dataLen, int timestamp) {int bodySize = dataLen + 2;RTMPPacket rtmpPacket;RTMPPacket_Alloc(&rtmpPacket, bodySize);RTMPPacket_Reset(&rtmpPacket);char* body = rtmpPacket.m_body;//前四位表示音頻數據格式 10(十進制)表示AAC,16進制就是A//第5-6位的數值表示采樣率,0 = 5.5 kHz,1 = 11 kHz,2 = 22 kHz,3(11) = 44 kHz。//第7位表示采樣精度,0 = 8bits,1 = 16bits。//第8位表示音頻類型,0 = mono,1 = stereobody[0] = 0xA2 | ((samplerateIndex << 2) & 0x0c) | (isStereo & 0x1);//0x00 aac頭信息, 0x01 aac 原始數據//這里都用0x01都可以body[1] = 0x01;//datamemcpy(&body[2], data, dataLen);rtmpPacket.m_packetType = RTMP_PACKET_TYPE_AUDIO;rtmpPacket.m_nBodySize = bodySize;//持續播放時間rtmpPacket.m_nTimeStamp = timestamp;//使用絕對時間戳rtmpPacket.m_hasAbsTimestamp = 1;rtmpPacket.m_nChannel = 0x04;//音頻或者視頻rtmpPacket.m_headerType = RTMP_PACKET_SIZE_LARGE;rtmpPacket.m_nInfoField2 = rtmp->m_stream_id;int nRet = RTMP_SendPacket(rtmp, &rtmpPacket, TRUE);RTMPPacket_Free(&rtmpPacket); }三、完整推流
1.實時流
同時推流時需要注意:視頻音頻都需要在同一個線程推流。推送視頻dr幀之前先推送sps、pps。音頻一般采用固定時間戳,通過下標計數計算會比較準確。其中NaluParse對象參考《C++ 讀取h264中的nalu》。
#ifdef _WIN32 #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") #endif // _WIN32 #include<exception> #include "rtmp.h" #include"NaluParse.h" int main(int argc, char* argv[]) { #ifdef _WIN32WSADATA wsaData;int nRet;if ((nRet = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) {return nRet;} #endif // _Win32RTMP* rtmp = NULL;int64_t audioCount = 0;try {rtmp = RTMP_Alloc();RTMP_Init(rtmp);if (!RTMP_SetupURL(rtmp, (char*)"rtmp://127.0.0.1/live/123") ){throw std::exception("Setuping url failed!");}RTMP_EnableWrite(rtmp);if (!RTMP_Connect(rtmp, NULL)){throw std::exception("Connecting failed!");}if (!RTMP_ConnectStream(rtmp, 0)){throw std::exception("Connecting stream failed!");}while (1){//TODO:在實時流隊列中取得h264和aac幀,videoFrame、audioFrame。略//解析h264獲取nalu,這里示例的數據打包格式是Annex-B。auto nalus = AC::NaluParse::GetNalusFromFrame(videoFrame.Data, videoFrame.DataLength);AC::Nalu* sps = nullptr, * pps = nullptr;bool isIdr = true;//遍歷h264幀內的nalufor (auto& i : nalus){switch (i.GetNaluType()){case 01:case 02:case 03:case 04:isIdr = false;case 05:{//推視頻幀,視頻使用采集時的時間戳,單位毫秒PushH264Data(rtmp, i.GetData(), i.GetDataLength(), isIdr, videoFrame.Timestamp);}break;case 7:{sps = &i;}break;case 8:{pps = &i;if (sps){//推sps和ppsPushSPSPPS(rtmp, sps->GetData(), sps->GetDataLength(), pps->GetData(), pps->GetDataLength());}}break;}}//推音頻幀。這里音頻是44100,雙聲道。采用固定間隔時間戳: audioCount++ * 1024 * 1000 / sampleRate。PushAacData(rtmp, 3, true, audioFrame.Data, audioFrame.DataLength, audioCount++ * 1024 * 1000 / 44100);}}catch (const std::exception& e) {printf("%s\n", e.what());}if (rtmp){RTMP_Close(rtmp);RTMP_Free(rtmp);}return 0; }總結
以上就是今天要講的內容,使用librtmp推流其實是比較簡單的,而且只需要初始化之后,直接推音視頻數據即可,設置metada都不是必須的,唯一需要注意的就是在單線程中使用其方法。
總結
以上是生活随笔為你收集整理的使用librtmp推h264、aac实时流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件工程毕业设计选题c语言,经典软件工程
- 下一篇: 调研报告:原型图出稿