我知道关于这个话题有很多问题,我已经查看了大部分问题以及谷歌搜索来帮助我解决这个问题,但无济于事。
我想要做的是发布我的代码的相关部分,这些部分产生位图并在我的UI中将位图渲染到PictureBox上,我想知道是否有人可以发现具体导致此错误的内容,并且可以建议如何避免或绕过它。
我将从我的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中的两个排队帧属性并将帧渲染到两个PictureBoxes:
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以便它接受新的帧?