视频播放压缩的相关知识点:I帧、P帧、B帧、RTMP协议、RTSP协议、GB28181协议等学习记录

引言

在写本文的时候又触及到了两个浩瀚的知识点,帧内预测和帧间预测。大家可以自行查找相关资料,一下是我学习过程中学习理解的记录,刚刚触及不一定正确。

1. I帧、P帧、B帧

大家都知道视频是由一帧一帧的图像构成,视频压缩实际上就是在减少每帧图像上面所携带的内容改变每帧图像的大小。在压缩后的视频图像通常会被分为三个大类I帧、P帧、B帧

1.1 P帧

P帧表示预测帧,P帧参考前面的帧进行运动估计。也就是说P帧记录的信息是与上一帧不同的内容。解码时须要用之前缓存的画面叠加上本帧定义的区别,生成终于画面。

1.2 B帧

B帧与P帧的区别就是B帧是双向区别帧,解码时须要用之前缓存的画面叠加上本帧定义的区别,生成完整画面。B帧比P帧能节约很多其它的空间,但可以以来压力就给到了解码器这边,他们在解码时不仅要缓存前面的画面而且需要预读预解码B帧后面的画面。

1.3 I帧

I帧表示关键帧,表示当前帧的画面数据被完整保留;解码时仅仅须要本帧数据就能够解析出一幅图像。
I帧可以被独立地编码、解码,这使得它在视频压缩中得到广泛应用。

  • 1.刷新视频的质量
I帧通常在一个编码小组的结尾,这样可以理解为在下一个解压中P帧或B帧可以读到完整数据。
  • 2.恢复比特流错误
  • 3.快进快退

注意:I帧、P帧、B帧都是是实实在在的物理帧,他们的位置顺序由不同的算法所决定,一但首个I帧位置确定,那么后面的排列都会呈现一定的规律性

2.ONVIF协议与GB28181协议,RTMP与RTSP协议

2.1 ONVIF协议与GB28181协议

首先可以理解为 ONVIF协议与GB28181都是为了主要解决:不同平台、不同设备之间的对接和互通问题。是详细的视频监控联网系统信息传输、交换、控制技术要求

2.2 视频传输协议RTMP、RTSP、HLS

RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。在互联网TCP/IP五层体系结构中应用层,RTMP协议是基于TCP协议的,也就是说RTMP实际上是使用TCP作为传输协议。
RTMP传输的数据的基本单元为Message,但是实际上传输的最小单元是Chunk(消息块),因为RTMP协议为了提升传输速度,在传输数据的时候,会把Message拆分开来,形成更小的块,这些块就是Chunk。

用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题,优势在于低延迟,稳定性高,支持所有摄像头格式,浏览器加载 flash插件就可以直接播放

RTSP (Real-Time Stream Protocol)

基于文本的多媒体播放控制协议。RTSP定义流格式,流数据经由RTP传输;RTSP实时效果非常好,适合视频聊天,视频监控等方向。

监控视频中主码流和子码流

3. 一些专有名词

3.1 DVS

网络视频服务器(DVS,digital video server),又叫数字视频编码器,是一种压缩、处理音视频数据的专业网络传输设备,由音视频压缩编解码器芯片、输入输出通道、网络接口、音视频接口、RS485串行接口控制、协议接口控制、系统软件管理等构成,主要是提供视频压缩或解压功能,完成图象数据的采集或复原等

3.2 NVR

