Easypusher接口推送RTP数据
2016-11-15 17:02
162 查看
最近在整合asterisk系统的视频通话录像功能,开始使用mp4v2库直接在asterisk系统中进行mp4视频文件的生成,测试发现性能上存在瓶颈,只能做到50路(双向即100个mp4文件)左右的并发通话的mp4文件生成,初步分析问题主要在写文件上,鉴于此,希望能把RTP流送出到别的服务中进行录制。在网上查找了相关的流媒体服务,发现easydarwin比较适合我们当前的应用场景,准备使用其作为我们的流媒体服务器使用,把对接过程所遇到的问题进行记录,希望对有这方面应用的XDJM有一定的帮助。
一、环境说明:
1、EasyDarwin版本:Server: EasyDarwin/7.0.5 (Build/16.0518; Platform/Win32; Release/EasyDarwin; State/Development; )
下载地址:https://github.com/EasyDarwin/EasyDarwin/releases
2、Easypusher静态库版本:v1.2.16.1105
静态库下载地址:https://github.com/EasyDarwin/EasyPusher
3、Asterisk版本:Asterisk 1.8.20.1 built (自行到官网下载一个能用的版本应该都行)
4、
二、源码:
isActivated = EasyPusher_Activate(KEY);/* 目前用到.a库的话,需要临时授权
*/
switch(isActivated)
{
case EASY_ACTIVATE_INVALID_KEY:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_INVALID_KEY!\n");
break;
case EASY_ACTIVATE_TIME_ERR:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_TIME_ERR!\n");
break;
case EASY_ACTIVATE_PROCESS_NAME_LEN_ERR:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_PROCESS_NAME_LEN_ERR!\n");
break;
case EASY_ACTIVATE_PROCESS_NAME_ERR:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_PROCESS_NAME_ERR!\n");
break;
case EASY_ACTIVATE_VALIDITY_PERIOD_ERR:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_VALIDITY_PERIOD_ERR!\n");
break;
case EASY_ACTIVATE_SUCCESS:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_SUCCESS!\n");
break;
}
if(EASY_ACTIVATE_SUCCESS != isActivated)
{
ast_log(LOG_ERROR, "EasyPusher_Activate : NOT EASY_ACTIVATE_SUCCESS\n");
//return -1;
}
memset(&fSourceMediaInfo, 0x00, sizeof(EASY_MEDIA_INFO_T));
fSourceMediaInfo.u32VideoCodec = EASY_SDK_VIDEO_CODEC_H264;// fSourceMediaInfo.u32VideoFps = 25; //
/*711语音处理*/
fSourceMediaInfo.u32AudioCodec = 0x10007;//EASY_SDK_AUDIO_CODEC_G711A;
fSourceMediaInfo.u32AudioSamplerate = 8000;
fSourceMediaInfo.u32AudioChannel = 1;
fPusherHandle = EasyPusher_Create();
fPusherBuff = malloc(2048*1024);
If (NULL == fPusherBuff )
Return -1;
memset(fPusherBuff, 0, 2048*1024);
EasyPusher_SetEventCallback(fPusherHandle, __EasyPusher_Callback, 0, NULL);
EasyPusher_StartStream(fPusherHandle, ConfigIP, atoi(ConfigPort), ConfigName, "admin", "admin", &fSourceMediaInfo, 2048, 0);//2M缓冲区
ast_log(LOG_WARNING, "*** live streaming url:rtsp://%s:%d/%s ***\n", ConfigIP, atoi(ConfigPort), ConfigName);
bfile=fopen("/Data/h264test.264", "at+");/*调试用,写h264裸文件*/
{
now = ast_tvnow();
if( (f->frametype == AST_FRAME_VOICE) && spsflag )// spsflag:
先写入视频数据
{
EASY_AV_Frame avoiceFrame;
if(f->subclass.codec & AST_FORMAT_ALAW)// 例子里只处理ALAW语音,实际看接口,还支持ULAW、G726、AAC
{
//ast_log(LOG_WARNING, "AST_FORMAT_ALAW f->datalen[%d]\n", f->datalen);
memset(&avoiceFrame, 0x00, sizeof(EASY_AV_Frame));
avoiceFrame.u32AVFrameLen = f->datalen ;
avoiceFrame.pBuffer =AST_FRAME_GET_BUFFER(f) ;/* RTP包中的771裸数据,即RTP包中的PAYLOAD数据
*/
avoiceFrame.u32AVFrameFlag = EASY_SDK_AUDIO_FRAME_FLAG;
avoiceFrame.u32TimestampSec =now.tv_sec;
avoiceFrame.u32TimestampUsec = now.tv_usec;
EasyPusher_PushFrame(fPusherHandle, &avoiceFrame);
}
}
else if (f->frametype == AST_FRAME_VIDEO) //写入视频数据
{
if (f->subclass.codec & AST_FORMAT_H264)
{
EASY_AV_Frame avideoFrame;
unsigned char *frame = NULL;
unsigned char ttype ;
unsigned char h264_hdr[4] = {0};
frame = AST_FRAME_GET_BUFFER(f);/* RTP包中的h264裸数据,即RTP包中的PAYLOAD数据
*/
ttype = (unsigned char)frame[0] & 0x1F;
memset(&avideoFrame, 0x00, sizeof(EASY_AV_Frame));
avideoFrame.u32TimestampSec = now.tv_sec;
avideoFrame.u32TimestampUsec = now.tv_usec;
h264_hdr[3] |= 0x1;
/* 只有是SPS和P帧才去调用API发送;否则会出现easydarwin保存成文件后,播放不出来的问题(直播不存在问题),具体原因不明确
*/
if ((0x7 == ttype) || (0x1 == ttype))
{
spsflag += 1;
if (spsflag >1)
{
fwrite(h264_hdr, 4, 1, bfile);
fwrite(AST_FRAME_GET_BUFFER(f), 1, f->datalen, bfile);
ttype = (unsigned char)fPusherBuff[4] & 0x1F;
avideoFrame.u32VFrameType = (ttype==0x07)?EASY_SDK_VIDEO_FRAME_I:EASY_SDK_VIDEO_FRAME_P;
avideoFrame.u32AVFrameFlag = EASY_SDK_VIDEO_FRAME_FLAG;
avideoFrame.u32AVFrameLen = fPusherBuffOffset;
avideoFrame.pBuffer = fPusherBuff;
spsflag = 1;
EasyPusher_PushFrame(fPusherHandle, &avideoFrame);
fPusherBuffOffset = 0;
memset(fPusherBuff, 0, 2048*1024);
memcpy(fPusherBuff+fPusherBuffOffset, h264_hdr, 4);
fPusherBuffOffset += 4;
memcpy(fPusherBuff+fPusherBuffOffset , AST_FRAME_GET_BUFFER(f), f->datalen);
fPusherBuffOffset += f->datalen;
}
else
{
/*调试用,写h264裸文件*/
fwrite(h264_hdr, 4, 1, bfile);
fwrite(AST_FRAME_GET_BUFFER(f), 1, f->datalen, bfile);
memcpy(fPusherBuff+fPusherBuffOffset, h264_hdr, 4);
fPusherBuffOffset += 4;
memcpy(fPusherBuff+fPusherBuffOffset , AST_FRAME_GET_BUFFER(f), f->datalen);
fPusherBuffOffset += f->datalen;
}
}
else
{
fwrite(h264_hdr, 4, 1, bfile);/*调试用,写h264裸文件*/
fwrite(AST_FRAME_GET_BUFFER(f), 1, f->datalen, bfile);
memcpy(fPusherBuff+fPusherBuffOffset, h264_hdr, 4);
fPusherBuffOffset += 4;
memcpy(fPusherBuff+fPusherBuffOffset , AST_FRAME_GET_BUFFER(f), f->datalen);
fPusherBuffOffset += f->datalen;
}
}
}
}
三、参考:
1、利用EasyRTSPClient工具检查摄像机RTSP流不能播放原因以及排查音视频数据无法播放问题:http://blog.csdn.net/xiejiashu/article/details/53105103
2、H264裸数据推送参考:https://github.com/EasyDarwin/EasyPusher 解压后的EasyPusher_File目录
四、感谢:
非常感谢在调测试过程中,Babosa大神给予的指点,具体问题可以在EasyDarwin开源流媒体服务器QQ群中提出:496258327(群)
五、后续:
后续会针对并发性能做个测试,有新进展的话贴出分享受给大家
一、环境说明:
1、EasyDarwin版本:Server: EasyDarwin/7.0.5 (Build/16.0518; Platform/Win32; Release/EasyDarwin; State/Development; )
下载地址:https://github.com/EasyDarwin/EasyDarwin/releases
2、Easypusher静态库版本:v1.2.16.1105
静态库下载地址:https://github.com/EasyDarwin/EasyPusher
3、Asterisk版本:Asterisk 1.8.20.1 built (自行到官网下载一个能用的版本应该都行)
4、
二、源码:
isActivated = EasyPusher_Activate(KEY);/* 目前用到.a库的话,需要临时授权
*/
switch(isActivated)
{
case EASY_ACTIVATE_INVALID_KEY:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_INVALID_KEY!\n");
break;
case EASY_ACTIVATE_TIME_ERR:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_TIME_ERR!\n");
break;
case EASY_ACTIVATE_PROCESS_NAME_LEN_ERR:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_PROCESS_NAME_LEN_ERR!\n");
break;
case EASY_ACTIVATE_PROCESS_NAME_ERR:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_PROCESS_NAME_ERR!\n");
break;
case EASY_ACTIVATE_VALIDITY_PERIOD_ERR:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_VALIDITY_PERIOD_ERR!\n");
break;
case EASY_ACTIVATE_SUCCESS:
ast_log(LOG_DEBUG, "KEY is EASY_ACTIVATE_SUCCESS!\n");
break;
}
if(EASY_ACTIVATE_SUCCESS != isActivated)
{
ast_log(LOG_ERROR, "EasyPusher_Activate : NOT EASY_ACTIVATE_SUCCESS\n");
//return -1;
}
memset(&fSourceMediaInfo, 0x00, sizeof(EASY_MEDIA_INFO_T));
fSourceMediaInfo.u32VideoCodec = EASY_SDK_VIDEO_CODEC_H264;// fSourceMediaInfo.u32VideoFps = 25; //
/*711语音处理*/
fSourceMediaInfo.u32AudioCodec = 0x10007;//EASY_SDK_AUDIO_CODEC_G711A;
fSourceMediaInfo.u32AudioSamplerate = 8000;
fSourceMediaInfo.u32AudioChannel = 1;
fPusherHandle = EasyPusher_Create();
fPusherBuff = malloc(2048*1024);
If (NULL == fPusherBuff )
Return -1;
memset(fPusherBuff, 0, 2048*1024);
EasyPusher_SetEventCallback(fPusherHandle, __EasyPusher_Callback, 0, NULL);
EasyPusher_StartStream(fPusherHandle, ConfigIP, atoi(ConfigPort), ConfigName, "admin", "admin", &fSourceMediaInfo, 2048, 0);//2M缓冲区
ast_log(LOG_WARNING, "*** live streaming url:rtsp://%s:%d/%s ***\n", ConfigIP, atoi(ConfigPort), ConfigName);
bfile=fopen("/Data/h264test.264", "at+");/*调试用,写h264裸文件*/
{
now = ast_tvnow();
if( (f->frametype == AST_FRAME_VOICE) && spsflag )// spsflag:
先写入视频数据
{
EASY_AV_Frame avoiceFrame;
if(f->subclass.codec & AST_FORMAT_ALAW)// 例子里只处理ALAW语音,实际看接口,还支持ULAW、G726、AAC
{
//ast_log(LOG_WARNING, "AST_FORMAT_ALAW f->datalen[%d]\n", f->datalen);
memset(&avoiceFrame, 0x00, sizeof(EASY_AV_Frame));
avoiceFrame.u32AVFrameLen = f->datalen ;
avoiceFrame.pBuffer =AST_FRAME_GET_BUFFER(f) ;/* RTP包中的771裸数据,即RTP包中的PAYLOAD数据
*/
avoiceFrame.u32AVFrameFlag = EASY_SDK_AUDIO_FRAME_FLAG;
avoiceFrame.u32TimestampSec =now.tv_sec;
avoiceFrame.u32TimestampUsec = now.tv_usec;
EasyPusher_PushFrame(fPusherHandle, &avoiceFrame);
}
}
else if (f->frametype == AST_FRAME_VIDEO) //写入视频数据
{
if (f->subclass.codec & AST_FORMAT_H264)
{
EASY_AV_Frame avideoFrame;
unsigned char *frame = NULL;
unsigned char ttype ;
unsigned char h264_hdr[4] = {0};
frame = AST_FRAME_GET_BUFFER(f);/* RTP包中的h264裸数据,即RTP包中的PAYLOAD数据
*/
ttype = (unsigned char)frame[0] & 0x1F;
memset(&avideoFrame, 0x00, sizeof(EASY_AV_Frame));
avideoFrame.u32TimestampSec = now.tv_sec;
avideoFrame.u32TimestampUsec = now.tv_usec;
h264_hdr[3] |= 0x1;
/* 只有是SPS和P帧才去调用API发送;否则会出现easydarwin保存成文件后,播放不出来的问题(直播不存在问题),具体原因不明确
*/
if ((0x7 == ttype) || (0x1 == ttype))
{
spsflag += 1;
if (spsflag >1)
{
fwrite(h264_hdr, 4, 1, bfile);
fwrite(AST_FRAME_GET_BUFFER(f), 1, f->datalen, bfile);
ttype = (unsigned char)fPusherBuff[4] & 0x1F;
avideoFrame.u32VFrameType = (ttype==0x07)?EASY_SDK_VIDEO_FRAME_I:EASY_SDK_VIDEO_FRAME_P;
avideoFrame.u32AVFrameFlag = EASY_SDK_VIDEO_FRAME_FLAG;
avideoFrame.u32AVFrameLen = fPusherBuffOffset;
avideoFrame.pBuffer = fPusherBuff;
spsflag = 1;
EasyPusher_PushFrame(fPusherHandle, &avideoFrame);
fPusherBuffOffset = 0;
memset(fPusherBuff, 0, 2048*1024);
memcpy(fPusherBuff+fPusherBuffOffset, h264_hdr, 4);
fPusherBuffOffset += 4;
memcpy(fPusherBuff+fPusherBuffOffset , AST_FRAME_GET_BUFFER(f), f->datalen);
fPusherBuffOffset += f->datalen;
}
else
{
/*调试用,写h264裸文件*/
fwrite(h264_hdr, 4, 1, bfile);
fwrite(AST_FRAME_GET_BUFFER(f), 1, f->datalen, bfile);
memcpy(fPusherBuff+fPusherBuffOffset, h264_hdr, 4);
fPusherBuffOffset += 4;
memcpy(fPusherBuff+fPusherBuffOffset , AST_FRAME_GET_BUFFER(f), f->datalen);
fPusherBuffOffset += f->datalen;
}
}
else
{
fwrite(h264_hdr, 4, 1, bfile);/*调试用,写h264裸文件*/
fwrite(AST_FRAME_GET_BUFFER(f), 1, f->datalen, bfile);
memcpy(fPusherBuff+fPusherBuffOffset, h264_hdr, 4);
fPusherBuffOffset += 4;
memcpy(fPusherBuff+fPusherBuffOffset , AST_FRAME_GET_BUFFER(f), f->datalen);
fPusherBuffOffset += f->datalen;
}
}
}
}
三、参考:
1、利用EasyRTSPClient工具检查摄像机RTSP流不能播放原因以及排查音视频数据无法播放问题:http://blog.csdn.net/xiejiashu/article/details/53105103
2、H264裸数据推送参考:https://github.com/EasyDarwin/EasyPusher 解压后的EasyPusher_File目录
四、感谢:
非常感谢在调测试过程中,Babosa大神给予的指点,具体问题可以在EasyDarwin开源流媒体服务器QQ群中提出:496258327(群)
五、后续:
后续会针对并发性能做个测试,有新进展的话贴出分享受给大家
相关文章推荐
- android硬编码h264数据,并使用rtp推送数据流,实现一个简单的直播-MediaCodec(一)
- web全栈应用【爬取(scrapy)数据 -> 通过restful接口存入数据库 -> websocket推送展示到前台】
- live555 播放视频 play 不能推送rtp数据
- 在PHP中怎么接收post过来的JSON数据(可以是接口推送过来的数据)
- EasyPusher如何推送纯音频RTSP流媒体数据
- 数据访问层的数据库提供者接口和数据操作接口
- 数据结构和程序接口
- 本人初学设计模式时写的一个支持多数据库的数据访问接口
- XML数据接口DOM入门介绍和常用对象
- 与数据绑定相关的接口
- web组件设计,利用接口(IPostBackDataHandler)产生数据回传的问题
- RTP/RTCP 视频数据传输 (续)
- 利用抽象工厂实现自定义多数据类型接口
- 用C#接口实现通用的文本数据序列化
- SYBASE数据访问接口CTLIB简单的内存池分配
- web组件设计,利用接口(IPostBackDataHandler)产生数据回传的问题
- 有线电缆数据服务接口规范(DOCSIS)--网络大典
- Oracle表数据的C++存取接口
- 数据访问接口体系及数据对象模型探讨
- 数据统一接口?