学习笔记(新手):从PS封装格式的视频文件中提取H.264及Nalu(方法+资料+代码)

本文是新手学习笔记,介绍如何从PS封装格式的视频文件中提取H.264和Nalu。通过理解封装、PS格式的概念,以及详细步骤和代码实现,适合初学者掌握音视频处理的基本方法。
摘要由CSDN通过智能技术生成

学习笔记(新手):从PS封装格式的视频文件中提取H.264及Nalu(方法+资料+代码)

目的

从一个.ps文件中提取出H.264裸码,博主此前从未了解过音视频的相关知识,以下内容适合新手理解这些概念和过程,并可以进行相关实验。
初学,以下内容根据我的理解来表述,希望大家能够指出我的错误!

相关概念

1、封装

所谓封装就是把一段视频流进行一个包装,相当于给它套上塑料袋、装进盒子里,一个盒子装不下的就分开装。因此对于计算机里的一段视频,它本身是一段二进制的码流,经过封装之后,分成了若干部分,在每一部分前就会有该部分的相关信息。比如说,接下来一段是视频还是音频,长度多少,是否有加密信息,其他信息等等。
那么解封装也就是一个“撕开包装”的过程。因此就需要了解你所要解封装的这个格式,是如何进行“包装”的,每一层都有哪些信息。

2、PS格式

H.264分为I帧(关键帧)、P帧和B帧,这三类具体是如何的跟算法相关,此篇不详述,简单来说就是I帧包含这一帧的全部信息,P帧和B帧就包含了与I帧的差异(以此达到压缩的目的)。
先看下I帧 和B、P帧的包格式。

在这里插入图片描述
在这里插入图片描述
最外一层是PSH头,相当于一个产品最外面的纸箱,里面是PSM头,相当于纸箱里小盒子(只有I帧有),然后就是小盒子里的塑料袋包装,也就PES头,然后就是实际的数据。在一个PSH里面可能有1个或n个PES。

PSH

先看PSH头的格式,具体每一位是代表什么,我也理解的不深刻,所以就讲一些能够帮助我们找到H.264码流的信息。(具体内容看文末链接)0x000001BA是PSH头的标志,在数据中出现了这个就表示找到了一个PSH的头,头的长度最短为14个字节,在第14个字节的末尾3位,代表了后面的附加信息的长度,以此确定PSH头的长度。
对于I帧,可能还有拓展头(系统头)0x000001BB ,BB后两个字节的大小代表了拓展头接下来的长度,比如“00 00 01 BB 00 0C”,就表示接下来还有12个字节。
到此为止,确定了PSH头的全部内容

PSM

0x000001BC是PSM头的标志,跟在000001BC后的两位是说明了Program Stream map,他也是pes包的一种,包的长度program_stream_map_length,比如是00 5A,说明跟在其后的数据长度为90,跳过这其后的90byte数据是以000001E0开始的包。

PES

what is 000001E0?找到了这个差不多就快成功了,因为这是代表了PES头,E0表示视频流,C0表示音频流,BD表示私有数据流。
e.g.00 00 01 E0 00 1A 8C 80 0A 21 1C C9 AE 0D FF FF FF FF FC
00 1A: 2字节表示长度,表示再这两个字节之后的数据长度(如果有附加数据包括了其后的附加数据,和负载数据,我们希望得到的是负载数据,因此要略过附加数据部分)
8C 80 这两个字节跟长度无关,跳过。
此后的一个字节就是附加信息的长度,0A就是10个字节,也就是21 1C C9 AE 0D FF FF FF FF FC ,此后就是帧数的数据。
最后的五个字节的FF FF FF FF FC是海康自己的一个自减计数值 ,具体几个字节也是根据“0A”这一位的值来定的,不需要过度纠结。

至此,把之后的数据单独取出来直到下一个“00 00 01 E0”/“00 00 01 C0”/“00 00 01 BA”(一个PES后跟的可能是PES 也可能是PS)

Nalu

每个视频帧分为若干NAL单元(NALU)。视频PS格式码流以NALU为单位进行打包。若当前为I帧或P帧的第一个NALU则需加PSH头部。若当前为I帧的第一个NALU还需要加PSM头部。每个NALU分为若干段,每段前需加PES头部,每段数据与PES头部组成PES包。

我们在提取出H.264裸码之后,再做一步,就是找出其中每一个Nalu的起始位置、长度和类别。
Nalu头的标志有两种 0x000001或者0x00000001,这只是标志,不是种类。
标志后的一位的值 0x67代表SPS,0x68代表PPS
我认为这篇博客已经写得非常详细和易懂了,除了0x67,0x68还有很多,了解Nalu的规则有利于我们之后学习RTP。
编写代码的时候,就只要标志头就可以了,但要小心两种标志都是可以的,不要出bug。
这里就不放这部分代码了,只放提取H.264的代码。
链接: https://blog.csdn.net/qq_29350001/article/details/78226286.

总结

对于码流的提取,最关键的就是搞清楚实际的码流数据从哪一位开始,到哪一位结束,因此就要了解清楚头信息里的关于其他信息的长度,才能给准确定位。在编写代码时,我的思路就是:
1、遍历每一字节,先找到PSH头,然后确定PSH头的长度
2、取紧接的4个字节,判断是系统头还是PSM头还是PES头
3、根据上面说的规则确定长度
4、提取数据,检测下面4个字节是哪一种头,直到遍历结束。

代码

代码没有经过优化和简化,作为参考,如果看完上面的内容知道了,那就不需要看代码也行。
当然,为了达到我们要的目的,可以直接遍历,然后找PES头的标志E0,C0,这样子代码三十行就可以搞定了。
(不建议大家直接看代码,先要理解,再看代码)

// An highlighted block
#include <stdio.h>
#pragma pack(1)
#include <WINSOCK2.H> 
#pragma comment(lib, "ws2_32.lib")
#define SEEK_CUR 1
#define PRINTF_DEBUG

#define SEEK_CUR2 2
#define MAX_PS_STARTCODE_LEN 4
#define MAX_PDTS_LEN 5
#define MAX_ES_NUMS 6
#define MAX_PDTS_STRING_LEN 12
#define MMIN_PS_HEADER_LEN 14
#define MAX_PES_PACKET_LEN 65496

#define SCODE_PS_END 0x000001B9
#define SCODE_PS_HEADER 0x000001BA
#define SCODE_PES_E0 0x000001E0
#define SCODE_PES_C0 0x000001C0
#define SCODE_PES_BD 0x000001BD
#define SCODE_PS_SYSTEM_HEADER 0x000001BB
#define SCODE_PS_SYSTEM_MAP_HEADER 0x000001BC
unsigned char *p = NULL;
int p
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值