react-native-video/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
2018-08-01 15:58:02 +02:00

953 lines
34 KiB
Java

package com.brentvatne.exoplayer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.accessibility.CaptioningManager;
import android.widget.FrameLayout;
import com.brentvatne.react.R;
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
import com.brentvatne.receiver.BecomingNoisyListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.lang.Math;
import java.util.Map;
import java.lang.Object;
import java.util.ArrayList;
import java.util.Locale;
@SuppressLint("ViewConstructor")
class ReactExoplayerView extends FrameLayout implements
LifecycleEventListener,
ExoPlayer.EventListener,
BecomingNoisyListener,
AudioManager.OnAudioFocusChangeListener,
MetadataRenderer.Output {
private static final String TAG = "ReactExoplayerView";
private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
private static final CookieManager DEFAULT_COOKIE_MANAGER;
private static final int SHOW_PROGRESS = 1;
static {
DEFAULT_COOKIE_MANAGER = new CookieManager();
DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
}
private final VideoEventEmitter eventEmitter;
private Handler mainHandler;
private ExoPlayerView exoPlayerView;
private DataSource.Factory mediaDataSourceFactory;
private SimpleExoPlayer player;
private MappingTrackSelector trackSelector;
private boolean playerNeedsSource;
private int resumeWindow;
private long resumePosition;
private boolean loadVideoStarted;
private boolean isFullscreen;
private boolean isInBackground;
private boolean isPaused;
private boolean isBuffering;
private float rate = 1f;
private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
// Props from React
private Uri srcUri;
private String extension;
private boolean repeat;
private String audioTrackType;
private Dynamic audioTrackValue;
private ReadableArray audioTracks;
private String textTrackType;
private Dynamic textTrackValue;
private ReadableArray textTracks;
private boolean disableFocus;
private float mProgressUpdateInterval = 250.0f;
private boolean playInBackground = false;
private boolean useTextureView = false;
private Map<String, String> requestHeaders;
// \ End props
// React
private final ThemedReactContext themedReactContext;
private final AudioManager audioManager;
private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver;
private final Handler progressHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PROGRESS:
if (player != null
&& player.getPlaybackState() == ExoPlayer.STATE_READY
&& player.getPlayWhenReady()
) {
long pos = player.getCurrentPosition();
long bufferedDuration = player.getBufferedPercentage() * player.getDuration() / 100;
eventEmitter.progressChanged(pos, bufferedDuration, player.getDuration());
msg = obtainMessage(SHOW_PROGRESS);
sendMessageDelayed(msg, Math.round(mProgressUpdateInterval));
}
break;
}
}
};
public ReactExoplayerView(ThemedReactContext context) {
super(context);
this.themedReactContext = context;
createViews();
this.eventEmitter = new VideoEventEmitter(context);
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
themedReactContext.addLifecycleEventListener(this);
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
initializePlayer();
}
@Override
public void setId(int id) {
super.setId(id);
eventEmitter.setViewId(id);
}
private void createViews() {
clearResumePosition();
mediaDataSourceFactory = buildDataSourceFactory(true);
mainHandler = new Handler();
if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
}
LayoutParams layoutParams = new LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
exoPlayerView = new ExoPlayerView(getContext());
exoPlayerView.setLayoutParams(layoutParams);
addView(exoPlayerView, 0, layoutParams);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
initializePlayer();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopPlayback();
}
// LifecycleEventListener implementation
@Override
public void onHostResume() {
if (!playInBackground || !isInBackground) {
setPlayWhenReady(!isPaused);
}
isInBackground = false;
}
@Override
public void onHostPause() {
isInBackground = true;
if (playInBackground) {
return;
}
setPlayWhenReady(false);
}
@Override
public void onHostDestroy() {
stopPlayback();
}
public void cleanUpResources() {
stopPlayback();
}
// Internal methods
private void initializePlayer() {
if (player == null) {
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
DefaultLoadControl defaultLoadControl = new DefaultLoadControl(allocator, minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, -1, true);
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, defaultLoadControl);
player.addListener(this);
player.setMetadataOutput(this);
exoPlayerView.setPlayer(player);
audioBecomingNoisyReceiver.setListener(this);
setPlayWhenReady(!isPaused);
playerNeedsSource = true;
PlaybackParameters params = new PlaybackParameters(rate, 1f);
player.setPlaybackParameters(params);
}
if (playerNeedsSource && srcUri != null) {
ArrayList<MediaSource> mediaSourceList = buildTextSources();
MediaSource videoSource = buildMediaSource(srcUri, extension);
MediaSource mediaSource;
if (mediaSourceList.size() == 0) {
mediaSource = videoSource;
} else {
mediaSourceList.add(0, videoSource);
MediaSource[] textSourceArray = mediaSourceList.toArray(
new MediaSource[mediaSourceList.size()]
);
mediaSource = new MergingMediaSource(textSourceArray);
}
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
if (haveResumePosition) {
player.seekTo(resumeWindow, resumePosition);
}
player.prepare(mediaSource, !haveResumePosition, false);
playerNeedsSource = false;
eventEmitter.loadStart();
loadVideoStarted = true;
}
}
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
: uri.getLastPathSegment());
switch (type) {
case C.TYPE_SS:
return new SsMediaSource(uri, buildDataSourceFactory(false),
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, null);
case C.TYPE_DASH:
return new DashMediaSource(uri, buildDataSourceFactory(false),
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, null);
case C.TYPE_HLS:
return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, null);
case C.TYPE_OTHER:
return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(),
mainHandler, null);
default: {
throw new IllegalStateException("Unsupported type: " + type);
}
}
}
private ArrayList<MediaSource> buildTextSources() {
ArrayList<MediaSource> textSources = new ArrayList<>();
if (textTracks == null) {
return textSources;
}
for (int i = 0; i < textTracks.size(); ++i) {
ReadableMap textTrack = textTracks.getMap(i);
String language = textTrack.getString("language");
String title = textTrack.hasKey("title")
? textTrack.getString("title") : language + " " + i;
Uri uri = Uri.parse(textTrack.getString("uri"));
MediaSource textSource = buildTextSource(title, uri, textTrack.getString("type"),
language);
if (textSource != null) {
textSources.add(textSource);
}
}
return textSources;
}
private MediaSource buildTextSource(String title, Uri uri, String mimeType, String language) {
Format textFormat = Format.createTextSampleFormat(title, mimeType, Format.NO_VALUE, language);
return new SingleSampleMediaSource(uri, mediaDataSourceFactory, textFormat, C.TIME_UNSET);
}
private void releasePlayer() {
if (player != null) {
isPaused = player.getPlayWhenReady();
updateResumePosition();
player.release();
player.setMetadataOutput(null);
player = null;
trackSelector = null;
}
progressHandler.removeMessages(SHOW_PROGRESS);
themedReactContext.removeLifecycleEventListener(this);
audioBecomingNoisyReceiver.removeListener();
}
private boolean requestAudioFocus() {
if (disableFocus) {
return true;
}
int result = audioManager.requestAudioFocus(this,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
private void setPlayWhenReady(boolean playWhenReady) {
if (player == null) {
return;
}
if (playWhenReady) {
boolean hasAudioFocus = requestAudioFocus();
if (hasAudioFocus) {
player.setPlayWhenReady(true);
}
} else {
player.setPlayWhenReady(false);
}
}
private void startPlayback() {
if (player != null) {
switch (player.getPlaybackState()) {
case ExoPlayer.STATE_IDLE:
case ExoPlayer.STATE_ENDED:
initializePlayer();
break;
case ExoPlayer.STATE_BUFFERING:
case ExoPlayer.STATE_READY:
if (!player.getPlayWhenReady()) {
setPlayWhenReady(true);
}
break;
default:
break;
}
} else {
initializePlayer();
}
if (!disableFocus) {
setKeepScreenOn(true);
}
}
private void pausePlayback() {
if (player != null) {
if (player.getPlayWhenReady()) {
setPlayWhenReady(false);
}
}
setKeepScreenOn(false);
}
private void stopPlayback() {
onStopPlayback();
releasePlayer();
}
private void onStopPlayback() {
if (isFullscreen) {
setFullscreen(false);
}
setKeepScreenOn(false);
audioManager.abandonAudioFocus(this);
}
private void updateResumePosition() {
resumeWindow = player.getCurrentWindowIndex();
resumePosition = player.isCurrentWindowSeekable() ? Math.max(0, player.getCurrentPosition())
: C.TIME_UNSET;
}
private void clearResumePosition() {
resumeWindow = C.INDEX_UNSET;
resumePosition = C.TIME_UNSET;
}
/**
* Returns a new DataSource factory.
*
* @param useBandwidthMeter Whether to set {@link #BANDWIDTH_METER} as a listener to the new
* DataSource factory.
* @return A new DataSource factory.
*/
private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, useBandwidthMeter ? BANDWIDTH_METER : null, requestHeaders);
}
// AudioManager.OnAudioFocusChangeListener implementation
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS:
eventEmitter.audioFocusChanged(false);
break;
case AudioManager.AUDIOFOCUS_GAIN:
eventEmitter.audioFocusChanged(true);
break;
default:
break;
}
if (player != null) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// Lower the volume
player.setVolume(0.8f);
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Raise it back to normal
player.setVolume(1);
}
}
}
// AudioBecomingNoisyListener implementation
@Override
public void onAudioBecomingNoisy() {
eventEmitter.audioBecomingNoisy();
}
// ExoPlayer.EventListener implementation
@Override
public void onLoadingChanged(boolean isLoading) {
// Do nothing.
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState=";
switch (playbackState) {
case ExoPlayer.STATE_IDLE:
text += "idle";
eventEmitter.idle();
break;
case ExoPlayer.STATE_BUFFERING:
text += "buffering";
onBuffering(true);
break;
case ExoPlayer.STATE_READY:
text += "ready";
eventEmitter.ready();
onBuffering(false);
startProgressHandler();
videoLoaded();
break;
case ExoPlayer.STATE_ENDED:
text += "ended";
eventEmitter.end();
onStopPlayback();
break;
default:
text += "unknown";
break;
}
Log.d(TAG, text);
}
private void startProgressHandler() {
progressHandler.sendEmptyMessage(SHOW_PROGRESS);
}
private void videoLoaded() {
if (loadVideoStarted) {
loadVideoStarted = false;
setSelectedAudioTrack(audioTrackType, audioTrackValue);
setSelectedTextTrack(textTrackType, textTrackValue);
Format videoFormat = player.getVideoFormat();
int width = videoFormat != null ? videoFormat.width : 0;
int height = videoFormat != null ? videoFormat.height : 0;
eventEmitter.load(player.getDuration(), player.getCurrentPosition(), width, height,
getAudioTrackInfo(), getTextTrackInfo());
}
}
private WritableArray getAudioTrackInfo() {
WritableArray audioTracks = Arguments.createArray();
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
int index = getTrackRendererIndex(C.TRACK_TYPE_AUDIO);
if (info == null || index == C.INDEX_UNSET) {
return audioTracks;
}
TrackGroupArray groups = info.getTrackGroups(index);
for (int i = 0; i < groups.length; ++i) {
Format format = groups.get(i).getFormat(0);
WritableMap textTrack = Arguments.createMap();
textTrack.putInt("index", i);
textTrack.putString("title", format.id != null ? format.id : "");
textTrack.putString("type", format.sampleMimeType);
textTrack.putString("language", format.language != null ? format.language : "");
audioTracks.pushMap(textTrack);
}
return audioTracks;
}
private WritableArray getTextTrackInfo() {
WritableArray textTracks = Arguments.createArray();
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
int index = getTrackRendererIndex(C.TRACK_TYPE_TEXT);
if (info == null || index == C.INDEX_UNSET) {
return textTracks;
}
TrackGroupArray groups = info.getTrackGroups(index);
for (int i = 0; i < groups.length; ++i) {
Format format = groups.get(i).getFormat(0);
WritableMap textTrack = Arguments.createMap();
textTrack.putInt("index", i);
textTrack.putString("title", format.id != null ? format.id : "");
textTrack.putString("type", format.sampleMimeType);
textTrack.putString("language", format.language != null ? format.language : "");
textTracks.pushMap(textTrack);
}
return textTracks;
}
private void onBuffering(boolean buffering) {
if (isBuffering == buffering) {
return;
}
isBuffering = buffering;
if (buffering) {
eventEmitter.buffering(true);
} else {
eventEmitter.buffering(false);
}
}
@Override
public void onPositionDiscontinuity(int reason) {
if (playerNeedsSource) {
// This will only occur if the user has performed a seek whilst in the error state. Update the
// resume position so that if the user then retries, playback will resume from the position to
// which they seeked.
updateResumePosition();
}
// When repeat is turned on, reaching the end of the video will not cause a state change
// so we need to explicitly detect it.
if (reason == ExoPlayer.DISCONTINUITY_REASON_PERIOD_TRANSITION
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
eventEmitter.end();
}
}
@Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
// Do nothing.
}
@Override
public void onSeekProcessed() {
// Do nothing.
}
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
// Do nothing.
}
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// Do Nothing.
}
@Override
public void onPlaybackParametersChanged(PlaybackParameters params) {
eventEmitter.playbackRateChange(params.speed);
}
@Override
public void onPlayerError(ExoPlaybackException e) {
String errorString = null;
Exception ex = e;
if (e.type == ExoPlaybackException.TYPE_RENDERER) {
Exception cause = e.getRendererException();
if (cause instanceof MediaCodecRenderer.DecoderInitializationException) {
// Special case for decoder initialization failures.
MediaCodecRenderer.DecoderInitializationException decoderInitializationException =
(MediaCodecRenderer.DecoderInitializationException) cause;
if (decoderInitializationException.decoderName == null) {
if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
errorString = getResources().getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorString = getResources().getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType);
} else {
errorString = getResources().getString(R.string.error_no_decoder,
decoderInitializationException.mimeType);
}
} else {
errorString = getResources().getString(R.string.error_instantiating_decoder,
decoderInitializationException.decoderName);
}
}
}
else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
ex = e.getSourceException();
errorString = getResources().getString(R.string.unrecognized_media_format);
}
if (errorString != null) {
eventEmitter.error(errorString, ex);
}
playerNeedsSource = true;
if (isBehindLiveWindow(e)) {
clearResumePosition();
initializePlayer();
} else {
updateResumePosition();
}
}
private static boolean isBehindLiveWindow(ExoPlaybackException e) {
if (e.type != ExoPlaybackException.TYPE_SOURCE) {
return false;
}
Throwable cause = e.getSourceException();
while (cause != null) {
if (cause instanceof BehindLiveWindowException) {
return true;
}
cause = cause.getCause();
}
return false;
}
public int getTrackRendererIndex(int trackType) {
int rendererCount = player.getRendererCount();
for (int rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) {
if (player.getRendererType(rendererIndex) == trackType) {
return rendererIndex;
}
}
return C.INDEX_UNSET;
}
@Override
public void onMetadata(Metadata metadata) {
eventEmitter.timedMetadata(metadata);
}
// ReactExoplayerViewManager public api
public void setSrc(final Uri uri, final String extension, Map<String, String> headers) {
if (uri != null) {
boolean isOriginalSourceNull = srcUri == null;
boolean isSourceEqual = uri.equals(srcUri);
this.srcUri = uri;
this.extension = extension;
this.requestHeaders = headers;
this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, BANDWIDTH_METER, this.requestHeaders);
if (!isOriginalSourceNull && !isSourceEqual) {
reloadSource();
}
}
}
public void setProgressUpdateInterval(final float progressUpdateInterval) {
mProgressUpdateInterval = progressUpdateInterval;
}
public void setRawSrc(final Uri uri, final String extension) {
if (uri != null) {
boolean isOriginalSourceNull = srcUri == null;
boolean isSourceEqual = uri.equals(srcUri);
this.srcUri = uri;
this.extension = extension;
this.mediaDataSourceFactory = DataSourceUtil.getRawDataSourceFactory(this.themedReactContext);
if (!isOriginalSourceNull && !isSourceEqual) {
reloadSource();
}
}
}
public void setTextTracks(ReadableArray textTracks) {
this.textTracks = textTracks;
reloadSource();
}
private void reloadSource() {
playerNeedsSource = true;
initializePlayer();
}
public void setResizeModeModifier(@ResizeMode.Mode int resizeMode) {
exoPlayerView.setResizeMode(resizeMode);
}
public void setRepeatModifier(boolean repeat) {
if (player != null) {
if (repeat) {
player.setRepeatMode(Player.REPEAT_MODE_ONE);
} else {
player.setRepeatMode(Player.REPEAT_MODE_OFF);
}
}
this.repeat = repeat;
}
public void setSelectedTrack(int trackType, String type, Dynamic value) {
int rendererIndex = getTrackRendererIndex(trackType);
if (rendererIndex == C.INDEX_UNSET) {
return;
}
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
if (info == null) {
return;
}
TrackGroupArray groups = info.getTrackGroups(rendererIndex);
int trackIndex = C.INDEX_UNSET;
if (TextUtils.isEmpty(type)) {
type = "default";
}
if (type.equals("disabled")) {
trackSelector.setSelectionOverride(rendererIndex, groups, null);
return;
} else if (type.equals("language")) {
for (int i = 0; i < groups.length; ++i) {
Format format = groups.get(i).getFormat(0);
if (format.language != null && format.language.equals(value.asString())) {
trackIndex = i;
break;
}
}
} else if (type.equals("title")) {
for (int i = 0; i < groups.length; ++i) {
Format format = groups.get(i).getFormat(0);
if (format.id != null && format.id.equals(value.asString())) {
trackIndex = i;
break;
}
}
} else if (type.equals("index")) {
if (value.asInt() < groups.length) {
trackIndex = value.asInt();
}
} else { // default
if (rendererIndex == C.TRACK_TYPE_TEXT) { // Use system settings if possible
int sdk = android.os.Build.VERSION.SDK_INT;
if (sdk > 18 && groups.length > 0) {
CaptioningManager captioningManager
= (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
if (captioningManager != null && captioningManager.isEnabled()) {
trackIndex = getTrackIndexForDefaultLocale(groups);
}
} else {
trackSelector.setSelectionOverride(rendererIndex, groups, null);
return;
}
} else if (rendererIndex == C.TRACK_TYPE_AUDIO) {
trackIndex = getTrackIndexForDefaultLocale(groups);
}
}
if (trackIndex == C.INDEX_UNSET) {
trackSelector.clearSelectionOverrides(trackIndex);
return;
}
MappingTrackSelector.SelectionOverride override
= new MappingTrackSelector.SelectionOverride(
new FixedTrackSelection.Factory(), trackIndex, 0);
trackSelector.setSelectionOverride(rendererIndex, groups, override);
}
private int getTrackIndexForDefaultLocale(TrackGroupArray groups) {
int trackIndex = 0; // default if no match
String locale2 = Locale.getDefault().getLanguage(); // 2 letter code
String locale3 = Locale.getDefault().getISO3Language(); // 3 letter code
for (int i = 0; i < groups.length; ++i) {
Format format = groups.get(i).getFormat(0);
String language = format.language;
if (language != null && (language.equals(locale2) || language.equals(locale3))) {
trackIndex = i;
break;
}
}
return trackIndex;
}
public void setSelectedAudioTrack(String type, Dynamic value) {
audioTrackType = type;
audioTrackValue = value;
setSelectedTrack(C.TRACK_TYPE_AUDIO, audioTrackType, audioTrackValue);
}
public void setSelectedTextTrack(String type, Dynamic value) {
textTrackType = type;
textTrackValue = value;
setSelectedTrack(C.TRACK_TYPE_TEXT, textTrackType, textTrackValue);
}
public void setPausedModifier(boolean paused) {
isPaused = paused;
if (player != null) {
if (!paused) {
startPlayback();
} else {
pausePlayback();
}
}
}
public void setMutedModifier(boolean muted) {
if (player != null) {
player.setVolume(muted ? 0 : 1);
}
}
public void setVolumeModifier(float volume) {
if (player != null) {
player.setVolume(volume);
}
}
public void seekTo(long positionMs) {
if (player != null) {
eventEmitter.seek(player.getCurrentPosition(), positionMs);
player.seekTo(positionMs);
}
}
public void setRateModifier(float newRate) {
rate = newRate;
if (player != null) {
PlaybackParameters params = new PlaybackParameters(rate, 1f);
player.setPlaybackParameters(params);
}
}
public void setPlayInBackground(boolean playInBackground) {
this.playInBackground = playInBackground;
}
public void setDisableFocus(boolean disableFocus) {
this.disableFocus = disableFocus;
}
public void setFullscreen(boolean fullscreen) {
if (fullscreen == isFullscreen) {
return; // Avoid generating events when nothing is changing
}
isFullscreen = fullscreen;
Activity activity = themedReactContext.getCurrentActivity();
if (activity == null) {
return;
}
Window window = activity.getWindow();
View decorView = window.getDecorView();
int uiOptions;
if (isFullscreen) {
if (Util.SDK_INT >= 19) { // 4.4+
uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| SYSTEM_UI_FLAG_FULLSCREEN;
} else {
uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_FULLSCREEN;
}
eventEmitter.fullscreenWillPresent();
decorView.setSystemUiVisibility(uiOptions);
eventEmitter.fullscreenDidPresent();
} else {
uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
eventEmitter.fullscreenWillDismiss();
decorView.setSystemUiVisibility(uiOptions);
eventEmitter.fullscreenDidDismiss();
}
}
public void setUseTextureView(boolean useTextureView) {
exoPlayerView.setUseTextureView(useTextureView);
}
public void setLoadControl(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) {
player.release();
this.player = null;
this.minBufferMs = newMinBufferMs;
this.maxBufferMs = newMaxBufferMs;
this.bufferForPlaybackMs = newBufferForPlaybackMs;
this.bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs;
initializePlayer();
}
}