小视频是微信6.0版本重大功能之一,在开发过程中遇到不少问题。本文先叙述小视频的产品需求,介绍了几个实现方案,分析每个方案的优缺点,最后总结出最优的解决方案。

小视频播放需求

  1. 可以同时播放多个视频

  2. 用户操作界面时视频可以继续播放

  3. 播放时不能卡住界面,视频滑进界面内后要立即播放

  4. 视频在列表内播放是静音播放,点击放大是有声播放

小视频播放方案

1. MPMoviePlayerController

MPMoviePlayerController是一个简单易用的视频播放控件,可以播放本地文件和网络流媒体,支持mov、mp4、mpv、3gp等H.264和MPEG-4视频编码格式,支持拖动进度条、快进、后退、暂停、全屏等操作,并为开发者提供了一系列播放状态事件通知。使用时先设置URL,然后把它的view add到某个parent view里,再调用play即可。

但这方案的缺点是,同一时间只能有一个MPMoviePlayerController对象播放,不满足同时多个播放的需求;而且也不支持静音播放。MPMoviePlayerController适合于全屏播放视频的场景。

2. AVPlayer

AVPlayer是AVFoundation.Framework提供的偏向于底层的视频播放控件,用起来复杂,但功能强大。单独使用AVPlayer是无法显示视频的,要把它添加到AVPlayerLayer里才行。另外它需要配合AVPlayerItem使用,AVPlayerItem类似于MVC里的Model层,负责资源加载、视频播放设置及播放状态管理(通过KVO方式来观察状态)。它们关系如下:

首先创建一个AVPlayerItem对象:

NSURL* videoUrl = [NSURL fileURLWithPath:m_path isDirectory:NO];
m_playItem = [AVPlayerItem playerItemWithURL:videoUrl];
// 监听playItem的status属性
[m_playItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

接下来是创建AVPlayer和AVPlayerLayerView对象。AVPlayerLayerView是自定义的UIView,用于AVPlayer播放,其layerClass是AVPlayerLayer:

// AVPlayer
m_player = [AVPlayer playerWithPlayerItem:m_playItem];
m_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
// AVPlayerLayerView
m_playerView = [[AVPlayerLayerView alloc] initWithFrame:self.bounds];
[self addSubview:m_playerView];
// 把AVPlayer添加到AVPlayerLayer
[(AVPlayerLayer*)[m_playerView layer] setPlayer:m_player];
// 观察AVPlayerItem播放结束的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemPlayEnded:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_playItem];

AVPlayerItem的status属性有三种状态:AVPlayerStatusUnknown、AVPlayerStatusReadyToPlay及AVPlayerStatusFailed。当status=AVPlayerStatusReadyToPlay时,就代表视频能播放了,此时调用AVPlayer的play方法就能播放视频了。

相比MPMoviePlayerController,AVPlayer有最多可以同时播放16个视频。另外AVPlayer在使用时会占用AudioSession,这个会影响用到AudioSession的地方,如聊天窗口开启小视频功能。还有AVPlayer释放时最好先把AVPlayerItem置空,否则会有解码线程残留着。最后是性能问题,如果聊天窗口连续播放几个小视频,列表滑动时会非常卡。通过Instrument测试性能,看不出哪里耗时,怀疑是视频播放互相抢锁引起的。

3. AVAssetReader+AVAssetReaderTrackOutput

既然AVPlayer在播放视频时会有性能问题,我们不如做自己的播放器。AVAssetReader可以从原始数据里获取解码后的音视频数据。结合AVAssetReaderTrackOutput,能读取一帧帧的CMSampleBufferRef。CMSampleBufferRef可以转化成CGImageRef。为此,我们可以写个MMovieDecoder的类,负责视频解码,每读出一个SampleBuffer就往上层回调:

AVAssetReader* reader = [[AVAssetReader alloc] initWithAsset:m_asset error:&error];
NSArray* videoTracks = [m_asset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack* videoTrack = [videoTracks objectAtIndex:0];
// 视频播放时,m_pixelFormatType=kCVPixelFormatType_32BGRA
// 其他用途,如视频压缩,m_pixelFormatType=kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
NSDictionary* options = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:(int)m_pixelFormatType] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
AVAssetReaderTrackOutput* videoReaderOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:options];
[reader addOutput:videoReaderOutput];
[reader startReading];
// 要确保nominalFrameRate>0,之前出现过android拍的0帧视频
while ([reader status] == AVAssetReaderStatusReading && videoTrack.nominalFrameRate > 0) {// 读取video sampleCMSampleBufferRef videoBuffer = [videoReaderOutput copyNextSampleBuffer];[m_delegate mMovieDecoder:self onNewVideoFrameReady:videoBuffer);CFRelease(videoBuffer);    // 根据需要休眠一段时间;比如上层播放视频时每帧之间是有间隔的[NSThread sleepForTimeInterval:sampleInternal];
}// 告诉上层视频解码结束
[m_delegate mMovieDecoderOnDecodeFinished:self];

