IOSurfaces - 视频中的伪影,无法抓取视频表面
Posted
技术标签:
【中文标题】IOSurfaces - 视频中的伪影,无法抓取视频表面【英文标题】:IOSurfaces - Artefacts in video and unable to grab video surfaces 【发布时间】:2013-01-03 08:17:14 【问题描述】:这是一个两部分的问题。我有以下代码工作,它抓取当前显示表面并从表面创建视频(一切都在后台发生)。
for(int i=0;i<100;i++)
IOMobileFramebufferConnection connect;
kern_return_t result;
iosurfaceRef screenSurface = NULL;
io_service_t framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleH1CLCD"));
if(!framebufferService)
framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleM2CLCD"));
if(!framebufferService)
framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleCLCD"));
result = IOMobileFramebufferOpen(framebufferService, mach_task_self(), 0, &connect);
result = IOMobileFramebufferGetLayerDefaultSurface(connect, 0, &screenSurface);
uint32_t aseed;
IOSurfaceLock(screenSurface, kIOSurfaceLockReadOnly, &aseed);
uint32_t width = IOSurfaceGetWidth(screenSurface);
uint32_t height = IOSurfaceGetHeight(screenSurface);
m_width = width;
m_height = height;
CFMutableDictionaryRef dict;
int pitch = width*4, size = width*height*4;
int bPE=4;
char pixelFormat[4] = 'A','R','G','B';
dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, kIOSurfaceIsGlobal, kCFBooleanTrue);
CFDictionarySetValue(dict, kIOSurfaceBytesPerRow, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pitch));
CFDictionarySetValue(dict, kIOSurfaceBytesPerElement, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bPE));
CFDictionarySetValue(dict, kIOSurfaceWidth, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &width));
CFDictionarySetValue(dict, kIOSurfaceHeight, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &height));
CFDictionarySetValue(dict, kIOSurfacePixelFormat, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, pixelFormat));
CFDictionarySetValue(dict, kIOSurfaceAllocSize, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &size));
IOSurfaceRef destSurf = IOSurfaceCreate(dict);
IOSurfaceAcceleratorRef outAcc;
IOSurfaceAcceleratorCreate(NULL, 0, &outAcc);
IOSurfaceAcceleratorTransferSurface(outAcc, screenSurface, destSurf, dict, NULL);
IOSurfaceUnlock(screenSurface, kIOSurfaceLockReadOnly, &aseed);
CFRelease(outAcc);
// MOST RELEVANT PART OF CODE
CVPixelBufferCreateWithBytes(NULL, width, height, kCVPixelFormatType_32BGRA, IOSurfaceGetBaseAddress(destSurf), IOSurfaceGetBytesPerRow(destSurf), NULL, NULL, NULL, &sampleBuffer);
CMTime frameTime = CMTimeMake(frameCount, (int32_t)5);
[adaptor appendPixelBuffer:sampleBuffer withPresentationTime:frameTime];
CFRelease(sampleBuffer);
CFRelease(destSurf);
frameCount++;
P.S:最后 4-5 行代码是最相关的(如果需要过滤)。
1) 制作的视频有伪像。我以前做过视频,以前也遇到过这样的问题。我想这可能有两个原因: 一世。传递给适配器的 PixelBuffer 在处理(编码 + 写入)完成之前被修改或释放。这可能是由于异步调用。但我不确定这本身是否是问题以及如何解决它。 ii.传递的时间戳不准确(例如,2 帧具有相同的时间戳或一个帧的时间戳低于前一帧)。我注销了时间戳值,这似乎不是问题。
2) 上面的代码在播放视频或玩游戏时无法抓取表面。我得到的只是输出中的一个空白屏幕。这可能是由于在这种情况下发生的硬件加速解码。
对这两个问题中的任何一个部分的任何输入都会非常有帮助。另外,如果您有任何关于 IOSurfaces 的一般阅读链接,请在此处发布。
【问题讨论】:
我试图在后台获取屏幕截图,您的代码在大多数应用程序中运行良好,但在游戏中它返回黑屏,如第二个问题中所述。你有任何想法如何解决这个问题吗?此外,RecordMyScreen 在游戏中运行良好,但它现在不是开源的,所以我无法弄清楚它是如何捕获屏幕的。 以上代码无法捕获使用 openGL 渲染的屏幕。 RecordMyScreen 使用 CARenderServerRenderDisplay() API。代码位于 github - github.com/coolstar/RecordMyScreen。 CSScreenRecorder.m 文件包含核心代码。我比较了这两个 API 的性能,发现 IOMobileFramebufferGetLayerDefaultSurface() 比 CARenderServerRenderDisplay() 更快。 RecordMyScreen 在 github 上的代码现在已经过时了。我在iOS7中尝试了CARenderServerRenderDisplay(),只返回黑屏。 你是对的。 CARenderServerRenderDisplay() 在 iOS7 上不起作用。您是否在 iOS 7 上尝试过上述代码?一年前我在做这个项目,我还没有在 iOS 7 上检查过。 上面的代码在iOS7上运行良好,但不适用于游戏......游戏只给我黑屏。我试图对最新的 RecordMyScreen 进行逆向工程,似乎他们仍然使用 CARenderServerRenderDisplay() 来捕获屏幕截图,不知道它是如何工作的。 【参考方案1】:我做了一些实验并得出结论,即使在内容传输完成之前,从中复制内容的屏幕表面也在发生变化(调用 IOSurfaceAcceleratorTransferSurface() )。我正在使用一个锁(尝试了异步和只读),但它被 iOS 覆盖了。我将锁定/解锁部分之间的代码更改为以下最小值:
IOSurfaceLock(screenSurface, kIOSurfaceLockReadOnly, &aseed);
aseed1 = IOSurfaceGetSeed(screenSurface);
IOSurfaceAcceleratorTransferSurface(outAcc, screenSurface, destSurf, dict, NULL);
aseed2 = IOSurfaceGetSeed(screenSurface);
IOSurfaceUnlock(screenSurface, kIOSurfaceLockReadOnly, &aseed);
GetSeed 函数判断表面的内容是否已更改。而且,我记录了一个计数,指示种子更改的帧数。计数非零。所以,下面的代码解决了这个问题:
if(aseed1 != aseed2)
//Release the created surface
continue; //Do not use this surface/frame since it has artefacts
但这确实会影响性能,因为许多框架/表面由于伪影而被拒绝。 对此的任何添加/更正都会有所帮助。
【讨论】:
我遇到了非常相似的问题,试图找出输出视频失真的原因,但仍然找不到答案。有没有办法我可以访问您用来获取屏幕捕获工作的其余功能?喜欢你可以将它们上传到github吗?保重 您是否尝试过我提到的解决方案,即比较aseed1 和aseed2,如果种子不同则丢弃?这将确保图像被正确复制。以上是关于IOSurfaces - 视频中的伪影,无法抓取视频表面的主要内容,如果未能解决你的问题,请参考以下文章
如何摆脱使用 SkiaSharp 创建的 ImageSource 中的伪影