ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载H264视频流
前言:
RTSP,RTCP,RTP一般是一起使用,在FFmpeg和live555这些库中,它们为了更好的适用性,所以实现起来非常复杂,直接查看FFmpeg和Live555源代码来熟悉这些协议非常吃力,这里将它们独立出来实现,以便更好的理解协议。本文主要介绍RTSP,RTCP,RTP加载H264数据流。
说明:
(1)大华IPC摄像头作为服务端
(2)在ubuntu16.04中编译实现测试程序
(3)服务端IP: 192.168.0.120
(4)客户端IP: 192.168.0.128
协议介绍:
用一句简单的话总结:RTSP发起/终结流媒体、RTP传输流媒体数据 、RTCP对RTP进行控制,同步。RTSP属于四层网络当中的应用层,RTP,RTCP属于传输层,如下图所示意。

RTSP在博客《ONVIF网络摄像头(IPC)客户端开发—最简RTSP客户端实现》中已经介绍,这里不再重复,这里主要介绍RTCP和RTP。
RTCP协议:
RTCP控制协议需要与RTP数据协议一起配合使用,当应用程序启动一个RTP会话时将同时占用两个端口,分别供RTP和RTCP使用,其中RTCP端口一定要是基数,RTP端口一定要是偶数,且是两个相邻的端口。RTP本身并不能为按序传输数据包提供可靠的保证,也不提供流量控制和拥塞控制,这些都由RTCP来负责完成。通常RTCP会采用与RTP相同的分发机制,向会话中的所有成员周期性地发送控制信息,应用程序通过接收这些数据,从中获取会话参与者的相关资料,以及网络状况、分组丢失概率等反馈信息,从而能够对服务质量进行控制或者对网络状况进行诊断。
RTCP协议的功能是通过不同的RTCP数据报来实现的,主要有如下几种类型:
- SR:发送端报告,所谓发送端是指发出RTP数据报的应用程序或者终端,发送端同时也可以是接收端。
 - RR:接收端报告,所谓接收端是指仅接收但不发送RTP数据报的应用程序或者终端。
 - SDES:源描述,主要功能是作为会话成员有关标识信息的载体,如用户名、邮件地址、电话号码等,此外还具有向会话成员传达会话控制信息的功能。
 - BYE:通知离开,主要功能是指示某一个或者几个源不再有效,即通知会话中的其他成员自己将退出会话。
 - APP:由应用程序自己定义,解决了RTCP的扩展性问题,并且为协议的实现者提供了很大的灵活性。
 
如果只是测试加载H264数据流,这里建立RTCP连接后不发送RTCP数据包也是可以的,那就是客户端和服务端都采用默认的发送和接收方式收发数据。下面的测试程序实现了RTCP协议,但是没有发送RTCP数据包。
RTP协议:
在加载H264数据流的时候,比较麻烦的处理环节就是RTP网络数据包的解析,这个需要完全按照协议来解析,否则会出现花屏现象或是显示不出来的情况。RTP网络数据包结构图如下:

这里对上图中的名词做个解析:
H264图像结构:
- NAL Network Abstraction Layer
 - NALU Network Abstraction Layer Unit
 - VCL Video Coding Layer
 - VCLU Video Coding Layer Unit
 
H264的图像数据是在NAL里面,并且上图中00 00 00 01 6X 这些数据是解包的时候再添加上去的,在RTP网络包中并不会传输这些信息,但是正常的h264图像数据中是需要这些标签来识别不同的图片帧的。
RTP分包模式:
- SNP Single NALU Packet
 - AP Aggregation Packet
 - FU Fragmentation Units
 
