C# 使用DirectX Texture2D显示摄像头预览会导致Windows Phone 8上出现振荡
我目前正在编写一个小应用程序,它使用SharpDX sprite批处理显示手机摄像头的预览。对于拥有诺基亚开发者帐户的用户,代码主要来自 问题 有时,前几帧似乎被吸引到尖叫声(“视频”来回跳跃),因为一秒钟的断裂看起来像振荡/闪烁 我想到了一个线程问题(因为PreviewFrameAvailable事件处理程序是由不同于负责呈现的方法的线程调用的),但是在这两个方法中插入lock语句会使代码速度太慢(帧速率降至15帧/秒以下) 有没有人知道如何解决这个问题,或者在这种情况下如何在不损失太多性能的情况下实现线程同步 代码 首先,创建所有资源,而设备是GraphicsDevice的有效实例:C# 使用DirectX Texture2D显示摄像头预览会导致Windows Phone 8上出现振荡,c#,windows-phone-8,thread-safety,texture2d,sharpdx,C#,Windows Phone 8,Thread Safety,Texture2d,Sharpdx,我目前正在编写一个小应用程序,它使用SharpDX sprite批处理显示手机摄像头的预览。对于拥有诺基亚开发者帐户的用户,代码主要来自 问题 有时,前几帧似乎被吸引到尖叫声(“视频”来回跳跃),因为一秒钟的断裂看起来像振荡/闪烁 我想到了一个线程问题(因为PreviewFrameAvailable事件处理程序是由不同于负责呈现的方法的线程调用的),但是在这两个方法中插入lock语句会使代码速度太慢(帧速率降至15帧/秒以下) 有没有人知道如何解决这个问题,或者在这种情况下如何在不损失太多性能的
spriteBatch = new SpriteBatch(device);
photoDevice = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Back, captureSize);
photoDevice.FocusRegion = null;
width = (int)photoDevice.PreviewResolution.Width;
height = (int)photoDevice.PreviewResolution.Height;
previewData = new int[width * height];
cameraTexture = Texture2D.New(device, width, height, PixelFormat.B8G8R8A8.UNorm);
photoDevice.PreviewFrameAvailable += photoDevice_PreviewFrameAvailable;
然后,每当预览帧更改时,我都将数据设置为纹理:
void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
sender.GetPreviewBufferArgb(previewData);
cameraTexture.SetData(previewData);
}
最后,使用SpriteBatch绘制纹理,而参数backBufferCenter、textureCenter、textureScaling和Math.Pi/2用于横向居中和调整纹理:
spriteBatch.Begin();
spriteBatch.Draw(cameraTexture, backBufferCenter, null, Color.White, (float)Math.PI / 2, textureCenter, textureScaling, SpriteEffects.None, 1.0f);
spriteBatch.End();
render方法由SharpDX游戏类调用,该类基本上使用IDrawingSurfaceBackgroundContentProvider接口,该接口是Windows Phone 8运行时的组件
解决方案
除了Olydis解决方案(见下文),由于SharpDX错误,我还必须将Game.IsFixedTimeStep设置为false(详见此)
此外,由于跨线程访问,在PreviewFrameAvailable的处理程序中调用sender.GetPreviewBufferArgb(previewData)是不安全的。请参阅中相应的线程 我的猜测
void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
sender.GetPreviewBufferArgb(previewData2);
// swap buffers
var previewDataTemp = previewData1;
previewData1 = previewData2;
previewData2 = previewDataTemp;
}
正如您所猜测的,我也非常确定这可能是由于线程。例如,我怀疑相对较长的SetData
调用可能会被Draw
调用拦截,从而导致意外的输出
解决方案
以下解决方案不使用同步,而是将“关键”部分(对纹理的访问)移动到相同的上下文中
另外,让我们分配两个int[]
,而不是一个,我们将交替使用
代码片段
void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
sender.GetPreviewBufferArgb(previewData2);
// swap buffers
var previewDataTemp = previewData1;
previewData1 = previewData2;
previewData2 = previewDataTemp;
}
然后将其添加到Draw
调用(或同等上下文)中:
结论
这实际上可以防止您的问题,因为只绘制“完全更新”的纹理,并且没有对它们的并发访问。使用两个int[]
可以降低同时访问同一数组的SetData
和GetPreviewBufferArgb
的风险,但是并不能消除这种风险(但不知道同时访问int[]
是否会导致奇怪的行为)