h264文件分析(纯c解析代码)
参考链接:1. 解析H264的SPS信息 https://blog.csdn.net/lizhijian21/article/details/80982403
2. h.264的POC计算 https://www.cnblogs.com/TaigaCon/p/3551001.html
3. 视音频数据处理入门:H.264视频码流解析 https://blog.csdn.net/leixiaohua1020/article/details/50534369
代码中的注释, 有对SPS,PPS,SLICE的分析,未进行代码分析(有些可能不准确)。
SPS语法
PPS语法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h> #define TAB44 " "
#define PRINTF_DEBUG #define PRTNTF_STR_LEN 10 /************************************************************************************************************
** nalu header: 负责将VCL产生的比特字符串适配到各种各样的网络和多元环境中,
覆盖了所有片级以上的语法级别(NALU的作用, 方便网络传输)
**
-------------------------------------------------------------------------------------------------------------
** 字段名称 | 长度(bits) | 有关描述
-------------------------------------------------------------------------------------------------------------
** forbidden_bit | 1 | 编码中默认值为0, 当网络识别此单元中存在比特错误时, 可将其设为1, 以便接收方丢掉该单元
** nal_reference_idc | 2 | 0~3标识这个NALU的重要级别
** nal_unit_type | 5 | NALU的类型(类型1~12是H.264定义的, 类型24~31是用于H.264以外的,
RTP负荷规范使用这其中的一些值来定义包聚合和分裂, 其他值为H.264保留) ** nal_unit_type:
0 未使用
1 未使用Data Partitioning, 非IDR图像的Slice
2 使用Data Partitioning且为Slice A
3 使用Data Partitioning且为Slice B
4 使用Data Partitioning且为Slice C
5 IDR图像的Slice(立即刷新)
6 补充增强信息(SEI)
7 序列参数集(sequence parameter set, SPS)
8 图像参数集(picture parameter set, PPS)
9 分界符
10 序列结束
11 码流结束
12 填充
13...23 保留
24...31 未使用 ** SPS, PPS. SLICE等信息就不解析了. 为了减少bits, 用了哥伦布编码(自己解析比较麻烦, 但是网上有很多). ** SPS信息说明:
1. 视频宽高, 帧率等信息;
2. seq_parameter_set_id, 指明本序列参数集的id号, 这个id号将被picture参数集引用;
3. pic_width_in_mbs_minus1, 加1指定以宏块(16*16)为单位的每个解码图像的宽度, 即width = (pic_width_in_mbs_minus1 + 1) * 16
4. pic_height_in_map_units_minus1;
5. pic_order_cnt_type, 视频的播放顺序序号叫做POC(picture order count), 取值0,1,2;
6. time_scale, fixed_frame_rate_flag, 计算帧率(fps).
视频帧率信息在SPS的VUI parameters syntax中, 需要根据time_scale, fixed_frame_rate_flag计算得到: fps = time_scale / num_units_in_tick.
但是需要判断参数timing_info_present_flag是否存在, 若不存在表示FPS在信息流中无法获取.
同时还存在另外一种情况: fixed_frame_rate_flag为1时, 两个连续图像的HDR输出时间频率为单位, 获取的fps是实际的2倍. ** PPS信息说明:
1. pic_parameter_set_id, 用以指定本参数集的序号, 该序号在各片的片头被引用;
2. seq_parameter_set_id, 指明本图像参数集所引用的序列参数集的序号;
3. 其他高深的暂时还不理解, 指明参考帧队列等. ** SLICE信息说明:
1. slice_type, 片的类型;
2. pic_parameter_set_id, 引用的图像索引;
3. frame_num, 每个参考帧都有一个连续的frame_num作为它们的标识, 它指明了各图像的解码顺序. 非参考帧也有,但没有意义;
4. least significant bits;
5. 综合三种poc(pic_order_cnt_type), 类型2应该是最省bit的, 因为直接从frame_num获得, 但是序列方式限制最大;
类型1, 只需要一定的bit量在sps标志出一些信息还在slice header中表示poc的变化, 但是比类型0要节省bit, 但是其序列并不是随意的, 要周期变化;
对于类型0因为要对poc的lsb(pic_order_cnt_lsb, last bit)进行编码所以用到的bit最多, 优点是序列可以随意.
** 自我理解, 不一定准确(这边算显示顺序, 要根据SPS中的pic_order_cnt_type, 为2, 意味着码流中没有B帧, frame_num即为显示顺序;
为1, 依赖frame_num求解POC; 为0, 把POC的低位编进码流内, 但这只是低位, 而POC的高位PicOrderCntMsb则要求解码器自行计数,
计数方式依赖于前一编码帧(PrevPicOrderCntMsb与PrevPicOrderCntLsb. ** 一般的码流分析所见(未仔细证实): pic_order_cnt_type=2, 只有frame_num(无B帧);
pic_order_cnt_type=1, 暂未分析到;
pic_order_cnt_type=0, pic_order_cnt_lsb指示显示顺序, 一般为偶数增长(0, 2, 4, 6, 据说是什么场方式和帧方式, 场时其实是0 0 2 2 4 4). ** 编码与显示的原因: 视频编码顺序与视频的播放顺序, 并不完全相同, 视频编码时, 如果采用了B帧编码, 由于B帧很多时候都是双向预测得来的,
这时会先编码B帧的后向预测图像(P帧), 然后再进行B帧编码, 因此会把视频原来的播放顺序打乱, 以新的编码顺序输出码流,
而在解码断接收到码流后, 需要把顺序还原成原本的播放顺序, 以输出正确的视频. 在编解码中, 视频的播放顺序序号叫做POC(picture order count). ** 总结: 1. 码流中有很多SPS(序列), 一个序列中有多个图像, 一个图像中有多个片, 一个片中有多个块;
2. SPS中有seq_parameter_set_id. PPS中有pic_parameter_set_id, 并通过seq_parameter_set_id指明关联的序列.
SLICE中有pic_parameter_set_id, 指明关联的图像;
3. SPS中可计算宽高以及帧率, pic_order_cnt_type(显示顺序的类型);
SLICE HEADER中可算出解码的顺序, 以及根据pic_order_cnt_type算出显示顺序.
************************************************************************************************************/
typedef enum e_h264_nalu_priority
{
NALU_PRIORITY_DISPOSABLE = ,
NALU_PRIORITY_LOW = ,
NALU_PRIORITY_HIGH = ,
NALU_PRIORITY_HIGHEST = ,
} E_H264_NALU_PRIORITY; typedef enum e_h264_nalu_type
{
NALU_TYPE_SLICE = ,
NALU_TYPE_DPA = ,
NALU_TYPE_DPB = ,
NALU_TYPE_DPC = ,
NALU_TYPE_IDR = ,
NALU_TYPE_SEI = ,
NALU_TYPE_SPS = ,
NALU_TYPE_PPS = ,
NALU_TYPE_AUD = ,
NALU_TYPE_EOSEQ = ,
NALU_TYPE_EOSTREAM = ,
NALU_TYPE_FILL = ,
} E_H264_NALU_TYPE; typedef struct t_h264_nalu_header
{
unsigned char forbidden_bit:, nal_reference_idc:, nal_unit_type:;
} T_H264_NALU_HEADER; typedef struct t_h264_nalu
{
int startCodeLen; T_H264_NALU_HEADER h264NaluHeader; unsigned int bodyLen; unsigned char *bodyData;
} T_H264_NALU; /**********************************************************************************
1. h264的起始码: 0x000001(3 Bytes)或0x00000001(4 Bytes);
2. 文件流中用起始码来区分NALU.
***********************************************************************************/
static int FindStartCode3Bytes(unsigned char *scData)
{
int isFind = ; if ((==scData[]) && (==scData[]) && (==scData[]))
{
isFind = ;
} return isFind;
} static int FindStartCode4Bytes(unsigned char *scData)
{
int isFind = ; if ((==scData[]) && (==scData[]) && (==scData[]) && ( == scData[]))
{
isFind = ;
} return isFind;
} static int GetNaluDataLen(int startPos, int h264BitsSize, unsigned char *h264Bits)
{
int parsePos = ; parsePos = startPos; while (parsePos < h264BitsSize)
{
if (FindStartCode3Bytes(&h264Bits[parsePos]))
{
return parsePos - startPos;
}
else if (FindStartCode4Bytes(&h264Bits[parsePos]))
{
return parsePos - startPos;
}
else
{
parsePos++;
}
} return parsePos - startPos; // if file is end
} static void ParseNaluData(const unsigned int naluLen, unsigned char* const nuluData)
{
static int naluNum = ; unsigned char *data = NULL;
unsigned char priorityStr[PRTNTF_STR_LEN+] = {};
unsigned char typeStr[PRTNTF_STR_LEN+] = {}; T_H264_NALU_HEADER h264NaluHeader = {}; data = nuluData; memset(&h264NaluHeader, 0x0, sizeof(T_H264_NALU_HEADER)); h264NaluHeader.nal_reference_idc = data[]>> & 0x3;
h264NaluHeader.nal_unit_type = data[] & 0x1f; naluNum++; #ifdef PRINTF_DEBUG
switch (h264NaluHeader.nal_reference_idc)
{
case NALU_PRIORITY_DISPOSABLE:
sprintf(priorityStr, "DISPOS");
break; case NALU_PRIORITY_LOW:
sprintf(priorityStr, "LOW");
break; case NALU_PRIORITY_HIGH:
sprintf(priorityStr, "HIGH");
break; case NALU_PRIORITY_HIGHEST:
sprintf(priorityStr, "HIGHEST");
break; default:
break;
} switch (h264NaluHeader.nal_unit_type)
{
case NALU_TYPE_SLICE:
sprintf(typeStr, "SLICE");
break; case NALU_TYPE_DPA:
sprintf(typeStr, "DPA");
break; case NALU_TYPE_DPB:
sprintf(typeStr, "DPB");
break; case NALU_TYPE_DPC:
sprintf(typeStr, "DPC");
break; case NALU_TYPE_IDR:
sprintf(typeStr, "IDR");
break; case NALU_TYPE_SEI:
sprintf(typeStr, "SEI");
break; case NALU_TYPE_SPS:
sprintf(typeStr, "SPS");
break; case NALU_TYPE_PPS:
sprintf(typeStr, "PPS");
break; case NALU_TYPE_AUD:
sprintf(typeStr, "AUD");
break; case NALU_TYPE_EOSEQ:
sprintf(typeStr, "EOSEQ");
break; case NALU_TYPE_EOSTREAM:
sprintf(typeStr, "EOSTREAM");
break; case NALU_TYPE_FILL:
sprintf(typeStr, "FILL");
break; default:
break;
} printf("%5d| %7s| %6s| %8d|\n", naluNum, priorityStr, typeStr, naluLen);
#endif
} int main(int argc, char *argv[])
{
int fileLen = ;
int naluLen = ;
int h264BitsPos = ; unsigned char *h264Bits = NULL;
unsigned char *naluData = NULL; FILE *fp = NULL; if ( != argc)
{
printf("Usage: flvparse **.flv\n"); return -;
} fp = fopen(argv[], "rb");
if (!fp)
{
printf("open file[%s] error!\n", argv[]); return -;
} fseek(fp, , SEEK_END); fileLen = ftell(fp); fseek(fp, , SEEK_SET); h264Bits = (unsigned char*)malloc(fileLen);
if (!h264Bits)
{
printf("maybe file is too long, or memery is not enough!\n"); fclose(fp); return -;
} memset(h264Bits, 0x0, fileLen); if (fread(h264Bits, , fileLen, fp) < )
{
printf("read file data to h264Bits error!\n"); fclose(fp);
free(h264Bits); h264Bits = NULL; return -;
} fclose(fp); printf("-----+-------- NALU Table ------+\n");
printf(" NUM | IDC | TYPE | LEN |\n");
printf("-----+--------+-------+---------+\n"); while (h264BitsPos < (fileLen-))
{
if (FindStartCode3Bytes(&h264Bits[h264BitsPos]))
{
naluLen = GetNaluDataLen(h264BitsPos+, fileLen, h264Bits); naluData = (unsigned char*)malloc(naluLen);
if (naluData)
{
memset(naluData, 0x0, naluLen); memcpy(naluData, h264Bits+h264BitsPos+, naluLen); ParseNaluData(naluLen, naluData); free(naluData);
naluData = NULL;
} h264BitsPos += (naluLen+);
}
else if (FindStartCode4Bytes(&h264Bits[h264BitsPos]))
{
naluLen = GetNaluDataLen(h264BitsPos+, fileLen, h264Bits); naluData = (unsigned char*)malloc(naluLen);
if (naluData)
{
memset(naluData, 0x0, naluLen); memcpy(naluData, h264Bits+h264BitsPos+, naluLen); ParseNaluData(naluLen, naluData); free(naluData);
naluData = NULL;
} h264BitsPos += (naluLen+);
}
else
{
h264BitsPos++;
}
} return ;
}
最后如果您觉得本篇对您有帮助,可以打赏下,谢谢!!!
h264文件分析(纯c解析代码)的更多相关文章
- ts文件分析(纯c解析代码)
参考链接: 1. MPEG-2 TS码流分析 https://blog.csdn.net/zhubin215130/article/details/8958567 TS Header PAT PMT ...
- mpeg4文件分析(纯c解析代码)
参考链接: 1. MPEG4码流的帧率计算 https://blog.csdn.net/littlebee90/article/details/68924690 2. M ...
- h265文件分析(纯c解析代码)
参考链接: 1. HEVC码流解析 https://blog.csdn.net/CrystalShaw/article/details/80624804 2. HEVC编码结构:序列参数集SPS. ...
- mpeg2文件分析(纯c解析代码)
参考链接: 1. MPEG-2码流结构分析 https://www.cnblogs.com/CoderTian/p/9246225.html(本文语法采用这里的截图,代码原创) 1. mpeg2的码流 ...
- flv文件解析(纯c解析代码)
参考链接: 1. FLV科普12 FLV脚本数据解析-Metadata Tag解析 https://blog.csdn.net/cabbage2008/article/details/50500021 ...
- mp4文件解析(纯c解析代码)
参考链接:1. mp4文件格式解析 https://www.cnblogs.com/ranson7zop/p/7889272.html 2. MP4文件格式分析及分割实现(附源码) https: ...
- ps文件解析(纯c解析代码)
参考链接:1. PS流的格式和解析总结 http://www.cnblogs.com/lihaiping/p/4181607.html 2. TS科普5 PES包解析 https://blog.cs ...
- 使用ffmpeg将BMP图片编码为x264视频文件,将H264视频保存为BMP图片,yuv视频文件保存为图片的代码
ffmpeg开源库,实现将bmp格式的图片编码成x264文件,并将编码好的H264文件解码保存为BMP文件. 实现将视频文件yuv格式保存的图片格式的測试,图像格式png,jpg, gif等等測试均O ...
- linux内核中链表代码分析---list.h头文件分析(一)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...
随机推荐
- pycharm 序列号/行号 的宽度太宽了如何调整
问题:行号宽度嫌宽: 解决:在settings里面,如下图顺序,取消“show gutter icons”的勾,即可. 调整后如下图:
- git\CentOS6.5中gitlab安装教程
一.Git 起源: Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本 ...
- DAY 21内存处理与正则
一.python的垃圾回收机制 1.引用计数(垃圾回收机制的根本) 1.引用计数是用来记录值的内存地址被记录的次数的 2.每一次对值地址的引用都可以使该值的引用计数+1 3.每一次对值地址的释放都可以 ...
- 【转】EDID的简介和解析
转自:https://www.cnblogs.com/beyond-rxl/p/9266997.html 一.EDID简介 EDID: Extended Display Identification ...
- 常见的eclipse和真机出现的问题
1.eclipse和手机连接时间过断导致运行时报错(时间,,,) 2.adk中文件夹中文件遗失错乱: tools下的zipalign丢失(打包时出现提示the zipalign tool was no ...
- docker容器配置nginx负载均衡 -----加权
首先要准备三个nginx的容器: 第二个容器: 第三个容器: 进入第一个容器 要配置的容器(docker exec -it 容器名 /bin/bash) vi /usr/local/nginx/co ...
- Mtlab:抛物型方程的交替方向隐格式(ADI)
tic; clear clc M=[,,,,]; N=M; :length(M) h=/M(p);% 这里定义空间步长等距 tau=/N(p); % 时间步长 x=:h:; y=:h:; t=:tau ...
- 【记录】【3】设置bing为chrome的默认搜索引擎
方法:设置→搜索→管理搜索引擎→其他搜索引擎→设置bing搜索的网址为 http://cn.bing.com/search?q=%s 注:search?q=%s 是必须的,否则无法将其设置为默认 ...
- 使用Visual Studio Installer 2015打包WPF程序
前言 做过WPF项目,就少不了要将程序打包部署到客户现场,因为一般长时间不会更新打包程序,每次变动较大需要重新配置打包程序时,就会有些生疏,不那么得心应手.为了方便记忆,记录到博客中. 准备 因为做过 ...
- jsp页面输出当前时间
<% out.print(new java.text.SimpleDateFormat("yyyy年MM月dd号 hh:mm:ss").format(new Date())) ...