iOS中為直播APP集成美顏功能

最近需要給直播項(xiàng)目中添加美顏的功能,調(diào)研了很多SDK和開(kāi)源代碼(視決,涂圖,七牛,金山云,videoCore等),綜合成本/效果/對(duì)項(xiàng)目侵入性,最后決定使用一款基于GPUImage實(shí)現(xiàn)的 BeautifyFaceDemo美顏濾鏡。

關(guān)于濾鏡代碼和實(shí)現(xiàn)思路可以到BeautifyFace Github和作者琨君簡(jiǎn)書(shū)中查看。

集成GPUImageBeautifyFilter和GPUImage Framework

首先需要集成好GPUImage,通過(guò)觀(guān)察目前iOS平臺(tái),90%以上美顏方案都是基于這個(gè)框架來(lái)做的。
原來(lái)項(xiàng)目中的AVCaptureDevice需要替換成GPUImageVideoCamera,刪除諸如AVCaptureSession/AVCaptureDeviceInput/AVCaptureVideoDataOutput這種GPUImage實(shí)現(xiàn)了的部分。修改一些生命周期,攝像頭切換,橫豎屏旋轉(zhuǎn)等相關(guān)邏輯,保證前后行為統(tǒng)一。

聲明需要的屬性

 @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的相關(guān)設(shè)置

  //這里我在GPUImageBeautifyFilter中增加個(gè)了初始化方法用來(lái)設(shè)置美顏程度intensity
  self.beautifyFilter = [[GPUImageBeautifyFilter alloc] initWithIntensity:0.6];

為filterView增加美顏濾鏡

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

然后調(diào)用startCameraCapture方法就可以看到效果了

[self.videoCamera startCameraCapture];

到這里,僅僅是屏幕顯示的內(nèi)容帶有濾鏡效果,而作為直播應(yīng)用,還需要輸出帶有美顏效果的視頻流

輸出帶有美顏效果的視頻流

剛開(kāi)始集成的時(shí)候碰見(jiàn)一個(gè)坑,原本的邏輯是實(shí)現(xiàn)AVCaptureVideoDataOutputSampleBufferDelegate方法來(lái)獲得原始幀

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

而GPUImageVideoCamera也實(shí)現(xiàn)了一個(gè)類(lèi)似的代理:

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

而替換之后發(fā)現(xiàn)輸出的流依舊是未經(jīng)美顏的圖像,看了實(shí)現(xiàn)后發(fā)現(xiàn)果不其然,GPUImageVideoCameraDelegate還是通過(guò)AVCaptureVideoDataOutputSampleBufferDelegate直接返回的數(shù)據(jù),所以想輸出帶有濾鏡的流這里就得借助GPUImageRawDataOutput了

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

這個(gè)GPUImageRawDataOutput其實(shí)就是beautifyFilter的輸出工具,可在setNewFrameAvailableBlock方法的block中獲得帶有濾鏡效果的數(shù)據(jù)

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

[rawDataOutput setNewFrameAvailableBlock:^{
    __strong GPUImageRawDataOutput *strongOutput = weakOutput;
    [strongOutput lockFramebufferForReading];
 
    // 這里就可以獲取到添加濾鏡的數(shù)據(jù)了
    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進(jìn)行硬編碼再結(jié)合rtmp協(xié)議傳輸視頻流了
    [weakSelf encodeWithCVPixelBufferRef:pixelBuffer];

    [strongOutput unlockFramebufferAfterReading];
    CFRelease(pixelBuffer);
    
}];

目前依舊存在的問(wèn)題

經(jīng)過(guò)和其他產(chǎn)品對(duì)比,GPUImageBeautifyFilter磨皮效果和花椒最為類(lèi)似。這里采用雙邊濾波, 花椒應(yīng)該用了高斯模糊實(shí)現(xiàn)。同印客對(duì)比,美白效果一般。

還存在些關(guān)于性能的問(wèn)題:

1 調(diào)用setNewFrameAvailableBlock后很多機(jī)型只能跑到不多不少15fps
2 在6s這代機(jī)型上溫度很高,幀率可到30fps但不穩(wěn)定

Update(8-13)

  1. 關(guān)于性能問(wèn)題,最近把項(xiàng)目中集成的美顏濾鏡(BeautifyFace)里用到的 GPUImageCannyEdgeDetectionFilter 替換為 GPUImageSobelEdgeDetectionFilter 會(huì)有很大改善,而且效果幾乎一致,6s經(jīng)過(guò)長(zhǎng)時(shí)間測(cè)試沒(méi)有再次出現(xiàn)高溫警告了。(替換也十分簡(jiǎn)單,直接改倆處類(lèi)名/變量名就可以了)

  2. 分享一個(gè)BUG,最近發(fā)現(xiàn)當(dāng)開(kāi)啟美顏的時(shí)候,關(guān)閉直播內(nèi)存竟然沒(méi)有釋放。分析得出GPUImageRawDataOutput的setNewFrameAvailableBlock方法的block參數(shù)仍然保持著self,解決思路就是將GPUImageRawDataOutput移除。

先附上之前的相關(guān)release代碼:

    [self.videoCamera stopCameraCapture];
    [self.videoCamera removeInputsAndOutputs];
    [self.videoCamera removeAllTargets];

開(kāi)始以為camera調(diào)用removeAllTargets會(huì)把camera上面的filter,以及filter的output一同釋放,但實(shí)際camera并不會(huì)'幫忙'移除filter的target,所以需要添加:

    [self.beautifyFilter removeAllTargets]; //修復(fù)開(kāi)啟美顏內(nèi)存無(wú)法釋放的問(wèn)題

關(guān)閉美顏output是直接加在camera上,camera直接removeAllTargets就可以;
開(kāi)啟美顏output加在filter上,camera和filter都需要removeAllTargets。

最后編輯于
?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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