GB28181协议--实时视音频点播(预览)

article/2025/9/19 11:51:20

1、基本要求

根据《GB/T 28181 —2016》第9章关于实时视音频点播的描述,其内容如下:

  • 实时视音频点播的SIP 消息应通过本域或其他域的SIP 服务器进行路由、 转发, 目标设备的实时视音频流宜通过本域内的媒体服务器进行转发。
  • 实时视音频点播采用SIP 协议(IETF RFC3261) 中的Invite 方法实现会话连接, 采用 RTP/RTCP
    协议(IETF RFC3550) 实现媒体传输。

2、基本流程

客户端主动发起的实时视音频点播流程见下图:
在这里插入图片描述
其中, 信令1、8、9、10、11、12 为SIP 服务器接收到客户端的呼叫请求后通过 B2BUA 代理方式建立媒体流接收者与媒体服务器之间的媒体流信令过程, 信令2-7 为SIP 服务器通过三方呼叫控制建立媒体服务器与媒体流发送者之间的媒体流信令过程, 信令13~16 为媒体流接收者断开与媒体服务器之间的媒体流信令过程, 信令17 ~20 为 SIP 服务器断开媒体服务器与媒体流发送者之间的媒体流信令过程。
命令流程描述如下:

  • 媒体流接收者向SIP 服务器发送Invite 消息, 消息头域中携带 Subject 字段, 表明点播的视频源ID、 发送方媒体流序列号、 媒体流接收者ID、 接收端媒体流序列号等参数,SDP 消息体中s 字段为“Play”代表实时点播。
  • SIP 服务器收到Invite 请求后, 通过三方呼叫控制建立媒体服务器和媒体流发送者之间的媒体连接。 向媒体服务器发送Invite 消息, 此消息不携带SDP 消息体。
  • 媒体服务器收到SIP 服务器的Invite 请求后, 回复200 OK 响应, 携带SDP 消息体, 消息体中描述了媒体服务器接收媒体流的IP、 端口、 媒体格式等内容。
  • SIP 服务器收到媒体服务器返回的200 OK 响应后, 向媒体流发送者发送Invite 请求, 请求中携带消息3 中媒体服务器回复的200 OK 响应消息体,s 字段为“Play”代表实时点播, 增加y 字段描述SSRC 值,f 字段描述媒体参数。
  • 媒体流发送者收到SIP 服务器的Invite 请求后, 回复200 OK 响应, 携带SDP 消息体, 消息体中描述了媒体流发送者发送媒体流的IP、 端口、 媒体格式、SSRC 字段等内容。
  • SIP 服务器收到媒体流发送者返回的200 OK 响应后, 向媒体服务器发送 ACK 请求, 请求中携带消息5 中媒体流发送者回复的200 OK 响应消息体, 完成与媒体服务器的Invite 会话建立过程。
  • SIP 服务器收到媒体流发送者返回的200 OK 响应后, 向媒体流发送者发送 ACK 请求, 请求中不携带消息体, 完成与媒体流发送者的Invite 会话建立过程。
  • 完成三方呼叫控制后,SIP 服务器通过B2BUA 代理方式建立媒体流接收者和媒体服务器之间的媒体连接。 在消息1 中增加SSRC 值, 转发给媒体服务器。
  • 媒体服务器收到Invite 请求, 回复200 OK 响应, 携带SDP 消息体, 消息体中描述了媒体服务器发送媒体流的IP、 端口、 媒体格式、SSRC 值等内容。
  • SIP 服务器将消息9 转发给媒体流接收者。
  • 媒体流接收者收到200 OK 响应后, 回复 ACK 消息, 完成与SIP 服务器的Invite 会话建立过程。
  • SIP 服务器将消息11 转发给媒体服务器, 完成与媒体服务器的Invite 会话建立过程。
  • 媒体流接收者向SIP 服务器发送 BYE 消息, 断开消息1、10、11 建立的同媒体流接收者的Invite 会话。
  • SIP 服务器收到 BYE 消息后回复200 OK 响应, 会话断开。
  • SIP 服务器收到 BYE 消息后向媒体服务器发送 BYE 消息, 断开消息8、9、12 建立的同媒体服务器的Invite 会话。
  • 媒体服务器收到 BYE 消息后回复200 OK 响应, 会话断开。
  • SIP 服务器向媒体服务器发送 BYE 消息, 断开消息2、3、6 建立的同媒体服务器的Invite会话。
  • 媒体服务器收到 BYE 消息后回复200 OK 响应, 会话断开。
  • SIP 服务器向媒体流发送者发送 BYE 消息, 断开消息4、5、7 建立的同媒体流发送者的Invite 会话。
  • 媒体流发送者收到 BYE 消息后回复200 OK 响应, 会话断开。

