From 98c51f114b4224f8bf179d33cac508bc9f062196 Mon Sep 17 00:00:00 2001 From: Dom DiCicco Date: Thu, 20 Apr 2017 13:10:06 -0500 Subject: [PATCH] issue 310 - Added a new property, ignoreSilentSwitch. (#403) * issue 310 - Added a new property, ignoreSilentSwitch. When true, audio will play even when the silent switch on an iOS device is set to silent. When false, the audio will toggle with the silent switch. Sets the AVAudioSession to either playback or ambient. * Added ignoreSilentSwitch usage to example app and to readme * Changed ignoreSilentSwitch to accept two string values, ignore and obey. This accounts for the case where the user does not want to modify the audio session from a particular video instance. The user would not use the ignoreSilentSwitch property at all in that case. Also, the audio session will only be updated when the video is unpaused, instead of whenever the video component has updated props. This allows for multiple videos to be on the screen, with the most recent video unpaused (aka played) being the video that has control over the audio session. --- README.md | 2 ++ Video.js | 1 + example/index.ios.js | 40 ++++++++++++++++++++++++++++++++++++++++ ios/RCTVideo.m | 17 +++++++++++++++-- ios/RCTVideoManager.m | 1 + 5 files changed, 59 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 65acf58248..e473a766f6 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ If you would like to allow other apps to play music over your video component, a ... } ``` +Note: you can also use the `ignoreSilentSwitch` prop, shown below. #### Android @@ -138,6 +139,7 @@ using System.Collections.Generic; repeat={true} // Repeat forever. playInBackground={false} // Audio continues to play when app entering background. playWhenInactive={false} // [iOS] Video continues to play when control or notification center are shown. + ignoreSilentSwitch={"ignore"} // [iOS] ignore | obey - When 'ignore', audio will still play with the iOS hard silent switch set to silent. When 'obey', audio will toggle with the switch. When not specified, will inherit audio settings as usual. progressUpdateInterval={250.0} // [iOS] Interval to fire onProgress (default to ~250ms) onLoadStart={this.loadStart} // Callback when video starts to load onLoad={this.setDuration} // Callback when video loads diff --git a/Video.js b/Video.js index 44e55f1a5e..4d69b3331c 100644 --- a/Video.js +++ b/Video.js @@ -278,6 +278,7 @@ Video.propTypes = { rate: PropTypes.number, playInBackground: PropTypes.bool, playWhenInactive: PropTypes.bool, + ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']), disableFocus: PropTypes.bool, controls: PropTypes.bool, currentTime: PropTypes.number, diff --git a/example/index.ios.js b/example/index.ios.js index 21c56decb7..2abd3b9ff3 100644 --- a/example/index.ios.js +++ b/example/index.ios.js @@ -6,6 +6,7 @@ import React, { import { AlertIOS, AppRegistry, + Platform, StyleSheet, Text, TouchableOpacity, @@ -31,6 +32,7 @@ class VideoPlayer extends Component { controls: false, paused: true, skin: 'custom', + ignoreSilentSwitch: null, isBuffering: false, }; @@ -106,6 +108,18 @@ class VideoPlayer extends Component { ) } + renderIgnoreSilentSwitchControl(ignoreSilentSwitch) { + const isSelected = (this.state.ignoreSilentSwitch == ignoreSilentSwitch); + + return ( + { this.setState({ignoreSilentSwitch: ignoreSilentSwitch}) }}> + + {ignoreSilentSwitch} + + + ) + } + renderCustomSkin() { const flexCompleted = this.getCurrentTimePercentage() * 100; const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100; @@ -120,6 +134,7 @@ class VideoPlayer extends Component { paused={this.state.paused} volume={this.state.volume} muted={this.state.muted} + ignoreSilentSwitch={this.state.ignoreSilentSwitch} resizeMode={this.state.resizeMode} onLoad={this.onLoad} onBuffer={this.onBuffer} @@ -156,6 +171,15 @@ class VideoPlayer extends Component { {this.renderResizeModeControl('stretch')} + + { + (Platform.OS === 'ios') ? + + {this.renderIgnoreSilentSwitchControl('ignore')} + {this.renderIgnoreSilentSwitchControl('obey')} + : null + } + @@ -180,6 +204,7 @@ class VideoPlayer extends Component { paused={this.state.paused} volume={this.state.volume} muted={this.state.muted} + ignoreSilentSwitch={this.state.ignoreSilentSwitch} resizeMode={this.state.resizeMode} onLoad={this.onLoad} onBuffer={this.onBuffer} @@ -216,6 +241,15 @@ class VideoPlayer extends Component { {this.renderResizeModeControl('stretch')} + + { + (Platform.OS === 'ios') ? + + {this.renderIgnoreSilentSwitchControl('ignore')} + {this.renderIgnoreSilentSwitchControl('obey')} + : null + } + @@ -290,6 +324,12 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center' }, + ignoreSilentSwitchControl: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, controlOption: { alignSelf: 'center', fontSize: 11, diff --git a/ios/RCTVideo.m b/ios/RCTVideo.m index f261b9389a..45965a887c 100644 --- a/ios/RCTVideo.m +++ b/ios/RCTVideo.m @@ -43,6 +43,7 @@ @implementation RCTVideo BOOL _playbackStalled; BOOL _playInBackground; BOOL _playWhenInactive; + NSString * _ignoreSilentSwitch; NSString * _resizeMode; BOOL _fullscreenPlayerPresented; UIViewController * _presentingViewController; @@ -66,6 +67,7 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher _playerBufferEmpty = YES; _playInBackground = false; _playWhenInactive = false; + _ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) @@ -118,7 +120,7 @@ - (CMTimeRange)playerItemSeekableTimeRange { return [playerItem seekableTimeRanges].firstObject.CMTimeRangeValue; } - + return (kCMTimeRangeZero); } @@ -376,7 +378,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N } else orientation = @"portrait"; } - + if(self.onVideoLoad) { self.onVideoLoad(@{@"duration": [NSNumber numberWithFloat:duration], @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(_playerItem.currentTime)], @@ -497,12 +499,23 @@ - (void)setPlayWhenInactive:(BOOL)playWhenInactive _playWhenInactive = playWhenInactive; } +- (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch +{ + _ignoreSilentSwitch = ignoreSilentSwitch; + [self applyModifiers]; +} + - (void)setPaused:(BOOL)paused { if (paused) { [_player pause]; [_player setRate:0.0]; } else { + if([_ignoreSilentSwitch isEqualToString:@"ignore"]) { + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; + } else if([_ignoreSilentSwitch isEqualToString:@"obey"]) { + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; + } [_player play]; [_player setRate:_rate]; } diff --git a/ios/RCTVideoManager.m b/ios/RCTVideoManager.m index 03952ece44..a5cf85e0b6 100644 --- a/ios/RCTVideoManager.m +++ b/ios/RCTVideoManager.m @@ -28,6 +28,7 @@ - (dispatch_queue_t)methodQueue RCT_EXPORT_VIEW_PROPERTY(volume, float); RCT_EXPORT_VIEW_PROPERTY(playInBackground, BOOL); RCT_EXPORT_VIEW_PROPERTY(playWhenInactive, BOOL); +RCT_EXPORT_VIEW_PROPERTY(ignoreSilentSwitch, NSString); RCT_EXPORT_VIEW_PROPERTY(rate, float); RCT_EXPORT_VIEW_PROPERTY(seek, float); RCT_EXPORT_VIEW_PROPERTY(currentTime, float);