Metal--視頻渲染

案例效果圖如下:


視頻渲染.gif

案例流程如下:

  • 使用自定義的CCAssetReader工具類,讀取mov/mp4視頻文件
  • Metal渲染回調(diào) 還原成CMSampleBufferRef圖像數(shù)據(jù),然后將讀取到CVPixelBufferRef視頻像素緩存區(qū)
  • 通過CoreVideo獲取Y紋理,UV紋理
  • 在自定義著色器將顏色編碼格式由YUV轉(zhuǎn)換為RGB,顯示到屏幕上

CCAssetReader

CCAssetReader的功能與AVAssetReader與類似。

AVAssetReader功能

  • 直接從存儲(chǔ)中讀取原始未解碼的媒體樣本,獲取解碼為可渲染形式的樣本。
  • 混合資產(chǎn)的多個(gè)?軌,并使?和組合多個(gè)視頻軌道

流程圖如下:

AVAssetReader.png

CCAssetReader代碼

  • CCAssetReader.h
//
//  CCAssetReader.h
//  002--MetalRenderMOV
//
//  Created by CC老師 on 2019/5/7.
//  Copyright ? 2019年 CC老師. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface CCAssetReader : NSObject

//初始化
- (instancetype)initWithUrl:(NSURL *)url;

//從MOV文件讀取CMSampleBufferRef 數(shù)據(jù)
- (CMSampleBufferRef)readBuffer;
@end
  • CCAssetReader.m
//
//  CCAssetReader.m
//  002--MetalRenderMOV
//
//  Created by CC老師 on 2019/5/7.
//  Copyright ? 2019年 CC老師. All rights reserved.
//

#import "CCAssetReader.h"

@implementation CCAssetReader
{
    //軌道
    AVAssetReaderTrackOutput *readerVideoTrackOutput;
    //AVAssetReader可以從原始數(shù)據(jù)里獲取解碼后的音視頻數(shù)據(jù)
    AVAssetReader   *assetReader;
    //視頻地址
    NSURL *videoUrl;
    //鎖
    NSLock *lock;
}

//初始化
- (instancetype)initWithUrl:(NSURL *)url{
    
    self = [super init];
    if(self != nil)
    {
        videoUrl = url;
        lock = [[NSLock alloc]init];
        [self setUpAsset];
    }
    return self;
}

//Asset 相關(guān)設(shè)置
-(void)setUpAsset{
   
    //AVURLAssetPreferPreciseDurationAndTimingKey 默認(rèn)為NO,YES表示提供精確的時(shí)長
    NSDictionary *inputOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
    
    //1. 創(chuàng)建AVURLAsset 是AVAsset 子類,用于從本地/遠(yuǎn)程URL初始化資源
    AVURLAsset *inputAsset = [[AVURLAsset alloc] initWithURL:videoUrl options:inputOptions];
    
    //2.異步加載資源
    //weakSelf 解決循環(huán)引用
    __weak typeof(self) weakSelf = self;
    
    //定義屬性名稱
    NSString *tracks = @"tracks";
   
    //對(duì)資源所需的鍵執(zhí)行標(biāo)準(zhǔn)的異步載入操作,這樣就可以訪問資源的tracks屬性時(shí),就不會(huì)受到阻礙.
    [inputAsset loadValuesAsynchronouslyForKeys:@[tracks] completionHandler: ^{
        
            //延長self 生命周期
            __strong typeof(self) strongSelf = weakSelf;
       
      //開辟子線程并發(fā)隊(duì)列異步函數(shù)來處理讀取的inputAsset
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSError *error = nil;
    
            //獲取狀態(tài)碼.
            AVKeyValueStatus tracksStatus = [inputAsset statusOfValueForKey:@"tracks" error:&error];
            //如果狀態(tài)不等于成功加載,則返回并打印錯(cuò)誤信息
            if (tracksStatus != AVKeyValueStatusLoaded)
            {
                NSLog(@"error %@", error);
                return;
            }
            //處理讀取的inputAsset
            [weakSelf processWithAsset:inputAsset];
        });
    }];
    
}

