From 507ae13857c573b5c1af102631496328778b7cfc Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Mon, 22 Aug 2016 16:43:45 -0500 Subject: [PATCH 01/18] Fix bugs related to compass angle calculations. --- .../NYT360EulerAngleCalculationsTests.m | 76 +++++++++++++++++++ Sources/NYT360CameraController.h | 16 +++- Sources/NYT360CameraController.m | 18 ++--- Sources/NYT360EulerAngleCalculations.h | 3 + Sources/NYT360EulerAngleCalculations.m | 19 +++++ Sources/NYT360ViewController.h | 9 ++- Sources/NYT360ViewController.m | 12 ++- 7 files changed, 134 insertions(+), 19 deletions(-) diff --git a/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m b/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m index 09a78f0..e75ae4e 100644 --- a/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m +++ b/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m @@ -7,6 +7,7 @@ // @import XCTest; +@import SceneKit; #import "NYT360DataTypes.h" #import "NYT360EulerAngleCalculations.h" @@ -132,4 +133,79 @@ - (void)testItCalculatesTheOptimalYFovForAVarietyOfInputs { XCTAssertEqualWithAccuracy(NYT360OptimalYFovForViewSize(CGSizeMake(0, 1)), 120.0, 2.0); } +- (void)testItCalculatesTheCorrectCompassAngleForAVarietyOfInputs { + + SCNVector3 eulerAngles; + float pi = M_PI; + float compassAngle; + float referenceAngle; + + referenceAngle = NYT360EulerAngleCalculationDefaultReferenceCompassAngle; + + eulerAngles.y = 0; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi, 0.001); + + eulerAngles.y = pi; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); + + eulerAngles.y = -pi; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); + + eulerAngles.y = pi * 0.5; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi * 1.5, 0.001); + + eulerAngles.y = pi * -0.5; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi * 0.5, 0.001); + + eulerAngles.y = pi * 1.5; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi * 0.5, 0.001); + + eulerAngles.y = pi * -1.5; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi * -0.5, 0.001); + + eulerAngles.y = pi * 2.0; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi, 0.001); + + eulerAngles.y = pi * -2.0; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, -pi, 0.001); + + eulerAngles.y = pi * 2.5; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi * 1.5, 0.001); + + eulerAngles.y = pi * -2.5; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi * -1.5, 0.001); + + eulerAngles.y = pi * 3.0; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); + + eulerAngles.y = pi * -3.0; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, 0, 0.001); + + eulerAngles.y = pi * 4.0; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, pi, 0.001); + + eulerAngles.y = pi * -4.0; + compassAngle = NYT360CompassAngleForEulerAngles(eulerAngles, referenceAngle); + XCTAssertEqualWithAccuracy(compassAngle, -pi, 0.001); +} + @end + + + + + diff --git a/Sources/NYT360CameraController.h b/Sources/NYT360CameraController.h index f8e16c5..736a5ae 100644 --- a/Sources/NYT360CameraController.h +++ b/Sources/NYT360CameraController.h @@ -15,6 +15,13 @@ @class NYT360CameraPanGestureRecognizer; +/** + * The block type used for compass angle updates. + * + * @param compassAngle The compass angle in radians. + */ +typedef void(^NYT360CompassAngleUpdateBlock)(float compassAngle); + NS_ASSUME_NONNULL_BEGIN @interface NYT360CameraController : NSObject @@ -22,9 +29,14 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Camera Angle Direction /** - Returns the latest camera angle direction. + * Returns the current compass angle in radians + */ +@property (nonatomic, readonly) float compassAngle; + +/** + A block invoked whenever the compass angle has been updated. */ -@property (nonatomic, readonly) double cameraAngleDirection; +@property (nonatomic, copy) NYT360CompassAngleUpdateBlock compassAngleUpdateBlock; #pragma mark - Initializers diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index 62c6adc..ce9590d 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -69,16 +69,10 @@ - (void)stopMotionUpdates { self.motionUpdateToken = nil; } -#pragma mark - Camera Angle Direction +#pragma mark - Compass Angle -- (double)cameraAngleDirection { - float x = 0, y = 0, z = -1; - SCNMatrix4 worldMatrix = self.pointOfView.worldTransform; - - float qx = worldMatrix.m11 * x + worldMatrix.m12 * y + worldMatrix.m13 * z + worldMatrix.m14; - float qz = worldMatrix.m31 * x + worldMatrix.m32 * y + worldMatrix.m33 * z + worldMatrix.m34; - - return atan2(qx, qz); +- (float)compassAngle { + return NYT360CompassAngleForEulerAngles(self.pointOfView.eulerAngles, NYT360EulerAngleCalculationDefaultReferenceCompassAngle); } #pragma mark - Camera Angle Updates @@ -96,6 +90,9 @@ - (void)updateCameraAngle { result = NYT360DeviceMotionCalculation(self.currentPosition, rotationRate, orientation, self.allowedPanningAxes, NYT360EulerAngleCalculationNoiseThresholdDefault); self.currentPosition = result.position; self.pointOfView.eulerAngles = result.eulerAngles; + if (self.compassAngleUpdateBlock) { + self.compassAngleUpdateBlock(self.compassAngle); + } } - (void)updateCameraFOV:(CGSize)viewSize { @@ -130,6 +127,9 @@ - (void)handlePan:(UIPanGestureRecognizer *)recognizer { NYT360EulerAngleCalculationResult result = NYT360PanGestureChangeCalculation(self.currentPosition, self.rotateDelta, self.view.bounds.size, self.allowedPanningAxes); self.currentPosition = result.position; self.pointOfView.eulerAngles = result.eulerAngles; + if (self.compassAngleUpdateBlock) { + self.compassAngleUpdateBlock(self.compassAngle); + } break; default: break; diff --git a/Sources/NYT360EulerAngleCalculations.h b/Sources/NYT360EulerAngleCalculations.h index 08761eb..c8912d7 100644 --- a/Sources/NYT360EulerAngleCalculations.h +++ b/Sources/NYT360EulerAngleCalculations.h @@ -11,6 +11,7 @@ @import CoreMotion; extern CGFloat const NYT360EulerAngleCalculationNoiseThresholdDefault; +extern float const NYT360EulerAngleCalculationDefaultReferenceCompassAngle; #import "NYT360DataTypes.h" @@ -31,3 +32,5 @@ NYT360EulerAngleCalculationResult NYT360DeviceMotionCalculation(CGPoint position NYT360EulerAngleCalculationResult NYT360PanGestureChangeCalculation(CGPoint position, CGPoint rotateDelta, CGSize viewSize, NYT360PanningAxis allowedPanningAxes); CGFloat NYT360OptimalYFovForViewSize(CGSize viewSize); + +float NYT360CompassAngleForEulerAngles(SCNVector3 eulerAngles, float referenceAngle); diff --git a/Sources/NYT360EulerAngleCalculations.m b/Sources/NYT360EulerAngleCalculations.m index 9a068c6..6538baa 100644 --- a/Sources/NYT360EulerAngleCalculations.m +++ b/Sources/NYT360EulerAngleCalculations.m @@ -11,6 +11,7 @@ #pragma mark - Constants CGFloat const NYT360EulerAngleCalculationNoiseThresholdDefault = 0.12; +float const NYT360EulerAngleCalculationDefaultReferenceCompassAngle = M_PI; #pragma mark - Inline Functions @@ -37,6 +38,20 @@ static inline CGPoint NYT360AdjustPositionForAllowedAxes(CGPoint position, NYT36 return position; } +static inline float NYT360UnitRotationForCameraRotation(float cameraRotation) { + float oneRotation = 2.0 * M_PI; + float rawResult = fmodf(cameraRotation, oneRotation); + // `rawResult` will be less than `oneRotation`, but if it's equal (or very + // close to equal) to `oneRotation` than wrap the result back around to + // zero radians. This is so that we don't pass the host application a radian + // value that is greater than one rotation, which wouldn't make sense in + // the context of a compass animation. + float accuracy = 0.0001; + float difference = oneRotation - fabsf(rawResult); + float wrappedAround = (difference < accuracy) ? 0 : rawResult; + return wrappedAround; +} + #pragma mark - Calculations NYT360EulerAngleCalculationResult NYT360UpdatedPositionAndAnglesForAllowedAxes(CGPoint position, NYT360PanningAxis allowedPanningAxes) { @@ -136,3 +151,7 @@ CGFloat NYT360OptimalYFovForViewSize(CGSize viewSize) { } return yFov; } + +float NYT360CompassAngleForEulerAngles(SCNVector3 eulerAngles, float referenceAngle) { + return NYT360UnitRotationForCameraRotation(eulerAngles.y + referenceAngle); +} diff --git a/Sources/NYT360ViewController.h b/Sources/NYT360ViewController.h index ea7797b..be96aa7 100644 --- a/Sources/NYT360ViewController.h +++ b/Sources/NYT360ViewController.h @@ -24,11 +24,12 @@ NS_ASSUME_NONNULL_BEGIN @protocol NYT360ViewControllerDelegate /** - * Called when the camera angle was updated. + * Called when the compass angle is updated. * * @param viewController The view controller that updated the angle. + * @param compassAngle The current compass angle. */ -- (void)cameraAngleWasUpdated:(NYT360ViewController *)viewController; +- (void)nyt360ViewController:(NYT360ViewController *)viewController didUpdateCompassAngle:(float)compassAngle; @end @@ -51,9 +52,9 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Camera Movement /** - Returns the latest camera angle direction. + Returns the current compass angle. */ -@property (nonatomic, readonly) double cameraAngleDirection; +@property (nonatomic, readonly) float compassAngle; /** * An otherwise vanilla subclass of UIPanGestureRecognizer used by NYT360Video to enable manual camera panning. This class is exposed so that host applications can more easily configure interaction with other gesture recognizers without having to have references to specific instances of an NYT360Video pan recognizer. diff --git a/Sources/NYT360ViewController.m b/Sources/NYT360ViewController.m index fd83a40..0643d7e 100644 --- a/Sources/NYT360ViewController.m +++ b/Sources/NYT360ViewController.m @@ -62,6 +62,12 @@ - (instancetype)initWithAVPlayer:(AVPlayer *)player motionManager:(id)renderer updateAtTime:(NSTimeInterval)time { [self.cameraController updateCameraAngle]; - - [self.delegate cameraAngleWasUpdated:self]; } @end From 6d136bee7e1190d8f13c34fba6f119f0f36d2c9f Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Mon, 22 Aug 2016 16:53:53 -0500 Subject: [PATCH 02/18] Remove some whitespace. --- NYT360VideoTests/NYT360EulerAngleCalculationsTests.m | 5 ----- 1 file changed, 5 deletions(-) diff --git a/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m b/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m index e75ae4e..ff439ae 100644 --- a/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m +++ b/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m @@ -204,8 +204,3 @@ - (void)testItCalculatesTheCorrectCompassAngleForAVarietyOfInputs { } @end - - - - - From f9a527337db35f9228398aa172493314aa9f9eee Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Mon, 22 Aug 2016 16:57:03 -0500 Subject: [PATCH 03/18] Update comments. --- Sources/NYT360EulerAngleCalculations.m | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Sources/NYT360EulerAngleCalculations.m b/Sources/NYT360EulerAngleCalculations.m index 6538baa..7037b94 100644 --- a/Sources/NYT360EulerAngleCalculations.m +++ b/Sources/NYT360EulerAngleCalculations.m @@ -39,16 +39,19 @@ static inline CGPoint NYT360AdjustPositionForAllowedAxes(CGPoint position, NYT36 } static inline float NYT360UnitRotationForCameraRotation(float cameraRotation) { + + // Use a modulus so that we don't pass the host application a compass angle + // value that is greater than one rotation, which wouldn't make sense in the + // context of a compass animation. float oneRotation = 2.0 * M_PI; float rawResult = fmodf(cameraRotation, oneRotation); - // `rawResult` will be less than `oneRotation`, but if it's equal (or very - // close to equal) to `oneRotation` than wrap the result back around to - // zero radians. This is so that we don't pass the host application a radian - // value that is greater than one rotation, which wouldn't make sense in - // the context of a compass animation. + + // `rawResult` will be less than `oneRotation`, but if it's very very close + // to `oneRotation` than wrap the result back around to zero radians. float accuracy = 0.0001; float difference = oneRotation - fabsf(rawResult); float wrappedAround = (difference < accuracy) ? 0 : rawResult; + return wrappedAround; } From 4eb4289d32098bf216f8eaf1c361787a79a9593a Mon Sep 17 00:00:00 2001 From: Julieta Date: Tue, 23 Aug 2016 10:35:50 -0300 Subject: [PATCH 04/18] Send New Video Analytics I need to create a method to expose in the delegate when the camera moved. https://jira.nyt.net/browse/MO-7039 --- Sources/NYT360CameraController.h | 18 ++++++++++++++++++ Sources/NYT360CameraController.m | 18 ++++++++++++++++++ Sources/NYT360ViewController.h | 8 ++++++++ Sources/NYT360ViewController.m | 9 ++++++++- 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Sources/NYT360CameraController.h b/Sources/NYT360CameraController.h index f8e16c5..1169703 100644 --- a/Sources/NYT360CameraController.h +++ b/Sources/NYT360CameraController.h @@ -14,11 +14,29 @@ #import "NYT360MotionManagement.h" @class NYT360CameraPanGestureRecognizer; +@class NYT360CameraController; NS_ASSUME_NONNULL_BEGIN +@protocol NYT360CameraControllerDelegate + +/** + * Called when the camera moved. + * + * @param controller The controller that start movement. + * @param method The method name that start movement 'touch' or 'gyroscope'. + */ +- (void)cameraController:(NYT360CameraController *)controller didMoveWithMethod:(NSString *)method; + +@end + @interface NYT360CameraController : NSObject +/** + * The delegate of the controller. + */ +@property (nullable, nonatomic, weak) id delegate; + #pragma mark - Camera Angle Direction /** diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index 62c6adc..12a10ef 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -11,6 +11,8 @@ #import "NYT360CameraPanGestureRecognizer.h" static const NSTimeInterval NYT360CameraControllerPreferredMotionUpdateInterval = (1.0 / 60.0); +static const NSString * const NYT360CameraControllerMethodTouch = @"touch"; +static const NSString * const NYT360CameraControllerMethodGyroscope = @"gyroscope"; static inline CGPoint subtractPoints(CGPoint a, CGPoint b) { return CGPointMake(b.x - a.x, b.y - a.y); @@ -28,6 +30,8 @@ @interface NYT360CameraController () @property (nonatomic, assign) CGPoint rotateDelta; @property (nonatomic, assign) CGPoint currentPosition; +@property (nonatomic, assign) BOOL cameraMoved; + @end @implementation NYT360CameraController @@ -94,6 +98,11 @@ - (void)updateCameraAngle { UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; NYT360EulerAngleCalculationResult result; result = NYT360DeviceMotionCalculation(self.currentPosition, rotationRate, orientation, self.allowedPanningAxes, NYT360EulerAngleCalculationNoiseThresholdDefault); + + if (CGPointEqualToPoint(self.currentPosition, result.position) == NO ) { + [self cameraMovedWithMethod:NYT360CameraControllerMethodGyroscope]; + } + self.currentPosition = result.position; self.pointOfView.eulerAngles = result.eulerAngles; } @@ -130,10 +139,19 @@ - (void)handlePan:(UIPanGestureRecognizer *)recognizer { NYT360EulerAngleCalculationResult result = NYT360PanGestureChangeCalculation(self.currentPosition, self.rotateDelta, self.view.bounds.size, self.allowedPanningAxes); self.currentPosition = result.position; self.pointOfView.eulerAngles = result.eulerAngles; + + [self cameraMovedWithMethod:NYT360CameraControllerMethodTouch]; break; default: break; } } +- (void)cameraMovedWithMethod:(NSString *)method { + if (!self.cameraMoved) { + self.cameraMoved = YES; + [self.delegate cameraController:self didMoveWithMethod:method]; + } +} + @end diff --git a/Sources/NYT360ViewController.h b/Sources/NYT360ViewController.h index ea7797b..ce1544b 100644 --- a/Sources/NYT360ViewController.h +++ b/Sources/NYT360ViewController.h @@ -30,6 +30,14 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)cameraAngleWasUpdated:(NYT360ViewController *)viewController; +/** + * Called when the camera moved. + * + * @param viewController The view Controller that start movement. + * @param method The method name that start movement 'touch' or 'gyroscope'. + */ +- (void)videoViewController:(NYT360ViewController *)viewController didMoveWithMethod:(NSString *)method; + @end @interface NYT360ViewController : UIViewController diff --git a/Sources/NYT360ViewController.m b/Sources/NYT360ViewController.m index fd83a40..30e1ad2 100644 --- a/Sources/NYT360ViewController.m +++ b/Sources/NYT360ViewController.m @@ -40,7 +40,7 @@ CGRect NYT360ViewControllerSceneBoundsForScreenBounds(CGRect screenBounds) { return CGRectMake(0, 0, max, min); } -@interface NYT360ViewController () +@interface NYT360ViewController () @property (nonatomic, readonly) CGSize underlyingSceneSize; @property (nonatomic, readonly) SCNView *sceneView; @@ -62,6 +62,7 @@ - (instancetype)initWithAVPlayer:(AVPlayer *)player motionManager:(id)renderer updateAtTime:(NSTimeInterval)ti [self.delegate cameraAngleWasUpdated:self]; } +#pragma mark - NYT360CameraControllerDelegate + +- (void)cameraController:(NYT360CameraController *)controller didMoveWithMethod:(NSString *)method { + [self.delegate videoViewController:self didMoveWithMethod:method]; +} + @end From 548d2987ea5cabcc8ff00948b0bae59d0d983ced Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Tue, 23 Aug 2016 14:03:49 -0500 Subject: [PATCH 05/18] Add workarounds for iOS 10 regressions. --- Sources/NYT360PlayerScene.m | 82 +++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/Sources/NYT360PlayerScene.m b/Sources/NYT360PlayerScene.m index dc00f10..4429529 100644 --- a/Sources/NYT360PlayerScene.m +++ b/Sources/NYT360PlayerScene.m @@ -7,6 +7,7 @@ // @import SpriteKit; +@import AVFoundation; #import "NYT360PlayerScene.h" @@ -28,6 +29,8 @@ - (BOOL)videoNodeShouldAllowPlaybackToBegin:(NYTSKVideoNode *)videoNode; /** * There is a bug in SceneKit wherein a paused video node will begin playing again when the application becomes active. This is caused by cascading calls to `[fooNode setPaused:NO]` across all nodes in a scene. To prevent the video node from unpausing along with the rest of the nodes, we must subclass SKVideoNode and override `setPaused:`, only unpausing the node if `nytDelegate` allows it. + * + * This SceneKit bug is present on iOS 9 as well as iOS 10 (at least up to beta 7, the latest at the time of this writing). */ @interface NYTSKVideoNode: SKVideoNode @@ -60,6 +63,7 @@ @interface NYT360PlayerScene () @property (nonatomic, assign) BOOL videoPlaybackIsPaused; @property (nonatomic, readonly) SCNNode *cameraNode; @property (nonatomic, readonly) NYTSKVideoNode *videoNode; +@property (nonatomic, readonly) AVPlayer *player; @end @@ -70,6 +74,8 @@ - (instancetype)initWithAVPlayer:(AVPlayer *)player boundToView:(SCNView *)view _videoPlaybackIsPaused = YES; + _player = player; + _camera = [SCNCamera new]; _cameraNode = ({ @@ -123,10 +129,34 @@ - (void)play { // See note in NYTSKVideoNode above. self.videoPlaybackIsPaused = NO; - // Internally, SceneKit prefers to use `setPaused:` to toggle playback on a - // video node. Mimic this usage here to ensure consistency and avoid putting - // the player into an out-of-sync state. - self.videoNode.paused = NO; + if ([self isIOS10OrLater]) { + // On iOS 10, AVPlayer playback on a video node seems to work most + // reliably by directly invoking `play` and `pause` on the player, + // rather than the alternatives: calling the `play` and `pause` methods + // or the `setPaused:` setter on the video node. Those alternatives + // either do not work at all in certain cases, or they lead to spurious + // rate changes on the AVPlayer, which fire KVO notifications that + // NYTVideoViewController is not designed to handle. In an ideal world, + // we could refactor NYTVideoViewController to handle such edge cases. + // In practice, it is preferable to use the following technique so that + // 360 AVPlayer behavior on iOS 10 is the same as on iOS 9, and the same + // as standard AVPlayer behavior on both operating systems. + [self.player play]; + // On iOS 10, you must also update the `paused` property of the video + // node to match the playback state of its AVPlayer if you have invoked + // play/pause directly on the AVPlayer, otherwise the video node's + // internal state and the AVPlayer's timeControlStatus can get out of + // sync. One symptom of this problem is where the video can look like + // it's paused even though the audio is still playing in the background. + // The steps to reproduce this particular bug are finicky but reliable. + self.videoNode.paused = NO; + } + else { + // Prior to iOS 10, SceneKit prefers to use `setPaused:` alone to toggle + // playback on a video node. Mimic this usage here to ensure consistency + // and avoid putting the player into an out-of-sync state. + self.videoNode.paused = NO; + } } @@ -135,16 +165,52 @@ - (void)pause { // See note in NYTSKVideoNode above. self.videoPlaybackIsPaused = YES; - // Internally, SceneKit prefers to use `setPaused:` to toggle playback on a - // video node. Mimic this usage here to ensure consistency and avoid putting - // the player into an out-of-sync state. - self.videoNode.paused = YES; + if ([self isIOS10OrLater]) { + // On iOS 10, AVPlayer playback on a video node seems to work most + // reliably by directly invoking `play` and `pause` on the player, + // rather than the alternatives: calling the `play` and `pause` methods + // or the `setPaused:` setter on the video node. Those alternatives + // either do not work at all in certain cases, or they lead to spurious + // rate changes on the AVPlayer, which fire KVO notifications that + // NYTVideoViewController is not designed to handle. In an ideal world, + // we could refactor NYTVideoViewController to handle such edge cases. + // In practice, it is preferable to use the following technique so that + // 360 AVPlayer behavior on iOS 10 is the same as on iOS 9, and the same + // as standard AVPlayer behavior on both operating systems. + [self.player pause]; + // On iOS 10, you must also update the `paused` property of the video + // node to match the playback state of its AVPlayer if you have invoked + // play/pause directly on the AVPlayer, otherwise the video node's + // internal state and the AVPlayer's timeControlStatus can get out of + // sync. One symptom of this problem is where the video can look like + // it's paused even though the audio is still playing in the background. + // The steps to reproduce this particular bug are finicky but reliable. + self.videoNode.paused = YES; + } + else { + // Prior to iOS 10, SceneKit prefers to use `setPaused:` alone to toggle + // playback on a video node. Mimic this usage here to ensure consistency + // and avoid putting the player into an out-of-sync state. + self.videoNode.paused = YES; + } } +#pragma mark - NYTSKVideoNodeDelegate + - (BOOL)videoNodeShouldAllowPlaybackToBegin:(NYTSKVideoNode *)videoNode { // See note in NYTSKVideoNode above. return !self.videoPlaybackIsPaused; } +#pragma mark - Convenience + +- (BOOL)isIOS10OrLater { + NSOperatingSystemVersion ios10; + ios10.majorVersion = 10; + ios10.minorVersion = 0; + ios10.patchVersion = 0; + return [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios10]; +} + @end From c65c56123bc506e3e9be0f82cefdc7d1e6881b54 Mon Sep 17 00:00:00 2001 From: Julieta Date: Wed, 24 Aug 2016 09:10:30 -0300 Subject: [PATCH 06/18] Changes after PR --- Sources/NYT360CameraController.h | 2 +- Sources/NYT360CameraController.m | 18 ++++++++++-------- Sources/NYT360DataTypes.h | 5 +++++ Sources/NYT360ViewController.h | 4 ++-- Sources/NYT360ViewController.m | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Sources/NYT360CameraController.h b/Sources/NYT360CameraController.h index 1169703..1855f77 100644 --- a/Sources/NYT360CameraController.h +++ b/Sources/NYT360CameraController.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN * @param controller The controller that start movement. * @param method The method name that start movement 'touch' or 'gyroscope'. */ -- (void)cameraController:(NYT360CameraController *)controller didMoveWithMethod:(NSString *)method; +- (void)cameraController:(NYT360CameraController *)controller didMoveWithMethod:(NYT360VideoMovedMethod)method; @end diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index 12a10ef..e939be7 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -11,8 +11,6 @@ #import "NYT360CameraPanGestureRecognizer.h" static const NSTimeInterval NYT360CameraControllerPreferredMotionUpdateInterval = (1.0 / 60.0); -static const NSString * const NYT360CameraControllerMethodTouch = @"touch"; -static const NSString * const NYT360CameraControllerMethodGyroscope = @"gyroscope"; static inline CGPoint subtractPoints(CGPoint a, CGPoint b) { return CGPointMake(b.x - a.x, b.y - a.y); @@ -30,7 +28,7 @@ @interface NYT360CameraController () @property (nonatomic, assign) CGPoint rotateDelta; @property (nonatomic, assign) CGPoint currentPosition; -@property (nonatomic, assign) BOOL cameraMoved; +@property (nonatomic, assign) BOOL hasCameraAlreadyMoved; @end @@ -55,6 +53,8 @@ - (instancetype)initWithView:(SCNView *)view motionManager:(id)renderer updateAtTime:(NSTimeInterval)ti #pragma mark - NYT360CameraControllerDelegate -- (void)cameraController:(NYT360CameraController *)controller didMoveWithMethod:(NSString *)method { +- (void)cameraController:(NYT360CameraController *)controller didMoveWithMethod:(NYT360VideoMovedMethod)method { [self.delegate videoViewController:self didMoveWithMethod:method]; } From 29a5ef675f2f816f50e64c1b4bcc645d11453194 Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Wed, 24 Aug 2016 10:43:05 -0500 Subject: [PATCH 07/18] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 920843d..2d1b8ba 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ NYT360Video is available through [CocoaPods](http://cocoapods.org). To install i pod 'NYT360Video' ``` +## Known Issues + +- **iOS 10 CoreAudio Crash** - On devices running iOS 10 (at least as of Beta 7), host applications will crash if the device is locked while an NYT360ViewController is visible (whether paused or not). The crash is caused by a CoreAudio exception. [An extended discussion of the issue can be found here](https://github.com/nytm/ios-360-videos/issues/37). A workaround that appears to work for some, though not all, apps is to enable the background audio capability in the host application's plist. + ## Authors - Maxwell Dayvson Da Silva: From 4db3b341de6e63ebb2245c5cc7780b293b7511fc Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Wed, 24 Aug 2016 11:47:28 -0500 Subject: [PATCH 08/18] Add additional camera angle options. --- NYT360VideoExample/ViewController.m | 7 +++ Sources/NYT360CameraController.h | 26 +++++++-- Sources/NYT360CameraController.m | 72 +++++++++++++++++++++--- Sources/NYT360ViewController.h | 20 ++++++- Sources/NYT360ViewController.m | 22 ++++++-- ios-360-videos.xcodeproj/project.pbxproj | 3 + 6 files changed, 129 insertions(+), 21 deletions(-) diff --git a/NYT360VideoExample/ViewController.m b/NYT360VideoExample/ViewController.m index 247c128..3fbcbff 100644 --- a/NYT360VideoExample/ViewController.m +++ b/NYT360VideoExample/ViewController.m @@ -35,6 +35,13 @@ - (void)viewDidLoad { [self.nyt360VC didMoveToParentViewController:self]; [self.player play]; + + UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reorientVerticalCameraAngle:)]; + [self.view addGestureRecognizer:tapRecognizer]; +} + +- (void)reorientVerticalCameraAngle:(id)sender { + [self.nyt360VC reorientVerticalCameraAngleToHorizon:YES]; } @end diff --git a/Sources/NYT360CameraController.h b/Sources/NYT360CameraController.h index f8e16c5..29b5e66 100644 --- a/Sources/NYT360CameraController.h +++ b/Sources/NYT360CameraController.h @@ -56,15 +56,22 @@ NS_ASSUME_NONNULL_BEGIN /** * Updates the camera angle based on the current device motion. It's assumed that this method will be called many times a second during SceneKit rendering updates. */ -- (void)updateCameraAngle; - -#pragma mark - Panning Options +- (void)updateCameraAngleForCurrentDeviceMotion; /** * Updates the yFov of the camera to provide the optimal viewing angle for a given view size. Portrait videos will use a wider angle than landscape videos. */ - (void)updateCameraFOV:(CGSize)viewSize; +/** + * Reorients the camera's vertical angle component so it's pointing directly at the horizon. + * + * @param animated Passing `YES` will animate the change with a standard duration. + */ +- (void)reorientVerticalCameraAngleToHorizon:(BOOL)animated; + +#pragma mark - Panning Options + /** * An otherwise vanilla subclass of UIPanGestureRecognizer used by NYT360Video to enable manual camera panning. This class is exposed so that host applications can more easily configure interaction with other gesture recognizers without having to have references to specific instances of an NYT360Video pan recognizer. */ @@ -72,13 +79,22 @@ NS_ASSUME_NONNULL_BEGIN /** - * Changing this property will allow you to suppress undesired range of motion along either the x or y axis. For example, y axis input should be suppressed when a 360 video is playing inline in a scroll view. + * Changing this property will allow you to suppress undesired range of motion along either the x or y axis for device motion input. For example, y axis input might be suppressed when a 360 video is playing inline in a scroll view. + + * When this property is set, any disallowed axis will cause the current camera angles to be clamped to zero for that axis. Existing angles for the any allowed axes will not be affected. + + * Defaults to NYT360PanningAxisHorizontal | NYT360PanningAxisVertical. + */ +@property (nonatomic, assign) NYT360PanningAxis allowedDeviceMotionPanningAxes; + +/** + * Changing this property will allow you to suppress undesired range of motion along either the x or y axis for pan gesture recognizer input. For example, y axis input should probably be suppressed when a 360 video is playing inline in a scroll view. * When this property is set, any disallowed axis will cause the current camera angles to be clamped to zero for that axis. Existing angles for the any allowed axes will not be affected. * Defaults to NYT360PanningAxisHorizontal | NYT360PanningAxisVertical. */ -@property (nonatomic, assign) NYT360PanningAxis allowedPanningAxes; +@property (nonatomic, assign) NYT360PanningAxis allowedPanGesturePanningAxes; @end diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index 62c6adc..052be62 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -28,6 +28,8 @@ @interface NYT360CameraController () @property (nonatomic, assign) CGPoint rotateDelta; @property (nonatomic, assign) CGPoint currentPosition; +@property (nonatomic, assign) BOOL isAnimatingReorientation; + @end @implementation NYT360CameraController @@ -44,7 +46,8 @@ - (instancetype)initWithView:(SCNView *)view motionManager:(id)renderer updateAtTime:(NSTimeInterval)time { - [self.cameraController updateCameraAngle]; + [self.cameraController updateCameraAngleForCurrentDeviceMotion]; [self.delegate cameraAngleWasUpdated:self]; } diff --git a/ios-360-videos.xcodeproj/project.pbxproj b/ios-360-videos.xcodeproj/project.pbxproj index b858f10..f4e0403 100644 --- a/ios-360-videos.xcodeproj/project.pbxproj +++ b/ios-360-videos.xcodeproj/project.pbxproj @@ -317,6 +317,7 @@ }; 934A496A1D46B3FD001AD295 = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = FL458598BU; }; }; }; @@ -684,6 +685,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = FL458598BU; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -735,6 +737,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = FL458598BU; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; From d069cc1aba7311eba541f57c03d1c133fa5c82d5 Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Wed, 24 Aug 2016 11:56:20 -0500 Subject: [PATCH 09/18] Revert Xcode project change. --- ios-360-videos.xcodeproj/project.pbxproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ios-360-videos.xcodeproj/project.pbxproj b/ios-360-videos.xcodeproj/project.pbxproj index f4e0403..a0674fa 100644 --- a/ios-360-videos.xcodeproj/project.pbxproj +++ b/ios-360-videos.xcodeproj/project.pbxproj @@ -317,7 +317,6 @@ }; 934A496A1D46B3FD001AD295 = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = FL458598BU; }; }; }; @@ -685,7 +684,6 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = FL458598BU; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -737,7 +735,6 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = FL458598BU; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -802,4 +799,4 @@ /* End XCConfigurationList section */ }; rootObject = 934A493E1D46AFBB001AD295 /* Project object */; -} +} \ No newline at end of file From 8b9c0ffc093504e2c258a40501427012cbdff74c Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Wed, 24 Aug 2016 11:57:17 -0500 Subject: [PATCH 10/18] Revert whitespace change to Xcode project. --- ios-360-videos.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios-360-videos.xcodeproj/project.pbxproj b/ios-360-videos.xcodeproj/project.pbxproj index a0674fa..b858f10 100644 --- a/ios-360-videos.xcodeproj/project.pbxproj +++ b/ios-360-videos.xcodeproj/project.pbxproj @@ -799,4 +799,4 @@ /* End XCConfigurationList section */ }; rootObject = 934A493E1D46AFBB001AD295 /* Project object */; -} \ No newline at end of file +} From 1396b19bba4d589765d29329673edf995a73a1b5 Mon Sep 17 00:00:00 2001 From: Julieta Date: Thu, 25 Aug 2016 09:34:47 -0300 Subject: [PATCH 11/18] Prevent the gyroscope throw with a small movements. --- Sources/NYT360CameraController.m | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index e939be7..040e82a 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -11,6 +11,11 @@ #import "NYT360CameraPanGestureRecognizer.h" static const NSTimeInterval NYT360CameraControllerPreferredMotionUpdateInterval = (1.0 / 60.0); +static const CGFloat NYT360CameraControllerMinimalRotationDistance = 0.70; + +static inline CGFloat distance(CGPoint a, CGPoint b) { + return sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2)); +} static inline CGPoint subtractPoints(CGPoint a, CGPoint b) { return CGPointMake(b.x - a.x, b.y - a.y); @@ -98,13 +103,12 @@ - (void)updateCameraAngle { UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; NYT360EulerAngleCalculationResult result; result = NYT360DeviceMotionCalculation(self.currentPosition, rotationRate, orientation, self.allowedPanningAxes, NYT360EulerAngleCalculationNoiseThresholdDefault); + self.currentPosition = result.position; + self.pointOfView.eulerAngles = result.eulerAngles; - if (CGPointEqualToPoint(self.currentPosition, result.position) == NO ) { + if (distance(CGPointZero, self.currentPosition) > NYT360CameraControllerMinimalRotationDistance) { [self cameraMovedWithMethod:NYT360VideoMovedMethodGyroscope]; } - - self.currentPosition = result.position; - self.pointOfView.eulerAngles = result.eulerAngles; } - (void)updateCameraFOV:(CGSize)viewSize { @@ -119,8 +123,7 @@ - (void)setAllowedPanningAxes:(NYT360PanningAxis)allowedPanningAxes { _allowedPanningAxes = allowedPanningAxes; NYT360EulerAngleCalculationResult result = NYT360UpdatedPositionAndAnglesForAllowedAxes(self.currentPosition, allowedPanningAxes); self.currentPosition = result.position; - self.pointOfView.eulerAngles = result.eulerAngles; - + self.pointOfView.eulerAngles = result.eulerAngles; } } From da55e6af2a8e9b4408b405286c99f5231a7015a9 Mon Sep 17 00:00:00 2001 From: Chris Dzombak Date: Thu, 25 Aug 2016 14:49:52 -0400 Subject: [PATCH 12/18] Clean up naming & documentation for initial-movement delegate --- Sources/NYT360CameraController.h | 10 ++++++---- Sources/NYT360CameraController.m | 21 ++++++++++----------- Sources/NYT360DataTypes.h | 6 +++--- Sources/NYT360ViewController.h | 12 +++++++----- Sources/NYT360ViewController.m | 4 ++-- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/Sources/NYT360CameraController.h b/Sources/NYT360CameraController.h index 1855f77..7877c61 100644 --- a/Sources/NYT360CameraController.h +++ b/Sources/NYT360CameraController.h @@ -21,12 +21,14 @@ NS_ASSUME_NONNULL_BEGIN @protocol NYT360CameraControllerDelegate /** - * Called when the camera moved. + * Called the first time the user moves the camera. * - * @param controller The controller that start movement. - * @param method The method name that start movement 'touch' or 'gyroscope'. + * @note This method is called synchronously when the camera angle is updated; an implementation should return quickly to avoid performance implications. + * + * @param controller The camera controller with which the user interacted. + * @param method The method by which the user moved the camera. */ -- (void)cameraController:(NYT360CameraController *)controller didMoveWithMethod:(NYT360VideoMovedMethod)method; +- (void)cameraController:(NYT360CameraController *)controller userInitallyMovedCameraViaMethod:(NYT360UserInteractionMethod)method; @end diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index 040e82a..57cfec1 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -33,7 +33,7 @@ @interface NYT360CameraController () @property (nonatomic, assign) CGPoint rotateDelta; @property (nonatomic, assign) CGPoint currentPosition; -@property (nonatomic, assign) BOOL hasCameraAlreadyMoved; +@property (nonatomic, assign) BOOL hasReportedInitialCameraMovement; @end @@ -58,8 +58,8 @@ - (instancetype)initWithView:(SCNView *)view motionManager:(id NYT360CameraControllerMinimalRotationDistance) { - [self cameraMovedWithMethod:NYT360VideoMovedMethodGyroscope]; + [self reportInitialCameraMovementIfNeededViaMethod:NYT360UserInteractionMethodGyroscope]; } } @@ -143,19 +143,18 @@ - (void)handlePan:(UIPanGestureRecognizer *)recognizer { self.currentPosition = result.position; self.pointOfView.eulerAngles = result.eulerAngles; - [self cameraMovedWithMethod:NYT360VideoMovedMethodTouch]; + [self reportInitialCameraMovementIfNeededViaMethod:NYT360UserInteractionMethodTouch]; break; default: break; } } - -- (void)cameraMovedWithMethod:(NYT360VideoMovedMethod)method { - //only fire once per video - if (self.hasCameraAlreadyMoved == NO) { - self.hasCameraAlreadyMoved = YES; - [self.delegate cameraController:self didMoveWithMethod:method]; +- (void)reportInitialCameraMovementIfNeededViaMethod:(NYT360UserInteractionMethod)method { + // only fire once per video: + if (!self.hasReportedInitialCameraMovement) { + self.hasReportedInitialCameraMovement = YES; + [self.delegate cameraController:self userInitallyMovedCameraViaMethod:method]; } } diff --git a/Sources/NYT360DataTypes.h b/Sources/NYT360DataTypes.h index 44994cf..f155cb1 100644 --- a/Sources/NYT360DataTypes.h +++ b/Sources/NYT360DataTypes.h @@ -13,7 +13,7 @@ typedef NS_OPTIONS(NSInteger, NYT360PanningAxis) { NYT360PanningAxisVertical = 1 << 1 }; -typedef NS_ENUM(NSInteger, NYT360VideoMovedMethod) { - NYT360VideoMovedMethodGyroscope = 0, - NYT360VideoMovedMethodTouch +typedef NS_ENUM(NSInteger, NYT360UserInteractionMethod) { + NYT360UserInteractionMethodGyroscope = 0, + NYT360UserInteractionMethodTouch, }; diff --git a/Sources/NYT360ViewController.h b/Sources/NYT360ViewController.h index 10cb485..1a41d1e 100644 --- a/Sources/NYT360ViewController.h +++ b/Sources/NYT360ViewController.h @@ -26,17 +26,19 @@ NS_ASSUME_NONNULL_BEGIN /** * Called when the camera angle was updated. * - * @param viewController The view controller that updated the angle. + * @note This method is called synchronously from SCNSceneRendererDelegate; its implementation should return quickly to avoid performance implications. + * + * @param viewController The view controller whose camera angle was updated. */ - (void)cameraAngleWasUpdated:(NYT360ViewController *)viewController; /** - * Called when the camera moved. + * Called when the user first moves the camera. * - * @param viewController The view Controller that start movement. - * @param method The method enum that start movement 'touch' or 'gyroscope'. + * @param viewController The view controller with which the user interacted. + * @param method The method by which the user moved the camera. */ -- (void)videoViewController:(NYT360ViewController *)viewController cameraDidMoveWithMethod:(NYT360VideoMovedMethod)method; +- (void)videoViewController:(NYT360ViewController *)viewController userInitallyMovedCameraViaMethod:(NYT360UserInteractionMethod)method; @end diff --git a/Sources/NYT360ViewController.m b/Sources/NYT360ViewController.m index b1a954f..0e537c9 100644 --- a/Sources/NYT360ViewController.m +++ b/Sources/NYT360ViewController.m @@ -174,8 +174,8 @@ - (void)renderer:(id )renderer updateAtTime:(NSTimeInterval)ti #pragma mark - NYT360CameraControllerDelegate -- (void)cameraController:(NYT360CameraController *)controller didMoveWithMethod:(NYT360VideoMovedMethod)method { - [self.delegate videoViewController:self didMoveWithMethod:method]; +- (void)cameraController:(NYT360CameraController *)controller userInitallyMovedCameraViaMethod:(NYT360UserInteractionMethod)method { + [self.delegate videoViewController:self userInitallyMovedCameraViaMethod:method]; } @end From b9a6aceb6cbfc3debda8133316d335ee1e39dd20 Mon Sep 17 00:00:00 2001 From: Chris Dzombak Date: Thu, 25 Aug 2016 14:50:24 -0400 Subject: [PATCH 13/18] Move constants used once closer to usage sites --- Sources/NYT360CameraController.m | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index 57cfec1..6373f09 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -10,9 +10,6 @@ #import "NYT360EulerAngleCalculations.h" #import "NYT360CameraPanGestureRecognizer.h" -static const NSTimeInterval NYT360CameraControllerPreferredMotionUpdateInterval = (1.0 / 60.0); -static const CGFloat NYT360CameraControllerMinimalRotationDistance = 0.70; - static inline CGFloat distance(CGPoint a, CGPoint b) { return sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2)); } @@ -68,7 +65,9 @@ - (instancetype)initWithView:(SCNView *)view motionManager:(id NYT360CameraControllerMinimalRotationDistance) { + + static const CGFloat minimalRotationDistanceToReport = 0.75; + if (distance(CGPointZero, self.currentPosition) > minimalRotationDistanceToReport) { [self reportInitialCameraMovementIfNeededViaMethod:NYT360UserInteractionMethodGyroscope]; } } From ab6838cfcb4a38d00e84ebd0211f2af2637842bd Mon Sep 17 00:00:00 2001 From: Chris Dzombak Date: Thu, 25 Aug 2016 14:50:34 -0400 Subject: [PATCH 14/18] Whitespace and style fixes --- Sources/NYT360CameraController.m | 6 ++++-- Sources/NYT360DataTypes.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index 6373f09..4f05441 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -11,7 +11,7 @@ #import "NYT360CameraPanGestureRecognizer.h" static inline CGFloat distance(CGPoint a, CGPoint b) { - return sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2)); + return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); } static inline CGPoint subtractPoints(CGPoint a, CGPoint b) { @@ -97,11 +97,13 @@ - (void)updateCameraAngle { NSLog(@"Warning: %@ called while %@ is not receiving motion updates", NSStringFromSelector(_cmd), NSStringFromClass(self.class)); } #endif - + CMRotationRate rotationRate = self.motionManager.deviceMotion.rotationRate; UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + NYT360EulerAngleCalculationResult result; result = NYT360DeviceMotionCalculation(self.currentPosition, rotationRate, orientation, self.allowedPanningAxes, NYT360EulerAngleCalculationNoiseThresholdDefault); + self.currentPosition = result.position; self.pointOfView.eulerAngles = result.eulerAngles; diff --git a/Sources/NYT360DataTypes.h b/Sources/NYT360DataTypes.h index f155cb1..f03d670 100644 --- a/Sources/NYT360DataTypes.h +++ b/Sources/NYT360DataTypes.h @@ -10,7 +10,7 @@ typedef NS_OPTIONS(NSInteger, NYT360PanningAxis) { NYT360PanningAxisHorizontal = 1 << 0, - NYT360PanningAxisVertical = 1 << 1 + NYT360PanningAxisVertical = 1 << 1, }; typedef NS_ENUM(NSInteger, NYT360UserInteractionMethod) { From fc9597db09e318c49c53960f452c608af0c8dc7c Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Thu, 25 Aug 2016 15:34:04 -0500 Subject: [PATCH 15/18] Update pragma mark. --- Sources/NYT360CameraController.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NYT360CameraController.h b/Sources/NYT360CameraController.h index b990884..279341c 100644 --- a/Sources/NYT360CameraController.h +++ b/Sources/NYT360CameraController.h @@ -46,7 +46,7 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nullable, nonatomic, weak) id delegate; -#pragma mark - Camera Angle Direction +#pragma mark - Compass Angle /** * Returns the current compass angle in radians From eb90d015bd08b2c110b34612b639b08907bac3b1 Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Thu, 25 Aug 2016 15:34:37 -0500 Subject: [PATCH 16/18] Add nullability specifier. --- Sources/NYT360CameraController.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NYT360CameraController.h b/Sources/NYT360CameraController.h index 279341c..283ebcf 100644 --- a/Sources/NYT360CameraController.h +++ b/Sources/NYT360CameraController.h @@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN * * @note This method is called synchronously from SCNSceneRendererDelegate. Its implementation should return quickly to avoid performance implications. */ -@property (nonatomic, copy) NYT360CompassAngleUpdateBlock compassAngleUpdateBlock; +@property (nonatomic, copy, nullable) NYT360CompassAngleUpdateBlock compassAngleUpdateBlock; #pragma mark - Initializers From 07feac111d4353ea3cff0828b8eee26a92c4b6a1 Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Thu, 25 Aug 2016 15:37:05 -0500 Subject: [PATCH 17/18] Remove whitespace. --- Sources/NYT360CameraController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NYT360CameraController.m b/Sources/NYT360CameraController.m index 052be62..1f44055 100644 --- a/Sources/NYT360CameraController.m +++ b/Sources/NYT360CameraController.m @@ -93,7 +93,7 @@ - (void)updateCameraAngleForCurrentDeviceMotion { // be jarring otherwise. if (self.isAnimatingReorientation) { return; } - + #ifdef DEBUG if (!self.motionManager.isDeviceMotionActive) { NSLog(@"Warning: %@ called while %@ is not receiving motion updates", NSStringFromSelector(_cmd), NSStringFromClass(self.class)); From 2fff351302177a16a3f88c06fe6ef29f1d4c9d3b Mon Sep 17 00:00:00 2001 From: Jared Sinclair Date: Thu, 25 Aug 2016 16:36:23 -0500 Subject: [PATCH 18/18] Bump version to 0.6.0. --- NYT360Video.podspec | 2 +- NYT360VideoExample/Info.plist | 2 +- NYT360VideoTests/Info.plist | 2 +- Sources/Info.plist | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/NYT360Video.podspec b/NYT360Video.podspec index b7da563..6af3534 100644 --- a/NYT360Video.podspec +++ b/NYT360Video.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NYT360Video' - s.version = '0.5.3' + s.version = '0.6.0' s.summary = 'NYT360Video plays 360ยบ video streamed from an AVPlayer.' s.description = <<-DESC diff --git a/NYT360VideoExample/Info.plist b/NYT360VideoExample/Info.plist index 19b385a..7f3057e 100644 --- a/NYT360VideoExample/Info.plist +++ b/NYT360VideoExample/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.5.3 + 0.6.0 CFBundleSignature ???? CFBundleVersion diff --git a/NYT360VideoTests/Info.plist b/NYT360VideoTests/Info.plist index 24f2e09..3b73345 100644 --- a/NYT360VideoTests/Info.plist +++ b/NYT360VideoTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.5.3 + 0.6.0 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/Info.plist b/Sources/Info.plist index c89d5f2..63c3e67 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.5.3 + 0.6.0 CFBundleSignature ???? CFBundleVersion