idea principal
A través de la devolución de llamada de SCNView, obtenga id<SCNSceneRenderer>
la textura de destino de representación de metal renderPass, copie la textura de destino de representación en la textura de metal creada en base a CVPixelBuffer y luego escriba CVPixelBuffer en el archivo de video.
Preparar la herramienta de escritura de video
La creación de videos se puede realizar fácilmente utilizando el propio AVAssetWriter
código integrado del sistema, que se puede crear y configurar a través del siguiente códigoAVAssetWriter
self.videoWriter = [AVAssetWriter assetWriterWithURL:outputURL fileType:AVFileTypeQuickTimeMovie error:&error];
NSDictionary *writerInputParams = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecTypeH264, AVVideoCodecKey,
[NSNumber numberWithInt:width], AVVideoWidthKey,
[NSNumber numberWithInt:height], AVVideoHeightKey,
AVVideoScalingModeResizeAspectFill, AVVideoScalingModeKey,
nil];
self.assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:writerInputParams];
if ([self.videoWriter canAddInput:self.assetWriterInput]) {
[self.videoWriter addInput:self.assetWriterInput];
} else {
// show error message
}
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange], (NSString*)kCVPixelBufferPixelFormatTypeKey,
[NSNumber numberWithBool:YES], (NSString *)kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES], (NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey,
nil];
self.writerAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:self.assetWriterInput sourcePixelBufferAttributes:attributes];
复制代码
CVPixelBuffer se puede escribir en un archivo de video a través de la siguiente API
[self.writerAdaptor appendPixelBuffer:cvBuffer withPresentationTime:presentationTime]
复制代码
CVPixelBuffer listo para aceptar datos de imagen
Cree CVPixelBuffer y cree MTLTexture a partir de CVPixelBuffer para copiar la textura de destino de procesamiento de SCNView
int imageWidth = size.width;
int imageHeight = size.height;
NSDictionary *pixelAttributes = @{( id )kCVPixelBufferIOSurfacePropertiesKey : @{}};
CVPixelBufferCreate(
kCFAllocatorDefault,
imageWidth,
imageHeight,
kCVPixelFormatType_32BGRA,
(__bridge CFDictionaryRef)pixelAttributes,
&_renderTargetPixelBuffer);
CVReturn ret = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _mtlDevice, nil, &_textureCache);
CVMetalTextureRef renderTargetMetalTextureRef;
ret = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCache, _renderTargetPixelBuffer, nil, MTLPixelFormatBGRA8Unorm, imageWidth, imageHeight, 0, &renderTargetMetalTextureRef);
id<MTLTexture> mtlTexture = CVMetalTextureGetTexture(renderTargetMetalTextureRef);
CFRelease(renderTargetMetalTextureRef);
复制代码
Copie el resultado de renderizado de SCNView a CVPixelBuffer
Necesitamos - (void)renderer:(id<SCNSceneRenderer>)renderer didRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)time
copiar el resultado de renderizado en la devolución de llamada de SCNView. Para copiar la textura de destino de renderizado de SCNView, se deben realizar las siguientes configuraciones
CAMetalLayer *metalLayer = (CAMetalLayer *)self.sceneView.layer;
[metalLayer setAllowsNextDrawableTimeout:NO];
metalLayer.framebufferOnly = NO;
复制代码
En la devolución de llamada, MTLBlitCommandEncoder
copiamos la textura de destino del renderizado en nuestra propia textura .
MTLRenderPassDescriptor *renderPassDesc = renderer.currentRenderPassDescriptor;
id<MTLTexture> readTexture = renderPassDesc.colorAttachments[0].texture;
if (readTexture.width != _renderTargetTexture.width || readTexture.height != _renderTargetTexture.height) {
_renderTargetTexture = [self createRenderTargetTexture:CGSizeMake(readTexture.width, readTexture.height)];
}
id<MTLCommandBuffer> blitCommandBuffer = [renderer.commandQueue commandBuffer];
id <MTLBlitCommandEncoder> blitEncoder = [blitCommandBuffer blitCommandEncoder];
[blitEncoder copyFromTexture:readTexture toTexture:_renderTargetTexture];
[blitEncoder endEncoding];
[blitCommandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull buffer) {
[self updateWithFrame:self->_renderTargetPixelBuffer];
}];
[blitCommandBuffer commit];
复制代码
blitCommandBuffer
Viene de SCNView commandQueue
, por lo que el comando para copiar la textura se ejecutará después de renderizar SCNView, lo que garantiza que la textura copiada sea una renderización completa. Tenga en cuenta que blitCommandBuffer
no se pueden usar subprocesos de bloqueo waitUntilCompleted
, lo que conducirá a interbloqueos. Finalmente, updateWithFrame
escribe el CVPixelBuffer AVAssetWriter
.