//處理獲取到的asset
- (void)processWithAsset:(AVAsset *)asset
{
    //鎖定
    [lock lock];
    NSLog(@"processWithAsset");
    NSError *error = nil;
    
    //1.創(chuàng)建AVAssetReader
    assetReader = [AVAssetReader assetReaderWithAsset:asset error:&error];
    
    //2.kCVPixelBufferPixelFormatTypeKey 像素格式.
    /*
     kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange : 420v
     kCVPixelFormatType_32BGRA : iOS在內(nèi)部進(jìn)行YUV至BGRA格式轉(zhuǎn)換
     */
    NSMutableDictionary *outputSettings = [NSMutableDictionary dictionary];
    [outputSettings setObject:@(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) forKey:(id)kCVPixelBufferPixelFormatTypeKey];
    
    /*3. 設(shè)置readerVideoTrackOutput
     assetReaderTrackOutputWithTrack:(AVAssetTrack *)track outputSettings:(nullable NSDictionary<NSString *, id> *)outputSettings
     參數(shù)1: 表示讀取資源中什么信息
     參數(shù)2: 視頻參數(shù)
     */
    readerVideoTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] outputSettings:outputSettings];
    
    //alwaysCopiesSampleData : 表示緩存區(qū)的數(shù)據(jù)輸出之前是否會(huì)被復(fù)制.YES:輸出總是從緩存區(qū)提供復(fù)制的數(shù)據(jù),你可以自由的修改這些緩存區(qū)數(shù)據(jù)
    readerVideoTrackOutput.alwaysCopiesSampleData = NO;
    
    //4.為assetReader 填充輸出
    [assetReader addOutput:readerVideoTrackOutput];
    
    //5.assetReader 開始讀取.并且判斷是否開始.
    if ([assetReader startReading] == NO)
    {
        NSLog(@"Error reading from file at URL: %@", asset);
    }
    
    //取消鎖
    [lock unlock];
}

//讀取Buffer 數(shù)據(jù)
- (CMSampleBufferRef)readBuffer {
    //鎖定
    [lock lock];
    CMSampleBufferRef sampleBufferRef = nil;
    
    //1.判斷readerVideoTrackOutput 是否創(chuàng)建成功.
    if (readerVideoTrackOutput) {
       
        //復(fù)制下一個(gè)緩存區(qū)的內(nèi)容到sampleBufferRef
        sampleBufferRef = [readerVideoTrackOutput copyNextSampleBuffer];
    }
    
    //2.判斷assetReader 并且status 是已經(jīng)完成讀取 則重新清空readerVideoTrackOutput/assetReader.并重新初始化它們
    if (assetReader && assetReader.status == AVAssetReaderStatusCompleted) {
        NSLog(@"customInit");
        readerVideoTrackOutput = nil;
        assetReader = nil;
        [self setUpAsset];
    }
    
    //取消鎖
    [lock unlock];
    
    //3.返回讀取到的sampleBufferRef 數(shù)據(jù)
    return sampleBufferRef;
}
@end

LeoShaderTypes.h

  • 定義一個(gè)結(jié)構(gòu)體,存儲(chǔ)頂點(diǎn)結(jié)構(gòu)數(shù)據(jù)
typedef struct
{
    //頂點(diǎn)坐標(biāo)(x,y,z,w)
    vector_float4 position;
    //紋理坐標(biāo)(s,t)
    vector_float2 textureCoordinate;
} LeoVertex;
  • 設(shè)置一個(gè)轉(zhuǎn)換矩陣機(jī)構(gòu)體,從YUV轉(zhuǎn)換到RGB
//轉(zhuǎn)換矩陣 YUV - RGB轉(zhuǎn)換矩陣結(jié)構(gòu)
typedef struct {
    //三維矩陣
    matrix_float3x3 matrix;
    //偏移量
    vector_float3 offset;
} LeoConvertMatrix;
  • 頂點(diǎn)索引數(shù)據(jù)
//頂點(diǎn)函數(shù)輸入索引
typedef enum LeoVertexInputIndex
{
    LeoVertexInputIndexVertices     = 0,
} LeoVertexInputIndex;
  • 片元函數(shù)緩存區(qū)索引
//片元函數(shù)緩存區(qū)索引
typedef enum LeoFragmentBufferIndex
{
    LeoFragmentInputIndexMatrix     = 0,
} LeoFragmentBufferIndex;

  • 片元函數(shù)紋理索引
