今天看了不少,卻感覺收獲寥寥。
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、現在所學的很多東西真的是流於表面,即使我把這個視頻解碼做出來已經能播放了,但我還是覺得自己的提升好少。但我還是要繼續這樣做,時間緊迫。不知道我寫的東西對您有沒有幫助,希望各位懂這方面的我們多多交流。對於我的建議給在評論中提出,謝謝您對我的提議。每天記錄一點點,就能進步一點點。