近一年来,听到最多的就是某某功能网上有开源的,为什么不用开源的非得要自己写,诚然,开源的优点不言而喻,稳定、快速集成等等,可是既然你做了这个模块为何不认真的搞清楚其内部机制呢?写通过rtsp协议取海康IPC视频流的时候,个人花了一个月从前期的理解,查资料以及GitHub上找开源的优质代码,最终站在巨人的肩上,才匆匆忙忙的完成了模块的开发,虽然代码质量不及开源,然而机制了于胸,个人觉得已经满足了。
-------2022.07.18个人感想
RTSP(Real Time Streaming Protocol),实时流传输协议,是基于TCP/IP协议实现的一个应用协议,RTSP包括RTP和RTCP,其中RTP用于音视频传输协议,RTCP用于控制传输,数据的传输可以基于TCP也可以基于UDP完成。
上述的基本介绍包含了多重信息,通过下面图进一步加深了解。
根据上图,我们可以了解到,要实现一个RTSP CLIENT,我们要经历至少四步;
1、通过TCP/IP创建socket连接服务端;
2、实现RTSP协议,通过OPTIONS、DESCRIBE、SETUP、PLAY方法实现与server的交互;
3、通过TCP/UDP解析RTP包,实现对音视频数据解封装,根据协议视频解析为H264orH265等,音频解析为aac等。
4、销毁连接,释放资源
TCP/IP实现socket通过IP端口连接服务器
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include <sys/types.h>
#include<sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include<netinet/in.h>
//https://blog.csdn.net/xiongya8888/article/details/96996236
int socket_select_connect(int handler,unsigned int timeout)
{
int ret ;
fd_set write_set;
struct timeval tv;
FD_ZERO(&write_set);
FD_SET(handler,&write_set);
tv.tv_sec = timeout;
tv.tv_usec = 0;
ret = select(handler + 1,NULL, &write_set,NULL,&tv);
if(ret <= 0){
close(handler);
return -1;
}
return 0;
}
int socket_connect_by_time(int handler,struct sockaddr *server,size_t len,unsigned int timeout)
{
int ret = 0;
if(handler < 0){
return -1;
}
if(NULL == server){
return -1;
}
ret = connect(handler,server,len);
if(0 != ret && EINPROGRESS == errno){
ret = socket_select_connect(handler,timeout);
}
return ret;
}
/*
socket connect server
1、创建socket,将socket设置为非阻塞模式
2、调用connect链接,若立即返回0则表示链接成功,不能链接则返回-1,这时候通过错误码是否表示暂时不能完成,如果是继续下一步。
3、调用select在指定的时间内判断socket是否可写,如果可写表明connect链接成功,返回socket句柄
*/
int socket_connect_server(char *ip_addr,unsigned short port,unsigned int timeout)
{
struct sockaddr_in server_addr;
int socket_fd = 0;
int r = 0;
int flags;
if(NULL == ip_addr){
printf("[%s:%d] ip addr is null\n",__FUNCTION__,__LINE__);
return -1;
}
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip_addr);
server_addr.sin_port = htons(port);
socket_fd = socket(AF_INET,SOCK_STREAM,0);
if(socket_fd < 0){
printf("[%s:%d]create socket fail\n",__FUNCTION__,__LINE__);
return -1;
}
//非阻塞
flags = fcntl(socket_fd,F_GETFL,0);
fcntl(socket_fd,F_SETFL,flags|O_NONBLOCK);
r = socket_connect_by_time(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr),timeout);
return 0 == r ? socket_fd:-1;
}
#if 0 //test
int main()
{
int handler = -1;
handler = socket_connect_server("192.168.1.30",8080,5);
printf("[%s:%d]handler is[%d]\n",__FUNCTION__,__LINE__,handler);
if(handler > 0){
close(handler);
}
}
#endif
以上代码通过IP和端口实现对server的链接,这也是我们实现rtsp client的第一步,通过TCP/IP链接server,基于这一步,后面实现rtsp 协议中的OPTIONS DESCRIBE SETUP PLAY TRARDOWN等方法的实现。