最近做音频录制、音视频播放遇到的一些坑

互联网大环境寒冬,今年有点衰,家里事多,工作也因为公司业务或者资金链导致不得不离职,连跳两家公司,一直很忙也没有总结。最近新公司做了一个模块,在APP上做易企秀H5那种类似的东西,需要用户可以添加图片、录音、视频、文本,并且有很多页,期间遇到了很多坑,自己总结一下。

1、视频

视频分为视频选取、视频播放(本地视频、网路哦视频)和视频上传。当然选取视频会涉及到视频处理,包括裁剪、水印、滤镜、添加音乐等等,本来也研究了一些结果时间原因这个版本没做,以后做了再总结吧。

(1)视频选取——APP退到后台压缩失败

视频选取暂时使用的是系统的UIImagePickerController,还挺方便,可以限制视频最长时长、自带时长裁剪、自带压缩等等。不过没想到也遇到点bug。
当点击选取视频之后,系统会自动压缩,视频越大时间越长。但是这个过程中,不能将APP退到后台,如果退到后台,系统视频就会选取失败(API并没有说明、网上页查不到任何别人有遇到这个问题,难道就我脸黑?),关键也并没有任何选取失败的回调方法。。。
最后发现视频选取压缩失败,imagePickerController didFinishPickingMediaWithInfo选取结束回调方法里的info会没有UIImagePickerControllerMediaURL参数,可以根据这个参数有无来判断。

(2)视频播放——使用了很强大的第三方ZFPlayer

视频播放要求可以小屏播放,也可以全屏播放,全屏模式下要支持横竖屏旋转。我使用了ZFPlayer这个第三方库,还是很强大的,不过不支持竖屏全屏播放,自己对源码做了简单修改也支持了。

问题1——屏幕旋转

ZFPlayer有相应的UIViewController拓展和API来处理屏幕可否旋转。但是因为接手别人项目,一直无法旋转,搞了好久发现是自定义的NavigationController 把旋转的方法写死了,也就是- (BOOL)shouldAutorotate- (UIInterfaceOrientationMask)supportedInterfaceOrientations这两个方法,而且每个控制器的旋转与否也是以导航控制器的设置为优先的。为了防止影响其他页面,做的处理是判断topViewController是我需要自己设置的控制器,就return这个控制器自己的设置。

问题2——视频的释放

ZFPlayer全屏模式是将视频视图直接加到了Window上,而且加了一个控制亮度的控件,也是放在Window最顶层的,正常使用一般没有问题。
不过我因为自己加了全屏支持横竖屏旋转,而且我们的需求也是可能一个控制器左右切换有多个视频,如果是用UICollectionView的话ZFPlayer已经提供了解决方案,而我是使用UIPageViewController做的。这就导致释放不及时,一个视频全屏播放时,竖屏是本视频,横屏是前一个视频。当时就懵逼了。
我之后做的解决处理就是当左右滑动本视频离开页面显示区域时,就暂停并释放掉。当返回时重新配置ZFPlayer。

问题3——网路视频格式处理

这个问题可能比较特殊,也是因为接手的老项目,公司原来的视频有mp4的还有m3u8的,不过接口里的路径统一都是mp4,有的需要自己加.m3u8。。。这就尴尬了。
所以在给ZFPlayer设置视频路径之前,需要自己先NSURLConnection调一下路径看远程文件是否存在,再配置相应的后缀后设置视频路径。

2、音频

音频包括录音、删除重录和播放、暂停继续播放等,自己写UI控件。状态比较多,定时器来控制播放和录音过程中时长显示。需求是点击录音,再点结束,最长可录3分钟。而不是常见的长按录音松手结束,这样在录音过程中就可以有其他的操作,需要控制录音、播放的暂停、结束和释放(尤其是页面跳转的时候viewWillDisappear)。

(1)录音

问题1:定时器不准

好久不用定时器都忘记了,[[NSRunLoop currentRunLoop] addTimer:self.recordTimer forMode:NSRunLoopCommonModes];当使用定时器的同时还有其他操作,一定要加runloop处理。

问题2:录音时间不准——最长可录3分钟,时间总是不正确

刚开始一直以为是定时器的问题,结果调了好久。。。后来使用AVAudioRecorder的APIrecordForDuration来控制最长可录时长(到时间自动停止录音),currentTime来显示当前录制的时间和录音停止的判断(当录音自动停止后,currentTime会变成0.00f)还是有问题,确定来不再是定时器的问题。
查了好久,最后锁定为录音结束转码caf转mp3有问题。也是个百度的坑,百度的很多都是不准确的,如果不是长时间录音或者对时间有精确要求的一般看不出来,像我这里的需求就会比较明显了,详情可见iOS 使用 Lame 转码 MP3 的最正确姿势。结果也就是两句代码的问题,调了好几个小时。

(2)音频播放

问题1:音频播放的暂停处理

音频播放采取的是网络音频用MPMoviePlayerController,本地用AVAudioPlayer(一方面AVAudioPlayer不能播放网络音频,另一方面是用的老项目代码的逻辑)
因为需求是音频可以暂停,再继续播放,结果就遇到坑了,刚开始对于两个不同控件,我都是用pause方法来控制暂停的。
后来发现AVAudioPlayer这个控件,pause方法暂停,其实只是声音暂停,其实是默默继续播放的。。。如果APP退到后台再进来,声音又出来了。。。而stop方法是真正的暂停,再使用play方法是继续播放。。。
另外AVAudioPlayer不能同时播放多个音频文件,每次播放不同的音频需要先清掉前一个。。。如果想要多个音频都能控制暂停一会儿再继续播放,自己记录当前播放的时间,播放时再手动设置吧少年

问题2:音视频时间处理

这不是问题,只是留个记录,音视频时间都是带小数的,而我们通常的需求都是整数显示,那便四舍五入吧(int)(videoTime + 0.5)
另,秒时间转字符串:

- (NSString *)videoTimeStringWithVideoTime:(CGFloat)videoTime {
    NSInteger second = (int)(videoTime + 0.5);
    NSInteger minute = 0;
    
    if (second >= 60) {
        NSInteger index = second / 60;
        minute = index;
        second = second - index*60;
    }
    
    NSString *timeLongStr = [NSString stringWithFormat:@"%02ld:%02ld", minute, second];
    return timeLongStr;
}

推荐阅读更多精彩内容