Merge pull request #3211 from mysport12/master
Feature: audioOutput - Play over phone earpiece
This commit is contained in:
commit
0bf7f70e24
8
API.md
8
API.md
@ -294,6 +294,7 @@ var styles = StyleSheet.create({
|
|||||||
| [adTagUrl](#adTagUrl) | Android, iOS |
|
| [adTagUrl](#adTagUrl) | Android, iOS |
|
||||||
| [allowsExternalPlayback](#allowsexternalplayback) | iOS |
|
| [allowsExternalPlayback](#allowsexternalplayback) | iOS |
|
||||||
| [audioOnly](#audioonly) | All |
|
| [audioOnly](#audioonly) | All |
|
||||||
|
| [audioOutput](#audioOutput) | Android, iOS |
|
||||||
| [automaticallyWaitsToMinimizeStalling](#automaticallyWaitsToMinimizeStalling) | iOS |
|
| [automaticallyWaitsToMinimizeStalling](#automaticallyWaitsToMinimizeStalling) | iOS |
|
||||||
| [backBufferDurationMs](#backBufferDurationMs) | Android |
|
| [backBufferDurationMs](#backBufferDurationMs) | Android |
|
||||||
| [bufferConfig](#bufferconfig) | Android |
|
| [bufferConfig](#bufferconfig) | Android |
|
||||||
@ -418,6 +419,13 @@ For this to work, the poster prop must be set.
|
|||||||
|
|
||||||
Platforms: all
|
Platforms: all
|
||||||
|
|
||||||
|
#### audioOutput
|
||||||
|
Changes the audio output.
|
||||||
|
* **speaker (default)** - plays through speaker
|
||||||
|
* **earpiece** - plays through earpiece
|
||||||
|
|
||||||
|
Platforms: Android, iOS
|
||||||
|
|
||||||
#### automaticallyWaitsToMinimizeStalling
|
#### automaticallyWaitsToMinimizeStalling
|
||||||
A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling. For clients linked against iOS 10.0 and later
|
A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling. For clients linked against iOS 10.0 and later
|
||||||
* **false** - Immediately starts playback
|
* **false** - Immediately starts playback
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
- Feature: playing audio over earpiece [#2887](https://github.com/react-native-video/react-native-video/issues/2887)
|
||||||
|
|
||||||
### Version 6.0.0-alpha.7
|
### Version 6.0.0-alpha.7
|
||||||
- All: clean JS warnings (https://github.com/react-native-video/react-native-video/pull/3183)
|
- All: clean JS warnings (https://github.com/react-native-video/react-native-video/pull/3183)
|
||||||
- Android: Add shutterView color configurtion (https://github.com/react-native-video/react-native-video/pull/3179)
|
- Android: Add shutterView color configurtion (https://github.com/react-native-video/react-native-video/pull/3179)
|
||||||
|
1
Video.js
1
Video.js
@ -515,6 +515,7 @@ Video.propTypes = {
|
|||||||
disableBuffering: PropTypes.bool,
|
disableBuffering: PropTypes.bool,
|
||||||
controls: PropTypes.bool,
|
controls: PropTypes.bool,
|
||||||
audioOnly: PropTypes.bool,
|
audioOnly: PropTypes.bool,
|
||||||
|
audioOutput: PropTypes.oneOf(['earpiece', 'speaker']),
|
||||||
fullscreenAutorotate: PropTypes.bool,
|
fullscreenAutorotate: PropTypes.bool,
|
||||||
fullscreenOrientation: PropTypes.oneOf(['all', 'landscape', 'portrait']),
|
fullscreenOrientation: PropTypes.oneOf(['all', 'landscape', 'portrait']),
|
||||||
progressUpdateInterval: PropTypes.number,
|
progressUpdateInterval: PropTypes.number,
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
|
public enum AudioOutput {
|
||||||
|
SPEAKER("speaker", C.STREAM_TYPE_MUSIC),
|
||||||
|
EARPIECE("earpiece", C.STREAM_TYPE_VOICE_CALL);
|
||||||
|
|
||||||
|
private final int streamType;
|
||||||
|
private final String mName;
|
||||||
|
|
||||||
|
AudioOutput(final String name, int stream) {
|
||||||
|
mName = name;
|
||||||
|
streamType = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AudioOutput get(String name) {
|
||||||
|
for (AudioOutput d : values()) {
|
||||||
|
if (d.mName.equalsIgnoreCase(name))
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
return SPEAKER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(" + this.mName + ", " + streamType + ")";
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ import androidx.activity.OnBackPressedCallback;
|
|||||||
|
|
||||||
import com.brentvatne.common.Track;
|
import com.brentvatne.common.Track;
|
||||||
import com.brentvatne.common.VideoTrack;
|
import com.brentvatne.common.VideoTrack;
|
||||||
|
import com.brentvatne.exoplayer.AudioOutput;
|
||||||
import com.brentvatne.react.R;
|
import com.brentvatne.react.R;
|
||||||
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
|
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
|
||||||
import com.brentvatne.receiver.BecomingNoisyListener;
|
import com.brentvatne.receiver.BecomingNoisyListener;
|
||||||
@ -48,6 +49,7 @@ import com.google.android.exoplayer2.Player;
|
|||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.Tracks;
|
import com.google.android.exoplayer2.Tracks;
|
||||||
|
import com.google.android.exoplayer2.audio.AudioAttributes;
|
||||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
|
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
|
||||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManagerProvider;
|
import com.google.android.exoplayer2.drm.DefaultDrmSessionManagerProvider;
|
||||||
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
|
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
|
||||||
@ -162,6 +164,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
private boolean muted = false;
|
private boolean muted = false;
|
||||||
private boolean hasAudioFocus = false;
|
private boolean hasAudioFocus = false;
|
||||||
private float rate = 1f;
|
private float rate = 1f;
|
||||||
|
private AudioOutput audioOutput = AudioOutput.SPEAKER;
|
||||||
private float audioVolume = 1f;
|
private float audioVolume = 1f;
|
||||||
private int minLoadRetryCount = 3;
|
private int minLoadRetryCount = 3;
|
||||||
private int maxBitRate = 0;
|
private int maxBitRate = 0;
|
||||||
@ -653,6 +656,7 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
PlaybackParameters params = new PlaybackParameters(rate, 1f);
|
PlaybackParameters params = new PlaybackParameters(rate, 1f);
|
||||||
player.setPlaybackParameters(params);
|
player.setPlaybackParameters(params);
|
||||||
|
changeAudioOutput(this.audioOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) {
|
private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) {
|
||||||
@ -1819,6 +1823,29 @@ class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void changeAudioOutput(AudioOutput output) {
|
||||||
|
if (player != null) {
|
||||||
|
int usage = Util.getAudioUsageForStreamType(audioOutput.streamType);
|
||||||
|
int contentType = Util.getAudioContentTypeForStreamType(audioOutput.streamType);
|
||||||
|
AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(usage)
|
||||||
|
.setContentType(contentType)
|
||||||
|
.build();
|
||||||
|
player.setAudioAttributes(audioAttributes, false);
|
||||||
|
AudioManager audioManager = (AudioManager) themedReactContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
audioManager.setMode(
|
||||||
|
audioOutput == AudioOutput.SPEAKER ? AudioManager.MODE_NORMAL
|
||||||
|
: AudioManager.MODE_IN_COMMUNICATION);
|
||||||
|
audioManager.setSpeakerphoneOn(audioOutput == AudioOutput.SPEAKER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAudioOutput(AudioOutput output) {
|
||||||
|
if (audioOutput != output) {
|
||||||
|
this.audioOutput = output;
|
||||||
|
changeAudioOutput(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setVolumeModifier(float volume) {
|
public void setVolumeModifier(float volume) {
|
||||||
audioVolume = volume;
|
audioVolume = volume;
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
|
@ -49,6 +49,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
private static final String PROP_TEXT_TRACKS = "textTracks";
|
private static final String PROP_TEXT_TRACKS = "textTracks";
|
||||||
private static final String PROP_PAUSED = "paused";
|
private static final String PROP_PAUSED = "paused";
|
||||||
private static final String PROP_MUTED = "muted";
|
private static final String PROP_MUTED = "muted";
|
||||||
|
private static final String PROP_AUDIO_OUTPUT = "audioOutput";
|
||||||
private static final String PROP_VOLUME = "volume";
|
private static final String PROP_VOLUME = "volume";
|
||||||
private static final String PROP_BACK_BUFFER_DURATION_MS = "backBufferDurationMs";
|
private static final String PROP_BACK_BUFFER_DURATION_MS = "backBufferDurationMs";
|
||||||
private static final String PROP_BUFFER_CONFIG = "bufferConfig";
|
private static final String PROP_BUFFER_CONFIG = "bufferConfig";
|
||||||
@ -277,6 +278,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setMutedModifier(muted);
|
videoView.setMutedModifier(muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = PROP_AUDIO_OUTPUT)
|
||||||
|
public void setAudioOutput(final ReactExoplayerView videoView, final String audioOutput) {
|
||||||
|
videoView.setAudioOutput(ReactExoplayerView.AudioOutput.get(audioOutput));
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = PROP_VOLUME, defaultFloat = 1.0f)
|
@ReactProp(name = PROP_VOLUME, defaultFloat = 1.0f)
|
||||||
public void setVolume(final ReactExoplayerView videoView, final float volume) {
|
public void setVolume(final ReactExoplayerView videoView, final float volume) {
|
||||||
videoView.setVolumeModifier(volume);
|
videoView.setVolumeModifier(volume);
|
||||||
|
@ -197,7 +197,7 @@ enum RCTPlayerOperations {
|
|||||||
var options:AVAudioSession.CategoryOptions? = nil
|
var options:AVAudioSession.CategoryOptions? = nil
|
||||||
|
|
||||||
if (ignoreSilentSwitch == "ignore") {
|
if (ignoreSilentSwitch == "ignore") {
|
||||||
category = AVAudioSession.Category.playback
|
category = AVAudioSession.Category.playAndRecord
|
||||||
} else if (ignoreSilentSwitch == "obey") {
|
} else if (ignoreSilentSwitch == "obey") {
|
||||||
category = AVAudioSession.Category.ambient
|
category = AVAudioSession.Category.ambient
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
private var _controls:Bool = false
|
private var _controls:Bool = false
|
||||||
|
|
||||||
/* Keep track of any modifiers, need to be applied after each play */
|
/* Keep track of any modifiers, need to be applied after each play */
|
||||||
|
private var _audioOutput: String = "speaker"
|
||||||
private var _volume:Float = 1.0
|
private var _volume:Float = 1.0
|
||||||
private var _rate:Float = 1.0
|
private var _rate:Float = 1.0
|
||||||
private var _maxBitRate:Float?
|
private var _maxBitRate:Float?
|
||||||
@ -517,6 +518,20 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
applyModifiers()
|
applyModifiers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func setAudioOutput(_ audioOutput:String) {
|
||||||
|
_audioOutput = audioOutput
|
||||||
|
do {
|
||||||
|
if audioOutput == "speaker" {
|
||||||
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
|
||||||
|
} else if audioOutput == "earpiece" {
|
||||||
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.none)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("Error occurred: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
func setVolume(_ volume:Float) {
|
func setVolume(_ volume:Float) {
|
||||||
_volume = volume
|
_volume = volume
|
||||||
@ -587,6 +602,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
setMaxBitRate(_maxBitRate)
|
setMaxBitRate(_maxBitRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAudioOutput(_audioOutput)
|
||||||
setSelectedAudioTrack(_selectedAudioTrackCriteria)
|
setSelectedAudioTrack(_selectedAudioTrackCriteria)
|
||||||
setSelectedTextTrack(_selectedTextTrackCriteria)
|
setSelectedTextTrack(_selectedTextTrackCriteria)
|
||||||
setResizeMode(_resizeMode)
|
setResizeMode(_resizeMode)
|
||||||
|
@ -17,6 +17,7 @@ RCT_EXPORT_VIEW_PROPERTY(selectedAudioTrack, NSDictionary);
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(paused, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(paused, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(muted, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(muted, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(controls, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(controls, BOOL);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(audioOutput, NSString);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(volume, float);
|
RCT_EXPORT_VIEW_PROPERTY(volume, float);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(playInBackground, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(playInBackground, BOOL);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(preventsDisplaySleepDuringVideoPlayback, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(preventsDisplaySleepDuringVideoPlayback, BOOL);
|
||||||
|
Loading…
Reference in New Issue
Block a user