SCNViewをビデオに録画するには、いくつのステップが必要ですか?

コアアイデア

SCNViewのコールバックを介して、メタルレンダーパスのレンダリングターゲットテクスチャを取得id<SCNSceneRenderer>し、レンダリングターゲットテクスチャをCVPixelBufferに基づいて作成されたメタルテクスチャにコピーしてから、CVPixelBufferをビデオファイルに書き込みます。

ビデオライティングツールを準備する

ビデオの作成は、システム独自の組み込みAVAssetWriterコードを使用して簡単に実行できます。このコードは、次のコードを使用して作成および構成できます。AVAssetWriter

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は、次のAPIを介してビデオファイルに書き込むことができます

[self.writerAdaptor appendPixelBuffer:cvBuffer withPresentationTime:presentationTime]
复制代码

CVPixelBufferは画像データを受け入れる準備ができています

CVPixelBufferを作成し、CVPixelBufferからMTLTextureを作成して、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);
复制代码

SCNViewのレンダリング結果をCVPixelBufferにコピーします

SCNViewのコールバックでレンダリング結果をコピーする必要があり- (void)renderer:(id<SCNSceneRenderer>)renderer didRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)timeます。SCNViewのレンダリングターゲットテクスチャをコピーするには、次の設定を行う必要があります。

CAMetalLayer *metalLayer = (CAMetalLayer *)self.sceneView.layer;
[metalLayer setAllowsNextDrawableTimeout:NO];
metalLayer.framebufferOnly = NO;
复制代码

コールバックではMTLBlitCommandEncoder、レンダリングターゲットテクスチャを独自のテクスチャにコピーします

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これはSCNViewから取得commandQueueされるため、テクスチャをコピーするコマンドはSCNViewのレンダリング後に実行され、コピーされたテクスチャが完全なレンダリングであることを確認します。blitCommandBufferブロッキングスレッドは使用できないことに注意してくださいwaitUntilCompleted。これにより、デッドロックが発生します。最後にupdateWithFrame、CVPixelBufferを記述しますAVAssetWriter

おすすめ

転載: juejin.im/post/7119884702999117854
おすすめ