Mac OS 使用Metal渲染NV12、YUV420、CMSampleBufferRef视频

Mac OS 使用Metal渲染NV12、YUV420、CMSampleBufferRef视频


资料较少,整合后仅作为记录学习使用。

需求

yuv420原始视频数据使用metal渲染。

MTKView初始化

vector_uint2 viewportSize;
MTKView *mMtkview;
id <MTLDevice> mDevice;
id <MTLCommandQueue> mCmdQueue;
id <MTLRenderPipelineState> mPipeline;
id <MTLBuffer> mBuffer;
id <MTLTexture> mTexture;
CVMetalTextureCacheRef mTextureCache;
NSUInteger vertexCount;

id<MTLTexture> mTextureY;
id<MTLTexture> mTextureUV;

id <MTLBuffer> mConvertMatrix;

setMetal

mMtkview = [[MTKView alloc] initWithFrame:self.view.frame device:MTLCreateSystemDefaultDevice()];
mDevice = mMtkview.device;
self.view = mMtkview;
mMtkview.delegate = self;
mCmdQueue = [mDevice newCommandQueue];

CVMetalTextureCacheCreate(NULL, NULL, mDevice, NULL, &mTextureCache);

setPipeline

id <MTLLibrary> library = [mDevice newDefaultLibrary];
id <MTLFunction> vertexfunc = [library newFunctionWithName:@"verfunc"];
id <MTLFunction> fragfunc = [library newFunctionWithName:@"fragfunc"];
MTLRenderPipelineDescriptor *renderdes = [MTLRenderPipelineDescriptor new];
renderdes.vertexFunction = vertexfunc;
renderdes.fragmentFunction = fragfunc;
renderdes.colorAttachments[0].pixelFormat = mMtkview.colorPixelFormat;
mPipeline = [mDevice newRenderPipelineStateWithDescriptor:renderdes error:nil];

setupMatrix

matrix_float3x3 kColorConversion601FullRangeMatrix = (matrix_float3x3){
    
    
    (simd_float3){
    
    1.0,    1.0,    1.0},
    (simd_float3){
    
    0.0,    -0.343, 1.765},
    (simd_float3){
    
    1.4,    -0.711, 0.0},
};
vector_float3 kColorConversion601FullRangeOffset = (vector_float3){
    
     -(16.0/255.0), -0.5, -0.5}; // 这个是偏移

HQHConvertMatrix converMatrix;
converMatrix.matrix = kColorConversion601FullRangeMatrix;
converMatrix.offset = kColorConversion601FullRangeOffset;
mConvertMatrix = [mDevice newBufferWithBytes:&converMatrix length:sizeof(HQHConvertMatrix) options:MTLResourceStorageModeShared];

MTKViewDelegate

- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
    
    
    viewportSize = (vector_uint2){
    
    size.width, size.height};
}

- (void)drawInMTKView:(MTKView *)view {
    
    
    if (mTextureY && mTextureUV) {
    
    
        id <MTLCommandBuffer> cmdBuffer = [mCmdQueue commandBuffer];
        MTLRenderPassDescriptor *passdes = view.currentRenderPassDescriptor;
        if (passdes != nil) {
    
    
            id <MTLRenderCommandEncoder> cmdEncoder = [cmdBuffer renderCommandEncoderWithDescriptor:passdes];
            [cmdEncoder setViewport:(MTLViewport){
    
    0.0,0.0,viewportSize.x,viewportSize.y, -1.0,1.0}];
            [cmdEncoder setRenderPipelineState:mPipeline];
            [cmdEncoder setVertexBuffer:mBuffer offset:0 atIndex:0];

            [cmdEncoder setFragmentTexture:mTextureY atIndex:0];
            [cmdEncoder setFragmentTexture:mTextureUV atIndex:1];
            [cmdEncoder setFragmentBuffer:mConvertMatrix offset:0 atIndex:HQHFragmentInputindexMatrix];
            [cmdEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:vertexCount];
            
            
            /**
             rgb 数据
             
            [cmdEncoder setFragmentTexture:mTexture atIndex:0];
            [cmdEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:vertexCount];
            */
            [cmdEncoder endEncoding];
            [cmdBuffer presentDrawable:view.currentDrawable];
            [cmdBuffer commit];
        }
    }
}

摄像头采集CMSampleBufferRef

AVCaptureSession *mCaptureSession;
AVCaptureDeviceInput *mCaptureInput;
AVCaptureVideoDataOutput *mCaptureOutput;

setupSession

mCaptureSession = [[AVCaptureSession alloc] init];
mCaptureSession.sessionPreset = AVCaptureSessionPreset1280x720;
if (@available(macOS 10.15, *)) {
    
    
    AVCaptureDevice *cameraDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
    mCaptureInput = [[AVCaptureDeviceInput alloc] initWithDevice:cameraDevice error:nil];
} else {
    
    
    AVCaptureDevice *cameraDevice = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo].firstObject;
    mCaptureInput = [[AVCaptureDeviceInput alloc] initWithDevice:cameraDevice error:nil];
}
if ([mCaptureSession canAddInput:mCaptureInput]) {
    
    
    [mCaptureSession addInput:mCaptureInput];
}

mCaptureOutput = [[AVCaptureVideoDataOutput alloc] init];
[mCaptureOutput setAlwaysDiscardsLateVideoFrames:NO];
[mCaptureOutput setSampleBufferDelegate:self queue:dispatch_queue_create("bd", DISPATCH_QUEUE_SERIAL)];