上面说过RTP网络传输包一包大小为1500字节,那么对于大于1500字节的视频帧,比如I帧,那就需要分开很多包来传输,所采用的就是FU分片模式。如果数据包小于1500字节,比如SPS,PPS,SEI数据包,直接一个RTP包就可以发送完,那使用的就是SNP单包模式。另外组合包AP表示一个包里有多种视频帧类型。在这里我们用到的是单包SNP和分片包FU两种模式。
RTP Pack:
RTP协议从网络中接收或是发送的一个数据包,一般最大1500字节。实际解析的时候也是以这样的一个数据包为单位来解析。
RTP Header:
RTP网络数据包的包头,长度为12字节具体格式如下:
/***************************************************************
0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           synchronization source (SSRC) identifier            |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|            contributing source (CSRC) identifiers             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*****************************************************************/ 
/**RTP 头结构**/
typedef struct
{
    /** byte 0 **/
    unsigned char bit4CsrcLen:4;        /** expect 0 **/
    unsigned char bit1Extension:1;      /** expect 1, see RTP_OP below **/
    unsigned char bit1Padding:1;        /** expect 0 **/
    unsigned char bit1Version:2;        /** expect 2 **/
    /** byte 1 **/
    unsigned char bit7PayLoadType:7;    /** RTP_PAYLOAD_RTSP **/
    unsigned char bit1Marker:1;         /** expect 1 **/
    /** bytes 2,3 **/
    unsigned int u32SeqNum;             /** RTP sequence number **/
    /** bytes 4-7 **/
    unsigned int u32TimeStamp;          /** RTP sequence number **/
    /** bytes 8-11 **/
    unsigned int u32Ssrc;               /**stream number is used here **/
}RTP_HEADER_S;
RTP payload:
RTP加载的实际数据,可以是视频,也可以是音频,也可以是其他类型,这里加载的是h264视频数据,对应下面的96。数据类型如下:
/***********************************************************
PT      encoding    media type  clock rate
		name                    (Hz)
_____________________________________________
24      unassigned  V
25      CelB        V           90,000
26      JPEG        V           90,000
27      unassigned  V
28      nv          V           90,000
29      unassigned  V
30      unassigned  V
31      H261        V           90,000
32      MPV         V           90,000
33      MP2T        AV          90,000
34      H263        V           90,000
35-71   unassigned  ?
72-76   reserved    N/A         N/A
77-95   unassigned  ?
96-127  dynamic     ?
dyn     H263-1998   V           90,000
Table 5: Payload types (PT) for video and combined
		encodings
***********************************************************/
NALU Header:
网络抽象层单元头结构体
/*****************
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |
+---------------+
*****************/
typedef struct
{
    unsigned char bit5TYPE:5;
    unsigned char bit2NRI:2;
    unsigned char bit1F:1;
}RTP_NALU_HEADER_S;
FU Indicator:
分片包指示符
/****************
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |
+---------------+
*****************/
typedef struct
{
	unsigned char Bit5TYPE:5;
	unsigned char BitNRI:2;
	unsigned char BitF:1;
}RTP_FU_INDICATOR_S;
FU Header:
分片包头结构
/******************
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R|  Type   |
+---------------+
*******************/
typedef struct
{
	unsigned char Bit5TYPE:5;
	unsigned char Bit1R:1;
	unsigned char Bit1E:1;
	unsigned char Bit1S:1;
}RTP_FU_HEADER_S;
设计思路:
- 通过ONVIF协议获取IPC网络摄像头RTSP的URL地址,为了简化测试程序,不在这里介绍,本文中使用固定地址:rtsp://192.168.0.120:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif
 - 通过RTSP协议发起数据流,同时获取服务端的RTCP和RTP的网络端口号,数据类型等信息。
 - 建立RTCP,RTP连接,RTCP是TCP连接,RTP在这里走的是UDP连接。
 - 客户端接收RTP网络数据包
 - 对RTP客户端接收到的网络数据包进行解包
 - 将解包之后的数据写入文件,对于H264视频帧的第一包数据,需要添加上H264帧头标签00 00 00 01 6X信息。
 - 为方便其他地方使用,可以将视频数据按帧添加进队列。
 
问题分析:
对于网络视频流,客户端接收到视频数据,有可能出现显示不出来或是花屏的现象,问题一般定位方法有:
- 查看H264 帧标签没有添加或是添加错误。
 - 查看接收到的RTP网络数据包,看网络包序号是否连续,是否有丢包,统计丢包率有多高。
 - 查看RTP网络数据包的时间戳,看时间戳是否正常增加。
 - 查看RTP连接的系统网络缓存有多大,看是否有数据因为缓存满了而导致数据丢失。
 - 查看RTP数据流中是否同时传输了多个流,比如同时传输音频流和视频流。
 
代码实现:
完整代码结构如下:

在 liwen01 公众号中回复 网络编程 获取工程代码,本章代码工程名为:RtspRtcpRtpLoad_h264.tar.gz
---------------------------End---------------------------
长按识别二维码
关注 liwen01 公众号
ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载H264视频流的更多相关文章
- Android--------WebView+H5开发仿美团 预加载,加载失败和重新加载
		
Android嵌入式开发已经占大多数了,很多界面都是以网页的形式展示,WebView可以使得网页轻松的内嵌到app里,还可以直接跟js相互调用. 本博客主要是模仿美团的旅游出行模块的预加载,网页加载失 ...
 - iOS开发UI篇—懒加载
		
iOS开发UI篇—懒加载 1.懒加载基本 懒加载——也称为延迟加载,即在需要的时候才加载(效率低,占用内存小).所谓懒加载,写的是其get方法. 注意:如果是懒加载的话则一定要注意先判断是否已经有了, ...
 - seajs实现JavaScript 的 模块开发及按模块加载
		
seajs实现了JavaScript 的 模块开发及按模块加载.用来解决繁琐的js命名冲突,文件依赖等问题,其主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载. 官方文档:http:/ ...
 - Android开发中如何解决加载大图片时内存溢出的问题
		
