iOS 中为直播 APP 集成美颜功能

2,744 阅读2分钟
原文链接: www.jianshu.com

最近需要给直播项目中添加美颜的功能,调研了很多SDK和开源代码(视决,涂图,七牛,金山云,videoCore等),综合成本/效果/对项目侵入性,最后决定使用一款基于GPUImage实现的 BeautifyFaceDemo美颜滤镜。

关于滤镜代码和实现思路可以到BeautifyFace Github和作者琨君简书中查看。

集成GPUImageBeautifyFilter和GPUImage Framework

首先需要集成好GPUImage,通过观察目前iOS平台,90%以上美颜方案都是基于这个框架来做的。
原来项目中的AVCaptureDevice需要替换成GPUImageVideoCamera,删除诸如AVCaptureSession/AVCaptureDeviceInput/AVCaptureVideoDataOutput这种GPUImage实现了的部分。修改一些生命周期,摄像头切换,横竖屏旋转等相关逻辑,保证前后行为统一。

声明需要的属性

 @property (nonatomic, strong) GPUImageVideoCamera *videoCamera;  
 //屏幕上显示的View
 @property (nonatomic, strong) GPUImageView *filterView;
 //BeautifyFace美颜滤镜
 @property (nonatomic, strong) GPUImageBeautifyFilter *beautifyFilter;

然后初始化

  self.sessionPreset = AVCaptureSessionPreset1280x720;
  self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:self.sessionPreset cameraPosition:AVCaptureDevicePositionBack];

  self.filterView = [[GPUImageView alloc] init];
  [self.view insertSubview:self.filterView atIndex:1]; //省略frame的相关设置

  //这里我在GPUImageBeautifyFilter中增加个了初始化方法用来设置美颜程度intensity
  self.beautifyFilter = [[GPUImageBeautifyFilter alloc] initWithIntensity:0.6];

为filterView增加美颜滤镜

[self.videoCamera addTarget:self.beautifyFilter];
[self.beautifyFilter addTarget:self.filterView];

然后调用startCameraCapture方法就可以看到效果了

[self.videoCamera startCameraCapture];

到这里,仅仅是屏幕显示的内容带有滤镜效果,而作为直播应用,还需要输出带有美颜效果的视频流

输出带有美颜效果的视频流

刚开始集成的时候碰见一个坑,原本的逻辑是实现AVCaptureVideoDataOutputSampleBufferDelegate方法来获得原始帧

- (void) captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;

而GPUImageVideoCamera也实现了一个类似的代理:

@protocol GPUImageVideoCameraDelegate 
@optional
- (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer;
@end

而替换之后发现输出的流依旧是未经美颜的图像,看了实现后发现果不其然,GPUImageVideoCameraDelegate还是通过AVCaptureVideoDataOutputSampleBufferDelegate直接返回的数据,所以想输出带有滤镜的流这里就得借助GPUImageRawDataOutput了

CGSize outputSize = {720, 1280};
GPUImageRawDataOutput *rawDataOutput = [[GPUImageRawDataOutput alloc] initWithImageSize:CGSizeMake(outputSize.width, outputSize.height) resultsInBGRAFormat:YES];
[self.beautifyFilter addTarget:rawDataOutput];

这个GPUImageRawDataOutput其实就是beautifyFilter的输出工具,可在setNewFrameAvailableBlock方法的block中获得带有滤镜效果的数据

__weak GPUImageRawDataOutput *weakOutput = rawDataOutput;
__weak typeof(self) weakSelf = self;

[rawDataOutput setNewFrameAvailableBlock:^{
    __strong GPUImageRawDataOutput *strongOutput = weakOutput;
    [strongOutput lockFramebufferForReading];

    // 这里就可以获取到添加滤镜的数据了
    GLubyte *outputBytes = [strongOutput rawBytesForImage];
    NSInteger bytesPerRow = [strongOutput bytesPerRowInOutput];
    CVPixelBufferRef pixelBuffer = NULL;
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault, outputSize.width, outputSize.height, kCVPixelFormatType_32BGRA, outputBytes, bytesPerRow, nil, nil, nil, &pixelBuffer);

    // 之后可以利用VideoToolBox进行硬编码再结合rtmp协议传输视频流了
    [weakSelf encodeWithCVPixelBufferRef:pixelBuffer];

    [strongOutput unlockFramebufferAfterReading];
    CFRelease(pixelBuffer);

}];

目前依旧存在的问题

经过和其他产品对比,GPUImageBeautifyFilter磨皮效果和花椒最为类似。这里采用双边滤波, 花椒应该用了高斯模糊实现。同印客对比,美白效果一般。

还存在些关于性能的问题:

1 调用setNewFrameAvailableBlock后很多机型只能跑到不多不少15fps
2 在6s这代机型上温度很高,帧率可到30fps但不稳定