[这是第六篇]
导语:VR是个比较火的话题,在iOS中集成全景和VR播放功能,是个值得考虑和去实践的idea。目前有一些能帮助我们快速实现VR的项目,其中Google提供的GVRSDK(Google VR SDK)就是非常好的代表,基于此,我们可以快速地实现一个性能不错的全景和VR播放器。(图片全景播放+视频全景播放)
一、背景
1、集成GVRSDK(Google VR SDK)很容易。目前最新版本是1.40.0
2、GVRSDK提供了图片的视频的全景和VR播放接口。
3、全景图片和全景视频功能可以支持手势处理和重力感应,支持VR播放
4、在Google VR SDK的基础上做了简单的定制,可以达到较好的效果。
二、项目中集成GVRSDK
1、Podfile文件中定义
target 'QSUseGoogleVRDemo' do
#google VR SDK
pod 'GVRSDK'
end
2、安装GVRSDK
pod install
通过pod install安装慢的话,建议使用 pod install --verbose --no-repo-update
GVRSDK下载下来的库比较大,约320MB。获取GVRSDK库的同时,还会获取与其相关的GoogleToolboxForMac和GTMSessionFetcher
-
遇到的问题:pod install安装下来的不是最新版本,是1.0.1版本。原因是:Cocoapods的全局缓存带来的,好的解决办法是,删除全局的缓存。使用命令如下:
sudo rm -fr ~/Library/Caches/CocoaPodssudo rm -fr ~/.cocoapods/repos/master
三、全景图片的播放器
1、GVRSDK提供的
-
GVRSDK提供全景图片播放的类是GVRPanoramaView,它支持两个load接口,分别如下:
- (void)loadImage:(UIImage *)image; - (void)loadImage:(UIImage *)image ofType:(GVRPanoramaImageType)imageType;
接口分析:
从接口中可以看出,并不可以直接load线上的图片资源,所以在使用这两个接口之前,需要先从网络上下载图片资源。
枚举类型GVRPanoramaImageType的有两个可选值。 kGVRPanoramaImageTypeMono和 kGVRPanoramaImageTypeStereoOverUnder,前者指定单个图像源图像,后者指有上下两部分图像源的图像,上半部分对应左眼,下半部对应右眼。demo中使用的是kGVRPanoramaImageTypeMono,这也是loadImage: 接口默认使用的参数值。
GVRPanoramaView的父类GVRWidgetView,从GVRWidgetView头文件中看出,它可以允许操作某些属性,如在View上是否显示信息按钮、跳转VR的按钮等,如展示模式(嵌入父View/全景/全景+VR/)。我们根据需要在GVRPanoramaView的子类中设置好值。GVRWidgetView还提供代理,可以帮我开发者去了解GVRPanoramaView的load情况(如load成功或失败)。
2、我们可以做的
1)实现图片的下载。使用SDWebImage库提供的下载器去下载图片是个不错的办法。但是我选择了自己实现一个下载器QSDownloadManager。目的是不仅仅支持图片的下载,还支持视频等资源的下载,此外QSDownloadManager还支持多任务下载,下载进度的监听,暂停下载 以及 资源的断点下载,支持文件的缓存和清理。QSDownloadManager的接口定义如下:
@interface QSDownloadManager : NSObject
+ (instancetype)sharedInstance;
/**
* 下载
*/
- (void)download:(NSString *)url progress:(QSDownloadProgressBlock)progressBlock completedBlock:(QSDownloadCompletedBlock)completedBlock;
/**
* 取消下载
*/
- (void)cancelDownLoad:(NSString *)url;
#pragma mark - 缓存文件的大小 & 删除
/**
所有缓存资源大小
*/
- (NSString *)getAllCacheFileSizeString;
/**
* 删除url对应的资源 && 如果url对应的资源还在下载,立即取消下载并清除对应未下载完整的资源
*/
- (void)deleteFileCache:(NSString *)url;
/**
* 清空所有下载资源
*/
- (void)deleteAllFileCache;
@end
2)增加placeholderView和ProgressHUD
这是为了在图片播放前(图片下载过程 和 图片加载到GVRSDK),让用户等待不再那么无聊。此外,为了简单起见,placeholderView只是一张纯白的View。
3、图片的全景播放QSPanoramaView
1)QSPanoramaView继承GVRPanoramaView,实现GVRWidgetViewDelegate协议方法。对外提供两个接口
//GVRPanoramaView.h
/**
全景图片播放 + VR
*/
@interface QSPanoramaView : GVRPanoramaView
/**
加载线上图片
*/
- (void)loadImageUrl:(NSURL *)imageUrl;
- (void)loadImageUrl:(NSURL *)imageUrl ofType:(GVRPanoramaImageType)imageType;
@end
说明:你也可以直接使用GVRPanoramaView的接口。
2)主要代码实现
- (void)loadImageUrl:(NSURL *)imageUrl{
[self loadImageUrl:imageUrl ofType:kGVRPanoramaImageTypeMono];
}
- (void)loadImageUrl:(NSURL *)imageUrl ofType:(GVRPanoramaImageType)imageType{
if ([self.imageUrl isEqual:imageUrl]) {
return;
}
[self cancelCurrentDownLoad];
self.imageUrl = imageUrl;
self.placeholderView.alpha = 1.0f; //遮盖
[MBProgressHUD showHUDWithContent:@"图片加载中..." toView:self];
[[QSDownloadManager sharedInstance]download:[imageUrl absoluteString]
progress:^(CGFloat progress, NSString *speed, NSString *remainingTime) {
NSLog(@"当前下载进度:%.2lf%%,当前的下载速度是:%@,还需要时间:%@,",progress * 100,speed,remainingTime);
} completedBlock:^(NSString *fileCacheFile) {
NSData *imageData = [NSData dataWithContentsOfFile:fileCacheFile];
UIImage *image = [UIImage imageWithData:imageData];
[self loadImage:image ofType:imageType];
}];
}
4、效果图
1)图片load过程,显示ProgressHUD
2)图片全景播放中,支持重力感应和手势控制,调整视角,实现360度全方位的观看。右下角是VR播放的按钮
3)VR播放的效果,分屏,支持360度全方位的观看
四、全景视频的播放器
1、GVRSDK提供的
-
GVRSDK提供全景图片播放的类是GVRVideoView,它支持load和对视频源播放、暂停和停止的控制,接口分别如下:
- (void)loadFromUrl:(NSURL*)videoUrl; - (void)loadFromUrl:(NSURL*)videoUrl ofType:(GVRVideoType)videoType; - (void)pause; - (void)play; - (void)stop;
接口分析:
loadFromUrl:中的参数videoUrl,不仅可以是线上的视频源的URL,还可以是本地的视频资源的URL,比GVRPanoramaView的load接口更强大些。我们可以不用去操心下载视频的问题的。(QSDownloadManager完全能驾驭下载视频,但是这里不需要了)
枚举类型GVRVideoType的有三个可选值。 kGVRVideoTypeMono、 kGVRVideoTypeStereoOverUnder 和 kGVRVideoTypeSphericalV2,kGVRVideoTypeMono代表单个视频源的视频,kGVRVideoTypeStereoOverUnder是有上下两部分视频源的视频,kGVRVideoTypeSphericalV2代表是球形视频源的视频。
GVRVideoView的也是父类GVRWidgetView。
GVRVideoView还提供GVRVideoViewDelegate,代理中方法可以获得视频的播放进度。
2、我们能做的
1)增加placeholderView、ProgressHUD、播放进度条和播放按钮
- 在视频加载的同时,使用placeholderView和ProgressHUD遮盖其上,告诉用户app在干啥,加载结束后,默认不立即play,出现play按钮,点击play按钮,视频才真正的去play。播放进度条显示视频播放的进度。
2)处理耳机的插入和拔出
- 增加耳机的处理,是因为耳机的插入和拔出会影响视频的播放,提供简单的UI处理,如拔出耳机,视频暂停,出现play按钮,点击按钮,继续播放,这些简单的UI使用户操作更加自然。
3、视频的全景播放QSVideoView#####
1)QSVideoView继承GVRVideoView,实现GVRWidgetViewDelegate和GVRVideoViewDelegate协议方法。对外提供两个接口
//QSVideoView
@interface QSVideoView : GVRVideoView
/**
加载线上视频
*/
- (void)loadFromOnlineUrl:(NSURL*)videoUrl;
- (void)loadFromOnlineUrl:(NSURL *)videoUrl ofType:(GVRVideoType)videoType;
@end
2)主要代码实现
- (void)loadFromOnlineUrl:(NSURL*)videoUrl{
[self loadFromOnlineUrl:videoUrl ofType:kGVRVideoTypeMono];
}
- (void)loadFromOnlineUrl:(NSURL *)videoUrl ofType:(GVRVideoType)videoType{
if ([self.videoUrl isEqual:videoUrl]) {
return;
}
self.videoUrl = videoUrl;
[MBProgressHUD showHUDWithContent:@"视频加载中..." toView:self];
self.placeholderView.alpha = 1.0f; //遮盖
[self loadFromUrl:videoUrl ofType:videoType];
}
#pragma mark - GVRVideoViewDelegate & GVRWidgetViewDelegate
- (void)widgetView:(GVRWidgetView *)widgetView didLoadContent:(id)content {
NSLog(@"视频加载结束...");
[UIView animateWithDuration:1.0 delay:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.placeholderView.alpha = 0;
} completion:^(BOOL finished) {
[_placeholderView removeFromSuperview];
_placeholderView = nil;
[MBProgressHUD hideHUDInView:self];
[self seekTo:0];
}];
}
- (void)widgetView:(GVRWidgetView *)widgetView didFailToLoadContent:(id)content withErrorMessage:(NSString *)errorMessage {
NSLog(@"Failed to load video: %@", errorMessage);
[MBProgressHUD hideHUDInView:self];
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"⚠️" message:@"视频加载失败..." delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alertView show];
}
- (void)videoView:(GVRVideoView*)videoView didUpdatePosition:(NSTimeInterval)position {
CGFloat progress = position / videoView.duration;
NSLog(@"播放进度: %lf", progress);
BOOL isAnimation = (progress == 0);
[self.playProgressView setProgress:progress animated:isAnimation];
}
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification{
NSDictionary *interuptionDict = notification.userInfo;
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:{
NSLog(@"耳机插入");
[self play];
[self updateUI];
}
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:{
NSLog(@"耳机拔出,停止播放操作");
[self pause];
[self updateUI];
}
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
// called at start - also when other audio wants to play
NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
break;
}
}
4、效果图#####
1) 视频加载过程中
2) 视频加载成功后,出现play按钮
3) 点击play按钮,播放线上视频,NavigationBar下面是细长的播放进度条,底部是切换到VR的按钮
4) 视频的VR播放
五、总结
- GVR For IOS 可以帮助普通的开发者们,在iOS上快速实现全景视频和图片的播放,这篇文章只是记录了在了解GVR的同时,对它做的一点点优化工作。
- 在优化工作成功中,最复杂的工作莫过于为优化编写的下载器,虽然没有派上太大用场,但是希望有机会在真正的生产环境中去使用,去发现问题,去完善。
- Panorama 和 MD360Player4iOS也是不错的库
具体的代码实现见源码QSUseGoogleVRDemo