私はこのトピックに関して多くの質問があることを知っています、そして私はこの問題を解決するのを助けるためにグーグルと同様にそれらのほとんどを調べましたが、役に立ちませんでした。
私がやりたいのは、ビットマップを生成し、ビットマップをレンダリングするコードの関連セクションを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; } } } }
FrameTransportからのビデオフレームでマーシャリングするMoveFrameToBitmapは、成功した場合にビットマップを作成し、それを複製してフレームをキューに入れます。
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クラスの関連するプロパティを示します。このクラスは、2つのビデオレンダラーからのフレームをカプセル化し、制御し、返します。これは、最初のレンダラーのキューに入れられたフレームを公開するプロパティです(videoPreviewRendererへのすべての呼び出しはVPR_Lockでロックされます):
public static Bitmap QueuedVideoPreviewFrame
{
get
{
lock (VPR_Lock) { return videoPreviewRenderer.QueuedFrame; }
}
}
2番目のレンダラーのプロパティは、独自のロックオブジェクトを除いて同じです。
次に進むと、MyVideoの2つのキューに入れられたフレームプロパティにアクセスし、2つのPictureBoxにフレームをレンダリングするUIのスレッドとその呼び出しがあります。
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()呼び出しは、メモリリークがあったため、ビットマップメモリを強制的に解放します(まだ1つである可能性があります。複製されたビットマップは手動で破棄されておらず、現時点ではどこに配置すればよいかわかりません。 )。
System.DrawingのInvalidOperationExceptionはどこにありますか?これにより、元のPictureBoxに赤い十字が描画されます。ロックとアクセスに関して何が間違っていますか。このエラーを回避/バイパスするにはどうすればよいですか。
私はcatch例外でそれをバイパスし、スレッドでロジックを続行しようとしていますが、動作を確認しました。。。時折。また、失敗した描画の試行が完了しすぎて、とにかく赤十字を描画する場合もあります。その後、PictureBoxは完全に応答しなくなり、ビデオが正常に実行されている場合でも、新しいフレームを描画できなくなります。
おそらく、新しいフレームを受け入れるようにPictureBoxを更新する方法はありますか?