Merge branch 'master' of https://github.com/react-native-video/react-native-video into fix/report_time_position_when_updated

# Conflicts:
#	CHANGELOG.md
#	android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
This commit is contained in:
olivier bouillet
2022-06-15 21:57:29 +02:00
160 changed files with 12038 additions and 9682 deletions

View File

@@ -9,7 +9,7 @@ https://github.com/google/ExoPlayer
## Benefits over `react-native-video@0.9.0`:
- Android Video library built by Google, with a lot of support
- Supports DASH, HlS, & SmoothStreaming adaptive streams
- Supports DASH, HLS, & SmoothStreaming adaptive streams
- Supports formats such as MP4, M4A, FMP4, WebM, MKV, MP3, Ogg, WAV, MPEG-TS, MPEG-PS, FLV and ADTS (AAC).
- Fewer device specific issues
- Highly customisable

View File

@@ -5,8 +5,8 @@ def safeExtGet(prop, fallback) {
}
android {
compileSdkVersion safeExtGet('compileSdkVersion', 28)
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
compileSdkVersion safeExtGet('compileSdkVersion', 31)
buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
@@ -14,7 +14,7 @@ android {
}
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 16)
minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 28)
versionCode 1
versionName "1.0"
@@ -33,7 +33,7 @@ repositories {
dependencies {
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
implementation('com.google.android.exoplayer:exoplayer:2.13.3') {
implementation('com.google.android.exoplayer:exoplayer:2.17.1') {
exclude group: 'com.android.support'
}
@@ -42,7 +42,7 @@ dependencies {
implementation "androidx.core:core:1.1.0"
implementation "androidx.media:media:1.1.0"
implementation('com.google.android.exoplayer:extension-okhttp:2.13.3') {
implementation('com.google.android.exoplayer:extension-okhttp:2.17.1') {
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
}
implementation 'com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}'

View File

@@ -1,9 +1,3 @@
<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>

View File

@@ -4,13 +4,14 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.CookieJarContainer;
import com.facebook.react.modules.network.ForwardingCookieHandler;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory;
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Util;
import okhttp3.Call;
import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
import java.util.Map;
@@ -75,7 +76,7 @@ public class DataSourceUtil {
}
private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
return new DefaultDataSourceFactory(context, bandwidthMeter,
return new DefaultDataSource.Factory(context,
buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders));
}
@@ -84,10 +85,12 @@ public class DataSourceUtil {
CookieJarContainer container = (CookieJarContainer) client.cookieJar();
ForwardingCookieHandler handler = new ForwardingCookieHandler(context);
container.setCookieJar(new JavaNetCookieJar(handler));
OkHttpDataSourceFactory okHttpDataSourceFactory = new OkHttpDataSourceFactory(client, getUserAgent(context), bandwidthMeter);
OkHttpDataSource.Factory okHttpDataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) client)
.setUserAgent(getUserAgent(context))
.setTransferListener(bandwidthMeter);
if (requestHeaders != null)
okHttpDataSourceFactory.getDefaultRequestProperties().set(requestHeaders);
okHttpDataSourceFactory.setDefaultRequestProperties(requestHeaders);
return okHttpDataSourceFactory;
}

View File

@@ -9,16 +9,28 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {
private final DefaultBandwidthMeter bandwidthMeter;
private boolean disableDisconnectError = false;
public DefaultReactExoplayerConfig(Context context) {
this.bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build();
}
@Override
public LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount) {
if (this.disableDisconnectError) {
// Use custom error handling policy to prevent throwing an error when losing network connection
return new ReactExoplayerLoadErrorHandlingPolicy(minLoadRetryCount);
}
return new DefaultLoadErrorHandlingPolicy(minLoadRetryCount);
}
public void setDisableDisconnectError(boolean disableDisconnectError) {
this.disableDisconnectError = disableDisconnectError;
}
public boolean getDisableDisconnectError() {
return this.disableDisconnectError;
}
@Override
public DefaultBandwidthMeter getBandwidthMeter() {
return bandwidthMeter;

View File

@@ -1,155 +0,0 @@
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();
}
}

View File

@@ -13,18 +13,16 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.SubtitleView;
import com.google.android.exoplayer2.video.VideoSize;
import java.util.List;
@@ -36,11 +34,12 @@ public final class ExoPlayerView extends FrameLayout {
private final SubtitleView subtitleLayout;
private final AspectRatioFrameLayout layout;
private final ComponentListener componentListener;
private SimpleExoPlayer player;
private ExoPlayer player;
private Context context;
private ViewGroup.LayoutParams layoutParams;
private boolean useTextureView = true;
private boolean useSecureView = false;
private boolean hideShutterView = false;
public ExoPlayerView(Context context) {
@@ -103,7 +102,15 @@ public final class ExoPlayerView extends FrameLayout {
}
private void updateSurfaceView() {
View view = useTextureView ? new TextureView(context) : new SurfaceView(context);
View view;
if (!useTextureView || useSecureView) {
view = new SurfaceView(context);
if (useSecureView) {
((SurfaceView)view).setSecure(true);
}
} else {
view = new TextureView(context);
}
view.setLayoutParams(layoutParams);
surfaceView = view;
@@ -122,26 +129,25 @@ public final class ExoPlayerView extends FrameLayout {
}
/**
* Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#addTextOutput} and
* {@link SimpleExoPlayer#addVideoListener} method of the player will be called and previous
* Set the {@link ExoPlayer} to use. The {@link ExoPlayer#addListener} method of the
* player will be called and previous
* assignments are overridden.
*
* @param player The {@link SimpleExoPlayer} to use.
* @param player The {@link ExoPlayer} to use.
*/
public void setPlayer(SimpleExoPlayer player) {
public void setPlayer(ExoPlayer player) {
if (this.player == player) {
return;
}
if (this.player != null) {
this.player.removeTextOutput(componentListener);
this.player.removeVideoListener(componentListener);
this.player.removeListener(componentListener);
clearVideoView();
}
this.player = player;
shutterView.setVisibility(VISIBLE);
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
if (player != null) {
setVideoView();
player.addVideoListener(componentListener);
player.addListener(componentListener);
player.addTextOutput(componentListener);
}
}
@@ -175,6 +181,13 @@ public final class ExoPlayerView extends FrameLayout {
}
}
public void useSecureView(boolean useSecureView) {
if (useSecureView != this.useSecureView) {
this.useSecureView = useSecureView;
updateSurfaceView();
}
}
public void setHideShutterView(boolean hideShutterView) {
this.hideShutterView = hideShutterView;
updateShutterViewVisibility();
@@ -203,7 +216,7 @@ public final class ExoPlayerView extends FrameLayout {
}
}
// Video disabled so the shutter must be closed.
shutterView.setVisibility(VISIBLE);
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
}
public void invalidateAspectRatio() {
@@ -211,8 +224,7 @@ public final class ExoPlayerView extends FrameLayout {
layout.invalidateAspectRatio();
}
private final class ComponentListener implements VideoListener,
TextOutput, ExoPlayer.EventListener {
private final class ComponentListener implements Player.Listener {
// TextRenderer.Output implementation
@@ -221,12 +233,12 @@ public final class ExoPlayerView extends FrameLayout {
subtitleLayout.onCues(cues);
}
// SimpleExoPlayer.VideoListener implementation
// ExoPlayer.VideoListener implementation
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
public void onVideoSizeChanged(VideoSize videoSize) {
boolean isInitialRatio = layout.getAspectRatio() == 0;
layout.setAspectRatio(height == 0 ? 1 : (width * pixelWidthHeightRatio) / height);
layout.setAspectRatio(videoSize.height == 0 ? 1 : (videoSize.width * videoSize.pixelWidthHeightRatio) / videoSize.height);
// React native workaround for measuring and layout on initial load.
if (isInitialRatio) {
@@ -242,32 +254,37 @@ public final class ExoPlayerView extends FrameLayout {
// ExoPlayer.EventListener implementation
@Override
public void onLoadingChanged(boolean isLoading) {
public void onIsLoadingChanged(boolean isLoading) {
// Do nothing.
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
public void onPlaybackStateChanged(int playbackState) {
// Do nothing.
}
@Override
public void onPlayerError(ExoPlaybackException e) {
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
// Do nothing.
}
@Override
public void onPositionDiscontinuity(int reason) {
public void onPlayerError(PlaybackException e) {
// Do nothing.
}
@Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
// Do nothing.
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
public void onTimelineChanged(Timeline timeline, int reason) {
// Do nothing.
}
@Override
public void onTracksInfoChanged(TracksInfo tracksInfo) {
updateForCurrentTrackSelections();
}
@@ -276,11 +293,6 @@ public final class ExoPlayerView extends FrameLayout {
// Do nothing
}
@Override
public void onSeekProcessed() {
// Do nothing.
}
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
// Do nothing.

View File

@@ -9,5 +9,8 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
public interface ReactExoplayerConfig {
LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount);
void setDisableDisconnectError(boolean disableDisconnectError);
boolean getDisableDisconnectError();
DefaultBandwidthMeter getBandwidthMeter();
}

View File

@@ -0,0 +1,36 @@
package com.brentvatne.exoplayer;
import java.io.IOException;
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.LoadErrorInfo;
import com.google.android.exoplayer2.C;
public final class ReactExoplayerLoadErrorHandlingPolicy extends DefaultLoadErrorHandlingPolicy {
private int minLoadRetryCount = Integer.MAX_VALUE;
public ReactExoplayerLoadErrorHandlingPolicy(int minLoadRetryCount) {
super(minLoadRetryCount);
this.minLoadRetryCount = minLoadRetryCount;
}
@Override
public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
if (
loadErrorInfo.exception instanceof HttpDataSourceException &&
(loadErrorInfo.exception.getMessage() == "Unable to connect" || loadErrorInfo.exception.getMessage() == "Software caused connection abort")
) {
// Capture the error we get when there is no network connectivity and keep retrying it
return 1000; // Retry every second
} else if(loadErrorInfo.errorCount < this.minLoadRetryCount) {
return Math.min((loadErrorInfo.errorCount - 1) * 1000, 5000); // Default timeout handling
} else {
return C.TIME_UNSET; // Done retrying and will return the error immediately
}
}
@Override
public int getMinimumLoadableRetryCount(int dataType) {
return Integer.MAX_VALUE;
}
}

View File

@@ -49,11 +49,15 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_PAUSED = "paused";
private static final String PROP_MUTED = "muted";
private static final String PROP_VOLUME = "volume";
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_MIN_BUFFER_MS = "minBufferMs";
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs";
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs";
private static final String PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent";
private static final String PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent";
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent";
private static final String PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK = "preventsDisplaySleepDuringVideoPlayback";
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth";
@@ -62,10 +66,13 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount";
private static final String PROP_MAXIMUM_BIT_RATE = "maxBitRate";
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
private static final String PROP_CONTENT_START_TIME = "contentStartTime";
private static final String PROP_DISABLE_FOCUS = "disableFocus";
private static final String PROP_DISABLE_BUFFERING = "disableBuffering";
private static final String PROP_DISABLE_DISCONNECT_ERROR = "disableDisconnectError";
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_SECURE_VIEW = "useSecureView";
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type";
private static final String PROP_SELECTED_VIDEO_TRACK_VALUE = "value";
@@ -295,21 +302,41 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
videoView.setDisableFocus(disableFocus);
}
@ReactProp(name = PROP_BACK_BUFFER_DURATION_MS, defaultInt = 0)
public void setBackBufferDurationMs(final ReactExoplayerView videoView, final int backBufferDurationMs) {
videoView.setBackBufferDurationMs(backBufferDurationMs);
}
@ReactProp(name = PROP_CONTENT_START_TIME, defaultInt = 0)
public void setContentStartTime(final ReactExoplayerView videoView, final int contentStartTime) {
videoView.setContentStartTime(contentStartTime);
}
@ReactProp(name = PROP_DISABLE_BUFFERING, defaultBoolean = false)
public void setDisableBuffering(final ReactExoplayerView videoView, final boolean disableBuffering) {
videoView.setDisableBuffering(disableBuffering);
}
@ReactProp(name = PROP_DISABLE_DISCONNECT_ERROR, defaultBoolean = false)
public void setDisableDisconnectError(final ReactExoplayerView videoView, final boolean disableDisconnectError) {
videoView.setDisableDisconnectError(disableDisconnectError);
}
@ReactProp(name = PROP_FULLSCREEN, defaultBoolean = false)
public void setFullscreen(final ReactExoplayerView videoView, final boolean fullscreen) {
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);
}
@ReactProp(name = PROP_SECURE_VIEW, defaultBoolean = true)
public void useSecureView(final ReactExoplayerView videoView, final boolean useSecureView) {
videoView.useSecureView(useSecureView);
}
@ReactProp(name = PROP_HIDE_SHUTTER_VIEW, defaultBoolean = false)
public void setHideShutterView(final ReactExoplayerView videoView, final boolean hideShutterView) {
videoView.setHideShutterView(hideShutterView);
@@ -326,6 +353,10 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
if (bufferConfig != null) {
minBufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BUFFER_MS)
? bufferConfig.getInt(PROP_BUFFER_CONFIG_MIN_BUFFER_MS) : minBufferMs;
@@ -335,7 +366,13 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS) : bufferForPlaybackMs;
bufferForPlaybackAfterRebufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS)
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS) : bufferForPlaybackAfterRebufferMs;
videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs);
maxHeapAllocationPercent = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT)
? bufferConfig.getDouble(PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT) : maxHeapAllocationPercent;
minBackBufferMemoryReservePercent = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT)
? bufferConfig.getDouble(PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT) : minBackBufferMemoryReservePercent;
minBufferMemoryReservePercent = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT)
? bufferConfig.getDouble(PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT) : minBufferMemoryReservePercent;
videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, maxHeapAllocationPercent, minBackBufferMemoryReservePercent, minBufferMemoryReservePercent);
}
}

