C # Bitmap LockBits / UnlockBits в многопоточности - PullRequest
0 голосов
/ 07 марта 2019

Я работаю над проектом CCTV, в котором работает ONVIF.Я использую пример Winform, предоставленный проектом «ONVIF Device Manager», для получения видеокадров с камеры.(Вы можете найти его здесь ).Я обнаружил, что образец помещает код блока CopyMemory () в поток пользовательского интерфейса с помощью dispatcher.BeginInvoke ().Я бы замедлил основной поток пользовательского интерфейса, потому что этот блок повторяется для отображения изображений в 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
    }

Я попытался исключить оператор CopyMemory () вне потока пользовательского интерфейса, вызвав BeginInvoke () после UnlockBits () растровое изображение.Но возникает ошибка «Битовая карта уже заблокирована».Там был один вопрос , который был опубликован, я следил за ответом на этот вопрос, но при перерисовке imageBox возникает другая ошибка «Неверный параметр».Я предполагаю, что если мы заблокируем растровое изображение lock (bmp) {CopyMemory (); ...} , то imageBox не сможет получить информацию о растровом изображении, связанном с ним.

Любая помощь будет высоко оценена.

Обновить предлагаемое решение

        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();
            }

        });
    }

1 Ответ

0 голосов
/ 07 марта 2019

Появляется ошибка «Битовая карта уже заблокирована» из-за следующей строки:

Bitmap bmp = img as Bitmap;

Кажется, что img объявлено глобально, и оно используется как вашим потоком, так и потоком пользовательского интерфейса одновременно. Когда объект Bitmap отображается в пользовательском интерфейсе, он блокируется потоком пользовательского интерфейса для рисования. Метод Lock в вашем потоке конфликтует с этой операцией в потоке пользовательского интерфейса.

Чтобы получить лучшую производительность, я рекомендую вам генерировать растровое изображение для каждого кадра, который вы получаете в своем потоке. Затем начните вызывать его для отображения готового изображения. В потоке пользовательского интерфейса вы должны позаботиться об удалении растрового изображения при замене в свойстве PictureBox.

...