GB28181-2022相对2016版“基于TCP协议的视音频媒体传输要求“调整

简介: GB28181-2022针对“基于TCP协议的视音频媒体传输”实时点播、历史视频回放与下载中,TCP媒体传输重连机制,做了说明。

规范解读

GB28181-2022针对“基于TCP协议的视音频媒体传输”实时点播、历史视频回放与下载中,TCP媒体传输重连机制,做了说明。


修改后的“基于TCP协议的视音频媒体传输要求”如下:


实时视频点播、历史视频回放与下载的TCP媒体传输应支持基于RTP封装的视音频PS流,封装格式参照IETF RFC 4571。


流媒体服务器宜同时支持作为TCP媒体流传输服务端和客户端。在默认情况下,前端设备向流媒体服务器发送媒体流时,前端设备应作为TCP媒体流传输客户端,流媒体服务器作为TCP媒体流传输服务端;同级或跨级流媒体服务器间基于TCP协议传输视频流时,媒体流的接收方宜作为TCP媒体流传输服务端。


媒体流的发送方和接收方可扩展SDP参数进行TCP媒体流传输服务端和客户端的协商,协商机制应符合附录G及IETF RFC 4571的定义。


实时视频点播、历史视频回放与下载的TCP媒体传输在建立TCP连接时应支持重连机制。首次TCP连接失败,TCP媒体流传输客户端应间隔一段时间进行重连,重连间隔应不小于l s,重连次数应不小于3次。

代码实现

本文以大牛直播SDK实现的Andorid平台GB28181设备接入模块为例,收到Invite处理如下,其中SetRTPSenderTransportProtocol()设置TCP/UDP传输模式:

0900883464011a0406294269dfa01b4a.jpg

ntsOnInvitePlay()处理代码如下:

// Author: daniusdk.com
@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;
            // 28181 视频使用PS打包
            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;
            }
            if (null == ps_rtpmap_attr) {
                gb28181_agent_.respondPlayInvite(488, device_id_);
                Log.i(TAG, "ntsOnInvitePlay get ps rtp map attribute 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_  = ps_rtpmap_attr.getPayloadType();
            gb28181_rtp_encoding_name_ =  ps_rtpmap_attr.getEncodingName();
            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, 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(ps_rtpmap_attr.getPayloadType()));
            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后:

// Author: daniusdk.com
@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;
            }
            if (!isRTSPPublisherRunning && !isPushingRtmp && !isRecording) {
                CheckInitAudioRecorder();
            }
            startLayerPostThread();
            isGB28181StreamRunning = true;
        }
        private String device_id_;
        public Runnable set(String device_id) {
            this.device_id_ = device_id;
            return this;
        }
    }.set(deviceId),0);
}

总结

TCP媒体传输重连机制,非常必要,实际上在2022出来之前,我们也已经做了很好的重连处理,GB28181-2022对此专门做了详细的解释说明,具体实现难度不大,感兴趣的开发者可以酌情参考。

相关文章
|
12月前
|
消息中间件 网络协议 网络性能优化
为什么TCP要做成流式协议,而非包呢?
为什么TCP要做成流式协议,而非包呢?
91 1
|
5月前
|
网络协议 API 网络安全
探讨TCP传输视频流并利用FFmpeg进行播放的过程
探讨TCP传输视频流并利用FFmpeg进行播放的过程
462 0
|
编解码 监控 网络协议
GB/T28181-2016基于RTP的视音频数据封装和技术实现
首先我们先回顾下相关技术规范,看看基于RTP的音视频数据PS封装。
139 0
GB/T28181-2016基于RTP的视音频数据封装和技术实现
|
网络协议 前端开发 开发工具
GB28181基于TCP协议的视音频媒体传输探究及实现
我们先看看官方规范针对TCP协议的视音频传输描述: 实时视频点播、历史视频回放与下载的 TCP媒体传输应支持基于RTP封装的视音频PS流,封装格式参照IETFRFC4571。
158 0
|
网络协议 前端开发 开发工具
GB/T28181-2022相对2016版“基于TCP协议的视音频媒体传输要求“规范解读和技术实现
GB/T28181-2022和GB/T28181-2016规范,有这么一条“更改了附录 D 基于 TCP 协议的视音频媒体传输要求(见附录 D,2016 年版的附录 L)。”。
357 0
|
编解码 C++
国标GB28181协议客户端开发(四)实时视频数据传输
国标GB28181协议客户端开发(四)实时视频数据传输
408 0
|
编解码 边缘计算 算法
一文详述流媒体传输网络MediaUni
LiveVideoStackCon2023上海站,阿里云视频云专场系列演讲-1
504 0
|
存储 编解码 网络协议
音视频基础(网络传输): RTMP封包
RTMP 概念 与 HTTP(超文本传输协议)同样是一个基于 TCP 的 Real Time Messaging Protocol(实时消息传输协议)。由 Adobe Systems 公司为 Flash 播放器和服务器之间音频、视频和数据传输开发的一种开放协议 。在国内被广泛的应用于直播 领域。HTTP 默认端口为 80,RTMP 则为 1935。
246 0
音视频基础(网络传输): RTMP封包
|
网络协议
v4l2帧的tcp传输模板
v4l2帧的tcp传输模板
55 0