GB/T28181-2016基于RTP的视音频数据封装和技术实现

简介: 首先我们先回顾下相关技术规范,看看基于RTP的音视频数据PS封装。

首先我们先回顾下相关技术规范,看看基于RTP的音视频数据PS封装。

C.1 基于RTP的视音频数据PS封装

基于RTP的 PS封装首先按照ISO/IEC13818-1:2000将视音频流封装成PS包,再将PS包以负载的方式封装成 RTP包。


进行PS封装时,应将每个视频帧封装为一个PS包,且每个关键帧的PS包中应包含系统头(System Header)和 PSM(ProgramStream Map),系统头和 PSM放置于PS包头之后、第一个PES包之前。

c9887cec76a74c15bc005554b5e06283.png

其中 PESV为视频 PES包,PESA 为音频PES包;视频非关键帧的PS包结构中一般不包含系统头和 PSM。PS包中各部分的具体数据结构参见ISO/IEC 13818-1:2000中的相关描述。


图C.1典型的视频关键帧PS包结构系统头应包含对PS包中码流种类的描述,其中视频和音频的流ID(stream_id)取值如下:


  • a) 视频流ID:0xE0;
  • b) 音频流ID:0xC0。


针对本文档规定的几种视音频格式,PSM 中流类型(stream_type)的取值如下:

  • a) MPEG-4视频流:0x10;
  • b) H.264视频流:0x1B;
  • c) SVAC视频流:0x80;
  • d) G.711音频流:0x90;
  • e) G.722.1音频流:0x92;
  • f) G.723.1音频流:0x93;
  • g) G.729音频流:0x99;
  • h) SVAC音频流:0x9B。


PS包封装的其他具体技术规范详见ISO/IEC13818-1:2000。


PS包的 RTP封装格式参照IETFRFC2250,RTP的主要参数设置如下:


a) 负载类型(payloadtype):96;


b) 编码名称(encodingname):PS;


c) 时钟频率(clockrate):90kHz;


d) SDP描述中“m”字段的“media”项:video。


C.2 基于 RTP的视音频基本流封装


该方式直接将视音频数据以负载的方式封装成 RTP包。


C.2.1 MPEG-4视频流的 RTP封装


MPEG-4视频流的 RTP封装格式应符合IETFRFC3016协议中的相关规定。 MPEG-4视频流 RTP包的负载类型(PayloadType)标识号选定:从IETFRFC3551—2003表5 中的动态范围(96~127)中选择,建议定为97。


C.2.2 H.264视频流的 RTP封装


H.264的 RTP载荷格式应符合IETFRFC3984中的相关规定。


H.264视频流RTP包的负载类型(PayloadType)标识号选定:从IETFRFC3551—2003表5中的动态范围(96~127)中选择,建议定为98。


C.2.3 SVAC视频流的 RTP封装


SVAC视频流的 RTP载荷格式可参照IETFRFC3984中的相关规定。


SVAC视频流 RTP包的负载类型(Payload Type)标志号选定,从IETF RFC 3551-2003表5中的动态范围(96~127)中选择,建议定为99。


C.2.4 音频流的 RTP封装


语音比特流宜采用标准的 RTP协议进行打包。


在一个RTP包中,音频载荷数据应为整数个音频编码帧,且时间长度在20ms~180ms之间。


音频载荷数据的 RTP封装参数如下:


a) G.711的主要参数


G.711A律语音编码 RTP包的负载类型(PayloadType)的参数规定如下(见IETFRFC3551— 2003中的表4):


  • 负载类型(PT):8;
  • 编码名称(encodingname):PCMA;
  • 时钟频率(clockrate):8kHz;
  • 通道数:1;
  • SDP描述中“m”字段的“media”项:audio。


b) SVAC音频的主要参数


SVAC语音编码 RTP包的负载类型(PayloadType)的参数规定如下:


  • 负载类型(PT):20;
  • 编码名称(encodingname):SVACA;
  • 时钟频率(clockrate):8kHz;
  • 通道数:1;
  • SDP描述中“m”字段的“media”项:audio。


c) G.723.1的主要参数


