x265源码分析:main函数及CLIOptions结构体解释
来源:互联网 发布:怎么找淘宝女主播 编辑:程序博客网 时间:2024/06/05 18:17
/** * 返回码信息: * 0 – 编码成功; * 1 – 命令行解释失败; * 2 – 编码器打开失败; * 3 – 生成流头部失败; * 4 – 编码出错; * 5 – 打开csv文件失败. */int main(int argc, char **argv){ // 获取控制台窗口的标题,保存在静态数组orgConsoleTitle中 GetConsoleTitle(orgConsoleTitle, CONSOLE_TITLE_SIZE); // 取消电源管理,避免睡眠、待机 SetThreadExecutionState(ES_CONTINUOUS|ES_SYSTEM_REQUIRED|ES_AWAYMODE_REQUIRED); ReconPlay* reconPlay = NULL; CLIOptions cliopt; // 分析命令行参数,对编码器的参数进行设置,打开相关文件 if (cliopt.parse(argc, argv)) { cliopt.destroy(); if (cliopt.api) cliopt.api->param_free(cliopt.param); exit(1); } x265_param* param = cliopt.param; const x265_api* api = cliopt.api; /* This allows muxers to modify bitstream format */ cliopt.output->setParam(param); if (cliopt.reconPlayCmd) reconPlay = new ReconPlay(cliopt.reconPlayCmd, *param); // 命令行解释阶段,依据参数profile的值可选择不同的 libx265 API. // 打开编码器 x265_encoder *encoder = api->encoder_open(param); if (!encoder) { x265_log(param, X265_LOG_ERROR, "failed to open encoder\n"); cliopt.destroy(); api->param_free(param); api->cleanup(); exit(2); } // 获取编码参数 api->encoder_parameters(encoder, param); if (cliopt.csvfn) { cliopt.csvfpt = x265_csvlog_open(*api, *param, cliopt.csvfn, cliopt.csvLogLevel); if (!cliopt.csvfpt) { x265_log_file(param, X265_LOG_ERROR, "Unable to open CSV log file <%s>, aborting\n", cliopt.csvfn); cliopt.destroy(); if (cliopt.api) cliopt.api->param_free(cliopt.param); exit(5); } } // 当按下Ctrl-C的时候,当前执行程序调用指针函数sigint_handler 执行完后,再返回原 // 来执行的地方接着往下走。 此处将变量b_ctrl_c设置为1,停止当前编码工作。 if (signal(SIGINT, sigint_handler) == SIG_ERR) x265_log(param, X265_LOG_ERROR, "Unable to register CTRL+C handler: %s\n", strerror(errno)); // 定义x265的输入pic_orig和输出pic_out x265_picture pic_orig, pic_out; x265_picture *pic_in = &pic_orig; // 获取x265的输入pic_orig的地址 // Allocate recon picture if analysisMode is enabled std::priority_queue<int64_t>* pts_queue = cliopt.output->needPTS() ? new std::priority_queue<int64_t>() : NULL; x265_picture *pic_recon = (cliopt.recon || !!param->analysisMode || pts_queue || reconPlay || cliopt.csvLogLevel) ? &pic_out : NULL; // 输入、输出的帧数 uint32_t inFrameCount = 0; uint32_t outFrameCount = 0; x265_nal *p_nal; x265_stats stats; uint32_t nal; int16_t *errorBuf = NULL; int ret = 0; // 如果不需要复制sps和pps放在每个关键帧的前面,则直接编码视频流的头 if (!param->bRepeatHeaders) { if (api->encoder_headers(encoder, &p_nal, &nal) < 0) { x265_log(param, X265_LOG_ERROR, "Failure generating stream headers\n"); ret = 3; goto fail; } else // 更新总字节数:增加头部字节数目 cliopt.totalbytes += cliopt.output->writeHeaders(p_nal, nal); } api->picture_init(param, pic_in); if (cliopt.bDither) { errorBuf = X265_MALLOC(int16_t, param->sourceWidth + 1); if (errorBuf) memset(errorBuf, 0, (param->sourceWidth + 1) * sizeof(int16_t)); else cliopt.bDither = false; } // main encoder loop(编码主循环) while (pic_in && !b_ctrl_c) { pic_orig.poc = inFrameCount; if (cliopt.qpfile) { if (!cliopt.parseQPFile(pic_orig)) { x265_log(NULL, X265_LOG_ERROR, "can't parse qpfile for frame %d\n", pic_in->poc); fclose(cliopt.qpfile); cliopt.qpfile = NULL; } } // 待编码的帧数大于0 且 输入的帧数大于或等于将要编码的帧数 if (cliopt.framesToBeEncoded && inFrameCount >= cliopt.framesToBeEncoded) pic_in = NULL; // 成功读取一帧,则输入帧数增加1 else if (cliopt.input->readPicture(pic_orig)) inFrameCount++; else pic_in = NULL; if (pic_in) { if (pic_in->bitDepth > param->internalBitDepth && cliopt.bDither) { x265_dither_image(*api, *pic_in, cliopt.input->getWidth(), cliopt.input->getHeight(), errorBuf, param->internalBitDepth); pic_in->bitDepth = param->internalBitDepth; } /* Overwrite PTS */ pic_in->pts = pic_in->poc; } // 进行编码入口函数,读入几十帧(具体多少帧依赖命令行参数的设置)之后才开始编码。 // numEncoded是本次编码出来的帧数,大部分情况下为1,如果是负数表示编码出错。 int numEncoded = api->encoder_encode(encoder, &p_nal, &nal, pic_in, pic_recon); if (numEncoded < 0) { b_ctrl_c = 1; ret = 4; break; } if (reconPlay && numEncoded) reconPlay->writePicture(*pic_recon); if (numEncoded && pic_recon && cliopt.recon) cliopt.recon->writePicture(pic_out); if (nal) { cliopt.totalbytes += cliopt.output->writeFrame(p_nal, nal, pic_out); if (pts_queue) { pts_queue->push(-pic_out.pts); if (pts_queue->size() > 2) pts_queue->pop(); } } // 打印编码帧的具体信息 cliopt.printStatus(outFrameCount); if (numEncoded && cliopt.csvLogLevel) x265_csvlog_frame(cliopt.csvfpt, *param, *pic_recon, cliopt.csvLogLevel); } // 编码主循环结束 /* Flush the encoder */ // 前面读入几十帧之后才开始编码,此处其实就是处理对应的倒数的几十帧,将其存储 while (!b_ctrl_c) { ... ... } /* clear progress report */ if (cliopt.bProgress) fprintf(stderr, "%*s\r", 80, " ");fail: ... ... return ret;}
x265 的各种参数主要是通过CLIOptions结构体及相关函数传递给x265的各API,接下来我们分析CLIOptions结构体的构成及重要函数的功能,如下:
/* 命令行接口 */struct CLIOptions{ InputFile* input; // 输入文件,抽象类, 定义在 \input\input.h 中 ReconFile* recon; // 重构文件,抽象类,定义在 \output\output.h 中 OutputFile* output; // 输出文件,抽象类,定义在 \output\output.h 中 FILE* qpfile; // 量化因子文件指针 FILE* csvfpt; // csv日志文件指针 const char* csvfn; // csv日志文件名 const char* reconPlayCmd; const x265_api* api; // x265_param* param; // x265编码器参数集 bool bProgress; // 是否输出编码进度和其他一些编码状态数据 bool bForceY4m; // 如果输入文件是Y4M格式,需要强制指定输入格式 bool bDither; int csvLogLevel; // csv日志级别,log level定义在 x265.h中 uint32_t seek; // 输入视频起始需要跳过的帧数 uint32_t framesToBeEncoded; // 待编码的帧数 uint64_t totalbytes; // 已编码的数据字节数 int64_t startTime; // 起始编码时间点 int64_t prevUpdateTime; // 上一次编码信息输出的时间点 /* in microseconds,相邻两次编码状态输出的最小时间间隔,单位微妙 */ static const int UPDATE_INTERVAL = 250000; // 构造函数,初始化上述各成员变量 CLIOptions() { … … … … } void destroy(); void printStatus(uint32_t frameNum); // 打印编码状态信息 bool parse(int argc, char **argv); // 解释命令行参数 bool parseQPFile(x265_picture &pic_org); // 解释量化因子QP文件};/* 打印编码状态信息 */void CLIOptions::printStatus(uint32_t frameNum){ char buf[200]; int64_t time = x265_mdate(); // 是否输出编码状态信息取决于:进度开关、当前编码帧数、上次信息输出和当前时间的间隔 if (!bProgress || !frameNum || (prevUpdateTime && time - prevUpdateTime < UPDATE_INTERVAL)) return; // 计算编码帧率和码率 int64_t elapsed = time - startTime; double fps = elapsed > 0 ? frameNum * 1000000. / elapsed : 0; float bitrate = 0.008f * totalbytes * (param->fpsNum / param->fpsDenom) / ((float)frameNum); if (framesToBeEncoded) { int eta = (int)(elapsed * (framesToBeEncoded - frameNum) / ((int64_t)frameNum * 1000000)); sprintf(buf, "x265 [%.1f%%] %d/%d frames, %.2f fps, %.2f kb/s, eta %d:%02d:%02d", 100. * frameNum / framesToBeEncoded, frameNum, framesToBeEncoded, fps, bitrate, eta / 3600, (eta / 60) % 60, eta % 60); } else sprintf(buf, "x265 %d frames: %.2f fps, %.2f kb/s", frameNum, fps, bitrate); fprintf(stderr, "%s \r", buf + 5); SetConsoleTitle(buf); fflush(stderr); // needed in windows prevUpdateTime = time;}/* 解释命令行参数 */bool CLIOptions::parse(int argc, char **argv){ bool bError = false; int bShowHelp = false; int inputBitDepth = 8; int outputBitDepth = 0; int reconFileBitDepth = 0; const char *inputfn = NULL; // 输入文件名 const char *reconfn = NULL; // 重构文件名 const char *outputfn = NULL; // 输出文件名 const char *preset = NULL; const char *tune = NULL; const char *profile = NULL; if (argc <= 1) { x265_log(NULL, X265_LOG_ERROR, "No input file. Run x265 --help for a list of options.\n"); return true; } /* Presets are applied before all other options. */ for (optind = 0;; ) { int c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) // 选项结束或错误,退出循环 break; else if (c == 'p') preset = optarg; else if (c == 't') tune = optarg; else if (c == 'D') outputBitDepth = atoi(optarg); else if (c == 'P') profile = optarg; else if (c == '?') bShowHelp = true; } if (!outputBitDepth && profile) { /* try to derive the output bit depth from the requested profile */ if (strstr(profile, "10")) outputBitDepth = 10; else if (strstr(profile, "12")) outputBitDepth = 12; else outputBitDepth = 8; } api = x265_api_get(outputBitDepth); if (!api) { x265_log(NULL, X265_LOG_WARNING, "falling back to default bit-depth\n"); api = x265_api_get(0); } param = api->param_alloc(); if (!param) { x265_log(NULL, X265_LOG_ERROR, "param alloc failed\n"); return true; } if (api->param_default_preset(param, preset, tune) < 0) { x265_log(NULL, X265_LOG_ERROR, "preset or tune unrecognized\n"); return true; } if (bShowHelp) { printVersion(param, api); showHelp(param); } for (optind = 0;; ) { int long_options_index = -1; int c = getopt_long(argc, argv, short_options, long_options, &long_options_index); if (c == -1) break; switch (c) { case 'h': printVersion(param, api); showHelp(param); break; case 'V': printVersion(param, api); x265_report_simd(param); exit(0); default: if (long_options_index < 0 && c > 0) { for (size_t i = 0; i < sizeof(long_options) / sizeof(long_options[0]); i++) { if (long_options[i].val == c) { long_options_index = (int)i; break; } } if (long_options_index < 0) { /* getopt_long might have already printed an error message */ if (c != 63) x265_log(NULL, X265_LOG_WARNING, "internal error: short option '%c' has no long option\n", c); return true; } } ... ... } ... ...}
1 0
- x265源码分析:main函数及CLIOptions结构体解释
- x265源码分析:SAO 函数总结及逻辑关系图
- redis源码分析----main函数
- X265代码学习之main()函数
- MySQL源码分析及核心内幕之4 -- 源码服务端main函数开始及启动流程
- MySQL源码分析及核心内幕之4 -- 源码服务端main函数开始及启动流程
- Main函数之结构体变量数组CmdTip[]分析
- squid的main函数源码分析
- Squid的main函数源码分析
- Appium源码分析(2)-main函数
- RTMPDump源码分析-main函数(1)
- webbench源码分析之main函数
- FreeSWITCH源码分析之主函数main()
- x264_param_t结构体解释,设置及对应函数位置
- main() 函数解释
- main函数及ISR init分析
- x265探索与研究(六):main()函数
- x264源码分析一:main函数和encode函数
- 网易2017校园招聘笔试题:优雅的点
- 【Day3】如何防止表单重复提交?
- 安卓开发之ScrollView嵌套ListView的一些问题和解决
- React实战-通过ReactRouter-example分析Router用法
- hdu5758 2016 Multi-University Training Contest 3 Explorer Bo 解题报告
- x265源码分析:main函数及CLIOptions结构体解释
- 【华为OJ】查找兄弟单词(未通过)
- NOIP提高组 【JZOJ4778】数列编辑器
- 学习日记20160915
- maze
- HTML5 <canvas> 参考手册
- Hust oj 1949 寻找宝藏(BFS)
- 探讨C++中对象的“浅拷贝”与“深拷贝”
- VS2015使用小技巧 使用键盘home键快速到达一行代码的最前端