1. 程式人生 > >C# WinForm 呼叫海思 H264 解碼庫進行解碼

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 裸資料時,建議使用第二種方式。而對於讀取檔案進行本地播放時,建議使用第一種方式。

另外,需要注意海思解碼庫為商業軟體,在解碼輸出時,畫面中會包含海思公司水印,自己想辦法去除吧。

附上海思解碼庫下載(包含庫檔案、標頭檔案、文件以及官方測試程式和原始碼):

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