Merge remote-tracking branch 'origin/master' into ivan/merging-shaka-hls
This commit is contained in:
@@ -91,9 +91,10 @@ def configStringPath = ExoplayerDependencies
|
||||
.concat("buildFromSource:$media3_buildFromSource")
|
||||
.md5()
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
apply plugin: "com.facebook.react"
|
||||
}
|
||||
// commented as new architecture not yet fully supported
|
||||
// if (isNewArchitectureEnabled()) {
|
||||
// apply plugin: "com.facebook.react"
|
||||
// }
|
||||
|
||||
android {
|
||||
if (supportsNamespace()) {
|
||||
|
||||
@@ -11,5 +11,5 @@ RNVideo_useExoplayerSmoothStreaming=true
|
||||
RNVideo_useExoplayerDash=true
|
||||
RNVideo_useExoplayerHls=true
|
||||
RNVideo_androidxCoreVersion=1.13.1
|
||||
RNVideo_androidxActivityVersion=1.8.2
|
||||
RNVideo_androidxActivityVersion=1.9.3
|
||||
RNVideo_buildFromMedia3Source=false
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.brentvatne.common.api
|
||||
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import com.brentvatne.common.toolbox.DebugLog
|
||||
import com.brentvatne.common.toolbox.ReactBridgeUtils
|
||||
import com.facebook.react.bridge.ReadableMap
|
||||
|
||||
@@ -26,8 +25,6 @@ class AdsProps {
|
||||
@JvmStatic
|
||||
fun parse(src: ReadableMap?): AdsProps {
|
||||
val adsProps = AdsProps()
|
||||
DebugLog.w("olivier", "uri: parse AdsProps")
|
||||
|
||||
if (src != null) {
|
||||
val uriString = ReactBridgeUtils.safeGetString(src, PROP_AD_TAG_URL)
|
||||
if (TextUtils.isEmpty(uriString)) {
|
||||
|
||||
@@ -23,6 +23,23 @@ class BufferConfig {
|
||||
|
||||
var live: Live = Live()
|
||||
|
||||
/** return true if this and src are equals */
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null || other !is BufferConfig) return false
|
||||
return (
|
||||
cacheSize == other.cacheSize &&
|
||||
minBufferMs == other.minBufferMs &&
|
||||
maxBufferMs == other.maxBufferMs &&
|
||||
bufferForPlaybackMs == other.bufferForPlaybackMs &&
|
||||
bufferForPlaybackAfterRebufferMs == other.bufferForPlaybackAfterRebufferMs &&
|
||||
backBufferDurationMs == other.backBufferDurationMs &&
|
||||
maxHeapAllocationPercent == other.maxHeapAllocationPercent &&
|
||||
minBackBufferMemoryReservePercent == other.minBackBufferMemoryReservePercent &&
|
||||
minBufferMemoryReservePercent == other.minBufferMemoryReservePercent &&
|
||||
live == other.live
|
||||
)
|
||||
}
|
||||
|
||||
class Live {
|
||||
var maxPlaybackSpeed: Float = BufferConfigPropUnsetDouble.toFloat()
|
||||
var minPlaybackSpeed: Float = BufferConfigPropUnsetDouble.toFloat()
|
||||
@@ -30,12 +47,23 @@ class BufferConfig {
|
||||
var minOffsetMs: Long = BufferConfigPropUnsetInt.toLong()
|
||||
var targetOffsetMs: Long = BufferConfigPropUnsetInt.toLong()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null || other !is Live) return false
|
||||
return (
|
||||
maxPlaybackSpeed == other.maxPlaybackSpeed &&
|
||||
minPlaybackSpeed == other.minPlaybackSpeed &&
|
||||
maxOffsetMs == other.maxOffsetMs &&
|
||||
minOffsetMs == other.minOffsetMs &&
|
||||
targetOffsetMs == other.targetOffsetMs
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val PROP_BUFFER_CONFIG_LIVE_MAX_PLAYBACK_SPEED = "maxPlaybackSpeed"
|
||||
private val PROP_BUFFER_CONFIG_LIVE_MIN_PLAYBACK_SPEED = "minPlaybackSpeed"
|
||||
private val PROP_BUFFER_CONFIG_LIVE_MAX_OFFSET_MS = "maxOffsetMs"
|
||||
private val PROP_BUFFER_CONFIG_LIVE_MIN_OFFSET_MS = "minOffsetMs"
|
||||
private val PROP_BUFFER_CONFIG_LIVE_TARGET_OFFSET_MS = "targetOffsetMs"
|
||||
private const val PROP_BUFFER_CONFIG_LIVE_MAX_PLAYBACK_SPEED = "maxPlaybackSpeed"
|
||||
private const val PROP_BUFFER_CONFIG_LIVE_MIN_PLAYBACK_SPEED = "minPlaybackSpeed"
|
||||
private const val PROP_BUFFER_CONFIG_LIVE_MAX_OFFSET_MS = "maxOffsetMs"
|
||||
private const val PROP_BUFFER_CONFIG_LIVE_MIN_OFFSET_MS = "minOffsetMs"
|
||||
private const val PROP_BUFFER_CONFIG_LIVE_TARGET_OFFSET_MS = "targetOffsetMs"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(src: ReadableMap?): Live {
|
||||
@@ -54,16 +82,16 @@ class BufferConfig {
|
||||
val BufferConfigPropUnsetInt = -1
|
||||
val BufferConfigPropUnsetDouble = -1.0
|
||||
|
||||
private val PROP_BUFFER_CONFIG_CACHE_SIZE = "cacheSizeMB"
|
||||
private val PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs"
|
||||
private val PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs"
|
||||
private val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs"
|
||||
private val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs"
|
||||
private val PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent"
|
||||
private val PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent"
|
||||
private val PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent"
|
||||
private val PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS = "backBufferDurationMs"
|
||||
private val PROP_BUFFER_CONFIG_LIVE = "live"
|
||||
private const val PROP_BUFFER_CONFIG_CACHE_SIZE = "cacheSizeMB"
|
||||
private const val PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs"
|
||||
private const val PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs"
|
||||
private const val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs"
|
||||
private const val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs"
|
||||
private const val PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent"
|
||||
private const val PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent"
|
||||
private const val PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent"
|
||||
private const val PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS = "backBufferDurationMs"
|
||||
private const val PROP_BUFFER_CONFIG_LIVE = "live"
|
||||
|
||||
@JvmStatic
|
||||
fun parse(src: ReadableMap?): BufferConfig {
|
||||
|
||||
@@ -37,8 +37,8 @@ data class CMCDProps(
|
||||
|
||||
return (0 until array.size()).mapNotNull { i ->
|
||||
val item = array.getMap(i)
|
||||
val key = item?.getString("key")
|
||||
val value = when (item?.getType("value")) {
|
||||
val key = item.getString("key")
|
||||
val value = when (item.getType("value")) {
|
||||
ReadableType.Number -> item.getDouble("value")
|
||||
ReadableType.String -> item.getString("value")
|
||||
else -> null
|
||||
|
||||
@@ -30,6 +30,12 @@ class Source {
|
||||
/** Parsed value of source to playback */
|
||||
var uri: Uri? = null
|
||||
|
||||
/** True if source is a local JS asset */
|
||||
var isLocalAssetFile: Boolean = false
|
||||
|
||||
/** True if source is a local file asset://, ... */
|
||||
var isAsset: Boolean = false
|
||||
|
||||
/** Start position of playback used to resume playback */
|
||||
var startPositionMs: Int = -1
|
||||
|
||||
@@ -48,6 +54,9 @@ class Source {
|
||||
/** Metadata to display in notification */
|
||||
var metadata: Metadata? = null
|
||||
|
||||
/** Allowed reload before failure notification */
|
||||
var minLoadRetryCount = 3
|
||||
|
||||
/** http header list */
|
||||
val headers: MutableMap<String, String> = HashMap()
|
||||
|
||||
@@ -71,6 +80,11 @@ class Source {
|
||||
*/
|
||||
var adsProps: AdsProps? = null
|
||||
|
||||
/*
|
||||
* buffering configuration
|
||||
*/
|
||||
var bufferConfig = BufferConfig()
|
||||
|
||||
/**
|
||||
* The list of sideLoaded text tracks
|
||||
*/
|
||||
@@ -91,7 +105,11 @@ class Source {
|
||||
contentStartTime == other.contentStartTime &&
|
||||
cmcdProps == other.cmcdProps &&
|
||||
sideLoadedTextTracks == other.sideLoadedTextTracks &&
|
||||
adsProps == other.adsProps
|
||||
adsProps == other.adsProps &&
|
||||
minLoadRetryCount == other.minLoadRetryCount &&
|
||||
isLocalAssetFile == other.isLocalAssetFile &&
|
||||
isAsset == other.isAsset &&
|
||||
bufferConfig == other.bufferConfig
|
||||
)
|
||||
}
|
||||
|
||||
@@ -147,6 +165,8 @@ class Source {
|
||||
companion object {
|
||||
private const val TAG = "Source"
|
||||
private const val PROP_SRC_URI = "uri"
|
||||
private const val PROP_SRC_IS_LOCAL_ASSET_FILE = "isLocalAssetFile"
|
||||
private const val PROP_SRC_IS_ASSET = "isAsset"
|
||||
private const val PROP_SRC_START_POSITION = "startPosition"
|
||||
private const val PROP_SRC_CROP_START = "cropStart"
|
||||
private const val PROP_SRC_CROP_END = "cropEnd"
|
||||
@@ -159,6 +179,8 @@ class Source {
|
||||
private const val PROP_SRC_ADS = "ad"
|
||||
private const val PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION = "textTracksAllowChunklessPreparation"
|
||||
private const val PROP_SRC_TEXT_TRACKS = "textTracks"
|
||||
private const val PROP_SRC_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount"
|
||||
private const val PROP_SRC_BUFFER_CONFIG = "bufferConfig"
|
||||
|
||||
@SuppressLint("DiscouragedApi")
|
||||
private fun getUriFromAssetId(context: Context, uriString: String): Uri? {
|
||||
@@ -211,6 +233,8 @@ class Source {
|
||||
}
|
||||
source.uriString = uriString
|
||||
source.uri = uri
|
||||
source.isLocalAssetFile = safeGetBool(src, PROP_SRC_IS_LOCAL_ASSET_FILE, false)
|
||||
source.isAsset = safeGetBool(src, PROP_SRC_IS_ASSET, false)
|
||||
source.startPositionMs = safeGetInt(src, PROP_SRC_START_POSITION, -1)
|
||||
source.cropStartMs = safeGetInt(src, PROP_SRC_CROP_START, -1)
|
||||
source.cropEndMs = safeGetInt(src, PROP_SRC_CROP_END, -1)
|
||||
@@ -223,6 +247,8 @@ class Source {
|
||||
}
|
||||
source.textTracksAllowChunklessPreparation = safeGetBool(src, PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION, true)
|
||||
source.sideLoadedTextTracks = SideLoadedTextTrackList.parse(safeGetArray(src, PROP_SRC_TEXT_TRACKS))
|
||||
source.minLoadRetryCount = safeGetInt(src, PROP_SRC_MIN_LOAD_RETRY_COUNT, 3)
|
||||
source.bufferConfig = BufferConfig.parse(safeGetMap(src, PROP_SRC_BUFFER_CONFIG))
|
||||
|
||||
val propSrcHeadersArray = safeGetArray(src, PROP_SRC_HEADERS)
|
||||
if (propSrcHeadersArray != null) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.brentvatne.exoplayer
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration
|
||||
import com.brentvatne.common.api.CMCDProps
|
||||
import com.brentvatne.common.toolbox.DebugLog
|
||||
import com.google.common.collect.ImmutableListMultimap
|
||||
|
||||
class CMCDConfig(private val props: CMCDProps) {
|
||||
@@ -15,9 +16,21 @@ class CMCDConfig(private val props: CMCDProps) {
|
||||
object : CmcdConfiguration.RequestConfig {
|
||||
override fun getCustomData(): ImmutableListMultimap<String, String> = buildCustomData()
|
||||
},
|
||||
props.mode
|
||||
intToCmcdMode(props.mode)
|
||||
)
|
||||
|
||||
private fun intToCmcdMode(mode: Int): Int =
|
||||
when (mode) {
|
||||
0 -> CmcdConfiguration.MODE_REQUEST_HEADER
|
||||
|
||||
1 -> CmcdConfiguration.MODE_QUERY_PARAMETER
|
||||
|
||||
else -> {
|
||||
DebugLog.e("CMCDConfig", "Unsupported mode: $mode, fallback on MODE_REQUEST_HEADER")
|
||||
CmcdConfiguration.MODE_REQUEST_HEADER
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildCustomData(): ImmutableListMultimap<String, String> =
|
||||
ImmutableListMultimap.builder<String, String>().apply {
|
||||
addFormattedData(this, CmcdConfiguration.KEY_CMCD_OBJECT, props.cmcdObject)
|
||||
|
||||
@@ -40,7 +40,7 @@ class ExoPlayerView(private val context: Context) :
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
private var adOverlayFrameLayout: FrameLayout
|
||||
private var adOverlayFrameLayout: FrameLayout? = null
|
||||
val isPlaying: Boolean
|
||||
get() = player != null && player?.isPlaying == true
|
||||
|
||||
@@ -72,12 +72,9 @@ class ExoPlayerView(private val context: Context) :
|
||||
|
||||
updateSurfaceView(viewType)
|
||||
|
||||
adOverlayFrameLayout = FrameLayout(context)
|
||||
|
||||
layout.addView(shutterView, 1, layoutParams)
|
||||
if (localStyle.subtitlesFollowVideo) {
|
||||
layout.addView(subtitleLayout, layoutParams)
|
||||
layout.addView(adOverlayFrameLayout, layoutParams)
|
||||
}
|
||||
|
||||
addViewInLayout(layout, 0, aspectRatioParams)
|
||||
@@ -194,22 +191,21 @@ class ExoPlayerView(private val context: Context) :
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideShutterView() {
|
||||
shutterView.setVisibility(INVISIBLE)
|
||||
surfaceView?.setAlpha(1f)
|
||||
}
|
||||
|
||||
private fun showShutterView() {
|
||||
shutterView.setVisibility(VISIBLE)
|
||||
surfaceView?.setAlpha(0f)
|
||||
}
|
||||
|
||||
var adsShown = false
|
||||
fun showAds() {
|
||||
adOverlayFrameLayout.setVisibility(View.VISIBLE)
|
||||
if (!adsShown) {
|
||||
adOverlayFrameLayout = FrameLayout(context)
|
||||
layout.addView(adOverlayFrameLayout, layoutParams)
|
||||
adsShown = true
|
||||
}
|
||||
}
|
||||
|
||||
fun hideAds() {
|
||||
adOverlayFrameLayout.setVisibility(View.GONE)
|
||||
if (adsShown) {
|
||||
layout.removeView(adOverlayFrameLayout)
|
||||
adOverlayFrameLayout = null
|
||||
adsShown = false
|
||||
}
|
||||
}
|
||||
|
||||
fun updateShutterViewVisibility() {
|
||||
|
||||
@@ -84,7 +84,6 @@ 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;
|
||||
@@ -208,8 +207,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
private float rate = 1f;
|
||||
private AudioOutput audioOutput = AudioOutput.SPEAKER;
|
||||
private float audioVolume = 1f;
|
||||
private int minLoadRetryCount = 3;
|
||||
private BufferConfig bufferConfig = new BufferConfig();
|
||||
private int maxBitRate = 0;
|
||||
private boolean hasDrmFailed = false;
|
||||
private boolean isUsingContentResolution = false;
|
||||
@@ -714,7 +711,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
runtime = Runtime.getRuntime();
|
||||
ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(ThemedReactContext.ACTIVITY_SERVICE);
|
||||
double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
|
||||
? bufferConfig.getMaxHeapAllocationPercent()
|
||||
? config.getMaxHeapAllocationPercent()
|
||||
: DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
|
||||
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeap * 1024 * 1024);
|
||||
}
|
||||
@@ -733,8 +730,8 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
||||
long freeMemory = runtime.maxMemory() - usedMemory;
|
||||
double minBufferMemoryReservePercent = bufferConfig.getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
|
||||
? bufferConfig.getMinBufferMemoryReservePercent()
|
||||
double minBufferMemoryReservePercent = source.getBufferConfig().getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
|
||||
? source.getBufferConfig().getMinBufferMemoryReservePercent()
|
||||
: ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
|
||||
long reserveMemory = (long) minBufferMemoryReservePercent * runtime.maxMemory();
|
||||
long bufferedMs = bufferedDurationUs / (long) 1000;
|
||||
@@ -766,10 +763,20 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
if (runningSource.getUri() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player == null) {
|
||||
// Initialize core configuration and listeners
|
||||
initializePlayerCore(self);
|
||||
}
|
||||
if (!source.isLocalAssetFile() && !source.isAsset() && source.getBufferConfig().getCacheSize() > 0) {
|
||||
RNVSimpleCache.INSTANCE.setSimpleCache(
|
||||
this.getContext(),
|
||||
source.getBufferConfig().getCacheSize()
|
||||
);
|
||||
useCache = true;
|
||||
} else {
|
||||
useCache = false;
|
||||
}
|
||||
if (playerNeedsSource) {
|
||||
// Will force display of shutter view if needed
|
||||
exoPlayerView.updateShutterViewVisibility();
|
||||
@@ -836,7 +843,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
|
||||
RNVLoadControl loadControl = new RNVLoadControl(
|
||||
allocator,
|
||||
bufferConfig
|
||||
source.getBufferConfig()
|
||||
);
|
||||
DefaultRenderersFactory renderersFactory =
|
||||
new DefaultRenderersFactory(getContext())
|
||||
@@ -849,23 +856,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
|
||||
}
|
||||
|
||||
if (BuildConfig.USE_EXOPLAYER_IMA) {
|
||||
AdsProps adProps = source.getAdsProps();
|
||||
|
||||
// Create an AdsLoader.
|
||||
ImaAdsLoader.Builder imaLoaderBuilder = new ImaAdsLoader
|
||||
.Builder(themedReactContext)
|
||||
.setAdEventListener(this)
|
||||
.setAdErrorListener(this);
|
||||
|
||||
if (adProps != null && adProps.getAdLanguage() != null) {
|
||||
ImaSdkSettings imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
|
||||
imaSdkSettings.setLanguage(adProps.getAdLanguage());
|
||||
imaLoaderBuilder.setImaSdkSettings(imaSdkSettings);
|
||||
}
|
||||
adsLoader = imaLoaderBuilder.build();
|
||||
}
|
||||
|
||||
mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
|
||||
|
||||
player = new ExoPlayer.Builder(getContext(), renderersFactory)
|
||||
@@ -881,9 +871,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
player.setVolume(muted ? 0.f : audioVolume * 1);
|
||||
exoPlayerView.setPlayer(player);
|
||||
|
||||
if (adsLoader != null) {
|
||||
adsLoader.setPlayer(player);
|
||||
}
|
||||
audioBecomingNoisyReceiver.setListener(self);
|
||||
bandwidthMeter.addEventListener(new Handler(), self);
|
||||
setPlayWhenReady(!isPaused);
|
||||
@@ -898,6 +885,41 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
private AdsMediaSource initializeAds(MediaSource videoSource, Source runningSource) {
|
||||
AdsProps adProps = runningSource.getAdsProps();
|
||||
Uri uri = runningSource.getUri();
|
||||
if (adProps != null && uri != null) {
|
||||
Uri adTagUrl = adProps.getAdTagUrl();
|
||||
if (adTagUrl != null) {
|
||||
exoPlayerView.showAds();
|
||||
// Create an AdsLoader.
|
||||
ImaAdsLoader.Builder imaLoaderBuilder = new ImaAdsLoader
|
||||
.Builder(themedReactContext)
|
||||
.setAdEventListener(this)
|
||||
.setAdErrorListener(this);
|
||||
|
||||
if (adProps.getAdLanguage() != null) {
|
||||
ImaSdkSettings imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
|
||||
imaSdkSettings.setLanguage(adProps.getAdLanguage());
|
||||
imaLoaderBuilder.setImaSdkSettings(imaSdkSettings);
|
||||
}
|
||||
adsLoader = imaLoaderBuilder.build();
|
||||
adsLoader.setPlayer(player);
|
||||
if (adsLoader != null) {
|
||||
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
|
||||
.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
|
||||
DataSpec adTagDataSpec = new DataSpec(adTagUrl);
|
||||
return new AdsMediaSource(videoSource,
|
||||
adTagDataSpec,
|
||||
ImmutableList.of(uri, adTagUrl),
|
||||
mediaSourceFactory, adsLoader, exoPlayerView);
|
||||
}
|
||||
}
|
||||
}
|
||||
exoPlayerView.hideAds();
|
||||
return null;
|
||||
}
|
||||
|
||||
private DrmSessionManager initializePlayerDrm() {
|
||||
DrmSessionManager drmSessionManager = null;
|
||||
DRMProps drmProps = source.getDrmProps();
|
||||
@@ -931,32 +953,17 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
return;
|
||||
}
|
||||
// init source to manage ads and external text tracks
|
||||
MediaSource subtitlesSource = buildTextSource();
|
||||
MediaSource videoSource = buildMediaSource(runningSource.getUri(), runningSource.getExtension(), drmSessionManager, runningSource.getCropStartMs(), runningSource.getCropEndMs());
|
||||
MediaSource mediaSourceWithAds = null;
|
||||
Uri adTagUrl = null;
|
||||
if (source.getAdsProps() != null) {
|
||||
adTagUrl = source.getAdsProps().getAdTagUrl();
|
||||
}
|
||||
if (adTagUrl != null && adsLoader != null) {
|
||||
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
|
||||
.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
|
||||
DataSpec adTagDataSpec = new DataSpec(adTagUrl);
|
||||
DebugLog.w(TAG, "ads " + adTagUrl);
|
||||
mediaSourceWithAds = new AdsMediaSource(videoSource, adTagDataSpec, ImmutableList.of(runningSource.getUri(), adTagUrl), mediaSourceFactory, adsLoader, exoPlayerView);
|
||||
exoPlayerView.showAds();
|
||||
}
|
||||
MediaSource mediaSource;
|
||||
if (subtitlesSource == null) {
|
||||
mediaSource = Objects.requireNonNullElse(mediaSourceWithAds, videoSource);
|
||||
} else {
|
||||
ArrayList<MediaSource> mediaSourceList = new ArrayList<>();
|
||||
mediaSourceList.add(subtitlesSource);
|
||||
mediaSourceList.add(0, Objects.requireNonNullElse(mediaSourceWithAds, videoSource));
|
||||
MediaSource[] mediaSourceArray = mediaSourceList.toArray(
|
||||
new MediaSource[mediaSourceList.size()]
|
||||
);
|
||||
MediaSource videoSource = buildMediaSource(runningSource.getUri(),
|
||||
runningSource.getExtension(),
|
||||
drmSessionManager,
|
||||
runningSource.getCropStartMs(),
|
||||
runningSource.getCropEndMs());
|
||||
MediaSource mediaSourceWithAds = initializeAds(videoSource, runningSource);
|
||||
MediaSource mediaSource = Objects.requireNonNullElse(mediaSourceWithAds, videoSource);
|
||||
|
||||
MediaSource subtitlesSource = buildTextSource();
|
||||
if (subtitlesSource != null) {
|
||||
MediaSource[] mediaSourceArray = {mediaSource, subtitlesSource};
|
||||
mediaSource = new MergingMediaSource(mediaSourceArray);
|
||||
}
|
||||
|
||||
@@ -1143,7 +1150,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils.getLiveConfiguration(bufferConfig);
|
||||
MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils.getLiveConfiguration(source.getBufferConfig());
|
||||
mediaItemBuilder.setLiveConfiguration(liveConfiguration.build());
|
||||
|
||||
MediaSource.Factory mediaSourceFactory;
|
||||
@@ -1155,6 +1162,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
drmProvider = new DefaultDrmSessionManagerProvider();
|
||||
}
|
||||
|
||||
|
||||
switch (type) {
|
||||
case CONTENT_TYPE_SS:
|
||||
if(!BuildConfig.USE_EXOPLAYER_SMOOTH_STREAMING) {
|
||||
@@ -1184,8 +1192,14 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
throw new IllegalStateException("HLS is not enabled!");
|
||||
}
|
||||
|
||||
DataSource.Factory dataSourceFactory = mediaDataSourceFactory;
|
||||
|
||||
if (useCache) {
|
||||
dataSourceFactory = RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true));
|
||||
}
|
||||
|
||||
mediaSourceFactory = new HlsMediaSource.Factory(
|
||||
mediaDataSourceFactory
|
||||
dataSourceFactory
|
||||
).setAllowChunklessPreparation(source.getTextTracksAllowChunklessPreparation());
|
||||
break;
|
||||
case CONTENT_TYPE_OTHER:
|
||||
@@ -1194,7 +1208,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
DataSource.Factory assetDataSourceFactory = DataSourceUtil.buildAssetDataSourceFactory(themedReactContext, uri);
|
||||
mediaSourceFactory = new ProgressiveMediaSource.Factory(assetDataSourceFactory);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("cannot open input file" + uri);
|
||||
throw new IllegalStateException("cannot open input file:" + uri);
|
||||
}
|
||||
} else if ("file".equals(uri.getScheme()) ||
|
||||
!useCache) {
|
||||
@@ -1231,7 +1245,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
MediaSource mediaSource = mediaSourceFactory
|
||||
.setDrmSessionManagerProvider(drmProvider)
|
||||
.setLoadErrorHandlingPolicy(
|
||||
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
||||
config.buildLoadErrorHandlingPolicy(source.getMinLoadRetryCount())
|
||||
)
|
||||
.createMediaSource(mediaItem);
|
||||
|
||||
@@ -1934,7 +1948,8 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
|
||||
if (!isSourceEqual) {
|
||||
reloadSource();
|
||||
playerNeedsSource = true;
|
||||
initializePlayer();
|
||||
}
|
||||
} else {
|
||||
clearSrc();
|
||||
@@ -1961,11 +1976,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
mReportBandwidth = reportBandwidth;
|
||||
}
|
||||
|
||||
private void reloadSource() {
|
||||
playerNeedsSource = true;
|
||||
initializePlayer();
|
||||
}
|
||||
|
||||
public void setResizeModeModifier(@ResizeMode.Mode int resizeMode) {
|
||||
if (exoPlayerView != null) {
|
||||
exoPlayerView.setResizeMode(resizeMode);
|
||||
@@ -2308,12 +2318,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
public void setMinLoadRetryCountModifier(int newMinLoadRetryCount) {
|
||||
minLoadRetryCount = newMinLoadRetryCount;
|
||||
releasePlayer();
|
||||
initializePlayer();
|
||||
}
|
||||
|
||||
public void setPlayInBackground(boolean playInBackground) {
|
||||
this.playInBackground = playInBackground;
|
||||
}
|
||||
@@ -2405,21 +2409,6 @@ public class ReactExoplayerView extends FrameLayout implements
|
||||
exoPlayerView.setHideShutterView(hideShutterView);
|
||||
}
|
||||
|
||||
public void setBufferConfig(BufferConfig config) {
|
||||
bufferConfig = config;
|
||||
if (bufferConfig.getCacheSize() > 0) {
|
||||
RNVSimpleCache.INSTANCE.setSimpleCache(
|
||||
this.getContext(),
|
||||
bufferConfig.getCacheSize()
|
||||
);
|
||||
useCache = true;
|
||||
} else {
|
||||
useCache = false;
|
||||
}
|
||||
releasePlayer();
|
||||
initializePlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||
DebugLog.d("DRM Info", "onDrmKeysLoaded");
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.brentvatne.exoplayer
|
||||
|
||||
import android.graphics.Color
|
||||
import android.util.Log
|
||||
import com.brentvatne.common.api.BufferConfig
|
||||
import com.brentvatne.common.api.BufferingStrategy
|
||||
import com.brentvatne.common.api.ControlsConfig
|
||||
import com.brentvatne.common.api.ResizeMode
|
||||
@@ -36,13 +35,11 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
|
||||
private const val PROP_MUTED = "muted"
|
||||
private const val PROP_AUDIO_OUTPUT = "audioOutput"
|
||||
private const val PROP_VOLUME = "volume"
|
||||
private const val PROP_BUFFER_CONFIG = "bufferConfig"
|
||||
private const val PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK =
|
||||
"preventsDisplaySleepDuringVideoPlayback"
|
||||
private const val PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval"
|
||||
private const val PROP_REPORT_BANDWIDTH = "reportBandwidth"
|
||||
private const val PROP_RATE = "rate"
|
||||
private const val PROP_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount"
|
||||
private const val PROP_MAXIMUM_BIT_RATE = "maxBitRate"
|
||||
private const val PROP_PLAY_IN_BACKGROUND = "playInBackground"
|
||||
private const val PROP_DISABLE_FOCUS = "disableFocus"
|
||||
@@ -187,11 +184,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
|
||||
videoView.setMaxBitRateModifier(maxBitRate.toInt())
|
||||
}
|
||||
|
||||
@ReactProp(name = PROP_MIN_LOAD_RETRY_COUNT)
|
||||
fun setMinLoadRetryCount(videoView: ReactExoplayerView, minLoadRetryCount: Int) {
|
||||
videoView.setMinLoadRetryCountModifier(minLoadRetryCount)
|
||||
}
|
||||
|
||||
@ReactProp(name = PROP_PLAY_IN_BACKGROUND, defaultBoolean = false)
|
||||
fun setPlayInBackground(videoView: ReactExoplayerView, playInBackground: Boolean) {
|
||||
videoView.setPlayInBackground(playInBackground)
|
||||
@@ -248,12 +240,6 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
|
||||
videoView.setShutterColor(color)
|
||||
}
|
||||
|
||||
@ReactProp(name = PROP_BUFFER_CONFIG)
|
||||
fun setBufferConfig(videoView: ReactExoplayerView, bufferConfig: ReadableMap?) {
|
||||
val config = BufferConfig.parse(bufferConfig)
|
||||
videoView.setBufferConfig(config)
|
||||
}
|
||||
|
||||
@ReactProp(name = PROP_SHOW_NOTIFICATION_CONTROLS)
|
||||
fun setShowNotificationControls(videoView: ReactExoplayerView, showNotificationControls: Boolean) {
|
||||
videoView.setShowNotificationControls(showNotificationControls)
|
||||
|
||||
Reference in New Issue
Block a user