另一个是MVideoPlayerView,负责视频的显示,它接收MMovieDecoder回调的CMSampleBufferRef后,把它转为CGImageRef,然后设置layer.contents为这个CGImageRef对象。创建CGImageRef不会做图片数据的内存拷贝,它只会当Core Animation执行Transaction::commit()触发layer -display时,才把图片数据拷贝到layer buffer里。

AVAssetReader也能decode音频的SampleBuffer,不过本人还没想到如何播放CMSampleBufferRef的音频,目前只能静音播放。

4. 方案对比

对方案二、三做了滑动性能对比和耗电对比,测试条件分别是

滑动:在iPhone4的聊天窗口,有30个小视频,来回做4次列表滑动

耗电:在iPhone5s,屏幕亮度调到最大,禁止自动锁屏,开启飞行模式,聊天窗口同时播放着3个小视频,10分钟

方案三无论滑动性能和耗电均优于方案二,由于方案三只能静音播放,所以方案三用于聊天窗口和朋友圈列表播放,方案二用于点击放大时的有声播放。

小视频录制需求

  1. 支持白平衡、对焦、缩放

  2. 录制视频长度6秒,30帧/秒,尽量不丢帧

  3. 能录制不同尺寸和码率的视频

小视频录制方案

对于需求1,AVFoundation有API可以支持,这里不多说。这里重点说说需求2、3的实现方案。

前期录制方案如下:

  1. 创建AVCaptureSession,设置拍摄分辨率

  2. 添加AVCaptureInput,如摄像头和麦克风

  3. 添加AVCaptureOutput,如AVCaptureVideoDataOutput、AVCaptureAudioDataOutput。这里AVCaptureAudioDataOutput建议在Session -startRunning后才添加,避免影响摄像头启动时间

  4. 添加AVCaptureVideoPreviewLayer,为用户提供拍摄预览界面

  5. 创建MMovieWriter,里面包含AVAssetWriter对象,用于写视频

  6. 开始捕捉-startRunning

  7. AVCaptureVideoDataOutput和AVCaptureAudioDataOutput不停地往MMovieWriter传递VideoSampleBuffer和AudioSampleBuffer,MMovieWriter对VideoSampleBuffer做分辨率压缩,以及对AudioSampleBuffer做码率压缩

  8. 结束捕捉-stopRunning,MMovieWriter停止写视频,把生成的视频文件抛给上层

在4s以上的设备拍摄小视频挺流畅,帧率能达到要求。但是在iPhone4,录制的时候特别卡,录到的视频只有6~8帧/秒。尝试把录制视频时的界面动画去掉,稍微流畅些,帧率多了3~4帧/秒,还是不满足需求。通过Instrument检测,发现跟写音频时的压缩有关,写音频时阻塞了AVFoundation的线程,引起后续的丢帧。网上也有人反馈类似问题 http://stackoverflow.com/questions/16686076/performance-issues-with-avassetwriterinput-audio-and-single-core-devices。把写音频去掉后,帧率果然上去了。但是系统相机的拍摄视频是非常流畅的。于是用AVCaptureMovieFileOutput(640*480)直接生成视频文件,拍视频很流畅。然而录制的6s视频大小有2M+,再用MMovieDecoder+MMovieWriter压缩至少要7~8s,影响聊天窗口发小视频的速度。

