2017-01-11 05:51:45 -07:00
|
|
|
package com.brentvatne.exoplayer;
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint;
|
2018-05-17 16:42:44 -06:00
|
|
|
import android.app.Activity;
|
2017-01-11 05:51:45 -07:00
|
|
|
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;
|
2018-05-17 16:42:44 -06:00
|
|
|
import android.view.View;
|
|
|
|
import android.view.Window;
|
2017-01-11 05:51:45 -07:00
|
|
|
import android.widget.FrameLayout;
|
|
|
|
|
|
|
|
import com.brentvatne.react.R;
|
|
|
|
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
|
|
|
|
import com.brentvatne.receiver.BecomingNoisyListener;
|
|
|
|
import com.facebook.react.bridge.LifecycleEventListener;
|
|
|
|
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;
|
2017-06-13 16:45:12 -06:00
|
|
|
import com.google.android.exoplayer2.PlaybackParameters;
|
2017-01-11 05:51:45 -07:00
|
|
|
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;
|
2017-02-13 19:38:02 -07:00
|
|
|
import com.google.android.exoplayer2.metadata.Metadata;
|
|
|
|
import com.google.android.exoplayer2.metadata.MetadataRenderer;
|
2017-03-21 14:25:17 -06:00
|
|
|
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
2017-01-11 05:51:45 -07:00
|
|
|
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
|
|
|
import com.google.android.exoplayer2.source.LoopingMediaSource;
|
|
|
|
import com.google.android.exoplayer2.source.MediaSource;
|
|
|
|
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;
|
2017-06-13 16:45:12 -06:00
|
|
|
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
2017-01-11 05:51:45 -07:00
|
|
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
|
|
|
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.DefaultBandwidthMeter;
|
|
|
|
import com.google.android.exoplayer2.util.Util;
|
|
|
|
|
|
|
|
import java.net.CookieHandler;
|
|
|
|
import java.net.CookieManager;
|
|
|
|
import java.net.CookiePolicy;
|
2017-03-31 10:15:39 -06:00
|
|
|
import java.lang.Math;
|
2018-04-03 11:19:04 -06:00
|
|
|
import java.lang.Object;
|
2017-01-11 05:51:45 -07:00
|
|
|
|
|
|
|
@SuppressLint("ViewConstructor")
|
|
|
|
class ReactExoplayerView extends FrameLayout implements
|
|
|
|
LifecycleEventListener,
|
|
|
|
ExoPlayer.EventListener,
|
|
|
|
BecomingNoisyListener,
|
2017-02-13 19:38:02 -07:00
|
|
|
AudioManager.OnAudioFocusChangeListener,
|
2017-03-21 14:25:17 -06:00
|
|
|
MetadataRenderer.Output {
|
2017-01-11 05:51:45 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2017-03-21 14:25:17 -06:00
|
|
|
private int resumeWindow;
|
|
|
|
private long resumePosition;
|
2017-01-11 05:51:45 -07:00
|
|
|
private boolean loadVideoStarted;
|
2018-05-17 16:42:44 -06:00
|
|
|
private boolean isFullscreen;
|
2017-01-11 05:51:45 -07:00
|
|
|
private boolean isPaused = true;
|
|
|
|
private boolean isBuffering;
|
2017-06-13 16:45:12 -06:00
|
|
|
private float rate = 1f;
|
2017-01-11 05:51:45 -07:00
|
|
|
|
|
|
|
// Props from React
|
|
|
|
private Uri srcUri;
|
|
|
|
private String extension;
|
|
|
|
private boolean repeat;
|
|
|
|
private boolean disableFocus;
|
2017-03-31 10:15:39 -06:00
|
|
|
private float mProgressUpdateInterval = 250.0f;
|
2017-05-08 14:30:45 -06:00
|
|
|
private boolean playInBackground = false;
|
2017-01-11 05:51:45 -07:00
|
|
|
// \ 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();
|
2018-05-17 15:12:04 -06:00
|
|
|
long bufferedDuration = player.getBufferedPercentage() * player.getDuration();
|
|
|
|
eventEmitter.progressChanged(pos, bufferedDuration, player.getDuration());
|
2017-01-11 05:51:45 -07:00
|
|
|
msg = obtainMessage(SHOW_PROGRESS);
|
2017-03-31 10:15:39 -06:00
|
|
|
sendMessageDelayed(msg, Math.round(mProgressUpdateInterval));
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
public ReactExoplayerView(ThemedReactContext context) {
|
|
|
|
super(context);
|
|
|
|
createViews();
|
|
|
|
this.eventEmitter = new VideoEventEmitter(context);
|
|
|
|
this.themedReactContext = context;
|
|
|
|
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
|
|
|
themedReactContext.addLifecycleEventListener(this);
|
|
|
|
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
|
2017-03-21 14:25:17 -06:00
|
|
|
|
|
|
|
initializePlayer();
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setId(int id) {
|
|
|
|
super.setId(id);
|
|
|
|
eventEmitter.setViewId(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void createViews() {
|
2017-03-21 14:25:17 -06:00
|
|
|
clearResumePosition();
|
2017-01-11 05:51:45 -07:00
|
|
|
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() {
|
2017-05-08 14:30:45 -06:00
|
|
|
if (playInBackground) {
|
|
|
|
return;
|
|
|
|
}
|
2017-02-13 19:38:15 -07:00
|
|
|
setPlayWhenReady(!isPaused);
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onHostPause() {
|
2017-05-08 14:30:45 -06:00
|
|
|
if (playInBackground) {
|
|
|
|
return;
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
setPlayWhenReady(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onHostDestroy() {
|
|
|
|
stopPlayback();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void cleanUpResources() {
|
|
|
|
stopPlayback();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Internal methods
|
|
|
|
|
|
|
|
private void initializePlayer() {
|
|
|
|
if (player == null) {
|
2017-06-13 16:45:12 -06:00
|
|
|
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
|
2017-01-11 05:51:45 -07:00
|
|
|
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
|
|
|
player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, new DefaultLoadControl());
|
|
|
|
player.addListener(this);
|
2017-02-13 19:38:02 -07:00
|
|
|
player.setMetadataOutput(this);
|
2017-01-11 05:51:45 -07:00
|
|
|
exoPlayerView.setPlayer(player);
|
|
|
|
audioBecomingNoisyReceiver.setListener(this);
|
|
|
|
setPlayWhenReady(!isPaused);
|
|
|
|
playerNeedsSource = true;
|
2017-06-13 16:45:12 -06:00
|
|
|
|
|
|
|
PlaybackParameters params = new PlaybackParameters(rate, 1f);
|
|
|
|
player.setPlaybackParameters(params);
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
if (playerNeedsSource && srcUri != null) {
|
|
|
|
MediaSource mediaSource = buildMediaSource(srcUri, extension);
|
|
|
|
mediaSource = repeat ? new LoopingMediaSource(mediaSource) : mediaSource;
|
2017-03-21 14:25:17 -06:00
|
|
|
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
|
|
|
|
if (haveResumePosition) {
|
|
|
|
player.seekTo(resumeWindow, resumePosition);
|
|
|
|
}
|
|
|
|
player.prepare(mediaSource, !haveResumePosition, false);
|
2017-01-11 05:51:45 -07:00
|
|
|
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 void releasePlayer() {
|
|
|
|
if (player != null) {
|
|
|
|
isPaused = player.getPlayWhenReady();
|
2017-03-21 14:25:17 -06:00
|
|
|
updateResumePosition();
|
2017-01-11 05:51:45 -07:00
|
|
|
player.release();
|
2017-02-13 19:38:02 -07:00
|
|
|
player.setMetadataOutput(null);
|
2017-01-11 05:51:45 -07:00
|
|
|
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() {
|
2018-05-17 16:42:44 -06:00
|
|
|
if (isFullscreen) {
|
|
|
|
setFullscreen(false);
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
setKeepScreenOn(false);
|
|
|
|
audioManager.abandonAudioFocus(this);
|
|
|
|
}
|
|
|
|
|
2017-03-21 14:25:17 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
/**
|
|
|
|
* 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(getContext(), useBandwidthMeter ? BANDWIDTH_METER : null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void onBuffering(boolean buffering) {
|
|
|
|
if (isBuffering == buffering) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isBuffering = buffering;
|
|
|
|
if (buffering) {
|
|
|
|
eventEmitter.buffering(true);
|
|
|
|
} else {
|
|
|
|
eventEmitter.buffering(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-04-03 11:19:04 -06:00
|
|
|
public void onPositionDiscontinuity(int reason) {
|
2017-03-21 14:25:17 -06:00
|
|
|
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();
|
|
|
|
}
|
2018-05-28 22:26:23 -06:00
|
|
|
// When repeat is turned on, reaching the end of the video will not cause a state change
|
|
|
|
// so we need to explicitly detect it.
|
2018-05-28 22:22:47 -06:00
|
|
|
if (reason == ExoPlayer.DISCONTINUITY_REASON_PERIOD_TRANSITION
|
|
|
|
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
|
|
|
|
eventEmitter.end();
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-04-03 11:19:04 -06:00
|
|
|
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) {
|
2017-03-21 14:25:17 -06:00
|
|
|
// Do nothing.
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
|
|
|
// Do Nothing.
|
|
|
|
}
|
|
|
|
|
2017-06-13 16:45:12 -06:00
|
|
|
@Override
|
|
|
|
public void onPlaybackParametersChanged(PlaybackParameters params) {
|
|
|
|
eventEmitter.playbackRateChange(params.speed);
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
@Override
|
|
|
|
public void onPlayerError(ExoPlaybackException e) {
|
|
|
|
String errorString = null;
|
2017-12-06 09:56:41 -07:00
|
|
|
Exception ex = e;
|
2017-01-11 05:51:45 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-12-06 09:56:41 -07:00
|
|
|
else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
|
|
|
|
ex = e.getSourceException();
|
|
|
|
errorString = getResources().getString(R.string.unrecognized_media_format);
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
if (errorString != null) {
|
2017-12-06 09:56:41 -07:00
|
|
|
eventEmitter.error(errorString, ex);
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
playerNeedsSource = true;
|
2017-03-21 14:25:17 -06:00
|
|
|
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;
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
|
2017-02-13 19:38:02 -07:00
|
|
|
@Override
|
|
|
|
public void onMetadata(Metadata metadata) {
|
|
|
|
eventEmitter.timedMetadata(metadata);
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
// ReactExoplayerViewManager public api
|
|
|
|
|
|
|
|
public void setSrc(final Uri uri, final String extension) {
|
|
|
|
if (uri != null) {
|
2017-03-21 14:26:23 -06:00
|
|
|
boolean isOriginalSourceNull = srcUri == null;
|
|
|
|
boolean isSourceEqual = uri.equals(srcUri);
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
this.srcUri = uri;
|
|
|
|
this.extension = extension;
|
|
|
|
this.mediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(getContext(), BANDWIDTH_METER);
|
2017-03-21 14:26:23 -06:00
|
|
|
|
|
|
|
if (!isOriginalSourceNull && !isSourceEqual) {
|
|
|
|
reloadSource();
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-31 10:15:39 -06:00
|
|
|
public void setProgressUpdateInterval(final float progressUpdateInterval) {
|
|
|
|
mProgressUpdateInterval = progressUpdateInterval;
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
public void setRawSrc(final Uri uri, final String extension) {
|
|
|
|
if (uri != null) {
|
2017-03-21 14:26:23 -06:00
|
|
|
boolean isOriginalSourceNull = srcUri == null;
|
|
|
|
boolean isSourceEqual = uri.equals(srcUri);
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
this.srcUri = uri;
|
|
|
|
this.extension = extension;
|
|
|
|
this.mediaDataSourceFactory = DataSourceUtil.getRawDataSourceFactory(getContext());
|
2017-03-21 14:26:23 -06:00
|
|
|
|
|
|
|
if (!isOriginalSourceNull && !isSourceEqual) {
|
|
|
|
reloadSource();
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-21 14:26:23 -06:00
|
|
|
private void reloadSource() {
|
|
|
|
playerNeedsSource = true;
|
|
|
|
initializePlayer();
|
|
|
|
}
|
|
|
|
|
2017-01-11 05:51:45 -07:00
|
|
|
public void setResizeModeModifier(@ResizeMode.Mode int resizeMode) {
|
|
|
|
exoPlayerView.setResizeMode(resizeMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setRepeatModifier(boolean repeat) {
|
2018-05-28 22:01:22 -06:00
|
|
|
if (player != null) {
|
|
|
|
if (repeat) {
|
|
|
|
player.setRepeatMode(Player.REPEAT_MODE_ONE);
|
|
|
|
} else {
|
|
|
|
player.setRepeatMode(Player.REPEAT_MODE_OFF);
|
|
|
|
}
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
this.repeat = repeat;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-13 16:45:12 -06:00
|
|
|
public void setRateModifier(float newRate) {
|
|
|
|
rate = newRate;
|
|
|
|
|
|
|
|
if (player != null) {
|
|
|
|
PlaybackParameters params = new PlaybackParameters(rate, 1f);
|
|
|
|
player.setPlaybackParameters(params);
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void setPlayInBackground(boolean playInBackground) {
|
2017-05-08 14:30:45 -06:00
|
|
|
this.playInBackground = playInBackground;
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setDisableFocus(boolean disableFocus) {
|
|
|
|
this.disableFocus = disableFocus;
|
|
|
|
}
|
2018-05-17 16:42:44 -06:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
2017-01-11 05:51:45 -07:00
|
|
|
}
|