今天看了不少,卻感覺收獲寥寥。
1、H264相關知識
因為RTP協議發過來的數據已經經過了H264編碼,所以這邊需要解碼。補充一下H264的相關知識。
與以往的視頻壓縮標准相比,H.264 視頻壓縮標准(簡稱H.264)具有更出色的性能,因此H.264 被稱為新一代視頻壓縮標准。H.264 與H.263 或MPEG-4 相比, 主要新增特性如下:
1、采用更為精細和豐富的幀內編碼及幀間預測方式,有效地減少殘差數據。
2、引入新的算術編碼方式,使得數據壓縮比更高。
3、 視頻數據分層更為合理,引入 NAL 更利於網絡傳輸。
4、取消傳統的幀結構,引入 slice 結構和參數集,提高碼流的抗誤碼能力。
5、引入靈活的參考幀管理機制,參考幀數目最多可以達到 16 個。
上述特性使得H.264 在視頻信噪比、圖像質量以及應用的靈活性上有了質的飛躍,但帶來的問題是H.264 在實現上復雜度較高。
2、一些可能的解決方案(並未實踐)
Intel media SDK,windows media sdk,http://www.ffmpeg-csharp.com/(這個網站上說是最簡單的方式使用ffmpeg,我也沒有學會怎麼使用,等以後再回來學習吧),C#調用ffmpeg開發庫。以上這些方式只是搜索到的可能的解決方法,因為太懶了,著急做出東西,所以沒有去學習,後來在海康的播放庫API中看到H264的相關知識(http://www.cnblogs.com/over140/archive/2009/03/22/1418946.html),以前做海康攝像頭調用的時候就經常看他的博客。最後在晚上的時候找到一種解決方式。使用海思提供的H264解碼庫,有專門的解碼庫,但也是用C++寫的。不過這沒有問題,想起以前海康攝像頭的時候也是C++的代碼,通過C#來調用。方法是通過PInvoke.net轉一下就可以了。
轉換之後的代碼,將hi_h264dec_w.dll轉換為C#中的類(也是參考網上的代碼)
class Hi264Dec { public const int HI_SUCCESS = 0; public const int HI_FAILURE = -1; public const int HI_LITTLE_ENDIAN = 1234; public const int HI_BIG_ENDIAN = 4321; public const int HI_DECODER_SLEEP_TIME = 60000; public const int HI_H264DEC_OK = 0; public const int HI_H264DEC_NEED_MORE_BITS = -1; public const int HI_H264DEC_NO_PICTURE = -2; public const int HI_H264DEC_ERR_HANDLE = -3; [DllImport("hi_h264dec_w.dll",EntryPoint = "Hi264DecImageEnhance", CallingConvention = CallingConvention.Cdecl)] public static extern int Hi264DecImageEnhance(IntPtr hDec, ref hiH264_DEC_FRAME_S pDecFrame, uint uEnhanceCoeff); [DllImport("hi_h264dec_w.dll",EntryPoint = "Hi264DecCreate", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr Hi264DecCreate(ref hiH264_DEC_ATTR_S pDecAttr); [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecDestroy", CallingConvention = CallingConvention.Cdecl)] public static extern void Hi264DecDestroy(IntPtr hDec); [DllImport("hi_h264dec_w.dll", EntryPoint = "Hi264DecGetInfo", CallingConvention = CallingConvention.Cdecl)] public static extern int Hi264DecGetInfo(ref hiH264_LIBINFO_S pLibInfo); [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", EntryPoint = "Hi264DecAU", CallingConvention = CallingConvention.Cdecl)] public static extern int Hi264DecAU(IntPtr hDec, IntPtr pStream, uint iStreamLen, ulong ullPTS, ref hiH264_DEC_FRAME_S pDecFrame, uint uFlags); [StructLayout(LayoutKind.Sequential)] public struct hiH264_DEC_ATTR_S { public uint uPictureFormat; public uint uStreamInType; public uint uPicWidthInMB; public uint uPicHeightInMB; public uint uBufNum; public uint uWorkMode; public IntPtr pUserData; public uint uReserved; } [StructLayout(LayoutKind.Sequential)] public struct hiH264_DEC_FRAME_S { public IntPtr pY; public IntPtr pU; public IntPtr pV; public uint uWidth; public uint uHeight; public uint uYStride; public uint uUVStride; public uint uCroppingLeftOffset; public uint uCroppingRightOffset; public uint uCroppingTopOffset; public uint uCroppingBottomOffset; public uint uDpbIdx; public uint uPicFlag; public uint bError; public uint bIntra; public ulong ullPTS; public uint uPictureID; public uint uReserved; public IntPtr pUserData; } [StructLayout(LayoutKind.Sequential)] public struct hiH264_LIBINFO_S { public uint uMajor; public uint uMinor; public uint uRelease; public uint uBuild; [MarshalAs(UnmanagedType.LPStr)] public string sVersion; [MarshalAs(UnmanagedType.LPStr)] public string sCopyRight; public uint uFunctionSet; public uint uPictureFormat; public uint uStreamInType; public uint uPicWidth; public uint uPicHeight; public uint uBufNum; public uint uReserved; } [StructLayout(LayoutKind.Sequential)] public struct hiH264_USERDATA_S { public uint uUserDataType; public uint uUserDataSize; public IntPtr pData; public IntPtr pNext; } }
可以在窗體加載的時候加載如下代碼
//初始化,可以在 FormLoad 事務裡完成 var decAttr = new Hi264Dec.hiH264_DEC_ATTR_S(); decAttr.uPictureFormat = 0; decAttr.uStreamInType = 0; decAttr.uPicWidthInMB = 480 >> 4; decAttr.uPicHeightInMB = 640 >> 4; decAttr.uBufNum = 8; decAttr.uWorkMode = 16; IntPtr _decHandle = Hi264Dec.Hi264DecCreate(ref decAttr); Hi264Dec.hiH264_DEC_FRAME_S _decodeFrame = new Hi264Dec.hiH264_DEC_FRAME_S(); //解碼 //pData 為須要解碼的 H264 nalu 數據,length 為該數據的長度 if (Hi264Dec.Hi264DecAU(_decHandle, pData, (uint) length, 0, ref _decodeFrame, 0) == 0) { if (_decodeFrame.bError == 0) { //策畫 y u v 的長度 var yLength = _decodeFrame.uHeight * _decodeFrame.uYStride; var uLength = _decodeFrame.uHeight * _decodeFrame.uUVStride / 2; var vLength = uLength; var yBytes = new byte[yLength]; var uBytes = new byte[uLength]; var vBytes = new byte[vLength]; var decodedBytes = new byte[yLength + uLength + vLength]; //_decodeFrame 是解碼後的數據對象,裡面包含 YUV 數據、寬度、高度等信息 Marshal.Copy(_decodeFrame.pY, yBytes, 0, (int)yLength); Marshal.Copy(_decodeFrame.pU, uBytes, 0, (int)uLength); Marshal.Copy(_decodeFrame.pV, vBytes, 0, (int)vLength); //將從 _decodeFrame 中取出的 YUV 數據放入 decodedBytes 中 Array.Copy(yBytes, decodedBytes, yLength); Array.Copy(uBytes, 0, decodedBytes, yLength, uLength); Array.Copy(vBytes, 0, decodedBytes, yLength + uLength, vLength); //decodedBytes 為yuv數據,可以將其轉換為 RGB 數據後再轉換為 BitMap 然後經由過程 PictureBox 控件即可顯示 //這類代碼網上斗勁常見,我就不貼了 }
在關閉的時候銷毀解碼器句柄
Hi264Dec.Hi264DecDestroy(_decHandle);
在上面也說了,傳進來的數據轉為YUV數據後,還需要轉為RGB,再轉為圖片在控件中進行顯示。粗略從網上看了一下,這類算法還是比較多的,所以明天的目標是將視頻顯示出來。爭取讓已有的輪子跑起來。
3、C++學習
今天上午還看了一小部分C++,看的比較慢,學習了基本的輸入輸出語句,只看到第28頁。
4、總結
今天收畢業論文題目了,我還沒怎麼開始寫呢。等把RTP和H264視頻解碼這一塊解決就開始准備畢業論文了。晚上的時候給周兆熊老師發的郵件已經收到回復。對於我自己目前的問題也總結一下:(1)語言的確沒什麼再糾結的意思了,對我現在來說沒有孰優孰劣,我需要的做的是盡量用熟悉的語言去解決現在存在的問題。(2)未來想找工作的話不單單需要專業方面的知識,也需要軟實力,想起前幾天犯得小錯誤,對自己的成長還是有幫助。需要繼續看書,讀過的書也需要做筆記。(3)關於怎麼讓自己堅持去學習,前幾天的煩躁心裡真的因為好好做事情減輕了呢。這幾天一直嘗試著用番茄工作法讓自己安定下來,堅持使用第四天,每天大約5個番茄鐘(效率好低啊)。看了高效工作後,裡面的對拖延症的分析三個原因真的讓我印象深刻:(1)其他人強迫你做的事情違背你的意願(好像的確如此哈哈);(2)你給自己壓力要有完美的表現;(3)害怕犯錯誤受批評。三個原因真的是非常准確。不愉快也因為忙碌起來而漸漸消失了。
5、現在所學的很多東西真的是流於表面,即使我把這個視頻解碼做出來已經能播放了,但我還是覺得自己的提升好少。但我還是要繼續這樣做,時間緊迫。不知道我寫的東西對您有沒有幫助,希望各位懂這方面的我們多多交流。對於我的建議給在評論中提出,謝謝您對我的提議。每天記錄一點點,就能進步一點點。