//片元函數(shù)紋理索引
typedef enum LeoFragmentTextureIndex
{
    //Y紋理
    LeoFragmentTextureIndexTextureY     = 0,
    //UV紋理
    LeoFragmentTextureIndexTextureUV     = 1,
} LeoFragmentTextureIndex;

LeoShaders.metal

  • 定義一個(gè)結(jié)構(gòu)體用來存放從頂點(diǎn)著色器到片元著色器的數(shù)據(jù)
//結(jié)構(gòu)體(用于頂點(diǎn)函數(shù)輸出/片元函數(shù)輸入)
typedef struct{
    float4 clipSpacePosition [[position]]; // position的修飾符表示這個(gè)是頂點(diǎn)
    float2 textureCoordinate; // 紋理坐標(biāo)    
} RasterizerData;
  • 頂點(diǎn)著色器函數(shù),這個(gè)項(xiàng)目中,我們只需要將頂點(diǎn)坐標(biāo)和紋理坐標(biāo)傳遞到片元著色器
//RasterizerData 返回?cái)?shù)據(jù)類型->片元函數(shù)
// vertex_id是頂點(diǎn)shader每次處理的index,用于定位當(dāng)前的頂點(diǎn)
// buffer表明是緩存數(shù)據(jù),0是索引
vertex RasterizerData
vertexShader(uint vertexID [[ vertex_id ]],
             constant LeoVertex *vertexArray [[buffer(LeoVertexInputIndexVertices)]])
{
    RasterizerData out;
    //頂點(diǎn)坐標(biāo)
    out.clipSpacePosition = vertexArray[vertexID].position;
    //紋理坐標(biāo)
    out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
    return out;
}
  • 片元著色器的任務(wù)就是講YUV轉(zhuǎn)換成RGB
// stage_in表示這個(gè)數(shù)據(jù)來自光柵化。(光柵化是頂點(diǎn)處理之后的步驟,業(yè)務(wù)層無法修改)
// texture表明是紋理數(shù)據(jù),CCFragmentTextureIndexTextureY是索引
// texture表明是紋理數(shù)據(jù),CCFragmentTextureIndexTextureUV是索引
// buffer表明是緩存數(shù)據(jù), CCFragmentInputIndexMatrix是索引
fragment float4
samplingShader(RasterizerData input [[stage_in]],
               texture2d<float> textureY [[ texture(LeoFragmentTextureIndexTextureY) ]],
               texture2d<float> textureUV [[ texture(LeoFragmentTextureIndexTextureUV) ]],
               constant LeoConvertMatrix *convertMatrix [[ buffer(LeoFragmentInputIndexMatrix) ]])
{
    //1.獲取紋理采樣器
    constexpr sampler textureSampler (mag_filter::linear,
                                      min_filter::linear);
    /*
     2. 讀取YUV 顏色值
        textureY.sample(textureSampler, input.textureCoordinate).r
        從textureY中的紋理采集器中讀取,紋理坐標(biāo)對(duì)應(yīng)上的R值.(Y)
        textureUV.sample(textureSampler, input.textureCoordinate).rg
        從textureUV中的紋理采集器中讀取,紋理坐標(biāo)對(duì)應(yīng)上的RG值.(UV)
     */
    float3 yuv = float3(textureY.sample(textureSampler, input.textureCoordinate).r,
                        textureUV.sample(textureSampler, input.textureCoordinate).rg);
    
    //3.將YUV 轉(zhuǎn)化為 RGB值.convertMatrix->matrix * (YUV + convertMatrix->offset)
    float3 rgb = convertMatrix->matrix * (yuv + convertMatrix->offset);
    
    //4.返回顏色值(RGBA)
    return float4(rgb, 1.0);
}

ViewController

  • 初始化MTKView
-(void)setupMTKView{
    //1.初始化mtkView
    self.mtkView = [[MTKView alloc] initWithFrame:self.view.bounds];
    // 獲取默認(rèn)的device
    self.mtkView.device = MTLCreateSystemDefaultDevice();
    //設(shè)置self.view = self.mtkView;
    self.view = self.mtkView;
    //設(shè)置代理
    self.mtkView.delegate = self;
    //獲取視口size
    self.viewportSize = (vector_uint2){self.mtkView.drawableSize.width, self.mtkView.drawableSize.height};
}
  • 設(shè)置CCAssetReader,同時(shí)創(chuàng)建紋理緩存CVMetalTextureCacheRef
