標籤:
ONVIF ODM在onvif領域裡名氣很大,是一款開源的NVC實現。其實現採用了c# c++ F#。項目很大,也很複雜。最近研究了一下,自己調用其類庫寫了一個c#版的RTSP的播放器。難度不大。但要明白其中原理,還需要多研究研究ODM源碼。
:
目前痛點在於解碼過程,BGR轉為RGB排列,兼顧效率使用了unsafe 指標。對於不是專門搞映像的,還是需要慢慢理解。
private void DecoderFrame(Bitmap bitmap, VideoBuffer videoBuffer, PlaybackStatistics statistics) { try { using (var md = videoBuffer.Lock()) { Rectangle rect = new Rectangle(0, 0, videoBuffer.width, videoBuffer.height); //將位元影像鎖定到記憶體中 BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat); //BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //擷取掃描寬度,例如1280*3=3840 1280為寬度即1280個像素點,每個像素有R\G\B三種,3位元組 bitmapData.Stride = videoBuffer.stride; byte[] buffer = new byte[videoBuffer.size]; //將md中資料copy到數組buffer Marshal.Copy(md.value.scan0Ptr, buffer, 0, videoBuffer.size); //將buffer中資料copy到bitmapData Marshal.Copy(buffer, 0, bitmapData.Scan0, w * h * 3); //迴圈處理 ,將BGR轉換為RGB排列 //http://blog.csdn.net/lulu831110/article/details/4820377 unsafe { byte* ptr = (byte*)(bitmapData.Scan0); for (int i = 0; i < bitmapData.Height; i++) { for (int j = 0; j < bitmapData.Width; j++) { //將B和R對調,即第一個和第三個對調 byte B = *ptr;//B的值 byte R = *(ptr + 2);//R的值 //對調 *ptr = R; *(ptr + 2) = B; ptr += 3; } /* * 表示跨過無用的地區,跳過這些多餘的位元組,讓指標指向下一行, * 其原因是映像資料在記憶體中儲存時是按4位元組對齊的, * 具體解釋如下:假設有一張圖片寬度為6, * 假設是Format24bppRgb格式的(每像素3位元組,在以下的討論中,除非特別說明, * 否則Bitmap都被認為是24位RGB)。顯然,每一行需要6*3=18個位元組儲存。 * 對於Bitmap就是如此。 * 但對於BitmapData,雖然data.Width還是等於image.Width, * 但大概是出於顯示效能的考慮, * 每行的實際的位元組數將變成大於等於它的那個離它最近的4的整倍數, * 此時的實際位元組數就是Stride。就此例而言,18不是4的整倍數, * 而比18大的離18最近的4的倍數是20,所以這個data.Stride = 20。 * 顯然,當寬度本身就是4的倍數時,bitmapData.Stride = image.Width * 3。 * * |-------Stride-------------| * |-------Width----------| | * Scan0: * BGR BGR BGR BGR BGR BGR XX * BGR BGR BGR BGR BGR BGR XX * BGR BGR BGR BGR BGR BGR XX * * 首先用data.Scan0找到第0個像素的第0個分量的地址, * 這個地址指向的是個byte類型,所以當時定義為byte* ptr。 * 行掃描時,在當前指標位置(不妨看成當前像素的第0個顏色分量) * 連續取出三個值(3個原色分量。 * 注意,0 1 2代表的次序是B G R。在取指標指向的值時, * 貌似p[n]和p += n再取p[0]是等價的),然後下移3個位置(ptr += 3, * 看成指到下一個像素的第0個顏色分量)。做過Bitmap.Width次操作後, * 就到達了Bitmap.Width * 3的位置, * 應該要跳過圖中標記為X的位元組了(共有Stride - Width * 3個位元組), * 代碼中就是 ptr += dataIn.Stride - dataIn.Width * 3。 * * * * 一般來說,如果一個像素是一個位元組的話(你的代碼做這樣的假設,其實很不好), * bmpData.Stride 應該等於bmpWidth,但實際上往往不相等,要差幾個位元組, * 因為bmpData.Stride必須是4的倍數,如果不足,則補上幾個位元組, * 讓bmpData.Stride是4的倍數,這些多餘的位元組不會儲存任何顏色資料, * ptr += bmpData.Stride - bmpWidth;只是跳過這些多餘的位元組, * 讓指標指向下一行 * * */ ptr += bitmapData.Stride - bitmapData.Width * 3; } } bitmap.UnlockBits(bitmapData); Bitmap bt = (Bitmap)bitmap.Clone(); lock (this) { bitmap_queue.Enqueue(bt); } if (statistics.isNoSignal) { if (!panel1.Visible) { showLable(true); } Console.WriteLine("No Signal"); } else { if (panel1.Visible) { showLable(false); } } } } catch { } }
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
c# RTSP播放器