Android开发中如何解决加载大图片时内存溢出的问题 在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...
 - Linux内核启动代码分析二之开发板相关驱动程序加载分析
		
Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c start_ke ...
 - Google官方网络框架-Volley的使用解析Json以及加载网络图片方法
		
Google官方网络框架-Volley的使用解析Json以及加载网络图片方法 Volley是什么? Google I/O 大会上,Google 推出 Volley的一个网络框架 Volley适合什么场 ...
 - 【Java Web开发学习】Spring加载外部properties配置文件
		
[Java Web开发学习]Spring加载外部properties配置文件 转载:https://www.cnblogs.com/yangchongxing/p/9136505.html 1.声明属 ...
 - 基于JRebel开发的MybatisPlus热加载插件
		
前言 前天项目中使用了mybatis-plus,但是搭配Jrebel开发项目时,发现修改mapper的xml,或者mapper方法中的注解,Jrebel并没有能够reload mapper.于是就有了 ...
 - .NET混合开发解决方案11 WebView2加载的网页中JS调用C#方法
		
系列目录 [已更新最新开发文章,点击查看详细] WebView2控件应用详解系列博客 .NET桌面程序集成Web网页开发的十种解决方案 .NET混合开发解决方案1 WebView2简介 .NE ...
 - Android 框架修炼-自己开发高效异步图片加载框架
		
一.概述 目前为止,第三方的图片加载框架挺多的,比如UIL , Volley Imageloader等等.但是最好能知道实现原理,所以下面就来看看设计并开发一个加载网络.本地的图片框架. 总所周知,图 ...
 
随机推荐
- 反转数组、打印数组元素、加强(增强)for循环
			
package com.guoba.array; public class Demo04 { public static void main(String[] args) { int[] arr = ...
 - redis + AOP + 自定义注解实现接口限流
			
限流介绍 限流(rate limiting)  是指在一定时间内,对某些资源的访问次数进行限制,以避免资源被滥用或过度消耗.限流可以防止服务器崩溃.保证用户体验.提高系统可用性. 限流的方法有很多种 ...
 - 华硕AX系列路由器选购,以及华硕WIFI6路由器智能设备家电无法互联的解决方法。
			
家里昨天换了一整套wifi6路由器(华硕AX82U+XD4R),刚刚换上就发现原来的欧普智能灯和部分其他设备无法使用了,而小米等设备等都可以互联,智能家居绝大部分用的是2.4G的协议,所以说,问题出现 ...
 - OpenWRT的TTYD终端显示已拒绝连接
			
更改openwrt软路由后台管理地址后,发现TTYD终端无法连接,显示已拒绝连接,无法使用的解决方法. 解决方法: 1.使用puty工具连接软路由 2.编辑ttyd配置文件 root@OpenWrt: ...
 - Java中单体应用锁的局限性&分布式锁
			
互联网系统架构的演进 在互联网系统发展之初,系统比较简单,消耗资源小,用户访问量也比较少,我们只部署一个Tomcat应用就可以满足需求.系统架构图如下: 一个Tomcat可以看作是一个JVM进程,当大 ...
 - Asp .Net Core 系列: 集成 Consul 实现 服务注册与健康检查
			
目录 什么是 Consul? 安装和运行 Consul Asp .Net Core 如何集成 Consul 实现服务注册和健康检查 Consul.AspNetCore 中的 AddConsul 和 A ...
 - 案例解析丨 Spark Hive 自定义函数应用
			
摘要:Spark目前支持UDF,UDTF,UDAF三种类型的自定义函数. 1. 简介 Spark目前支持UDF,UDTF,UDAF三种类型的自定义函数.UDF使用场景:输入一行,返回一个结果,一对一, ...
 - 华为云GaussDB数据库荣获国际CC EAL4+级别认证
			
摘要:近日,华为云GaussDB企业级分布式数据库内核正式通过了全球知名独立认证机构欧洲SGS Brightsight实验室的安全评估,获得全球权威信息技术安全性评估标准CC EAL4+级别认证. 本 ...
 - GaussDB(DWS) NOT IN优化技术解密:排他分析场景400倍性能提升
			
摘要:本文针对8.1.2版本中的NOT IN场景的Mixed-HashJoin新技术进行介绍.该技术在GaussDB(DWS)与招商银行的联创项目中落地,为招商银行的批量作业带来了总体15%的性能提升 ...
 - Ambari2.7.3.0添加组件
			
Ambari 2.7.3.0安装新组件和之前版本略有不同,本文将简述安装新组件的简单过程. 前提是大家已经安装好Ambari 2.7.3.0 这时候由于有一些组件没有添加,就需要安装新的组件. 首先我 ...