//2.CCAssetReader設(shè)置
-(void)setupCCAsset{
    //注意CCAssetReader 支持MOV/MP4文件都可以
    //1.視頻文件路徑
    //NSURL *url = [[NSBundle mainBundle] URLForResource:@"kun" withExtension:@"mov"];
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"kun2" withExtension:@"mp4"];
    //2.初始化CCAssetReader
    self.reader = [[CCAssetReader alloc] initWithUrl:url];
    //3._textureCache的創(chuàng)建(通過CoreVideo提供給CPU/GPU高速緩存通道讀取紋理數(shù)據(jù))
    CVMetalTextureCacheCreate(NULL, NULL, self.mtkView.device, NULL, &_textureCache);
}
  • 設(shè)置渲染管道
// 設(shè)置渲染管道
-(void)setupPipeline {
    
    //1 獲取.metal
    /*
     newDefaultLibrary: 默認(rèn)一個(gè)metal 文件時(shí),推薦使用
     newLibraryWithFile:error: 從Library 指定讀取metal 文件
     newLibraryWithData:error: 從Data 中獲取metal 文件
     */
    id<MTLLibrary> defaultLibrary = [self.mtkView.device newDefaultLibrary];
    // 頂點(diǎn)shader,vertexShader是函數(shù)名
    id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
    // 片元shader,samplingShader是函數(shù)名
    id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"samplingShader"];
    
    //2.渲染管道描述信息類
    MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
    //設(shè)置vertexFunction
    pipelineStateDescriptor.vertexFunction = vertexFunction;
    //設(shè)置fragmentFunction
    pipelineStateDescriptor.fragmentFunction = fragmentFunction;
    // 設(shè)置顏色格式
    pipelineStateDescriptor.colorAttachments[0].pixelFormat = self.mtkView.colorPixelFormat;
    
    //3.初始化渲染管道根據(jù)渲染管道描述信息
    // 創(chuàng)建圖形渲染管道,耗性能操作不宜頻繁調(diào)用
    self.pipelineState = [self.mtkView.device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor
                                                                             error:NULL];
    //4.CommandQueue是渲染指令隊(duì)列,保證渲染指令有序地提交到GPU
    self.commandQueue = [self.mtkView.device newCommandQueue];
}
  • 設(shè)置頂點(diǎn)
// 設(shè)置頂點(diǎn)
- (void)setupVertex {
    
    //1.頂點(diǎn)坐標(biāo)(x,y,z,w);紋理坐標(biāo)(x,y)
    //注意: 為了讓視頻全屏鋪滿,所以頂點(diǎn)大小均設(shè)置[-1,1]
    static const LeoVertex quadVertices[] =
    {   // 頂點(diǎn)坐標(biāo),分別是x、y、z、w;    紋理坐標(biāo),x、y;
        { {  1.0, -1.0, 0.0, 1.0 },  { 1.f, 1.f } },
        { { -1.0, -1.0, 0.0, 1.0 },  { 0.f, 1.f } },
        { { -1.0,  1.0, 0.0, 1.0 },  { 0.f, 0.f } },
        
        { {  1.0, -1.0, 0.0, 1.0 },  { 1.f, 1.f } },
        { { -1.0,  1.0, 0.0, 1.0 },  { 0.f, 0.f } },
        { {  1.0,  1.0, 0.0, 1.0 },  { 1.f, 0.f } },
    };
    
    //2.創(chuàng)建頂點(diǎn)緩存區(qū)
    self.vertices = [self.mtkView.device newBufferWithBytes:quadVertices
                                                     length:sizeof(quadVertices)
                                                    options:MTLResourceStorageModeShared];
    //3.計(jì)算頂點(diǎn)個(gè)數(shù)
    self.numVertices = sizeof(quadVertices) / sizeof(LeoVertex);
}
  • 設(shè)置轉(zhuǎn)換矩陣,在Metal中可以將RGB轉(zhuǎn)換成YUV
