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);