onvif协议+高性能rtspclient打通大屏投屏协议(一)

1、onvif协议

  1.1 onvif简述和作用

a) 搜索列表
onvif协议本身是一个非常好的协议,帮助打通网内所有视频,搜索,播放和控制都是可以的。
b) 如何搜索
使用多播协议,以前写过很多多播的文章可以看,读者可以查看我其他文章,我是使用多播协议,因为正好,搜索所有大屏也需要使用多播协议,例如ssdp协议,而实际上onvif使用的是多播和soap,打通的流程过程就是同时搜索大屏和onvif,将两种设备放入到各自的列表,
c) 融合
这里又有另外一种模式了,就是我们的客户希望大屏作为一个融合过后的投屏,而不是一个一个地投上去,毕竟,投屏协议同时只支持一路视频,那么如何做呢,以下就是做法

  1.2 制作高性能rtspclient

为什么要rtspclient,因为onvif协议需要,抓住设备列表以后获取协议地址,然后开始做融合,问题是性能,rtsp拉流是比较耗性能和线程的,所以如果使用其他比如ffmpeg或者live555 等不符合要求。
任何高性能的客户端只能自己制作,使用单一进程联结多路,或者使用多进程联结多路,这个需要指挥调度,问题是我们需要视频融合做mcu,所以,使用单一进程是最好的,同时联结9路画到一个虚拟屏幕里,硬件解码,再重新硬件进行一路融合编码,发送到服务器,以下是rtspclient 伪代码

int options( sock and string)
{
	cout << ======<<"OPTIONS"<<============= << endl;
	char sendBuf[2048];
	snprintf(sendBuf, 1024,
		"OPTIONS rtsp://1.1.1.1:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0\r\n"
		"CSeq: 2\r\n"
		"User-Agent: qianbo/3.0.12 (18091589062)\r\n"
		"\r\n");
	//printf("sendBuf :\n%s\n", sendBuf);

	sendto(sock, sendBuf, strlen(sendBuf), 0, (sockaddr*)&sockAddr, sizeof(sockAddr));

	return 0;
}

void describe(sock and string)
{
//发送describe返回401 安全需要认证机制
	cout << ======<<"DESCRIBE"<<============= << endl;
	char sendBuf[2048];
	snprintf(sendBuf, 2048,
		"DESCRIBE rtsp://1.1.1.1:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0\r\n"
		"CSeq: 3\r\n"
		"User-Agent: qianbo/1.1.1 (18091589062)\r\n"
		"Accept: application/sdp\r\n"
		"\r\n");
	//printf("sendBuf :\n%s\n", sendBuf);

	sendto(sock, sendBuf, strlen(sendBuf), 0, (sockaddr*)&sockAddr, sizeof(sockAddr));
	return 0;
}


int describe_authorization(sock and address,  char *realm, char *nonce, const char *response)
{
//返回401时需要客户端认证
cout << "DESCRIBE_AUTHORIZATION" << endl;
	char sendBuf[2048];
	snprintf(sendBuf, 2048,
		"DESCRIBE rtsp://1.1.1.1:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0\r\n"
		"CSeq: 4\r\n"
		"Authorization: Digest username=\"admin\", realm=\"%s\", nonce=\"%s\", uri=\"rtsp://192.168.8.250:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1\", response=\"%s\"\r\n"
		"User-Agent: qianbo/1.1.1 (18091589062)\r\n"
		"Accept: application/sdp\r\n"
		"\r\n",
		realm, nonce, response);


	return sendto(sock, sendBuf, strlen(sendBuf), 0, (sockaddr*)&sockAddr, sizeof(sockAddr));
	
}

int setup(SOCKET& sock, sockaddr_in& sockAddr, char* realm, char* nonce, const char* response)
{
	cout << "SETUP" << endl;
	char sendBuf[2048];
	snprintf(sendBuf, 2048,
	"SETUP rtsp://192.168.8.250:554/Streaming/Channels/101/trackID=1?transportmode=unicast&profile=Profile_1 RTSP/1.0\r\n"
		"CSeq: 5\r\n"
		"Authorization: Digest username=\"admin\", realm=\"%s\", nonce=\"%s\", uri=\"rtsp://192.168.8.250:554/Streaming/Channels/101/\", response=\"%s\"\r\n"
		"User-Agent: qianbo/1.1.1 (18091589062)\r\n"
		"Transport: RTP/AVP;unicast;client_port=23332-23333\r\n"
		"\r\n",
		realm, nonce, response);

	return sendto(sock, sendBuf, strlen(sendBuf), 0, (sockaddr*)&sockAddr, sizeof(sockAddr));
}

