c# RTSP播放器

ONVIF ODM在onvif领域里名气很大,是一款开源的NVC实现。其实现采用了c# c++ F#。项目很大,也很复杂。最近研究了一下,自己调用其类库写了一个c#版的RTSP的播放器。难度不大。但要明白其中原理,还需要多研究研究ODM源码。live555+ffmpeg

效果图:

c# RTSP播放器_第1张图片

 

c# RTSP播放器_第2张图片

 

 

目前难点在于解码过程,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
            {

            }


        }


 

 

你可能感兴趣的:(ffmpeg,live555,C#)