学习ios Metal(6)—前置相机视频流的实时渲染

        metal的基础知识入门,首推Metal By Example系列:http://metalbyexample.com/。博主的相关文章,主要给出工程实际遇到的典型问题及其解决方案。

            

       本节源码:https://github.com/sjy234sjy234/Learn-Metal/tree/master/TextureRenderer。这一次,主要介绍利用metal做视频流的实时渲染。源码实现了从前置摄像头获取实时的视频流,并且全屏渲染显示到一个视图中。

      首先,是ios的调用相机session的基础知识,建议自行搜索了解,或者直接看博主的封装。博主这里简单封装了一个FrontCamera类,只支持portrait方向的preset为AVCaptureSessionPresetHigh的前置摄像头的session调用。在ViewController中初始化FrontCamera实例,重写FrontCamera的代理,将获得实时刷新的视频流:

//FrontCameraDelegate
- (void)didOutputVideoBuffer:(CVPixelBufferRef)videoPixelBuffer
{
    if(videoPixelBuffer)
    {
        [self.textureRenderer draw: [_metalContext textureFromPixelBuffer: videoPixelBuffer]];
    }
}

        可以看到,实时的视频流格式是(CVPixelBufferRef)videoPixelBuffer,不能直接进行绘制,因此需要实现一个函数由videoPixelBuffer转化获取id<MTLTexture>格式的纹理texture,该方法被封装在MetalContext类中:

- (id<MTLTexture>) textureFromPixelBuffer:(CVPixelBufferRef)videoPixelBuffer
{
    id<MTLTexture> texture = nil;
    {
        size_t width = CVPixelBufferGetWidth(videoPixelBuffer);
        size_t height = CVPixelBufferGetHeight(videoPixelBuffer);
        MTLPixelFormat pixelFormat = MTLPixelFormatBGRA8Unorm;
        CVMetalTextureCacheRef textureCache;
        CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _device, nil, &textureCache);
        CVMetalTextureRef metalTextureRef = NULL;
        CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, textureCache, videoPixelBuffer, NULL, pixelFormat, width, height, 0, &metalTextureRef);
        if(status == kCVReturnSuccess)
        {
            texture = CVMetalTextureGetTexture(metalTextureRef);
            CFRelease(metalTextureRef);
            CFRelease(textureCache);
        }
    }
    return texture;
}

        由视频流转化得到纹理texture以后,传递给TextureRender类的实例绘制即可。而TextureRender类,封装了把一个texture绘制到整个MetalView上的方法,实质上就是把整个视图区域当成一个矩形,将纹理texture做为贴图贴上即可。纹理贴图的概念:https://github.com/sjy234sjy234/sample-code/tree/master/objc/06-Texturing。下面只给出TextureRender的shader代码:

#include <metal_stdlib>

using namespace metal;

struct TextureVertex
{
    float4 position [[position]];
    float2 texCoords;
};

vertex TextureVertex texture_vertex_main(constant TextureVertex *vertices [[buffer(0)]],
                                      uint vid [[vertex_id]])
{
    return vertices[vid];
}

fragment float4 texture_fragment_main(TextureVertex vert [[stage_in]],
                                 texture2d<float> videoTexture [[texture(0)]],
                                 sampler samplr [[sampler(0)]])
{
    float3 texColor = videoTexture.sample(samplr, vert.texCoords).rgb;
    float2 texCoords=vert.texCoords;
    float2 texCenter={0.5,0.5};
    if(distance(texCoords,texCenter)<=0.2859)
    {
        return float4(texColor, 1);
    }
    else
    {
//        return float4(texColor/3, 1);
        return float4(texColor, 1);
    }
}

        大家注意看一下倒数第二行的注释,可以尝试运行一下,可以对metal的纹理坐标值了解的更多一些,metal的纹理坐标值与openGL是有区别的,在fragment shader中它的范围是[0, 1]。

小结:        

      (1)视频流和纹理的渲染是非常简单的一件事情,之所以当作一节专门介绍,是因为它是实现离屏渲染的最后一个环节。在做渲染时,很多情况下,我们需要对三维渲染做图像后处理。这个时候就需要将中间结果先离屏渲染到一张纹理中,对纹理做一系列图像后处理,最后才把该纹理渲染到视图中。本文介绍的就是这最后一步的操作。此外,除了相机session的实时获取的视频流,无论是从I/O方式读取的文件视频流,还是从网络上实时获取的数据视频流,都是可以用本节的方式进行实时渲染的。

     (2)博主封装的FrontCamera相机设备类,还提供portrait方向的preset为AVCaptureSessionPreset640x480的真实感深度相机的session调用,只适用于iphone X系列。这是博主在ios平台上实现KinectFusion算法时用到的,需要调用iphone X的真实感深度相机的小伙伴可以用来参考。顺便吐槽一下iphone X真实感深度相机调用的槽点,官方文档没有给出相机session调用的demo,需要开发者自行摸索。想要获取实时的iphone X的实时深度帧,目前有两种方式:一种是使用ARSession的高层封装,官方给出了demo的,大家自行检索关键字ARKitFaceExample,这种方式的缺点是只能获取15fps的帧率,而且镜头的利用率似乎没有达到100%。另一种是使用相机session的调用,用法可以参考博主的FrontCamera类,这种方式的优点是能获取30fps的帧率,镜头利用率高,缺点是获取30fps的帧率的同时,获取的视频流的分辨率较低只有640x480,当然可以修改preset,但是其他的preset值要么不支持深度相机,要么只支持15fps的深度帧率,大家可以自己尝试修改preset。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值