代码拉取完成,页面将自动刷新
2023-12-25【rtsp、rtmp、国标gb28181 推流支持纯音频模式 ,即没有视频 ,视频参数设置为 VideoCodec_NONE 】
1、更新北京-老陈 media-server 媒体库
2023-12-20【rtsp、rtmp、国标gb28181 推流支持纯音频模式 ,即没有视频 ,视频参数设置为 VideoCodec_NONE 】
1、把网络库更新为异步版本
2023-07-15【rtsp、rtmp、国标gb28181 推流支持纯音频模式 ,即没有视频 ,视频参数设置为 VideoCodec_NONE 】
1、增加 arm 平台编译版本
arm_bin 已经编译,包括SDK、rtsp\rtmp\国标推流调用例子
\SDK\arm 已经编译,ABLPush 推流库SDK
\example\arm\rtspDemo arm 平台 调用推流库例子。源码过程包括拉取1路rtsp源流,然后分别推送rtsp、rtmp、国标gb28181
\media-server-master\arm_lib arm 平台 media-server 静态链接库,已经编译好
2023-07-08【rtsp、rtmp、国标gb28181 推流支持纯音频模式 ,即没有视频 ,视频参数设置为 VideoCodec_NONE 】
1、更新media-server 为最新版本 ,并且livflv 设置一个宏 FLV_ENHANCE_RTMP 进行编译 rtmp\flv 265 设置为 FLV_ENHANCE_RTMP
2023-02-06【rtsp、rtmp、国标gb28181 推流支持纯音频模式 ,即没有视频 ,视频参数设置为 VideoCodec_NONE 】
1、网络接收线程池 CNetBaseThreadPool、网络发送线程池 CMediaSendThreadPool 支持信号通知方式
1)、定义信号量
std::condition_variable cv[MaxMediaSendThreadCount];
std::mutex mtx[MaxMediaSendThreadCount];
2)、插入任务时,通知信号量
bool CNetBaseThreadPool::InsertIntoTask(uint64_t nClientID)
{
std::lock_guard<std::mutex> lock(threadLock);
int nThreadThread = 0;
ClientProcessThreadMap::iterator it;
it = clientThreadMap.find(nClientID);
if (it != clientThreadMap.end())
{//找到
nThreadThread = (*it).second;
}
else
{//尚未加入过
nThreadThread = nThreadProcessCount % nTrueNetThreadPoolCount;
clientThreadMap.insert(ClientProcessThreadMap::value_type(nClientID, nThreadThread));
nThreadProcessCount ++;
}
m_NetHandleQueue[nThreadThread].push(nClientID);
cv[nThreadThread].notify_one();
return true;
}
3)、网络接收时没有数据则让线程休眠
void CNetBaseThreadPool::ProcessFunc()
{
int nCurrentThreadID = nGetCurrentThreadOrder;
bExitProcessThreadFlag[nCurrentThreadID] = false;
uint64_t nClientID;
bCreateThreadFlag = true; //创建线程完毕
while (bRunFlag)
{
if (m_NetHandleQueue[nCurrentThreadID].pop(nClientID) && bRunFlag )
{
boost::shared_ptr<CNetRevcBase> pClient = GetNetRevcBaseClient(nClientID);
if (pClient != NULL)
{
pClient->ProcessNetData();//任务执行
}
}
else
{
if (bRunFlag)
{
std::unique_lock<std::mutex> lck(mtx[nCurrentThreadID]);
cv[nCurrentThreadID].wait(lck);
}
else
break;
}
}
bExitProcessThreadFlag[nCurrentThreadID] = true;
}
2023-01-30
1、修正只推音频情况下,握手成功通知
nRecvDataTimerBySecond = 0;
if (bHandshakeFlag == false && netBaseNetType == NetBaseNetType_NetGB28181SendRtpUDP)
{//udp只要绑定成,就算是握手成功
WriteLog(Log_Debug, " nClient = %llu ,url = %s 和服务器握手成功 !可以推流 ", nClient, szClientIP);
bHandshakeFlag = true; //代表成功交互
calllBack.iEvent = ABLPush_STATE_PUSHING;
pCallBackFifo.push((unsigned char*)&calllBack, sizeof(CallBackParam));
}
2023-01-28
1、增加支持北京老陈的PS打包库
//使用自研的PS打包
#ifdef UseSelfPsPacketFlag
psMuxHandle = 0;
#else //使用北京老陈的PS打包
nVideoStreamID = nAudioStreamID = -1;
handler.alloc = ps_alloc;
handler.write = ps_write;
handler.free = ps_free;
videoPTS = audioPTS = 0;
ps = ps_muxer_create(&handler, this);
#endif
//PS打包
#ifdef UseSelfPsPacketFlag
input.mediatype = e_psmux_mt_video;
if (m_mediaCodeInfo.videoType == VideoCodec_H264 )
input.streamtype = e_psmux_st_h264;
else if (m_mediaCodeInfo.videoType == VideoCodec_H265)
input.streamtype = e_psmux_st_h265;
else
{
m_videoFifo.pop_front();
return 0;
}
input.data = pData;
input.datasize = nLength;
ps_mux_input(&input);
#else
if (nVideoStreamID != -1 && ps != NULL && m_mediaCodeInfo.videoType != VideoCodec_NONE)
{
if(m_mediaCodeInfo.videoType == VideoCodec_H264)
nflags = CheckVideoIsIFrame("H264", pData, nLength);
else
nflags = CheckVideoIsIFrame("H265", pData, nLength);
ps_muxer_input((ps_muxer_t*)ps, nVideoStreamID, nflags, videoPTS, videoPTS, pData, nLength);
videoPTS += (90000 / m_mediaCodeInfo.nVideoFrameRate);
}
#endif
2023-01-26
1、支持只有声音的推流
if (m_mediaCodeInfo.audioType == AudioCodec_G711A)
{
optionAudio.ttincre = 320; //g711a 的 长度增量
optionAudio.streamtype = e_rtppkt_st_g711a;
nAudioPayload = 8;
if (m_mediaCodeInfo.videoType != VideoCodec_NONE) //有视频,音频
sprintf(szRtspAudioSDP, "m=audio 0 RTP/AVP %d\r\nb=AS:50\r\na=recvonly\r\na=rtpmap:%d PCMA/%d\r\na=control:streamid=1\r\na=framerate:25\r\n",nAudioPayload, nAudioPayload, 8000);
else // 只有音频
sprintf(szRtspAudioSDP, "m=audio 0 RTP/AVP %d\r\nb=AS:50\r\na=recvonly\r\na=rtpmap:%d PCMA/%d\r\na=control:streamid=0\r\na=framerate:25\r\n", nAudioPayload, nAudioPayload, 8000);
nMediaCount++;
}
else if (m_mediaCodeInfo.audioType == AudioCodec_G711U)
{
optionAudio.ttincre = 320; //g711u 的 长度增量
optionAudio.streamtype = e_rtppkt_st_g711u;
nAudioPayload = 0;
if (m_mediaCodeInfo.videoType != VideoCodec_NONE) //有视频,音频
sprintf(szRtspAudioSDP, "m=audio 0 RTP/AVP %d\r\nb=AS:50\r\na=recvonly\r\na=rtpmap:%d PCMU/%d\r\na=control:streamid=1\r\na=framerate:25\r\n",nAudioPayload, nAudioPayload, 8000);
else // 只有音频
sprintf(szRtspAudioSDP, "m=audio 0 RTP/AVP %d\r\nb=AS:50\r\na=recvonly\r\na=rtpmap:%d PCMU/%d\r\na=control:streamid=0\r\na=framerate:25\r\n", nAudioPayload, nAudioPayload, 8000);
nMediaCount++;
}
else if (m_mediaCodeInfo.audioType == AudioCodec_AAC || m_mediaCodeInfo.audioType == AudioCodec_G711AtoAAC || m_mediaCodeInfo.audioType == AudioCodec_G711UtoAAC)
{
optionAudio.ttincre = 1024 ;//aac 的长度增量
optionAudio.streamtype = e_rtppkt_st_aac;
nAudioPayload = 104;
if(m_mediaCodeInfo.videoType != VideoCodec_NONE) //有视频,音频
sprintf(szRtspAudioSDP, "m=audio 0 RTP/AVP %d\r\na=rtpmap:%d MPEG4-GENERIC/%d/%d\r\na=fmtp:%d profile-level-id=15; streamtype=5; mode=AAC-hbr; config=1408;SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;\r\na=control:streamid=1\r\n",nAudioPayload, nAudioPayload, m_mediaCodeInfo.nSampleRate, m_mediaCodeInfo.nChannels, nAudioPayload);
else // 只有音频
sprintf(szRtspAudioSDP, "m=audio 0 RTP/AVP %d\r\na=rtpmap:%d MPEG4-GENERIC/%d/%d\r\na=fmtp:%d profile-level-id=15; streamtype=5; mode=AAC-hbr; config=1408;SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;\r\na=control:streamid=0\r\n", nAudioPayload, nAudioPayload, m_mediaCodeInfo.nSampleRate, m_mediaCodeInfo.nChannels, nAudioPayload);
nMediaCount++;
}
2、如果只有声音,不要累计声音数据,直接发送出去
void CNetClientSendRtsp::ProcessRtpAudioData(unsigned char* pRtpAudio, int nDataLength)
{
if (m_mediaCodeInfo.videoType != VideoCodec_NONE)
{
if ((MaxRtpSendAudioMediaBufferLength - nSendRtpAudioMediaBufferLength < nDataLength + 4) && nSendRtpAudioMediaBufferLength > 0 )
{//剩余空间不够存储 ,防止出错
SumSendRtpMediaBuffer(szSendRtpAudioMediaBuffer, nSendRtpAudioMediaBufferLength);
nSendRtpAudioMediaBufferLength = 0;
nCalcAudioFrameCount = 0;
}
szSendRtpAudioMediaBuffer[nSendRtpAudioMediaBufferLength + 0] = '$';
szSendRtpAudioMediaBuffer[nSendRtpAudioMediaBufferLength + 1] = 2;
nAudioRtpLen = htons(nDataLength);
memcpy(szSendRtpAudioMediaBuffer + (nSendRtpAudioMediaBufferLength + 2), (unsigned char*)&nAudioRtpLen, sizeof(nAudioRtpLen));
memcpy(szSendRtpAudioMediaBuffer + (nSendRtpAudioMediaBufferLength + 4), pRtpAudio, nDataLength);
nSendRtpAudioMediaBufferLength += nDataLength + 4;
nCalcAudioFrameCount ++;
if (nCalcAudioFrameCount >= 3 && nSendRtpAudioMediaBufferLength > 0 )
{
SumSendRtpMediaBuffer(szSendRtpAudioMediaBuffer, nSendRtpAudioMediaBufferLength);
nSendRtpAudioMediaBufferLength = 0;
nCalcAudioFrameCount = 0;
}
}
else
{//单独发送音频
nSendRtpAudioMediaBufferLength = 0;
szSendRtpAudioMediaBuffer[nSendRtpAudioMediaBufferLength + 0] = '$';
szSendRtpAudioMediaBuffer[nSendRtpAudioMediaBufferLength + 1] = 0;
nAudioRtpLen = htons(nDataLength);
memcpy(szSendRtpAudioMediaBuffer + (nSendRtpAudioMediaBufferLength + 2), (unsigned char*)&nAudioRtpLen, sizeof(nAudioRtpLen));
memcpy(szSendRtpAudioMediaBuffer + (nSendRtpAudioMediaBufferLength + 4), pRtpAudio, nDataLength);
XHNetSDK_Write(nClient, szSendRtpAudioMediaBuffer, nDataLength+4, 1);
}
}
2022-12-31
1、视频,音频不能同时为NULL
if (videoType == VideoCodec_NONE && audioType == AudioCodec_NONE)
return ABLPush_ErrorCode_VideoAudioIsNull;
2、删除 检测rtmp 时的音频格式,否则 可能会造成不能创建rtmp推流句柄
if (memcmp(szURL, "rtmp://", 7) == 0 && (audioType == AudioCodec_G711A || audioType == AudioCodec_G711U || audioType == AudioCodec_AACtoG711A || audioType == AudioCodec_AACtoG711U))
return ABLPush_ErrorCode_UnsupportAudio;
3、rtmp推流时,AAC音频需要增加条件判断
int CNetClientSendRtmp::PushAudio(uint8_t* pAudioData, uint32_t nDataLength, char* szAudioCodec, int nChannels, int SampleRate)
{
if (!bRunFlag || !bAddThreadPoolFlag ) //rtmp 交互完毕才能发送
return -1;
//转g711 为 aac
if (m_mediaCodeInfo.audioType == AudioCodec_G711AtoAAC || m_mediaCodeInfo.audioType == AudioCodec_G711UtoAAC )
{
if (aacEnc.hEncoder == NULL)
aacEnc.InitAACEncodec(64000, 8000, 1, &nAACEncodeLength);
if (ConvertG711ToAAC(m_mediaCodeInfo.audioType, pAudioData, nDataLength, pOutAACData, nOutAACDataLength) == false)
return false;
m_audioFifo.push(pOutAACData, nOutAACDataLength);
}
else if (m_mediaCodeInfo.audioType == AudioCodec_AAC) //不转码
m_audioFifo.push(pAudioData, nDataLength);
return 0;
}
2022-12-23
1、检测ssrc\src_port 是否已经被使用
int CheckSSRC_SRC_Port(char* ssrc, char* src_port)
{
std::lock_guard<std::mutex> lock(ABL_CNetRevcBase_ptrMapLock);
CNetRevcBase_ptrMap::iterator iterator1;
for( iterator1 = xh_ABLNetRevcBaseMap.begin(); iterator1 != xh_ABLNetRevcBaseMap.end();++iterator1)
{
if (atoi(ssrc) != 0 && strcmp((*iterator1).second->gb28181PushStruct.ssrc, ssrc) == 0)
return ABLPush_ErrorCode_SsrcIdentical;
else if(atoi(src_port)!= 0 && strcmp((*iterator1).second->gb28181PushStruct.src_port, src_port) == 0)
return ABLPush_ErrorCode_Ssrc_PortIdentical;
}
return 0; //没有重复
}
2、在推送国标流时,支持转g711a,g711u 为 aac
1) 执行转码
if (m_mediaCodeInfo.audioType == AudioCodec_G711AtoAAC || m_mediaCodeInfo.audioType == AudioCodec_G711UtoAAC)
{
if (aacEnc.hEncoder == NULL)
aacEnc.InitAACEncodec(64000, 8000, 1, &nAACEncodeLength);
if (ConvertG711ToAAC(m_mediaCodeInfo.audioType, pAudioData, nDataLength, pOutAACData, nOutAACDataLength) == false)
return false;
m_audioFifo.push(pOutAACData, nOutAACDataLength);
}
else //不转码
m_audioFifo.push(pAudioData, nDataLength);
2)、国标打包时更改音频编码格式
else if (m_mediaCodeInfo.audioType == AudioCodec_G711AtoAAC)
input.streamtype = e_psmux_st_aac;
else if (m_mediaCodeInfo.audioType == AudioCodec_G711UtoAAC)
input.streamtype = e_psmux_st_aac;
3、增加两个参数 ssrc、src_port 错误提示、并且检测
ABLPush_ErrorCode_SsrcIdentical = -10 ,//SSRC相同了,必须唯一
ABLPush_ErrorCode_Ssrc_PortIdentical = -11,//src_port 相同了,必须唯一
//检测ssrc ,src_port
if (memcmp(szURL, "rtp://", 6) == 0)
{
char szSSRC[256] = { 0 };
char szSrc_Port[256] = { 0 };
strcpy(szSSRC, "0");
strcpy(szSrc_Port, "0");
string strURL = szURL ;
int nPos1, nPos2;
nPos1 = strURL.find("ssrc=", 0);
if (nPos1 > 0)
{
nPos2 = strURL.find("&", nPos1 + 1);
if (nPos2 > nPos1)
memcpy(szSSRC, szURL + nPos1 + 5, nPos2 - nPos1 - 5);
else
memcpy(szSSRC, szURL + nPos1 + 5, strlen(szURL) - nPos1 - 5);
}
nPos1 = strURL.find("src_port=", 0);
if (nPos1 > 0)
{
nPos2 = strURL.find("&", nPos1 + 1);
if (nPos2 > nPos1)
memcpy(szSrc_Port, szURL + nPos1 + 9, nPos2 - nPos1 - 9);
else
memcpy(szSrc_Port, szURL + nPos1 + 9, strlen(szURL) - nPos1 - 9 );
}
//检测两个参数是否被占用
int nCheck = CheckSSRC_SRC_Port(szSSRC, szSrc_Port);
if (nCheck != 0)
return nCheck;
}
2022-12-22
1、把linux、windows两个平台的md5验证方面的代码合并为一个版本
2、增加国标推流 NetGB28181RtpClient.cpp NetGB28181RtpClient.h
2022-12-21
1、移植到linux平台
2022-12-13
1、增加G711A、G711U转码为AAC
bool CNetRevcBase::ConvertG711ToAAC(int nCodec, unsigned char* pG711, int nBytes, unsigned char* szOutAAC, int& nAACLength)
{
if (pG711 == NULL || nBytes <= 0)
return false;
bool bRet = false;
if (nBytes < 320)
{//需要拼接
memcpy(g711CacheBuffer + nG711CacheLength, pG711, nBytes);
nG711CacheLength += nBytes;
if (nG711CacheLength < 320)
return false;
}
if (nCodec == AudioCodec_G711AtoAAC)
{
if (nBytes < 320)
alaw_to_pcm16(320, (const char*)g711CacheBuffer, g711toPCM);
else
alaw_to_pcm16(320, (const char*)pG711, g711toPCM);
}
else if (nCodec == AudioCodec_G711UtoAAC)
{
if (nBytes < 320)
ulaw_to_pcm16(320, (const char*)g711CacheBuffer, g711toPCM);
else
ulaw_to_pcm16(320, (const char*)pG711, g711toPCM);
}
else
return false;
//需要拼接
if (nBytes < 320)
nG711CacheLength -= 320;
if (1024 * 16 - nG711ToPCMCacheLength > 640)
{
memcpy(g711ToPCMCache + nG711ToPCMCacheLength, g711toPCM, 640);
nG711ToPCMCacheLength += 640;
}
if (nG711ToPCMCacheLength >= nAACEncodeLength)
{
memcpy(aacEnc.pbPCMBuffer, g711ToPCMCache, nAACEncodeLength);
aacEnc.EncodecAAC(&nRetunEncodeLength);
if (nRetunEncodeLength > 0)
{
memcpy(szOutAAC, (unsigned char*)aacEnc.pbAACBuffer, nRetunEncodeLength);
nAACLength = nRetunEncodeLength;
bRet = true;
}
memmove(g711ToPCMCache, g711ToPCMCache + nAACEncodeLength, nG711ToPCMCacheLength - nAACEncodeLength);
nG711ToPCMCacheLength = nG711ToPCMCacheLength - nAACEncodeLength;
#ifdef WriteEncodeAACFlag
if (fWriteAACFile && nAACLength > 0)
{
fwrite(szOutAAC, 1, nAACLength, fWriteAACFile);
fflush(fWriteAACFile);
}
#endif
return bRet;
}
else
return false; //不够AAC音频长度
}
2、rtsp 推流 支持 G711U、G711A 转码为AAC
int CNetClientSendRtsp::PushAudio(uint8_t* pAudioData, uint32_t nDataLength, char* szAudioCodec, int nChannels, int SampleRate)
{
if (!bRunFlag || !bHandshakeFlag)
return -1;
//转g711 为 aac
if (m_mediaCodeInfo.audioType == AudioCodec_G711AtoAAC || m_mediaCodeInfo.audioType == AudioCodec_G711UtoAAC)
{
if(aacEnc.hEncoder == NULL)
aacEnc.InitAACEncodec(64000, 8000, 1, &nAACEncodeLength);
if (ConvertG711ToAAC(m_mediaCodeInfo.audioType, pAudioData, nDataLength, pOutAACData, nOutAACDataLength) == false)
return false;
m_audioFifo.push(pOutAACData, nOutAACDataLength);
}else //不转码
m_audioFifo.push(pAudioData, nDataLength);
return 0;
}
3、rtmp 推流 支持 G711U、G711A 转码为AAC
int CNetClientSendRtmp::PushAudio(uint8_t* pAudioData, uint32_t nDataLength, char* szAudioCodec, int nChannels, int SampleRate)
{
if (!bRunFlag || !bAddThreadPoolFlag ) //rtmp 交互完毕才能发送
return -1;
//转g711 为 aac
if (m_mediaCodeInfo.audioType == AudioCodec_G711AtoAAC || m_mediaCodeInfo.audioType == AudioCodec_G711UtoAAC )
{
if (aacEnc.hEncoder == NULL)
aacEnc.InitAACEncodec(64000, 8000, 1, &nAACEncodeLength);
if (ConvertG711ToAAC(m_mediaCodeInfo.audioType, pAudioData, nDataLength, pOutAACData, nOutAACDataLength) == false)
return false;
m_audioFifo.push(pOutAACData, nOutAACDataLength);
}
else //不转码
m_audioFifo.push(pAudioData, nDataLength);
return 0;
}
2022-12-04
1、rtsp推流增加所有事件
2、推流调用例子支持rtsp,rtmp推流
3、修正调用例子的码流发送
2022-12-03
1、增加rtsp推流
2、增加日志
2022-12-02
1、工程刚刚创建
2、能推264、265视频
3、完成事件通知
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。