前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >国网B接口调阅实时视频规范解读和代码示例分析

国网B接口调阅实时视频规范解读和代码示例分析

原创
作者头像
音视频牛哥
发布于 2023-04-14 04:57:40
发布于 2023-04-14 04:57:40
63900
代码可运行
举报
运行总次数:0
代码可运行

接口描述

国网B接口调阅实时视频,相关规范写的比较粗略:

调阅实时视频包括信令接口和媒体流接口,采用标准的SIP INVITE+SDP流程,媒体传输使用RTP/RTCP

SDP 中 RTP Payload 的取值应遵守下面接口参数中的定义:

a) SDP 中的媒体信息,应仅有一个 m 行,用于描述视频格式。

b) 视频数据用 RTP 打包传输时,应考虑每个传输分组不大于 MTU,可采用的技术包括编码器层支持(如 ITU-T H.264 的 multi-slice 技术),或采用 RTP 层的分片机制(如 IETF RFC 3984 定义的 FU-A 技术)。

前端设备收到平台的INVITE请求后根据SDP描述进行媒体协商,协商通过后打开前端系统摄像机设备将获得的媒体流通过媒体通道发送到平台。

会话建立成功后,前端系统在某些特殊情况下可以主动结束当前呼叫。

平台应支持视频流的分发,以降低对前端系统的操作频繁性和节省网络带宽。

调阅实时视频的接口流程

主要功能流程如下:

a) F1:用户发送 INVITE 消息,携带 SDP 内容通过平台转发到前端设备。

b) F2:按照 SIP 要求,如前端系统在 0.5s 内未能处理该请求,则先发送 1xx 临时响应通过平台转发到用户。

c) F3:前端系统接受了调阅请求的操作,则发送携带 SDP 的 200 OK 响应通过平台转发到用户。

d) F4:用户发送 ACK 通过平台转发到前端设备。

e) 视频流从前端系统传输经平台转发到用户。

f) F5:用户结束会话,发送 BYE 消息到通过平台转发到前端系统。

g)F6:前端系统发送确认,将媒体通道拆线。

技术实现

由于国网B接口的invite实现和GB28181的差异不大,之前我们GB28181这块,已经有非常好的积累了。

​编辑 启动B接口后,完成平台端的register和PushResourse交互,有些平台注册后,会接着响应Push_Resource request,其他不表,这里主要谈下invte和Ack相关回调处理:

Invite信令如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    INVITE sip:1301110005010100001@192.168.0.102:5060 SIP/2.0
    Via: SIP/2.0/UDP 192.168.0.104:15060;branch=z9hG4bK864531896
    From: <sip:000000000000000001@0000000000>;tag=482531896
    To: <sip:1301110005010100001@192.168.0.102:5060>
    Call-ID: 804531783
    CSeq: 6 INVITE
    Content-Type: application/sdp
    Contact: <sip:000000000000000001@192.168.0.104:15060>
    Max-Forwards: 70
    User-Agent: SIPB
    Request-URI: <sip:1301110005010100001@192.168.0.102:5060>
    Content-Length: 152
    
    v=0
    o=- 0 0 IN IP4 192.168.0.104
    s=Play
    c=IN IP4 192.168.0.104
    t=0 0
    m=video 30004 RTP/AVP 100
    a=recvonly
    a=rtpmap:100 H264/90000
    y=0130111000