综上所述,要想拍视频不卡,就要在录制过程中尽量不做CPU耗时操作,而且AVCaptureOutput传递数据给上层时不能卡住AV线程。最终想到个方案,加个Cache层,先把AVCaptureOutput传递的SampleBuffer缓存下来,不在AV的线程写视频;等CPU空闲时,再唤起movieWriter线程写视频。流程如下图所示:

通过这样处理,拍视频流畅度跟系统相机接近了,只是刚拍的前1s帧数只有18帧,后面稳定到30帧/秒左右了。而且用户松手拍完后,最多等1s就能把视频写完文件了;也优化了之前的视频截图生成接口,减少200ms。不过拍摄稳定性不够好,经常出现下面的写失败错误,频率大概是6次/100次:

[GL]  INFO: audio writer status 3, desc Error Domain=AVFoundationErrorDomain Code=-11800 "这项操作无法完成" UserInfo=0x11495910 {NSLocalizedDescription=这项操作无法完成, NSUnderlyingError=0x1146e8d0 "The operation couldn’t be completed. (OSStatus error -12633.)", NSLocalizedFailureReason=发生未知错误(-12633)}

通过google搜索,网上说这错误原因是同一个FrameTime写入了两帧。但是FrameTime是从SampleBuffer里取的,理论上不会时间重合(我没打log验证);而且老方案没出现这种错误,新方案延后处理才会出现的。经过多次试验,把Buffer Cache设置上限,当Buffer数达到一定数量后强制让MovieWriter写入文件,同时把下面这行代码注释,错误不再出现了:

//m_writer.movieFragmentInterval = CMTimeMakeWithSeconds(1.0, 1000); // AVAssetWriter

方案对比:

在iPhone4聊天窗口拍摄若干个6s视频10次,算平均值

