iOS实录6:iOS中实现全景播放器(GVR For iOS)

[这是第六篇]
导语: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;
    

接口分析:

  1. 从接口中可以看出,并不可以直接load线上的图片资源,所以在使用这两个接口之前,需要先从网络上下载图片资源。

  2. 枚举类型GVRPanoramaImageType的有两个可选值。 kGVRPanoramaImageTypeMono和 kGVRPanoramaImageTypeStereoOverUnder,前者指定单个图像源图像,后者指有上下两部分图像源的图像,上半部分对应左眼,下半部对应右眼。demo中使用的是kGVRPanoramaImageTypeMono,这也是loadImage: 接口默认使用的参数值。

  3. 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

图片load过程.png

2)图片全景播放中,支持重力感应和手势控制,调整视角,实现360度全方位的观看。右下角是VR播放的按钮

图片全景播放.png

3)VR播放的效果,分屏,支持360度全方位的观看

图片VR播放.png

四、全景视频的播放器

1、GVRSDK提供的
  • GVRSDK提供全景图片播放的类是GVRVideoView,它支持load和对视频源播放、暂停和停止的控制,接口分别如下:

     - (void)loadFromUrl:(NSURL*)videoUrl;
    
     - (void)loadFromUrl:(NSURL*)videoUrl ofType:(GVRVideoType)videoType;
    
     - (void)pause;
    
     - (void)play;
    
     - (void)stop;
    

接口分析:

  1. loadFromUrl:中的参数videoUrl,不仅可以是线上的视频源的URL,还可以是本地的视频资源的URL,比GVRPanoramaView的load接口更强大些。我们可以不用去操心下载视频的问题的。(QSDownloadManager完全能驾驭下载视频,但是这里不需要了)

  2. 枚举类型GVRVideoType的有三个可选值。 kGVRVideoTypeMono、 kGVRVideoTypeStereoOverUnder 和 kGVRVideoTypeSphericalV2,kGVRVideoTypeMono代表单个视频源的视频,kGVRVideoTypeStereoOverUnder是有上下两部分视频源的视频,kGVRVideoTypeSphericalV2代表是球形视频源的视频。

  3. GVRVideoView的也是父类GVRWidgetView。

  4. 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) 视频加载过程中

视频加载中.png

2) 视频加载成功后,出现play按钮

加载成功后.png

3) 点击play按钮,播放线上视频,NavigationBar下面是细长的播放进度条,底部是切换到VR的按钮

视频播放中.png

4) 视频的VR播放

视频VR.png

五、总结

  • GVR For IOS 可以帮助普通的开发者们,在iOS上快速实现全景视频和图片的播放,这篇文章只是记录了在了解GVR的同时,对它做的一点点优化工作。
  • 在优化工作成功中,最复杂的工作莫过于为优化编写的下载器,虽然没有派上太大用场,但是希望有机会在真正的生产环境中去使用,去发现问题,去完善。
  • PanoramaMD360Player4iOS也是不错的库

具体的代码实现见源码QSUseGoogleVRDemo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容