c#网站开发框架有,做网站一个月赚多少,wordpress 中文 seo 插件,上海嘉定区网站建设公司文章目录 前言一、关键实现1、主要流程2、解决端口冲突#xff08;1#xff09;、出现原因#xff08;2#xff09;、解决方法 3、解析sdp#xff08;1#xff09;、定义实体#xff08;2#xff09;、解析视频#xff08;3#xff09;、解析音频 4、命令行推拉流1、出现原因2、解决方法 3、解析sdp1、定义实体2、解析视频3、解析音频 4、命令行推拉流1、视频推流2、音频推流3、音视频播放 二、sipua接口设计三、使用示例四、完整代码五、效果预览总结 前言
使用sip做视频通话时会遇到需要使用ip摄像头作为视频源的情况查了资料使用pjsip通常也需要改源码。pjsip包含的功能很完整但有点过于庞大很多功能并不需要。而且笔者有一个想法只要有个能处理sip交互的库比如eXosip音视频这块另外实现比如先使用ffmpeg和ffplay命令行作为音视频测试成功后再写代码实现。本文就是测试成功的方案真正灵活的方式还是要写代码调ffmpeg本文更多的是提供一种实现思路。 一、关键实现
主要的实现步骤是使用eXosip处理sip、自己解析sdp、流媒体使用ffmpeg、ffplay命令行。
1、主要流程 2、解决端口冲突
1、出现原因
按照上述流程会遇到端口冲突问题推流和拉流需要使用同一个本地udp端口由于ffmpeg和ffplay是两个进程同使用相同的端口就会冲突。具体细节如下
2、解决方法
一般想到的解决办法是使用jrtplib只建立一个rtp会话兼顾发送和接收且流媒体通过ffmpeg代码实现。本文没有使用此方法为了坚持使用ffmpeg和ffplay命令行最好的方法是使用udp代理监听端口对数据进行转发就可以有效的解决端口冲突问题。 3、解析sdp
虽然eXosip提供了sdp的获取方法但是对于具体的信息还是需要自己解析其实也是比较简单。
1、定义实体
//流类型
enum StreamType {STREAMTYPE_VIDEO,STREAMTYPE_AUDIO
};
/// summary
/// 流信息
/// /summary
class StreamInfo {
public://流类型StreamType type;//rtp推流地址可以用此地址ffmpeg直接推流也可以用下面参数自定义推流char rtpAdress[128] { 0 };//流的远端地址char remoteIp[32] { 0 };//流的远端端口int remotePort 0;//本地接收/发送端口int localPort 0;//编码格式char codec[16];//负载类型int payload 0;union{//采样率音频int sampleRate 0;//时间基、视频int timebase;};//声道数int channels 0;
};2、解析视频
std::vectorStreamInfo SipUA::_getVideoStreams(sdp_message_t* sdp_msg)
{std::vectorStreamInfo streams;if (!sdp_msg)return streams;sdp_connection_t* connection eXosip_get_video_connection(sdp_msg);if (!connection)return streams;std::string ip connection-c_addr; sdp_media_t* sdp eXosip_get_video_media(sdp_msg);if (!sdp)return streams;int port atoi(sdp-m_port); for (int i 0; i sdp-a_attributes.nb_elt; i){sdp_attribute_t* attr (sdp_attribute_t*)osip_list_get(sdp-a_attributes, i);if (attr){std::string audio_filed attr-a_att_field;if (audio_filed rtpmap){StreamInfo stream;stream.type StreamType::STREAMTYPE_VIDEO;snprintf(stream.remoteIp, 32, ip.c_str());stream.remotePort port;std::string value attr-a_att_value;std::string::size_type pt_idx value.find_first_of(0x20);if (pt_idx std::string::npos)continue;stream.payload atoi(value.substr(0, pt_idx).c_str());std::string::size_type bitrate_idx value.find_first_of(/);if (bitrate_idx std::string::npos)continue;stream.timebase atoi(value.substr(bitrate_idx 1).c_str());snprintf(stream.codec, 32, value.substr(pt_idx 1, bitrate_idx - pt_idx - 1).c_str());streams.push_back(stream);}}}return streams;
}3、解析音频
std::vectorStreamInfo SipUA::_getAudioStreams(sdp_message_t* sdp_msg)
{std::vectorStreamInfo streams;if (!sdp_msg)return streams;sdp_connection_t* connection eXosip_get_audio_connection(sdp_msg);if (!connection)return streams;std::string audio_ip connection-c_addr; //audio_ipsdp_media_t* audio_sdp eXosip_get_audio_media(sdp_msg);if (!audio_sdp)return streams;int audio_port atoi(audio_sdp-m_port); //audio_portfor (int i 0; i audio_sdp-a_attributes.nb_elt; i){sdp_attribute_t* attr (sdp_attribute_t*)osip_list_get(audio_sdp-a_attributes, i);if (attr){std::string audio_filed attr-a_att_field;if (audio_filed rtpmap){StreamInfo stream;stream.type StreamType::STREAMTYPE_AUDIO;snprintf(stream.remoteIp, 32, audio_ip.c_str());stream.remotePort audio_port;std::string value attr-a_att_value;auto strs StringHelper::split(value, );if (strs.size() 1){stream.payload atoi(strs[0].c_str());auto format StringHelper::split(strs[1], /);if (format.size() 1){snprintf(stream.codec, 16, format[0].c_str());stream.sampleRate atoi(format[1].c_str());if (format.size() 2)stream.channels atoi(format[2].c_str());}}streams.push_back(stream);}}}return streams;
}4、命令行推拉流
1、视频推流
转发rtsp的h264流为例rtp推流同时显示预览框。
ffmpeg -i rtmp://127.0.0.1/live/a123 -an -vcodec copy -payload_type 96 -f rtp rtp://127.0.0.1:25026?localrtpport15514 -window_size 192x108 -f sdl 2、音频推流
以本地文件转码为g.711u为例每个包大小160bytes。
ffmpeg -re -stream_loop -1 -i D:\test_music.wav -vn -acodec pcm_mulaw -ar 8000 -ac 1 -af aresample8000[0];[0]asetnsamplesn160:p0 -payload_type 0 -f rtp rtp://127.0.0.1:15026?localrtpport25514音频设备采集编码为g.711u为例每个包大小160bytes。
ffmpeg -f dshow -i audio音频设备名称 -vn -acodec pcm_mulaw -ar 8000 -ac 1 -af aresample8000[0];[0]asetnsamplesn160:p0 -payload_type 0 -f rtp rtp://127.0.0.1:15026?localrtpport25514注如果音频与视频为同一个输入源也可以合并为同一条命令。
3、音视频播放
将sdp字符串保存本地文件 本地播放的sdp
v0
o1002 158 1 IN IP4 127.0.0.1
sTalk
cIN IP4 127.0.0.1
t0 0
mvideo 25008 RTP/AVP 96
artpmap:96 H264/90000
artcp:25008
maudio 25310 RTP/AVP 0
artpmap:0 PCMU/8000
artcp:25310保存到test.sdp
FILE* fNULL;
fopen_s(f, test.sdp, wb);
if (f)
{fwrite(call-sdp, 1, strlen(call-sdp), f);fclose(f);
}命令行播放
ffplay.exe -x 640 -y 360 -protocol_whitelist \file,udp,rtp\ -i test.sdp二、sipua接口设计
#pragma once
#includefunctional
#include string
#include vector
#include UdpProxy.h
#include eXosip2\eXosip.h
#includeMessageQueue.h/// 这是一个sipua内部实现是eXosip2只提供sip交互sdp解析、udp代理功能。
/// udp代理分离端口功能:
/// sdp的每个m媒体的推拉流需要使用一个端口sip服务器要检查来源。
/// 如果此时采样ffmpeg.exe推流、ffplay.exe拉流两个进程都需要绑定本地同一个端口就会产生端口冲突。
/// 那就只能个使用jrtplib之类的库打开一个连接同时发送和接收数据。
/// 但是有一个巧妙的解决办法那就是使用udp代理转发数据就可以将端口拓展为多个了。/// summary
/// sip状态
/// /summary
enum SipUAState {//收到对方inviteSIPUAEVENT_INVITE,//收到对方回复SIPUAEVENT_ANSWER,//处理流媒体推流拉流端口有做分离便于推拉流分开实现。SIPUAEVENT_STREAM,//结束通话对方挂断SIPUAEVENT_ENDED,
};/// summary
/// 流类型
/// /summary
enum StreamType {STREAMTYPE_VIDEO,STREAMTYPE_AUDIO
};/// summary
/// 流信息
/// /summary
class StreamInfo {
public://流类型StreamType type;//rtp推流地址可以用此地址ffmpeg直接推流也可以用下面参数自定义推流char rtpAdress[128] { 0 };//流的远端地址char remoteIp[32] { 0 };//流的远端端口int remotePort 0;//本地接收/发送端口int localPort 0;//编码格式char codec[16];//负载类型int payload 0;union{//采样率音频int sampleRate 0;//时间基、视频int timebase;};//声道数int channels 0;
};/// summary
/// 通话对象
/// /summary
class SipCall {
public:int callId 0;//对方idconst char* userId nullptr;//播发的sdpconst char* sdp nullptr;//需要推流的视频信息StreamInfo* video nullptr;//需要推流的音频信息StreamInfo* audio nullptr;
};
class SipUA
{
public:/// summary/// 状态改变回调目前版本除媒体流外只有对方的消息会触发状态改变/// /summarystd::functionvoid(SipUAState state, SipCall* call) onState [](auto, auto) {};SipUA(const std::string serverIp, int serverPort, const std::string username, const std::string password);~SipUA();/// summary/// 开启客户端此方法是阻塞的可以在线程中开启。/// /summary/// param nameexitFlag退出标记值为true则退出/paramvoid exec(int* exitFlag);/// summary/// 呼叫/// /summary/// param nameremoteUserID对方id/param/// param namehasVideo有视频否/param/// param namehasAudio有音频否/param/// returns是否呼叫成功/returnsbool call(const std::string remoteUserID, bool hasVideo true, bool hasAudio true);/// summary/// 应答/// /summary/// param namehasVideo有视频否/param/// param namehasAudio有音频否/paramvoid answer(bool hasVideo, bool hasAudio);/// summary/// 挂断/// /summaryvoid hangup();
}; 三、使用示例
/// summary
/// 本示例启动后会自动拨号
/// 接收到通话请求会自动接听
/// /summary
void main() {SipUA ua(192.168.1.10, 5060, 1002, 1234);int exitFlag false;ua.onState [](SipUAState state, SipCall* call) {switch (state){case SIPUAEVENT_INVITE:ua.answer(true, true);break;case SIPUAEVENT_ANSWER:break;case SIPUAEVENT_STREAM://视频推流if (call-video){std::string srcUrl test.mp4;std::string format -re -stream_loop -1;auto codec StringHelper::toLower(call-video-codec);std::string params ;char cmd[512]; if (codec h264){params -preset ultrafast -tune zerolatency -level 4.2;}//发送桌面流,同时使用sdl本地预览sprintf_s(cmd, ffmpeg %s -i %s -an -vcodec %s -pix_fmt yuv420p %s -s 640x360 -b:v 500k -r 30 -g 10 -payload_type %d -f rtp %s -window_size 192x108 -f sdl \%s\ ,format.c_str(), srcUrl.c_str(), codec.c_str(), params.c_str(), call-video-payload, call-video-rtpAdress, srcUrl.c_str());//运行命令行runCmd(cmd);}//音频推流,如何是同一个输入流也可以和视频合并为一条命令if (call-audio){ std::string srcUrl test_music.wav;std::string format -re -stream_loop -1; auto codec StringHelper::toLower(call-audio-codec);std::string params ;char cmd[512];if (codec opus){codec libopus;}if (codec pcmu){codec pcm_mulaw;params -ac 1 -af \aresample8000[0];[0]asetnsamplesn160:p0\;//af滤镜确保每个包160bytes}//转发本地文件sprintf_s(cmd, ffmpeg %s -i %s -vn -acodec %s -ar %d %s -payload_type %d -f rtp %s,format.c_str(), srcUrl.c_str(), codec.c_str(), call-audio-sampleRate, params.c_str() , call-audio-payload, call-audio-rtpAdress);printf(cmd);//运行命令行runCmd(cmd);}//播放对方音视频if (call-sdp){FILE* fNULL;fopen_s(f, test.sdp, wb);if (f){fwrite(call-sdp, 1, strlen(call-sdp), f);fclose(f);std::string cmd ffplay.exe -x 640 -y 360 -protocol_whitelist \file,udp,rtp\ -i test.sdp;//运行命令行runCmd(cmd);}else{printf(fopen_s test.sdp error\n);}}break;case SIPUAEVENT_ENDED://关闭所有子进程closeJobObject();break;default:break;}};//开启测试拨号new std::thread([]() {Sleep(2000);ua.call(1004, true);});ua.exec(exitFlag);
}四、完整代码
eXosip版本为5.1ffmpeg.exe为4.3vs2022项目。
https://download.csdn.net/download/u013113678/88180712 五、效果预览
使用freeswitch作为sip服务器 本文程序的运行效果 推送本地mp4到sip 使用linphone作为对端运行效果 总结
以上就是今天讲述的内容本文使用的技术很简单但是实现过程有点曲折。尤其是端口冲突问题花了不少的时间确定原因解决办法也是无意中想到的否则可能很早就用代码去实现整个sip客户端了。本文的实现方式很好的解耦了sip和流媒体以及rtpsip可以单独实现、流媒体也可以自由选择、也不需要共用一个rtp会话有时想要快速搭建一个测试项目就变得容易多了。 文章转载自: http://www.morning.c7497.cn.gov.cn.c7497.cn http://www.morning.npbnc.cn.gov.cn.npbnc.cn http://www.morning.gwqq.cn.gov.cn.gwqq.cn http://www.morning.ggnkt.cn.gov.cn.ggnkt.cn http://www.morning.sacxbs.cn.gov.cn.sacxbs.cn http://www.morning.gzxnj.cn.gov.cn.gzxnj.cn http://www.morning.mtrrf.cn.gov.cn.mtrrf.cn http://www.morning.eshixi.com.gov.cn.eshixi.com http://www.morning.pwrkl.cn.gov.cn.pwrkl.cn http://www.morning.ybhjs.cn.gov.cn.ybhjs.cn http://www.morning.tzzkm.cn.gov.cn.tzzkm.cn http://www.morning.mxgpp.cn.gov.cn.mxgpp.cn http://www.morning.nhgkm.cn.gov.cn.nhgkm.cn http://www.morning.skkmz.cn.gov.cn.skkmz.cn http://www.morning.dxtxk.cn.gov.cn.dxtxk.cn http://www.morning.xcjwm.cn.gov.cn.xcjwm.cn http://www.morning.rwpfb.cn.gov.cn.rwpfb.cn http://www.morning.qwyms.cn.gov.cn.qwyms.cn http://www.morning.zsleyuan.cn.gov.cn.zsleyuan.cn http://www.morning.mrpqg.cn.gov.cn.mrpqg.cn http://www.morning.kmcby.cn.gov.cn.kmcby.cn http://www.morning.grpfj.cn.gov.cn.grpfj.cn http://www.morning.txzqf.cn.gov.cn.txzqf.cn http://www.morning.djlxz.cn.gov.cn.djlxz.cn http://www.morning.kntsd.cn.gov.cn.kntsd.cn http://www.morning.litao7.cn.gov.cn.litao7.cn http://www.morning.tlfmr.cn.gov.cn.tlfmr.cn http://www.morning.hrhwn.cn.gov.cn.hrhwn.cn http://www.morning.jzsgn.cn.gov.cn.jzsgn.cn http://www.morning.hkysq.cn.gov.cn.hkysq.cn http://www.morning.jyzqn.cn.gov.cn.jyzqn.cn http://www.morning.fprll.cn.gov.cn.fprll.cn http://www.morning.ddxjr.cn.gov.cn.ddxjr.cn http://www.morning.qcfcz.cn.gov.cn.qcfcz.cn http://www.morning.gnfkl.cn.gov.cn.gnfkl.cn http://www.morning.bqxxq.cn.gov.cn.bqxxq.cn http://www.morning.kkysz.cn.gov.cn.kkysz.cn http://www.morning.wbfg.cn.gov.cn.wbfg.cn http://www.morning.hxwrs.cn.gov.cn.hxwrs.cn http://www.morning.nwcgj.cn.gov.cn.nwcgj.cn http://www.morning.tldfp.cn.gov.cn.tldfp.cn http://www.morning.qtltg.cn.gov.cn.qtltg.cn http://www.morning.ryfpx.cn.gov.cn.ryfpx.cn http://www.morning.bxqpl.cn.gov.cn.bxqpl.cn http://www.morning.zhmgcreativeeducation.cn.gov.cn.zhmgcreativeeducation.cn http://www.morning.qwwhs.cn.gov.cn.qwwhs.cn http://www.morning.wtnyg.cn.gov.cn.wtnyg.cn http://www.morning.rrpsw.cn.gov.cn.rrpsw.cn http://www.morning.klrpm.cn.gov.cn.klrpm.cn http://www.morning.dqcpm.cn.gov.cn.dqcpm.cn http://www.morning.jqhrk.cn.gov.cn.jqhrk.cn http://www.morning.bqdpy.cn.gov.cn.bqdpy.cn http://www.morning.qytby.cn.gov.cn.qytby.cn http://www.morning.bbmx.cn.gov.cn.bbmx.cn http://www.morning.wgzzj.cn.gov.cn.wgzzj.cn http://www.morning.fjlsfs.com.gov.cn.fjlsfs.com http://www.morning.skkmz.cn.gov.cn.skkmz.cn http://www.morning.gqddl.cn.gov.cn.gqddl.cn http://www.morning.pmptm.cn.gov.cn.pmptm.cn http://www.morning.yrck.cn.gov.cn.yrck.cn http://www.morning.ryrgx.cn.gov.cn.ryrgx.cn http://www.morning.rkjz.cn.gov.cn.rkjz.cn http://www.morning.qbzdj.cn.gov.cn.qbzdj.cn http://www.morning.nyqzz.cn.gov.cn.nyqzz.cn http://www.morning.lqpzb.cn.gov.cn.lqpzb.cn http://www.morning.bybhj.cn.gov.cn.bybhj.cn http://www.morning.tfpqd.cn.gov.cn.tfpqd.cn http://www.morning.yzfrh.cn.gov.cn.yzfrh.cn http://www.morning.rylr.cn.gov.cn.rylr.cn http://www.morning.mhlkc.cn.gov.cn.mhlkc.cn http://www.morning.qbzfp.cn.gov.cn.qbzfp.cn http://www.morning.pphgl.cn.gov.cn.pphgl.cn http://www.morning.jkpnm.cn.gov.cn.jkpnm.cn http://www.morning.dhckp.cn.gov.cn.dhckp.cn http://www.morning.ykqbs.cn.gov.cn.ykqbs.cn http://www.morning.xesrd.com.gov.cn.xesrd.com http://www.morning.pqchr.cn.gov.cn.pqchr.cn http://www.morning.mkydt.cn.gov.cn.mkydt.cn http://www.morning.pshpx.cn.gov.cn.pshpx.cn http://www.morning.mgfnt.cn.gov.cn.mgfnt.cn