Merge branch 'master' into patch-1
This commit is contained in:
@@ -26,9 +26,14 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Remove this repository line after google releases to google() or mavenCentral()
|
||||
maven { url "https://dl.google.com/android/maven2" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
|
||||
implementation('com.google.android.exoplayer:exoplayer:2.11.4') {
|
||||
implementation('com.google.android.exoplayer:exoplayer:2.13.3') {
|
||||
exclude group: 'com.android.support'
|
||||
}
|
||||
|
||||
@@ -37,9 +42,9 @@ dependencies {
|
||||
implementation "androidx.core:core:1.1.0"
|
||||
implementation "androidx.media:media:1.1.0"
|
||||
|
||||
implementation('com.google.android.exoplayer:extension-okhttp:2.11.4') {
|
||||
implementation('com.google.android.exoplayer:extension-okhttp:2.13.3') {
|
||||
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
|
||||
}
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.14.3'
|
||||
implementation 'com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}'
|
||||
|
||||
}
|
||||
|
@@ -1,3 +1,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.brentvatne.react">
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.brentvatne.exoplayer.ExoPlayerFullscreenVideoActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:launchMode="singleTop" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
@@ -0,0 +1,155 @@
|
||||
package com.brentvatne.exoplayer;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.brentvatne.react.R;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.ui.PlayerControlView;
|
||||
|
||||
public class ExoPlayerFullscreenVideoActivity extends AppCompatActivity implements ReactExoplayerView.FullScreenDelegate {
|
||||
public static final String EXTRA_EXO_PLAYER_VIEW_ID = "extra_id";
|
||||
public static final String EXTRA_ORIENTATION = "extra_orientation";
|
||||
|
||||
private ReactExoplayerView exoplayerView;
|
||||
private PlayerControlView playerControlView;
|
||||
private SimpleExoPlayer player;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
int exoplayerViewId = getIntent().getIntExtra(EXTRA_EXO_PLAYER_VIEW_ID, -1);
|
||||
exoplayerView = ReactExoplayerView.getViewInstance(exoplayerViewId);
|
||||
if (exoplayerView == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
String orientation = getIntent().getStringExtra(EXTRA_ORIENTATION);
|
||||
if ("landscape".equals(orientation)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||
} else if ("portrait".equals(orientation)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
|
||||
}
|
||||
setContentView(R.layout.exo_player_fullscreen_video);
|
||||
player = exoplayerView.getPlayer();
|
||||
|
||||
ExoPlayerView playerView = findViewById(R.id.player_view);
|
||||
playerView.setPlayer(player);
|
||||
playerView.setOnClickListener(v -> togglePlayerControlVisibility());
|
||||
|
||||
playerControlView = findViewById(R.id.player_controls);
|
||||
playerControlView.setPlayer(player);
|
||||
// Set the fullscreen button to "close fullscreen" icon
|
||||
ImageView fullscreenIcon = playerControlView.findViewById(R.id.exo_fullscreen_icon);
|
||||
fullscreenIcon.setImageResource(R.drawable.exo_controls_fullscreen_exit);
|
||||
playerControlView.findViewById(R.id.exo_fullscreen_button)
|
||||
.setOnClickListener(v -> {
|
||||
if (exoplayerView != null) {
|
||||
exoplayerView.setFullscreen(false);
|
||||
}
|
||||
});
|
||||
//Handling the playButton click event
|
||||
playerControlView.findViewById(R.id.exo_play).setOnClickListener(v -> {
|
||||
if (player != null && player.getPlaybackState() == Player.STATE_ENDED) {
|
||||
player.seekTo(0);
|
||||
}
|
||||
if (exoplayerView != null) {
|
||||
exoplayerView.setPausedModifier(false);
|
||||
}
|
||||
});
|
||||
|
||||
//Handling the pauseButton click event
|
||||
playerControlView.findViewById(R.id.exo_pause).setOnClickListener(v -> {
|
||||
if (exoplayerView != null) {
|
||||
exoplayerView.setPausedModifier(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (exoplayerView != null) {
|
||||
exoplayerView.syncPlayerState();
|
||||
exoplayerView.registerFullScreenDelegate(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
player.setPlayWhenReady(false);
|
||||
if (exoplayerView != null) {
|
||||
exoplayerView.registerFullScreenDelegate(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus) {
|
||||
playerControlView.postDelayed(this::hideSystemUI, 200);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
|
||||
if (exoplayerView != null) {
|
||||
exoplayerView.setFullscreen(false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private void togglePlayerControlVisibility() {
|
||||
if (playerControlView.isVisible()) {
|
||||
playerControlView.hide();
|
||||
} else {
|
||||
playerControlView.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables regular immersive mode.
|
||||
*/
|
||||
private void hideSystemUI() {
|
||||
View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE
|
||||
// Set the content to appear under the system bars so that the
|
||||
// content doesn't resize when the system bars hide and show.
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
// Hide the nav bar and status bar
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the system bars by removing all the flags
|
||||
* except for the ones that make the content appear under the system bars.
|
||||
*/
|
||||
private void showSystemUI() {
|
||||
View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeFullScreen() {
|
||||
finish();
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.video.VideoListener;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
@@ -85,6 +86,14 @@ public final class ExoPlayerView extends FrameLayout {
|
||||
addViewInLayout(layout, 0, aspectRatioParams);
|
||||
}
|
||||
|
||||
private void clearVideoView() {
|
||||
if (surfaceView instanceof TextureView) {
|
||||
player.clearVideoTextureView((TextureView) surfaceView);
|
||||
} else if (surfaceView instanceof SurfaceView) {
|
||||
player.clearVideoSurfaceView((SurfaceView) surfaceView);
|
||||
}
|
||||
}
|
||||
|
||||
private void setVideoView() {
|
||||
if (surfaceView instanceof TextureView) {
|
||||
player.setVideoTextureView((TextureView) surfaceView);
|
||||
@@ -113,29 +122,26 @@ public final class ExoPlayerView extends FrameLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and
|
||||
* {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous
|
||||
* Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#addTextOutput} and
|
||||
* {@link SimpleExoPlayer#addVideoListener} method of the player will be called and previous
|
||||
* assignments are overridden.
|
||||
*
|
||||
* @param player The {@link SimpleExoPlayer} to use.
|
||||
*/
|
||||
public void setPlayer(SimpleExoPlayer player) {
|
||||
if (this.player == player) {
|
||||
return;
|
||||
}
|
||||
if (this.player != null) {
|
||||
this.player.setTextOutput(null);
|
||||
this.player.setVideoListener(null);
|
||||
this.player.removeTextOutput(componentListener);
|
||||
this.player.removeVideoListener(componentListener);
|
||||
this.player.removeListener(componentListener);
|
||||
this.player.setVideoSurface(null);
|
||||
clearVideoView();
|
||||
}
|
||||
this.player = player;
|
||||
shutterView.setVisibility(VISIBLE);
|
||||
if (player != null) {
|
||||
setVideoView();
|
||||
player.setVideoListener(componentListener);
|
||||
player.addVideoListener(componentListener);
|
||||
player.addListener(componentListener);
|
||||
player.setTextOutput(componentListener);
|
||||
player.addTextOutput(componentListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +211,7 @@ public final class ExoPlayerView extends FrameLayout {
|
||||
layout.invalidateAspectRatio();
|
||||
}
|
||||
|
||||
private final class ComponentListener implements SimpleExoPlayer.VideoListener,
|
||||
private final class ComponentListener implements VideoListener,
|
||||
TextOutput, ExoPlayer.EventListener {
|
||||
|
||||
// TextRenderer.Output implementation
|
||||
|
@@ -1,8 +1,8 @@
|
||||
package com.brentvatne.exoplayer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
@@ -10,7 +10,6 @@ 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 android.widget.ImageButton;
|
||||
@@ -30,16 +29,14 @@ import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
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.drm.DefaultDrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
||||
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
|
||||
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
|
||||
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
|
||||
@@ -61,8 +58,8 @@ 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.ExoTrackSelection;
|
||||
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.ui.PlayerControlView;
|
||||
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||
@@ -76,9 +73,10 @@ import java.net.CookieHandler;
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookiePolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class ReactExoplayerView extends FrameLayout implements
|
||||
@@ -88,7 +86,7 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
BecomingNoisyListener,
|
||||
AudioManager.OnAudioFocusChangeListener,
|
||||
MetadataOutput,
|
||||
DefaultDrmSessionEventListener {
|
||||
DrmSessionEventListener {
|
||||
|
||||
private static final String TAG = "ReactExoplayerView";
|
||||
|
||||
@@ -100,6 +98,9 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
||||
}
|
||||
|
||||
private static Map<Integer, ReactExoplayerView> instances = new HashMap<>();
|
||||
private FullScreenDelegate fullScreenDelegate;
|
||||
|
||||
private final VideoEventEmitter eventEmitter;
|
||||
private final ReactExoplayerConfig config;
|
||||
private final DefaultBandwidthMeter bandwidthMeter;
|
||||
@@ -118,10 +119,13 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
private long resumePosition;
|
||||
private boolean loadVideoStarted;
|
||||
private boolean isFullscreen;
|
||||
private String fullScreenOrientation;
|
||||
private boolean isInBackground;
|
||||
private boolean isInFullscreen;
|
||||
private boolean isPaused;
|
||||
private boolean isBuffering;
|
||||
private boolean muted = false;
|
||||
private boolean hasAudioFocus = false;
|
||||
private float rate = 1f;
|
||||
private float audioVolume = 1f;
|
||||
private int minLoadRetryCount = 3;
|
||||
@@ -133,8 +137,6 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
|
||||
private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
|
||||
|
||||
private Handler mainHandler;
|
||||
|
||||
// Props from React
|
||||
private Uri srcUri;
|
||||
private String extension;
|
||||
@@ -201,7 +203,6 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
createViews();
|
||||
|
||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
themedReactContext.addLifecycleEventListener(this);
|
||||
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
|
||||
}
|
||||
|
||||
@@ -226,8 +227,6 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
exoPlayerView.setLayoutParams(layoutParams);
|
||||
|
||||
addView(exoPlayerView, 0, layoutParams);
|
||||
|
||||
mainHandler = new Handler();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -250,7 +249,15 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
if (!playInBackground || !isInBackground) {
|
||||
setPlayWhenReady(!isPaused);
|
||||
if (isInFullscreen) {
|
||||
if (player != null) {
|
||||
exoPlayerView.setPlayer(player);
|
||||
syncPlayerState();
|
||||
}
|
||||
isInFullscreen = false;
|
||||
} else {
|
||||
setPlayWhenReady(!isPaused);
|
||||
}
|
||||
}
|
||||
isInBackground = false;
|
||||
}
|
||||
@@ -271,6 +278,7 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
|
||||
public void cleanUpResources() {
|
||||
stopPlayback();
|
||||
instances.remove(this.getId());
|
||||
}
|
||||
|
||||
//BandwidthMeter.EventListener implementation
|
||||
@@ -289,6 +297,29 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
public static ReactExoplayerView getViewInstance(Integer uid) {
|
||||
return instances.get(uid);
|
||||
}
|
||||
|
||||
public SimpleExoPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public void syncPlayerState() {
|
||||
if (player == null) return;
|
||||
if (player.getPlaybackState() == Player.STATE_ENDED) {
|
||||
// Try to get last frame displayed
|
||||
player.seekTo(player.getDuration() - 200);
|
||||
player.setPlayWhenReady(true);
|
||||
} else {
|
||||
player.setPlayWhenReady(!isPaused);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerFullScreenDelegate(FullScreenDelegate delegate) {
|
||||
this.fullScreenDelegate = delegate;
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
|
||||
/**
|
||||
@@ -304,6 +335,15 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
private void showFullscreen() {
|
||||
instances.put(this.getId(), this);
|
||||
Intent intent = new Intent(getContext(), ExoPlayerFullscreenVideoActivity.class);
|
||||
intent.putExtra(ExoPlayerFullscreenVideoActivity.EXTRA_EXO_PLAYER_VIEW_ID, this.getId());
|
||||
intent.putExtra(ExoPlayerFullscreenVideoActivity.EXTRA_ORIENTATION, this.fullScreenOrientation);
|
||||
getContext().startActivity(intent);
|
||||
isInFullscreen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializing Player control
|
||||
*/
|
||||
@@ -316,6 +356,7 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
playerControlView.setPlayer(player);
|
||||
playerControlView.show();
|
||||
playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
|
||||
playerControlView.findViewById(R.id.exo_fullscreen_button).setOnClickListener(v -> setFullscreen(true));
|
||||
|
||||
// Invoking onClick event for exoplayerView
|
||||
exoPlayerView.setOnClickListener(new OnClickListener() {
|
||||
@@ -388,13 +429,14 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
|
||||
private void initializePlayer() {
|
||||
themedReactContext.addLifecycleEventListener(this);
|
||||
ReactExoplayerView self = this;
|
||||
// This ensures all props have been settled, to avoid async racing conditions.
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (player == null) {
|
||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
||||
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
||||
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||
trackSelector.setParameters(trackSelector.buildUponParameters()
|
||||
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
|
||||
@@ -409,23 +451,11 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
DefaultRenderersFactory renderersFactory =
|
||||
new DefaultRenderersFactory(getContext())
|
||||
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
|
||||
// DRM
|
||||
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = null;
|
||||
if (self.drmUUID != null) {
|
||||
try {
|
||||
drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
|
||||
self.drmLicenseHeader);
|
||||
} catch (UnsupportedDrmException e) {
|
||||
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
|
||||
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
|
||||
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
|
||||
eventEmitter.error(getResources().getString(errorStringId), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// End DRM
|
||||
player = ExoPlayerFactory.newSimpleInstance(getContext(), renderersFactory,
|
||||
trackSelector, defaultLoadControl, drmSessionManager, bandwidthMeter);
|
||||
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
|
||||
.setTrackSelector(trackSelector)
|
||||
.setBandwidthMeter(bandwidthMeter)
|
||||
.setLoadControl(defaultLoadControl)
|
||||
.build();
|
||||
player.addListener(self);
|
||||
player.addMetadataOutput(self);
|
||||
exoPlayerView.setPlayer(player);
|
||||
@@ -440,8 +470,24 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
if (playerNeedsSource && srcUri != null) {
|
||||
exoPlayerView.invalidateAspectRatio();
|
||||
|
||||
// DRM
|
||||
DrmSessionManager drmSessionManager = null;
|
||||
if (self.drmUUID != null) {
|
||||
try {
|
||||
drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
|
||||
self.drmLicenseHeader);
|
||||
} catch (UnsupportedDrmException e) {
|
||||
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
|
||||
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
|
||||
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
|
||||
eventEmitter.error(getResources().getString(errorStringId), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// End DRM
|
||||
|
||||
ArrayList<MediaSource> mediaSourceList = buildTextSources();
|
||||
MediaSource videoSource = buildMediaSource(srcUri, extension);
|
||||
MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager);
|
||||
MediaSource mediaSource;
|
||||
if (mediaSourceList.size() == 0) {
|
||||
mediaSource = videoSource;
|
||||
@@ -460,6 +506,7 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
player.prepare(mediaSource, !haveResumePosition, false);
|
||||
playerNeedsSource = false;
|
||||
|
||||
reLayout(exoPlayerView);
|
||||
eventEmitter.loadStart();
|
||||
loadVideoStarted = true;
|
||||
}
|
||||
@@ -472,7 +519,7 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
}, 1);
|
||||
}
|
||||
|
||||
private DrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManager(UUID uuid,
|
||||
private DrmSessionManager buildDrmSessionManager(UUID uuid,
|
||||
String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException {
|
||||
if (Util.SDK_INT < 18) {
|
||||
return null;
|
||||
@@ -485,11 +532,11 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
keyRequestPropertiesArray[i + 1]);
|
||||
}
|
||||
}
|
||||
return new DefaultDrmSessionManager<>(uuid,
|
||||
return new DefaultDrmSessionManager(uuid,
|
||||
FrameworkMediaDrm.newInstance(uuid), drmCallback, null, false, 3);
|
||||
}
|
||||
|
||||
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
|
||||
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
|
||||
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
|
||||
: uri.getLastPathSegment());
|
||||
switch (type) {
|
||||
@@ -497,26 +544,30 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
return new SsMediaSource.Factory(
|
||||
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
||||
buildDataSourceFactory(false)
|
||||
).setLoadErrorHandlingPolicy(
|
||||
).setDrmSessionManager(drmSessionManager)
|
||||
.setLoadErrorHandlingPolicy(
|
||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||
).createMediaSource(uri);
|
||||
case C.TYPE_DASH:
|
||||
return new DashMediaSource.Factory(
|
||||
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
||||
buildDataSourceFactory(false)
|
||||
).setLoadErrorHandlingPolicy(
|
||||
).setDrmSessionManager(drmSessionManager)
|
||||
.setLoadErrorHandlingPolicy(
|
||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||
).createMediaSource(uri);
|
||||
case C.TYPE_HLS:
|
||||
return new HlsMediaSource.Factory(
|
||||
mediaDataSourceFactory
|
||||
).setLoadErrorHandlingPolicy(
|
||||
).setDrmSessionManager(drmSessionManager)
|
||||
.setLoadErrorHandlingPolicy(
|
||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||
).createMediaSource(uri);
|
||||
case C.TYPE_OTHER:
|
||||
return new ProgressiveMediaSource.Factory(
|
||||
mediaDataSourceFactory
|
||||
).setLoadErrorHandlingPolicy(
|
||||
).setDrmSessionManager(drmSessionManager)
|
||||
.setLoadErrorHandlingPolicy(
|
||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||
).createMediaSource(uri);
|
||||
default: {
|
||||
@@ -567,7 +618,7 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
|
||||
private boolean requestAudioFocus() {
|
||||
if (disableFocus || srcUri == null) {
|
||||
if (disableFocus || srcUri == null || this.hasAudioFocus) {
|
||||
return true;
|
||||
}
|
||||
int result = audioManager.requestAudioFocus(this,
|
||||
@@ -582,8 +633,8 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
|
||||
if (playWhenReady) {
|
||||
boolean hasAudioFocus = requestAudioFocus();
|
||||
if (hasAudioFocus) {
|
||||
this.hasAudioFocus = requestAudioFocus();
|
||||
if (this.hasAudioFocus) {
|
||||
player.setPlayWhenReady(true);
|
||||
}
|
||||
} else {
|
||||
@@ -631,9 +682,6 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
|
||||
private void onStopPlayback() {
|
||||
if (isFullscreen) {
|
||||
setFullscreen(false);
|
||||
}
|
||||
audioManager.abandonAudioFocus(this);
|
||||
}
|
||||
|
||||
@@ -678,6 +726,7 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
public void onAudioFocusChange(int focusChange) {
|
||||
switch (focusChange) {
|
||||
case AudioManager.AUDIOFOCUS_LOSS:
|
||||
this.hasAudioFocus = false;
|
||||
eventEmitter.audioFocusChanged(false);
|
||||
pausePlayback();
|
||||
audioManager.abandonAudioFocus(this);
|
||||
@@ -686,6 +735,7 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
eventEmitter.audioFocusChanged(false);
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_GAIN:
|
||||
this.hasAudioFocus = true;
|
||||
eventEmitter.audioFocusChanged(true);
|
||||
break;
|
||||
default:
|
||||
@@ -1001,7 +1051,6 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
|
||||
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;
|
||||
@@ -1011,12 +1060,23 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
|
||||
this.requestHeaders);
|
||||
|
||||
if (!isOriginalSourceNull && !isSourceEqual) {
|
||||
if (!isSourceEqual) {
|
||||
reloadSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clearSrc() {
|
||||
if (srcUri != null) {
|
||||
player.stop(true);
|
||||
this.srcUri = null;
|
||||
this.extension = null;
|
||||
this.requestHeaders = null;
|
||||
this.mediaDataSourceFactory = null;
|
||||
clearResumePosition();
|
||||
}
|
||||
}
|
||||
|
||||
public void setProgressUpdateInterval(final float progressUpdateInterval) {
|
||||
mProgressUpdateInterval = progressUpdateInterval;
|
||||
}
|
||||
@@ -1027,14 +1087,13 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
|
||||
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 = buildDataSourceFactory(true);
|
||||
|
||||
if (!isOriginalSourceNull && !isSourceEqual) {
|
||||
if (!isSourceEqual) {
|
||||
reloadSource();
|
||||
}
|
||||
}
|
||||
@@ -1276,34 +1335,23 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
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);
|
||||
showFullscreen();
|
||||
eventEmitter.fullscreenDidPresent();
|
||||
} else {
|
||||
uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
|
||||
eventEmitter.fullscreenWillDismiss();
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
if (fullScreenDelegate != null) {
|
||||
fullScreenDelegate.closeFullScreen();
|
||||
}
|
||||
eventEmitter.fullscreenDidDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFullscreenOrientation(String orientation) {
|
||||
this.fullScreenOrientation = orientation;
|
||||
}
|
||||
|
||||
public void setUseTextureView(boolean useTextureView) {
|
||||
boolean finallyUseTextureView = useTextureView && this.drmUUID == null;
|
||||
exoPlayerView.setUseTextureView(finallyUseTextureView);
|
||||
@@ -1336,23 +1384,23 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
|
||||
|
||||
@Override
|
||||
public void onDrmKeysLoaded() {
|
||||
public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||
Log.d("DRM Info", "onDrmKeysLoaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrmSessionManagerError(Exception e) {
|
||||
public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) {
|
||||
Log.d("DRM Info", "onDrmSessionManagerError");
|
||||
eventEmitter.error("onDrmSessionManagerError", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrmKeysRestored() {
|
||||
public void onDrmKeysRestored(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||
Log.d("DRM Info", "onDrmKeysRestored");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrmKeysRemoved() {
|
||||
public void onDrmKeysRemoved(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||
Log.d("DRM Info", "onDrmKeysRemoved");
|
||||
}
|
||||
|
||||
@@ -1373,4 +1421,8 @@ class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface FullScreenDelegate {
|
||||
void closeFullScreen();
|
||||
}
|
||||
}
|
||||
|
@@ -64,6 +64,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
||||
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
|
||||
private static final String PROP_DISABLE_FOCUS = "disableFocus";
|
||||
private static final String PROP_FULLSCREEN = "fullscreen";
|
||||
private static final String PROP_FULLSCREEN_ORIENTATION = "fullscreenOrientation";
|
||||
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView";
|
||||
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
|
||||
private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type";
|
||||
@@ -144,6 +145,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
||||
Map<String, String> headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null;
|
||||
|
||||
if (TextUtils.isEmpty(uriString)) {
|
||||
videoView.clearSrc();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -298,6 +300,11 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
||||
videoView.setFullscreen(fullscreen);
|
||||
}
|
||||
|
||||
@ReactProp(name = PROP_FULLSCREEN_ORIENTATION)
|
||||
public void setFullscreenOrientation(final ReactExoplayerView videoView, final String fullscreenOrientation) {
|
||||
videoView.setFullscreenOrientation(fullscreenOrientation);
|
||||
}
|
||||
|
||||
@ReactProp(name = PROP_USE_TEXTURE_VIEW, defaultBoolean = true)
|
||||
public void setUseTextureView(final ReactExoplayerView videoView, final boolean useTextureView) {
|
||||
videoView.setUseTextureView(useTextureView);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
@@ -71,6 +72,22 @@
|
||||
android:paddingRight="4dp"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="#FFBEBEBE"/>
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/exo_fullscreen_button"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/exo_fullscreen_icon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/exo_controls_fullscreen_enter" />
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/enclosing_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black">
|
||||
|
||||
<com.brentvatne.exoplayer.ExoPlayerView
|
||||
android:id="@+id/player_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.google.android.exoplayer2.ui.PlayerControlView
|
||||
android:id="@+id/player_controls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom" />
|
||||
</FrameLayout>
|
Reference in New Issue
Block a user