diff --git a/CHANGELOG.md b/CHANGELOG.md index 27ff1395..2f75f0f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix iOS bug which would break size of views when video is displayed with controls on a non full-screen React view. [#1931](https://github.com/react-native-community/react-native-video/pull/1931) - Fix video dimensions being undefined when playing HLS in ios. [#1992](https://github.com/react-native-community/react-native-video/pull/1992) +- Add support for audio mix with other apps for iOS. [#1978](https://github.com/react-native-community/react-native-video/pull/1978) ### Version 5.1.0-alpha5 diff --git a/README.md b/README.md index 07f37404..436a684f 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,7 @@ var styles = StyleSheet.create({ * [ignoreSilentSwitch](#ignoresilentswitch) * [maxBitRate](#maxbitrate) * [minLoadRetryCount](#minLoadRetryCount) +* [mixWithOthers](#mixWithOthers) * [muted](#muted) * [paused](#paused) * [pictureInPicture](#pictureinpicture) @@ -531,6 +532,14 @@ minLoadRetryCount={5} // retry 5 times Platforms: Android ExoPlayer +#### mixWithOthers +Controls how Audio mix with other apps. +* **"inherit" (default)** - Use the default AVPlayer behavior +* **"mix"** - Audio from this video mixes with audio from other apps. +* **"duck"** - Reduces the volume of other apps while audio from this video plays. + +Platforms: iOS + #### muted Controls whether the audio is muted * **false (default)** - Don't mute audio diff --git a/examples/basic/index.ios.js b/examples/basic/index.ios.js index 1bc0ac47..c4c21c3d 100644 --- a/examples/basic/index.ios.js +++ b/examples/basic/index.ios.js @@ -52,6 +52,7 @@ class VideoPlayer extends Component { paused: true, skin: 'custom', ignoreSilentSwitch: null, + mixWithOthers: null, isBuffering: false, filter: FilterType.NONE, filterEnabled: true @@ -155,6 +156,18 @@ class VideoPlayer extends Component { ) } + renderMixWithOthersControl(mixWithOthers) { + const isSelected = (this.state.mixWithOthers == mixWithOthers); + + return ( + { this.setState({mixWithOthers: mixWithOthers}) }}> + + {mixWithOthers} + + + ) + } + renderCustomSkin() { const flexCompleted = this.getCurrentTimePercentage() * 100; const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100; @@ -170,6 +183,7 @@ class VideoPlayer extends Component { volume={this.state.volume} muted={this.state.muted} ignoreSilentSwitch={this.state.ignoreSilentSwitch} + mixWithOthers={this.state.mixWithOthers} resizeMode={this.state.resizeMode} onLoad={this.onLoad} onBuffer={this.onBuffer} @@ -226,10 +240,16 @@ class VideoPlayer extends Component { { (Platform.OS === 'ios') ? - - {this.renderIgnoreSilentSwitchControl('ignore')} - {this.renderIgnoreSilentSwitchControl('obey')} - : null + <> + + {this.renderIgnoreSilentSwitchControl('ignore')} + {this.renderIgnoreSilentSwitchControl('obey')} + + + {this.renderMixWithOthersControl('mix')} + {this.renderMixWithOthersControl('duck')} + + : null } @@ -257,6 +277,7 @@ class VideoPlayer extends Component { volume={this.state.volume} muted={this.state.muted} ignoreSilentSwitch={this.state.ignoreSilentSwitch} + mixWithOthers={this.state.mixWithOthers} resizeMode={this.state.resizeMode} onLoad={this.onLoad} onBuffer={this.onBuffer} @@ -313,10 +334,16 @@ class VideoPlayer extends Component { { (Platform.OS === 'ios') ? - - {this.renderIgnoreSilentSwitchControl('ignore')} - {this.renderIgnoreSilentSwitchControl('obey')} - : null + <> + + {this.renderIgnoreSilentSwitchControl('ignore')} + {this.renderIgnoreSilentSwitchControl('obey')} + + + {this.renderMixWithOthersControl('mix')} + {this.renderMixWithOthersControl('duck')} + + : null } @@ -399,6 +426,12 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center' }, + mixWithOthersControl: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, controlOption: { alignSelf: 'center', fontSize: 11, diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m index e9c49b91..a9b8e568 100644 --- a/ios/Video/RCTVideo.m +++ b/ios/Video/RCTVideo.m @@ -67,6 +67,7 @@ static int const RCTVideoUnset = -1; BOOL _playWhenInactive; BOOL _pictureInPicture; NSString * _ignoreSilentSwitch; + NSString * _mixWithOthers; NSString * _resizeMode; BOOL _fullscreen; BOOL _fullscreenAutorotate; @@ -108,6 +109,7 @@ static int const RCTVideoUnset = -1; _playWhenInactive = false; _pictureInPicture = false; _ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey + _mixWithOthers = @"inherit"; // inherit, mix, duck #if TARGET_OS_IOS _restoreUserInterfaceForPIPStopCompletionHandler = NULL; #endif @@ -861,18 +863,42 @@ static int const RCTVideoUnset = -1; [self applyModifiers]; } +- (void)setMixWithOthers:(NSString *)mixWithOthers +{ + _mixWithOthers = mixWithOthers; + [self applyModifiers]; +} + - (void)setPaused:(BOOL)paused { if (paused) { [_player pause]; [_player setRate:0.0]; } else { + AVAudioSession *session = [AVAudioSession sharedInstance]; + AVAudioSessionCategory category = nil; + AVAudioSessionCategoryOptions options = nil; + if([_ignoreSilentSwitch isEqualToString:@"ignore"]) { - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; + category = AVAudioSessionCategoryPlayback; } else if([_ignoreSilentSwitch isEqualToString:@"obey"]) { - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; + category = AVAudioSessionCategoryAmbient; } - + + if([_mixWithOthers isEqualToString:@"mix"]) { + options = AVAudioSessionCategoryOptionMixWithOthers; + } else if([_mixWithOthers isEqualToString:@"duck"]) { + options = AVAudioSessionCategoryOptionDuckOthers; + } + + if (category != nil && options != nil) { + [session setCategory:category withOptions:options error:nil]; + } else if (category != nil && options == nil) { + [session setCategory:category error:nil]; + } else if (category == nil && options != nil) { + [session setCategory:session.category withOptions:options error:nil]; + } + if (@available(iOS 10.0, *) && !_automaticallyWaitsToMinimizeStalling) { [_player playImmediatelyAtRate:_rate]; } else { diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index 7233646f..e8bda250 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -35,6 +35,7 @@ RCT_EXPORT_VIEW_PROPERTY(playInBackground, BOOL); RCT_EXPORT_VIEW_PROPERTY(playWhenInactive, BOOL); RCT_EXPORT_VIEW_PROPERTY(pictureInPicture, BOOL); RCT_EXPORT_VIEW_PROPERTY(ignoreSilentSwitch, NSString); +RCT_EXPORT_VIEW_PROPERTY(mixWithOthers, NSString); RCT_EXPORT_VIEW_PROPERTY(rate, float); RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary); RCT_EXPORT_VIEW_PROPERTY(currentTime, float);