[x265] [PATCH 4 of 4] cli: add cli options analysis-out and analysis-in

Steve Borho steve at borho.org
Wed Sep 10 15:54:01 CEST 2014


On 09/10, sagar at multicorewareinc.com wrote:
> # HG changeset patch
> # User Sagar Kotecha <sagar at multicorewareinc.com>
> # Date 1410353329 -19800
> #      Wed Sep 10 18:18:49 2014 +0530
> # Node ID f313d0bb2c75799cf7d180950fe63b94d59c175b
> # Parent  44004db9861ae68799243fd4f2e395c216f2bb36
> cli: add cli options analysis-out and analysis-in
> 
> analysis-out - dump the intra/inter information into file
> analysis-in -  read the intra/inter information from file
> 
> diff -r 44004db9861a -r f313d0bb2c75 doc/reST/cli.rst
> --- a/doc/reST/cli.rst	Wed Sep 10 17:37:40 2014 +0530
> +++ b/doc/reST/cli.rst	Wed Sep 10 18:18:49 2014 +0530
> @@ -918,6 +918,17 @@
>  	* :option:`--subme` = MIN(2, :option:`--subme`)
>  	* :option:`--rd` = MIN(2, :option:`--rd`)

api.rst also needs updating, since you are adding new public functions.
you need to add a new section describing how the new functions can be
used to save and re-use analysis data.

> +.. option:: --analysis-out, --no-analysis-out
> +
> +	The analysis information of each frame is dumped into analysis.txt file.
> +	I frames   - For each CU split decisions and luma intra directions are dumped.
> +	P/B frames - For each CU motion vectors are dumped for every depth.
> +	
> +.. option:: --analysis-in, --no-analysis-in	
> +
> +	The analysis information of each frame is read into buffers from analysis.txt file.
> +	This information is forced to use for encoding same clip with diffrent bitrate.
> +
>  Loop filters
>  ============
>  
> diff -r 44004db9861a -r f313d0bb2c75 source/common/param.cpp
> --- a/source/common/param.cpp	Wed Sep 10 17:37:40 2014 +0530
> +++ b/source/common/param.cpp	Wed Sep 10 18:18:49 2014 +0530
> @@ -732,6 +732,8 @@
>          p->rc.bStatRead = pass & 2;
>      }
>      OPT("stats") p->rc.statFileName = strdup(value);
> +    OPT("analysis-out") p->bAnalysisDataOut = atobool(value);
> +    OPT("analysis-in") p->bAnalysisDataIn = atobool(value);
>      else
>          return X265_PARAM_BAD_NAME;
>  #undef OPT
> diff -r 44004db9861a -r f313d0bb2c75 source/x265.cpp
> --- a/source/x265.cpp	Wed Sep 10 17:37:40 2014 +0530
> +++ b/source/x265.cpp	Wed Sep 10 18:18:49 2014 +0530
> @@ -202,6 +202,10 @@
>      { "pass",           required_argument, NULL, 0 },
>      { "slow-firstpass",       no_argument, NULL, 0 },
>      { "no-slow-firstpass",    no_argument, NULL, 0 },
> +    { "analysis-out",         no_argument, NULL, 0 },
> +    { "analysis-in",          no_argument, NULL, 0 },
> +    { "no-analysis-out",      no_argument, NULL, 0 },
> +    { "no-analysis-in",       no_argument, NULL, 0 },
>      { 0, 0, 0, 0 }
>  };
>  
> @@ -229,6 +233,7 @@
>      int64_t prevUpdateTime;
>      float   frameRate;
>      FILE*   qpfile;
> +    FILE*   analysisFile;
>  
>      /* in microseconds */
>      static const int UPDATE_INTERVAL = 250000;
> @@ -245,6 +250,7 @@
>          prevUpdateTime = 0;
>          bDither = false;
>          qpfile = NULL;
> +        analysisFile = NULL;
>      }
>  
>      void destroy();
> @@ -254,6 +260,8 @@
>      void showHelp(x265_param *param);
>      bool parse(int argc, char **argv, x265_param* param);
>      bool parseQPFile(x265_picture &pic_org);
> +    void readAnalysisFile(x265_picture* pic, x265_param*);
> +    void writeAnalysisFile(x265_picture* pic, x265_param*);
>  };
>  
>  void CLIOptions::destroy()
> @@ -267,6 +275,9 @@
>      if (qpfile)
>          fclose(qpfile);
>      qpfile = NULL;
> +    if (analysisFile)
> +        fclose(analysisFile);
> +    analysisFile = NULL;
>  }
>  
>  void CLIOptions::writeNALs(const x265_nal* nal, uint32_t nalcount)
> @@ -432,6 +443,9 @@
>         "                                   - 2 : Last pass, does not overwrite stats file\n"
>         "                                   - 3 : Nth pass, overwrites stats file\n");
>      H0("   --[no-]slow-firstpass         Enable a slow first pass in a multipass rate control mode. Default %s\n", OPT(param->rc.bEnableSlowFirstPass));
> +    H0("   --analysis-out                Dump analysis info into analysis.txt file.\n");
> +    H0("                                 Analysis info can be used to encode for diffrent bitrate. Default %d\n", OPT(param->bAnalysisDataOut));
> +    H0("   --analysis-in                 Read analysis info from analysis.txt file. Default %d\n", OPT(param->bAnalysisDataIn));
>      H0("   --scaling-list <string>       Specify a file containing HM style quant scaling lists or 'default' or 'off'. Default: off\n");
>      H0("   --lambda-file <string>        Specify a file containing replacement values for the lambda tables\n");
>      H0("                                 MAX_MAX_QP+1 floats for lambda table, then again for lambda2 table\n");
> @@ -725,9 +739,86 @@
>          x265_log(NULL, X265_LOG_ERROR, "failed to open bitstream file <%s> for writing\n", bitstreamfn);
>          return true;
>      }

