前言:

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;

设计思路:

  1. 通过ONVIF协议获取IPC网络摄像头RTSP的URL地址,为了简化测试程序,不在这里介绍,本文中使用固定地址:rtsp://192.168.0.120:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif
  2. 通过RTSP协议发起数据流,同时获取服务端的RTCP和RTP的网络端口号,数据类型等信息。
  3. 建立RTCP,RTP连接,RTCP是TCP连接,RTP在这里走的是UDP连接。
  4. 客户端接收RTP网络数据包
  5. 对RTP客户端接收到的网络数据包进行解包
  6. 将解包之后的数据写入文件,对于H264视频帧的第一包数据,需要添加上H264帧头标签00 00 00 01 6X信息。
  7. 为方便其他地方使用,可以将视频数据按帧添加进队列。

问题分析:

对于网络视频流,客户端接收到视频数据,有可能出现显示不出来或是花屏的现象,问题一般定位方法有:

  1. 查看H264 帧标签没有添加或是添加错误。
  2. 查看接收到的RTP网络数据包,看网络包序号是否连续,是否有丢包,统计丢包率有多高。
  3. 查看RTP网络数据包的时间戳,看时间戳是否正常增加。
  4. 查看RTP连接的系统网络缓存有多大,看是否有数据因为缓存满了而导致数据丢失。
  5. 查看RTP数据流中是否同时传输了多个流,比如同时传输音频流和视频流。

代码实现:

完整代码结构如下:

​在 liwen01 公众号中回复 网络编程 获取工程代码,本章代码工程名为:RtspRtcpRtpLoad_h264.tar.gz

---------------------------End---------------------------
长按识别二维码
关注 liwen01 公众号

ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载H264视频流的更多相关文章

  1. Android--------WebView+H5开发仿美团 预加载,加载失败和重新加载

    Android嵌入式开发已经占大多数了,很多界面都是以网页的形式展示,WebView可以使得网页轻松的内嵌到app里,还可以直接跟js相互调用. 本博客主要是模仿美团的旅游出行模块的预加载,网页加载失 ...

  2. iOS开发UI篇—懒加载

    iOS开发UI篇—懒加载 1.懒加载基本 懒加载——也称为延迟加载,即在需要的时候才加载(效率低,占用内存小).所谓懒加载,写的是其get方法. 注意:如果是懒加载的话则一定要注意先判断是否已经有了, ...

  3. seajs实现JavaScript 的 模块开发及按模块加载

    seajs实现了JavaScript 的 模块开发及按模块加载.用来解决繁琐的js命名冲突,文件依赖等问题,其主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载. 官方文档:http:/ ...

  4. Android开发中如何解决加载大图片时内存溢出的问题

    Android开发中如何解决加载大图片时内存溢出的问题    在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...

  5. Linux内核启动代码分析二之开发板相关驱动程序加载分析

    Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c  start_ke ...

  6. Google官方网络框架-Volley的使用解析Json以及加载网络图片方法

    Google官方网络框架-Volley的使用解析Json以及加载网络图片方法 Volley是什么? Google I/O 大会上,Google 推出 Volley的一个网络框架 Volley适合什么场 ...

  7. 【Java Web开发学习】Spring加载外部properties配置文件

    [Java Web开发学习]Spring加载外部properties配置文件 转载:https://www.cnblogs.com/yangchongxing/p/9136505.html 1.声明属 ...

  8. 基于JRebel开发的MybatisPlus热加载插件

    前言 前天项目中使用了mybatis-plus,但是搭配Jrebel开发项目时,发现修改mapper的xml,或者mapper方法中的注解,Jrebel并没有能够reload mapper.于是就有了 ...

  9. .NET混合开发解决方案11 WebView2加载的网页中JS调用C#方法

    系列目录     [已更新最新开发文章,点击查看详细] WebView2控件应用详解系列博客 .NET桌面程序集成Web网页开发的十种解决方案 .NET混合开发解决方案1 WebView2简介 .NE ...

  10. Android 框架修炼-自己开发高效异步图片加载框架

    一.概述 目前为止,第三方的图片加载框架挺多的,比如UIL , Volley Imageloader等等.但是最好能知道实现原理,所以下面就来看看设计并开发一个加载网络.本地的图片框架. 总所周知,图 ...

随机推荐

  1. Using PostMessage/SendMessage to send keys to c# IE WebBrowser

    [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool PostM ...

  2. cs上线Linux

    cs上线Linux 下载和配置crossC2 首先到项目地址:https://github.com/gloxec/CrossC2/releases下载两个文件 后缀为.cna的为cs的插件文件,启动c ...

  3. 文心一言 VS 讯飞星火 VS chatgpt (172)-- 算法导论13.3 1题

    一.用go语言,在 RB-INSERT 的第 16 行,将新插人的结点 z 着为红色.注意到,如果将 z 着为黑色,则红黑树的性质4就不会被破坏.那么为什么不选择将 z 着为黑色呢? 文心一言: 在红 ...

  4. 设计模式Java实战,彻底学会

    ​ ​这是全网最强的Java设计模式实战教程.此教程用实际项目场景,结合SpringBoot,让你真正掌握设计模式. 网址是:Java设计模式实战专栏介绍 - 自学精灵(也可以百度搜索"自学 ...

  5. MySQL 基础(二)日志

    在操作系统和数据库管理系统中,为了提高数据的容灾性,一般都会通过写入相关日志的方式来记录数据的修改,使得系统受到灾难时能够从之前的数据中恢复过来.MySQL 也提供了日志的机制来提高数据的容灾性,主要 ...

  6. UE5: 探究Actor Tick的注册与执行

    1. 前情提要 因工作需要,有在编辑器模式下执行Actor的Tick函数的需求.经过查阅资料,了解到重载Actor::ShouldTickIfViewportOnly函数可以实现在编辑器视口下也可以执 ...

  7. Typora+Docsify快速入门

    Typora是什么? ​ Typora中文版是一款好用极简的跨平台Markdown编辑器,软件使用这款软件能够帮助用户轻松将文本转换到HTML,软件从底层向上设计,软件支持markdown的标准语法, ...

  8. 高效联调,可靠发布!华为云推出CodeArts Release发布管理服务

    摘要:华为云全新推出CodeArts Release发布管理服务,旨在将华为多年形成的发布实践外溢,帮助企业提升软件发布质量和效率,降低生产环境的发布风险. 本文分享自华为云社区<高效联调,可靠 ...

  9. 从缓存的本质说起,说服技术大佬用Redis

    摘要:在技术领域中,没有银弹.我们需要不断探索和研究新的技术,结合具体问题和需求,选择最适合的解决方案. 本文分享自华为云社区<知乎问题:如何说服技术老大用 Redis ?>,作者:勇哥j ...

  10. Tarjan:这个算法大神

    摘要:图的算法是进行静态分析的基础数据算法,如何提高图的分析效率,就需要对图的算法有进一步的认识. 1. 引言 在静态分析技术中, 我们常用会将代码转成抽象语法树(AST), 然后采用深度遍历(DFS ...