Explore Enterprise Education Gitee Premium Gitee AI AI teammates
Fetch the repository succeeded.
forked from 黄老邪/AVDataProcess
Create your Gitee Account
Explore and code with more than 14 million developers,Free private repositories !:)
Sign up
Already have an account? Sign in
文件
master
Branches (1)
master
This repository doesn't specify license. Please pay attention to the specific project description and its upstream code dependency when using it.
The license selected for the repository is subject to the license used by the main branch of the repository.
master
Branches (1)
master
Clone or Download
Clone/Download
Prompt
To download the code, please copy the following command and execute it in the terminal
To ensure that your submitted code identity is correctly recognized by Gitee, please execute the following command.
When using the SSH protocol for the first time to clone or push code, follow the prompts below to complete the SSH configuration.
1 Generate RSA keys.
2 Obtain the content of the RSA public key and configure it in SSH Public Keys
To use SVN on Gitee, please visit the usage guide
When using the HTTPS protocol, the command line will prompt for account and password verification as follows. For security reasons, Gitee recommends configure and use personal access tokens instead of login passwords for cloning, pushing, and other operations.
Username for 'https://gitee.com': userName
Password for 'https://userName@gitee.com': # Private Token
master
Branches (1)
master
AVDataProcess
/
h264_parser.cpp
AVDataProcess
/
h264_parser.cpp
h264_parser.cpp 9.22 KB
Copy Edit Raw Blame History
黄老邪 authored 2017年11月11日 17:38 +08:00 . h264码流分析代码添加注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
/*该程序可以从H.264码流中分析得到它的基本单元NALU,并且可以简单解析NALU首部的字段。通过修改该程序可以实现不同的H.264码流处理功能*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//0x00000001或0x000001是一个nalu的起始标志,遇到下一个此标志时为该nalu的结尾。
//起始标志的后面第一个字节(type)里包含有nalu的类型,type & 0x1F即为该nalu的类型(nalu_type)。
typedef enum {
NALU_TYPE_SLICE = 1, //非IDR图像中不采用数据划分的片段,为P或B帧
NALU_TYPE_DPA = 2, //非IDR图像中A类数据划分片段
NALU_TYPE_DPB = 3, //非IDR图像中B类数据划分片段
NALU_TYPE_DPC = 4, //非IDR图像中C类数据划分片段
NALU_TYPE_IDR = 5, //IDR图像的片段,关键帧,I帧
NALU_TYPE_SEI = 6, //补充增强信息
NALU_TYPE_SPS = 7, //序列参数集(Sequence Parameter Set)
NALU_TYPE_PPS = 8, //图像参数集PPS(Picture Parameter Set)
NALU_TYPE_AUD = 9, //分割符
NALU_TYPE_EOSEQ = 10, //序列结束符
NALU_TYPE_EOSTREAM = 11, //流结束符
NALU_TYPE_FILL = 12, //填充数据
}NaluType;
//类型说明参考:http://www.cnblogs.com/lidabo/p/4481601.html
typedef enum {
NALU_PRIORITY_DISPOSABLE = 0,
NALU_PRIORITY_LOW = 1,
NALU_PRIORITY_HIGH = 2,
NALU_PRIORITY_HIGHEST = 3,
}NaluPriority;
typedef struct
{
int startcode_len; //! 起始码长度,3个字节或4个字节
unsigned len; //! NALU长度,不包括起始码长度
unsigned max_size; //! Nal Unit Buffer size
int forbidden_bit; //! should be always FALSE
//! 禁止位:网络发现NAL单元有比特错误时可设置该比特为1,以便接收方丢掉该单元。
int nal_reference_idc; //! 表示NAL的优先级。0〜3,取值越大,表示当前NAL越重要,需要优先受到保护。
//! 如果当前NAL是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的单位时,
//! 本句法元素必需大于0。
int nalu_type; //! NALU类型
char *buf; //! contains the first byte followed by the EBSP
} NALU_t;
//如果NALU对应的Slice为一帧的开始就用0x00000001(四个字节),否则就用0x000001(三个字节)。
int findStartCode3(unsigned char *buf)
{
if ( buf[0]!=0 || buf[1]!=0 || buf[2]!=1 ) {
return 0; //0x000001
} else {
return 1;
}
}
int findStartCode4(unsigned char *buf)
{
if ( buf[0]!=0 || buf[1]!=0 || buf[2]!=0 || buf[3]!=1 ) {
return 0; //0x00000001
} else {
return 1;
}
}
//如果NALU对应的Slice为一帧的开始就用0x00000001(即4个字节长度),否则就用0x000001(即3个字节长度)。
int findStartCode(const unsigned char *buf)
{
if (strlen((const char*)buf) >= 3) {
if ( buf[0]==0 && buf[1]==0 && buf[2]==1) {
return 3;
}
}
if (strlen((const char*)buf) >= 4) {
if ( buf[0]==0 && buf[1]==0 && buf[2]==0 && buf[3]==1) {
return 4;
}
}
return 0;
}
//获取NALU单元
int getNalu(NALU_t *nalu, FILE *fp)
{
if (nalu == NULL) {
return -1;
}
unsigned char *buf = (unsigned char *)malloc(nalu->max_size);
if (buf == NULL) {
return 0;
} else {
memset(buf, 0, nalu->max_size);
}
//默认起始码长度为3个字节
nalu->startcode_len = 3;
//读取前三个字节以判断NALU的起始标志
//如果fread返回值不等于3,则表示已到文件结尾
if (3 != fread(buf, 1, 3, fp)) {
free(buf);
return 0;
}
//获取起始码长度
int info4 = 0; //4个字节长度的起始码,一帧的开始
int info3 = 0; //3个字节长度的起始码
int pos = 0; //当前位置
//首先查找3字节的起始码
info3 = findStartCode3(buf);
if (info3 != 1) {
//如果不是3字节长度的起始码,就再读取一个字节以判断是否为4字节长度的起始码
if (1 != fread(buf+3, 1, 1, fp)) {
free(buf);
return 0;
}
info4 = findStartCode4(buf);
if (info4 != 1) {
free(buf);
return -1;
} else {
pos = 4;
nalu->startcode_len = 4;
}
} else {
pos = 3;
nalu->startcode_len = 3;
}
while (1) {
//如果文件结束
if (feof(fp)) {
nalu->len = (pos-1) - nalu->startcode_len;
memcpy(nalu->buf, &buf[nalu->startcode_len], nalu->len);
nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[0] & 0x60; //2 bit
nalu->nalu_type = nalu->buf[0] & 0x1f; //5 bit
free(buf);
return pos-1;
}
//循环读取一个字符,为了查找下一个起始码的位置
buf[pos++] = fgetc(fp);
//首先查找4字节的起始码
info4 = findStartCode4(&buf[pos-4]);
if (info4 != 1) {
//如果不是4字节的起始码,再判断是否是3字节的起始码
info3 = findStartCode3(&buf[pos-3]);
}
//如果查找到了起始码就退出
if (info3==1 || info4==1) {
break;
}
}
//程序能走到这一步表示查找到起始码
//文件指针往回走3/4个字节,即起始码字节长度
int rewind = (info4==1) ? -4 : -3;
if (0 != fseek(fp, rewind, SEEK_CUR)) {
free(buf);
}
//NALU长度不包括起始码长度
//当前位置减去下一个起始码长度,再减去当前起始码长度
nalu->len = (pos+rewind) - nalu->startcode_len;
//NALU内容不包括起始码
memcpy(nalu->buf, &buf[nalu->startcode_len], nalu->len);
nalu->forbidden_bit = nalu->buf[0] & 0x80; //1000 0000,禁止位(1bit)
nalu->nal_reference_idc = nalu->buf[0] & 0x60; //0110 0000,重要性指示位(2bit)
nalu->nalu_type = nalu->buf[0] & 0x1f; //0001 1111,NALU类型(5bit)
free(buf);
//返回NALU长度(包括起始码长度)
return pos+rewind;
}
int h264_parser(char *file)
{
if (file == NULL) {
return 0;
}
//从文件中读取h264码流
FILE *h264BitStream = fopen(file, "rb+");
if (h264BitStream == NULL) {
return 0;
}
NALU_t *nalu = (NALU_t *)malloc(sizeof(NALU_t));
if (nalu == NULL) {
return 0;
} else {
memset(nalu, 0, sizeof(NALU_t));
}
nalu->max_size = 100000;
nalu->buf = (char *)malloc(100000);
if (nalu->buf == NULL) {
return 0;
} else {
memset(nalu->buf, 0, 100000);
}
printf("-----+-------- NALU Table ------+---------+\n");
printf(" NUM | POS | IDC | TYPE | LEN |\n");
printf("-----+---------+--------+-------+---------+\n");
int data_offset=0;
int nal_num=0; //NALU计数
while(!feof(h264BitStream)) {
//分析一个NALU,返回NALU长度(包括起始码长度)
int dataLen = getNalu(nalu, h264BitStream);
char sType[20] = {0};
switch(nalu->nalu_type) {
case NALU_TYPE_SLICE: sprintf(sType, "SLICE"); break;
case NALU_TYPE_DPA: sprintf(sType, "DPA"); break;
case NALU_TYPE_DPB: sprintf(sType, "DPB"); break;
case NALU_TYPE_DPC: sprintf(sType, "DPC"); break;
case NALU_TYPE_IDR: sprintf(sType, "IDR"); break;
case NALU_TYPE_SEI: sprintf(sType, "SEI"); break;
case NALU_TYPE_SPS: sprintf(sType, "SPS"); break;
case NALU_TYPE_PPS: sprintf(sType, "PPS"); break;
case NALU_TYPE_AUD: sprintf(sType, "AUD"); break;
case NALU_TYPE_EOSEQ: sprintf(sType, "EOSEQ"); break;
case NALU_TYPE_EOSTREAM: sprintf(sType, "EOSTREAM"); break;
case NALU_TYPE_FILL: sprintf(sType, "FILL"); break;
}
char idc_str[20] = {0};
switch(nalu->nal_reference_idc >> 5) {
case NALU_PRIORITY_DISPOSABLE:sprintf(idc_str,"DISPOS"); break;
case NALU_PRIORITY_LOW: sprintf(idc_str,"LOW"); break;
case NALU_PRIORITY_HIGH: sprintf(idc_str,"HIGH"); break;
case NALU_PRIORITY_HIGHEST: sprintf(idc_str,"HIGHEST");break;
}
FILE *out = stdout;
fprintf(out,"%5d| %8d| %7s| %6s| %8d|\n",nal_num,data_offset,idc_str,sType,nalu->len);
//NAL单元计数
nal_num++;
//数据偏移,也就是下一个NALU起始位置(起始码的位置)
data_offset = data_offset + dataLen;
}
//释放内存
if (nalu) {
if (nalu->buf) {
free(nalu->buf);
nalu->buf = NULL;
}
free(nalu);
}
return 0;
}
int main(int argc, char *argv[])
{
if (argc == 2) {
h264_parser(argv[1]);
}
return 0;
}
Loading...
Report
Report success
We will send you the feedback within 2 working days through the letter!
Please fill in the reason for the report carefully. Provide as detailed a description as possible.
Please select a report type
Cancel
Send
误判申诉

此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。

如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。

取消
提交

About

视音频数据处理入门。
Cancel

Releases

No release

Contributors

All

Activities

can not load any more
Edit
About
Homepage
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/harray/AVDataProcess.git
git@gitee.com:harray/AVDataProcess.git
harray
AVDataProcess
AVDataProcess
master
Going to Help Center

Search

Comment
Repository Report
Back to the top
Login prompt
This operation requires login to the code cloud account. Please log in before operating.
Go to login
No account. Register

AltStyle によって変換されたページ (->オリジナル) /