收到Invite回调处理逻辑如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public void ntsOnInvitePlay(String deviceId, SessionDescription session_des) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 先振铃响应下
                gb28181_agent_.respondPlayInvite(180, device_id_);

                MediaSessionDescription video_des = null;
                SDPRtpMapAttribute ps_rtpmap_attr = null;

                Vector<MediaSessionDescription> video_des_list = session_des_.getVideoPSDescriptions();
                if (video_des_list != null && !video_des_list.isEmpty()) {
                    for(MediaSessionDescription m : video_des_list) {
                        if (m != null && m.isValidAddressType() && m.isHasAddress() ) {
                            video_des = m;
                            ps_rtpmap_attr = video_des.getPSRtpMapAttribute();
                            break;
                        }
                    }
                }

                if (null == video_des) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInvitePlay get video description is null, response 488, device_id:" + device_id_);
                    return;
                }

                Log.i(TAG,"ntsOnInvitePlay, device_id:" +device_id_+", is_tcp:" + video_des.isRTPOverTCP()
                        + " rtp_port:" + video_des.getPort() + " ssrc:" + video_des.getSSRC()
                        + " address_type:" + video_des.getAddressType() + " address:" + video_des.getAddress());

                long rtp_sender_handle = libPublisher.CreateRTPSender(0);
                if ( rtp_sender_handle == 0 ) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInvitePlay CreateRTPSender failed, response 488, device_id:" + device_id_);
                    return;
                }


                gb28181_rtp_payload_type_  = 100;
                gb28181_rtp_encoding_name_ =  "PS";

                libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, video_des.isRTPOverUDP()?0:1);
                libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, video_des.isIPv4()?0:1);
                libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);
                libPublisher.SetRTPSenderSSRC(rtp_sender_handle, video_des.getSSRC());
                libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2M
                libPublisher.SetRTPSenderClockRate(rtp_sender_handle, 90000 /*ps_rtpmap_attr.getClockRate()*/);
                libPublisher.SetRTPSenderDestination(rtp_sender_handle, video_des.getAddress(), video_des.getPort());

                if ( libPublisher.InitRTPSender(rtp_sender_handle) != 0 ) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    return;
                }

                int local_port = libPublisher.GetRTPSenderLocalPort(rtp_sender_handle);
                if (local_port == 0) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    return;
                }

                Log.i(TAG,"get local_port:" + local_port);

                String local_ip_addr = IPAddrUtils.getIpAddress(context_);

                MediaSessionDescription local_video_des = new MediaSessionDescription(video_des.getType());


                local_video_des.addFormat(String.valueOf(100));
                local_video_des.addRtpMapAttribute(ps_rtpmap_attr);

                local_video_des.setAddressType(video_des.getAddressType());
                local_video_des.setAddress(local_ip_addr);
                local_video_des.setPort(local_port);

                local_video_des.setTransportProtocol(video_des.getTransportProtocol());
                local_video_des.setSSRC(video_des.getSSRC());

                if (!gb28181_agent_.respondPlayInviteOK(device_id_,local_video_des) ) {
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    Log.e(TAG, "ntsOnInvitePlay call respondPlayInviteOK failed.");
                    return;
                }

                gb28181_rtp_sender_handle_ = rtp_sender_handle;
            }

            private String device_id_;
            private SessionDescription session_des_;

            public Runnable set(String device_id, SessionDescription session_des) {
                this.device_id_ = device_id;
                this.session_des_ = session_des;
                return this;
            }
        }.set(deviceId, session_des),0);
    }

ack信令如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    ACK sip:1301110005010100001@192.168.0.102:5060 SIP/2.0
    Via: SIP/2.0/UDP 192.168.0.104:15060;branch=z9hG4bK991532349
    From: <sip:000000000000000001@0000000000>;tag=482531896
    To: <sip:1301110005010100001@192.168.0.102:5060>;tag=2d6ebb3d
    Call-ID: 804531783
    CSeq: 6 ACK
    Contact: <sip:000000000000000001@192.168.0.104:15060>
    Max-Forwards: 70
    User-Agent: SIPB
    Request-URI: <sip:1301110005010100001@192.168.0.102:5060>
    Content-Length: 0

ack回调代码处理逻辑如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public void ntsOnAckPlay(String deviceId) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);

                if (!isRTSPPublisherRunning && !isPushingRtmp && !isRecording) {
                    InitAndSetConfig();
                }

                libPublisher.SetGB28181RTPSender(publisherHandle, gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_, gb28181_rtp_encoding_name_);
                int startRet = libPublisher.StartGB28181MediaStream(publisherHandle);
                if (startRet != 0) {

                    if (!isRTSPPublisherRunning && !isPushingRtmp  && !isRecording) {
                        if (publisherHandle != 0) {
                            libPublisher.SmartPublisherClose(publisherHandle);
                            publisherHandle = 0;
                        }
                    }

                    destoryRTPSender();

                    Log.e(TAG, "Failed to start GB28181 service..");
                    return;
                }

                startAudioRecorder();

                startLayerPostThread();
                isGB28181StreamRunning = true;
            }

            private String device_id_;

            public Runnable set(String device_id) {
                this.device_id_ = device_id;
                return this;
            }

        }.set(deviceId),0);
    }

