提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

C#学习系列之H264解码

  • 唠叨
  • 一、H264/H265是什么?
  • 二、使用问题
    • 1.H264解码基础理论
    • 2.H264实际应用
  • 总结
  • 引用

唠叨

最近忙着修改代码,但是遇到比较棘手的问题。修改了底层引用的文件,替换了地址,修改了图标。还是躲不过H264、H265的解码问题。作为一位图像处理专业的学生,我也是有点爱莫能助。写文章,记录自己的学习心得,也希望大家能分享一些经验!


一、H264/H265是什么?

  • H.264

MPEG-4第十部分,是由ITU-T视频编码专家组(VCEG)和ISO/IEC动态图像专家组(MPEG)联合组成的联合视频组(JVT,Joint Video Team)提出的高度压缩数字视频编解码器标准。这个标准通常被称之为H.264/AVC(或者AVC/H.264或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC)
优点
1、低码率(与MPEG相比);
2、高质量的图像(连续、流畅);
3、容错能力强(丢包问题);
4、网络适应性强(传输性好)
编码
1、帧内预测编码——压缩图像空间冗余。通过已有宏块预测其他宏块的预测值与实际值,差值进行编码;
2、帧间预测编码——连续帧中的时间冗余来进行运动估计和补偿。流间传送帧(SP帧)——类似内容、不同码率的码流之间快速切换。(运动补偿、多帧预测、自适应去除块的滤波器)

  • H265
    H265就是在H264的基础上,进行优化。
    优点
    1、降码率——编码单位;
    2、块的四叉树分化结构——预测与变化;
    3、传输速度、内容更多更快,存储空间少。

二、使用问题

1.H264解码基础理论

  • 内容

在VS平台上,使用.net搭配C#来解决H264解码,但是会出现图像马赛克的情况。
了解H264解码中,图像的连续传输可以理解成目标跟踪的原理,以第一帧图像作为参考,后面的图像就在第一帧的基础上进行差别对比,不断演变成后续的图像。后续的图像也会不会演变为第一张,然后依次循环。
H264编码数据从帧——片——宏块(基本单元)

  • 相关术语
    协议中定义三种帧:
    I帧:完整图像帧
    B帧:参考前后图像帧编码生成
    P帧:参考I帧生成
    GOP 画面组:变化不大的图像集,其中M指定I帧与P帧之间的距离;N指定两个I帧之间的距离
    IDR关键帧:为I帧,但是I不一定是关键帧。作为已解码、重新开始的机会,分水岭。
  • 压缩方式
    1、分组:GOP
    2、定义帧:划分为三类帧
    3、预测帧:I帧为基础,I帧预测P帧,I帧与P帧预测B帧
    4、数据传输:I帧与预测差值信息进行存储和传输
  • 分层结构
    1、视频编码(VCL)——视频编码层——视频内容
    2、网口抽象(NAL)——网络提取层——按照一定协议传输数据

2.H264实际应用

实际在项目中需要使用c#来调用海思H264的解码库。
1、读取压缩图像的H264裸码转化为yuv数据;
2、将yuv数据转化为图像数据;
3、利用图像数据用c#的控件将图像进行显示。

