diff --git a/android/src/main/java/com/brentvatne/common/api/MixWithOthers.kt b/android/src/main/java/com/brentvatne/common/api/MixWithOthers.kt
new file mode 100644
index 0000000000..208c41038e
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/common/api/MixWithOthers.kt
@@ -0,0 +1,39 @@
+package com.brentvatne.common.api
+
+import androidx.annotation.IntDef
+import kotlin.annotation.Retention
+
+internal object MixWithOthers {
+ /**
+ * Either the width or height is decreased to obtain the desired aspect ratio.
+ */
+ const val MIX_INHERIT = 0
+
+ /**
+ * The width is fixed and the height is increased or decreased to obtain the desired aspect ratio.
+ */
+ const val MIX_DUCK = 1
+
+ /**
+ * The height is fixed and the width is increased or decreased to obtain the desired aspect ratio.
+ */
+ const val MIX_MIX = 2
+
+ @JvmStatic
+ @Mode
+ fun toMixWithOthers(ordinal: String): Int =
+ when (ordinal) {
+ "inherit" -> MIX_INHERIT
+ "duck" -> MIX_DUCK
+ "mix" -> MIX_MIX
+ else -> MIX_INHERIT
+ }
+
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(
+ MIX_INHERIT,
+ MIX_DUCK,
+ MIX_MIX,
+ )
+ annotation class Mode
+}
diff --git a/android/src/main/java/com/brentvatne/common/api/RNVPlayerInterface.kt b/android/src/main/java/com/brentvatne/common/api/RNVPlayerInterface.kt
new file mode 100644
index 0000000000..f65fae4504
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/common/api/RNVPlayerInterface.kt
@@ -0,0 +1,26 @@
+package com.brentvatne.common.api
+
+import com.brentvatne.common.react.VideoEventEmitter
+
+// RNVPlayerInterface is an abstraction of a player implementation
+// It allows to use player in a generic code
+interface RNVPlayerInterface {
+ // return true if playback is muted
+
+ val isMuted: Boolean
+
+ // return true if playback is ongoing and false when paused
+ val isPlaying: Boolean
+
+ // return the eventEmitter associated to the player
+ val eventEmitter: VideoEventEmitter
+
+ // pause player
+ fun pausePlayback()
+
+ // decrease audio volume internally to handle audio ducking request
+ fun audioDuck()
+
+ // decrease audio volume internally from ducking request
+ fun audioRestoreFromDuck()
+}
diff --git a/android/src/main/java/com/brentvatne/common/toolbox/AudioManagerDelegate.kt b/android/src/main/java/com/brentvatne/common/toolbox/AudioManagerDelegate.kt
new file mode 100644
index 0000000000..ba03f6af1a
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/common/toolbox/AudioManagerDelegate.kt
@@ -0,0 +1,109 @@
+package com.brentvatne.common.toolbox
+
+import android.content.Context
+import android.media.AudioManager
+import android.media.AudioManager.OnAudioFocusChangeListener
+import androidx.annotation.IntDef
+import androidx.media3.common.FileTypes.Type
+import com.brentvatne.common.api.MixWithOthers
+import com.brentvatne.common.api.RNVPlayerInterface
+import com.facebook.react.uimanager.ThemedReactContext
+
+/**
+ * Delegate audio management to this class
+ * This is an helper to group all generic android code which do not depend on player implementation
+ */
+class AudioManagerDelegate(player: RNVPlayerInterface, themedReactContext: ThemedReactContext) {
+
+ companion object {
+ const val TAG = "AudioFocusDelegate"
+ }
+
+ // indicates if audio focus shall be handled
+ var mixWithOthers: Int = MixWithOthers.MIX_INHERIT
+
+ // indicates app currently have audio focus
+ var hasAudioFocus = false
+
+ val audioManager: AudioManager = themedReactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+
+ private val audioFocusChangeListener = OnAudioFocusChangedListener(player, themedReactContext, this)
+
+ /** implementation of OnAudioFocusChangedListener
+ * It reports audio events to the app and request volume change to the player
+ **/
+ private class OnAudioFocusChangedListener(
+ private val player: RNVPlayerInterface,
+ private val themedReactContext: ThemedReactContext,
+ private val audioFocusDelegate: AudioManagerDelegate
+ ) : OnAudioFocusChangeListener {
+ override fun onAudioFocusChange(focusChange: Int) {
+ when (focusChange) {
+ AudioManager.AUDIOFOCUS_LOSS -> {
+ audioFocusDelegate.hasAudioFocus = false
+ player.eventEmitter.onAudioFocusChanged.invoke(false)
+ // FIXME this pause can cause issue if content doesn't have pause capability (can happen on live channel)
+ themedReactContext.currentActivity?.runOnUiThread(player::pausePlayback)
+ audioFocusDelegate.audioManager.abandonAudioFocus(this)
+ }
+
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> player.eventEmitter.onAudioFocusChanged.invoke(false)
+
+ AudioManager.AUDIOFOCUS_GAIN -> {
+ audioFocusDelegate.hasAudioFocus = true
+ player.eventEmitter.onAudioFocusChanged.invoke(true)
+ }
+
+ else -> {
+ DebugLog.e(TAG, "unhandled audioFocusChange $focusChange")
+ }
+ }
+ if (player.isPlaying) {
+ if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+ // Lower the volume
+ if (!player.isMuted) {
+ player.audioDuck()
+ }
+ } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ // Raise it back to normal
+ if (!player.isMuted) {
+ player.audioRestoreFromDuck()
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * request audio Focus
+ */
+ fun requestAudioFocus(): Boolean {
+ if (mixWithOthers == MixWithOthers.MIX_MIX || hasAudioFocus) {
+ return true
+ }
+ val result: Int = audioManager.requestAudioFocus(
+ audioFocusChangeListener,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN
+ )
+ hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
+ return hasAudioFocus
+ }
+
+ /**
+ * Abandon audio Focus
+ */
+ fun abandonAudioFocus() {
+ audioManager.abandonAudioFocus(audioFocusChangeListener)
+ }
+
+ /**
+ * change system audio output
+ */
+ fun changeOutput(isSpeakerOutput: Boolean) {
+ audioManager.setMode(
+ if (isSpeakerOutput) AudioManager.MODE_NORMAL else AudioManager.MODE_IN_COMMUNICATION
+ )
+ audioManager.setSpeakerphoneOn(isSpeakerOutput)
+ }
+}
diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
index 8fefa34c7d..90647db86b 100644
--- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
@@ -15,7 +15,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
@@ -108,6 +107,8 @@
import com.brentvatne.common.api.BufferingStrategy;
import com.brentvatne.common.api.ControlsConfig;
import com.brentvatne.common.api.DRMProps;
+import com.brentvatne.common.api.MixWithOthers;
+import com.brentvatne.common.api.RNVPlayerInterface;
import com.brentvatne.common.api.ResizeMode;
import com.brentvatne.common.api.SideLoadedTextTrack;
import com.brentvatne.common.api.Source;
@@ -116,6 +117,7 @@
import com.brentvatne.common.api.Track;
import com.brentvatne.common.api.VideoTrack;
import com.brentvatne.common.react.VideoEventEmitter;
+import com.brentvatne.common.toolbox.AudioManagerDelegate;
import com.brentvatne.common.toolbox.DebugLog;
import com.brentvatne.common.toolbox.ReactBridgeUtils;
import com.brentvatne.react.BuildConfig;
@@ -141,7 +143,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
@@ -157,7 +158,8 @@ public class ReactExoplayerView extends FrameLayout implements
BecomingNoisyListener,
DrmSessionEventListener,
AdEvent.AdEventListener,
- AdErrorEvent.AdErrorListener {
+ AdErrorEvent.AdErrorListener,
+ RNVPlayerInterface {
public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1;
public static final double DEFAULT_MIN_BUFFER_MEMORY_RESERVE = 0;
@@ -172,7 +174,8 @@ public class ReactExoplayerView extends FrameLayout implements
DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
}
- protected final VideoEventEmitter eventEmitter;
+ public final VideoEventEmitter eventEmitter = new VideoEventEmitter();
+
private final ReactExoplayerConfig config;
private final DefaultBandwidthMeter bandwidthMeter;
private LegacyPlayerControlView playerControlView;
@@ -203,7 +206,6 @@ public class ReactExoplayerView extends FrameLayout implements
private boolean isPaused;
private boolean isBuffering;
private boolean muted = false;
- private boolean hasAudioFocus = false;
private float rate = 1f;
private AudioOutput audioOutput = AudioOutput.SPEAKER;
private float audioVolume = 1f;
@@ -234,7 +236,7 @@ public class ReactExoplayerView extends FrameLayout implements
private String videoTrackValue;
private String textTrackType = "disabled";
private String textTrackValue;
- private boolean disableFocus;
+ private int mixWithOthers = MixWithOthers.MIX_INHERIT;
private boolean focusable = true;
private BufferingStrategy.BufferingStrategyEnum bufferingStrategy;
private boolean disableDisconnectError;
@@ -249,9 +251,8 @@ public class ReactExoplayerView extends FrameLayout implements
// React
private final ThemedReactContext themedReactContext;
- private final AudioManager audioManager;
private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver;
- private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;
+ private AudioManagerDelegate audioFocusDelegate;
// store last progress event values to avoid sending unnecessary messages
private long lastPos = -1;
@@ -269,6 +270,13 @@ public void setCmcdConfigurationFactory(CmcdConfiguration.Factory factory) {
this.cmcdConfigurationFactory = factory;
}
+ public boolean isPlaying() {
+ if (player == null || !player.getPlayWhenReady()) {
+ return false;
+ }
+ return true;
+ }
+
private void updateProgress() {
if (player != null) {
if (playerControlView != null && isPlayingAd() && controls) {
@@ -314,16 +322,21 @@ public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig config) {
super(context);
this.themedReactContext = context;
- this.eventEmitter = new VideoEventEmitter();
this.config = config;
this.bandwidthMeter = config.getBandwidthMeter();
mainHandler = new Handler();
createViews();
- audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
themedReactContext.addLifecycleEventListener(this);
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
- audioFocusChangeListener = new OnAudioFocusChangedListener(this, themedReactContext);
+ }
+
+ private AudioManagerDelegate getAudioFocusDelegate() {
+ if (audioFocusDelegate == null) {
+ audioFocusDelegate = new AudioManagerDelegate(this, themedReactContext);
+ audioFocusDelegate.setMixWithOthers(mixWithOthers);
+ }
+ return audioFocusDelegate;
}
private boolean isPlayingAd() {
@@ -667,6 +680,17 @@ public void setViewType(int viewType) {
exoPlayerView.updateSurfaceView(viewType);
}
+ @NonNull
+ @Override
+ public VideoEventEmitter getEventEmitter() {
+ return eventEmitter;
+ }
+
+ @Override
+ public boolean isMuted() {
+ return muted;
+ }
+
private class RNVLoadControl extends DefaultLoadControl {
private final int availableHeapInBytes;
private final Runtime runtime;
@@ -1280,78 +1304,14 @@ private void releasePlayer() {
}
}
- private static class OnAudioFocusChangedListener implements AudioManager.OnAudioFocusChangeListener {
- private final ReactExoplayerView view;
- private final ThemedReactContext themedReactContext;
-
- private OnAudioFocusChangedListener(ReactExoplayerView view, ThemedReactContext themedReactContext) {
- this.view = view;
- this.themedReactContext = themedReactContext;
- }
-
- @Override
- public void onAudioFocusChange(int focusChange) {
- Activity activity = themedReactContext.getCurrentActivity();
-
- switch (focusChange) {
- case AudioManager.AUDIOFOCUS_LOSS:
- view.hasAudioFocus = false;
- view.eventEmitter.onAudioFocusChanged.invoke(false);
- // FIXME this pause can cause issue if content doesn't have pause capability (can happen on live channel)
- if (activity != null) {
- activity.runOnUiThread(view::pausePlayback);
- }
- view.audioManager.abandonAudioFocus(this);
- break;
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
- view.eventEmitter.onAudioFocusChanged.invoke(false);
- break;
- case AudioManager.AUDIOFOCUS_GAIN:
- view.hasAudioFocus = true;
- view.eventEmitter.onAudioFocusChanged.invoke(true);
- break;
- default:
- break;
- }
-
- if (view.player != null && activity != null) {
- if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
- // Lower the volume
- if (!view.muted) {
- activity.runOnUiThread(() ->
- view.player.setVolume(view.audioVolume * 0.8f)
- );
- }
- } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
- // Raise it back to normal
- if (!view.muted) {
- activity.runOnUiThread(() ->
- view.player.setVolume(view.audioVolume * 1)
- );
- }
- }
- }
- }
- }
-
- private boolean requestAudioFocus() {
- if (disableFocus || source.getUri() == null || this.hasAudioFocus) {
- return true;
- }
- int result = audioManager.requestAudioFocus(audioFocusChangeListener,
- AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN);
- return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
- }
-
private void setPlayWhenReady(boolean playWhenReady) {
if (player == null) {
return;
}
if (playWhenReady) {
- this.hasAudioFocus = requestAudioFocus();
- if (this.hasAudioFocus) {
+ boolean hasAudioGranted = getAudioFocusDelegate().requestAudioFocus();
+ if (hasAudioGranted) {
player.setPlayWhenReady(true);
}
} else {
@@ -1368,7 +1328,7 @@ private void resumePlayback() {
}
}
- private void pausePlayback() {
+ public void pausePlayback() {
if (player != null) {
if (player.getPlayWhenReady()) {
setPlayWhenReady(false);
@@ -1383,7 +1343,7 @@ private void stopPlayback() {
}
private void onStopPlayback() {
- audioManager.abandonAudioFocus(audioFocusChangeListener);
+ getAudioFocusDelegate().abandonAudioFocus();
}
private void updateResumePosition() {
@@ -2224,12 +2184,8 @@ private void changeAudioOutput(AudioOutput output) {
.setContentType(contentType)
.build();
player.setAudioAttributes(audioAttributes, false);
- AudioManager audioManager = (AudioManager) themedReactContext.getSystemService(Context.AUDIO_SERVICE);
boolean isSpeakerOutput = output == AudioOutput.SPEAKER;
- audioManager.setMode(
- isSpeakerOutput ? AudioManager.MODE_NORMAL
- : AudioManager.MODE_IN_COMMUNICATION);
- audioManager.setSpeakerphoneOn(isSpeakerOutput);
+ getAudioFocusDelegate().changeOutput(isSpeakerOutput);
}
}
@@ -2242,7 +2198,7 @@ public void setAudioOutput(AudioOutput output) {
public void setVolumeModifier(float volume) {
audioVolume = volume;
- if (player != null) {
+ if (player != null && !muted) {
player.setVolume(audioVolume);
}
}
@@ -2286,8 +2242,13 @@ public void setPlayInBackground(boolean playInBackground) {
this.playInBackground = playInBackground;
}
- public void setDisableFocus(boolean disableFocus) {
- this.disableFocus = disableFocus;
+ public void setMixWithOthers(int _mixWithOthers) {
+ mixWithOthers = _mixWithOthers;
+ // do not use getAudioFocusDelegate()
+ // should not be created here as we are not sure the playback is really required
+ if (audioFocusDelegate != null) {
+ audioFocusDelegate.setMixWithOthers(mixWithOthers);
+ }
}
public void setFocusable(boolean focusable) {
@@ -2470,4 +2431,20 @@ public void setControlsStyles(ControlsConfig controlsStyles) {
controlsConfig = controlsStyles;
refreshControlsStyles();
}
+
+ public void audioDuck() {
+ if (themedReactContext.getCurrentActivity() != null && !muted) {
+ themedReactContext.getCurrentActivity().runOnUiThread(() -> player.setVolume(audioVolume * 0.8f));
+ }
+ }
+
+ public void audioRestoreFromDuck() {
+ if (themedReactContext.getCurrentActivity() != null) {
+ themedReactContext.getCurrentActivity().runOnUiThread(() -> {
+ if (!muted) {
+ player.setVolume(audioVolume);
+ }
+ });
+ }
+ }
}
diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt
index 967da9ff20..feccf0ebed 100644
--- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.kt
@@ -5,6 +5,7 @@ import android.util.Log
import com.brentvatne.common.api.BufferConfig
import com.brentvatne.common.api.BufferingStrategy
import com.brentvatne.common.api.ControlsConfig
+import com.brentvatne.common.api.MixWithOthers
import com.brentvatne.common.api.ResizeMode
import com.brentvatne.common.api.Source
import com.brentvatne.common.api.SubtitleStyle
@@ -45,7 +46,7 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
private const val PROP_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount"
private const val PROP_MAXIMUM_BIT_RATE = "maxBitRate"
private const val PROP_PLAY_IN_BACKGROUND = "playInBackground"
- private const val PROP_DISABLE_FOCUS = "disableFocus"
+ private const val PROP_DISABLE_MIX_WITH_OTHERS = "mixWithOthers"
private const val PROP_BUFFERING_STRATEGY = "bufferingStrategy"
private const val PROP_DISABLE_DISCONNECT_ERROR = "disableDisconnectError"
private const val PROP_FOCUSABLE = "focusable"
@@ -197,9 +198,9 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
videoView.setPlayInBackground(playInBackground)
}
- @ReactProp(name = PROP_DISABLE_FOCUS, defaultBoolean = false)
- fun setDisableFocus(videoView: ReactExoplayerView, disableFocus: Boolean) {
- videoView.setDisableFocus(disableFocus)
+ @ReactProp(name = PROP_DISABLE_MIX_WITH_OTHERS)
+ fun setMixWithOthers(videoView: ReactExoplayerView, disableFocus: String) {
+ videoView.setMixWithOthers(MixWithOthers.toMixWithOthers(disableFocus))
}
@ReactProp(name = PROP_FOCUSABLE, defaultBoolean = true)
diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx
index 3d2549a23e..74c2f782d7 100644
--- a/docs/pages/component/props.mdx
+++ b/docs/pages/component/props.mdx
@@ -220,12 +220,15 @@ debug={{
### `disableFocus`
+> [!WARNING]
+> Deprecated, use mixWithOthers instead
+
Determines whether video audio should override background music/audio in Android devices.
-- **false (default)** - Override background audio/music
-- **true** - Let background audio/music from other apps play
+- **false (default)** - Override background audio/music. Equivalent to mixWithOthers={'inherit'}
+- **true** - Let background audio/music from other apps play. equivalent to mixWithOthers={'duck'}
Note: Allows multiple videos to play if set to `true`. If `false`, when one video is playing and another is started, the first video will be paused.
@@ -394,11 +397,11 @@ minLoadRetryCount={5} // retry 5 times
### `mixWithOthers`
-
+
Controls how Audio mix with other apps.
-- **"inherit" (default)** - Use the default AVPlayer behavior
+- **"inherit" (default)** - Use the default player 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.
diff --git a/src/Video.tsx b/src/Video.tsx
index 8bdd92ed17..13a214ce30 100644
--- a/src/Video.tsx
+++ b/src/Video.tsx
@@ -46,7 +46,7 @@ import {
} from './utils';
import NativeVideoManager from './specs/NativeVideoManager';
import type {VideoSaveData} from './specs/NativeVideoManager';
-import {CmcdMode, ViewType} from './types';
+import {CmcdMode, MixWithOthersType, ViewType} from './types';
import type {
OnLoadData,
OnTextTracksData,
@@ -89,6 +89,8 @@ const Video = forwardRef(
selectedTextTrack,
useTextureView,
useSecureView,
+ disableFocus,
+ mixWithOthers,
viewType,
shutterColor,
adTagUrl,
@@ -784,6 +786,14 @@ const Video = forwardRef(
}),
[showPoster],
);
+ if (disableFocus != undefined && mixWithOthers) {
+ console.warn('disableFocus is deprecated, please use only mixWithOthers');
+ }
+ const _mixWithOthers = mixWithOthers
+ ? mixWithOthers
+ : disableFocus
+ ? MixWithOthersType.DUCK
+ : MixWithOthersType.INHERIT;
return (
@@ -800,6 +810,7 @@ const Video = forwardRef(
selectedAudioTrack={_selectedAudioTrack}
selectedVideoTrack={_selectedVideoTrack}
shutterColor={_shutterColor}
+ mixWithOthers={_mixWithOthers}
onGetLicense={usingExternalGetLicense ? onGetLicense : undefined}
onVideoLoad={
onLoad || hasPoster
diff --git a/src/specs/VideoNativeComponent.ts b/src/specs/VideoNativeComponent.ts
index c87af6835b..ecfc86399b 100644
--- a/src/specs/VideoNativeComponent.ts
+++ b/src/specs/VideoNativeComponent.ts
@@ -326,7 +326,6 @@ export type OnControlsVisibilityChange = Readonly<{
export interface VideoNativeProps extends ViewProps {
src?: VideoSrc;
allowsExternalPlayback?: boolean; // ios, true
- disableFocus?: boolean; // android
maxBitRate?: Float;
resizeMode?: WithDefault;
repeat?: boolean;
diff --git a/src/types/video.ts b/src/types/video.ts
index ff965bc53a..f10d407995 100644
--- a/src/types/video.ts
+++ b/src/types/video.ts
@@ -295,6 +295,7 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
contentStartTime?: number; // Android
controls?: boolean;
currentPlaybackTime?: number; // Android
+ /** @deprecated Use mixWithOthers */
disableFocus?: boolean;
disableDisconnectError?: boolean; // Android
filter?: EnumValues; // iOS
@@ -307,7 +308,7 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
ignoreSilentSwitch?: EnumValues; // iOS
minLoadRetryCount?: number; // Android
maxBitRate?: number;
- mixWithOthers?: EnumValues; // iOS
+ mixWithOthers?: EnumValues; // iOS, android
muted?: boolean;
paused?: boolean;
pictureInPicture?: boolean; // iOS