Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio mix with other apps for iOS #1978

Merged
merged 5 commits into from
May 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ var styles = StyleSheet.create({
* [ignoreSilentSwitch](#ignoresilentswitch)
* [maxBitRate](#maxbitrate)
* [minLoadRetryCount](#minLoadRetryCount)
* [mixWithOthers](#mixWithOthers)
* [muted](#muted)
* [paused](#paused)
* [pictureInPicture](#pictureinpicture)
Expand Down Expand Up @@ -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
Expand Down
49 changes: 41 additions & 8 deletions examples/basic/index.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class VideoPlayer extends Component {
paused: true,
skin: 'custom',
ignoreSilentSwitch: null,
mixWithOthers: null,
isBuffering: false,
filter: FilterType.NONE,
filterEnabled: true
Expand Down Expand Up @@ -155,6 +156,18 @@ class VideoPlayer extends Component {
)
}

renderMixWithOthersControl(mixWithOthers) {
const isSelected = (this.state.mixWithOthers == mixWithOthers);

return (
<TouchableOpacity onPress={() => { this.setState({mixWithOthers: mixWithOthers}) }}>
<Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
{mixWithOthers}
</Text>
</TouchableOpacity>
)
}

renderCustomSkin() {
const flexCompleted = this.getCurrentTimePercentage() * 100;
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
Expand All @@ -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}
Expand Down Expand Up @@ -226,10 +240,16 @@ class VideoPlayer extends Component {
<View style={styles.generalControls}>
{
(Platform.OS === 'ios') ?
<View style={styles.ignoreSilentSwitchControl}>
{this.renderIgnoreSilentSwitchControl('ignore')}
{this.renderIgnoreSilentSwitchControl('obey')}
</View> : null
<>
<View style={styles.ignoreSilentSwitchControl}>
{this.renderIgnoreSilentSwitchControl('ignore')}
{this.renderIgnoreSilentSwitchControl('obey')}
</View>
<View style={styles.mixWithOthersControl}>
{this.renderMixWithOthersControl('mix')}
{this.renderMixWithOthersControl('duck')}
</View>
</> : null
}
</View>

Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -313,10 +334,16 @@ class VideoPlayer extends Component {
<View style={styles.generalControls}>
{
(Platform.OS === 'ios') ?
<View style={styles.ignoreSilentSwitchControl}>
{this.renderIgnoreSilentSwitchControl('ignore')}
{this.renderIgnoreSilentSwitchControl('obey')}
</View> : null
<>
<View style={styles.ignoreSilentSwitchControl}>
{this.renderIgnoreSilentSwitchControl('ignore')}
{this.renderIgnoreSilentSwitchControl('obey')}
</View>
<View style={styles.mixWithOthersControl}>
{this.renderMixWithOthersControl('mix')}
{this.renderMixWithOthersControl('duck')}
</View>
</> : null
}
</View>
</View>
Expand Down Expand Up @@ -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,
Expand Down
32 changes: 29 additions & 3 deletions ios/Video/RCTVideo.m
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ @implementation RCTVideo
BOOL _playWhenInactive;
BOOL _pictureInPicture;
NSString * _ignoreSilentSwitch;
NSString * _mixWithOthers;
NSString * _resizeMode;
BOOL _fullscreen;
BOOL _fullscreenAutorotate;
Expand Down Expand Up @@ -108,6 +109,7 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
_playWhenInactive = false;
_pictureInPicture = false;
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
_mixWithOthers = @"inherit"; // inherit, mix, duck
#if TARGET_OS_IOS
_restoreUserInterfaceForPIPStopCompletionHandler = NULL;
#endif
Expand Down Expand Up @@ -861,18 +863,42 @@ - (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch
[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 {
Expand Down
1 change: 1 addition & 0 deletions ios/Video/RCTVideoManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ - (dispatch_queue_t)methodQueue
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);
Expand Down