//初始化
// 这是解码器输出图像信息
hiH264_DEC_FRAME_S _decodeFrame = new hiH264_DEC_FRAME_S();
// 这是解码器属性信息
hiH264_DEC_ATTR_S decAttr = new hiH264_DEC_ATTR_S();
decAttr.uPictureFormat = 0;
decAttr.uStreamInType = 0;
/* 解码器最大图像宽高, D1图像(1280x720) */
decAttr.uPicWidthInMB = (uint)width / 16;
decAttr.uPicHeightInMB = (uint)height / 16;
/* 解码器最大参考帧数: 16 */
decAttr.uBufNum = 16;
/* bit0 = 1: 标准输出模式; bit0 = 0: 快速输出模式 */
/* bit4 = 1: 启动内部Deinterlace; bit4 = 0: 不启动内部Deinterlace */
decAttr.uWorkMode = 0x10;
//创建、初始化解码器句柄
IntPtr _decHandle = H264Dec.Hi264DecCreate(ref decAttr);//解码结束
bool isEnd = false;
int bufferLen = 0x1000;
IntPtr pData = Marshal.AllocHGlobal(0xFFFF);
//码流段
byte[] buf = new byte[0xFFFF];
int dataLenth = 0;
while (!isEnd && !isDispose)
{VideoFrameData frameDataTemp;byte tempByte;int j = 0;bool getData = dataFrameQueue2.TryDequeue(out frameDataTemp);if (getData){Array.Copy(frameDataTemp.Data, 0, buf, dataLenth, frameDataTemp.Data.Length);dataLenth += frameDataTemp.Data.Length;//GC.Collect();//GC.SuppressFinalize(this);}//获取一段码流,积累一定缓存量再解if (dataLenth >= bufferLen || isStop == 1){Marshal.Copy(buf, 0, pData, dataLenth);if (firstDecTimeBh){firstDecTimeBh = false;Console.WriteLine("解码前时间:" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"));}int result = -1;result = H264Dec.Hi264DecFrame(_decHandle, pData, (UInt32)dataLenth, 0, ref _decodeFrame, (uint)isStop);dataLenth = 0;//IntPtr _decHandle2 = H264Dec.Hi264DecCreate(ref decAttr);//hiH264_DEC_FRAME_S _decodeFrame2 = new hiH264_DEC_FRAME_S();//IntPtr pData2 = Marshal.AllocHGlobal(frameDataTemp.DataLenth);//Marshal.Copy(frameDataTemp.Data, 0, pData2, frameDataTemp.DataLenth);//int result2 = 0;//result2 = H264Dec.Hi264DecFrame(_decHandle2, pData2, (UInt32)frameDataTemp.DataLenth, 0, ref _decodeFrame2, (uint)isStop);//if (result2 >= 0)//    Console.WriteLine("发现帧");while (HI_H264DEC_NEED_MORE_BITS != result){if (firstDecTimeBf){firstDecTimeBf = false;Console.WriteLine("解码后时间:" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"));}if (HI_H264DEC_NO_PICTURE == result){isEnd = true;break;}if (HI_H264DEC_OK == result)/* 输出一帧图像 */{//获取yuvUInt32 tempWid = _decodeFrame.uWidth;UInt32 tempHeig = _decodeFrame.uHeight;UInt32 yStride = _decodeFrame.uYStride;UInt32 uvStride = _decodeFrame.uUVStride;byte[] y = new byte[tempHeig * yStride];byte[] u = new byte[tempHeig * uvStride / 2];byte[] v = new byte[tempHeig * uvStride / 2];Marshal.Copy(_decodeFrame.pY, y, 0, y.Length);Marshal.Copy(_decodeFrame.pU, u, 0, u.Length);Marshal.Copy(_decodeFrame.pV, v, 0, v.Length);_decodeFrame.uDpbIdx = (uint)frameDataTemp.FrameId;MyProcessEvent2(_decodeFrame);//转为yv12格式//byte[] yuvBytes = new byte[y.Length + u.Length + v.Length];//Array.Copy(y, 0, yuvBytes, 0, y.Length);//Array.Copy(v, 0, yuvBytes, y.Length , v.Length);//Array.Copy(u, 0, yuvBytes, y.Length + v.Length, u .Length);//更新显示//this.d3dSource.Render(_decodeFrame.pY, _decodeFrame.pU, _decodeFrame.pV);}/* 继续解码剩余H.264码流 */result = H264Dec.Hi264DecFrame(_decHandle, IntPtr.Zero, 0, 0, ref _decodeFrame, (uint)isStop);}System.Threading.Thread.Sleep(1);}}
/* 销毁解码器 */
H264Dec.Hi264DecDestroy(_decHandle);

在使用时,将得到的H264压缩的源码提取到dataFrameQueue2队列中,然后从队列中取出来,进行存储,存储到一定码流后再进行解码。
除了以上解码,还会使用到

 [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecFrame", CallingConvention = CallingConvention.Cdecl)]public static extern int Hi264DecFrame(IntPtr hDec, IntPtr pStream, uint iStreamLen, ulong ullPTS, ref hiH264_DEC_FRAME_S pDecFrame, uint uFlags);

使用DllImport来调用hi_h264dec_w.dll解码库中的需要使用到的函数,将码流做图像输出。使用到extern也是可以理解的。

总结

H264解码首先了解解码原理,其次利用已有的解码库对H264结构进行了解,然后利用代码将其实现。

引用

1、https://baike.baidu.com/item/H.264/1022230?fromtitle=H264&fromid=7338504&fr=aladdin
2、https://zhuanlan.zhihu.com/p/71270595
3、https://blog.csdn.net/go_str/article/details/80340564
4、https://www.cnblogs.com/cangyue080180/p/5873351.html

C#学习系列之H264解码相关推荐

  1. 各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式

    编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放license收费等等 ...

  2. 各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式 详解

    http://blog.csdn.net/wishfly/article/details/50187417 编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运 ...

  3. 【FFMPEG】各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式

    目录(?)[-] 编解码学习笔记二codec类型 编解码学习笔记三Mpeg系列Mpeg 1和Mpeg 2 编解码学习笔记四Mpeg系列Mpeg 4 编解码学习笔记五Mpeg系列AAC音频 编解码学习笔 ...

  4. Android音视频学习系列(七) — 从0~1开发一款Android端播放器(支持多协议网络拉流本地文件)

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  5. Android音视频学习系列(九) — Android端实现rtmp推流

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  6. Android音视频学习系列(六) — 掌握视频基础知识并使用OpenGL ES 2.0渲染YUV数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  7. Python学习系列(六)(模块)

    Python学习系列(六)(模块) Python学习系列(五)(文件操作及其字典) 一,模块的基本介绍 1,import引入其他标准模块 标准库:Python标准安装包里的模块. 引入模块的几种方式: ...

  8. Java学习系列(十六)Java面向对象之基于TCP协议的网络通信

    TCP/IP的网络分层模型:应用层(HTTP/FTP/SMTP/POPS...),传输层(TCP协议),网络层(IP协议,负责为网络上节点分配唯一标识),物理层+数据链路层). IP地址用于标识网络中 ...

  9. Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  10. 数字电视业务PSI-SI学习系列

    目录 MPEG-2简介 TS流的生成与结构 DVB的分类 DVB SIPSI分析和处理 PATProgram Association Table节目关联表 PMTProgram Map Table节目 ...

最新文章

  1. python管道_python中管道用法入门,举个例子
  2. 如何复制一个含有随机指针节点的链表
  3. RocketMQ:消息ACK机制源码解析
  4. 【Redis】使用Redis Sentinel实现Redis HA
  5. [react-router] React-Router的路由有几种模式?
  6. 自定义ClassLoader
  7. YOLOv3改进方法增加特征尺度和训练层数
  8. 我的一个朋友加班猝死了,我很恐慌
  9. 在DataWorks中实现指定UDF只能被指定账户访问
  10. .gitignore和“以下未跟踪的工作树文件将被签出覆盖”
  11. 计算两个经纬度之间的距离
  12. python第三篇:python、flask关系映射
  13. Latex写论文格式注意点
  14. Python 获取当天零点时间戳
  15. 解决百度网盘超过4G限制,
  16. ipad能不能装python_ipad能下载python么
  17. 微信小程序——微信小程序后台自定义分析,数据准确性分析
  18. 我的北漂在路上--------时不时的停下脚步思考
  19. 小升初随感--年年小升初,一段辛酸史
  20. 主板有电无法启动_电脑主板通电但是开不了机是什么原因?

热门文章

  1. Java Apache POI 小记(读取Word通过模板创建PPT)
  2. wgs84坐标系经纬度投影_南方cass坐标转经纬度_坐标转经纬度软件
  3. PERT图事件、活动、松弛时间与关键路径的相关计算
  4. DOS攻击工具——pentmenu
  5. Dos攻击工具burpsuite完整使用指南
  6. Android基础教程之Button事件发送消息到通知栏Notification
  7. 各种编程技术资料分享
  8. 【STM32-FatFs】FAT文件系统原理
  9. 安卓dj专业打碟机软件_18号免费软件集合
  10. EasyDarwin流媒体云平台:EasyCamera开源摄像机接入海康威视摄像机实时视频