总结

国网B接口调阅实时视频流程和GB28181流程基本一致,感兴趣的开发者,可以参考相关的规范实现,B接口相对GB28181来说,面更窄,资料也更少,如果产品化,有测试平台的话,还是不难实现的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
bootstrap-fileinput插件实现文件上传
bootstrap-fileinput插件下载:https://github.com/kartik-v/bootstrap-fileinput Demo地址:https://plugins.krajee.com/file-input/demo 中文文档:http://www.bootstrap-fileinput.com/ 下载好的目录如下图:
别团等shy哥发育
2023/02/25
3.1K0
bootstrap-fileinput插件实现文件上传
bootstrap fileinput api
<link href=”../css/bootstrap.min.css”rel=”stylesheet”>
全栈程序员站长
2022/09/01
7360
Bootstrap File Input,最好用的文件上传组件
本篇介绍如何使用 Bootstrap File Input(最好用的文件上传组件)来进行图片的展示、上传,以及如何在服务器端进行文件保存。
全栈程序员站长
2022/09/06
4.3K0
Bootstrap File Input,最好用的文件上传组件
HarmonyOS运动开发:深度解析文件预览的正确姿势
##鸿蒙核心技术##运动开发##Core File Kit(文件基础服务)##Preview Kit(文件预览服务)#
用户2475552
2025/06/13
570
<input type=file>文件上传
<input> type 类型为 file 时使得用户可以选择一个或多个元素以提交表单的方式上传到服务器上,或者通过 JavaScript 的 File API 对文件进行操作 .
全栈程序员站长
2022/09/06
4.5K0
关于flask入门教程-bootstrap-fileinput实现文件上传
bootstrap-fileinput是一款非常优秀的HTML5文件上传插件,支持文件预览、多选等一系列特性。
python与大数据分析
2022/03/11
1.5K0
关于flask入门教程-bootstrap-fileinput实现文件上传
input type="file"属性详解,利用capture调用手机摄像头
Value:DOMString 选择文件的路径。DOMString 是UTF-16字符串。由于JavaScript已经使用了这样的字符串,所以DOMString 直接映射到 String。
德顺
2019/11/13
11.8K0
input type="file"属性详解,利用capture调用手机摄像头
支持多文件上传,预览,拖拽,基于bootstrap的上传插件fileinput 的ajax异步上传(转载)
<link href="__PUBLIC__/CSS/bootstrap.css" rel="stylesheet">
lin_zone
2018/08/15
2.7K0
支持多文件上传,预览,拖拽,基于bootstrap的上传插件fileinput 的ajax异步上传(转载)
fileinput的使用
这段时间做项目用到bootstrap fileinput插件上传文件,在用的过程中,网上能查到的api都不是很全,所以想着整理一份比较详细的文档,方便自己今后使用,也希望能给大家带来帮助,如有错误,希望大家积极指正。
全栈程序员站长
2022/06/26
9750
ASP.NET MVC5+EF6+EasyUI 后台管理系统(32)-swfupload多文件上传[附源码]
文件上传这东西说到底有时候很痛,原来的asp.net服务器控件提供了很简单的上传,但是有回传,还没有进度条提示。这次我们演示利用swfupload多文件上传,项目上文件上传是比不可少的,大家这个心里都
用户1149182
2018/01/16
1.6K0
ASP.NET MVC5+EF6+EasyUI 后台管理系统(32)-swfupload多文件上传[附源码]
bootstrap fileinput 文件上传和回显「建议收藏」
官网下载文件http://plugins.krajee.com/file-input/demo
全栈程序员站长
2022/08/15
2.9K0
基于Django+Bootstrap框架,设计微型小说网站
 以上这段是官方的自我介绍,说说我个人感受吧。首先这个插件支持批量上传,异步上传等功能,简化大部分JS逻辑方面的代码,具体只要跟着官方的API文档看一看,修改一些参数即可。其次,对于上传时会显示一个进度条,用于显示上传的完成度,这样直观反映了完成度。
