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
原创粉丝点击