3、基于SIP库的点播开发

  • sip信令处理:
int SipEventProcess(GB28181Param_t *pGB28181Param)
{int ret = 0;eXosip_event_t *sipEvent = NULL;sipEvent = eXosip_event_wait( 0, 100);if (!sipEvent){//GB_PrintError("sipEvent is null. \n");return -1;}eXosip_lock();eXosip_default_action(sipEvent);eXosip_automatic_refresh();eXosip_unlock();switch(sipEvent->type){case EXOSIP_CALL_INVITE:/* INVITE方法建立会话 */ret = SipInviteProcess(pGB28181Param, sipEvent);break;case EXOSIP_CALL_ACK:ret = SipStartStreamProcess(pGB28181Param, sipEvent);break;case EXOSIP_CALL_CLOSED:ret = SipStopStreamProcess(pGB28181Param, sipEvent);break;default:break;}eXosip_event_free(sipEvent);return ret;
}
  • sip Invite处理
static int SipInviteProcess(GB28181Param_t *pGB28181Param, eXosip_event_t *sipEvent)
{int ret = -1;uint8_t iChn;char transMode[16] = "active";char *message = NULL;char sdpBody[2048] = {0,};osip_message_t *aswMsg = NULL;osip_message_t *cloneEvent = NULL;sdp_message_t *sdpMsg = NULL;if (!pGB28181Param){return -1;}if(MSG_IS_INVITE(sipEvent->request)){eXosip_lock();ret = eXosip_call_build_answer( sipEvent->tid, 200, &aswMsg);if(ret != OSIP_SUCCESS){eXosip_call_send_answer( sipEvent->tid, 603, NULL);eXosip_unlock();return -1;}eXosip_unlock();// 获取设备通道号if ((ret = osip_message_clone(sipEvent->request, &cloneEvent)) != OSIP_SUCCESS){return -1;}if ((ret = osip_to_to_str(cloneEvent->to, &message)) != OSIP_SUCCESS){return -1;}// 判断通道号是否有效iChn = GetChannelIdByMsg(message, pGB28181Param);if (!IS_ICHN_VALID(iChn)){return -1;}osip_message_free(cloneEvent);osip_free(message);sdpMsg = eXosip_get_remote_sdp(sipEvent->did); if(NULL == sdpMsg){return -1;}if (OSIP_SUCCESS == sdp_message_to_str(sdpMsg, &message)){char *str = NULL;char strTmp[16] = {0,};if ((str = strstr(message ,"a=setup:"))){strncpy(strTmp, str+strlen("a=setup:"), sizeof(strTmp));}if ((str = strstr(strTmp, "active"))){strncpy(transMode, "passive", strlen("passive"));}osip_free(message);}snprintf(sdpBody, sizeof(sdpBody),"v=0\r\n""o=%s 0 0 IN IP4 %s\r\n"		/*会话源*//*用户名/会话ID/版本/网络类型/地址类型/地址*/"s=Play\r\n"					/*会话名*/"c=IN IP4 %s\r\n" 				/*连接信息*//*网络类型/地址信息/多点会议的地址*/"t=0 0\n"						/*时间*//*开始时间/结束时间*/"m=video %s RTP/AVP %d\r\n"		/*媒体/端口/传送层协议/格式列表*/"a=setup:%s\r\n""a=sendonly\r\n"				/*收发模式*/"a=rtpmap:%d %s/%d\r\n"			/*净荷类型/编码名/时钟速率*/"a=username:%s\r\n""a=password:%s\r\n""a=filesize:0\r\n""y=%d\r\n",pGB28181Param->userParam.devSipID,pGB28181Param->userParam.devSipIP,pGB28181Param->userParam.devSipIP,pGB28181Param->userParam.devTcpActivePort[iChn],pGB28181Param->runParam.vedioPayload,transMode,pGB28181Param->runParam.vedioPayload,pGB28181Param->runParam.vedioPackType,pGB28181Param->runParam.vedioSample,pGB28181Param->userParam.devSipID,pGB28181Param->userParam.devSipPasswd,pGB28181Param->runParam.ssrc);eXosip_lock();osip_message_set_content_type(aswMsg, "application/sdp");osip_message_set_body(aswMsg, sdpBody, strlen(sdpBody));ret = eXosip_call_send_answer( sipEvent->tid, 200, aswMsg);eXosip_unlock();if (ret == OSIP_SUCCESS){ret = 0;}}return ret;
}
  • 开始点播函数
static int SipStartStreamProcess(GB28181Param_t *pGB28181Param, eXosip_event_t *sipEvent)
{int ret = 0;uint8_t iChn = 0;uint8_t transMode = TCP_PASSIVE;char *message = NULL;char *mediaSeverIP = NULL;char *mediaSeverPort = NULL;sdp_message_t *sdpMsg = NULL;osip_message_t *cloneEvent = NULL;if (!pGB28181Param){return -1;}if ((ret = osip_message_clone(sipEvent->request, &cloneEvent)) != OSIP_SUCCESS){return -1;}if ((ret = osip_to_to_str(cloneEvent->to, &message)) != OSIP_SUCCESS){return -1;}/* 从SIP服务器发过来的INVITE请求的o字段或c字段或m字段中获取媒体服务器的IP地址与端口 */iChn = GetChannelIdByMsg(message, pGB28181Param);// 判断端口是否有效if (!IS_ICHN_VALID(iChn)){return -1;}osip_message_free(cloneEvent);osip_free(message);sdpMsg = eXosip_get_remote_sdp(sipEvent->did); if(NULL == sdpMsg){return -1;}mediaSeverIP = sdp_message_o_addr_get(sdpMsg);    		/*媒体服务器IP地址*/mediaSeverPort = sdp_message_m_port_get(sdpMsg, 0); 	/*媒体服务器IP端口*/if (OSIP_SUCCESS == sdp_message_to_str(sdpMsg, &message)){char *str = NULL;char strTmp[16] = {0,};if ((str = strstr(message ,"a=setup:"))){strncpy(strTmp, str+strlen("a=setup:"), sizeof(strTmp));}if ((str = strstr(strTmp, "active"))){transMode = TCP_ACTIVE;}osip_free(message);}ret = StartStreamByChannel(mediaSeverIP, mediaSeverPort, iChn, transMode, pGB28181Param);sdp_message_free(sdpMsg);return ret;
}

上面的这个函数会根据通道号,给流媒体服务器发送相关的码流数据。

参考资料:
《GBT 28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求》
推荐阅读:
GB28181协议–设备注册和注销
网络流媒体–SDP会话描述协议(RFC-4566)


http://chatgpt.dhexx.cn/article/QIjAgutU.shtml

相关文章

GB28181协议--心跳

1、心跳介绍: 根据《GBT 28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求》9.6.1状态信息报送章节描述。 当源设备(包括网关、SIP 设备、SIP 客户端或联网系统) 发现工作异常时, 应立即向本 SIP 监控域的SIP服务器发送状态信息; 无异常时, 应定…

国标28181: 视频国标28181协议

国标的由来 GB28181国标解决平台与平台对接问题 比如A平台大连交警系统需要看B平台上海交警系统的视频。需要对接过来,实现调度视频。这时候需要知道他们取流的协议,各家厂商都自定义了一套协议,就很麻烦,因此国家就制定了GB28…

【C/C++】isalpha、islower、isupper、isalnum、isblank、isspace函数cctype / ctype.h头文件

isalpha、islower、isupper、isalnum、isblank、isspace这些函数都在<cctype>&#xff08;即C语言中的<ctype.h>&#xff09;的头文件里面&#xff0c;下图是它们所表示的范围&#xff1a; 总的来说就是&#xff1a; isalpha &#xff08;字母&#xff0c;包括大写…

细微之处见真章之StringUtils的isBlank函数细节解读

一、背景 技术群里有一个老铁分享了一段 commons-lang 的 StringUtils 工具类的代码&#xff1a; public static boolean isBlank(final CharSequence cs) {int strLen;if (cs null || (strLen cs.length()) 0) {return true;}for (int i 0; i < strLen; i) {if (Char…

字符串判空,isBlank 和 isEmpty 到底选那个?

字符串的判空&#xff0c;日常开发是经常要做的一种校验&#xff0c;common-lang包帮我们做了一些字符串判空的封装 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId> </dependency> 并对外提…

sbrk() 函数是干什么的?

brk和sbrk主要的工作是实现虚拟内存到内存的映射. 在GNU C中,内存分配是这样的: 每个进程可访问的虚拟内存空间为3G&#xff0c;但在程序编译时&#xff0c;不可能也没必要为程序分配这么大的空间&#xff0c;只分配并不大的数据段空间&#xff0c;程序中动态分配的空间就是从这…

ORACLE如何修改默认端口号

oracle在安装的的时候&#xff0c;除了可以默认端口号&#xff0c;也可以填入自定义的端口号。但是项目上会出现这样的情况&#xff0c;项目在进展时期&#xff0c;需要对数据库安全升级&#xff0c;首先要修改数据库的默认端口号。那如何来设置oracle的默认端口号&#xff0c;…

Oracle如何更改端口号

Oracle默认端口号为8080&#xff0c;因为Tomcat端口默认也是8080所以需要把Oracle端口更改一下&#xff0c;不建议更改Tomcat端口号 运行cmd命令行操作 查看Oracle版本&#xff1a; 链接Oracle数据库&#xff1a; conn system/root as sysdba&#xff0c;system/root用户名和…

Oracle 19c中默认端口

Oracle官网https://docs.oracle.com/en/database/oracle/oracle-database/19/rilin/port-numbers-and-protocols-of-oracle-components.html#GUID-D168F70C-BECE-4F9A-B616-D9103A35F1FFhttps://www.cndba.cn/hbhe0316/article/87226https://www.cndba.cn/hbhe0316/article/872…

oracle 数据库改端口

Oracle 11g修改默认端口1521为其他值 1、修改listener.ora 打开文件D:\app\Administrator\product\11.2.0\dbhome_1\NETWORK\ADMIN\listener.ora&#xff0c;修改PORT 后的数值&#xff0c;如下图 修改listener.ora文件 2、重启TNSListener服务 在Windows服务中重启OracleOr…

如何查看oracle的服务端口号

查看 oracle服务端口的步骤&#xff1a; 按住WinR键&#xff0c;弹出DOS命令窗口。 输入cmd命令&#xff0c;点击确定。 弹出的DOS窗口中&#xff0c;i输入 lsnrctl status 命令&#xff0c;并点击Enter键。 找到窗口中&#xff0c;以下文字‘监听端点概要 (DESCRIPTION…

JUC是什么?

JUC表示什么 JUC是java.util.concurrent包的缩写&#xff0c;其包结构如下。 JUC框架结构 JUC是包的简称&#xff0c;JUC可能也是Java核心里最难的一块儿&#xff0c;JUC指的是Java的并发工具包&#xff0c;里边提供了各种各样的控制同步和线程通信的工具类。学习JUC之前&a…

JUC总结系列篇 (二) : 对线程的理解和使用总结

文章内容&#xff1a; 一.为什么需要多线程 二.线程的创建 三.线程的方法sleep(),run(),wait(),yeid(),join(),interrupt()等方法归纳总结 四.线程的状态及其转换 五.线程的交替执行案例 六.多个线程依次执行案例 七.多线程并发带来的线程安全问题 一.为什么需要多线程&#x…

JUC源码系列-ReentrantReadWriteLock

继承关系 ReadLock和WriteLock是ReentrantReadWriteLock的两个内部类&#xff0c;Lock的上锁和释放锁都是通过AQS来实现的。 AQS定义了独占模式的acquire()和release()方法&#xff0c;共享模式的acquireShared()和releaseShared()方法。 还定义了抽象方法tryAcquire()、tryA…

了解JUC

高级技术之 JUC 高并发编程 内容概览 1 、什么是JUC2 、Lock接口3 、线程间通信4 、集合的线程安全5 、多线程锁6 、Callable接口7 、JUC三大辅助类: CountDownLatch CyclicBarrier Semaphore8 、读写锁: ReentrantReadWriteLock9 、阻塞队列10 、ThreadPool线程池11 、Fork/…

juc系列(1)---进程,线程,并行,并发

目录 概述进程线程关系并发并行&#xff1a;同步异步&#xff1a;对比 概述 进程 程序由指令和数据组成&#xff0c;但这些指令要运行&#xff0c;数据要读写&#xff0c;就必须将指令加载至CPU,数据加载至内 存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加…

JUC系列(九) CAS 与锁的理解

&#x1f4e3; &#x1f4e3; &#x1f4e3; &#x1f4e2;&#x1f4e2;&#x1f4e2; ☀️☀️你好啊&#xff01;小伙伴&#xff0c;我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。 &#x1f4d2; 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️&#xff0c;擅…

JUC系列(四)

1、CAS 1.1、没有CAS之前&#xff0c;保证线程安全的方式 多线程环境不使用原子类保证线程安全&#xff08;基本数据类型&#xff09; public class T3 {volatile int number 0;//读取public int getNumber(){return number;}//写入加锁保证原子性public synchronized void…

1、什么是juc

1、juc简介 在java中&#xff0c;线程部分是重点&#xff0c;juc就是java.util.concurrent工具包的简称。这是一个处理线程的工具包&#xff0c;从jdk1.5开始出现 2、进程与线程 进程&#xff1a;指在系统中正在运行的一个应用程序&#xff1b;程序一旦运行就是进程&#xff1…

JUC系列(六) 线程池

&#x1f4e3; &#x1f4e3; &#x1f4e3; &#x1f4e2;&#x1f4e2;&#x1f4e2; ☀️☀️你好啊&#xff01;小伙伴&#xff0c;我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。 &#x1f4d2; 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️&#xff0c;擅…