G.723.1语音编码 RTP包的负载类型(PayloadType)的参数规定参照IETFRFC3551—2003 表4中的 G.723,具体如下:


  • 负载类型(PT):4;
  • 编码名称(encodingname):G723;
  • 时钟频率(clockrate):8kHz;
  • 通道数:1;
  • SDP描述中“m”字段的“media”项:audio。


d) G.729的主要参数


G.729语音编码 RTP 包的负载类型(PayloadType)的参数规定如下(见IETFRFC3551— 2003中的表4):


  • 负载类型(PT):18;
  • 编码名称(encodingname):G729;
  • 时钟频率(clockrate):8kHz;
  • 通道数:1;
  • SDP描述中“m”字段的“media”项:audio。


e) G.722.1的主要参数


G.722.1语音编码 RTP包的负载类型(PayloadType)的参数规定参照IETFRFC3551—2003 表4中 G.722,具体如下:


  • 负载类型(PT):9;
  • 编码名称(encodingname):G722;
  • 时钟频率(clockrate):8kHz;
  • 通道数:1;
  • SDP描述中“m”字段的“media”项:audio。

技术实现

本文以Android平台为例,介绍下Android平台GB28181接入模块设计。实现不具备国标音视频能力的Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如智能监控、智慧零售、智慧教育、远程办公、生产运输、智慧交通、车载或执法记录仪等场景。Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、语音广播和语音对讲。


