我目前正在编写一个小应用程序,它使用 SharpDX sprite 批处理显示手机摄像头的预览。有诺基亚开发者账号的朋友,代码主要来自这篇文章。
问题
有时,似乎先前的帧被吸引到屏幕上(“视频”来回跳跃),一秒钟的断裂,看起来像振荡/闪烁。
我想到了一个线程问题(因为 PreviewFrameAvailable 事件处理程序是由与负责渲染的方法不同的线程调用的),但是在这两种方法中都插入了 lock 语句会使代码太慢(帧速率下降到 15 帧以下/秒)。
有谁知道如何解决这个问题或在这种情况下如何在不损失太多性能的情况下实现线程同步?
代码
首先,创建所有资源,而 device 是 GraphicsDevice 的有效实例:
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 运行时的 DrawingSurfaceBackgroundGrid 组件调用。
解决方案
除了 Olydis 解决方案(见下文),由于 SharpDX 错误,我还必须将 Game.IsFixedTimeStep 设置为 false(有关详细信息,请参阅GitHub 上的此问题)。
此外,由于跨线程访问,在处理程序内部调用 PreviewFrameAvailable 是不安全的。请参阅windows phone 开发者社区sender.GetPreviewBufferArgb(previewData)
中的相应线程。