本文介绍了多线程中的C#位图LockBits/UnlockBits的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从事使用ONVIF的CCTV项目.我使用Winform示例(由"ONVIF设备管理器"项目提供)从摄像机获取视频帧. (您可以在此处找到它).我发现该示例通过使用dispatcher.BeginInvoke()在UI线程中放置了一个CopyMemory()块代码.我会减慢主UI线程的速度,因为重复此块以在PictureBox中显示图像.

I am working on a CCTV project which employs ONVIF. I use a Winform sample, which is provided by "ONVIF Device Manager" project, to obtain video frames from a camera. (You can find it here). I found that the sample put a CopyMemory() block code inside UI thread by using dispatcher.BeginInvoke(). I would slow down main UI thread because this block is repeated to display images in a PictureBox.

void InitPlayback(VideoBuffer videoBuffer, bool isInitial)
    {
        //....

        var renderingTask = Task.Factory.StartNew(delegate
        {
            var statistics = PlaybackStatistics.Start(Restart, isInitial);
            using (videoBuffer.Lock())
            {
                try
                {
                    //start rendering loop
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        using (var processingEvent = new ManualResetEventSlim(false))
                        {
                            var dispOp = disp.BeginInvoke((MethodInvoker)delegate
                            {
                                using (Disposable.Create(() => processingEvent.Set()))
                                {
                                    if (!cancellationToken.IsCancellationRequested)
                                    {
                                        //update statisitc info
                                        statistics.Update(videoBuffer);

                                        //render farme to screen
                                        //DrawFrame(bitmap, videoBuffer, statistics);
                                        DrawFrame(videoBuffer, statistics);
                                    }
                                }
                            });
                            processingEvent.Wait(cancellationToken);
                        }
                        cancellationToken.WaitHandle.WaitOne(renderinterval);
                    }
                }
                catch (OperationCanceledException error) { } catch (Exception error) { } finally { }
            }
        }, cancellationToken);
    }

    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    public static extern void CopyMemory(IntPtr dest, IntPtr src, int count);
    private void DrawFrame(VideoBuffer videoBuffer, PlaybackStatistics statistics)
    {
        Bitmap bmp = img as Bitmap;
        BitmapData bd = null;
        try
        {
            bd = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);//bgra32

            using (var md = videoBuffer.Lock())
            {

                CopyMemory(bd.Scan0, md.value.scan0Ptr, videoBuff.stride * videoBuff.height);

                //bitmap.WritePixels(
                //    new Int32Rect(0, 0, videoBuffer.width, videoBuffer.height),
                //    md.value.scan0Ptr, videoBuffer.size, videoBuffer.stride,
                //    0, 0
                //);
            }

        }
        catch (Exception err)
        {
            //errBox.Text = err.Message;
            Debug.Print("DrawFrame:: " + err.Message);
        }
        finally
        {
            bmp.UnlockBits(bd);
        }
        imageBox.Image = bmp;
        // var dispOp = disp.BeginInvoke((MethodInvoker)delegate {imageBox.Image = bmp;}); =>>Bitmap is already locked
    }

我试图通过在 UnlockBits()位图之后调用 BeginInvoke()在UI线程之外排除 CopyMemory()语句.但是,将引发错误位图已被锁定".我有一个一个问题,我已经关注了该问题的答案,但是在重画imageBox时发生另一个错误无效参数".我想如果我们锁定位图 lock(bmp){CopyMemory(); ...} ,则imageBox无法获取与其关联的位图信息.

I tried to exclude the CopyMemory() statement outside of UI thread by calling BeginInvoke() after UnlockBits() bitmap. But, an error is raised "Bitmap is already locked". There has one question which was posted, I have followed the answer of that question but another error occurs "Invalid parameter" while redrawing imageBox. I guess if we lock at bitmap lock(bmp) {CopyMemory();...} the imageBox cannot get information of bitmap associated with it.

我们非常感谢您的帮助.

Any help is highly appreciated.

更新建议的解决方案

        private void DrawFrame(PlaybackStatistics statistics)
    {
        Bitmap bmp = new Bitmap(videoBuff.width, videoBuff.height);//img as Bitmap;
        //...
        imageBox.Invoke((MethodInvoker)delegate
        {
            Image bmTemp = imageBox.Image;
            imageBox.Image = bmp;
            if (bmTemp != null)
            {
                bmTemp.Dispose();
            }

        });
    }

推荐答案

由于以下几行,您将收到错误位图已被锁定":

You get the error "Bitmap is already locked" because of the following line:

Bitmap bmp = img as Bitmap;

似乎img是全局声明的,并且您的线程和UI线程正在同时使用它.当Bitmap对象在UI中显示时,它被UI线程锁定以进行绘画.您线程中的Lock方法与此UI线程中的此操作相冲突.

It seems that img is declared globally, and it is being used both by your thread, and the UI thread ath the same time. When a Bitmap object is being displayed in UI, it is being Locked by UI thread for painting. The Lock method in your thread conflicts with this operation in UI thread.

为了获得更好的性能,我建议您为线程中获得的每个帧生成一个位图.然后开始调用它以显示准备好的图像.在UI线程中,当在PictureBox属性中进行替换时,应该注意处理位图.

To get better performance, I recommend you to generate a Bitmap for each frame you get in your thread. Then BeginInvoke it to display the prepared image. In UI thread you should care for disposing the Bitmap when replacing in PictureBox property.

这篇关于多线程中的C#位图LockBits/UnlockBits的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 02:32
查看更多