【视頻编编解码·学习培训手记】6. H.264视频比特

摘要: 【视頻编编解码·学习培训手记】6. H.264视频比特率剖析工程项目建立在建一个VS工程项目SimpleH264analyzer, 改动工程项目特性主要参数- 輸出文件目录:$(SolutionDir)bin\$(Configuration)\,工作...

【视頻编编解码·学习培训手记】6. H.264视频比特率剖析工程项目建立
在建一个VS工程项目SimpleH264analyzer, 改动工程项目特性主要参数- 輸出文件目录:$(SolutionDir)bin\$(Configuration)\,工作中文件目录:$(SolutionDir)bin\$(Configuration)\

编译程序一下工程项目,工程项目文件目录下能转化成bin文档夹,在其中的debug文档夹中有刚刚编译程序转化成的exe文档。将一个.264视頻文档复制到这一文档夹中(此次应用的还是学习培训手记3中转化成的.264文档)。

将这一文档做为键入主要参数传入工程项目中:特性 - 调节 - 指令主要参数:test.264 (最终哪个文档名依据自身的改)

变更文件目录构造,并在建2个文档Stream.h Stream.cpp,变更后文件目录构造以下:
1 目录结构

在Stream.h头文档中,在建一个类CStreamFile,用于表明.264文档,在其中包含结构涵数、独享组员自变量,及自定涵数。编码以下:

#ifndef _STREAM_H_
#define _STREAM_H_
#include vector 
class CStreamFile
public:
 CStreamFile(TCHAR *fileName);
 ~CStreamFile();
 // Open API
 int Parse_h264_bitstream();
private:
 FILE *m_InputFile;
 TCHAR *m_fileName;
 std::vector uint8 m_nalVec;
 // 用于复印系统日志
 ();
 void file_error(int dex);
 // 获取NAL合理数据信息
 int find_nal_prefix();
#endif
在Stream.cpp文档中,完成其结构方式及组员涵数:

#include stdafx.h 
#include Stream.h 
#include iostream 
using namespace std;
// 结构涵数进行开启文档实际操作
CStreamFile::CStreamFile(TCHAR * fileName)
 m_fileName = fileName;
 ();
 // 开启视頻文档(写保护二进制)
 _tfopen_s( m_InputFile, m_fileName, _T( rb ));
 if (NULL == m_InputFile)
 file_error(0);
// 析构涵数进行关掉文档实际操作
CStreamFile::~CStreamFile()
 if (NULL != m_InputFile)
 fclose(m_InputFile);
 m_InputFile = NULL;
int CStreamFile::Parse_h264_bitstream()
 return 0;
int CStreamFile::find_nal_prefix()
 return 0;
// 复印文档信息内容
void CStreamFile::()
 if (m_fileName)
 wcout L File name: m_fileName endl;
// 复印不正确信息内容
void CStreamFile::file_error(int idx)
 switch (idx)
 case 0:
 wcout L Error: opening input file failed. endl;
 break;
 default:
 break;
}
以后在主涵数中,撰写开启文档编码,检测之上编码可否一切正常实行:

#include stdafx.h 
#include Stream.h 
int _tmain(int argc, _TCHAR* argv[])
 CStreamFile h264stream(argv[1]);
 // 此涵数做为最顶层涵数,实行全部作用(临时还未写一切作用完成)
 h264stream.Parse_h264_bitstream();
 return 0;
}
编译程序实行后,在cmd对话框中,可以复印出文档名字,即是恰当实行。

接下去,设定一个全局性的头文档,用于界定全部文档上都用到到的数据信息种类。
在Application文件目录下,在建Global.h头文档,键入下列编码:

#ifndef _GLOBAL_H_
#define _GLOBAL_H_
typedef unsigned char uint8;
typedef unsigned int uint32;
#endif // !_GLOBAL_H_
在stdafx.h文档中,引进刚刚在建的头文档:

#include Global.h 

完成find_nal_prefix()涵数。完成方式与学习培训手记4中编码基本一致,仅改动一些自变量名字。(学习培训手记4中有详尽解读,这儿已不表明)。Stream.cpp文档中,涵数完成以下:

int CStreamFile::find_nal_prefix()
 uint8 prefix[3] = { 0 };
 uint8 fileByte;

