C# WinForm 调用海思 H264 解码库进行解码

最近做视频监控监控项目,学习了一下如何在 C# WinForm 下进行 H264 解码。下面贴一下代码,让大家了解一下如何使用海思的 H264 解码库进行解码,以方便其他有需要的人使用。

 

1.首先根据海思解码库的头文件做 P/Invoke 调用封装处理,代码如下

 

复制代码
  1 using System;
  2 using System.Runtime.InteropServices;
  3 
  4 namespace Demo
  5 {
  6     public class hi_h264dec
  7     {
  8         public const int HI_SUCCESS = 0;
  9         public const int HI_FAILURE = -1;
 10         public const int HI_LITTLE_ENDIAN = 1234;
 11         public const int HI_BIG_ENDIAN = 4321;
 12         public const int HI_DECODER_SLEEP_TIME = 60000;
 13 
 14         public const int HI_H264DEC_OK = 0;
 15         public const int HI_H264DEC_NEED_MORE_BITS = -1;
 16         public const int HI_H264DEC_NO_PICTURE = -2;
 17         public const int HI_H264DEC_ERR_HANDLE = -3;
 18 
 19         [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecImageEnhance", CallingConvention = CallingConvention.Cdecl)]
 20         public static extern int Hi264DecImageEnhance(IntPtr hDec, ref hiH264_DEC_FRAME_S pDecFrame, uint uEnhanceCoeff);
 21 
 22         [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecCreate", CallingConvention = CallingConvention.Cdecl)]
 23         public static extern IntPtr Hi264DecCreate(ref hiH264_DEC_ATTR_S pDecAttr);
 24 
 25         [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecDestroy", CallingConvention = CallingConvention.Cdecl)]
 26         public static extern void Hi264DecDestroy(IntPtr hDec);
 27 
 28         [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecGetInfo", CallingConvention = CallingConvention.Cdecl)]
 29         public static extern int Hi264DecGetInfo(ref hiH264_LIBINFO_S pLibInfo);
 30 
 31         [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecFrame", CallingConvention = CallingConvention.Cdecl)]
 32         public static extern int Hi264DecFrame(IntPtr hDec, IntPtr pStream, uint iStreamLen, ulong ullPTS, ref hiH264_DEC_FRAME_S pDecFrame, uint uFlags);
 33 
 34         [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecAU", CallingConvention = CallingConvention.Cdecl)]
 35         public static extern int Hi264DecAU(IntPtr hDec, IntPtr pStream, uint iStreamLen, ulong ullPTS, ref hiH264_DEC_FRAME_S pDecFrame, uint uFlags);
 36 
 37         [StructLayout(LayoutKind.Sequential)]
 38         public struct hiH264_DEC_ATTR_S
 39         {
 40             public uint uPictureFormat;
 41             public uint uStreamInType;
 42             public uint uPicWidthInMB;
 43             public uint uPicHeightInMB;
 44             public uint uBufNum;
 45             public uint uWorkMode;
 46             public IntPtr pUserData;
 47             public uint uReserved;
 48         }
 49 
 50         [StructLayout(LayoutKind.Sequential)]
 51         public struct hiH264_DEC_FRAME_S
 52         {
 53             public IntPtr pY;
 54             public IntPtr pU;
 55             public IntPtr pV;
 56             public uint uWidth;
 57             public uint uHeight;
 58             public uint uYStride;
 59             public uint uUVStride;
 60             public uint uCroppingLeftOffset;
 61             public uint uCroppingRightOffset;
 62             public uint uCroppingTopOffset;
 63             public uint uCroppingBottomOffset;
 64             public uint uDpbIdx;
 65             public uint uPicFlag;
 66             public uint bError;
 67             public uint bIntra;
 68             public ulong ullPTS;
 69             public uint uPictureID;
 70             public uint uReserved;
 71             public IntPtr pUserData;
 72         }
 73 
 74         [StructLayout(LayoutKind.Sequential)]
 75         public struct hiH264_LIBINFO_S
 76         {
 77             public uint uMajor;
 78             public uint uMinor;
 79             public uint uRelease;
 80             public uint uBuild;
 81             [MarshalAs(UnmanagedType.LPStr)] public string sVersion;
 82             [MarshalAs(UnmanagedType.LPStr)] public string sCopyRight;
 83             public uint uFunctionSet;
 84             public uint uPictureFormat;
 85             public uint uStreamInType;
 86             public uint uPicWidth;
 87             public uint uPicHeight;
 88             public uint uBufNum;
 89             public uint uReserved;
 90         }
 91 
 92         [StructLayout(LayoutKind.Sequential)]
 93         public struct hiH264_USERDATA_S
 94         {
 95             public uint uUserDataType;
 96             public uint uUserDataSize;
 97             public IntPtr pData;
 98             public IntPtr pNext;
 99         }
100     }
101 }
复制代码

 

2.海思库解码分为两种方式:

第一种,输入分断的码流数据进行解码

第二种,输入一帧完整的码流数据进行解码

 

第一种方式的代码:

复制代码
 1 //初始化解码器,可以在 FormLoad 事件里完成
 2 var decAttr = new hi_h264dec.hiH264_DEC_ATTR_S();
 3 decAttr.uPictureFormat = 0;
 4 decAttr.uStreamInType = 0;
 5 decAttr.uPicWidthInMB = 480 >> 4;
 6 decAttr.uPicHeightInMB = 640 >> 4;
 7 decAttr.uBufNum = 8;
 8 decAttr.uWorkMode = 16;
 9 IntPtr _decHandle = hi_h264dec.Hi264DecCreate(ref decAttr);
10 
11 hi_h264dec.hiH264_DEC_FRAME_S _decodeFrame = new hi_h264dec.hiH264_DEC_FRAME_S();
12 //解码
13 //pData 为需要解码的 H264 nalu 数据,length 为该数据的长度
14 var resCode = hi_h264dec.Hi264DecFrame(_decHandle, pData, (uint)length, 0, ref _decodeFrame, 0);
15 while (resCode != hi_h264dec.HI_H264DEC_NEED_MORE_BITS)
16 {
17     if (resCode == hi_h264dec.HI_H264DEC_OK)
18     {
19         if (_decodeFrame.bError == 0)
20         {
21             //计算 y u v 的长度
22             var yLength = _decodeFrame.uHeight * _decodeFrame.uYStride;
23             var uLength = _decodeFrame.uHeight * _decodeFrame.uUVStride / 2;
24             var vLength = uLength;
25 
26             var yBytes = new byte[yLength];
27             var uBytes = new byte[uLength];
28             var vBytes = new byte[vLength];
29             var decodedBytes = new byte[yLength + uLength + vLength];
30 
31             //_decodeFrame 是解码后的数据对象,里面包含 YUV 数据、宽度、高度等信息
32             Marshal.Copy(_decodeFrame.pY, yBytes, 0, (int)yLength);
33             Marshal.Copy(_decodeFrame.pU, uBytes, 0, (int)uLength);
34             Marshal.Copy(_decodeFrame.pV, vBytes, 0, (int)vLength);
35             
36             //将从 _decodeFrame 中取出的 YUV 数据放入 decodedBytes 中
37             Array.Copy(yBytes, decodedBytes, yLength);
38             Array.Copy(uBytes, 0, decodedBytes, yLength, uLength);
39             Array.Copy(vBytes, 0, decodedBytes, yLength + uLength, vLength);
40 
41             //decodedBytes 为yuv数据,可以将其转换为 RGB 数据后再转换为 BitMap 然后通过 PictureBox 控件即可显示
42             //这类代码网上比较常见,我就不贴了
43         }
44     }
45 
46     resCode = hi_h264dec.Hi264DecFrame(_decHandle, IntPtr.Zero, 0, 0, ref _decodeFrame, 0);
47 }
48 
49 //当所有解码操作完成后需要释放解码库,可以放在 FormClosing 事件里做
50 hi_h264dec.Hi264DecDestroy(_decHandle);
复制代码

 

 第二种,完整帧方式代码:

复制代码
 1 //初始化解码器,可以在 FormLoad 事件里完成
 2 var decAttr = new hi_h264dec.hiH264_DEC_ATTR_S();
 3 decAttr.uPictureFormat = 0;
 4 decAttr.uStreamInType = 0;
 5 decAttr.uPicWidthInMB = 480 >> 4;
 6 decAttr.uPicHeightInMB = 640 >> 4;
 7 decAttr.uBufNum = 8;
 8 decAttr.uWorkMode = 16;
 9 IntPtr _decHandle = hi_h264dec.Hi264DecCreate(ref decAttr);
10 
11 hi_h264dec.hiH264_DEC_FRAME_S _decodeFrame = new hi_h264dec.hiH264_DEC_FRAME_S();
12 //解码
13 //pData 为需要解码的 H264 nalu 数据,length 为该数据的长度
14 if (hi_h264dec.Hi264DecAU(_decHandle, pData, (uint) length, 0, ref _decodeFrame, 0) == 0)
15 {
16     if (_decodeFrame.bError == 0)
17     {
18         //计算 y u v 的长度
19         var yLength = _decodeFrame.uHeight * _decodeFrame.uYStride;
20         var uLength = _decodeFrame.uHeight * _decodeFrame.uUVStride / 2;
21         var vLength = uLength;
22         var yBytes = new byte[yLength];
23         var uBytes = new byte[uLength];
24         var vBytes = new byte[vLength];
25         var decodedBytes = new byte[yLength + uLength + vLength];
26         //_decodeFrame 是解码后的数据对象,里面包含 YUV 数据、宽度、高度等信息
27         Marshal.Copy(_decodeFrame.pY, yBytes, 0, (int)yLength);
28         Marshal.Copy(_decodeFrame.pU, uBytes, 0, (int)uLength);
29         Marshal.Copy(_decodeFrame.pV, vBytes, 0, (int)vLength);
30         //将从 _decodeFrame 中取出的 YUV 数据放入 decodedBytes 中
31         Array.Copy(yBytes, decodedBytes, yLength);
32         Array.Copy(uBytes, 0, decodedBytes, yLength, uLength);
33         Array.Copy(vBytes, 0, decodedBytes, yLength + uLength, vLength);
34 
35         //decodedBytes 为yuv数据,可以将其转换为 RGB 数据后再转换为 BitMap 然后通过 PictureBox 控件即可显示
36         //这类代码网上比较常见,我就不贴了
37     }
38 }
39 
40 //当所有解码操作完成后需要释放解码库,可以放在 FormClosing 事件里做
41 hi_h264dec.Hi264DecDestroy(_decHandle);
复制代码

 

对于在设备 SDK 回调里每次都能获取到一帧完整的 H264 裸数据时,建议使用第二种方式。而对于读取文件进行本地播放时,建议使用第一种方式。

 

另外,需要注意海思解码库为商业软件,在解码输出时,画面中会包含海思公司水印,自己想办法去除吧。

 

附上海思解码库下载(包含库文件、头文件、文档以及官方测试程序和源码):

http://files.cnblogs.com/tracky/Hi_H.264_DEC_SDK_V2.1.0.3.rar

转自:https://www.cnblogs.com/tracky/archive/2013/06/05/3118205.html?utm_source=tuicool

猜你喜欢

转载自blog.csdn.net/lbc2100/article/details/79739015
今日推荐