本案例实现使用Metal读取视频文件,并渲染到屏幕上。(此时显示是没有声音的)
思路:
- 使用AVFundation中的AVAssetReaderTrackOutput方法,并将原始数据传入到CMSempleBuffer中。CMSempleBuffer存储的是每一帧的数据。
- Metal渲染回调将 CMSempleBuffer中的数据转成CVPixelBufferRef。
- 使用CoreVideo获取Y纹理和UV纹理。
- 自定义片元函数将YUV转成RGBA,显示出来。
1. 顶点函数和片元函数
- 定点函数
在顶点函数中需要传入顶点坐标和纹理坐标。
//结构体(用于顶点函数输出/片元函数输入)
typedef struct
{
float4 clipSpacePosition [[position]]; // position的修饰符表示这个是顶点
float2 textureCoordinate; // 纹理坐标
} RasterizerData;
//RasterizerData 返回数据类型->片元函数
// vertex_id是顶点shader每次处理的index,用于定位当前的顶点
// buffer表明是缓存数据,0是索引
vertex RasterizerData
vertexShader(uint vertexID [[ vertex_id ]],
constant CCVertex *vertexArray [[ buffer(CCVertexInputIndexVertices) ]])
{
RasterizerData out;
//顶点坐标
out.clipSpacePosition = vertexArray[vertexID].position;
//纹理坐标
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
return out;
}
- 片元函数
YUV转RGB使用转换矩阵:
float3 rgb = convertMatrix->matrix * (yuv + convertMatrix->offset);
在片元函数中需要传入Y、UV纹理和转换矩阵。
fragment float4
samplingShader(RasterizerData input [[stage_in]],
texture2d<float> textureY [[ texture(CCFragmentTextureIndexTextureY) ]],
texture2d<float> textureUV [[ texture(CCFragmentTextureIndexTextureUV) ]],
constant CCConvertMatrix *convertMatrix [[ buffer(CCFragmentInputIndexMatrix) ]])
{
//1.获取纹理采样器
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear);
/*
2. 读取YUV 颜色值
textureY.sample(textureSampler, input.textureCoordinate).r
从textureY中的纹理采集器中读取,纹理坐标对应上的R值.(Y)
textureUV.sample(textureSampler, input.textureCoordinate).rg
从textureUV中的纹理采集器中读取,纹理坐标对应上的RG值.(UV)
*/
float3 yuv = float3(textureY.sample(textureSampler, input.textureCoordinate).r,
textureUV.sample(textureSampler, input.textureCoordinate).rg);
//3.将YUV 转化为 RGB值.convertMatrix->matrix * (YUV + convertMatrix->offset)
float3 rgb = convertMatrix->matrix * (yuv + convertMatrix->offset);
//4.返回颜色值(RGBA)
return float4(rgb, 1.0);
}
2. 设置纹理
- 从现有图像缓冲区创建核心视频Metal纹理缓冲区。 将每一帧的数据从缓存中复制到Metal的纹理缓冲区。
参数1: allocator 内存分配器,默认kCFAllocatorDefault
参数2: textureCache 纹理缓存区对象
参数3: sourceImage 视频图像缓冲区
参数4: textureAttributes 纹理参数字典.默认为NULL
参数5: pixelFormat 图像缓存区数据的Metal 像素格式常量.注意如果MTLPixelFormatBGRA8Unorm和摄像头采集时设置的颜色格式不一致,则会出现图像异常的情况;
参数6: width,纹理图像的宽度(像素)
参数7: height,纹理图像的高度(像素)
参数8: planeIndex.如果图像缓冲区是平面的,则为映射纹理数据的平面索引。对于非平面图像缓冲区忽略。
参数9: textureOut,返回时,返回创建的Metal纹理缓冲区。
// Mapping a BGRA buffer:
CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, pixelBuffer, NULL, MTLPixelFormatBGRA8Unorm, width, height, 0, &outTexture);
// Mapping the luma plane of a 420v buffer:
CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, pixelBuffer, NULL, MTLPixelFormatR8Unorm, width, height, 0, &outTexture);
// Mapping the chroma plane of a 420v buffer as a source texture:
CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, pixelBuffer, NULL, MTLPixelFormatRG8Unorm width/2, height/2, 1, &outTexture);
// Mapping a yuvs buffer as a source texture (note: yuvs/f and 2vuy are unpacked and resampled -- not colorspace converted)
CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, pixelBuffer, NULL, MTLPixelFormatGBGR422, width, height, 1, &outTexture);
- 从CMSampleBuffer读取CVPixelBuffer
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
- 返回纹理缓冲区的Metal纹理对象。 将临时的纹理对象赋值到全局的纹理对象。
CVMetalTextureGetTexture(</