C# WinForm 呼叫海思 H264 解碼庫進行解碼
阿新 • • 發佈:2019-02-17
最近做視訊監控監控專案,學習了一下如何在 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, refhiH264_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 裸資料時,建議使用第二種方式。而對於讀取檔案進行本地播放時,建議使用第一種方式。
另外,需要注意海思解碼庫為商業軟體,在解碼輸出時,畫面中會包含海思公司水印,自己想辦法去除吧。
附上海思解碼庫下載(包含庫檔案、標頭檔案、文件以及官方測試程式和原始碼):
轉自:https://www.cnblogs.com/tracky/archive/2013/06/05/3118205.html?utm_source=tuicool