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

参考:


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

查看所有标签

猜你喜欢:

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

深入理解SPARK

深入理解SPARK

耿嘉安 / 机械工业出版社 / 2016-1-1 / 99

《深入理解SPARK:核心思想与源码分析》结合大量图和示例,对Spark的架构、部署模式和工作模块的设计理念、实现源码与使用技巧进行了深入的剖析与解读。 《深入理解SPARK:核心思想与源码分析》一书对Spark1.2.0版本的源代码进行了全面而深入的分析,旨在为Spark的优化、定制和扩展提供原理性的指导。阿里巴巴集团专家鼎力推荐、阿里巴巴资深Java开发和大数据专家撰写。 本书分为......一起来看看 《深入理解SPARK》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具