if ([mCaptureSession canAddOutput:mCaptureOutput]) {
    
    
    [mCaptureSession addOutput:mCaptureOutput];
}
NSLog(@"out = %@ ary = %@",mCaptureOutput,[mCaptureOutput availableVideoCodecTypes]);
[mCaptureOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
AVCaptureConnection *connection = [mCaptureOutput connectionWithMediaType:AVMediaTypeVideo];
[connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
[mCaptureSession startRunning];

AVCaptureVideoDataOutputSampleBufferDelegate

- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
    
    
    NSLog(@"data");
//    [self setTexture:sampleBuffer];
}

渲染CMSampleBufferRef

- (void)setTexture:(CMSampleBufferRef)samplebuffer {
    
    
    CVPixelBufferRef pixelbuffer = CMSampleBufferGetImageBuffer(samplebuffer);
    // textureY
    {
    
    
        size_t width = CVPixelBufferGetWidthOfPlane(pixelbuffer, 0);
        size_t height = CVPixelBufferGetHeightOfPlane(pixelbuffer, 0);
        CVMetalTextureRef texture = NULL;
        CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, mTextureCache, pixelbuffer, NULL, MTLPixelFormatR8Unorm, width, height, 0, &texture);
        if (status == kCVReturnSuccess) {
    
    
            mTextureY = CVMetalTextureGetTexture(texture);
            CFRelease(texture);
        }
    }
    // textureUV
    {
    
    
        size_t width = CVPixelBufferGetWidthOfPlane(pixelbuffer, 1);
        size_t height = CVPixelBufferGetHeightOfPlane(pixelbuffer, 1);
        CVMetalTextureRef texture = NULL;
        CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, mTextureCache, pixelbuffer, NULL, MTLPixelFormatRG8Unorm, width, height, 1, &texture);
        if (status == kCVReturnSuccess) {
    
    
            mTextureUV = CVMetalTextureGetTexture(texture);
            CFRelease(texture);
        }
    }
    
    size_t width = CVPixelBufferGetWidth(pixelbuffer);
    size_t heigth = CVPixelBufferGetHeight(pixelbuffer);
    CVMetalTextureRef temTexture = nil;
    CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, mTextureCache, pixelbuffer, NULL, MTLPixelFormatBGRA8Unorm, width, heigth, 0, &temTexture);
    if (status == kCVReturnSuccess) {
    
    
        mMtkview.drawableSize = CGSizeMake(width, heigth);
        mTexture = CVMetalTextureGetTexture(temTexture);
        CFRelease(temTexture);
    }
}

yuv420转NV12

void YUV420PtoNV12(unsigned char *Src, unsigned char* Dst,int Width,int Height){
    unsigned char* SrcU = Src + Width * Height;
    unsigned char* SrcV = SrcU + Width * Height / 4 ;
    memcpy(Dst, Src, Width * Height);
    unsigned char* DstU = Dst + Width * Height;
    for(int i = 0 ; i < Width * Height / 4 ; i++ ){
        ( *DstU++) = ( *SrcU++);
        ( *DstU++) = ( *SrcV++);
    }
}

渲染NV12

- (void)renderYUV420P:(EVFrame *)frame {
    
    
    int w = frame.vsize.width;
    int h = frame.vsize.height;
    unsigned char *i420 = frame.data;
    unsigned char *buffer = malloc(w*h*10);
    
    YUV420PtoNV12(i420, buffer, w, h);
    
    NSDictionary *pixelAttributes = @{
    
    (NSString*)kCVPixelBufferIOSurfacePropertiesKey:@{
    
    }};
    CVPixelBufferRef pixelBuffer = NULL;
    CVReturn result = CVPixelBufferCreate(kCFAllocatorDefault, w, h, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, (__bridge CFDictionaryRef)(pixelAttributes), &pixelBuffer);
    CVPixelBufferLockBaseAddress(pixelBuffer,0);
    void *yDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    unsigned char *y_ch0 = buffer;
    unsigned char *y_ch1 = buffer + w * h;
    memcpy(yDestPlane, y_ch0, w * h);
    void *uvDestPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
    // NV12
    memcpy(uvDestPlane, y_ch1, w * h * 0.5);
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
    
    if (result != kCVReturnSuccess) {
    
    
        NSLog(@"Unable to create cvpixelbuffer %d", result);
    }
    
    {
    
    
        size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
        size_t height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
        CVMetalTextureRef texture = NULL;
        CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, mTextureCache, pixelBuffer, NULL, MTLPixelFormatR8Unorm, width, height, 0, &texture);
        if (status == kCVReturnSuccess) {
    
    
            mTextureY = CVMetalTextureGetTexture(texture);
            CFRelease(texture);
        }
    }
    // textureUV
    {
    
    
        size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
        size_t height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
        CVMetalTextureRef texture = NULL;
        CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, mTextureCache, pixelBuffer, NULL, MTLPixelFormatRG8Unorm, width, height, 1, &texture);
        if (status == kCVReturnSuccess) {
    
    
            mTextureUV = CVMetalTextureGetTexture(texture);
            CFRelease(texture);
        }
    }

    size_t width = CVPixelBufferGetWidth(pixelBuffer);
    size_t heigth = CVPixelBufferGetHeight(pixelBuffer);
    CVMetalTextureRef temTexture = nil;
    CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, mTextureCache, pixelBuffer, NULL, MTLPixelFormatBGRA8Unorm, width, heigth, 0, &temTexture);
    if (status == kCVReturnSuccess) {
    
    
        mMtkview.drawableSize = CGSizeMake(width, heigth);
        mTexture = CVMetalTextureGetTexture(temTexture);
        CFRelease(temTexture);
    }
    
    CVPixelBufferRelease(pixelBuffer);
    free(buffer);
    buffer = NULL;
}

END

metal确实比OpenGL简单好用很多

猜你喜欢

转载自blog.csdn.net/quanhaoH/article/details/126585118
今日推荐