用MFC构建HEVC码流播放器
HEVC的测试代码HM中给出了一个解码器示例,但是该解码器只能输出结果到yuv文件,不能跟放电影一样阅览。所以离实际应用总有一定距离,我现在刚开始学习HEVC,也想深入了解HEVC的编解码原理和具体过程,所以创建了这样一个播放器。该播放器和以前所做的一个基于ffmpeg的播放器基本框架几乎完全一样,所不同的是解码环节。
下面我们开始着手分析构建这样一个真实的播放器的流程。
一、基本流程分析
最简单的,一个播放器应该具有的基本功能是让用户选择一个文件并且播放,暂时不支持复杂的功能的问题是考虑到界面设计带来的问题(能力有限就要集中能力做主要的事情嘛)。
要想播放视频,首先必须要得到视频的每一帧图像。怎么得到一帧一帧的图像呢,我们可以借助于HM的示例代码予以分析。
我们启动和调试TAppDecoder项目,该项目中演示了一个HEVC解码器所做的基本工作。界面显示应该归于应用程序,而不是我们的解码器,所以HM中没有考虑。示例程序的解码流程如图 1所示。
1 解码器的流程图
我们需要做的工作就是,把原来解码器的解码结果输出到文件(依赖于程序的运行参数),修改成把解码的每一帧图像送给我们的应用程序,由应用程序显示每一帧图像。
稍微细致地走一遍代码,设置断点并且跟踪执行,发现了一个重要突破点,在于TAppDecoder::decode函数的末尾部分,有一个函数调用,即xWriteOutput(字面意思是写入输出,解码器要输出什么,当然是解码的结果,yuv图像啦),该函数的调用表明了一帧图像的读取完毕,程序尝试把该帧图像写入到文件。
知道这点之后,我们的工作便大致有了方向,便是修改该函数(函数框架也需要修改),改成在这里做更新界面的操作。
二、一帧图像的获取
自己再细致地阅读一下这个 TAppDecTop::xWriteOutput函数的内容,发现后来在while循环中有这么一句话。
m_cTVideoIOYuvReconFile.write( pcPic->getPicYuvRec(),crop.getPicCropLeftOffset(), crop.getPicCropRightOffset(),crop.getPicCropTopOffset(), crop.getPicCropBottomOffset() );
对该函数的流程的把握和理解可以帮助自己认识,上面这段代码完成了解码的图像输出操作,其实其变量名也很显著啦(Video IO Recon File即视频IO记录文件),再加上后面的函数名字为write可以确定是该函数完成了解码出来的图像的写文件操作。
仔细地分析,发现它传递的参数不再是TComPic类型的指针,而变成了它的成员,TComPicYuv类型的指针。再进一步跟进去看,突然发现了有 getWidth(), getHeight()之类的函数,还有copyToPic, WritePlane等等函数调用。那么,传递进来的指针应该是表示了一帧图像。
我们再看看 TComPic的函数成员,类似 getPicYuvRec()的函数还有不少,大家不妨自己看一下,我就不列举了。从代码理解上来看,这个应该就是解码器重建的图像帧(Rec是Reconstruction的缩写)。
好了,现在大体确定了如何获得一帧图像。仿照那个xWriteOutput函数写就好了,下面我们需要做的就是把pcPic->getPicYuvRec()转换成我们可以显示的图像。
三、YUV图像显示
在MFC中绘图常用的是显示RGB格式的图像,而HEVC编码解码出来的结果一般都是YUV格式的图像,关于两者的区别大家可以百度或谷歌查一查,嫌麻烦的话戳这里:
http://blog.csdn.net/chen825919148/article/details/7921475
一个YUV颜色转换成RGB颜色值的公式为:
R = Y + 1.4075 *(V-128)
G = Y – 0.3455 *(U –128) – 0.7169 *(V –128)
B = Y + 1.779 *(U – 128)
如果我们得到了一个像素的YUV值,那么通过这个公式是很容易得到该点的RGB值的,那么问题在于什么地方呢?
A)HEVC的YUV格式
综合一二中的分析我们知道了解码出来的一帧图像实际上是由 TComPicYuv 类的对象再维护,那么该类的对象中YUV是啥格式呢,不妨看看该类的声明和实现代码。
TComPicYuv::create这个函数比较关键,中间显示了指针指向内存区域的分配,可以见到这样的一段代码:
m_apiPicBufY = (Pel*)xMalloc( Pel, ( m_iPicWidth + (m_iLumaMarginX <<1)) * (m_iPicHeight + (m_iLumaMarginY <<1)));m_apiPicBufU = (Pel*)xMalloc( Pel, ((m_iPicWidth >> 1) +(m_iChromaMarginX<<1)) * ((m_iPicHeight >> 1) +(m_iChromaMarginY<<1)));m_apiPicBufV = (Pel*)xMalloc( Pel, ((m_iPicWidth >> 1) +(m_iChromaMarginX<<1)) * ((m_iPicHeight >> 1) +(m_iChromaMarginY<<1)));m_piPicOrgY = m_apiPicBufY +m_iLumaMarginY * getStride() + m_iLumaMarginX;m_piPicOrgU = m_apiPicBufU +m_iChromaMarginY * getCStride() + m_iChromaMarginX;m_piPicOrgV = m_apiPicBufV + m_iChromaMarginY *getCStride() + m_iChromaMarginX;
再结合各个变量之间的关系(再该函数的前面一部分声明并赋值),可以发现m_apiPicBufY 所分配的内存空间是 m_apiPicBufU 和 m_apiPicBufV 的四倍,具体而言是长度和宽度都小一倍。也就是说没一个U像素(或V像素)对应着四个Y像素。怎么是这样子呢?
我查了一下HEVC的官方文档(JCT-VC_L1003_v28.doc),里面有这样一副图,编号为 Figure 6-1,在第20页,不同版本的可以搜索 YCbCr 找找。
图 2 YCbCr采样点位置示例图
这样子看来不知道大家有没有什么想法,就是U点和V点对应了图中的小圆圈,也就是说周围四个像素都采用这样一个值。
B)代码的实现
上面讲述了TComPicYuv中YUV图像的格式,这里我们出于界面显示的需要,需要把它转化为RGB格式,用bmp图像贴图的方式显示。
代码如下。
TComPic* pcPic = *(iterPic);if ( pcPic->getOutputMark() && (not_displayed > pcPic->getNumReorderPics(tId) && pcPic->getPOC() > m_iPOCLastDisplay)){// write to filenot_displayed--;width = pcPic->getPicYuvRec()->getWidth();height = pcPic->getPicYuvRec()->getHeight();if ( NULL == lpDiBits ){lpDiBits = new char[width * height * 3]; // 一般来说视频的图像宽度为 4 的倍数,所以不用管对齐了memset( lpDiBits, 0, width * height * 3 );}Pel * y = (Pel *)pcPic->getPicYuvRec()->getLumaAddr(), // 重建图像的 Y 颜色开始地址*u = (Pel *)pcPic->getPicYuvRec()->getCbAddr(), // 重建图像的 Cb 颜色开始地址*v = (Pel *)pcPic->getPicYuvRec()->getCrAddr(); // 重建图像的 Cr 颜色开始地址byte *pRGB = (byte *)lpDiBits; // bmp 的颜色开始for ( int j = 0; j < height; j++ ){for ( int i = 0; i < width; i+=2 ){// 三个像素值依次为GBR,而不是RGB*pRGB++ = static_cast<byte>(*y + 1.772*(*u-128) + 0); // B*pRGB++ = static_cast<byte>(*y - 0.34413*(*u-128) - 0.71414*(*v-128)); // G*pRGB++ = static_cast<byte>(*y + 8 + 1.402*(*v-128)); // Ry++;*pRGB++ = static_cast<byte>(*y + 1.772*(*u-128) + 0); // B*pRGB++ = static_cast<byte>(*y - 0.34413*(*u-128) - 0.71414*(*v-128)); // G*pRGB++ = static_cast<byte>(*y + 8 + 1.402*(*v-128)); // Ry++;u++; // 横向两个点才变化一次,因为其表示了 2 * 2个点的像素值v++;}y += ( pcPic->getStride() - width); // 像素值的偏移位置修正if ( j % 2 == 1 ){u -= width / 2; // 奇数行(从0开始编号)才变化,也就是到下一个偶数像素点时改变了v -= width / 2 ;}else{u += (pcPic->getCStride() - width / 2); // 像素值偏移的修正v += (pcPic->getCStride() - width / 2); // 像素值偏移的修正}}
/ / 后续部分省略。
代码中间还有一些细节的东西,即偏移位置的修正,这里可以参考一下之前所说的TComPicYuv::create 函数的内容,实际上几个图像(org、rec等等)所分配的内存空间是重叠在一起的,所以需要有上述修正才能正常显示。
四、项目编译中出现的问题
首先比较严重的是说error LNK2038RuntimeLibrary不匹配的解决
解决办法:
在工程上右键-》属性-》c/c++-》代码生成-》运行库
改成(release为MT,debug为MTD)即可解决:(所有项目要一样才行)
error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”
参考:http://blog.csdn.net/wpc320/article/details/8496957
此外还有怎么使用静态库,我是直接在工程中加入了几个lib文件。
参考:http://blog.csdn.net/liugy1126/article/details/1541466
五、代码及工程下载
该工程基本和上一篇中所讲述的ffmpeg构建播放器类似,只是替换了解码器的部分。
请看:http://blog.csdn.net/luofl1992/article/details/8293405
稍后会更新,把源代码(VS2012版,怎么用VS2010,自己改一下工程文件就行了)上传到CSDN供大家下载研究。
全部源代码下载:http://download.csdn.net/detail/luofl1992/5238393
用MFC构建HEVC码流播放器相关推荐
- 我的HEVC码流分析工具MFC小笔记:树形控件使用及窗口缩放
大约1个半月前,发布了自己写的HEVC码流分析工具.当时的版本显示语法元素使用是的Edit控件.很多主流分析工具都使用树形控件,看上去觉得比较高端,于是在纠结一天后下定决心自己也实现树形的HEVC语法 ...
- 原始 H.264 码流播放
我们平时遇到的视频文件各式各样,五花八门.通常它们会根据格式的不同,而有着不同的扩展名,比如 avi,rmvb,mkv,mp4 等等等.这些格式代表的都是 封装格式. 这些文件通常产生的过程是这样的: ...
- 国标MPEG-PS实时流播放器开发(附例子)
公安部制定的GBT 28181标准广泛应用于安防领域,这个标准规定了传输的视音频数据要封装成PS流格式.PS格式(原名叫MPEG-PS)在很多领域已经应用了很长一段时间,特别是在安防.广播电视.影音制 ...
- Android可以使用的直播流播放器Vitamio5.2.3
yixia大神的git上有两个Android直播流播放器的项目:GitHub - yixia/VitamioBundle: Vitamio for Android 弄了1天没弄出来,过! GitHu ...
- MFC入门-MCI简单音乐播放器实现
MFC入门-MCI简单音乐播放器实现 实现功能: 1) 播放 2) 暂停/恢复 3) 停止 4) 音量调节 实现步骤: 1创建工程 打开VS20 ...
- 完成一个H.265/HEVC码流分析工具
经过大约一个月左右的业余时间,终于初步完成一个H.265/HEVC码流分析工具.时间包括平时的周末.晚上,以及调休的集中时间.当然,中秋回家过节不写代码.截至今天,经过多种H.265序列测试,也有各种 ...
- 一个H.265/HEVC码流分析工具
经过大约一个月左右的业余时间,终于初步完成一个H.265/HEVC码流分析工具.时间包括平时的周末.晚上,以及调休的集中时间.当然,中秋回家过节不写代码.截至今天,经过多种H.265序列测试,也有各种 ...
- C++ RTMP直播流播放器
抛开flash,自己开发实现C++ RTMP直播流播放器 众所周知,RTMP是以flash为客户端播放器的直播协议,主要应用在B/S形式的场景中.本人研究并用C++开发实现了RTMP直播流协议的播放器 ...
- linux串流windows,【新品速递】高阶计算机讯源:SOtM sMS-1000SQ Windows Edition串流播放器...
原标题:[新品速递]高阶计算机讯源:SOtM sMS-1000SQ Windows Edition串流播放器 SOtM来自韩国,旗下产品以串流播放与音响级网络周边配备为主,这部sMS-1000SQ W ...
- Android 直播 直播测试拉流播放器和地址
Android 直播 直播测试拉流播放器和地址 直播拉流播放器 直播拉流测试地址 测试类抖音视频 直播拉流播放器 推荐VLC 直播拉流测试地址 香港财经 *****:rtmp://202.69.69. ...
最新文章
- (cljs/run-at (JSVM. :all) Metadata就这样哦)
- 在spring boot 配置actuator
- servlet指定时间到现在过了多久_就喝一瓶啤酒,多久能开车?交警:过了这个时间,100%没问题...
- (Mybaitis)分页
- Request_获取请求参数中文乱码问题处理
- Android之You need to use a Theme.AppCompat theme (or descendant) with this activity.
- iqooneo系统要不要更新_IQOOZ1即将发布,和IQOOneo3、OPPORenoACE对比,谁更值得入手?...
- 滴答定时器的计数模式_【高手私藏】STM32学习笔记:SysTick滴答时钟
- 超分辨率算法大战!AI in RTC 创新挑战赛——20万巨奖等你来拿!
- 第二章 在Linux上部署.net core
- servlet ---- 案例(简单)优化
- IE各浏览器HACK
- VMware系列序列号
- 图像处理:理想低通滤波器、butterworth滤波器(巴特沃斯)、高斯滤波器实现(python)
- 相机的光圈、快门、ISO到底是什么鬼?
- Python的Open CV学习三
- Echarts Y轴遮挡解决方案
- 打怪升级,看俄罗斯小哥是如何从互联网转行到自动驾驶行业?
- 回顾 | 女性 AI 专场 - 女性眼中的 ChatGPT
- 多表操作-外键级联操作