Понимание использования металла с видеовыходами AVCaptureSession

Я пытаюсь понять, как правильно управлять видеовыходом (CMPixelBuffer) с помощью Metal.

Насколько я понимаю, есть MTKView. Каждому CMPixelBuffer из видеовыхода назначается какой-то из Metal Texture. Итак, окончательный превью от MTKView?

Когда я вижу окончательный результат на экране, это:

1)CMSampleBuffer->Metal->CMSampleBuffer

or

2)CMSampleBuffer->Metal->MTKView

Довольно запутался. Может кто-нибудь поставить на место?


person Roi Mulia    schedule 06.06.2016    source источник


Ответы (2)


Я рекомендую вам сделать это в два этапа. Во-первых, научитесь использовать Металл и MTKView, чтобы рисовать что-нибудь, что угодно на экране. Во-вторых, после того, как вы научитесь рисовать с помощью Metal (создавать и кодировать в буферы команд, отображать чертежи и т. Д.), Вы можете применить текстуру, которую вы генерируете из CMSampleBuffer, к полноэкранному квадрату или любой другой геометрии, которую вы хотите.

здесь доступен образец захвата камеры от @McZonk, который реализует простой пример этого. Он использует двухплоскостной буфер выборки YCbCr, но, если он доступен, вы также можете запросить буфер выборки в формате BGRA, который затем можно преобразовать в один MTLTexture с помощью CVMetalTextureCacheCreateTextureFromImage и выполнить выборку в шейдере без дальнейшего ручного преобразования.

person warrenm    schedule 06.06.2016
comment
Уорренм, я думаю, что нашел своего наставника в области Металла :) Спасибо за подробный ответ. Можно ли организовать 10-минутный консультационный чат? (оплачивается, если необходимо), у меня просто есть куча вопросительных знаков, которые я хотел бы решить, и, к сожалению, Интернет еще не дал мне ответов. В любом случае - спасибо, сэр! - person Roi Mulia; 06.06.2016
comment
Я буду на связи :) - person warrenm; 06.06.2016
comment
Я собираюсь спамить этот ответ, чтобы создать чат. - person Roi Mulia; 06.06.2016
comment
Я чувствую себя довольно глупо, говоря о GOT при застрявшем ответе - person Roi Mulia; 06.06.2016
comment
Пожалуйста, начните чат !! - person Roi Mulia; 06.06.2016
comment
Предполагается, что для создания одного комментария будет два способа? - person Roi Mulia; 06.06.2016
comment
Проверьте свою электронную почту;) - person warrenm; 06.06.2016
comment
Ха-ха - это круто! - person Roi Mulia; 06.06.2016
comment
Уоррен, я отправил вам подробный вопрос к прикрепленному к письму электронному письму, которое вы сказали мне отправить. Хотел бы получить ответ на это - очень взволнован! Спасибо! - person Roi Mulia; 06.06.2016
comment
Спасибо за ответ @warrenm! Не могли бы вы также объяснить, в чем сложность CVMetalTextureCacheCreateTextureFromImage? Действительно ли мы выполняем какое-либо копирование памяти при преобразовании CVPixelBuffer в текстуру Metal? - person karama; 08.08.2019
comment
@karama Я бы не ожидал, что эта функция выполнит копию. Вместо этого он, вероятно, использует API, например IOSurface, для разделения памяти между изображением и текстурой. - person warrenm; 08.08.2019

Простейший минимум кода, необходимый для преобразования из буфера выборки, созданного видеокамерой, в текстуру Metal:

@import UIKit;
@import AVFoundation;
@import CoreMedia;
#import <MetalKit/MetalKit.h>
#import <Metal/Metal.h>
#import <MetalPerformanceShaders/MetalPerformanceShaders.h>

@interface ViewController : UIViewController <MTKViewDelegate, AVCaptureVideoDataOutputSampleBufferDelegate>  {
    NSString *_displayName;
    NSString *serviceType;
}