iOS——微信朋友圈小视频的播放和聊天窗口小视频的播放相关推荐

  1. 批量抓取微信朋友圈 Java_微信朋友圈转疯了(golang写小爬虫抓取朋友圈文章)...

    很多人在朋友圈里转发一些文章,标题都是什么转疯啦之类,虽然大多都也是广告啦,我觉得还蛮无聊的,但是的确是有一些文章是非常值得收藏的,比如老婆经常就会收藏一些养生和美容的文章在微信里看. 今天就突发奇想 ...

  2. 视频文件过长,怎样剪切长视频文件发微信朋友圈

    相信很多朋友都遇到过这样的问题,由于微信朋友圈最多只能发15秒的短视频,从而导致很多长视频文件不能发朋友圈,那么该怎么办呢?我们可以把长视频文件剪切出最精彩的15秒,这样就可以了,可以使用视频剪切合并 ...

  3. 小程序监听点击右上角按钮_朋友圈支持应用直达、公众号小程序支持行动按钮文案、原生页拉取...

    微信朋友圈广告支持应用直达 (全量发布 投放管理) · 选择iOS / Android应用推广目标: · 选择微信朋友圈广告版位: · 选择图片或视频类型的基础卡片或标签卡片的创意形式: · 当推广目 ...

  4. 你的微信朋友圈被直升机与多啦a梦动图结合动量守恒定律刷屏了吗

    你的微信朋友圈被直升机与多啦a梦视频结合动量守恒定律刷屏了吗 直升机原图: ▬▬▬.◙.▬▬▬      ▂▄▄▓▄▄▂ ◢◤ █▀▀████▄▄▄▄▄◢◤  █  开发网站的联系我们█▀▀▀▀╬   ...

  5. 类似微信朋友圈的mysql数据库

    这段时间模仿微信朋友圈准备写个类似于论坛的小交友平台 主框架就用springcloud+mybatis 开始第一步先写数据库,对数据库进行一个简单的设计,先提取功能: 一.功能提取: 用户信息的存储: ...

  6. 服务器春节微信创意文案,微信朋友圈春节的文案说说

    相信大家都知道,春节在中国是一个非常传统的节日.又到了春节,怎么在春节时期发朋友圈里?以下是小编精心收集整理的微信朋友圈春节的文案说说,下面小编就和大家分享,来欣赏一下吧. 春节时期的朋友圈短句 1. ...

  7. android微信朋友圈视频无法播放,微信朋友圈不能分享手机视频怎么回事?微信朋友圈大视频功能安卓不能用吗?...

    微信朋友圈不能分享手机视频怎么回事?微信朋友圈大视频功能安卓不能用吗?最近微信更新,用户可以进行分享自己手机里的视频,这一举措受到很多人的喜欢.那么微信朋友圈不能分享手机视频怎么回事?微信朋友圈大视频 ...

  8. iOS完美实现微信朋友圈视频截取

    点击上方"iOS开发",选择"置顶公众号" 关键时刻,第一时间送达! 先不说楚枫的这般年纪,能够踏入元武一重说明了什么,最主要的是,楚枫在刚刚踏入核心地带时,明 ...

  9. iOS纯Autolayout实现微信朋友圈和通讯录另附App启动页短视频效果

    2017/8/24更新 由于10.0以上的版本对于autolayout的布局有点变化,因此这里做一下修复 如果按照之前的操作,约束就会严重冲突,虽然不会影响对应的效果,但是看起来很不爽 1.首先更新下 ...

最新文章

  1. 【SSH网上商城项目实战17】购物车基本功能的实现
  2. 记录java在后台运行命令
  3. xajax中的中文乱码问题
  4. VS-c# web程序:gridview保存Excel文件遇到的问题
  5. dedeCMS如何进行关键词过滤替换和屏蔽非法词汇?
  6. ffmpeg实现摄像头拉流_干货 | 速看!乐橙K32Famp;K36F摄像头全彩夜视功能的不同点全在这了!...
  7. 每周精选:20万DBA都在关注的11个问题
  8. excel打开空白不显示内容 没有隐藏_办公软件操作技巧097:如何隐藏excel表格中没有数据的空白区域...
  9. 请用旧版的 Scala-2.11 搭配 Spark-2.4.8 / hadoop-2.7
  10. 服务器应用程序不可用的解决方法
  11. OpenContrail 体系架构文档
  12. 电脑开热点手机连不上
  13. python做桌面宠物_如何从零开始制作智能桌宠?
  14. 企业内的ITSM软件与OA系统有什么区别和关系?
  15. 运输小猫(斜率优化)
  16. DolphinDB Database丨 最简最快的WorldQuant 101 Alpha因子实现
  17. elasticsearch6.8.4-docker部署升级方式以及安全加密
  18. 【大数据】什么是数据集成?(SeaTunnel 集成工具介绍)
  19. 全国计算机二级等级考试报名官网入口
  20. 微星GS65 英雄联盟崩溃

热门文章

  1. python ffmpeg剪辑视频_用ffmpeg快速剪切和合并视频
  2. markdown如何换行——md文件
  3. onlyoffice开发java_如何集成Alfresco与ONLYOFFICE在线编辑器在Ubuntu 14.04
  4. 房地产的投资逻辑:隐藏在高房价下的致命陷阱
  5. 同济大学高等数学上册第七章微分方程以及每日一题
  6. 关于X开Y次方的算法
  7. 全媒舍:活动策划对于宣传的最终效果影响很大
  8. Java存储 转侵删
  9. 【认知跃迁】自动化任务与持续清理的港口思维
  10. heroku 服务器的使用