VR是个比较火的话题,在iOS中集成全景和VR播放功能,是非常值得考虑和去实践的。最近公司也准备在APP中集成VR功能。所以我也就了解了下VR功能的开发。目前有一些能帮助我们快速实现VR的项目,其中Google提供的GVRSDK(Google VR SDK)就是非常好的代表,基于此,我们可以快速地实现一个性能不错的全景和VR播放器。(图片全景播放+视频全景播放)
废话不多说,直接撸代码
一、SDK的导入
GVRSDK的导入也是很简单的,我们可以通过cocoapods导入。
target ‘VRDemo‘ do # Comment the next line if you don‘t want to use dynamic frameworks # Pods for VRDemo pod ‘GVRSDK‘ target ‘VRDemoTests‘ do inherit! :search_paths # Pods for testing end target ‘VRDemoUITests‘ do # Pods for testing end end
导入成功后就是这样的结果了。
二、全景图的加载展示
GVRSDK提供全景图片播放的类是GVRPanoramaView,它支持两个load接口。
/** * Load a 360-Panorama image from @c UIImage of type ::kGVRPanoramaImageTypeMono. * * If image is nil, it clears the view. */ - (void)loadImage:(UIImage *)image; /** * Load a 360-Panorama image from @c UIImage of type ::GVRPanoramaImageType. * * If image is nil, it clears the view. */ - (void)loadImage:(UIImage *)image ofType:(GVRPanoramaImageType)imageType;
从接口中可以看出,它并不是直接加载网络图片,而是使用的UIImage。所以在使用这两个接口之前,需要先从网络上下载图片资源。
枚举类型GVRPanoramaImageType的有两个可选值(kGVRPanoramaImageTypeMono和kGVRPanoramaImageTypeStereoOverUnder)。前者指定单个图像源图像,后者指有上下两部分图像源的图像,上半部分对应左眼,下半部对应右眼。
GVRPanoramaView的父类GVRWidgetView,从GVRWidgetView头文件中看出,它可以允许操作某些属性,如在View上是否显示信息按钮、跳转VR的按钮等,如展示模式(嵌入父View/全景/全景+VR/)。我们根据需要在GVRPanoramaView的子类中设置好值。GVRWidgetView还提供代理,可以帮我开发者去了解GVRPanoramaView的load情况(如load成功或失败)。
看了上面的分析,我们就知道怎么做了,我们可以使用常用的SDWebImage来下载图片然后加载全景图。
[[SDWebImageManager sharedManager] loadImageWithURL:[NSURL URLWithString:VR_IMG_URL] options:SDWebImageScaleDownLargeImages progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { float progress = receivedSize*100/expectedSize; NSLog(@"当前下载进度:%.2lf%%",progress); } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) { if (image) { // @autoreleasepool { [self.panoramaView loadImage:image]; // }; } if (error) { NSLog(@"下载图片失败"); } }];
// 自定义一些相关配置
- (GVRPanoramaView *)panoramaView { if (!_panoramaView) { _panoramaView = [[GVRPanoramaView alloc]initWithFrame:self.view.bounds]; _panoramaView.enableTouchTracking = YES; _panoramaView.enableInfoButton = YES; _panoramaView.enableFullscreenButton = YES; _panoramaView.enableCardboardButton = YES; } return _panoramaView; }
当然一些加载进度和placeholder的展示需要我们自己添加。我这里没有自己添加,之后可以根据不同要求进行自定义设置。
三、全景视频的加载展示
GVRSDK提供全景图片播放的类是GVRVideoView,它支持load和对视频源播放、暂停和停止的控制
/** * Load a local or remote video from a url and start playing. * * The video is assumed to be of type ::kGVRVideoTypeMono. */ - (void)loadFromUrl:(NSURL*)videoUrl; /** * Load a local or remote video from a url and start playing. * * The video type is set by @c videoType. */ - (void)loadFromUrl:(NSURL*)videoUrl ofType:(GVRVideoType)videoType; /** Pause the video. */ - (void)pause; /** Start or resume the video. */ - (void)play; /** Stop the video. */ - (void)stop;
loadFromUrl:中的参数videoUrl,不仅可以是线上的视频源的URL,还可以是本地的视频资源的URL,比GVRPanoramaView的load接口更强大。我们可以不用去操心下载视频的问题。
枚举类型GVRVideoType的有三个可选值。 kGVRVideoTypeMono、 kGVRVideoTypeStereoOverUnder 和 kGVRVideoTypeSphericalV2,kGVRVideoTypeMono代表单个视频源的视频,kGVRVideoTypeStereoOverUnder是有上下两部分视频源的视频,kGVRVideoTypeSphericalV2代表是球形视频源的视频。
GVRVideoView的也是父类GVRWidgetView。
GVRVideoView还提供GVRVideoViewDelegate,代理中方法可以获得视频的播放进度。
#pragma mark - UINavigationControllerDelegate // 将要显示控制器 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { // 判断要显示的控制器是否是自己 BOOL isShowHomePage = [viewController isKindOfClass:[self class]]; [self.navigationController setNavigationBarHidden:isShowHomePage animated:YES]; } #pragma mark - GVRVideoViewDelegate - (void)widgetViewDidTap:(GVRWidgetView *)widgetView { if (_isPaused) { [_videoView play]; } else { [_videoView pause]; } _isPaused = !_isPaused; } - (void)widgetView:(GVRWidgetView *)widgetView didLoadContent:(id)content { NSLog(@"Finished loading video"); [_videoView play]; _isPaused = NO; } - (void)widgetView:(GVRWidgetView *)widgetView didFailToLoadContent:(id)content withErrorMessage:(NSString *)errorMessage { NSLog(@"Failed to load video: %@", errorMessage); } - (void)videoView:(GVRVideoView*)videoView didUpdatePosition:(NSTimeInterval)position { // Loop the video when it reaches the end. NSLog(@"-------didUpdatePosition:::::%f\n------playableDuration:%f\n------duration:%f",position,videoView .playableDuration,videoView.duration); if (position == videoView.playableDuration || isnan(position)) { [_videoView seekTo:0]; [_videoView play]; }else{ } } - (GVRVideoView *)videoView { if (!_videoView) { _videoView = [[GVRVideoView alloc]initWithFrame:CGRectMake(0, 0, MAX(SCREEN_WIDTH, SCREEN_HEIGHT), MIN(SCREEN_WIDTH, SCREEN_HEIGHT))]; _videoView.delegate = self; _videoView.enableFullscreenButton = YES; _videoView.enableCardboardButton = YES; _videoView.enableTouchTracking = YES; _videoView.enableInfoButton = NO; } return _videoView; }
当然如果是x正式应用中,placeholderView、ProgressHUD、播放进度条和播放按钮这些都是必不可少的。耳机的插入和拔出也是需要处理的,我这边就没有进行处理。