@property (retain, nonatomic) SessionContainer *session;
@property (retain, nonatomic) AVCaptureSession *avSession;

@end;

#import "ViewController.h"

@interface ViewController () {
    MTKView *_metalView;

    id<MTLDevice> _device;
    id<MTLCommandQueue> _commandQueue;
    id<MTLTexture> _texture;

    CVMetalTextureCacheRef _textureCache;
}

@property (strong, nonatomic) AVCaptureDevice *videoDevice;
@property (nonatomic) dispatch_queue_t sessionQueue;

@end

@implementation ViewController

- (void)viewDidLoad {
    NSLog(@"%s", __PRETTY_FUNCTION__);
    [super viewDidLoad];

    _device = MTLCreateSystemDefaultDevice();
    _metalView = [[MTKView alloc] initWithFrame:self.view.bounds];
    [_metalView setContentMode:UIViewContentModeScaleAspectFit];
    _metalView.device = _device;
    _metalView.delegate = self;
    _metalView.clearColor = MTLClearColorMake(1, 1, 1, 1);
    _metalView.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
    _metalView.framebufferOnly = NO;
    _metalView.autoResizeDrawable = NO;

    CVMetalTextureCacheCreate(NULL, NULL, _device, NULL, &_textureCache);

    [self.view addSubview:_metalView];

    self.sessionQueue = dispatch_queue_create( "session queue", DISPATCH_QUEUE_SERIAL );

    if ([self setupCamera]) {
        [_avSession startRunning];
    }
}

- (BOOL)setupCamera {
    NSLog(@"%s", __PRETTY_FUNCTION__);
    @try {
        NSError * error;

            _avSession = [[AVCaptureSession alloc] init];
            [_avSession beginConfiguration];
            [_avSession setSessionPreset:AVCaptureSessionPreset640x480];

            // get list of devices; connect to front-facing camera
            self.videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
            if (self.videoDevice == nil) return FALSE;

            AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:self.videoDevice error:&error];
            [_avSession addInput:input];

            dispatch_queue_t sampleBufferQueue = dispatch_queue_create("CameraMulticaster", DISPATCH_QUEUE_SERIAL);

            AVCaptureVideoDataOutput * dataOutput = [[AVCaptureVideoDataOutput alloc] init];
            [dataOutput setAlwaysDiscardsLateVideoFrames:YES];
            [dataOutput setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
            [dataOutput setSampleBufferDelegate:self queue:sampleBufferQueue];

            [_avSession addOutput:dataOutput];
            [_avSession commitConfiguration]; 
    } @catch (NSException *exception) {
        NSLog(@"%s - %@", __PRETTY_FUNCTION__, exception.description);
        return FALSE;
    } @finally {
        return TRUE;
    }

}

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    {
        size_t width = CVPixelBufferGetWidth(pixelBuffer);
        size_t height = CVPixelBufferGetHeight(pixelBuffer);

        CVMetalTextureRef texture = NULL;
        CVReturn status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCache, pixelBuffer, NULL, MTLPixelFormatBGRA8Unorm, width, height, 0, &texture);
        if(status == kCVReturnSuccess)
        {
            _metalView.drawableSize = CGSizeMake(width, height);
            _texture = CVMetalTextureGetTexture(texture);
            _commandQueue = [_device newCommandQueue];
            CFRelease(texture);
        }
    }
}

- (void)drawInMTKView:(MTKView *)view {
    // creating command encoder
    if (_texture) {
        id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
        id<MTLTexture> drawingTexture = view.currentDrawable.texture;

        // set up and encode the filter
        MPSImageGaussianBlur *filter = [[MPSImageGaussianBlur alloc] initWithDevice:_device sigma:5];

        [filter encodeToCommandBuffer:commandBuffer sourceTexture:_texture destinationTexture:drawingTexture];

        // committing the drawing
        [commandBuffer presentDrawable:view.currentDrawable];
        [commandBuffer commit];
        _texture = nil;
    }
}

- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {

}

@end
person James Bush    schedule 28.08.2017