// 設(shè)置YUV->RGB轉(zhuǎn)換的矩陣
- (void)setupMatrix {
    
    //1.轉(zhuǎn)化矩陣
    // BT.601, which is the standard for SDTV.
    matrix_float3x3 kColorConversion601DefaultMatrix = (matrix_float3x3){
        (simd_float3){1.164,  1.164, 1.164},
        (simd_float3){0.0, -0.392, 2.017},
        (simd_float3){1.596, -0.813,   0.0},
    };
    
    // BT.601 full range
    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},
    };
   
    // BT.709, which is the standard for HDTV.
    matrix_float3x3 kColorConversion709DefaultMatrix[] = {
        (simd_float3){1.164,  1.164, 1.164},
        (simd_float3){0.0, -0.213, 2.112},
        (simd_float3){1.793, -0.533,   0.0},
    };
    
    //2.偏移量
    vector_float3 kColorConversion601FullRangeOffset = (vector_float3){ -(16.0/255.0), -0.5, -0.5};
    
    //3.創(chuàng)建轉(zhuǎn)化矩陣結(jié)構(gòu)體.
    LeoConvertMatrix matrix;
    //設(shè)置轉(zhuǎn)化矩陣
    /*
     kColorConversion601DefaultMatrix;
     kColorConversion601FullRangeMatrix;
     kColorConversion709DefaultMatrix;
     */
    matrix.matrix = kColorConversion601FullRangeMatrix;
    //設(shè)置offset偏移量
    matrix.offset = kColorConversion601FullRangeOffset;
    
    //4.創(chuàng)建轉(zhuǎn)換矩陣緩存區(qū).
    self.convertMatrix = [self.mtkView.device newBufferWithBytes:&matrix
                                                        length:sizeof(LeoConvertMatrix)
                                                options:MTLResourceStorageModeShared];
}
  • 執(zhí)行drawableSizeWillChange
//當(dāng)MTKView size 改變則修改self.viewportSize
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
    //設(shè)置視口    
    self.viewportSize = (vector_uint2){size.width, size.height};
}
  • 繪制視圖
//視圖繪制
- (void)drawInMTKView:(MTKView *)view {
  
    //1.每次渲染都要單獨(dú)創(chuàng)建一個(gè)CommandBuffer
    id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];
    //獲取渲染描述信息
    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
   
    //2. 從CCAssetReader中讀取圖像數(shù)據(jù)
    CMSampleBufferRef sampleBuffer = [self.reader readBuffer];
    
    //3.判斷renderPassDescriptor 和 sampleBuffer 是否已經(jīng)獲取到了?
    if(renderPassDescriptor && sampleBuffer)
    {
        //4.設(shè)置renderPassDescriptor中顏色附著(默認(rèn)背景色)
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.5, 0.5, 1.0f);
        
        //5.根據(jù)渲染描述信息創(chuàng)建渲染命令編碼器
        id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
        
        //6.設(shè)置視口大小(顯示區(qū)域)
        [renderEncoder setViewport:(MTLViewport){0.0, 0.0, self.viewportSize.x, self.viewportSize.y, -1.0, 1.0 }];
        
        //7.為渲染編碼器設(shè)置渲染管道
        [renderEncoder setRenderPipelineState:self.pipelineState];
        
        //8.設(shè)置頂點(diǎn)緩存區(qū)
        [renderEncoder setVertexBuffer:self.vertices
                                offset:0
                               atIndex:LeoVertexInputIndexVertices];
        
        //9.設(shè)置紋理(將sampleBuffer數(shù)據(jù) 設(shè)置到renderEncoder 中)
        [self setupTextureWithEncoder:renderEncoder buffer:sampleBuffer];
        
        //10.設(shè)置片元函數(shù)轉(zhuǎn)化矩陣
        [renderEncoder setFragmentBuffer:self.convertMatrix
                                  offset:0
                                 atIndex:LeoFragmentInputIndexMatrix];
        
        //11.開始繪制
        [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
                          vertexStart:0
                          vertexCount:self.numVertices];
        
        //12.結(jié)束編碼
        [renderEncoder endEncoding];
        
        //13.顯示
        [commandBuffer presentDrawable:view.currentDrawable];
    }
    
    //14.提交命令
    [commandBuffer commit];
   
}
  • sampleBuffer數(shù)據(jù) 設(shè)置到renderEncoder
