直播SDK加入GPU自定义美颜

栏目: IOS · 发布时间: 5年前

内容简介:直播SDK提供了默认美颜,效果一般,不支持Mac。项目要求能自定义美颜,并支持Mac,iOS双端。找了好多的文章,试了都不行,谷歌了几篇文章,加上自己摸索,凑起来总算搞通了。写下来给要接直播美颜SDK的伙伴捋一捋,PS:玩音视频的各路大神别见笑。查了SDK暴露出来的接口,跟系统AVFoundation的回调方法一致,直播SDK提供了Block回调,返回封装好的CMSampleBufferRef数据,然后在Block里把CMSampleBufferRef数据传输到SDK内部方法推流。采取的美颜滤镜(非第三方

直播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];
	 }];
}
复制代码

参考:


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

高效前端:Web高效编程与优化实践

高效前端:Web高效编程与优化实践

李银城 著 / 机械工业出版社 / 2018-3-15 / 89.00元

这不是一本单纯讲解前端编程技巧的书,而是一本注重思想提升和内功修炼的书。 全书以问题为导向,精选了前端开发中的34个疑难问题,从分析问题的原因入手,逐步给出解决方案,并分析各种方案的优劣,最后针对每个问题总结出高效编程的最佳实践和各种性能优化的方法。 全书共7章,内容从逻辑上大致可以分为两大类: 第一类,偏向实践,围绕HTML、CSS、JavaScript等传统前端技术,以及PW......一起来看看 《高效前端:Web高效编程与优化实践》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具