功能设计如下:


  • [视频格式]H.264/H.265(Android H.265硬编码);
  • [音频格式]G.711 A律、AAC;
  • [音量调节]Android平台采集端支持实时音量调节;
  • [H.264硬编码]支持H.264特定机型硬编码;
  • [H.265硬编码]支持H.265特定机型硬编码;
  • [软硬编码参数配置]支持gop间隔、帧率、bit-rate设置;
  • [软编码参数配置]支持软编码profile、软编码速度、可变码率设置;
  • 支持纯视频、音视频PS打包传输;
  • 支持RTP OVER UDP和RTP OVER TCP被动模式;
  • 支持信令通道网络传输协议TCP/UDP设置;
  • 支持注册、注销,支持注册刷新及注册有效期设置;
  • 支持设备目录查询应答;
  • 支持心跳机制,支持心跳间隔、心跳检测次数设置;
  • 支持移动设备位置(MobilePosition)订阅和通知;
  • 支持国标GB/T28181—2016平台接入;
  • 支持语音广播及语音对讲;
  • [实时水印]支持动态文字水印、png水印;
  • [镜像]Android平台支持前置摄像头实时镜像功能;
  • [实时静音]支持实时静音/取消静音;
  • [实时快照]支持实时快照;
  • [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  • [外部编码前视频数据对接]支持YUV数据对接;
  • [外部编码前音频数据对接]支持PCM对接;
  • [外部编码后视频数据对接]支持外部H.264数据对接;
  • [外部编码后音频数据对接]外部AAC数据对接;
  • 支持录像录像相关功能。


Android平台GB28181设备接入端,在收到平台端的invite请求和ack确认后,完成基础的信令交互,进入媒体数据发送阶段:


收到Invite后,开始创建RTP Sender,并完成相关的参数设定:

/*
 * CameraPublishActivity.java
 * Github: https://github.com/daniulive/SmarterStreaming
 */
@Override
public void ntsOnInvitePlay(String deviceId, PlaySessionDescription session_des) {
  handler_.postDelayed(new Runnable() {
    @Override
    public void run() {
      MediaSessionDescription video_des = session_des_.getVideoDescription();
      SDPRtpMapAttribute ps_rtpmap_attr = video_des.getPSRtpMapAttribute();
      Log.i(TAG,"ntsInviteReceived, 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());
      // 可以先给信令服务器发送临时振铃响应
      //sip_stack_android.respondPlayInvite(180, device_id_);
      long rtp_sender_handle = libPublisher.CreateRTPSender(0);
      if ( rtp_sender_handle == 0 ) {
        gb28181_agent_.respondPlayInvite(488, device_id_);
        Log.i(TAG, "ntsInviteReceived 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_);
      gb28181_agent_.respondPlayInviteOK(device_id_,local_ip_addr, local_port);
      gb28181_rtp_sender_handle_ = rtp_sender_handle;
    }
    private String device_id_;
    private PlaySessionDescription session_des_;
    public Runnable set(String device_id, PlaySessionDescription session_des) {
      this.device_id_ = device_id;
      this.session_des_ = session_des;
      return this;
    }
  }.set(deviceId, session_des),0);
}

Ack后,开始发送数据:

@Override
public void ntsOnAckPlay(String deviceId) {
  handler_.postDelayed(new Runnable() {
    @Override
    public void run() {
      Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);
      if (!isRecording && !isRTSPPublisherRunning && !isPushingRtmp) {
        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 (!isRecording && !isRTSPPublisherRunning && !isPushingRtmp ) {
          if (publisherHandle != 0) {
            libPublisher.SmartPublisherClose(publisherHandle);
            publisherHandle = 0;
          }
        }
        destoryRTPSender();
        Log.e(TAG, "Failed to start GB28181 service..");
        return;
      }
      if (!isRecording && !isRTSPPublisherRunning && !isPushingRtmp) {
        if (pushType == 0 || pushType == 1) {
          CheckInitAudioRecorder();    //enable pure video publisher..
        }
      }
      startLayerPostThread();
      isGB28181StreamRunning = true;
    }
    private String device_id_;
    public Runnable set(String device_id) {
      this.device_id_ = device_id;
      return this;
    }
  }.set(deviceId),0);
}

通过信令和媒体数据交互分离,设备注册后,心跳机制保持在线状态,无需音视频数据编码,平台端如果需要查看实时媒体数据,发起invite请求,采集音视频数据,编码并实现RTP的视音频数据PS封装后实时传输,达到随看随传的目的。

相关文章
|
22天前
|
编解码 网络协议 程序员
【RTP 传输协议】实时视频传输的艺术:深入探索 RTP 协议及其在 C++ 中的实现
【RTP 传输协议】实时视频传输的艺术:深入探索 RTP 协议及其在 C++ 中的实现
232 0
|
22天前
|
编解码 Linux C语言
实现一个传输h.264的rtsp服务器
实现一个传输h.264的rtsp服务器
57 0
|
9月前
|
网络协议 前端开发 开发工具
GB28181基于TCP协议的视音频媒体传输探究及实现
我们先看看官方规范针对TCP协议的视音频传输描述: 实时视频点播、历史视频回放与下载的 TCP媒体传输应支持基于RTP封装的视音频PS流,封装格式参照IETFRFC4571。
113 0
|
9月前
|
网络协议 前端开发 开发工具
GB/T28181-2022相对2016版“基于TCP协议的视音频媒体传输要求“规范解读和技术实现
GB/T28181-2022和GB/T28181-2016规范,有这么一条“更改了附录 D 基于 TCP 协议的视音频媒体传输要求(见附录 D,2016 年版的附录 L)。”。
229 0
|
9月前
|
网络协议 前端开发 开发工具
GB28181-2022相对2016版“基于TCP协议的视音频媒体传输要求“调整
GB28181-2022针对“基于TCP协议的视音频媒体传输”实时点播、历史视频回放与下载中,TCP媒体传输重连机制,做了说明。
|
11月前
|
编解码 C++
国标GB28181协议客户端开发(四)实时视频数据传输
国标GB28181协议客户端开发(四)实时视频数据传输
336 0
|
11月前
|
编解码 Linux 开发工具
C++实现RTMP协议发送H.264编码及AAC编码的音视频,摄像头直播
C++实现RTMP协议发送H.264编码及AAC编码的音视频,摄像头直播
193 0
|
编解码 网络性能优化 网络协议
|
数据采集 传感器 编解码
【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 关键帧数据格式 | 非关键帧数据格式 | x264 编码后的数据处理 | 封装 H.264 视频数据帧 )
【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 关键帧数据格式 | 非关键帧数据格式 | x264 编码后的数据处理 | 封装 H.264 视频数据帧 )
225 0

热门文章

最新文章