// 設(shè)置紋理
- (void)setupTextureWithEncoder:(id<MTLRenderCommandEncoder>)encoder buffer:(CMSampleBufferRef)sampleBuffer {
    
    //1.從CMSampleBuffer讀取CVPixelBuffer,
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    
    id<MTLTexture> textureY = nil;
    id<MTLTexture> textureUV = nil;
   
    //textureY 設(shè)置
    {
        //2.獲取紋理的寬高
        size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
        size_t height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
        
        //3.像素格式:普通格式,包含一個(gè)8位規(guī)范化的無符號(hào)整數(shù)組件。
        MTLPixelFormat pixelFormat = MTLPixelFormatR8Unorm;
        
        //4.創(chuàng)建CoreVideo的Metal紋理
        CVMetalTextureRef texture = NULL;
        
        /*5. 根據(jù)視頻像素緩存區(qū) 創(chuàng)建 Metal 紋理緩存區(qū)
         CVReturn CVMetalTextureCacheCreateTextureFromImage(CFAllocatorRef allocator,
         CVMetalTextureCacheRef textureCache,
         CVImageBufferRef sourceImage,
         CFDictionaryRef textureAttributes,
         MTLPixelFormat pixelFormat,
         size_t width,
         size_t height,
         size_t planeIndex,
         CVMetalTextureRef  *textureOut);
         
         功能: 從現(xiàn)有圖像緩沖區(qū)創(chuàng)建核心視頻Metal紋理緩沖區(qū)。
         參數(shù)1: allocator 內(nèi)存分配器,默認(rèn)kCFAllocatorDefault
         參數(shù)2: textureCache 紋理緩存區(qū)對(duì)象
         參數(shù)3: sourceImage 視頻圖像緩沖區(qū)
         參數(shù)4: textureAttributes 紋理參數(shù)字典.默認(rèn)為NULL
         參數(shù)5: pixelFormat 圖像緩存區(qū)數(shù)據(jù)的Metal 像素格式常量.注意如果MTLPixelFormatBGRA8Unorm和攝像頭采集時(shí)設(shè)置的顏色格式不一致,則會(huì)出現(xiàn)圖像異常的情況;
         參數(shù)6: width,紋理圖像的寬度(像素)
         參數(shù)7: height,紋理圖像的高度(像素)
         參數(shù)8: planeIndex.如果圖像緩沖區(qū)是平面的,則為映射紋理數(shù)據(jù)的平面索引。對(duì)于非平面圖像緩沖區(qū)忽略。
         參數(shù)9: textureOut,返回時(shí),返回創(chuàng)建的Metal紋理緩沖區(qū)。
         */
        CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, self.textureCache, pixelBuffer, NULL, pixelFormat, width, height, 0, &texture);
        
        //6.判斷textureCache 是否創(chuàng)建成功
        if(status == kCVReturnSuccess)
        {
            //7.轉(zhuǎn)成Metal用的紋理
            textureY = CVMetalTextureGetTexture(texture);
           
            //8.使用完畢釋放
            CFRelease(texture);
        }
    }
    
    //9.textureUV 設(shè)置(同理,參考于textureY 設(shè)置)
    {
        size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
        size_t height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
        MTLPixelFormat pixelFormat = MTLPixelFormatRG8Unorm;
        CVMetalTextureRef texture = NULL;
        CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, self.textureCache, pixelBuffer, NULL, pixelFormat, width, height, 1, &texture);
        if(status == kCVReturnSuccess)
        {
            textureUV = CVMetalTextureGetTexture(texture);
            CFRelease(texture);
        }
    }
    
    //10.判斷textureY 和 textureUV 是否讀取成功
    if(textureY != nil && textureUV != nil)
    {
        //11.向片元函數(shù)設(shè)置textureY 紋理
        [encoder setFragmentTexture:textureY atIndex:LeoFragmentTextureIndexTextureY];
        //12.向片元函數(shù)設(shè)置textureUV 紋理
        [encoder setFragmentTexture:textureUV atIndex:LeoFragmentTextureIndexTextureUV];
    }
    
    //13.使用完畢,則將sampleBuffer 及時(shí)釋放
    CFRelease(sampleBuffer);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容