美文网首页AR/VR -iOSSceneKitUnity技术VR/AR分享
SceneKIt+ AVFoundation 打造VR播放器(2

SceneKIt+ AVFoundation 打造VR播放器(2

作者: hhjdk | 来源:发表于2017-06-23 11:18 被阅读276次

    下面是我写的播放器

    SceneKIt+ AVFoundation 打造VR播放器(1)
    支持VR,全景,视频缩放,本地,网络视频播放,实时获取视频帧,获取播放时间,获取缓存时间,播放,暂停

    2017-06-22 17_47_06.gif

    要想完成一个Vr播放器,需要完成两个功能

    1、写一个可以实时获取视频帧的播放器
    2、写一个可以渲染每一帧图片为全景图片的view

    SCN3DPlayerView 图片渲染

    用于渲染每一帧图片为全景图片
    使用的是 <UIKit/UIKit.h><SceneKit/SceneKit.h> <GLKit/GLKit.h>

    下面是SCN3DPlayerView的一些方法

    //
    //  SCN3DPlayerView.h
    //  SCN3DPlayer
    //
    //  Created by 俞涛涛 on 16/11/11.
    //  Copyright © 2016年 俞涛涛. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    #import <SceneKit/SceneKit.h>
    #import <GLKit/GLKit.h>
    
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    /**
     触摸,重力,触摸重力
    
     - SCN3DInteractive_Touch: 触摸
     - SCN3DInteractive_Motion: 重力
     - SCN3DInteractive_MotionAndTouch: 触摸和重力
     */
    typedef NS_ENUM(NSInteger, SCN3DInteractive_) {
        SCN3DInteractive_Touch,
        SCN3DInteractive_Motion,
        SCN3DInteractive_MotionAndTouch,
    };
    
    /**
     形状模式枚举
    
     - SCN3DDisplayMode_Plane_Normal: 普通模式
     - SCN3DDisplayMode_Plane_Slide: 平面180模式
     - SCN3DDisplayMode_Tube: 圆柱模式
     - SCN3DDisplayMode_Sphere: 球模式
     - SCN3DDisplayMode_VR360: 全景模式
     - SCN3DDisplayMode_VRGlass: VR双屏模式
     */
    typedef NS_ENUM(NSUInteger, SCN3DDisplayMode_) {
        SCN3DDisplayMode_Plane_Normal = 0,
        SCN3DDisplayMode_Plane_Slide,
        SCN3DDisplayMode_Tube,
        SCN3DDisplayMode_Sphere,
        SCN3DDisplayMode_VR360,
        SCN3DDisplayMode_VRGlass,
    };
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    @interface SCN3DPlayerView : UIView
    
    @property (nonatomic, strong) SCNScene *scene;
    @property (nonatomic, strong) SCNView  *scViewLeft;
    @property (nonatomic, strong) SCNView  *scViewRight;
    @property (nonatomic, strong) SCNNode  *shapeNode;
    @property (nonatomic, strong) SCNNode  *cameraNode;
    
    //_________________________________________________________________________________________________
    
    /**
     设置使用重力,还是手指触摸,或者两者都持
    
     @param interactive SCN3DInteractive_ 枚举参数
     */
    - (void)setInteractiveMode:(SCN3DInteractive_)interactive;
    
    /**
     设置sceneKit 渲染模式,有Vr ,全景,等等模式
    
     @param displayMode SCN3DDisplayMode_ 枚举参数
     */
    - (void)setVideoDisplayMode:(SCN3DDisplayMode_)displayMode;
    
    /**
     是否,水平启用,垂直启用
    
     @param horEnabled Yes or no
     @param verEnabled yes or no
     */
    - (void)setHorizontalEnabled:(BOOL)horEnabled verticalEnabled:(BOOL)verEnabled;
    
    //是否开启重力传感器
    - (void)setGSensorMotionEnabled:(BOOL)GSensorEnabled;
    //是否开启缩放功能
    - (void)setPinchScaleEnabled:(BOOL)pinchEnabled;
    //设置缩放范围
    - (void)setMinScale:(float)minScale maxScale:(float)maxScale;
    //设置旋转范围
    - (void)setVerticalMinRotate:(float)minRotate maxRotate:(float)maxRotate;
    //设置纹理坐标平移
    - (void)setTextureOffsetX:(float)x offsetY:(float)y;
    //设置纹理坐标缩放
    - (void)setTextureScaleX:(float)x ScaleY:(float)y;
    
    //_________________________________________________________________________________________________
    //设置宽高比
    - (void)setVideoAspectRatio:(float)aspectRatio;
    //设置当前横屏或者竖屏
    - (void)setCurrentOrientation:(UIInterfaceOrientation)orientation;
    //设置当前缩放比例
    - (void)setCurrentScale:(float)curScale;
    //设置当前旋转角度
    - (void)setCurrentRotateX:(float)rotateX rotateY:(float)rotateY;
    
    //_________________________________________________________________________________________________
    //设置帧视频图像
    - (void)setFramesPerVideoImage:(UIImage *)image;
    
    @end
    
    
    

    下面是一些重要的方法

    下面是一些枚举的定义

    /**
     触摸,重力,触摸重力
    
     - SCN3DInteractive_Touch: 触摸
     - SCN3DInteractive_Motion: 重力
     - SCN3DInteractive_MotionAndTouch: 触摸和重力
     */
    typedef NS_ENUM(NSInteger, SCN3DInteractive_) {
        SCN3DInteractive_Touch,
        SCN3DInteractive_Motion,
        SCN3DInteractive_MotionAndTouch,
    };
    
    /**
     形状模式枚举
    
     - SCN3DDisplayMode_Plane_Normal: 普通模式
     - SCN3DDisplayMode_Plane_Slide: 平面180模式
     - SCN3DDisplayMode_Tube: 圆柱模式
     - SCN3DDisplayMode_Sphere: 球模式
     - SCN3DDisplayMode_VR360: 全景模式
     - SCN3DDisplayMode_VRGlass: VR双屏模式
     */
    typedef NS_ENUM(NSUInteger, SCN3DDisplayMode_) {
        SCN3DDisplayMode_Plane_Normal = 0,
        SCN3DDisplayMode_Plane_Slide,
        SCN3DDisplayMode_Tube,
        SCN3DDisplayMode_Sphere,
        SCN3DDisplayMode_VR360,
        SCN3DDisplayMode_VRGlass,
    };
    

    初始化SCNScene,SCNView

    - (void)initScene {
        self.scene  = [SCNScene scene];
        self.scViewLeft = [[SCNView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) options:nil];
        self.scViewLeft.backgroundColor = [UIColor blackColor];
        self.scViewLeft.scene = self.scene;
        self.scViewLeft.antialiasingMode = SCNAntialiasingModeMultisampling4X;
        [self addSubview:self.scViewLeft];
        
        self.cameraNode = [SCNNode node];
        self.cameraNode.camera = [SCNCamera camera];
        [self.scene.rootNode addChildNode:self.cameraNode];
        
        UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchScale:)];
        [self addGestureRecognizer:pinchGestureRecognizer];
    }
    

    根据不同的模式,设置相应的参数

    - (void)initSceneNodeWithMode:(SCN3DDisplayMode_)displayMode {
        [self.shapeNode   removeFromParentNode];
        [self.scViewRight removeFromSuperview];
        [self initParameterSetting];
        switch (displayMode) {
            case SCN3DDisplayMode_Plane_Normal: {
                float width  = 2.0;
                float height = width / self.videoAspRatio;
                self.shapeNode = [SCNNode nodeWithGeometry:[SCNPlane planeWithWidth:width height:height]];
                self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.cullMode = SCNCullBack;
                self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
                self.cameraNode.camera.usesOrthographicProjection = YES;
            }
                break;
            case SCN3DDisplayMode_Plane_Slide: {
                float width  = 2.0;
                float height = width / self.videoAspRatio;
                self.shapeNode = [SCNNode nodeWithGeometry:[SCNPlane planeWithWidth:width height:height]];
                self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeRepeat;
                self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeRepeat;
                self.shapeNode.geometry.firstMaterial.cullMode = SCNCullBack;
                self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
                self.cameraNode.camera.usesOrthographicProjection = YES;
                
            }
                break;
            case SCN3DDisplayMode_Tube: {
                SCNTube *tube = [SCNTube tubeWithInnerRadius:1.0 outerRadius:1.0 height:1.0];
                tube.radialSegmentCount = 96;
                self.shapeNode = [SCNNode nodeWithGeometry:tube];
                self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.cullMode = SCNCullBack;
                
                self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
                self.cameraNode.camera.usesOrthographicProjection = YES;
            }
                break;
            case SCN3DDisplayMode_Sphere: {
                
                SCNSphere *sphere = [SCNSphere sphereWithRadius:1.0];
                sphere.segmentCount = 96;
                self.shapeNode = [SCNNode nodeWithGeometry:sphere];
                self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.cullMode = SCNCullBack;// 设置剔除内表面
                self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
                self.cameraNode.camera.usesOrthographicProjection = YES;
            }
                break;
            case SCN3DDisplayMode_VR360: {
      
                SCNSphere *sphere = [SCNSphere sphereWithRadius:1.0];
                sphere.segmentCount = 96;
                self.shapeNode = [SCNNode nodeWithGeometry:sphere];
                self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.cullMode = SCNCullFront;
                self.shapeNode.geometry.firstMaterial.doubleSided = false; // 设置只渲染一个表面
                self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
                self.cameraNode.camera.usesOrthographicProjection = NO;
                
    //            SCNMatrix4 contentsTransform = self.shapeNode.geometry.firstMaterial.diffuse.contentsTransform;
    ////            SCNMatrix4 contentsTransform2 = self.shapeNode.transform;
    //       
    //            contentsTransform =SCNMatrix4Rotate(contentsTransform, 0, M_PI, 0, 0);
    //            self.shapeNode.transform = self.shapeNode.geometry.firstMaterial.diffuse.contentsTransform;
         
                
     
            }
                break;
            case SCN3DDisplayMode_VRGlass: {
                SCNSphere *sphere = [SCNSphere sphereWithRadius:1.0];
                sphere.segmentCount = 96;
                self.shapeNode = [SCNNode nodeWithGeometry:sphere];
                self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
                self.shapeNode.geometry.firstMaterial.cullMode = SCNCullFront;// 设置剔除外表面
                self.shapeNode.geometry.firstMaterial.doubleSided = false; // 设置只渲染一个表面
    //            self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
                self.cameraNode.camera.usesOrthographicProjection = NO;
                
                self.scViewLeft.frame  = CGRectMake(0, 0, self.frame.size.width / 2, self.frame.size.height);
                self.scViewRight = [[SCNView alloc] initWithFrame:CGRectMake(self.frame.size.width / 2, 0, self.frame.size.width / 2, self.frame.size.height) options:nil];
                self.scViewRight.backgroundColor = [UIColor blackColor];
                self.scViewRight.scene = self.scene;
                self.scViewRight.antialiasingMode = SCNAntialiasingModeMultisampling4X;
                [self addSubview:self.scViewRight];
            }
                break;
                
            default:
                break;
        }
        
        self.cameraNode.camera.zNear = 0.01f;
        self.cameraNode.camera.zFar  = 100.0f;
        self.shapeNode.castsShadow = NO;
        self.shapeNode.position = SCNVector3Make(0, 0, 0);
        [self.scene.rootNode addChildNode:self.shapeNode];
        
        self.modelMatrix = self.shapeNode.transform;
        [self setCurrentScale:1.0];
        [self setFramesPerVideoImage:nil];
    }
    

    手指滑动,捏合的相关处理

    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
        UITouch *touch = [touches anyObject];
        CGPoint currLoc = [touch locationInView:self];
        CGPoint lastLoc = [touch previousLocationInView:self];
        CGPoint moveDiff = CGPointMake(currLoc.x - lastLoc.x, currLoc.y - lastLoc.y);
        
        float rotX = -1 * GLKMathDegreesToRadians(moveDiff.y / 5.0);
        float rotY = -1 * GLKMathDegreesToRadians(moveDiff.x / 5.0);
        
        rotX = self.verticalEnabled   ? rotX : 0;
        rotY = self.horizontalEnabled ? rotY : 0;
        [self changeNodeTransformWithRotateX:rotX / self.curScale rotateY:rotY / self.curScale];
    }
    
    - (void)changeNodeTransformWithRotateX:(float)rotX rotateY:(float)rotY {
        switch (self.displayMode) {
            case SCN3DDisplayMode_Plane_Normal: {}
                break;
            case SCN3DDisplayMode_Plane_Slide: {
                SCNMatrix4 contentsTransform = self.shapeNode.geometry.firstMaterial.diffuse.contentsTransform;
                contentsTransform = SCNMatrix4Translate(contentsTransform, rotY, 0, 0);
                contentsTransform = SCNMatrix4Translate(contentsTransform, 0, rotX, 0);
                self.shapeNode.geometry.firstMaterial.diffuse.contentsTransform = contentsTransform;
            }
                break;
            case SCN3DDisplayMode_Tube: {
                self.rotateX += rotX;
                self.rotateY += rotY;
                float minRotate = self.minRotateX / self.curScale;
                float maxRotate = self.maxRotateX / self.curScale;
                
                minRotate = minRotate < (- M_PI / 2) ? (- M_PI / 2) : minRotate;
                maxRotate = maxRotate > (+ M_PI / 2) ? (+ M_PI / 2) : maxRotate;
                self.rotateX = self.rotateX < minRotate ? minRotate : self.rotateX;
                self.rotateX = self.rotateX > maxRotate ? maxRotate : self.rotateX;
                
                SCNMatrix4 modelViewMatrix = SCNMatrix4Identity;
                modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateY, 0, 1, 0);
                modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateX, 1, 0, 0);
                modelViewMatrix = SCNMatrix4Scale(modelViewMatrix, self.curScale, self.curScale, self.curScale);
                self.shapeNode.transform = modelViewMatrix;
            }
                break;
            case SCN3DDisplayMode_Sphere: {
                self.rotateX += rotX;
                self.rotateY += rotY;
                float minRotate = self.minRotateX / self.curScale;
                float maxRotate = self.maxRotateX / self.curScale;
                
                minRotate = minRotate < (- M_PI / 2) ? (- M_PI / 2) : minRotate;
                maxRotate = maxRotate > (+ M_PI / 2) ? (+ M_PI / 2) : maxRotate;
                self.rotateX = self.rotateX < minRotate ? minRotate : self.rotateX;
                self.rotateX = self.rotateX > maxRotate ? maxRotate : self.rotateX;
                
                SCNMatrix4 modelViewMatrix = SCNMatrix4Identity;
                modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateY, 0, 1, 0);
                modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateX, 1, 0, 0);
                modelViewMatrix = SCNMatrix4Scale(modelViewMatrix, self.curScale, self.curScale, self.curScale);
                self.shapeNode.transform = modelViewMatrix;
            }
                break;
                
            default: {
                self.rotateX += -rotX;
                self.rotateY += -rotY;
                float minRotate = self.minRotateX / self.curScale;
                float maxRotate = self.maxRotateX / self.curScale;
                
                minRotate = minRotate < (- M_PI / 2) ? (- M_PI / 2) : minRotate;
                maxRotate = maxRotate > (+ M_PI / 2) ? (+ M_PI / 2) : maxRotate;
                self.rotateX = self.rotateX < minRotate ? minRotate : self.rotateX;
                self.rotateX = self.rotateX > maxRotate ? maxRotate : self.rotateX;
    //            NSLog(@"self.rotateX = %f, %f", self.rotateX, GLKMathRadiansToDegrees(self.rotateX));
                
                SCNMatrix4 modelViewMatrix = SCNMatrix4Identity;
                modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateY, 0, 1, 0);
                modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateX, 1, 0, 0);
                modelViewMatrix = SCNMatrix4Scale(modelViewMatrix, self.curScale, self.curScale, self.curScale);
                self.shapeNode.transform = modelViewMatrix;
            }
                break;
        }
    }
    
    - (void)handlePinchScale:(UIPinchGestureRecognizer *)paramSender {
        if (!self.pinchEnabled) return;
        if (paramSender.state != UIGestureRecognizerStateEnded && paramSender.state != UIGestureRecognizerStateFailed) {
            if (paramSender.scale != NAN && paramSender.scale != 0.0) {
                
                float scale = (paramSender.scale - 1) * 0.50;
                self.curScale = scale + self.prevScale;
                
                if (self.curScale < self.minScale) {
                    self.curScale = self.minScale;
                }
                else if (self.curScale > self.maxScale) {
                    self.curScale = self.maxScale;
                }
                
                [self setCurrentScale:self.curScale];
            }
        } else if(paramSender.state == UIGestureRecognizerStateEnded) {
            self.prevScale = self.curScale;
        }
    }
    
    

    重力感应相关设置

    - (void)startGSENSORMotion {
        float gFPS = 30.0f;
        if (self.displayMode == SCN3DDisplayMode_VRGlass) {
            gFPS = 120.0f;
        }
        self.motionManager = [[CMMotionManager alloc] init];
        self.motionManager.deviceMotionUpdateInterval = 1.0f / gFPS;
        self.motionManager.gyroUpdateInterval = 1.0f / gFPS;
        self.motionManager.showsDeviceMovementDisplay = YES;
        self.rotateX = 0.0f;
        self.rotateY = 0.0f;
        
        NSOperationQueue* motionQueue = [[NSOperationQueue alloc] init];
        [self.motionManager startDeviceMotionUpdatesToQueue:motionQueue withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
            float  damping = 30.0;
            double rotateX = -motion.rotationRate.y / damping;  // X 轴旋转
            double rotateY = -motion.rotationRate.x / damping;  // Y 轴旋转
            
            switch (self.orientation) {
                case UIDeviceOrientationLandscapeRight:
                    rotateX = +motion.rotationRate.x / damping;
                    rotateY = -motion.rotationRate.y / damping;
                    break;
                case UIDeviceOrientationLandscapeLeft:
                    rotateX = -motion.rotationRate.x / damping;
                    rotateY = +motion.rotationRate.y / damping;
                    break;
                case UIDeviceOrientationPortrait:
                default:
                    break;
            }
            [self changeNodeTransformWithRotateX:(rotateY / 2) / self.curScale rotateY:rotateX / self.curScale];
        }];
    }
    
    - (void)stopGSENSORMotion {
        [self.motionManager stopDeviceMotionUpdates];
        self.motionManager = nil;
    }
    

    还有一些相关设置,这里就不介绍了,大家可以自己看,在.h文件里面都有相关注释,如有错误,请指正,谢谢
    源代码
    如果喜欢的话,就star一下

    相关文章

      网友评论

      本文标题:SceneKIt+ AVFoundation 打造VR播放器(2

      本文链接:https://www.haomeiwen.com/subject/npxccxtx.html