如何以 60fps 录制离屏 NSView
How to record an off-screen NSView at 60fps
我想在 macOS 上从屏幕外的 NSView 运行 录制视频输出(编码或未编码)。我很确定没有 API 可以做到这一点,但是我相信将它逐帧渲染到帧缓冲区中是可行的。
问题是我找不到以足够快的速度渲染视图的方法。我尝试过但没有成功的方法(在 MacBook M1 Pro 运行 Monterey 上测试过):
[view dataWithPDFInsideRect:]
和 [view dataWithEPSInsideRect:]
:执行大约需要 200 毫秒。
[view.layer renderInContext:]
: 执行大约需要 350 毫秒。
[view cacheDisplayInRect: toBitmapImageRep:]
: 执行大约需要 100 毫秒。
我还尝试将视图嵌入 window 并捕获 window。 Window 捕获功能(例如 CGWindowListCreateImage
)快得多,但在 windows 不在屏幕上时不起作用。
考虑到视图可以在 window 中以 60fps 的速度呈现而没有问题,为什么这些方法要花这么多时间?有没有我错过了将 NSView 渲染到帧缓冲区的方法?
我终于找到了一种高效的方法。通过以这种方式捕捉视图,我能够达到 60+ fps。
NSView* view = ...;
NSBitmapImageRep* bitmap = [
[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:nil
pixelsWide:view.bounds.size.width
pixelsHigh:view.bounds.size.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat:0
bytesPerRow:(4 * view.bounds.size.width)
bitsPerPixel:32
];
NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:graphicsContext];
[view displayRectIgnoringOpacity:view.bounds inContext:graphicsContext];
[NSGraphicsContext restoreGraphicsState];
// pixels are in format [R, G, B, A, R, G, B, A, ...]
unsigned char* pixels = [bitmap bitmapData];
我想在 macOS 上从屏幕外的 NSView 运行 录制视频输出(编码或未编码)。我很确定没有 API 可以做到这一点,但是我相信将它逐帧渲染到帧缓冲区中是可行的。
问题是我找不到以足够快的速度渲染视图的方法。我尝试过但没有成功的方法(在 MacBook M1 Pro 运行 Monterey 上测试过):
[view dataWithPDFInsideRect:]
和[view dataWithEPSInsideRect:]
:执行大约需要 200 毫秒。[view.layer renderInContext:]
: 执行大约需要 350 毫秒。[view cacheDisplayInRect: toBitmapImageRep:]
: 执行大约需要 100 毫秒。
我还尝试将视图嵌入 window 并捕获 window。 Window 捕获功能(例如 CGWindowListCreateImage
)快得多,但在 windows 不在屏幕上时不起作用。
考虑到视图可以在 window 中以 60fps 的速度呈现而没有问题,为什么这些方法要花这么多时间?有没有我错过了将 NSView 渲染到帧缓冲区的方法?
我终于找到了一种高效的方法。通过以这种方式捕捉视图,我能够达到 60+ fps。
NSView* view = ...;
NSBitmapImageRep* bitmap = [
[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:nil
pixelsWide:view.bounds.size.width
pixelsHigh:view.bounds.size.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat:0
bytesPerRow:(4 * view.bounds.size.width)
bitsPerPixel:32
];
NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:graphicsContext];
[view displayRectIgnoringOpacity:view.bounds inContext:graphicsContext];
[NSGraphicsContext restoreGraphicsState];
// pixels are in format [R, G, B, A, R, G, B, A, ...]
unsigned char* pixels = [bitmap bitmapData];