下面是我写的播放器
SceneKIt+ AVFoundation 打造VR播放器(1)
支持VR,全景,视频缩放,本地,网络视频播放,实时获取视频帧,获取播放时间,获取缓存时间,播放,暂停
要想完成一个Vr播放器,需要完成两个功能
1、写一个可以实时获取视频帧的播放器
2、写一个可以渲染每一帧图片为全景图片的view
SCN3DPlayerView 图片渲染
用于渲染每一帧图片为全景图片
使用的是
下面是SCN3DPlayerView的一些方法
//
// SCN3DPlayerView.h
// SCN3DPlayer
//
// Created by 俞涛涛 on 16/11/11.
// Copyright © 2016年 俞涛涛. All rights reserved.
//
#import
#import
#import
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
触摸,重力,触摸重力
- 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一下