int play(SOCKET& sock, sockaddr_in& sockAddr, char* realm, char* nonce, const char* response, char *session)
{
	cout << "PLAY" << endl;
	char sendBuf[2048];
	snprintf(sendBuf, 2048,
		"PLAY rtsp://192.168.8.250:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0\r\n"
		"CSeq: 6\r\n"
		"Authorization: Digest username=\"admin\", realm=\"%s\", nonce=\"%s\", uri=\"rtsp://192.168.8.250:554/Streaming/Channels/101/\", response=\"%s\"\r\n"
		"User-Agent: qianbo/1.1.1 (18091589062)\r\n"
		"Session: %s\r\n"
		"Range: npt=0.000-\r\n"
		"\r\n",
		realm, nonce, response, session);
	return sendto(sock, sendBuf, strlen(sendBuf), 0, (sockaddr*)&sockAddr, sizeof(sockAddr));
}

int teardown(SOCKET& sock, sockaddr_in& sockAddr, char* realm, char* nonce, const char* response, char* session)
{
	cout <<"TEARDOWN"" << endl;
	char sendBuf[2048];
	snprintf(sendBuf, 2048,
		"TEARDOWN rtsp://192.168.8.250:554/Streaming/Channels/101/?transportmode=unicast&profile=Profile_1 RTSP/1.0\r\n"
		"CSeq: 7\r\n"
		"Authorization: Digest username=\"admin\", realm=\"%s\", nonce=\"%s\", uri=\"rtsp://192.168.8.250:554/Streaming/Channels/101/\", response=\"%s\"\r\n"
		"User-Agent: qianbo (18091589062)\r\n"
		"Session: %s\r\n"
		"\r\n",
		realm, nonce, response, session);

	return sendto(sock, sendBuf, strlen(sendBuf), 0, (sockaddr*)&sockAddr, sizeof(sockAddr));
}

1.3 onvif 探针

需要向网络中发送soap包探听

int sendProbe(int fd,const char *uuid,const char *types)
{
    int r;
    struct sockaddr_in srv;
    memset( &srv, 0, sizeof(struct sockaddr_in) );
    srv.sin_family = AF_INET;
    srv.sin_port = htons(PUERTO);
	srv.sin_addr.s_addr = inet_addr(GRUPO);
  
    char Probe[4096];
    sprintf(Probe,"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
            "<Envelope xmlns:dn=\"http://www.onvif.org/ver10/network/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\">"\
            "<Header>"\
            "<wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">%s</wsa:MessageID>"\
            "<wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>"\
            "<wsa:Action xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>"\
            "</Header>"\
            "<Body>"\
            "<Probe xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\">"\
            "<Types>%s</Types>"\
            "<Scopes />"\
            "</Probe>"\
            "</Body>"\
            "</Envelope>\r\n",uuid,types);


    if( (r = sendto(fd, Probe, strlen(Probe), 0, (struct sockaddr *)&srv, sizeof(srv))) < 0 )
    {
		cout<<"sendto failed"<<endl;
        closesocket(fd);
        return -1;
    }

    return 0;
}

2 投屏协议

读者可以参考我的其他投屏协议的文章,实际上投屏协议需要一个服务器,大部分大屏是支持各类协议,我们制作通用的rtsp服务器就行,使用tcp方式发送rtp,这样防止服务器在外网,而大屏在内网。
服务器在外网的时候有一个好处,就是可以将内网的视频传送到另外一个地方的大屏中,这是可以的。但是需要一个寻找大屏和通知大屏的内网代理,这个以后说。

3 发送和通知

需要使用ssdp等协议发送刚才融合的视频,把地址给到大屏,如果同时投屏到多个,那么多个大屏会同时播放融合的视频,这种方案还可以使用到教学种,可以将教学视频同时投放到多个教室。

后面的几节会具体讲述各个过程的具体实现,未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qianbo_insist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值