使用RTMP拉视频流后I帧、P帧、B帧、sps、pps帧等如何判断,请用C++代码举例并表明注释
时间: 2024-02-01 21:01:54 浏览: 118
在使用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的形式存在于码流中。
阅读全文