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: 内部(程序中)位数据深度C
- Void 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;
- }
原文:http://blog.csdn.net/zhuyonghao123/article/details/9068181