直播SDK加入GPU自定义美颜

前言

直播SDK提供了默认美颜,效果一般,不支持Mac。项目要求能自定义美颜,并支持Mac,iOS双端。找了好多的文章,试了都不行,谷歌了几篇文章,加上自己摸索,凑起来总算搞通了。写下来给要接直播美颜SDK的伙伴捋一捋,PS:玩音视频的各路大神别见笑。

了解情况

查了SDK暴露出来的接口,跟系统AVFoundation的回调方法一致,直播SDK提供了Block回调,返回封装好的CMSampleBufferRef数据,然后在Block里把CMSampleBufferRef数据传输到SDK内部方法推流。

- (void)processVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer {
  // do something 
 [[SDK sharedSDK].netCallManager sendVideoSampleBuffer:sampleBuffer];
}
复制代码

大概思路:

  1. 取出图像数据CVPixelBufferRef,时间戳CMSampleTimingInfo
  2. 对CVPixelBufferRef进行美颜处理
  3. 重新封装CMSampleBufferRef发送到SDK方法内推流

采取的美颜滤镜(非第三方SDK,支持Mac,iOS):

  • iOS自带的CIFilter (单一滤镜多,貌似没现成组合美颜滤镜)
  • 强大的GPUImage(有现成组合过成的GPUImageBeautifyFilter美颜滤镜,且Demo有各种效果预览)

GPUImage用法 input -》do something -》output

网上很多关于GPUImage的用法是对视频文件,或者直接用Camera作为输入源。缺少直接对一帧图像进行处理,所以问题在于如何对CVPixelBufferRef进行滤镜处理,然后得到处理完的CVPixelBufferRef数据。对为此特意查阅很多资料(参考的网址在最后),并翻了翻GPUImage的代码。

  • GPUImage Framework的文件很清晰,Sources输入源,Filters是各种滤镜,Outputs输出源。

    用法基本可以列为管道式,addTarget 相当于 -》: 
    [Sources初始化对象 addTarget:Filter]; // 输入源 -》滤镜
    [filter addTarget:output输出源对象];  // 滤镜 -》输出源	
    复制代码
  • 输入源采用GPUImageMovie,因为里面有一个- (void)processMovieFrame:(CMSampleBufferRef)movieSampleBuffer方法,支持输入一帧进行处理。(注意初始化Movie一定要用initWithAsset的方法,传入nil,否则内部的GPUImageContext不会初始化)

  • 滤镜采取已经组合成美颜滤镜的GPUImageBeautifyFilter,它是一个FilterGroup。

  • 输出的时候,使用GPUImageRawDataOutput,newFrameAvailableBlock里取出图像数据,屏幕一片灰色。如果用GPUImageView直接addSubview显示,能成功显示用滤镜处理过的画面。

证明数据已经成功处理,在GPUImageMovie的processMovieFrame方法里也找到newFrameAvailableBlock的回调,回调确实有执行。

问题则是我在lockFramebufferForReading和unlockFramebufferAfterReading中间读取帧数据的操作不对,想到这边读数据这么复杂,就想着这个渲染完的数据到底放在哪里了,有没有别的办法取出?

在前面说过GPUImageBeautifyFilter是一个GPUImageFilterGroup,GPUImageFilterGroup的父类是GPUImageOutput,这就意味着,即使我不添加任何output的target,数据也是可以拿到的。

再翻了下资料,GPUImageOutput的头文件,发现一个frameProcessingCompletionBlock,这个跟我原来的processMovieFrame好像很对应。网上也有说怎么从GPUImageOutput取出帧数据。那就行了,直接从GPUImageBeautifyFilter里取,不用设置output的Target,大功告成。

代码代码代码

**说了一堆废话,不好意思。献上代码:**GPUImageBeautifyFilter代码在参考链接的最后一个

    // 初始化Filter和GPUImageMovie
    _beautifyFilter = [[GPUImageBeautifyFilter alloc] init];
	_gpumovie = [[GPUImageMovie alloc] initWithAsset:nil]; // 初始化内部数据结构
	[_gpumovie addTarget:_beautifyFilter]; //连接过滤器
复制代码

// 重新封装CMSampleBufferRef,并交给SDK推流
// 如果是rtmp协议传输视频流,自己用VideoToolBox封装。
- (void)sendVideoSampleBuffer:(CVPixelBufferRef)bufferRef time:(CMSampleTimingInfo)timingInfo  {
       CMSampleBufferRef newSampleBuffer = NULL;
       CMFormatDescriptionRef outputFormatDescription = NULL;
       CMVideoFormatDescriptionCreateForImageBuffer( kCFAllocatorDefault, bufferRef, &outputFormatDescription );
	   OSStatus err = CMSampleBufferCreateForImageBuffer( kCFAllocatorDefault, bufferRef, true, NULL, NULL, outputFormatDescription, &timingInfo, &newSampleBuffer );
       if(newSampleBuffer) {
          [[SDK sharedSDK].netCallManager sendVideoSampleBuffer:newSampleBuffer];
       }else {
          NSString *exceptionReason = [NSString stringWithFormat:@"sample buffer create failed (%i)", (int)err];
          @throw [NSException exceptionWithName:NSInvalidArgumentException reason:exceptionReason userInfo:nil];
       }
}
复制代码

最重要的处理部分:

// 此方法由直播SDK负责回调或者代理执行
// 自定义美颜时最好把直播SDK默认美颜关闭
- (void)processVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer {

    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
	//取出帧数据/时间戳 -》处理帧数据美颜 -》根据时间戳与像素数据重新封装包:
    [_gpumovie processMovieFrame:sampleBuffer]; // kCVPixelFormatType_420YpCbCr8BiPlanarFullRange 可能需要检查

    CMSampleTimingInfo timingInfo = {
		.duration               = CMSampleBufferGetDuration(sampleBuffer),
		.presentationTimeStamp  =   CMSampleBufferGetPresentationTimeStamp(sampleBuffer),
        .decodeTimeStamp        = CMSampleBufferGetDecodeTimeStamp(sampleBuffer)
    };
    __weak typeof(self) weakSelf = self;
    [_beautifyFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
 		GPUImageFramebuffer *imageFramebuffer = output.framebufferForOutput;
  		glFinish();
		[weakSelf sendVideoSampleBuffer: [imageFramebuffer getRenderTarget] time:timingInfo];
	 }];
}
复制代码

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值