#include <time.h>
#include <iostream>
#include <chrono>
#include <ctime>
#include "EncoderLib/EncLibCommon.h"
#include "EncApp.h"
#include "Utilities/program_options_lite.h"
一些头文件,前一段是引入标准库,后面一段是自定义的头文件
static constexpr uint32_t settingNameWidth = 66;
static constexpr uint32_t settingHelpWidth = 84;
static constexpr uint32_t settingValueWidth = 3;
-
static
:表示静态变量,具有静态存储持续时间,在整个程序执行期间都存在,并且只会被初始化一次。 -
constexpr
:表示常量表达式,编译时就可以计算出结果并替换为常量值,通常用于声明编译期间就能确定的常量。 -
uint32_t
:是C++中无符号32位整数类型的数据类型,表示范围在0到4294967295之间的整数。 -
settingNameWidth
:是变量名,表示设置名称的宽度。
#define PRINT_CONSTANT(NAME, NAME_WIDTH, VALUE_WIDTH) std::cout << std::setw(NAME_WIDTH) << #NAME << " = " << std::setw(VALUE_WIDTH) << NAME << std::endl;
static void printMacroSettings()
{
if( g_verbosity >= DETAILS )
{
std::cout << "Non-environment-variable-controlled macros set as follows: \n" << std::endl;
//------------------------------------------------
//setting macros
PRINT_CONSTANT( RExt__DECODER_DEBUG_BIT_STATISTICS, settingNameWidth, settingValueWidth );
PRINT_CONSTANT( RExt__HIGH_BIT_DEPTH_SUPPORT, settingNameWidth, settingValueWidth );
PRINT_CONSTANT( RExt__HIGH_PRECISION_FORWARD_TRANSFORM, settingNameWidth, settingValueWidth );
//------------------------------------------------
std::cout << std::endl;
}
}
上面这两块没看懂
主函数:
int main(int argc, char* argv[])
c语言中main函数参数argc,argv说明,及命令行中如何传参数_c语言 argv 赋值-CSDN博客
// print information
//打印编码信息(编码器版本平台等)
fprintf( stdout, "\n" );
fprintf( stdout, "VVCSoftware: VTM Encoder Version %s ", VTM_VERSION );
//fprintf( stdout, NVM_ONOS );
fprintf( stdout, NVM_COMPILEDBY );
fprintf( stdout, NVM_BITS );
#if ENABLE_SIMD_OPT
std::string SIMD;
ProgramOptionsLite::Options opts;
opts.addOptions()("SIMD", SIMD, std::string(""), "")("c", ProgramOptionsLite::parseConfigFile, "");
ProgramOptionsLite::SilentReporter err;
ProgramOptionsLite::scanArgv(opts, argc, (const char**) argv, err);
fprintf( stdout, "[SIMD=%s] ", read_x86_extension( SIMD ) );
#endif
#if ENABLE_TRACING
fprintf( stdout, "[ENABLE_TRACING] " );
#endif
fprintf( stdout, "\n" );
//stdout, stdin, stderr的中文名字分别是标准输出,标准输入和标准错误。fprintf()函数根据指定的format(格式)发送信息(参数)到由stream(流)指定的文件.因此fprintf()可以使得信息输出到指定的文件
/if endif 后为0执行 endif 后的程序,为1执行其中的程序。
std::fstream bitstream;
EncLibCommon encLibCommon;
//fstream是用using为basic_fstream这个类起的一个别名,bitstream是实例化的对象。规定了与C流相关联的输入/输出流
std::vector<EncApp*> pcEncApp(1);
bool resized = false;
int layerIdx = 0;
//初始化rom.cpp中的一些全局变量
initROM();
char** layerArgv = new char*[argc];
//遍历layers创建pcEncApp并初始化各个layer的参数
//在堆区开辟一块内存,存入命令行输入的每个参数的地址,然后用新建的指针 layerArgv来接收,这个指针(指向一块内存地址,该内存地址中存储的是 char* 类型的数据。指针的加减运算在这里的体现为: layerArgv + 1 表示地址加 8 字节(在 32 位系统中,地址加 4 字节)
do
{
pcEncApp[layerIdx] = new EncApp( bitstream, &encLibCommon );
// create application encoder class per layer
pcEncApp[layerIdx]->create();
// parse configuration per layer
//为当前遍历的layer初始化配置
try
{
int j = 0;
for( int i = 0; i < argc; i++ )
{
if( argv[i][0] == '-' && argv[i][1] == 'l' )
{
if (argc <= i + 1)
{
THROW("Command line parsing error: missing parameter after -lx\n");
}
int numParams = 1; // count how many parameters are consumed
// 检查以“——”开头的长参数。
const std::string param = argv[i + 1];
if (param.rfind("--", 0) != 0)
{
// only short parameters have a second parameter for the value
if (argc <= i + 2)
{
THROW("Command line parsing error: missing parameter after -lx\n");
}
numParams++;
}
// 检查图层索引是否正确
if( argv[i][2] == std::to_string( layerIdx ).c_str()[0] )
{
layerArgv[j] = argv[i + 1];
if (numParams > 1)
{
layerArgv[j + 1] = argv[i + 2];
}
j+= numParams;
}
i += numParams;
}
这一段是解析每层的配置,在命令行参数为5的情况时,i的值从0~4,将5个命令行参数的地址分别赋给了layerArgv[j],如果命令函参数出错,则报错检查
else
{
layerArgv[j] = argv[i];
j++;
}
}
//前面定义的layerArgv是开辟的一块内存来存储命令行参数argc地址的,比如这里存了5个地址,这里就是给这个地址作个排序,按顺序赋给 layerArgv
//解析输入的参数
if( !pcEncApp[layerIdx]->parseCfg( j, layerArgv ) )
{
pcEncApp[layerIdx]->destroy();
return 1;
}
}
catch (ProgramOptionsLite::ParseFailure& e)
{
std::cerr << "Error parsing option \"" << e.arg << "\" with argument \"" << e.val << "\"." << std::endl;
return 1;
}
pcEncApp[layerIdx]->createLib( layerIdx );
if( !resized )
{
pcEncApp.resize( pcEncApp[layerIdx]->getMaxLayers() );
resized = true;
}
layerIdx++;
} while( layerIdx < pcEncApp.size() );
//结束对各个layer的遍历初始化
delete[] layerArgv;
记录开始编码的时间
//**********************从此处开始计算编码时间*********
// 记录开始编码的时间
// starting time
auto startTime = std::chrono::steady_clock::now();
std::time_t startTime2 = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
fprintf(stdout, " started @ %s", std::ctime(&startTime2) );
clock_t startClock = clock();
编码器将多张图像进行编码后生产成一段一段的 GOP ( Group of Pictures ) , 解码器在播放时则是读取一段一段的 GOP 进行解码后读取画面再渲染显示。GOP ( Group of Pictures) 是一组连续的画面,由一张 I 帧和数张 B / P 帧组成,是视频图像编码器和解码器存取的基本单位,它的排列顺序将会一直重复到影像结束。GOP即Group of picture(图像组),指两个I帧之间的距离。一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。
bool eos = false;//eos:End Of Sequence,代表一个gop中的最后一帧。如果eos为true,则代表已经读取到输入视频的最后一帧
eos:End Of Sequence,代表一个gop中的最后一帧。如果eos为true,则代表已经读取到输入视频的最后一帧
//大循环直到编码完序列的所有帧才结束
while( !eos )
{
// read GOP
//编码前的预处理,主要作用有从输入视频中读取一帧,给帧分配缓存(存在EncLib的属性m_cListPic中),设置对应的CS
//keepLoop用于记录当前的GOP是否已经编完,若编完则true,否则False,这里初始化为true
bool keepLoop = true;
//小循环遍历当前的GOP内的所有帧进行预处理
while( keepLoop )
{
for( auto & encApp : pcEncApp )
{
#ifndef _DEBUG
try
{
#endif
//对当前GOP内的帧进行预处理的入口函数,keeploop是预处理函数的输出,若已处理完当前GOP所有帧则为true,否则为false
keepLoop = encApp->encodePrep( eos );
// encode GOP
//正式编码开始
keepLoop = true;
//小循环遍历当前的GOP内的所有帧进行编码
while( keepLoop )
{
for( auto & encApp : pcEncApp )
{
#ifndef _DEBUG
try
{
#endif
//编码当前帧的入口函数,与预处理的函数一样,keeploop是输出,若已处理完当前GOP所有帧则为true,否则为false
keepLoop = encApp->encode();
#ifndef _DEBUG
}
编码预处理后keeploop的值变成了false,结束了while循环,keepLoop = encApp->encode();