feat(android): replace deprecated ExoPlayer2 with AndroidX media3 (#3337)
* feat(android): implement AndroidX media3 dependencies * refactor(android): remove duplicate code * refactor(android): remove unused codes * feat(android): replace ExoPlayer2 with AndroidX media3 * fix(android): move default properties to gradle.properties * revert(android): prevent security exception * chore: align indent * chore: remove redundant comments * chore: reorder import * fix: apply media3's legacy player control view
This commit is contained in:
parent
1ba93f9e9d
commit
f2e80e9f2d
@ -19,28 +19,24 @@ def safeExtGet(prop) {
|
|||||||
return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : project.properties["RNVideo_" + prop]
|
return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : project.properties["RNVideo_" + prop]
|
||||||
}
|
}
|
||||||
|
|
||||||
def getExtOrDefault(name, defaultValue) {
|
|
||||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
def isNewArchitectureEnabled() {
|
def isNewArchitectureEnabled() {
|
||||||
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
def supportsNamespace() {
|
def supportsNamespace() {
|
||||||
def parsed = Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
def parsed = Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
||||||
def major = parsed[0].toInteger()
|
def major = parsed[0].toInteger()
|
||||||
def minor = parsed[1].toInteger()
|
def minor = parsed[1].toInteger()
|
||||||
|
|
||||||
// Namespace support was added in 7.3.0
|
// Namespace support was added in 7.3.0
|
||||||
if (major == 7 && minor >= 3) {
|
if (major == 7 && minor >= 3) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return major >= 8
|
return major >= 8
|
||||||
}
|
}
|
||||||
|
|
||||||
def useExoplayerIMA = getExtOrDefault("RNVUseExoplayerIMA", false)
|
def useExoplayerIMA = safeExtGet("RNVUseExoplayerIMA")?.toBoolean() ?: false
|
||||||
|
|
||||||
println "useExoplayerIMA:" + useExoplayerIMA
|
println "useExoplayerIMA:" + useExoplayerIMA
|
||||||
|
|
||||||
@ -58,13 +54,13 @@ if (isNewArchitectureEnabled()) {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
if (supportsNamespace()) {
|
if (supportsNamespace()) {
|
||||||
namespace 'com.brentvatne.react'
|
namespace 'com.brentvatne.react'
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileSdkVersion safeExtGet('compileSdkVersion')
|
compileSdkVersion safeExtGet('compileSdkVersion')
|
||||||
@ -72,14 +68,14 @@ android {
|
|||||||
|
|
||||||
def agpVersion = Version.ANDROID_GRADLE_PLUGIN_VERSION
|
def agpVersion = Version.ANDROID_GRADLE_PLUGIN_VERSION
|
||||||
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
|
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
@ -119,12 +115,12 @@ android {
|
|||||||
java {
|
java {
|
||||||
if (isNewArchitectureEnabled()) {
|
if (isNewArchitectureEnabled()) {
|
||||||
srcDirs += [
|
srcDirs += [
|
||||||
"src/fabric/java",
|
"src/fabric/java",
|
||||||
"${project.buildDir}/generated/source/codegen/java"
|
"${project.buildDir}/generated/source/codegen/java"
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
srcDirs += [
|
srcDirs += [
|
||||||
"src/oldarch/java"
|
"src/oldarch/java"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,6 +141,7 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def media3_version = safeExtGet('media3Version')
|
||||||
def kotlin_version = safeExtGet('kotlinVersion')
|
def kotlin_version = safeExtGet('kotlinVersion')
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -152,22 +149,36 @@ dependencies {
|
|||||||
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
||||||
//noinspection GradleDynamicVersion
|
//noinspection GradleDynamicVersion
|
||||||
implementation "com.facebook.react:react-native:+"
|
implementation "com.facebook.react:react-native:+"
|
||||||
implementation('com.google.android.exoplayer:exoplayer:2.18.1') {
|
|
||||||
exclude group: 'com.android.support'
|
|
||||||
}
|
|
||||||
|
|
||||||
implementation "androidx.annotation:annotation:1.7.0"
|
|
||||||
implementation "androidx.core:core:1.9.0"
|
implementation "androidx.core:core:1.9.0"
|
||||||
implementation "androidx.media:media:1.6.0"
|
|
||||||
implementation "androidx.activity:activity:1.6.0"
|
|
||||||
|
|
||||||
implementation('com.google.android.exoplayer:extension-okhttp:2.18.1') {
|
// For media playback using ExoPlayer
|
||||||
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
|
implementation "androidx.media3:media3-exoplayer:$media3_version"
|
||||||
}
|
|
||||||
|
|
||||||
|
// For Smooth Streaming playback support with ExoPlayer
|
||||||
|
implementation "androidx.media3:media3-exoplayer-smoothstreaming:$media3_version"
|
||||||
|
// For DASH playback support with ExoPlayer
|
||||||
|
implementation "androidx.media3:media3-exoplayer-dash:$media3_version"
|
||||||
|
// For HLS playback support with ExoPlayer
|
||||||
|
implementation "androidx.media3:media3-exoplayer-hls:$media3_version"
|
||||||
|
// For ad insertion using the Interactive Media Ads SDK with ExoPlayer
|
||||||
if (useExoplayerIMA) {
|
if (useExoplayerIMA) {
|
||||||
implementation 'com.google.android.exoplayer:extension-ima:2.18.1'
|
implementation "androidx.media3:media3-exoplayer-ima:$media3_version"
|
||||||
}
|
}
|
||||||
implementation "com.squareup.okhttp3:okhttp:" + '$OKHTTP_VERSION'
|
|
||||||
|
// For loading data using the OkHttp network stack
|
||||||
|
implementation "androidx.media3:media3-datasource-okhttp:$media3_version"
|
||||||
|
|
||||||
|
// For building media playback UIs
|
||||||
|
implementation "androidx.media3:media3-ui:$media3_version"
|
||||||
|
|
||||||
|
// For exposing and controlling media sessions
|
||||||
|
implementation "androidx.media3:media3-session:$media3_version"
|
||||||
|
|
||||||
|
// Common functionality for loading data
|
||||||
|
implementation "androidx.media3:media3-datasource:$media3_version"
|
||||||
|
// Common functionality used across multiple media libraries
|
||||||
|
implementation "androidx.media3:media3-common:$media3_version"
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
}
|
}
|
||||||
|
@ -4,3 +4,5 @@ RNVideo_targetSdkVersion=31
|
|||||||
RNVideo_compileSdkVersion=31
|
RNVideo_compileSdkVersion=31
|
||||||
RNVideo_ndkversion=21.4.7075529
|
RNVideo_ndkversion=21.4.7075529
|
||||||
RNVideo_buildToolsVersion=30.0.2
|
RNVideo_buildToolsVersion=30.0.2
|
||||||
|
RNVideo_media3Version=1.1.1
|
||||||
|
RNVideo_RNVUseExoplayerIMA=false
|
@ -1,22 +0,0 @@
|
|||||||
package com.brentvatne;
|
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file define static helpers to parse in an easier way input props
|
|
||||||
*/
|
|
||||||
public class ReactBridgeUtils {
|
|
||||||
/*
|
|
||||||
retrieve key from map as int. fallback is returned if not available
|
|
||||||
*/
|
|
||||||
static public int safeGetInt(ReadableMap map, String key, int fallback) {
|
|
||||||
return map != null && map.hasKey(key) && !map.isNull(key) ? map.getInt(key) : fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
retrieve key from map as double. fallback is returned if not available
|
|
||||||
*/
|
|
||||||
static public double safeGetDouble(ReadableMap map, String key, double fallback) {
|
|
||||||
return map != null && map.hasKey(key) && !map.isNull(key) ? map.getDouble(key) : fallback;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
package com.brentvatne.common.API
|
package com.brentvatne.common.API
|
||||||
|
|
||||||
import com.brentvatne.ReactBridgeUtils
|
import com.brentvatne.common.toolbox.ReactBridgeUtils
|
||||||
import com.facebook.react.bridge.ReadableMap
|
import com.facebook.react.bridge.ReadableMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.brentvatne.common.react;
|
package com.brentvatne.common.react;
|
||||||
|
|
||||||
import androidx.annotation.StringDef;
|
import androidx.annotation.StringDef;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.brentvatne.common.API.TimedMetadata;
|
import com.brentvatne.common.API.TimedMetadata;
|
||||||
@ -12,10 +13,10 @@ import com.facebook.react.bridge.WritableArray;
|
|||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class VideoEventEmitter {
|
public class VideoEventEmitter {
|
||||||
@ -124,8 +125,6 @@ public class VideoEventEmitter {
|
|||||||
private static final String EVENT_PROP_STEP_FORWARD = "canStepForward";
|
private static final String EVENT_PROP_STEP_FORWARD = "canStepForward";
|
||||||
private static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward";
|
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_DURATION = "duration";
|
||||||
private static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration";
|
private static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration";
|
||||||
private static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration";
|
private static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration";
|
||||||
@ -241,8 +240,7 @@ public class VideoEventEmitter {
|
|||||||
load( duration, currentPosition, videoWidth, videoHeight, waAudioTracks, waTextTracks, waVideoTracks, trackId);
|
load( duration, currentPosition, videoWidth, videoHeight, waAudioTracks, waTextTracks, waVideoTracks, trackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void load(double duration, double currentPosition, int videoWidth, int videoHeight,
|
||||||
public void load(double duration, double currentPosition, int videoWidth, int videoHeight,
|
|
||||||
WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks, String trackId) {
|
WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks, String trackId) {
|
||||||
WritableMap event = Arguments.createMap();
|
WritableMap event = Arguments.createMap();
|
||||||
event.putDouble(EVENT_PROP_DURATION, duration / 1000D);
|
event.putDouble(EVENT_PROP_DURATION, duration / 1000D);
|
||||||
@ -267,8 +265,6 @@ public class VideoEventEmitter {
|
|||||||
receiveEvent(EVENT_LOAD, event);
|
receiveEvent(EVENT_LOAD, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WritableMap arrayToObject(String field, WritableArray array) {
|
WritableMap arrayToObject(String field, WritableArray array) {
|
||||||
WritableMap event = Arguments.createMap();
|
WritableMap event = Arguments.createMap();
|
||||||
event.putArray(field, array);
|
event.putArray(field, array);
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import com.google.android.exoplayer2.C;
|
|
||||||
|
import androidx.media3.common.C;
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
public enum AudioOutput {
|
public enum AudioOutput {
|
||||||
SPEAKER("speaker", C.STREAM_TYPE_MUSIC),
|
SPEAKER("speaker", C.STREAM_TYPE_MUSIC),
|
||||||
EARPIECE("earpiece", C.STREAM_TYPE_VOICE_CALL);
|
EARPIECE("earpiece", C.STREAM_TYPE_VOICE_CALL);
|
||||||
|
|
||||||
private final int streamType;
|
private final @C.StreamType int streamType;
|
||||||
private final String mName;
|
private final String mName;
|
||||||
|
|
||||||
AudioOutput(final String name, int stream) {
|
AudioOutput(final String name, @C.StreamType int stream) {
|
||||||
mName = name;
|
mName = name;
|
||||||
streamType = stream;
|
streamType = stream;
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.datasource.DataSource;
|
||||||
|
import androidx.media3.datasource.DefaultDataSource;
|
||||||
|
import androidx.media3.datasource.HttpDataSource;
|
||||||
|
import androidx.media3.datasource.okhttp.OkHttpDataSource;
|
||||||
|
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReactContext;
|
import com.facebook.react.bridge.ReactContext;
|
||||||
import com.facebook.react.modules.network.CookieJarContainer;
|
import com.facebook.react.modules.network.CookieJarContainer;
|
||||||
import com.facebook.react.modules.network.ForwardingCookieHandler;
|
import com.facebook.react.modules.network.ForwardingCookieHandler;
|
||||||
import com.facebook.react.modules.network.OkHttpClientProvider;
|
import com.facebook.react.modules.network.OkHttpClientProvider;
|
||||||
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource;
|
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import java.util.Map;
|
||||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
|
||||||
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.Call;
|
||||||
import okhttp3.JavaNetCookieJar;
|
import okhttp3.JavaNetCookieJar;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class DataSourceUtil {
|
public class DataSourceUtil {
|
||||||
|
|
||||||
@ -76,8 +78,7 @@ public class DataSourceUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
|
private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
|
||||||
return new DefaultDataSource.Factory(context,
|
return new DefaultDataSource.Factory(context, buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders));
|
||||||
buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpDataSource.Factory buildHttpDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
|
private static HttpDataSource.Factory buildHttpDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
|
||||||
|
@ -2,9 +2,9 @@ package com.brentvatne.exoplayer;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
|
import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy;
|
||||||
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||||
|
|
||||||
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {
|
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {
|
||||||
|
|
||||||
|
@ -2,6 +2,20 @@ package com.brentvatne.exoplayer;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.media3.common.AdViewProvider;
|
||||||
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.PlaybackException;
|
||||||
|
import androidx.media3.common.PlaybackParameters;
|
||||||
|
import androidx.media3.common.Player;
|
||||||
|
import androidx.media3.common.Timeline;
|
||||||
|
import androidx.media3.common.Tracks;
|
||||||
|
import androidx.media3.common.VideoSize;
|
||||||
|
import androidx.media3.common.text.Cue;
|
||||||
|
import androidx.media3.common.util.Assertions;
|
||||||
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
|
||||||
|
import androidx.media3.ui.SubtitleView;
|
||||||
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
@ -13,19 +27,6 @@ import android.widget.FrameLayout;
|
|||||||
|
|
||||||
import com.brentvatne.common.API.ResizeMode;
|
import com.brentvatne.common.API.ResizeMode;
|
||||||
import com.brentvatne.common.API.SubtitleStyle;
|
import com.brentvatne.common.API.SubtitleStyle;
|
||||||
import com.google.android.exoplayer2.C;
|
|
||||||
import com.google.android.exoplayer2.PlaybackException;
|
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
|
||||||
import com.google.android.exoplayer2.Player;
|
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
|
||||||
import com.google.android.exoplayer2.Tracks;
|
|
||||||
import com.google.android.exoplayer2.text.Cue;
|
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
|
||||||
import com.google.android.exoplayer2.ui.AdViewProvider;
|
|
||||||
import com.google.android.exoplayer2.ui.SubtitleView;
|
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
|
||||||
import com.google.android.exoplayer2.video.VideoSize;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -106,6 +107,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
|
|||||||
player.setVideoSurfaceView((SurfaceView) surfaceView);
|
player.setVideoSurfaceView((SurfaceView) surfaceView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSubtitleStyle(SubtitleStyle style) {
|
public void setSubtitleStyle(SubtitleStyle style) {
|
||||||
// ensure we reset subtile style before reapplying it
|
// ensure we reset subtile style before reapplying it
|
||||||
subtitleLayout.setUserDefaultStyle();
|
subtitleLayout.setUserDefaultStyle();
|
||||||
@ -154,7 +156,7 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
|
|||||||
post(measureAndLayout);
|
post(measureAndLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdsLoader.AdViewProvider implementation.
|
// AdsLoader.AdViewProvider implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ViewGroup getAdViewGroup() {
|
public ViewGroup getAdViewGroup() {
|
||||||
@ -194,7 +196,6 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
|
|||||||
layout.setResizeMode(resizeMode);
|
layout.setResizeMode(resizeMode);
|
||||||
post(measureAndLayout);
|
post(measureAndLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,14 +227,11 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
|
|||||||
updateShutterViewVisibility();
|
updateShutterViewVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Runnable measureAndLayout = new Runnable() {
|
private final Runnable measureAndLayout = () -> {
|
||||||
@Override
|
measure(
|
||||||
public void run() {
|
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
|
||||||
measure(
|
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
|
||||||
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
|
layout(getLeft(), getTop(), getRight(), getBottom());
|
||||||
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
|
|
||||||
layout(getLeft(), getTop(), getRight(), getBottom());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private void updateForCurrentTrackSelections() {
|
private void updateForCurrentTrackSelections() {
|
||||||
@ -259,15 +257,11 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
|
|||||||
|
|
||||||
private final class ComponentListener implements Player.Listener {
|
private final class ComponentListener implements Player.Listener {
|
||||||
|
|
||||||
// TextRenderer.Output implementation
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCues(List<Cue> cues) {
|
public void onCues(List<Cue> cues) {
|
||||||
subtitleLayout.setCues(cues);
|
subtitleLayout.setCues(cues);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExoPlayer.VideoListener implementation
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onVideoSizeChanged(VideoSize videoSize) {
|
public void onVideoSizeChanged(VideoSize videoSize) {
|
||||||
boolean isInitialRatio = layout.getAspectRatio() == 0;
|
boolean isInitialRatio = layout.getAspectRatio() == 0;
|
||||||
@ -284,8 +278,6 @@ public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
|
|||||||
shutterView.setVisibility(INVISIBLE);
|
shutterView.setVisibility(INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExoPlayer.EventListener implementation
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIsLoadingChanged(boolean isLoading) {
|
public void onIsLoadingChanged(boolean isLoading) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
@ -7,74 +7,73 @@ import android.widget.FrameLayout;
|
|||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
|
import androidx.media3.ui.LegacyPlayerControlView;
|
||||||
import com.google.android.exoplayer2.ui.PlayerControlView;
|
|
||||||
|
|
||||||
public class FullScreenPlayerView extends Dialog {
|
public class FullScreenPlayerView extends Dialog {
|
||||||
private final PlayerControlView playerControlView;
|
private final LegacyPlayerControlView playerControlView;
|
||||||
private final ExoPlayerView exoPlayerView;
|
private final ExoPlayerView exoPlayerView;
|
||||||
private ViewGroup parent;
|
private ViewGroup parent;
|
||||||
private final FrameLayout containerView;
|
private final FrameLayout containerView;
|
||||||
private final OnBackPressedCallback onBackPressedCallback;
|
private final OnBackPressedCallback onBackPressedCallback;
|
||||||
|
|
||||||
public FullScreenPlayerView(Context context, ExoPlayerView exoPlayerView, PlayerControlView playerControlView, OnBackPressedCallback onBackPressedCallback) {
|
public FullScreenPlayerView(Context context, ExoPlayerView exoPlayerView, LegacyPlayerControlView playerControlView, OnBackPressedCallback onBackPressedCallback) {
|
||||||
super(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
|
super(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
|
||||||
this.playerControlView = playerControlView;
|
this.playerControlView = playerControlView;
|
||||||
this.exoPlayerView = exoPlayerView;
|
this.exoPlayerView = exoPlayerView;
|
||||||
this.onBackPressedCallback = onBackPressedCallback;
|
this.onBackPressedCallback = onBackPressedCallback;
|
||||||
containerView = new FrameLayout(context);
|
containerView = new FrameLayout(context);
|
||||||
setContentView(containerView, generateDefaultLayoutParams());
|
setContentView(containerView, generateDefaultLayoutParams());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
super.onBackPressed();
|
|
||||||
onBackPressedCallback.handleOnBackPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
parent = (FrameLayout)(exoPlayerView.getParent());
|
|
||||||
|
|
||||||
parent.removeView(exoPlayerView);
|
|
||||||
containerView.addView(exoPlayerView, generateDefaultLayoutParams());
|
|
||||||
|
|
||||||
if (playerControlView != null) {
|
|
||||||
ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
|
|
||||||
imageButton.setImageResource(com.google.android.exoplayer2.ui.R.drawable.exo_icon_fullscreen_exit);
|
|
||||||
imageButton.setContentDescription(getContext().getString(com.google.android.exoplayer2.ui.R.string.exo_controls_fullscreen_exit_description));
|
|
||||||
parent.removeView(playerControlView);
|
|
||||||
containerView.addView(playerControlView, generateDefaultLayoutParams());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onStart();
|
@Override
|
||||||
}
|
public void onBackPressed() {
|
||||||
|
super.onBackPressed();
|
||||||
@Override
|
onBackPressedCallback.handleOnBackPressed();
|
||||||
protected void onStop() {
|
|
||||||
containerView.removeView(exoPlayerView);
|
|
||||||
parent.addView(exoPlayerView, generateDefaultLayoutParams());
|
|
||||||
|
|
||||||
if (playerControlView != null) {
|
|
||||||
ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
|
|
||||||
imageButton.setImageResource(com.google.android.exoplayer2.ui.R.drawable.exo_icon_fullscreen_enter);
|
|
||||||
imageButton.setContentDescription(getContext().getString(com.google.android.exoplayer2.ui.R.string.exo_controls_fullscreen_enter_description));
|
|
||||||
containerView.removeView(playerControlView);
|
|
||||||
parent.addView(playerControlView, generateDefaultLayoutParams());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.requestLayout();
|
@Override
|
||||||
parent = null;
|
protected void onStart() {
|
||||||
|
parent = (FrameLayout)(exoPlayerView.getParent());
|
||||||
|
|
||||||
super.onStop();
|
parent.removeView(exoPlayerView);
|
||||||
}
|
containerView.addView(exoPlayerView, generateDefaultLayoutParams());
|
||||||
|
|
||||||
private FrameLayout.LayoutParams generateDefaultLayoutParams() {
|
if (playerControlView != null) {
|
||||||
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
|
ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
imageButton.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_exit);
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT
|
imageButton.setContentDescription(getContext().getString(androidx.media3.ui.R.string.exo_controls_fullscreen_exit_description));
|
||||||
);
|
parent.removeView(playerControlView);
|
||||||
layoutParams.setMargins(0, 0, 0, 0);
|
containerView.addView(playerControlView, generateDefaultLayoutParams());
|
||||||
return layoutParams;
|
}
|
||||||
}
|
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
containerView.removeView(exoPlayerView);
|
||||||
|
parent.addView(exoPlayerView, generateDefaultLayoutParams());
|
||||||
|
|
||||||
|
if (playerControlView != null) {
|
||||||
|
ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
|
||||||
|
imageButton.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter);
|
||||||
|
imageButton.setContentDescription(getContext().getString(androidx.media3.ui.R.string.exo_controls_fullscreen_enter_description));
|
||||||
|
containerView.removeView(playerControlView);
|
||||||
|
parent.addView(playerControlView, generateDefaultLayoutParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.requestLayout();
|
||||||
|
parent = null;
|
||||||
|
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private FrameLayout.LayoutParams generateDefaultLayoutParams() {
|
||||||
|
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
FrameLayout.LayoutParams.MATCH_PARENT
|
||||||
|
);
|
||||||
|
layoutParams.setMargins(0, 0, 0, 0);
|
||||||
|
return layoutParams;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package com.brentvatne.exoplayer;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.RawResourceDataSource;
|
import androidx.media3.datasource.RawResourceDataSource;
|
||||||
|
|
||||||
class RawResourceDataSourceFactory implements DataSource.Factory {
|
class RawResourceDataSourceFactory implements DataSource.Factory {
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension points to configure the Exoplayer instance
|
* Extension points to configure the Exoplayer instance
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
|
import androidx.media3.common.C;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
|
import androidx.media3.datasource.HttpDataSource.HttpDataSourceException;
|
||||||
import com.google.android.exoplayer2.C;
|
import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy;
|
||||||
|
|
||||||
public final class ReactExoplayerLoadErrorHandlingPolicy extends DefaultLoadErrorHandlingPolicy {
|
public final class ReactExoplayerLoadErrorHandlingPolicy extends DefaultLoadErrorHandlingPolicy {
|
||||||
private final int minLoadRetryCount;
|
private final int minLoadRetryCount;
|
||||||
|
|
||||||
public ReactExoplayerLoadErrorHandlingPolicy(int minLoadRetryCount) {
|
public ReactExoplayerLoadErrorHandlingPolicy(int minLoadRetryCount) {
|
||||||
super(minLoadRetryCount);
|
super(minLoadRetryCount);
|
||||||
this.minLoadRetryCount = minLoadRetryCount;
|
this.minLoadRetryCount = minLoadRetryCount;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
|
|
||||||
String errorMessage = loadErrorInfo.exception.getMessage();
|
|
||||||
|
|
||||||
if (
|
|
||||||
loadErrorInfo.exception instanceof HttpDataSourceException &&
|
|
||||||
errorMessage != null && (errorMessage.equals("Unable to connect") || errorMessage.equals("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
|
@Override
|
||||||
public int getMinimumLoadableRetryCount(int dataType) {
|
public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
|
||||||
return Integer.MAX_VALUE;
|
String errorMessage = loadErrorInfo.exception.getMessage();
|
||||||
}
|
|
||||||
|
if (
|
||||||
|
loadErrorInfo.exception instanceof HttpDataSourceException &&
|
||||||
|
errorMessage != null && (errorMessage.equals("Unable to connect") || errorMessage.equals("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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
import static com.google.android.exoplayer2.C.CONTENT_TYPE_DASH;
|
import static androidx.media3.common.C.CONTENT_TYPE_DASH;
|
||||||
import static com.google.android.exoplayer2.C.CONTENT_TYPE_HLS;
|
import static androidx.media3.common.C.CONTENT_TYPE_HLS;
|
||||||
import static com.google.android.exoplayer2.C.CONTENT_TYPE_OTHER;
|
import static androidx.media3.common.C.CONTENT_TYPE_OTHER;
|
||||||
import static com.google.android.exoplayer2.C.CONTENT_TYPE_SS;
|
import static androidx.media3.common.C.CONTENT_TYPE_SS;
|
||||||
import static com.google.android.exoplayer2.C.TIME_END_OF_SOURCE;
|
import static androidx.media3.common.C.TIME_END_OF_SOURCE;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@ -22,9 +22,70 @@ import android.view.accessibility.CaptioningManager;
|
|||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.media3.common.AudioAttributes;
|
||||||
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.Metadata;
|
||||||
|
import androidx.media3.common.PlaybackException;
|
||||||
|
import androidx.media3.common.PlaybackParameters;
|
||||||
|
import androidx.media3.common.Player;
|
||||||
|
import androidx.media3.common.StreamKey;
|
||||||
|
import androidx.media3.common.Timeline;
|
||||||
|
import androidx.media3.common.TrackGroup;
|
||||||
|
import androidx.media3.common.TrackSelectionOverride;
|
||||||
|
import androidx.media3.common.Tracks;
|
||||||
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.datasource.DataSource;
|
||||||
|
import androidx.media3.datasource.DataSpec;
|
||||||
|
import androidx.media3.datasource.HttpDataSource;
|
||||||
|
import androidx.media3.exoplayer.DefaultLoadControl;
|
||||||
|
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
||||||
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
|
import androidx.media3.exoplayer.dash.DashMediaSource;
|
||||||
|
import androidx.media3.exoplayer.dash.DashUtil;
|
||||||
|
import androidx.media3.exoplayer.dash.DefaultDashChunkSource;
|
||||||
|
import androidx.media3.exoplayer.dash.manifest.AdaptationSet;
|
||||||
|
import androidx.media3.exoplayer.dash.manifest.DashManifest;
|
||||||
|
import androidx.media3.exoplayer.dash.manifest.Period;
|
||||||
|
import androidx.media3.exoplayer.dash.manifest.Representation;
|
||||||
|
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager;
|
||||||
|
import androidx.media3.exoplayer.drm.DefaultDrmSessionManagerProvider;
|
||||||
|
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
||||||
|
import androidx.media3.exoplayer.drm.DrmSessionManager;
|
||||||
|
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
|
||||||
|
import androidx.media3.exoplayer.drm.FrameworkMediaDrm;
|
||||||
|
import androidx.media3.exoplayer.drm.HttpMediaDrmCallback;
|
||||||
|
import androidx.media3.exoplayer.drm.UnsupportedDrmException;
|
||||||
|
import androidx.media3.exoplayer.hls.HlsMediaSource;
|
||||||
|
import androidx.media3.exoplayer.mediacodec.MediaCodecInfo;
|
||||||
|
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
|
||||||
|
import androidx.media3.exoplayer.smoothstreaming.DefaultSsChunkSource;
|
||||||
|
import androidx.media3.exoplayer.smoothstreaming.SsMediaSource;
|
||||||
|
import androidx.media3.exoplayer.source.ClippingMediaSource;
|
||||||
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
|
import androidx.media3.exoplayer.source.MergingMediaSource;
|
||||||
|
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
|
||||||
|
import androidx.media3.exoplayer.source.SingleSampleMediaSource;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
|
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
|
||||||
|
import androidx.media3.exoplayer.trackselection.AdaptiveTrackSelection;
|
||||||
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
||||||
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
|
import androidx.media3.exoplayer.trackselection.MappingTrackSelector;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelection;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
|
||||||
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
|
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
|
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
|
import androidx.media3.extractor.metadata.emsg.EventMessage;
|
||||||
|
import androidx.media3.extractor.metadata.id3.Id3Frame;
|
||||||
|
import androidx.media3.extractor.metadata.id3.TextInformationFrame;
|
||||||
|
import androidx.media3.ui.LegacyPlayerControlView;
|
||||||
|
|
||||||
import com.brentvatne.common.API.ResizeMode;
|
import com.brentvatne.common.API.ResizeMode;
|
||||||
import com.brentvatne.common.API.SubtitleStyle;
|
import com.brentvatne.common.API.SubtitleStyle;
|
||||||
@ -42,86 +103,23 @@ import com.facebook.react.bridge.ReadableArray;
|
|||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.google.ads.interactivemedia.v3.api.AdEvent;
|
import com.google.ads.interactivemedia.v3.api.AdEvent;
|
||||||
import com.google.android.exoplayer2.C;
|
|
||||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
|
||||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
|
||||||
import com.google.android.exoplayer2.Format;
|
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
|
||||||
import com.google.android.exoplayer2.PlaybackException;
|
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
|
||||||
import com.google.android.exoplayer2.Player;
|
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
|
||||||
import com.google.android.exoplayer2.Tracks;
|
|
||||||
import com.google.android.exoplayer2.audio.AudioAttributes;
|
|
||||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
|
|
||||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManagerProvider;
|
|
||||||
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
|
|
||||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
|
||||||
import com.google.android.exoplayer2.drm.DrmSessionManagerProvider;
|
|
||||||
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
|
|
||||||
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
|
|
||||||
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
|
|
||||||
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
|
|
||||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
|
||||||
import com.google.android.exoplayer2.metadata.Metadata;
|
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.MergingMediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.TrackGroup;
|
|
||||||
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.MappingTrackSelector;
|
|
||||||
import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
|
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionOverride;
|
|
||||||
import com.google.android.exoplayer2.ui.PlayerControlView;
|
|
||||||
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultAllocator;
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
|
||||||
import com.google.android.exoplayer2.util.Util;
|
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
|
||||||
import com.google.android.exoplayer2.source.dash.DashUtil;
|
|
||||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
|
||||||
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
|
||||||
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
|
|
||||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
|
||||||
import com.google.android.exoplayer2.metadata.Metadata;
|
|
||||||
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
|
|
||||||
import com.google.android.exoplayer2.metadata.id3.Id3Frame;
|
|
||||||
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer2.ext.ima.ImaAdsLoader;
|
import com.google.android.exoplayer2.ext.ima.ImaAdsLoader;
|
||||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
|
||||||
import com.google.android.exoplayer2.source.ClippingMediaSource;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.net.CookieHandler;
|
import java.net.CookieHandler;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
import java.net.CookiePolicy;
|
import java.net.CookiePolicy;
|
||||||
import java.util.ArrayList;
|
import java.lang.Math;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.Map;
|
import java.util.concurrent.Callable;
|
||||||
import java.lang.Thread;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.lang.Integer;
|
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
public class ReactExoplayerView extends FrameLayout implements
|
public class ReactExoplayerView extends FrameLayout implements
|
||||||
@ -149,7 +147,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
private final VideoEventEmitter eventEmitter;
|
private final VideoEventEmitter eventEmitter;
|
||||||
private final ReactExoplayerConfig config;
|
private final ReactExoplayerConfig config;
|
||||||
private final DefaultBandwidthMeter bandwidthMeter;
|
private final DefaultBandwidthMeter bandwidthMeter;
|
||||||
private PlayerControlView playerControlView;
|
private LegacyPlayerControlView playerControlView;
|
||||||
private View playPauseControlContainer;
|
private View playPauseControlContainer;
|
||||||
private Player.Listener eventListener;
|
private Player.Listener eventListener;
|
||||||
|
|
||||||
@ -189,6 +187,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
|
private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
|
||||||
private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
|
private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
|
||||||
private Handler mainHandler;
|
private Handler mainHandler;
|
||||||
|
private Runnable mainRunnable;
|
||||||
|
|
||||||
// Props from React
|
// Props from React
|
||||||
private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
|
private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
|
||||||
@ -330,7 +329,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LifecycleEventListener implementation
|
// LifecycleEventListener implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHostResume() {
|
public void onHostResume() {
|
||||||
if (!playInBackground || !isInBackground) {
|
if (!playInBackground || !isInBackground) {
|
||||||
@ -379,7 +377,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
* Toggling the visibility of the player control view
|
* Toggling the visibility of the player control view
|
||||||
*/
|
*/
|
||||||
private void togglePlayerControlVisibility() {
|
private void togglePlayerControlVisibility() {
|
||||||
if(player == null) return;
|
if (player == null) return;
|
||||||
reLayout(playerControlView);
|
reLayout(playerControlView);
|
||||||
if (playerControlView.isVisible()) {
|
if (playerControlView.isVisible()) {
|
||||||
playerControlView.hide();
|
playerControlView.hide();
|
||||||
@ -393,7 +391,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
*/
|
*/
|
||||||
private void initializePlayerControl() {
|
private void initializePlayerControl() {
|
||||||
if (playerControlView == null) {
|
if (playerControlView == null) {
|
||||||
playerControlView = new PlayerControlView(getContext());
|
playerControlView = new LegacyPlayerControlView(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fullScreenPlayerView == null) {
|
if (fullScreenPlayerView == null) {
|
||||||
@ -410,34 +408,25 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
|
playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
|
||||||
|
|
||||||
// Invoking onClick event for exoplayerView
|
// Invoking onClick event for exoplayerView
|
||||||
exoPlayerView.setOnClickListener(new OnClickListener() {
|
exoPlayerView.setOnClickListener((View v) -> {
|
||||||
@Override
|
if (!isPlayingAd()) {
|
||||||
public void onClick(View v) {
|
togglePlayerControlVisibility();
|
||||||
if (!isPlayingAd()) {
|
|
||||||
togglePlayerControlVisibility();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Handling the playButton click event
|
//Handling the playButton click event
|
||||||
ImageButton playButton = playerControlView.findViewById(R.id.exo_play);
|
ImageButton playButton = playerControlView.findViewById(R.id.exo_play);
|
||||||
playButton.setOnClickListener(new View.OnClickListener() {
|
playButton.setOnClickListener((View v) -> {
|
||||||
@Override
|
if (player != null && player.getPlaybackState() == Player.STATE_ENDED) {
|
||||||
public void onClick(View v) {
|
player.seekTo(0);
|
||||||
if (player != null && player.getPlaybackState() == Player.STATE_ENDED) {
|
|
||||||
player.seekTo(0);
|
|
||||||
}
|
|
||||||
setPausedModifier(false);
|
|
||||||
}
|
}
|
||||||
|
setPausedModifier(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Handling the pauseButton click event
|
//Handling the pauseButton click event
|
||||||
ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause);
|
ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause);
|
||||||
pauseButton.setOnClickListener(new View.OnClickListener() {
|
pauseButton.setOnClickListener((View v) -> {
|
||||||
@Override
|
setPausedModifier(true);
|
||||||
public void onClick(View v) {
|
|
||||||
setPausedModifier(true);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Handling the fullScreenButton click event
|
//Handling the fullScreenButton click event
|
||||||
@ -476,7 +465,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
* Adding Player control to the frame layout
|
* Adding Player control to the frame layout
|
||||||
*/
|
*/
|
||||||
private void addPlayerControl() {
|
private void addPlayerControl() {
|
||||||
if(playerControlView == null) return;
|
if (playerControlView == null) return;
|
||||||
LayoutParams layoutParams = new LayoutParams(
|
LayoutParams layoutParams = new LayoutParams(
|
||||||
LayoutParams.MATCH_PARENT,
|
LayoutParams.MATCH_PARENT,
|
||||||
LayoutParams.MATCH_PARENT);
|
LayoutParams.MATCH_PARENT);
|
||||||
@ -551,64 +540,56 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
ReactExoplayerView self = this;
|
ReactExoplayerView self = this;
|
||||||
Activity activity = themedReactContext.getCurrentActivity();
|
Activity activity = themedReactContext.getCurrentActivity();
|
||||||
// This ensures all props have been settled, to avoid async racing conditions.
|
// This ensures all props have been settled, to avoid async racing conditions.
|
||||||
new Handler().postDelayed(new Runnable() {
|
mainRunnable = () -> {
|
||||||
@Override
|
try {
|
||||||
public void run() {
|
if (player == null) {
|
||||||
try {
|
// Initialize core configuration and listeners
|
||||||
if (player == null) {
|
initializePlayerCore(self);
|
||||||
// Initialize core configuration and listeners
|
}
|
||||||
initializePlayerCore(self);
|
if (playerNeedsSource && srcUri != null) {
|
||||||
}
|
exoPlayerView.invalidateAspectRatio();
|
||||||
if (playerNeedsSource && srcUri != null) {
|
// DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread
|
||||||
exoPlayerView.invalidateAspectRatio();
|
ExecutorService es = Executors.newSingleThreadExecutor();
|
||||||
// DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread
|
es.execute(() -> {
|
||||||
ExecutorService es = Executors.newSingleThreadExecutor();
|
// DRM initialization must run on a different thread
|
||||||
es.execute(new Runnable() {
|
DrmSessionManager drmSessionManager = initializePlayerDrm(self);
|
||||||
@Override
|
if (drmSessionManager == null && self.drmUUID != null) {
|
||||||
public void run() {
|
// Failed to intialize DRM session manager - cannot continue
|
||||||
// DRM initialization must run on a different thread
|
DebugLog.e("ExoPlayer Exception", "Failed to initialize DRM Session Manager Framework!");
|
||||||
DrmSessionManager drmSessionManager = initializePlayerDrm(self);
|
eventEmitter.error("Failed to initialize DRM Session Manager Framework!", new Exception("DRM Session Manager Framework failure!"), "3003");
|
||||||
if (drmSessionManager == null && self.drmUUID != null) {
|
return;
|
||||||
// Failed to intialize DRM session manager - cannot continue
|
}
|
||||||
DebugLog.e("ExoPlayer Exception", "Failed to initialize DRM Session Manager Framework!");
|
|
||||||
eventEmitter.error("Failed to initialize DRM Session Manager Framework!", new Exception("DRM Session Manager Framework failure!"), "3003");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activity == null) {
|
if (activity == null) {
|
||||||
DebugLog.e("ExoPlayer Exception", "Failed to initialize Player!");
|
DebugLog.e("ExoPlayer Exception", "Failed to initialize Player!");
|
||||||
eventEmitter.error("Failed to initialize Player!", new Exception("Current Activity is null!"), "1001");
|
eventEmitter.error("Failed to initialize Player!", new Exception("Current Activity is null!"), "1001");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize handler to run on the main thread
|
// Initialize handler to run on the main thread
|
||||||
activity.runOnUiThread(new Runnable() {
|
activity.runOnUiThread(() -> {
|
||||||
public void run() {
|
try {
|
||||||
try {
|
// Source initialization must run on the main thread
|
||||||
// Source initialization must run on the main thread
|
initializePlayerSource(self, drmSessionManager);
|
||||||
initializePlayerSource(self, drmSessionManager);
|
} catch (Exception ex) {
|
||||||
} catch (Exception ex) {
|
self.playerNeedsSource = true;
|
||||||
self.playerNeedsSource = true;
|
DebugLog.e("ExoPlayer Exception", "Failed to initialize Player!");
|
||||||
DebugLog.e("ExoPlayer Exception", "Failed to initialize Player!");
|
DebugLog.e("ExoPlayer Exception", ex.toString());
|
||||||
DebugLog.e("ExoPlayer Exception", ex.toString());
|
self.eventEmitter.error(ex.toString(), ex, "1001");
|
||||||
self.eventEmitter.error(ex.toString(), ex, "1001");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (srcUri != null) {
|
});
|
||||||
initializePlayerSource(self, null);
|
} else if (srcUri != null) {
|
||||||
}
|
initializePlayerSource(self, null);
|
||||||
} catch (Exception ex) {
|
|
||||||
self.playerNeedsSource = true;
|
|
||||||
DebugLog.e("ExoPlayer Exception", "Failed to initialize Player!");
|
|
||||||
DebugLog.e("ExoPlayer Exception", ex.toString());
|
|
||||||
eventEmitter.error(ex.toString(), ex, "1001");
|
|
||||||
}
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
self.playerNeedsSource = true;
|
||||||
|
DebugLog.e("ExoPlayer Exception", "Failed to initialize Player!");
|
||||||
|
DebugLog.e("ExoPlayer Exception", ex.toString());
|
||||||
|
eventEmitter.error(ex.toString(), ex, "1001");
|
||||||
}
|
}
|
||||||
}, 1);
|
};
|
||||||
|
mainHandler.postDelayed(mainRunnable, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializePlayerCore(ReactExoplayerView self) {
|
private void initializePlayerCore(ReactExoplayerView self) {
|
||||||
@ -637,14 +618,14 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
adsLoader = new ImaAdsLoader.Builder(themedReactContext).setAdEventListener(this).build();
|
adsLoader = new ImaAdsLoader.Builder(themedReactContext).setAdEventListener(this).build();
|
||||||
|
|
||||||
MediaSource.Factory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
|
MediaSource.Factory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
|
||||||
.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
|
.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
|
||||||
|
|
||||||
player = new ExoPlayer.Builder(getContext(), renderersFactory)
|
player = new ExoPlayer.Builder(getContext(), renderersFactory)
|
||||||
.setTrackSelector(self.trackSelector)
|
.setTrackSelector(self.trackSelector)
|
||||||
.setBandwidthMeter(bandwidthMeter)
|
.setBandwidthMeter(bandwidthMeter)
|
||||||
.setLoadControl(loadControl)
|
.setLoadControl(loadControl)
|
||||||
.setMediaSourceFactory(mediaSourceFactory)
|
.setMediaSourceFactory(mediaSourceFactory)
|
||||||
.build();
|
.build();
|
||||||
player.addListener(self);
|
player.addListener(self);
|
||||||
player.setVolume(muted ? 0.f : audioVolume * 1);
|
player.setVolume(muted ? 0.f : audioVolume * 1);
|
||||||
exoPlayerView.setPlayer(player);
|
exoPlayerView.setPlayer(player);
|
||||||
@ -759,8 +740,13 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
// When DRM fails using L1 we want to switch to L3
|
// When DRM fails using L1 we want to switch to L3
|
||||||
mediaDrm.setPropertyString("securityLevel", "L3");
|
mediaDrm.setPropertyString("securityLevel", "L3");
|
||||||
}
|
}
|
||||||
return new DefaultDrmSessionManager(uuid, mediaDrm, drmCallback, null, false, 3);
|
DefaultDrmSessionManager drmSessionManager = new DefaultDrmSessionManager.Builder()
|
||||||
} catch(UnsupportedDrmException ex) {
|
.setUuidAndExoMediaDrmProvider(uuid, (_uuid) -> mediaDrm)
|
||||||
|
.setKeyRequestParameters(null)
|
||||||
|
.setMultiSession(false)
|
||||||
|
.build(drmCallback);
|
||||||
|
return drmSessionManager;
|
||||||
|
} catch (UnsupportedDrmException ex) {
|
||||||
// Unsupported DRM exceptions are handled by the calling method
|
// Unsupported DRM exceptions are handled by the calling method
|
||||||
throw ex;
|
throw ex;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@ -790,61 +776,52 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaItem mediaItem = mediaItemBuilder.build();
|
MediaSource.Factory mediaSourceFactory;
|
||||||
MediaSource mediaSource;
|
|
||||||
DrmSessionManagerProvider drmProvider;
|
DrmSessionManagerProvider drmProvider;
|
||||||
|
List<StreamKey> streamKeys = new ArrayList();
|
||||||
if (drmSessionManager != null) {
|
if (drmSessionManager != null) {
|
||||||
drmProvider = new DrmSessionManagerProvider() {
|
drmProvider = ((_mediaItem) -> drmSessionManager);
|
||||||
@Override
|
|
||||||
public DrmSessionManager get(MediaItem mediaItem) {
|
|
||||||
return drmSessionManager;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
drmProvider = new DefaultDrmSessionManagerProvider();
|
drmProvider = new DefaultDrmSessionManagerProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CONTENT_TYPE_SS:
|
case CONTENT_TYPE_SS:
|
||||||
mediaSource = new SsMediaSource.Factory(
|
mediaSourceFactory = new SsMediaSource.Factory(
|
||||||
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
||||||
buildDataSourceFactory(false)
|
buildDataSourceFactory(false)
|
||||||
).setDrmSessionManagerProvider(drmProvider)
|
);
|
||||||
.setLoadErrorHandlingPolicy(
|
|
||||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
|
||||||
).createMediaSource(mediaItem);
|
|
||||||
break;
|
break;
|
||||||
case CONTENT_TYPE_DASH:
|
case CONTENT_TYPE_DASH:
|
||||||
mediaSource = new DashMediaSource.Factory(
|
mediaSourceFactory = new DashMediaSource.Factory(
|
||||||
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
||||||
buildDataSourceFactory(false)
|
buildDataSourceFactory(false)
|
||||||
).setDrmSessionManagerProvider(drmProvider)
|
);
|
||||||
.setLoadErrorHandlingPolicy(
|
|
||||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
|
||||||
).createMediaSource(mediaItem);
|
|
||||||
break;
|
break;
|
||||||
case CONTENT_TYPE_HLS:
|
case CONTENT_TYPE_HLS:
|
||||||
mediaSource = new HlsMediaSource.Factory(
|
mediaSourceFactory = new HlsMediaSource.Factory(
|
||||||
mediaDataSourceFactory
|
mediaDataSourceFactory
|
||||||
).setDrmSessionManagerProvider(drmProvider)
|
);
|
||||||
.setLoadErrorHandlingPolicy(
|
|
||||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
|
||||||
).createMediaSource(mediaItem);
|
|
||||||
break;
|
break;
|
||||||
case CONTENT_TYPE_OTHER:
|
case CONTENT_TYPE_OTHER:
|
||||||
mediaSource = new ProgressiveMediaSource.Factory(
|
mediaSourceFactory = new ProgressiveMediaSource.Factory(
|
||||||
mediaDataSourceFactory
|
mediaDataSourceFactory
|
||||||
).setDrmSessionManagerProvider(drmProvider)
|
);
|
||||||
.setLoadErrorHandlingPolicy(
|
|
||||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
|
||||||
).createMediaSource(mediaItem);
|
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
throw new IllegalStateException("Unsupported type: " + type);
|
throw new IllegalStateException("Unsupported type: " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startTimeMs >= 0 && endTimeMs >= 0)
|
MediaItem mediaItem = mediaItemBuilder.setStreamKeys(streamKeys).build();
|
||||||
{
|
MediaSource mediaSource = mediaSourceFactory
|
||||||
|
.setDrmSessionManagerProvider(drmProvider)
|
||||||
|
.setLoadErrorHandlingPolicy(
|
||||||
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||||
|
)
|
||||||
|
.createMediaSource(mediaItem);
|
||||||
|
|
||||||
|
if (startTimeMs >= 0 && endTimeMs >= 0) {
|
||||||
return new ClippingMediaSource(mediaSource, startTimeMs * 1000, endTimeMs * 1000);
|
return new ClippingMediaSource(mediaSource, startTimeMs * 1000, endTimeMs * 1000);
|
||||||
} else if (startTimeMs >= 0) {
|
} else if (startTimeMs >= 0) {
|
||||||
return new ClippingMediaSource(mediaSource, startTimeMs * 1000, TIME_END_OF_SOURCE);
|
return new ClippingMediaSource(mediaSource, startTimeMs * 1000, TIME_END_OF_SOURCE);
|
||||||
@ -869,7 +846,9 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
Uri uri = Uri.parse(textTrack.getString("uri"));
|
Uri uri = Uri.parse(textTrack.getString("uri"));
|
||||||
MediaSource textSource = buildTextSource(title, uri, textTrack.getString("type"),
|
MediaSource textSource = buildTextSource(title, uri, textTrack.getString("type"),
|
||||||
language);
|
language);
|
||||||
textSources.add(textSource);
|
if (textSource != null) {
|
||||||
|
textSources.add(textSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return textSources;
|
return textSources;
|
||||||
}
|
}
|
||||||
@ -905,6 +884,11 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
themedReactContext.removeLifecycleEventListener(this);
|
themedReactContext.removeLifecycleEventListener(this);
|
||||||
audioBecomingNoisyReceiver.removeListener();
|
audioBecomingNoisyReceiver.removeListener();
|
||||||
bandwidthMeter.removeEventListener(this);
|
bandwidthMeter.removeEventListener(this);
|
||||||
|
|
||||||
|
if (mainHandler != null && mainRunnable != null) {
|
||||||
|
mainHandler.removeCallbacks(mainRunnable);
|
||||||
|
mainRunnable = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class OnAudioFocusChangedListener implements AudioManager.OnAudioFocusChangeListener {
|
private static class OnAudioFocusChangedListener implements AudioManager.OnAudioFocusChangeListener {
|
||||||
@ -1065,16 +1049,13 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext, useBandwidthMeter ? bandwidthMeter : null, requestHeaders);
|
return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext, useBandwidthMeter ? bandwidthMeter : null, requestHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// AudioBecomingNoisyListener implementation
|
// AudioBecomingNoisyListener implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAudioBecomingNoisy() {
|
public void onAudioBecomingNoisy() {
|
||||||
eventEmitter.audioBecomingNoisy();
|
eventEmitter.audioBecomingNoisy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Player.Listener implementation
|
// Player.Listener implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIsLoadingChanged(boolean isLoading) {
|
public void onIsLoadingChanged(boolean isLoading) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
@ -1096,38 +1077,38 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
setKeepScreenOn(false);
|
setKeepScreenOn(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Player.STATE_BUFFERING:
|
case Player.STATE_BUFFERING:
|
||||||
text += "buffering";
|
text += "buffering";
|
||||||
onBuffering(true);
|
onBuffering(true);
|
||||||
clearProgressMessageHandler();
|
clearProgressMessageHandler();
|
||||||
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
|
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
|
||||||
break;
|
break;
|
||||||
case Player.STATE_READY:
|
case Player.STATE_READY:
|
||||||
text += "ready";
|
text += "ready";
|
||||||
eventEmitter.ready();
|
eventEmitter.ready();
|
||||||
onBuffering(false);
|
onBuffering(false);
|
||||||
clearProgressMessageHandler(); // ensure there is no other message
|
clearProgressMessageHandler(); // ensure there is no other message
|
||||||
startProgressHandler();
|
startProgressHandler();
|
||||||
videoLoaded();
|
videoLoaded();
|
||||||
if (selectTrackWhenReady && isUsingContentResolution) {
|
if (selectTrackWhenReady && isUsingContentResolution) {
|
||||||
selectTrackWhenReady = false;
|
selectTrackWhenReady = false;
|
||||||
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
|
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
|
||||||
}
|
}
|
||||||
// Setting the visibility for the playerControlView
|
// Setting the visibility for the playerControlView
|
||||||
if (playerControlView != null) {
|
if (playerControlView != null) {
|
||||||
playerControlView.show();
|
playerControlView.show();
|
||||||
}
|
}
|
||||||
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
|
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
|
||||||
break;
|
break;
|
||||||
case Player.STATE_ENDED:
|
case Player.STATE_ENDED:
|
||||||
text += "ended";
|
text += "ended";
|
||||||
eventEmitter.end();
|
eventEmitter.end();
|
||||||
onStopPlayback();
|
onStopPlayback();
|
||||||
setKeepScreenOn(false);
|
setKeepScreenOn(false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
text += "unknown";
|
text += "unknown";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DebugLog.d(TAG, text);
|
DebugLog.d(TAG, text);
|
||||||
}
|
}
|
||||||
@ -1137,13 +1118,13 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
progressHandler.sendEmptyMessage(SHOW_PROGRESS);
|
progressHandler.sendEmptyMessage(SHOW_PROGRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
The progress message handler will duplicate recursions of the onProgressMessage handler
|
* The progress message handler will duplicate recursions of the onProgressMessage handler
|
||||||
on change of player state from any state to STATE_READY with playWhenReady is true (when
|
* on change of player state from any state to STATE_READY with playWhenReady is true (when
|
||||||
the video is not paused). This clears all existing messages.
|
* the video is not paused). This clears all existing messages.
|
||||||
*/
|
*/
|
||||||
private void clearProgressMessageHandler() {
|
private void clearProgressMessageHandler() {
|
||||||
progressHandler.removeMessages(SHOW_PROGRESS);
|
progressHandler.removeMessages(SHOW_PROGRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void videoLoaded() {
|
private void videoLoaded() {
|
||||||
@ -1171,18 +1152,15 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
if (this.contentStartTime != -1L) {
|
if (this.contentStartTime != -1L) {
|
||||||
ExecutorService es = Executors.newSingleThreadExecutor();
|
ExecutorService es = Executors.newSingleThreadExecutor();
|
||||||
es.execute(new Runnable() {
|
es.execute(() -> {
|
||||||
@Override
|
// To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done
|
||||||
public void run() {
|
ArrayList<VideoTrack> videoTracks = getVideoTrackInfoFromManifest();
|
||||||
// To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done
|
if (videoTracks != null) {
|
||||||
ArrayList<VideoTrack> videoTracks = getVideoTrackInfoFromManifest();
|
isUsingContentResolution = true;
|
||||||
if (videoTracks != null) {
|
|
||||||
isUsingContentResolution = true;
|
|
||||||
}
|
|
||||||
eventEmitter.load(duration, currentPosition, width, height,
|
|
||||||
audioTracks, textTracks, videoTracks, trackId );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
eventEmitter.load(duration, currentPosition, width, height,
|
||||||
|
audioTracks, textTracks, videoTracks, trackId );
|
||||||
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1275,7 +1253,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
final Uri sourceUri = this.srcUri;
|
final Uri sourceUri = this.srcUri;
|
||||||
final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset
|
final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset
|
||||||
|
|
||||||
Future<ArrayList<VideoTrack>> result = es.submit(new Callable<>() {
|
Future<ArrayList<VideoTrack>> result = es.submit(new Callable() {
|
||||||
final DataSource ds = dataSource;
|
final DataSource ds = dataSource;
|
||||||
final Uri uri = sourceUri;
|
final Uri uri = sourceUri;
|
||||||
final long startTimeUs = startTime * 1000; // ms -> us
|
final long startTimeUs = startTime * 1000; // ms -> us
|
||||||
@ -1331,7 +1309,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Track exoplayerTrackToGenericTrack(Format format, int trackIndex, TrackSelection selection, TrackGroup group) {
|
private Track exoplayerTrackToGenericTrack(Format format, int trackIndex, TrackSelection selection, TrackGroup group) {
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
track.setIndex(trackIndex);
|
track.setIndex(trackIndex);
|
||||||
@ -1393,7 +1370,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
|
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
|
||||||
eventEmitter.end();
|
eventEmitter.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1770,11 +1746,11 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks);
|
TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks);
|
||||||
|
|
||||||
DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
|
DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRendererDisabled(rendererIndex, false)
|
.setRendererDisabled(rendererIndex, false)
|
||||||
.clearOverridesOfType(selectionOverride.getType())
|
.clearOverridesOfType(selectionOverride.getType())
|
||||||
.addOverride(selectionOverride)
|
.addOverride(selectionOverride)
|
||||||
.build();
|
.build();
|
||||||
trackSelector.setParameters(selectionParameters);
|
trackSelector.setParameters(selectionParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2053,12 +2029,21 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
this.drmLicenseHeader = header;
|
this.drmLicenseHeader = header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||||
DebugLog.d("DRM Info", "onDrmKeysLoaded");
|
DebugLog.d("DRM Info", "onDrmKeysLoaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDrmSessionAcquired(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, int state) {
|
||||||
|
DebugLog.d("DRM Info", "onDrmSessionAcquired");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDrmSessionReleased(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||||
|
DebugLog.d("DRM Info", "onDrmSessionReleased");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) {
|
public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) {
|
||||||
DebugLog.d("DRM Info", "onDrmSessionManagerError");
|
DebugLog.d("DRM Info", "onDrmSessionManagerError");
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package com.brentvatne.exoplayer;
|
package com.brentvatne.exoplayer;
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.datasource.RawResourceDataSource;
|
||||||
|
import androidx.media3.exoplayer.DefaultLoadControl;
|
||||||
|
|
||||||
import com.brentvatne.common.API.ResizeMode;
|
import com.brentvatne.common.API.ResizeMode;
|
||||||
import com.brentvatne.common.API.SubtitleStyle;
|
import com.brentvatne.common.API.SubtitleStyle;
|
||||||
import com.brentvatne.common.react.VideoEventEmitter;
|
import com.brentvatne.common.react.VideoEventEmitter;
|
||||||
@ -19,9 +23,6 @@ import com.facebook.react.common.MapBuilder;
|
|||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.facebook.react.uimanager.ViewGroupManager;
|
import com.facebook.react.uimanager.ViewGroupManager;
|
||||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
|
||||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
|
||||||
import com.google.android.exoplayer2.upstream.RawResourceDataSource;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -202,7 +203,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
|
|||||||
videoView.setAdTagUrl(adTagUrl);
|
videoView.setAdTagUrl(adTagUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ReactProp(name = PROP_RESIZE_MODE)
|
@ReactProp(name = PROP_RESIZE_MODE)
|
||||||
public void setResizeMode(final ReactExoplayerView videoView, final String resizeMode) {
|
public void setResizeMode(final ReactExoplayerView videoView, final String resizeMode) {
|
||||||
switch (resizeMode) {
|
switch (resizeMode) {
|
||||||
|
@ -11,12 +11,16 @@ import com.facebook.react.bridge.ReactMethod;
|
|||||||
import com.facebook.react.uimanager.UIManagerModule;
|
import com.facebook.react.uimanager.UIManagerModule;
|
||||||
|
|
||||||
public class VideoManagerModule extends ReactContextBaseJavaModule {
|
public class VideoManagerModule extends ReactContextBaseJavaModule {
|
||||||
ReactApplicationContext reactContext;
|
private static final String REACT_CLASS = "VideoManager";
|
||||||
|
|
||||||
|
public VideoManagerModule(ReactApplicationContext reactContext) {
|
||||||
|
super(reactContext);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "VideoManager";
|
return REACT_CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
@ -31,9 +35,4 @@ public class VideoManagerModule extends ReactContextBaseJavaModule {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public VideoManagerModule(ReactApplicationContext reactContext) {
|
|
||||||
super(reactContext);
|
|
||||||
this.reactContext = reactContext;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -2,11 +2,12 @@ package com.brentvatne.receiver;
|
|||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
public class AudioBecomingNoisyReceiver extends BroadcastReceiver {
|
public class AudioBecomingNoisyReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package com.google.android.exoplayer2.ext.ima;
|
package com.google.android.exoplayer2.ext.ima;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.AdViewProvider;
|
||||||
|
import androidx.media3.common.Player;
|
||||||
|
import androidx.media3.datasource.DataSpec;
|
||||||
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
|
import androidx.media3.exoplayer.source.ads.AdsLoader;
|
||||||
|
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
|
||||||
|
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
|
||||||
import com.google.android.exoplayer2.Player;
|
|
||||||
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
|
||||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
|
||||||
import com.google.android.exoplayer2.ui.AdViewProvider;
|
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ public class ImaAdsLoader implements AdsLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(AdsMediaSource adsMediaSource, DataSpec dataSpec, Object o, AdViewProvider adViewProvider, EventListener eventListener) {
|
public void start(AdsMediaSource adsMediaSource, DataSpec dataSpec, Object adsId, AdViewProvider adViewProvider, EventListener eventListener) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
android:includeFontPadding="false"
|
android:includeFontPadding="false"
|
||||||
android:textColor="#FFBEBEBE"/>
|
android:textColor="#FFBEBEBE"/>
|
||||||
|
|
||||||
<com.google.android.exoplayer2.ui.DefaultTimeBar
|
<androidx.media3.ui.DefaultTimeBar
|
||||||
android:id="@+id/exo_progress"
|
android:id="@+id/exo_progress"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
Loading…
Reference in New Issue
Block a user