我正在研究一个使用ONVIF的CCTV项目。我使用Winform示例(由“ ONVIF设备管理器”项目提供)从摄像机获取视频帧。 (您可以找到它here)。我发现该示例通过使用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()语句。但是,将引发错误“位图已被锁定”。已经发布了one question,我已经跟踪了该问题的答案,但是在重绘imageBox时发生了另一个错误“ Invalid parameter”。我想如果我们锁定位图 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();
}
});
}
答案 0 :(得分:0)
由于以下几行,您将收到错误“位图已被锁定”:
Bitmap bmp = img as Bitmap;
似乎img
是全局声明的,并且您的线程和UI线程正在同时使用它。当Bitmap
对象在UI中显示时,它被UI线程锁定以进行绘画。线程中的Lock
方法与UI线程中的此操作冲突。
为了获得更好的性能,我建议您为线程中获得的每个帧生成一个位图。然后开始调用它以显示准备好的图像。在UI线程中,当在PictureBox属性中进行替换时,应该注意处理位图。