NVR是硬盘录像机,用于接入本地局域网的摄像机,进行管理、预览、录像存储及录像回放,通过自带的HDMI/VGA接口输出视频画面。NVR是一个带有独立系统的设备,一般不能扩展解码上墙、外挂存储,所以适用于本地局域网IPC的集中管理和录像存储。NVR的产品规格丰富,从4路~256路、单盘位到十六盘位等不同规格,使用简单。它是一套进行图像存储处理的计算机系统,具有对图像/语音进行长时间录像、录音、远程监视和控制的功能。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
1、支持国标GB28181平台、国标GB28181 IPC和国标GB28181 NVR设备同时接入 (支持GB28181-2011版本和GB28181-2016版本)     2、支持国标GB28181设备注册和注销,对所有设备进行管理,获取资源 对资源列表进行管理     3、支持国标GB28181的目录订阅,对接收的订阅通知进行处理     4、支持国标GB28181实时视频请求(支持UDP、TCP主动(tcpactive)、TCP被动(tcppassive))     5、支持国标GB28181 PTZ控制     6、支持国标GB28181 录像查询     7、支持国标GB28181 历史视频点播 (支持UDP、TCP主动(tcpactive)、TCP被动(tcppassive))     8、支持国标GB28181 历史视频下载 (支持UDP、TCP主动(tcpactive)、TCP被动(tcppassive))     9、支持对接收的国标实时视频码流和历史视频码流进行管理     10、支持将国标的PS码流转换成ES码流     11、支持丢包打印和断流打印     13、支持RTSP服务和RTSP会话管理     14、支持RTSP客户端 UDP传输和TCP传输     15、支持国标GB28181设备5000路左右的接入管理,支持国标请求视频在100路左右     16、支持国标28181设备和通道写入mysql数据库      17、支持设备的云台PTZ控制,控制类型:上"up",下"down",左"left",右"right",左上"leftup",左下"leftdown",右上"rightup",右下"rightdown",镜头近"zoomin",镜头远"zoomout", 焦距远"focusfar",焦距近"focusnear", 设置预置位"setpos",调预置位"callpos"     18、支持历史视频的查询和历史视频的点播控制     19、支持对国标设备的控制,"record":录像开启和停止-通道id "guard":布放和撤防-报警通道id "reboot":设备重启-设备id "keyfame":强制关键帧-通道id     20、支持对实时视频的图片截图,通过http直接访问图片    21、支持rtmp和hls会话一直保留    22、支持报警消息(设备上线、下线和设备端报警)通过httpclient方式主动通知    23、支持公网和局域网同时存在
在使用RTMP视频流后,可以通过解析视频流的NALU(Network Abstraction Layer Unit)来判断视频帧类型,以及获取SPS、PPS等信息。下面是一个简单的C++代码示例: ```cpp #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> using namespace std; // NALU类型定义 #define NALU_TYPE_SLICE 1 // 非IDR图像的片 #define NALU_TYPE_DPA 2 // 非IDR图像的片 #define NALU_TYPE_DPB 3 // 非IDR图像的片 #define NALU_TYPE_DPC 4 // 非IDR图像的片 #define NALU_TYPE_IDR 5 // IDR图像中的片 #define NALU_TYPE_SEI 6 // 补充增强信息单元 #define NALU_TYPE_SPS 7 // 序列参数集 #define NALU_TYPE_PPS 8 // 图像参数集 #define NALU_TYPE_AUD 9 // 分界符 #define NALU_TYPE_EOSEQ 10 // 序列结束 #define NALU_TYPE_EOSTREAM 11 // 码流结束 #define NALU_TYPE_FILL 12 // 填充 // NALU结构体定义 typedef struct NALU_t { int startcodeprefix_len; // 码流中开始码的长度,一般为4 unsigned len; // 去掉起始码后,NALU数据部分的长度 unsigned max_size; // nalu的最大长度 int forbidden_bit; // 禁止位,始终为0 int nal_reference_idc; // NALU重要性,0~3依次降低 int nal_unit_type; // NALU类型 char* buf; // NALU数据缓冲区地址 } NALU_t; int FindStartCode2(unsigned char *Buf) { if(Buf[0]!=0 || Buf[1]!=0 || Buf[2]!=1) return 0; // 检查起始码前三个字节是否为0x000001 else return 1; } int FindStartCode3(unsigned char *Buf) { if(Buf[0]!=0 || Buf[1]!=0 || Buf[2]!=0 || Buf[3]!=1) return 0; // 检查起始码前四个字节是否为0x00000001 else return 1; } int GetAnnexbNALU(NALU_t *nalu) { int pos = 0; int StartCodeFound, rewind; unsigned char *Buf; if ((Buf = (unsigned char*)calloc(nalu->max_size , sizeof(char))) == NULL) return 0; nalu->startcodeprefix_len = 3; if (3 != fread(Buf, 1, 3, h264stream)) { free(Buf); return 0; } if (FindStartCode2(Buf)) { // 找到3字节起始码 nalu->startcodeprefix_len = 3; pos = 3; } else { // 找到4字节起始码 if (1 != fread(Buf+3, 1, 1, h264stream)) { free(Buf); return 0; } if (FindStartCode3(Buf)) { // 找到4字节起始码 nalu->startcodeprefix_len = 4; pos = 4; } else { // 没有找到起始码 free(Buf); return -1; } } // 查找下一个起始码前的字节数 StartCodeFound = 0; rewind = 0; while (!StartCodeFound) { if (feof(h264stream)) { nalu->len = (pos-1) - nalu->startcodeprefix_len; memcpy(nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len); nalu->forbidden_bit = nalu->buf[0] & 0x80; // 禁止位 nalu->nal_reference_idc = (nalu->buf[0] & 0x60) >> 5; // 重要性 nalu->nal_unit_type = (nalu->buf[0]) & 0x1f; // 类型 free(Buf); return pos-1; } Buf[pos++] = fgetc(h264stream); if (Buf[pos-1] == 0x00) rewind++; else { if (Buf[pos-1] == 0x01 && rewind >= 2) StartCodeFound = 1; else rewind = 0; } } // 找到下一个起始码前的字节数 rewind = (rewind == 2) ? 2 : 3; if (0 != fseek(h264stream, -rewind, SEEK_CUR)) { free(Buf); printf("GetAnnexbNALU: Cannot fseek in the bit stream file"); return 0; } // 去掉起始码,获取NALU数据部分长度 nalu->len = (pos-rewind) - nalu->startcodeprefix_len; memcpy(nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len); nalu->forbidden_bit = nalu->buf[0] & 0x80; // 禁止位 nalu->nal_reference_idc = (nalu->buf[0] & 0x60) >> 5; // 重要性 nalu->nal_unit_type = (nalu->buf[0]) & 0x1f; // 类型 free(Buf); return (pos-rewind); } int main() { // 打开视频流 FILE *h264stream = fopen("test.h264", "rb"); if (!h264stream) { printf("Can't open input video file.\n"); return 0; } // 初始化NALU结构体 NALU_t *nalu = (NALU_t*)calloc(1, sizeof(NALU_t)); if (!nalu) { printf("Alloc NALU_t failed.\n"); return 0; } // 设置最大NALU长度 nalu->max_size = 1024*1024; nalu->buf = (char*)calloc(nalu->max_size, sizeof(char)); if (!nalu->buf) { printf("Alloc NALU_t buf failed.\n"); return 0; } // 读取NALU数据 while (!feof(h264stream)) { int pos = GetAnnexbNALU(nalu); if (pos > 0) { // 判断NALU类型 switch (nalu->nal_unit_type) { case NALU_TYPE_SLICE: case NALU_TYPE_DPA: case NALU_TYPE_DPB: case NALU_TYPE_DPC: case NALU_TYPE_IDR: // I帧或P帧 break; case NALU_TYPE_SPS: // SPS帧 break; case NALU_TYPE_PPS: // PPS帧 break; default: // 其他帧类型 break; } } } // 关闭视频流 fclose(h264stream); // 释放内存 free(nalu->buf); free(nalu); return 0; } ``` 该代码打开一个H.264码流文件,然后从中读取NALU数据,并判断NALU类型。其中,`GetAnnexbNALU`函数用于从码流中读取一个NALU,`FindStartCode2`和`FindStartCode3`函数用于查找起始码。在判断NALU类型时,根据NALU类型的不同,可以判断出I帧、P帧、SPS帧、PPS帧等。由于H.264码流中的NALU是以起始码(0x000001或0x00000001)为分割的,因此需要在读取NALU数据时,先查找起始码,并去掉起始码后的数据才是NALU的有效数据。同时,SPS、PPS等信息也是以NALU的形式存在于码流中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值