我知道关于这个主题有很多问题,我已经查看了其中的大部分以及谷歌搜索来帮助我解决这个问题,但无济于事。
我想做的是将生成位图并将位图渲染到 UI 中的 PictureBoxes 的代码的相关部分发布,我想知道是否有人可以发现具体导致此错误的原因,并建议如何避免或绕过它。
我将从 VideoRenderer 类中的相关位 (3) 开始:
视频运行时不断调用 MoveFrameToBitmap 的定时器事件:
private void TimerTick(object sender, EventArgs e) { if (frameTransport.IsNewFrameAvailable()) { if (frameTransport.GetFrame()) { if (MoveFrameToBitmap()) { double msSinceLastFrame = (Int32)DateTime.Now.Subtract(lastFrameTimestamp).TotalMilliseconds; fps = 1000 / msSinceLastFrame; lastFrameTimestamp = DateTime.Now; } } else { if (frameTransport.channelKeyBufferBufidMismatch) { needsRestart = true; } } } }
MoveFrameToBitmap,它从 FrameTransport 编组视频帧,如果成功,则创建一个位图,克隆它并将帧排队:
internal bool MoveFrameToBitmap() { bool result = false; try { if (frameTransport.bitmapDataSize == 0) { return false; } bool ResolutionHasChanged = ((videoWidth != frameTransport.width) | (videoHeight != frameTransport.height)); videoHeight = frameTransport.height; videoWidth = frameTransport.width; Bitmap bitmap = new System.Drawing.Bitmap(videoWidth, videoHeight); Rectangle rectangle = new System.Drawing.Rectangle(0, 0, videoWidth, videoHeight); BitmapData bitmapData = new System.Drawing.Imaging.BitmapData(); bitmapData.Width = videoWidth; bitmapData.Height = videoHeight; bitmapData.PixelFormat = PixelFormat.Format24bppRgb; bitmap.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb, bitmapData); Marshal.Copy(frameTransport.bitmapData, 0, bitmapData.Scan0, frameTransport.bitmapDataSize); lock (frameQueueLock) { if (frameQueue.Count == 0) { frameQueue.Enqueue(bitmap.Clone()); } } bitmap.UnlockBits(bitmapData); if (ResolutionHasChanged) skypeRef.events.FireOnVideoResolutionChanged(this, new RootEvents.OnVideoResolutionChangedArgs(videoWidth, videoHeight)); bitmap.Dispose(); result = true; } catch (Exception) { } GC.Collect(); return result; }
公开队列帧的属性,即使帧当前没有排队,也可以安全地访问:
public Bitmap QueuedFrame { get { try { lock (frameQueueLock) { return frameQueue.Dequeue() as Bitmap; } } catch (Exception) { return null; } } }
这就是 VideoRenderer 的全部内容。现在我将展示静态 MyVideo 类的相关属性,它封装、控制和返回来自两个视频渲染器的帧。这是公开第一个渲染器的排队帧的属性(每次调用 videoPreviewRenderer 都被 VPR_Lock 锁定):
public static Bitmap QueuedVideoPreviewFrame
{
get
{
lock (VPR_Lock) { return videoPreviewRenderer.QueuedFrame; }
}
}
第二个渲染器的属性是相同的,除了它自己的锁定对象。
继续前进,这是我的 UI 中的线程及其调用,它访问 MyVideo 中的两个排队帧属性并将帧渲染到两个 PictureBox:
ThreadStart renderVCONF3501VideoThreadStart = new ThreadStart(new Action(() =>
{
while (MyAccount.IsLoggedIn)
{
if (MyVideo.VideoPreviewIsRendering)
{
if (MyVideo.VideoPreviewRenderer.NeedsRestart)
{
MyVideo.VideoPreviewRenderer.Stop();
MyVideo.VideoPreviewRenderer.Start();
MyVideo.VideoPreviewRenderer.NeedsRestart = false;
}
else
{
try
{
Bitmap newVideoPreviewFrame = MyVideo.QueuedVideoPreviewFrame;
if (newVideoPreviewFrame != null)
{
lock (VCONF3501_VPI_Lock)
{
VCONF3501_VideoPreview.Image = newVideoPreviewFrame;
}
}
}
catch (Exception) { continue; }
}
}
else
{
lock (VCONF3501_VPI_Lock)
{
VCONF3501_VideoPreview.Image = null;
}
}
if (MyVideo.LiveSessionParticipantVideoIsRendering)
{
if (MyVideo.LiveSessionParticipantVideoRenderer.NeedsRestart)
{
MyVideo.LiveSessionParticipantVideoRenderer.Stop();
MyVideo.LiveSessionParticipantVideoRenderer.Start();
MyVideo.LiveSessionParticipantVideoRenderer.NeedsRestart = false;
}
else
{
try
{
Bitmap newLiveSessionParticipantVideoFrame = MyVideo.QueuedLiveSessionParticipantVideoFrame;
if (newLiveSessionParticipantVideoFrame != null)
{
lock (VCONF3501_LSPVI_Lock)
{
VCONF3501_Video.Image = newLiveSessionParticipantVideoFrame;
}
}
}
catch (Exception) { continue; }
}
}
else
{
lock (VCONF3501_LSPVI_Lock)
{
VCONF3501_Video.Image = null;
}
}
GC.Collect();
}
}));
new Thread(renderVCONF3501VideoThreadStart).Start();
GC.Collect() 调用是强制位图内存释放,因为存在内存泄漏(并且仍然可能是一个 - 克隆的位图没有被手动处理,我不知道在哪里,在这一点上)。
System.Drawing 中的 InvalidOperationException 在哪里,这会导致在 PictureBox 上绘制一个红十字,我在锁定和访问方面做错了什么,如何避免/绕过这个错误?
我试图用 catch 异常绕过它并继续线程中的逻辑,并且我已经确认可以工作。. . 有时。在其他时候,失败的绘制尝试似乎完成得太远并且无论如何都绘制了红十字,并且在那之后,PictureBox 完全没有响应并且无法绘制新帧,即使视频仍然运行良好。
也许有一种方法可以刷新 PictureBox 以便它接受新帧?