标签:
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 { } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/q317379184/article/details/48048331