伴随贴纸、短视频越来越火爆,这两项功能也基本成为各大拍照App的标配,但每个App的技术路线又都有所不同。Camera360 iOS技术负责人唐雷在LiveVideoStack Meet上与我们分享了Camera360在iOS端新玩法的探索尝试、技术实现以及遇到的坑和优化方案。
演讲 / 唐雷
整理 / LiveVideoStack
大家下午好,我是来自Camera360的唐雷,今天与大家一同分享Camera360 iOS端的音频优化。对于一款拍照软件,贴纸、美妆、特效现在已经成为一种标配,而我们最大的区别在于左下角的相册——它支持连拍,不需要拍照预览再去保存。从产品角度,我们最开始只是简单的拍照软件,拍风景再加上一些滤镜处理,到后面开始添加美妆、贴纸等功能,包括短视频也有尝试。
作为技术团队,整个Camera360产品演变之路就是如何保证产品的质量以及稳定性。而决定拍照软件的流畅度有几个因素:分辨率、SDK处理速度、人脸识别速度以及帧率。接下来的内容也会主要分享在这四个方面如何进行优化的。
正确的选择分辨率
对于用户而言,第一感受也是最直观的感受就是照片是否会糊,这一点对女性用1户更为重要,而影响照片糊的最大影响因素就是分辨率。不同手机的分辨率也是不一样的,正确选择分辨率就显得至关重要,因此我们也是长期在做手机分辨率的测试。Camera360全球有8亿用户,而其中一半以上都在东南亚——泰国、越南,对于这些国家iPhone4,4s以及5是主要机型,而网络条件也会相对差很多。
这张表格是我们对拍照导出分辨率的方案,iPhone 6以前的机型使用导出分辨率就是依照200万的拍照分辨率,6代和7代基本是以手机前置摄像头的分辨率规定,而iPhone8因为自身机器性能较好,我们则是选择导出原图。虽然7和7Plus摄像头分辨率和8代相同,但内存消耗相对比较高,因此无法按照原图导出。
良好的内存控制
在对分辨率调优后,我们就需要考虑内存控制问题。对于一张200万的照片,它的内存使用率就是200万(像素)乘以4(RGBA的4个通道)再除以1024、除以1024,也就是7.6兆,而前面提到Camera360的一个特色就是连拍功能,它就会产生几个照片的拷贝,特别在底层SDK做渲染的时候也会做双缓冲,这样一张照片就等于有几个实例,大概要消耗掉30几兆的内存。同时对于拍完的照片,我们首先会存一张80万的图片在沙盒,再去根据不同机型自动导出不同分辨率的照片。
这张表格是我们对iPhone6和6Plus内存消耗的测试,它们的基准分辨率都是200万。假如做一张200万的图进到相机取景页面,并把所有资源加载完之后的内存是155兆,当我拍一张照片时峰值可以到218兆,这其中的内存差值就有63兆,当然普通的平均值大致在30兆左右;假如对400万和800万的图做测试,虽然两者渲染时内存的波动不大,但内存峰值(也就是实际内存)的波动是很大的,在iPhone6上400万的图最大消耗96兆内存,800万则需要163兆,而iPhone实测的崩溃值大致在360-440兆,最大崩溃内存是645兆,也就是拍两张照片内存就已经很满了。而对于6Plus而言,200万的图就已经需要消耗很大内存了。
这是我们做的不同机型的连拍崩溃测试。比如iPhone6在快速点击拍照大致20次左右就会崩溃,因为此时队列已经被塞满了,即使处理再快或者有异步线程队列依然无法解决。因此我们做了冷却处理的优化,其实对于真实用户如此频繁点击拍照是不可能的,而且系统相机也不允许这么快的拍照,它自身调用系统API的时间也会有延迟。同时我们内部也建立了性能监控系统,包括“鹰眼系统”来记录日志,我们可以实时看到当前特效、美妆、贴纸的渲染速度,帧率、人脸识别速度,App内存使用情况、剩余内存情况、渲染总时间等等这些关键数值,并且通过“鹰眼系统” 记录下来,绿色代表在可控范围内、黄颜色代表警告、红色则表示出现问题。
人脸识别
提到美妆和贴纸,不可或缺的技术就是人脸识别,人脸一共有106个点位,基于一些点位就可以去做贴纸的贴合算法。人脸识别基本有两种算法:精准模式和快速识别模式。当拍一张照片时,Camera360内部会判断拍照者人脸区域变动幅度,当变动较小时会切换到精准模式,这时人脸识别度是非常高的,贴合度也很高,即使由于镜片反光出现的眼睛也是可以区分出来的;而快速识别模式则是针对人脸区域出现变动较大,这时我们会取第一个点和最后一个点,而中间过程则是通过算法计算出移动轨迹,这种方法在短视频的贴纸动态贴合上比较明显。
时间间隔也是人脸识别中重要的一环,现在的手机吐原始帧Buffer时满帧是30帧,我们内部满帧是24帧,而当已经识别到人脸后,是没有必要每一帧都再去做检测的,因为人脸信息大致一样,我已经可以保证快速切换模式,因此可以调整检测的时间间隔,比如设置为40帧检测一次,当取景页面中没有人像时则调整为10帧检测一次,保证当重新出现人像时可以快速上脸。
很多自拍用户都喜欢仰望星空的45度角拍照,但这样就有可能会导致只识别出一个眼睛或者嘴的点位不齐全等情况,进而导致贴合度出现问题,但这个问题至今也还没有解决。最后一个问题就是人脸识别比较消耗CPU和GPU,与我们的SDK抢占资源导致手机发热、发烫,因此我采用延迟处理的方式,包括前面提到动态调整算法的时间间隔,当已识别人脸后适当增大间隔时间,以及切换模式的方法来减小SDK功率消耗。
短视频探索
在做短视频的探索中我们也发现了一些坑,首先是我们采取的方案是边录边写,也就是原始buffer会通过SDK处理后进入队列,我们会同步对处理好的Buffer开始写视频,最后再和音频合并。需要注意的一点这里的视频是无声的,因此我们需要录制现场声音,这时就会面临麦克风的选择问题,iPhone4和4S有2个麦克风,到了iPhone5则有3个,6S之后更是有4个麦克风,其中一个是专门做降噪处理的。而iPhone一共有2个功放——听筒和底部,如果采集人声的麦克风和功放在同一个位置,那么素材声音就会把人声给盖住。因此在使用前置摄像头时会从前置摄像头旁边的麦克风收声,使用后置摄像头时会切换到闪光灯旁边的麦克风。
用户对于拍摄的视频会要求尽量小,同时还要保证清晰度足够高,因此视频参数设置也是一个关键。我们也参考了行业中一些App,发现码率基本选择在1500-2000之间,因为我们自身滤镜特效对色彩饱和度的一些要求,我们选择了1800的码率,尺寸则是屏幕的1.5倍,这样保证制作出来的10秒视频,大小基本能控制在2兆以内。
动态贴纸——U3D
贴纸的研发此前我们是使用C 、OpenGL自己内部处理,但贴合度、素材以及调试却存在很多问题,最终我们选择了U3D。它的优势最直观的就是贴纸素材高了一个档次,在特效和素材贴合度上都有很大提升;而且使用U3D还可以加入一些游戏场景,比如利用碰撞检测达到用嘴吃掉天上掉下来的甜甜圈,增添了更多的乐趣性;第三U3D的开发效率高,相比用OpenGL和C 底层SDK,由于它们是JSON文件没有平台,开发效率、调试会很糟糕,而U3D引擎本身的平台就可以支持;最后它的可扩展性很高,可以预见AR是未来发展的一个方向,因此我们也希望可以借此做一些尝试。
我们在使用U3D时特别做了一个双缓冲策略——特效处理在异步线程、U3D渲染在主线程,一开始原始Buffer传入SDK Queue从两个纹理字段找一个空闲的做渲染,然后把纹理传给Rendering Queue,Unity Queue不停的从Rendering Queue询问是否有新的纹理,有就取过来做Unity渲染,然后把渲染结果呈现在屏幕上,再把空的纹理传回Rendering Queue,然后SDK Queue不停的询问Rendering Queue是否有新的空闲纹理,有就拿回来准备做新的渲染。
当然U3D也存在一些负面影响:首先是在安卓上我们的SDK跟U3D引擎有一些冲突从而导致启动慢;第二是发热,这主要是人脸识别SDK和底层SDK性能抢占的问题;而发热也带来了另一个问题——CPU降频,帧率会降到非常低,只有一两帧。还有就是素材包资源比较大,即使我们做了WebP的优化,但依然没有非常明显的改善。
U3D带来最大的问题是armv7编译,armv7在打包时_TEXT_字段不能超过32兆,但我们发现仅仅U3D一个SDK就占了18兆,我们自己SDK占了6兆,这样其他SDK也只有8兆的空间可用,虽然现在我们能把包打出来,但也只剩下几百K的空间可用。而当我们后期接入Swift后发现加剧了这个问题,在Xcode8中Build setting里Enable Code Coverage这个设置应该默认Release是No,但是接入Swift会导致设置失效,结果_TEXT_字段直接飙到70多兆,而且无法降下来,最后也是通过升级到Xcode9解决了这个问题。
以上是全部分享,谢谢大家。
LiveVideoStack招募全职技术编辑和社区编辑
LiveVideoStack是专注在音视频、多媒体开发的技术社区,通过传播最新技术探索与应用实践,帮助技术人员成长,解决企业应用场景中的技术难题。如果你有意为音视频、多媒体开发领域发展做出贡献,欢迎成为LiveVideoStack社区编辑的一员。你可以翻译、投稿、采访、提供内容线索等。