and here we get to the last sticking point.  In any non-trivial
environment the user is going to need to provide a filename for the
analysis data.  It's a problem, but it also gives us a chance to make
the UI a bit less error prone. I suggest the following:

in x265.h:

#define X265_ANALYSIS_OFF  0
#define X265_ANALYSIS_SAVE 1
#define X265_ANALYSIS_LOAD 2
static const char * const x265_analysis_mode[] = { "off", "save", "load" };
int x265_param.analysisMode;

in x265.cpp:

 { "analysis-mode",  required_argument, NULL, 0 },
 { "analysis-file",  required_argument, NULL, 0 },

--analysis-mode off|save|load is handled by x265_param_parse and parseName
--analysis-file <filename> is handled by x265.cpp itself like --output

Now it's impossible to configure the encoder to load and save at the
same time, and we can over-ride the default filename.

> +    if (param->bAnalysisDataIn)
> +    {
> +        this->analysisFile = fopen("analysis.txt", "rb");
> +        if (!this->analysisFile)
> +        {
> +            x265_log(NULL, X265_LOG_ERROR, "failed to open analysis file analysis.txt for reading\n");
> +            return true;
> +        }
> +    }
> +
> +    if (param->bAnalysisDataOut)
> +    {
> +        this->analysisFile = fopen("analysis.txt", "wb");
> +        if (!this->analysisFile)
> +        {
> +            x265_log(NULL, X265_LOG_ERROR, "failed to open analysis file analysis.txt for writing\n");
> +            return true;
> +        }
> +    }
> +
>      return false;
>  }
>  
> +void CLIOptions::readAnalysisFile(x265_picture* pic, x265_param* p)
> +{
> +    int poc, width, height;
> +    uint32_t numPart, numCU;
> +    fread(&width, sizeof(int), 1, this->analysisFile);
> +    fread(&height, sizeof(int), 1, this->analysisFile);
> +    fread(&poc, sizeof(int), 1, this->analysisFile);
> +    fread(&pic->sliceType, sizeof(int), 1, this->analysisFile);
> +    fread(&numCU, sizeof(int), 1, this->analysisFile);
> +    fread(&numPart, sizeof(int), 1, this->analysisFile);
> +
> +    if (poc != pic->poc || width != p->sourceWidth || height != p->sourceHeight)
> +    {
> +        x265_log(NULL, X265_LOG_WARNING, "Error in reading intra-inter data.\n");
> +        x265_free_analysis_data(pic);
> +        return;
> +    }
> +
> +    fread(pic->analysisData.intraData->depth,
> +        sizeof(uint8_t), pic->analysisData.numPartitions * pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fread(pic->analysisData.intraData->modes,
> +        sizeof(uint8_t), pic->analysisData.numPartitions * pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fread(pic->analysisData.intraData->partSizes,
> +        sizeof(char), pic->analysisData.numPartitions * pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fread(pic->analysisData.intraData->poc,
> +        sizeof(int), pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fread(pic->analysisData.intraData->cuAddr,
> +        sizeof(uint32_t), pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fread(pic->analysisData.interData, sizeof(x265_inter_data), pic->analysisData.numCUsInFrame * 85, this->analysisFile);
> +}
> +
> +void CLIOptions::writeAnalysisFile(x265_picture* pic, x265_param *p)
> +{
> +    uint32_t numPart = pic->analysisData.numPartitions, numCU = pic->analysisData.numCUsInFrame;
> +    uint32_t seekTo = pic->poc * ((sizeof(int) * 4 + sizeof(uint32_t) * 2) + sizeof(x265_inter_data) * numCU * 85 +
> +        sizeof(uint8_t) * 2 * numPart * numCU + sizeof(char) * numPart * numCU + sizeof(int) * numCU  + sizeof(uint32_t) * numCU);

the size of each record should be calculated once and stored in
CLIOptions.  uint32_t is probably not going to be enough, fpos_t would
be better.

it would be a lot more robust if a header was stored in the file, like
our 2-pass stats file, with param2string(), to make sure none of the key
parameters were changed between fanout encoders. the length of this
header would need to be added to each seek offset.

> +    fseek(this->analysisFile, seekTo, SEEK_SET);
> +    fwrite(&p->sourceWidth, sizeof(int), 1, this->analysisFile);
> +    fwrite(&p->sourceHeight, sizeof(int), 1, this->analysisFile);
> +    fwrite(&pic->poc, sizeof(int), 1, this->analysisFile);
> +    fwrite(&pic->sliceType, sizeof(int), 1, this->analysisFile);
> +    fwrite(&pic->analysisData.numCUsInFrame, sizeof(int), 1, this->analysisFile);
> +    fwrite(&pic->analysisData.numPartitions, sizeof(int), 1, this->analysisFile);
> +
> +    fwrite(pic->analysisData.intraData->depth,
> +        sizeof(uint8_t), pic->analysisData.numPartitions * pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fwrite(pic->analysisData.intraData->modes,
> +        sizeof(uint8_t), pic->analysisData.numPartitions * pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fwrite(pic->analysisData.intraData->partSizes,
> +        sizeof(char), pic->analysisData.numPartitions * pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fwrite(pic->analysisData.intraData->poc, sizeof(int), pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fwrite(pic->analysisData.intraData->cuAddr, sizeof(uint32_t), pic->analysisData.numCUsInFrame, this->analysisFile);
> +    fwrite(pic->analysisData.interData, sizeof(x265_inter_data), pic->analysisData.numCUsInFrame * 85, this->analysisFile);
> +}
> +
>  bool CLIOptions::parseQPFile(x265_picture &pic_org)
>  {
>      int32_t num = -1, qp, ret;
> @@ -820,6 +911,13 @@
>  
>      x265_picture_init(param, pic_in);
>  
> +    if ((param->bAnalysisDataOut || param->bAnalysisDataIn) && !pic_recon)
> +    {
> +        x265_log(NULL, X265_LOG_WARNING, "Must specify recon with analysis-out or analysis-in options, Disabling analysis options\n");
> +        param->bAnalysisDataOut = false;
> +        param->bAnalysisDataIn = false;
> +    }
> +
>      if (cliopt.bDither)
>      {
>          errorBuf = X265_MALLOC(int16_t, param->sourceWidth + 1);
> @@ -856,6 +954,16 @@
>              pic_in->bitDepth = X265_DEPTH;
>          }
>  
> +        if ((param->bAnalysisDataIn || param->bAnalysisDataOut) && pic_in)
> +        {
> +            x265_alloc_analysis_data(pic_in, param);
> +
> +            if (param->bAnalysisDataIn)
> +                cliopt.readAnalysisFile(pic_in, param);
> +        }
> +        else if (param->bAnalysisDataOut && pic_in)
> +            x265_alloc_analysis_data(pic_in, param);
> +
>          int numEncoded = x265_encoder_encode(encoder, &p_nal, &nal, pic_in, pic_recon);
>          if (numEncoded < 0)
>          {
> @@ -866,6 +974,12 @@
>          if (numEncoded && pic_recon)
>          {
>              cliopt.recon->writePicture(pic_out);
> +            if (param->bAnalysisDataIn || param->bAnalysisDataOut)
> +            {
> +                if (param->bAnalysisDataOut)
> +                    cliopt.writeAnalysisFile(pic_recon, param);
> +                x265_free_analysis_data(pic_recon);
> +            }
>          }
>  
>          if (nal)
> @@ -883,6 +997,12 @@
>          if (numEncoded && pic_recon)
>          {
>              cliopt.recon->writePicture(pic_out);
> +            if (param->bAnalysisDataIn || param->bAnalysisDataOut)
> +            {
> +                if (param->bAnalysisDataOut)
> +                    cliopt.writeAnalysisFile(pic_recon, param);
> +                x265_free_analysis_data(pic_recon);
> +            }
>          }
>  
>          if (nal)
> _______________________________________________
> x265-devel mailing list
> x265-devel at videolan.org
> https://mailman.videolan.org/listinfo/x265-devel

-- 
Steve Borho


More information about the x265-devel mailing list