迅达集团
2019/03/18
2.2K0
django 1.8 官方文档翻译:5-1-4 内建的Widget
Widget 是Django 对HTML 输入元素的表示。Widget 负责渲染HTML和提取GET/POST 字典中的数据。
ApacheCN_飞龙
2022/11/27
5.3K0
PHP开发——yii2多图上传组件的使用
最近在使用yii2开发一个表单页面的时候,有多图上传的需求,稍微找了找这方面的组件,基本都安利fileInput这个组件,于是就尝试着使用这个库来完成后端表单页面的多图上传功能。使用的过程中发现还是有不少小细节需要注意的,于是记录一下使用的过程。
Originalee
2018/08/30
1.4K0
文件上传控件bootstrap-fileinput与Python交互
[up-6108568931341e960672fcd7f8c2d51e57d.png] 注意 下面的原因,可能是和后端API交互不成功的原因 fileinput版本不正确,需要大于5.0 前端设置的name属性值和后端接收的属性值不一致 必要的css和js文件 请确保下面的js和css文件存在 <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <link rel="stylesheet" href="htt
嘉美伯爵
2021/01/05
1.1K0
扒一扒使用boostrap-fileinput上传插件遇到的坑,Bootstrap-fileinput上传插件的使用详解,「建议收藏」
由于公司项目的需求,需要实现动植物名录的添加,包括姓名等信息和图片等,需要使用bootstrap-fileinput的上传插件,在提交添加界面表单数据的同时上传一张或者多张图片,并将上传的图片保存到本地磁盘中(本文是f:盘的目录下),在在实现的时候,不适用bootstrap-fileinput上传插件本身的上传按钮(因为本身的按钮只能上传图片),需要点击提交,将表单的其他信息和图片一起提交到后台。
全栈程序员站长
2022/06/26
4.7K0
扒一扒使用boostrap-fileinput上传插件遇到的坑,Bootstrap-fileinput上传插件的使用详解,「建议收藏」
Web文件上传方法总结大全
文件上传在WEB开发中应用很广泛,我们经常发微博、发微信朋友圈都用到了图片上传功能。
用户7657330
2020/08/14
4.5K0
php之yii2 图片上传插件fileinput使用说明
在网上搜了很多yii2图片上传插件,感觉这个和bootstrap融合的很好,比较能满足需求,故推荐给大家。
IT工作者
2022/06/30
1.1K0
webuploader java版本
网上一些webuploader上传的资料,有php版和java版本的,做了一下整合,现分享以下成果,可以讨论,不喜勿碰。说一下过程。
用户5640963
2019/07/26
1.1K0
<input type=”file” />浏览时只显示指定文件类型
1.accept=”application/msexcel” 2.accept=”application/msword” 3.accept=”application/pdf” 4.accept=”application/poscript” 5.accept=”application/rtf” 6.accept=”application/x-zip-compressed” 7.accept=”audio/basic” 8.accept=”audio/x-aiff” 9.accept=”audio/x-mpeg” 10.accept=”audio/x-pn/realaudio” 11.accept=”audio/x-waw” 12.accept=”image/gif” 13.accept=”image/jpeg” 14.accept=”image/tiff” 15.accept=”image/x-ms-bmp” 16.accept=”image/x-photo-cd” 17.accept=”image/x-png” 18.accept=”image/x-portablebitmap” 19.accept=”image/x-portable-greymap” 20.accept=”image/x-portable-pixmap” 21.accept=”image/x-rgb” 22.accept=”text/html” 23.accept=”text/plain” 24.accept=”video/quicktime” 25.accept=”video/x-mpeg2″ 26.accept=”video/x-msvideo”
全栈程序员站长
2022/09/13
7560
<input type=”file” />浏览时只显示指定文件类型
推荐阅读
相关推荐
bootstrap-fileinput插件实现文件上传
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验