if ((prefix[pos % 3] == 0) (prefix[(pos + 1) % 3] == 0) (prefix[(pos + 2) % 3] == 1)) // 0x 00 00 01 found getPrefix = 1; m_nalVec.pop_back(); m_nalVec.pop_back(); m_nalVec.pop_back(); break; else if ((prefix[pos % 3] == 0) (prefix[(pos + 1) % 3] == 0) (prefix[(pos + 2) % 3] == 0)) if (1 == getc(m_InputFile)) // 0x 00 00 00 01 found getPrefix = 2; m_nalVec.pop_back(); m_nalVec.pop_back(); m_nalVec.pop_back(); break; else fileByte = getc(m_InputFile); prefix[(pos++) % 3] = fileByte; m_nalVec.push_back(fileByte); return getPrefix; }
改动Stream.cpp中Parse_h264_bitstream()涵数,循环系统启用find_nal_prefix()涵数,持续获得起止码中间数据信息。

int CStreamFile::Parse_h264_bitstream()
 int ret = 0;
 ret = find_nal_prefix();
 } while (ret);
 return 0;
}
对于此事文档编译程序、调节,查询之上所敲代码是不是不太好:
第一次循环系统时,文档指针移动到第一个起止码后;第二次循环系统时,载入到2个起止码间的合理数据信息,根据调节可见到以下数据信息,与test.264中第一组合理数据信息同样:
2 调试数据

2. 获取NAL Unit 类型: ① 最先获取每个NAL Unit的类型,改动Parse_h264_bitstream()涵数以下:

int CStreamFile::Parse_h264_bitstream()
 int ret = 0;
 ret = find_nal_prefix();
 // 分析NAL UNIT
 // 第一次实行循环系统的情况下,m_nalVec为空,因而加个分辨
 if (m_nalVec.size())
 // 鉴别NAL Unit类型
 // NAL Unit第一个字节数为NAL Header,后边5位表明NAL Type(应用按位与计算,提取后边五十位数据)
 uint8 nalType = m_nalVec[0] 0x1F;
 wcout L NAL Unit Type: nalType endl;
 } while (ret);
 return 0;
}
编译程序运作后,結果以下:
3
其所相匹配的种类为(可从H.264官方网文本文档,表7-1中查出):
4

三、NAL Unit 解除限制装: 1. EBSP - RBSP: 除去市场竞争校检位(详尽定义看学习培训手记5)
简单点来说,便是除去2个连零后边的03。00 00 03 xx xx xx (在其中的03即是市场竞争校检位,在拆包的情况下必须除去)

在 CStreamFile 类中加上独享涵数 void ebsp_to_rbsp();
涵数完成以下:

void CStreamFile::ebsp_to_rbsp()
 // 00 00 03 持续2个零零后面的03是避免市场竞争校检字节数,必须除掉
 // 在编码序列中找03,在查询前边2个不是是00,假如是,就要掉03
 if (m_nalVec.size() 3)
 return;
 for (vector uint8 ::iterator itor = m_nalVec.begin() + 2; itor != m_nalVec.end(); )
 // 迭代更新器提高力度为空,写在循环系统內部,便捷删掉原素
 if ((3 == *itor) (0 == *(itor - 1)) (0 == *(itor - 2)))
 // 这里应用erase()时要要留意:
 // 1、当启用erase()后Itor迭代更新器就无效了,变为了一野指针
 // 2、而erase()这一涵数会回到一个指针,仍偏向消除原素的部位,只不过是后边全部的数据信息都往前移动
 itor = m_nalVec.erase(itor);
 else
 itor++;
}

这儿本应也有RBSP - SODB的一部分,也便是除去 rbsp_trailing_bits ,但针对剖析 NAL Body 內部英语的语法原素不容易导致具体危害,这一部分临时缺口,有兴趣爱好的能够自身完成一下。


仙桃云科潜心于网站设计制作,自始至终追求完美“用更快的速率订制出最比较好的网站”。懂您需要、做您所感!大家一直在思索怎样为顾客造就更大的使用价值,让顾客更放心!


联系我们

全国服务热线:4000-399-000 公司邮箱:343111187@qq.com

  工作日 9:00-18:00

关注我们

官网公众号

官网公众号

Copyright?2020 广州凡科互联网科技股份有限公司 版权所有 粤ICP备10235580号 客服热线 18720358503

技术支持:自助建站