View File

@@ -15,6 +15,8 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.io.StringWriter;
import java.io.PrintWriter;
class VideoEventEmitter {
@@ -42,6 +44,7 @@ class VideoEventEmitter {
private static final String EVENT_RESUME = "onPlaybackResume";
private static final String EVENT_READY = "onReadyForDisplay";
private static final String EVENT_BUFFER = "onVideoBuffer";
private static final String EVENT_PLAYBACK_STATE_CHANGED = "onVideoPlaybackStateChanged";
private static final String EVENT_IDLE = "onVideoIdle";
private static final String EVENT_TIMED_METADATA = "onTimedMetadata";
private static final String EVENT_AUDIO_BECOMING_NOISY = "onVideoAudioBecomingNoisy";
@@ -63,6 +66,7 @@ class VideoEventEmitter {
EVENT_RESUME,
EVENT_READY,
EVENT_BUFFER,
EVENT_PLAYBACK_STATE_CHANGED,
EVENT_IDLE,
EVENT_TIMED_METADATA,
EVENT_AUDIO_BECOMING_NOISY,
@@ -87,6 +91,7 @@ class VideoEventEmitter {
EVENT_RESUME,
EVENT_READY,
EVENT_BUFFER,
EVENT_PLAYBACK_STATE_CHANGED,
EVENT_IDLE,
EVENT_TIMED_METADATA,
EVENT_AUDIO_BECOMING_NOISY,
@@ -104,6 +109,8 @@ class VideoEventEmitter {
private static final String EVENT_PROP_STEP_FORWARD = "canStepForward";
private static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward";
private static final String EVENT_PROP_BUFFER_START = "bufferStart";
private static final String EVENT_PROP_BUFFER_END = "bufferEnd";
private static final String EVENT_PROP_DURATION = "duration";
private static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration";
private static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration";
@@ -125,11 +132,14 @@ class VideoEventEmitter {
private static final String EVENT_PROP_ERROR = "error";
private static final String EVENT_PROP_ERROR_STRING = "errorString";
private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException";
private static final String EVENT_PROP_ERROR_TRACE = "errorStackTrace";
private static final String EVENT_PROP_ERROR_CODE = "errorCode";
private static final String EVENT_PROP_TIMED_METADATA = "metadata";
private static final String EVENT_PROP_BITRATE = "bitrate";
private static final String EVENT_PROP_BITRATE = "bitrate";
private static final String EVENT_PROP_IS_PLAYING = "isPlaying";
void setViewId(int viewId) {
this.viewId = viewId;
@@ -206,6 +216,12 @@ class VideoEventEmitter {
receiveEvent(EVENT_BUFFER, map);
}
void playbackStateChanged(boolean isPlaying) {
WritableMap map = Arguments.createMap();
map.putBoolean(EVENT_PROP_IS_PLAYING, isPlaying);
receiveEvent(EVENT_PLAYBACK_STATE_CHANGED, map);
}
void idle() {
receiveEvent(EVENT_IDLE, null);
}
@@ -231,9 +247,25 @@ class VideoEventEmitter {
}
void error(String errorString, Exception exception) {
_error(errorString, exception, "0001");
}
void error(String errorString, Exception exception, String errorCode) {
_error(errorString, exception, errorCode);
}
void _error(String errorString, Exception exception, String errorCode) {
// Prepare stack trace
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
String stackTrace = sw.toString();
WritableMap error = Arguments.createMap();
error.putString(EVENT_PROP_ERROR_STRING, errorString);
error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.toString());
error.putString(EVENT_PROP_ERROR_CODE, errorCode);
error.putString(EVENT_PROP_ERROR_TRACE, stackTrace);
WritableMap event = Arguments.createMap();
event.putMap(EVENT_PROP_ERROR, error);
receiveEvent(EVENT_ERROR, event);

View File

@@ -1,6 +1,5 @@
<?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"
@@ -72,22 +71,6 @@
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
<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>

View File

@@ -1,18 +0,0 @@
<?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>