[英]C# Bitmap LockBits/UnlockBits in multi-thread
我正在使用ONVIF的CCTV項目中工作。 我使用Winform示例(由“ ONVIF設備管理器”項目提供)從攝像機獲取視頻幀。 (您可以在這里找到它)。 我發現該示例通過使用dispatcher.BeginInvoke()在UI線程中放置了一個CopyMemory()塊代碼。 我會減慢主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
}
我試圖通過在UnlockBits()位圖之后調用BeginInvoke()來排除UI線程之外的CopyMemory()語句。 但是,將引發錯誤“位圖已被鎖定”。 有一個問題已發布,我已遵循該問題的答案,但在重繪imageBox時發生了另一個錯誤“ Invalid parameter”。 我想如果我們鎖定位圖鎖(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();
}
});
}
由於出現以下行,您將收到錯誤“位圖已被鎖定”:
Bitmap bmp = img as Bitmap;
似乎img
是全局聲明的,並且您的線程和UI線程正在同時使用它。 當在用戶界面中顯示Bitmap
對象時,它被UI線程鎖定以進行繪畫。 您線程中的Lock
方法與此UI線程中的此操作相沖突。
為了獲得更好的性能,我建議您為線程中的每個幀生成一個位圖。 然后開始調用它以顯示准備好的圖像。 在UI線程中,當在PictureBox屬性中進行替換時,應注意處理位圖。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.