HEVC学习:HM-10.1-dev代码分析之TLibVideoIO库

来源:互联网 发布:四川农业大学网络继续 编辑:程序博客网 时间:2024/05/07 16:35

HEVC学习:HM-10.1-dev代码分析之TLibVideoIO库

    视频输入输出库涉及编解码过程的起始和结束操作,即编码开始的时候读取视频文件数据,解码结束的时候写视频文件数据。

    在HEVC中视频文件即为yuv文件,yuv文件中图像格式为YUV420格式。视频文件数据的操作是HEVC编解码中最基本的操作过程之一,也是学习、分析、理解和调试HM代码的重要一环,下面来分析一下VideoIO库的基本操作。

    VideoIO库包含一个头文件TVideoIOYuv.h和一个实现文件TVideoIOYuv.cpp

TVideoIOYuv.h私有成员变量fstream   m_cHandle;//文件流句柄Int m_fileBitDepthY;//文件位深Y,即文件中图像亮度Y分量数据的位深度,一般为8或10位Int m_fileBitDepthC;//文件位深C,即文件中图像色度C分量数据的位深度,一般为8或10位Int m_bitDepthShiftY;//位深移动Y,即图像Y分量要改变的数据位深度,正表示增加,负表示减少Int m_bitDepthShiftC;//位深移动Y,即图像C分量要改变的数据位深度,正表示增加,负表示减少公共成员函数TVideoIOYuv();                     //构造函数virtual ~TVideoIOYuv();            //虚构函数Void open();                       //打开视频文件Void close();                      //关闭视频文件void skipFrames();                 //跳过数帧图像Bool read();                       //读视频文件Bool write();                      //写视频文件Bool isEof();                      //判断文件结束Bool isFail();                     //判断操作失败TVideoIOYuv.cpp全局函数/** * Perform division with rounding of all pixels in img by * 2<sup>shiftbits</sup>. All pixels are clipped to [minval, maxval] * * @param img        pointer to image to be transformed * @param stride     distance between vertically adjacent pixels of img. * @param width      width of active area in img. * @param height     height of active area in img. * @param shiftbits  number of rounding bits * @param minval     minimum clipping value * @param maxval     maximum clipping value */说明:反向缩放图像面函数,即缩小图像的某个分量像素值参数:  Pel* img:                 图像数据指针,Pel为16位Short类型  UInt stride:               图像行偏移量,即从前一行开头与后一行开头之间的间距  UInt width:                图像宽度  UInt height:               图像高度  UInt shiftbits:           移动位数,即在原来图像数据位数上调整的位数   Pel minval:                图像像素最小值  Pel maxval:                图像像素最小值static void invScalePlane(Pel* img, UInt stride, UInt width, UInt height,UInt shiftbits, Pel minval, Pel maxval){    Pel offset = 1 << (shiftbits-1);//求像素值调整量    for (UInt y = 0; y < height; y++)//遍历图像高度    {        for (UInt x = 0; x < width; x++)         //遍历图像宽度         {            Pel val = (img[x] + offset) >> shiftbits;//求缩小后像素值              img[x] = Clip3(minval, maxval, val);//限制像素值的范围         }        img += stride;//移动图像数据指针到下一行    }}/** * Multiply all pixels in img by 2<sup>shiftbits</sup>. * * @param img        pointer to image to be transformed * @param stride     distance between vertically adjacent pixels of img. * @param width      width of active area in img. * @param height     height of active area in img. * @param shiftbits  number of bits to shift */说明:正向缩放图像,即放大图像的某个分量像素值参数:  Pel* img:                图像数据指针  UInt stride:             图像行偏移量,即从前一行开头与后一行开头之间的间距  UInt width:              图像宽度  UInt height:             图像高度  UInt shiftbits:          移动位数,即在原来图像数据位数上调整的位数static void scalePlane(Pel* img, UInt stride, UInt width, UInt height,                       UInt shiftbits){  for (UInt y = 0; y < height; y++)         //遍历图像高度  {    for (UInt x = 0; x < width; x++)        //遍历图像宽度    {      img[x] <<= shiftbits;                 //放大像素值    }    img += stride;                          //数据指针移动到下一行  }}/*** Scale all pixels in img depending upon sign of shiftbits by a factor of* 2<sup>shiftbits</sup>.** @param img        pointer to image to be transformed* @param stride  distance between vertically adjacent pixels of img.* @param width   width of active area in img.* @param height  height of active area in img.* @param shiftbits if zero, no operation performed*                  if > 0, multiply by 2<sup>shiftbits</sup>, see scalePlane()*                  if < 0, divide and round by 2<sup>shiftbits</sup> and clip,*                          see invScalePlane().* @param minval  minimum clipping value when dividing.* @param maxval  maximum clipping value when dividing. */说明:缩放图像,即放大或者缩小图像的某个分量像素值参数:  Pel* img:                图像数据指针,Pel为16位Short类型  UInt stride:              图像数据行间隔,即从前一行开头与后一行开头之间的间距  UInt width:               图像宽度  UInt height:              图像高度  Int shiftbits:            移动位数,即在原来图像数据位数上调整的位数,正表示放大,负表示缩小  Pel minval:               图像像素最小值  Pel maxval:               图像像素最小值static void scalePlane(Pel* img, UInt stride, UInt width, UInt height,                       Int shiftbits, Pel minval, Pel maxval){  if (shiftbits == 0)              //移动位数为0,即不需要作缩放处理  {    return;  }  if (shiftbits > 0)               //移动位数大于0,即需要作放大处理  {    scalePlane(img, stride, width, height, shiftbits);  }  else                             //移动位数小于0,即需要作缩小处理  {    invScalePlane(img, stride, width, height, -shiftbits, minval, maxval);  }}类成员函数// ====================================================================================================================// Public member functions// ====================================================================================================================/** * Open file for reading/writing Y'CbCr frames. * * Frames read/written have bitdepth fileBitDepth, and are automatically * formatted as 8 or 16 bit word values (see TVideoIOYuv::write()). * * Image data read or written is converted to/from internalBitDepth * (See scalePlane(), TVideoIOYuv::read() and TVideoIOYuv::write() for * further details). * * \param pchFile          file name string * \param bWriteMode       file open mode: true=read, false=write * \param fileBitDepthY     bit-depth of input/output file data (luma component). * \param fileBitDepthC     bit-depth of input/output file data (chroma components). * \param internalBitDepthY bit-depth to scale image data to/from when reading/writing (luma component). * \param internalBitDepthC bit-depth to scale image data to/from when reading/writing (chroma components). */说明:为数据帧读写打开yuv文件参数:  Char* pchFile:                   文件名字  Bool bWriteMode:                 读写模式  Int fileBitDepthY:               文件数据位深度Y  Int fileBitDepthC:               文件数据位深度C  Int internalBitDepthY:           内部(程序中)数据位深度Y  Int internalBitDepthC:           内部(程序中)位数据深度CVoid TVideoIOYuv::open( Char* pchFile, Bool bWriteMode, Int fileBitDepthY, Int fileBitDepthC, Int internalBitDepthY, Int internalBitDepthC){  m_bitDepthShiftY = internalBitDepthY - fileBitDepthY;      //Y分量位移动深度(位数)  m_bitDepthShiftC = internalBitDepthC - fileBitDepthC;      //C分量位移动深度(位数)  m_fileBitDepthY = fileBitDepthY;                          //类成员(文件Y分量位深)赋值  m_fileBitDepthC = fileBitDepthC;                           //类成员(文件C分量位深)赋值  if ( bWriteMode )                                          //判断为写模式  {    m_cHandle.open( pchFile, ios::binary | ios::out );       //fstream以输出模式打开文件        if( m_cHandle.fail() )                                   //打开失败    {      printf("\nfailed to write reconstructed YUV file\n");      exit(0);    }  }  else                                                       //判断为读模式  {    m_cHandle.open( pchFile, ios::binary | ios::in );        //fstream以输入模式打开文件        if( m_cHandle.fail() )                                   //打开失败    {      printf("\nfailed to open Input YUV file\n");      exit(0);    }  }    return;}说明:关闭文件Void TVideoIOYuv::close(){  m_cHandle.close();                //由fstream函数完成文件关闭}说明:判断文件结束Bool TVideoIOYuv::isEof(){  return m_cHandle.eof();         //由fstream函数完成判断文件结束}说明:判断文件出错Bool TVideoIOYuv::isFail(){  return m_cHandle.fail();         //由fstream函数完成判断文件出错}/** * Skip numFrames in input. * * This function correctly handles cases where the input file is not * seekable, by consuming bytes. */说明:在输入文件中跳过数帧图像参数:  UInt numFrames:                  跳过的帧数  UInt width:                       图像宽度  UInt height:                      图像高度void TVideoIOYuv::skipFrames(UInt numFrames, UInt width, UInt height){  if (!numFrames)                //帧数为0,即不作跳跃操作    return;  const UInt wordsize = (m_fileBitDepthY > 8 || m_fileBitDepthC > 8) ? 2 : 1;  //字节长度  const streamoff framesize = wordsize * width * height * 3 / 2;               //YUV420图像帧大小  const streamoff offset = framesize * numFrames;                              //跳跃的偏移量  /* attempt to seek */  if (!!m_cHandle.seekg(offset, ios::cur))  //直接跳过offset    return; /* success */  m_cHandle.clear();                        //直接操作识别,清除stream,然后通过读方式来实现跳跃  /* fall back to consuming the input */  Char buf[512];                            //定义读stream缓冲区  const UInt offset_mod_bufsize = offset % sizeof(buf);                     //偏移量对缓冲区取模  for (streamoff i = 0; i < offset - offset_mod_bufsize; i += sizeof(buf))  //遍历跳跃帧  {    m_cHandle.read(buf, sizeof(buf));       //读到缓冲区  }  m_cHandle.read(buf, offset_mod_bufsize);  //读剩余量}/** * Read width*height pixels from fd into dst, optionally * padding the left and right edges by edge-extension.  Input may be * either 8bit or 16bit little-endian lsb-aligned words. * * @param dst     destination image * @param fd      input file stream * @param is16bit true if input file carries > 8bit data, false otherwise. * @param stride  distance between vertically adjacent pixels of dst. * @param width   width of active area in dst. * @param height  height of active area in dst. * @param pad_x   length of horizontal padding. * @param pad_y   length of vertical padding. * @return true for success, false in case of error */说明:从文件读图像某个分量数据参数:  Pel* dst:                目的数据指针  istream& fd:             输入数据流  Bool is16bit:            是否16位  UInt stride:             图像行偏移量  UInt width:              图像宽度  UInt height:             图像高度  UInt pad_x:              x对齐偏移量  UInt pad_y:              y对齐偏移量static Bool readPlane(Pel* dst, istream& fd, Bool is16bit,                      UInt stride,                      UInt width, UInt height,                      UInt pad_x, UInt pad_y){  Int read_len = width * (is16bit ? 2 : 1);                  //单次读取数据量  UChar *buf = new UChar[read_len];                           //申请一行图像缓冲区  for (Int y = 0; y < height; y++)                            //图像行遍历  {    fd.read(reinterpret_cast<Char*>(buf), read_len);         //从文件读一行图像数据    if (fd.eof() || fd.fail() )                               //判断文件结束和操作失败    {      delete[] buf;                                           //释放缓存      return false;    }    if (!is16bit)                                            //图像数据为非16位即8位单字节    {      for (Int x = 0; x < width; x++)                         //遍历宽度      {        dst[x] = buf[x];                                      //直接赋值给目的缓冲区      }    }    else                                                     //图像数据为非16位即双字节    {      for (Int x = 0; x < width; x++)//遍历宽度      {        dst[x] = (buf[2*x+1] << 8) | buf[2*x];                //双字节拼接后赋给目的缓冲区      }    }    for (Int x = width; x < width + pad_x; x++)               //对齐位置处理    {      dst[x] = dst[width - 1];                                //直接向左边界扩展    }    dst += stride;                                            //目的数据指针移动到下一行  }  for (Int y = height; y < height + pad_y; y++)               //处理高度方向对齐,即最后几行  {    for (Int x = 0; x < width + pad_x; x++)                  //遍历对齐行的对齐列    {      dst[x] = (dst - stride)[x];                             //直接向下边界扩展    }    dst += stride;                                            //移动目的数据指针到下一行  }  delete[] buf;                                               //释放缓冲区  return true;}/** * Write width*height pixels info fd from src. * * @param fd      output file stream * @param src     source image * @param is16bit true if input file carries > 8bit data, false otherwise. * @param stride  distance between vertically adjacent pixels of src. * @param width   width of active area in src. * @param height  height of active area in src. * @return true for success, false in case of error */说明:写图像某个分量数据到文件参数:  ostream& fd:             输出数据流  Pel* src:               图像源数据指针  Bool is16bit:            是否16位  UInt stride:             图像行偏移量  UInt width:              图像宽度  UInt height:             图像高度static Bool writePlane(ostream& fd, Pel* src, Bool is16bit,                       UInt stride,                       UInt width, UInt height){  Int write_len = width * (is16bit ? 2 : 1);  //写一行数据量  UChar *buf = new UChar[write_len];          //申请一行图像缓冲区  for (Int y = 0; y < height; y++)            //遍历图像行  {    if (!is16bit)                            //单字节数据    {      for (Int x = 0; x < width; x++)        //遍历图像列      {        buf[x] = (UChar) src[x];             //直接取数据低字节      }    }    else                                     //双字节数据    {      for (Int x = 0; x < width; x++)        //遍历图像列      {        buf[2*x] = src[x] & 0xff;            //取低字节        buf[2*x+1] = (src[x] >> 8) & 0xff;    //取高字节      }    }    fd.write(reinterpret_cast<Char*>(buf), write_len);   //写一行图像到文件    if (fd.eof() || fd.fail() )                           //判断文件结束和操作失败    {      delete[] buf;                //释放缓冲区      return false;    }    src += stride;                 //图像数据源指针移动到下一行  }  delete[] buf;                    //释放缓冲区  return true;}/** * Read one Y'CbCr frame, performing any required input scaling to change * from the bitdepth of the input file to the internal bit-depth. * * If a bit-depth reduction is required, and internalBitdepth >= 8, then * the input file is assumed to be ITU-R BT.601/709 compliant, and the * resulting data is clipped to the appropriate legal range, as if the * file had been provided at the lower-bitdepth compliant to Rec601/709. * * @param pPicYuv      input picture YUV buffer class pointer * @param aiPad        source padding size, aiPad[0] = horizontal, aiPad[1] = vertical * @return true for success, false in case of error */说明:读一帧图像数据,由读取一个Y分量和两个C分量实现参数:  TComPicYuv*  pPicYuv:             输入图像指针  Int aiPad[2]:                      图像对齐参数Bool TVideoIOYuv::read ( TComPicYuv*  pPicYuv, Int aiPad[2] ){  // check end-of-file  if ( isEof() ) return false;                       //判断是否文件结束    Int   iStride = pPicYuv->getStride();              //获取图像行偏移量    // compute actual YUV width & height excluding padding size  UInt pad_h = aiPad[0];                             //获取水平(列)对齐填充量  UInt pad_v = aiPad[1];                             //获取竖直(行)对齐填充量  UInt width_full = pPicYuv->getWidth();             //获取图像完全宽度  UInt height_full = pPicYuv->getHeight();           //获取图像完全高度  UInt width  = width_full - pad_h;                 //有效图像宽度  UInt height = height_full - pad_v;                 //有效图像高度  Bool is16bit = m_fileBitDepthY > 8 || m_fileBitDepthC > 8;   //是否需要双字节表示  Int desired_bitdepthY = m_fileBitDepthY + m_bitDepthShiftY;  //期望位深Y=文件位深+移动  Int desired_bitdepthC = m_fileBitDepthC + m_bitDepthShiftC;  //期望位深C=文件位深+移动  Pel minvalY = 0;                                   //Y分量像素最小值  Pel minvalC = 0;                                   //C分量像素最小值  Pel maxvalY = (1 << desired_bitdepthY) - 1;        //Y分量像素最大值  Pel maxvalC = (1 << desired_bitdepthC) - 1;        //C分量像素最大值#if CLIP_TO_709_RANGE  if (m_bitdepthShiftY < 0 && desired_bitdepthY >= 8)          //Y分量缩小处理  {    /* ITU-R BT.709 compliant clipping for converting say 10b to 8b */    minvalY = 1 << (desired_bitdepthY - 8);                   //Y最小值    maxvalY = (0xff << (desired_bitdepthY - 8)) -1;            //Y最大值  }  if (m_bitdepthShiftC < 0 && desired_bitdepthC >= 8)          //C分量缩小处理  {    /* ITU-R BT.709 compliant clipping for converting say 10b to 8b */  minvalC = 1 << (desired_bitdepthC - 8);             //Y最小值  maxvalC = (0xff << (desired_bitdepthC - 8)) -1;     //Y最大值  }#endif    if (! readPlane(pPicYuv->getLumaAddr(), m_cHandle, is16bit, iStride, width, height, pad_h, pad_v))//读取单帧图像中一个Y分量数据    return false;//读取失败返回  scalePlane(pPicYuv->getLumaAddr(), iStride, width_full, height_full, m_bitDepthShiftY, minvalY, maxvalY); //对Y分量进行缩放处理  iStride >>= 1;//C分量行偏移(420采样)  width_full >>= 1;//C分量图像完全宽度  height_full >>= 1;//C分量图像完全高度  width >>= 1;//C分量图像有效宽度  height >>= 1;//C分量图像有效高度  pad_h >>= 1;//C分量水平(列)填充量  pad_v >>= 1;//C分量竖直(行)填充量  if (! readPlane(pPicYuv->getCbAddr(), m_cHandle, is16bit, iStride, width, height, pad_h, pad_v))  //读取一个C分量(Cb)全部数据    return false;//失败返回  scalePlane(pPicYuv->getCbAddr(), iStride, width_full, height_full, m_bitDepthShiftC, minvalC, maxvalC); //对C分量进行缩放处理  if (! readPlane(pPicYuv->getCrAddr(), m_cHandle, is16bit, iStride, width, height, pad_h, pad_v))  //读取下一个C分量(Cr)全部数据    return false;//失败返回  scalePlane(pPicYuv->getCrAddr(), iStride, width_full, height_full, m_bitDepthShiftC, minvalC, maxvalC); //对C分量进行缩放处理  return true;}/** * Write one Y'CbCr frame. No bit-depth conversion is performed, pcPicYuv is * assumed to be at TVideoIO::m_fileBitdepth depth. * * @param pPicYuv     input picture YUV buffer class pointer * @param aiPad       source padding size, aiPad[0] = horizontal, aiPad[1] = vertical * @return true for success, false in case of error */说明:写一帧图像数据,由写一个Y分量和两个C分量实现参数:  TComPicYuv* pPicYuv:              输出图像指针  Int confLeft:                      左边界偏移  Int confRight:                     右边界偏移  Int confTop:                       上边界偏移  Int confBottom:                    下边界偏移Bool TVideoIOYuv::write( TComPicYuv* pPicYuv, Int confLeft, Int confRight, Int confTop, Int confBottom ){  // compute actual YUV frame size excluding padding size  Int   iStride = pPicYuv->getStride();                        //图像行偏移(Y分量)  UInt  width  = pPicYuv->getWidth()  - confLeft - confRight;  //有效图像宽度(去掉边界)  UInt  height = pPicYuv->getHeight() - confTop  - confBottom; //有效图像高度(去掉边界)  Bool is16bit = m_fileBitDepthY > 8 || m_fileBitDepthC > 8;   //判断是否需要双字节表示  TComPicYuv *dstPicYuv = NULL;    //定义目的图像指针  Bool retval = true;              //返回值  if (m_bitDepthShiftY != 0 || m_bitDepthShiftC != 0)          //位深移动不为0,需要位深处理  {    dstPicYuv = new TComPicYuv;    //申请目的图像    dstPicYuv->create( pPicYuv->getWidth(), pPicYuv->getHeight(), 1, 1, 0 );//创建缓冲区    pPicYuv->copyToPic(dstPicYuv);  //图像拷贝到目的图像    Pel minvalY = 0;                //Y分量像素最小值    Pel minvalC = 0;                //C分量像素最小值    Pel maxvalY = (1 << m_fileBitDepthY) - 1;         //Y分量像素最大值    Pel maxvalC = (1 << m_fileBitDepthC) - 1;         //C分量像素最大值#if CLIP_TO_709_RANGE    if (-m_bitDepthShiftY < 0 && m_fileBitDepthY >= 8)   //Y分量需要做位深处理    {      /* ITU-R BT.709 compliant clipping for converting say 10b to 8b */      minvalY = 1 << (m_fileBitDepthY - 8);              //Y分量像素最小值      maxvalY = (0xff << (m_fileBitDepthY - 8)) -1;        //Y分量像素最大值    }    if (-m_bitDepthShiftC < 0 && m_fileBitDepthC >= 8)    //C分量需要做位深处理    {      /* ITU-R BT.709 compliant clipping for converting say 10b to 8b */minvalC = 1 << (m_fileBitDepthC - 8);               //C分量像素最小值maxvalC = (0xff << (m_fileBitDepthC - 8)) -1;       //C分量像素最大值    }#endif    scalePlane(dstPicYuv->getLumaAddr(), dstPicYuv->getStride(), dstPicYuv->getWidth(), dstPicYuv->getHeight(), -m_bitDepthShiftY, minvalY, maxvalY);        //Y分量缩放处理    scalePlane(dstPicYuv->getCbAddr(), dstPicYuv->getCStride(), dstPicYuv->getWidth()>>1, dstPicYuv->getHeight()>>1, -m_bitDepthShiftC, minvalC, maxvalC);        //Cb分量缩放处理    scalePlane(dstPicYuv->getCrAddr(), dstPicYuv->getCStride(), dstPicYuv->getWidth()>>1, dstPicYuv->getHeight()>>1, -m_bitDepthShiftC, minvalC, maxvalC);        //Cr分量缩放处理  }  else                             //不需要做缩放处理  {    dstPicYuv = pPicYuv;           //直接获取图像  }  // location of upper left pel in a plane  Int planeOffset = confLeft + confTop * iStride;      //Y分量偏移    if (! writePlane(m_cHandle, dstPicYuv->getLumaAddr() + planeOffset, is16bit, iStride, width, height))  //写图像L分量数据到文件  {    retval=false;                 //失败退出    goto exit;  }  width >>= 1;//图像C分量有效宽度(420)  height >>= 1;//图像C分量有效高度  iStride >>= 1;//图像C分量行偏移  confLeft >>= 1;//图像C分量左边距  confRight >>= 1;//图像C分量右边距  confTop >>= 1;//图像C分量上边距  confBottom >>= 1;//图像C分量下边距  planeOffset = confLeft + confTop * iStride;        //C分量偏移  if (! writePlane(m_cHandle, dstPicYuv->getCbAddr() + planeOffset, is16bit, iStride, width, height)) //写图像Cb分量数据到文件  {  retval=false; //失败退出  goto exit;  }  if (! writePlane(m_cHandle, dstPicYuv->getCrAddr() + planeOffset, is16bit, iStride, width, height)) //写图像Cr分量数据到文件  {  retval=false; //失败退出  goto exit;  }  exit:  if (m_bitDepthShiftY != 0 || m_bitDepthShiftC != 0)  //做过位深处理  {      dstPicYuv->destroy();                       //销毁图像       delete dstPicYuv;                           //释放缓冲区  }    return retval;}