简体   繁体   English

多线程中的C#位图LockBits / UnlockBits

[英]C# Bitmap LockBits/UnlockBits in multi-thread

I am working on a CCTV project which employs ONVIF. 我正在使用ONVIF的CCTV项目中工作。 I use a Winform sample, which is provided by "ONVIF Device Manager" project, to obtain video frames from a camera. 我使用Winform示例(由“ ONVIF设备管理器”项目提供)从摄像机获取视频帧。 (You can find it here ). (您可以在这里找到它)。 I found that the sample put a CopyMemory() block code inside UI thread by using dispatcher.BeginInvoke(). 我发现该示例通过使用dispatcher.BeginInvoke()在UI线程中放置了一个CopyMemory()块代码。 I would slow down main UI thread because this block is repeated to display images in a PictureBox. 我会减慢主UI线程的速度,因为重复此块以在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
    }

I tried to exclude the CopyMemory() statement outside of UI thread by calling BeginInvoke() after UnlockBits() bitmap. 我试图通过在UnlockBits()位图之后调用BeginInvoke()来排除UI线程之外的CopyMemory()语句。 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. 一个问题已发布,我已遵循该问题的答案,但在重绘imageBox时发生了另一个错误“ Invalid parameter”。 I guess if we lock at bitmap lock(bmp) {CopyMemory();...} the imageBox cannot get information of bitmap associated with it. 我想如果我们锁定位图锁(bmp){CopyMemory(); ...},则imageBox无法获取与其关联的位图信息。

Any help is highly appreciated. 非常感谢您的帮助。

Update Proposed Solution 更新建议的解决方案

        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;

It seems that img is declared globally, and it is being used both by your thread, and the UI thread ath the same time. 似乎img是全局声明的,并且您的线程和UI线程正在同时使用它。 When a Bitmap object is being displayed in UI, it is being Locked by UI thread for painting. 当在用户界面中显示Bitmap对象时,它被UI线程锁定以进行绘画。 The Lock method in your thread conflicts with this operation in UI thread. 您线程中的Lock方法与此UI线程中的此操作相冲突。

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. 在UI线程中,当在PictureBox属性中进行替换时,应注意处理位图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM