From 002f39181a1136a4ae16ffce4692ce2fee8e4ccd Mon Sep 17 00:00:00 2001 From: Axel Vencatareddy Date: Wed, 9 Nov 2022 14:26:39 +0100 Subject: [PATCH] [ANDROID] Fix android after merge --- android-exoplayer/build.gradle | 77 - .../brentvatne/exoplayer/ExoPlayerView.java | 319 ---- .../exoplayer/ReactExoplayerView.java | 1410 ----------------- android/build.gradle | 2 + .../brentvatne/exoplayer/ExoPlayerView.java | 31 +- .../exoplayer/ReactExoplayerView.java | 43 +- 6 files changed, 69 insertions(+), 1813 deletions(-) delete mode 100644 android-exoplayer/build.gradle delete mode 100644 android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java delete mode 100644 android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java diff --git a/android-exoplayer/build.gradle b/android-exoplayer/build.gradle deleted file mode 100644 index 13eb889d..00000000 --- a/android-exoplayer/build.gradle +++ /dev/null @@ -1,77 +0,0 @@ -apply plugin: 'com.android.library' - -def safeExtGet(prop, fallback) { - rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback -} - -android { - compileSdkVersion safeExtGet('compileSdkVersion', 28) - buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3') - - compileOptions { - targetCompatibility JavaVersion.VERSION_1_8 - sourceCompatibility JavaVersion.VERSION_1_8 - } - - defaultConfig { - minSdkVersion safeExtGet('minSdkVersion', 16) - targetSdkVersion safeExtGet('targetSdkVersion', 28) - versionCode 1 - versionName "1.0" - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} - -dependencies { - implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" - implementation('com.google.android.exoplayer:exoplayer:2.11.4') { - exclude group: 'com.android.support' - } - - // All support libs must use the same version - implementation "androidx.annotation:annotation:1.1.0" - implementation "androidx.core:core:1.1.0" - implementation "androidx.media:media:1.1.0" - - implementation('com.google.android.exoplayer:extension-okhttp:2.11.4') { - exclude group: 'com.squareup.okhttp3', module: 'okhttp' - } - implementation 'com.google.android.exoplayer:extension-ima:2.11.4' - - implementation 'com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}' - -} - - -/* If one wants to open this module in Android studio. Uncomment these repositories and buildscript parts*/ - -/* -repositories { - maven { - url 'https://maven.google.com/' - name 'Google' - } - jcenter() - google() - mavenCentral() -} - -buildscript { - repositories { - maven { - url 'https://maven.google.com/' - name 'Google' - } - jcenter() - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' - } -} -*/ \ No newline at end of file diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java deleted file mode 100644 index 75512628..00000000 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ /dev/null @@ -1,319 +0,0 @@ -package com.brentvatne.exoplayer; - -import android.annotation.TargetApi; -import android.content.Context; -import androidx.core.content.ContextCompat; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.view.SurfaceView; -import android.view.TextureView; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlaybackException; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.SimpleExoPlayer; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.ads.AdsLoader; -import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.text.Cue; -import com.google.android.exoplayer2.text.TextRenderer; -import com.google.android.exoplayer2.text.TextOutput; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; -import com.google.android.exoplayer2.ui.SubtitleView; -import com.google.android.exoplayer2.util.Assertions; - -import java.util.List; -import java.util.ArrayList; - -@TargetApi(16) -public final class ExoPlayerView extends FrameLayout implements AdsLoader.AdViewProvider { - - private View surfaceView; - private final View shutterView; - private final SubtitleView subtitleLayout; - private final AspectRatioFrameLayout layout; - private final ComponentListener componentListener; - private SimpleExoPlayer player; - private Context context; - private ViewGroup.LayoutParams layoutParams; - private final FrameLayout adOverlayFrameLayout; - - private boolean useTextureView = true; - private boolean hideShutterView = false; - - public ExoPlayerView(Context context) { - this(context, null); - } - - public ExoPlayerView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - this.context = context; - - layoutParams = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - - componentListener = new ComponentListener(); - - FrameLayout.LayoutParams aspectRatioParams = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT); - aspectRatioParams.gravity = Gravity.CENTER; - layout = new AspectRatioFrameLayout(context); - layout.setLayoutParams(aspectRatioParams); - - shutterView = new View(getContext()); - shutterView.setLayoutParams(layoutParams); - shutterView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.black)); - - subtitleLayout = new SubtitleView(context); - subtitleLayout.setLayoutParams(layoutParams); - subtitleLayout.setUserDefaultStyle(); - subtitleLayout.setUserDefaultTextSize(); - - updateSurfaceView(); - - layout.addView(shutterView, 1, layoutParams); - layout.addView(subtitleLayout, 2, layoutParams); - - adOverlayFrameLayout = new FrameLayout(context); - - addViewInLayout(layout, 0, aspectRatioParams); - addViewInLayout(adOverlayFrameLayout, 1, layoutParams); - - } - - private void setVideoView() { - if (surfaceView instanceof TextureView) { - player.setVideoTextureView((TextureView) surfaceView); - } else if (surfaceView instanceof SurfaceView) { - player.setVideoSurfaceView((SurfaceView) surfaceView); - } - } - - private void updateSurfaceView() { - View view = useTextureView ? new TextureView(context) : new SurfaceView(context); - view.setLayoutParams(layoutParams); - - surfaceView = view; - if (layout.getChildAt(0) != null) { - layout.removeViewAt(0); - } - layout.addView(surfaceView, 0, layoutParams); - - if (this.player != null) { - setVideoView(); - } - } - - private void updateShutterViewVisibility() { - shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE); - } - - @Override - public void requestLayout() { - super.requestLayout(); - post(measureAndLayout); - } - - // AdsLoader.AdViewProvider implementation. - - @Override - public ViewGroup getAdViewGroup() { - return Assertions.checkNotNull(adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback"); - } - - @Override - public View[] getAdOverlayViews() { - ArrayList overlayViews = new ArrayList<>(); - if (adOverlayFrameLayout != null) { - overlayViews.add(adOverlayFrameLayout); - } - return overlayViews.toArray(new View[0]); - } - - /** - * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and - * {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous - * assignments are overridden. - * - * @param player The {@link SimpleExoPlayer} to use. - */ - public void setPlayer(SimpleExoPlayer player) { - if (this.player == player) { - return; - } - if (this.player != null) { - this.player.setTextOutput(null); - this.player.setVideoListener(null); - this.player.removeListener(componentListener); - this.player.setVideoSurface(null); - } - this.player = player; - shutterView.setVisibility(VISIBLE); - if (player != null) { - setVideoView(); - player.setVideoListener(componentListener); - player.addListener(componentListener); - player.setTextOutput(componentListener); - } - } - - /** - * Sets the resize mode which can be of value {@link ResizeMode.Mode} - * - * @param resizeMode The resize mode. - */ - public void setResizeMode(@ResizeMode.Mode int resizeMode) { - if (layout.getResizeMode() != resizeMode) { - layout.setResizeMode(resizeMode); - post(measureAndLayout); - } - - } - - /** - * Get the view onto which video is rendered. This is either a {@link SurfaceView} (default) - * or a {@link TextureView} if the {@code use_texture_view} view attribute has been set to true. - * - * @return either a {@link SurfaceView} or a {@link TextureView}. - */ - public View getVideoSurfaceView() { - return surfaceView; - } - - public void setUseTextureView(boolean useTextureView) { - if (useTextureView != this.useTextureView) { - this.useTextureView = useTextureView; - updateSurfaceView(); - } - } - - public void setHideShutterView(boolean hideShutterView) { - this.hideShutterView = hideShutterView; - updateShutterViewVisibility(); - } - - private final Runnable measureAndLayout = new Runnable() { - @Override - public void run() { - measure( - MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); - layout(getLeft(), getTop(), getRight(), getBottom()); - } - }; - - private void updateForCurrentTrackSelections() { - if (player == null) { - return; - } - TrackSelectionArray selections = player.getCurrentTrackSelections(); - for (int i = 0; i < selections.length; i++) { - if (player.getRendererType(i) == C.TRACK_TYPE_VIDEO && selections.get(i) != null) { - // Video enabled so artwork must be hidden. If the shutter is closed, it will be opened in - // onRenderedFirstFrame(). - return; - } - } - // Video disabled so the shutter must be closed. - shutterView.setVisibility(VISIBLE); - } - - public void invalidateAspectRatio() { - // Resetting aspect ratio will force layout refresh on next video size changed - layout.invalidateAspectRatio(); - } - - private final class ComponentListener implements SimpleExoPlayer.VideoListener, - TextOutput, ExoPlayer.EventListener { - - // TextRenderer.Output implementation - - @Override - public void onCues(List cues) { - subtitleLayout.onCues(cues); - } - - // SimpleExoPlayer.VideoListener implementation - - @Override - public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { - boolean isInitialRatio = layout.getAspectRatio() == 0; - layout.setAspectRatio(height == 0 ? 1 : (width * pixelWidthHeightRatio) / height); - - // React native workaround for measuring and layout on initial load. - if (isInitialRatio) { - post(measureAndLayout); - } - } - - @Override - public void onRenderedFirstFrame() { - shutterView.setVisibility(INVISIBLE); - } - - // ExoPlayer.EventListener implementation - - @Override - public void onLoadingChanged(boolean isLoading) { - // Do nothing. - } - - @Override - public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { - // Do nothing. - } - - @Override - public void onPlayerError(ExoPlaybackException e) { - // Do nothing. - } - - @Override - public void onPositionDiscontinuity(int reason) { - // Do nothing. - } - - @Override - public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { - // Do nothing. - } - - @Override - public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - updateForCurrentTrackSelections(); - } - - @Override - public void onPlaybackParametersChanged(PlaybackParameters params) { - // Do nothing - } - - @Override - public void onSeekProcessed() { - // Do nothing. - } - - @Override - public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { - // Do nothing. - } - - @Override - public void onRepeatModeChanged(int repeatMode) { - // Do nothing. - } - } - -} diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java deleted file mode 100644 index 819f7633..00000000 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ /dev/null @@ -1,1410 +0,0 @@ -package com.brentvatne.exoplayer; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.media.AudioManager; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.accessibility.CaptioningManager; -import android.widget.FrameLayout; -import android.widget.ImageButton; - -import com.brentvatne.react.R; -import com.brentvatne.receiver.AudioBecomingNoisyReceiver; -import com.brentvatne.receiver.BecomingNoisyListener; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.LifecycleEventListener; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.ThemedReactContext; -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.ExoPlaybackException; -import com.google.android.exoplayer2.ExoPlayerFactory; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.SimpleExoPlayer; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; -import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener; -import com.google.android.exoplayer2.drm.DrmSessionManager; -import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; -import com.google.android.exoplayer2.drm.FrameworkMediaDrm; -import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; -import com.google.android.exoplayer2.drm.UnsupportedDrmException; -import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; -import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; -import com.google.android.exoplayer2.metadata.Metadata; -import com.google.android.exoplayer2.metadata.MetadataOutput; -import com.google.android.exoplayer2.source.BehindLiveWindowException; -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.TrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; -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.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.ext.ima.ImaAdsLoader; -import com.google.android.exoplayer2.source.ads.AdsMediaSource; - -import java.net.CookieHandler; -import java.net.CookieManager; -import java.net.CookiePolicy; -import java.net.URI; -import java.util.ArrayList; -import java.util.Locale; -import java.util.UUID; -import java.util.Map; - -@SuppressLint("ViewConstructor") -class ReactExoplayerView extends FrameLayout implements - LifecycleEventListener, - Player.EventListener, - BandwidthMeter.EventListener, - BecomingNoisyListener, - AudioManager.OnAudioFocusChangeListener, - MetadataOutput, - DefaultDrmSessionEventListener, AdEvent.AdEventListener { - - private static final String TAG = "ReactExoplayerView"; - - private static final CookieManager DEFAULT_COOKIE_MANAGER; - private static final int SHOW_PROGRESS = 1; - - static { - DEFAULT_COOKIE_MANAGER = new CookieManager(); - DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); - } - - private final VideoEventEmitter eventEmitter; - private final ReactExoplayerConfig config; - private final DefaultBandwidthMeter bandwidthMeter; - private PlayerControlView playerControlView; - private View playPauseControlContainer; - private Player.EventListener eventListener; - - private ExoPlayerView exoPlayerView; - private ImaAdsLoader adsLoader; - private int initialOrientation; - - private DataSource.Factory mediaDataSourceFactory; - private SimpleExoPlayer player; - private DefaultTrackSelector trackSelector; - private boolean playerNeedsSource; - - private int resumeWindow; - private long resumePosition; - private boolean loadVideoStarted; - private boolean isFullscreen; - private boolean isInBackground; - private boolean isPaused; - private boolean isBuffering; - private boolean muted = false; - private float rate = 1f; - private float audioVolume = 1f; - private int minLoadRetryCount = 3; - private int maxBitRate = 0; - private long seekTime = C.TIME_UNSET; - - private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; - private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; - private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS; - private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; - - private Handler mainHandler; - - // Props from React - private Uri srcUri; - private String extension; - private boolean repeat; - private String audioTrackType; - private Dynamic audioTrackValue; - private String videoTrackType; - private Dynamic videoTrackValue; - private String textTrackType; - private Dynamic textTrackValue; - private ReadableArray textTracks; - private boolean disableFocus; - private boolean preventsDisplaySleepDuringVideoPlayback = true; - private float mProgressUpdateInterval = 250.0f; - private boolean playInBackground = false; - private Map requestHeaders; - private boolean mReportBandwidth = false; - private UUID drmUUID = null; - private String drmLicenseUrl = null; - private String[] drmLicenseHeader = null; - private boolean controls; - private Uri adTagUrl; - // \ End props - - // React - private final ThemedReactContext themedReactContext; - private final AudioManager audioManager; - private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver; - - private final Handler progressHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case SHOW_PROGRESS: - if (player != null - && player.getPlaybackState() == Player.STATE_READY - && player.getPlayWhenReady() - ) { - if (isPlayingAd()) { - playerControlView.hide(); - } - long pos = player.getCurrentPosition(); - long bufferedDuration = player.getBufferedPercentage() * player.getDuration() / 100; - eventEmitter.progressChanged(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos)); - msg = obtainMessage(SHOW_PROGRESS); - sendMessageDelayed(msg, Math.round(mProgressUpdateInterval)); - } - break; - } - } - }; - - public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) { - Timeline.Window window = new Timeline.Window(); - if(!player.getCurrentTimeline().isEmpty()) { - player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window); - } - return window.windowStartTimeMs + currentPosition; - } - - public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig config) { - super(context); - this.themedReactContext = context; - this.eventEmitter = new VideoEventEmitter(context); - this.config = config; - this.bandwidthMeter = config.getBandwidthMeter(); - - adsLoader = new ImaAdsLoader(this.themedReactContext, Uri.EMPTY); - - createViews(); - - audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - themedReactContext.addLifecycleEventListener(this); - audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext); - } - - private boolean isPlayingAd() { - return player != null && player.isPlayingAd() && player.getPlayWhenReady(); - } - - - @Override - public void setId(int id) { - super.setId(id); - eventEmitter.setViewId(id); - } - - private void createViews() { - clearResumePosition(); - mediaDataSourceFactory = buildDataSourceFactory(true); - if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) { - CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER); - } - - LayoutParams layoutParams = new LayoutParams( - LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); - exoPlayerView = new ExoPlayerView(getContext()); - exoPlayerView.setLayoutParams(layoutParams); - - addView(exoPlayerView, 0, layoutParams); - - mainHandler = new Handler(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - initializePlayer(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - /* We want to be able to continue playing audio when switching tabs. - * Leave this here in case it causes issues. - */ - // stopPlayback(); - } - - // LifecycleEventListener implementation - - @Override - public void onHostResume() { - if (!playInBackground || !isInBackground) { - setPlayWhenReady(!isPaused); - } - isInBackground = false; - } - - @Override - public void onHostPause() { - isInBackground = true; - if (playInBackground) { - return; - } - setPlayWhenReady(false); - } - - @Override - public void onHostDestroy() { - stopPlayback(); - } - - public void cleanUpResources() { - stopPlayback(); - } - - //BandwidthMeter.EventListener implementation - @Override - public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) { - if (mReportBandwidth) { - if (player == null) { - eventEmitter.bandwidthReport(bitrate, 0, 0, "-1"); - } else { - Format videoFormat = player.getVideoFormat(); - int width = videoFormat != null ? videoFormat.width : 0; - int height = videoFormat != null ? videoFormat.height : 0; - String trackId = videoFormat != null ? videoFormat.id : "-1"; - eventEmitter.bandwidthReport(bitrate, height, width, trackId); - } - } - } - - // Internal methods - - /** - * Toggling the visibility of the player control view - */ - private void togglePlayerControlVisibility() { - if(player == null) return; - reLayout(playerControlView); - if (playerControlView.isVisible()) { - playerControlView.hide(); - } else { - playerControlView.show(); - } - } - - /** - * Initializing Player control - */ - private void initializePlayerControl() { - if (playerControlView == null) { - playerControlView = new PlayerControlView(getContext()); - } - - // Setting the player for the playerControlView - playerControlView.setPlayer(player); - playerControlView.show(); - playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container); - - // Invoking onClick event for exoplayerView - exoPlayerView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (!isPlayingAd()) { - togglePlayerControlVisibility(); - } - } - }); - - //Handling the playButton click event - ImageButton playButton = playerControlView.findViewById(R.id.exo_play); - playButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (player != null && player.getPlaybackState() == Player.STATE_ENDED) { - player.seekTo(0); - } - setPausedModifier(false); - } - }); - - //Handling the pauseButton click event - ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause); - pauseButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - setPausedModifier(true); - } - }); - - // Invoking onPlayerStateChanged event for Player - eventListener = new Player.EventListener() { - @Override - public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { - reLayout(playPauseControlContainer); - //Remove this eventListener once its executed. since UI will work fine once after the reLayout is done - player.removeListener(eventListener); - } - }; - player.addListener(eventListener); - } - - /** - * Adding Player control to the frame layout - */ - private void addPlayerControl() { - if(player == null) return; - LayoutParams layoutParams = new LayoutParams( - LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); - playerControlView.setLayoutParams(layoutParams); - int indexOfPC = indexOfChild(playerControlView); - if (indexOfPC != -1) { - removeViewAt(indexOfPC); - } - addView(playerControlView, 1, layoutParams); - } - - /** - * Update the layout - * @param view view needs to update layout - * - * This is a workaround for the open bug in react-native: https://github.com/facebook/react-native/issues/17968 - */ - private void reLayout(View view) { - if (view == null) return; - view.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - view.layout(view.getLeft(), view.getTop(), view.getMeasuredWidth(), view.getMeasuredHeight()); - } - - private void initializePlayer() { - ReactExoplayerView self = this; - // This ensures all props have been settled, to avoid async racing conditions. - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - if (player == null) { - TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); - trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - trackSelector.setParameters(trackSelector.buildUponParameters() - .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); - - DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); - DefaultLoadControl.Builder defaultLoadControlBuilder = new DefaultLoadControl.Builder(); - defaultLoadControlBuilder.setAllocator(allocator); - defaultLoadControlBuilder.setBufferDurationsMs(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs); - defaultLoadControlBuilder.setTargetBufferBytes(-1); - defaultLoadControlBuilder.setPrioritizeTimeOverSizeThresholds(true); - DefaultLoadControl defaultLoadControl = defaultLoadControlBuilder.createDefaultLoadControl(); - DefaultRenderersFactory renderersFactory = - new DefaultRenderersFactory(getContext()) - .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); - // DRM - DrmSessionManager drmSessionManager = null; - if (self.drmUUID != null) { - try { - drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, - self.drmLicenseHeader); - } catch (UnsupportedDrmException e) { - int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported - : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME - ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); - eventEmitter.error(getResources().getString(errorStringId), e); - return; - } - } - // End DRM - player = ExoPlayerFactory.newSimpleInstance(getContext(), renderersFactory, - trackSelector, defaultLoadControl, drmSessionManager, bandwidthMeter); - player.addListener(self); - player.addMetadataOutput(self); //a random comment here - adsLoader.setPlayer(player); - exoPlayerView.setPlayer(player); - audioBecomingNoisyReceiver.setListener(self); - bandwidthMeter.addEventListener(new Handler(), self); - setPlayWhenReady(!isPaused); - playerNeedsSource = true; - - PlaybackParameters params = new PlaybackParameters(rate, 1f); - player.setPlaybackParameters(params); - } - if (playerNeedsSource && srcUri != null) { - exoPlayerView.invalidateAspectRatio(); - - ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(srcUri, extension); - MediaSource mediaSourceWithAds = new AdsMediaSource(videoSource, mediaDataSourceFactory, adsLoader, exoPlayerView); - MediaSource mediaSource; - if (mediaSourceList.size() == 0) { - mediaSource = mediaSourceWithAds; - } else { - mediaSourceList.add(0, mediaSourceWithAds); - MediaSource[] textSourceArray = mediaSourceList.toArray( - new MediaSource[mediaSourceList.size()] - ); - mediaSource = new MergingMediaSource(textSourceArray); - } - - boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; - if (haveResumePosition) { - player.seekTo(resumeWindow, resumePosition); - } - player.prepare(mediaSource, !haveResumePosition, false); - playerNeedsSource = false; - - eventEmitter.loadStart(); - loadVideoStarted = true; - } - - // Initializing the playerControlView - initializePlayerControl(); - setControls(controls); - applyModifiers(); - } - }, 1); - } - - private DrmSessionManager buildDrmSessionManager(UUID uuid, - String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException { - if (Util.SDK_INT < 18) { - return null; - } - HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, - buildHttpDataSourceFactory(false)); - if (keyRequestPropertiesArray != null) { - for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) { - drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i], - keyRequestPropertiesArray[i + 1]); - } - } - return new DefaultDrmSessionManager<>(uuid, - FrameworkMediaDrm.newInstance(uuid), drmCallback, null, false, 3); - } - - private MediaSource buildMediaSource(Uri uri, String overrideExtension) { - int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension - : uri.getLastPathSegment()); - switch (type) { - case C.TYPE_SS: - return new SsMediaSource.Factory( - new DefaultSsChunkSource.Factory(mediaDataSourceFactory), - buildDataSourceFactory(false) - ).setLoadErrorHandlingPolicy( - config.buildLoadErrorHandlingPolicy(minLoadRetryCount) - ).createMediaSource(uri); - case C.TYPE_DASH: - return new DashMediaSource.Factory( - new DefaultDashChunkSource.Factory(mediaDataSourceFactory), - buildDataSourceFactory(false) - ).setLoadErrorHandlingPolicy( - config.buildLoadErrorHandlingPolicy(minLoadRetryCount) - ).createMediaSource(uri); - case C.TYPE_HLS: - return new HlsMediaSource.Factory( - mediaDataSourceFactory - ).setLoadErrorHandlingPolicy( - config.buildLoadErrorHandlingPolicy(minLoadRetryCount) - ).createMediaSource(uri); - case C.TYPE_OTHER: - return new ProgressiveMediaSource.Factory( - mediaDataSourceFactory - ).setLoadErrorHandlingPolicy( - config.buildLoadErrorHandlingPolicy(minLoadRetryCount) - ).createMediaSource(uri); - default: { - throw new IllegalStateException("Unsupported type: " + type); - } - } - } - - private ArrayList buildTextSources() { - ArrayList textSources = new ArrayList<>(); - if (textTracks == null) { - return textSources; - } - - for (int i = 0; i < textTracks.size(); ++i) { - ReadableMap textTrack = textTracks.getMap(i); - String language = textTrack.getString("language"); - String title = textTrack.hasKey("title") - ? textTrack.getString("title") : language + " " + i; - Uri uri = Uri.parse(textTrack.getString("uri")); - MediaSource textSource = buildTextSource(title, uri, textTrack.getString("type"), - language); - if (textSource != null) { - textSources.add(textSource); - } - } - return textSources; - } - - private MediaSource buildTextSource(String title, Uri uri, String mimeType, String language) { - Format textFormat = Format.createTextSampleFormat(title, mimeType, Format.NO_VALUE, language); - return new SingleSampleMediaSource.Factory(mediaDataSourceFactory) - .createMediaSource(uri, textFormat, C.TIME_UNSET); - } - - private void releasePlayer() { - if (player != null) { - updateResumePosition(); - player.release(); - player.removeMetadataOutput(this); - trackSelector = null; - player = null; - } - adsLoader.release(); - progressHandler.removeMessages(SHOW_PROGRESS); - themedReactContext.removeLifecycleEventListener(this); - audioBecomingNoisyReceiver.removeListener(); - bandwidthMeter.removeEventListener(this); - } - - private boolean requestAudioFocus() { - if (disableFocus || srcUri == null) { - return true; - } - int result = audioManager.requestAudioFocus(this, - AudioManager.STREAM_MUSIC, - AudioManager.AUDIOFOCUS_GAIN); - return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; - } - - private void setPlayWhenReady(boolean playWhenReady) { - if (player == null) { - return; - } - - if (playWhenReady) { - boolean hasAudioFocus = requestAudioFocus(); - if (hasAudioFocus) { - player.setPlayWhenReady(true); - } - } else { - player.setPlayWhenReady(false); - } - } - - private void startPlayback() { - if (player != null) { - switch (player.getPlaybackState()) { - case Player.STATE_IDLE: - case Player.STATE_ENDED: - initializePlayer(); - break; - case Player.STATE_BUFFERING: - case Player.STATE_READY: - if (!player.getPlayWhenReady()) { - setPlayWhenReady(true); - } - break; - default: - break; - } - - } else { - initializePlayer(); - } - if (!disableFocus) { - setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback); - } - } - - private void pausePlayback() { - if (player != null) { - if (player.getPlayWhenReady()) { - setPlayWhenReady(false); - } - } - setKeepScreenOn(false); - } - - private void stopPlayback() { - onStopPlayback(); - releasePlayer(); - } - - private void onStopPlayback() { - if (isFullscreen) { - setFullscreen(false); - } - audioManager.abandonAudioFocus(this); - } - - private void updateResumePosition() { - resumeWindow = player.getCurrentWindowIndex(); - resumePosition = player.isCurrentWindowSeekable() ? Math.max(0, player.getCurrentPosition()) - : C.TIME_UNSET; - } - - private void clearResumePosition() { - resumeWindow = C.INDEX_UNSET; - resumePosition = C.TIME_UNSET; - } - - /** - * Returns a new DataSource factory. - * - * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new - * DataSource factory. - * @return A new DataSource factory. - */ - private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) { - return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, - useBandwidthMeter ? bandwidthMeter : null, requestHeaders); - } - - /** - * Returns a new HttpDataSource factory. - * - * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new - * DataSource factory. - * @return A new HttpDataSource factory. - */ - private HttpDataSource.Factory buildHttpDataSourceFactory(boolean useBandwidthMeter) { - return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext, useBandwidthMeter ? bandwidthMeter : null, requestHeaders); - } - - - // AudioManager.OnAudioFocusChangeListener implementation - - @Override - public void onAudioFocusChange(int focusChange) { - switch (focusChange) { - case AudioManager.AUDIOFOCUS_LOSS: - eventEmitter.audioFocusChanged(false); - audioManager.abandonAudioFocus(this); - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - eventEmitter.audioFocusChanged(false); - break; - case AudioManager.AUDIOFOCUS_GAIN: - eventEmitter.audioFocusChanged(true); - break; - default: - break; - } - - if (player != null) { - if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { - // Lower the volume - if (!muted) { - player.setVolume(audioVolume * 0.8f); - } - } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { - // Raise it back to normal - if (!muted) { - player.setVolume(audioVolume * 1); - } - } - } - } - - // AudioBecomingNoisyListener implementation - - @Override - public void onAudioBecomingNoisy() { - eventEmitter.audioBecomingNoisy(); - } - - // Player.EventListener implementation - - @Override - public void onLoadingChanged(boolean isLoading) { - // Do nothing. - } - - @Override - public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { - String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState="; - switch (playbackState) { - case Player.STATE_IDLE: - text += "idle"; - eventEmitter.idle(); - clearProgressMessageHandler(); - if (!playWhenReady) { - setKeepScreenOn(false); - } - break; - case Player.STATE_BUFFERING: - text += "buffering"; - onBuffering(true); - clearProgressMessageHandler(); - setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback); - break; - case Player.STATE_READY: - text += "ready"; - eventEmitter.ready(); - onBuffering(false); - startProgressHandler(); - videoLoaded(); - // Setting the visibility for the playerControlView - if (playerControlView != null) { - playerControlView.show(); - } - setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback); - break; - case Player.STATE_ENDED: - text += "ended"; - eventEmitter.end(); - onStopPlayback(); - setKeepScreenOn(false); - break; - default: - text += "unknown"; - break; - } - Log.d(TAG, text); - } - - private void startProgressHandler() { - progressHandler.sendEmptyMessage(SHOW_PROGRESS); - } - - /* - 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 - the video is not paused). This clears all existing messages. - */ - private void clearProgressMessageHandler() { - progressHandler.removeMessages(SHOW_PROGRESS); - } - - private void videoLoaded() { - if (!player.isPlayingAd() && loadVideoStarted) { - loadVideoStarted = false; - setSelectedAudioTrack(audioTrackType, audioTrackValue); - setSelectedVideoTrack(videoTrackType, videoTrackValue); - setSelectedTextTrack(textTrackType, textTrackValue); - Format videoFormat = player.getVideoFormat(); - int width = videoFormat != null ? videoFormat.width : 0; - int height = videoFormat != null ? videoFormat.height : 0; - String trackId = videoFormat != null ? videoFormat.id : "-1"; - eventEmitter.load(player.getDuration(), player.getCurrentPosition(), width, height, - getAudioTrackInfo(), getTextTrackInfo(), getVideoTrackInfo(), trackId); - } - } - - private WritableArray getAudioTrackInfo() { - WritableArray audioTracks = Arguments.createArray(); - - MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); - int index = getTrackRendererIndex(C.TRACK_TYPE_AUDIO); - if (info == null || index == C.INDEX_UNSET) { - return audioTracks; - } - - TrackGroupArray groups = info.getTrackGroups(index); - for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - WritableMap audioTrack = Arguments.createMap(); - audioTrack.putInt("index", i); - audioTrack.putString("title", format.id != null ? format.id : ""); - audioTrack.putString("type", format.sampleMimeType); - audioTrack.putString("language", format.language != null ? format.language : ""); - audioTrack.putString("bitrate", format.bitrate == Format.NO_VALUE ? "" - : String.format(Locale.US, "%.2fMbps", format.bitrate / 1000000f)); - audioTracks.pushMap(audioTrack); - } - return audioTracks; - } - private WritableArray getVideoTrackInfo() { - WritableArray videoTracks = Arguments.createArray(); - - MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); - int index = getTrackRendererIndex(C.TRACK_TYPE_VIDEO); - if (info == null || index == C.INDEX_UNSET) { - return videoTracks; - } - - TrackGroupArray groups = info.getTrackGroups(index); - for (int i = 0; i < groups.length; ++i) { - TrackGroup group = groups.get(i); - - for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { - Format format = group.getFormat(trackIndex); - WritableMap videoTrack = Arguments.createMap(); - videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width); - videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height); - videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); - videoTrack.putString("codecs", format.codecs != null ? format.codecs : ""); - videoTrack.putString("trackId", - format.id == null ? String.valueOf(trackIndex) : format.id); - videoTracks.pushMap(videoTrack); - } - } - return videoTracks; - } - - private WritableArray getTextTrackInfo() { - WritableArray textTracks = Arguments.createArray(); - - MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); - int index = getTrackRendererIndex(C.TRACK_TYPE_TEXT); - if (info == null || index == C.INDEX_UNSET) { - return textTracks; - } - - TrackGroupArray groups = info.getTrackGroups(index); - for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - WritableMap textTrack = Arguments.createMap(); - textTrack.putInt("index", i); - textTrack.putString("title", format.id != null ? format.id : ""); - textTrack.putString("type", format.sampleMimeType); - textTrack.putString("language", format.language != null ? format.language : ""); - textTracks.pushMap(textTrack); - } - return textTracks; - } - - private void onBuffering(boolean buffering) { - if (isBuffering == buffering) { - return; - } - - isBuffering = buffering; - if (buffering) { - eventEmitter.buffering(true); - } else { - eventEmitter.buffering(false); - } - } - - @Override - public void onPositionDiscontinuity(int reason) { - if (playerNeedsSource) { - // This will only occur if the user has performed a seek whilst in the error state. Update the - // resume position so that if the user then retries, playback will resume from the position to - // which they seeked. - updateResumePosition(); - } - // When repeat is turned on, reaching the end of the video will not cause a state change - // so we need to explicitly detect it. - if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION - && player.getRepeatMode() == Player.REPEAT_MODE_ONE) { - eventEmitter.end(); - } - } - - @Override - public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { - // Do nothing. - } - - @Override - public void onSeekProcessed() { - eventEmitter.seek(player.getCurrentPosition(), seekTime); - seekTime = C.TIME_UNSET; - } - - @Override - public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { - // Do nothing. - } - - @Override - public void onRepeatModeChanged(int repeatMode) { - // Do nothing. - } - - @Override - public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - // Do Nothing. - } - - @Override - public void onPlaybackParametersChanged(PlaybackParameters params) { - eventEmitter.playbackRateChange(params.speed); - } - - @Override - public void onPlayerError(ExoPlaybackException e) { - String errorString = "ExoPlaybackException type : " + e.type; - Exception ex = e; - if (e.type == ExoPlaybackException.TYPE_RENDERER) { - Exception cause = e.getRendererException(); - if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { - // Special case for decoder initialization failures. - MediaCodecRenderer.DecoderInitializationException decoderInitializationException = - (MediaCodecRenderer.DecoderInitializationException) cause; - if (decoderInitializationException.codecInfo.name == null) { - if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) { - errorString = getResources().getString(R.string.error_querying_decoders); - } else if (decoderInitializationException.secureDecoderRequired) { - errorString = getResources().getString(R.string.error_no_secure_decoder, - decoderInitializationException.mimeType); - } else { - errorString = getResources().getString(R.string.error_no_decoder, - decoderInitializationException.mimeType); - } - } else { - errorString = getResources().getString(R.string.error_instantiating_decoder, - decoderInitializationException.codecInfo.name); - } - } - } - else if (e.type == ExoPlaybackException.TYPE_SOURCE) { - errorString = getResources().getString(R.string.unrecognized_media_format); - } - eventEmitter.error(errorString, ex); - playerNeedsSource = true; - if (isBehindLiveWindow(e)) { - clearResumePosition(); - initializePlayer(); - } else { - updateResumePosition(); - } - } - - private static boolean isBehindLiveWindow(ExoPlaybackException e) { - Log.e("ExoPlayer Exception", e.toString()); - if (e.type != ExoPlaybackException.TYPE_SOURCE) { - return false; - } - Throwable cause = e.getSourceException(); - while (cause != null) { - if (cause instanceof BehindLiveWindowException || - cause instanceof HttpDataSource.HttpDataSourceException) { - return true; - } - cause = cause.getCause(); - } - return false; - } - - public int getTrackRendererIndex(int trackType) { - if (player != null) { - int rendererCount = player.getRendererCount(); - for (int rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) { - if (player.getRendererType(rendererIndex) == trackType) { - return rendererIndex; - } - } - } - return C.INDEX_UNSET; - } - - @Override - public void onMetadata(Metadata metadata) { - eventEmitter.timedMetadata(metadata); - } - - // ReactExoplayerViewManager public api - - public void setSrc(final Uri uri, final String extension, Map headers) { - if (uri != null) { - boolean isOriginalSourceNull = srcUri == null; - boolean isSourceEqual = uri.equals(srcUri); - - this.srcUri = uri; - this.extension = extension; - this.requestHeaders = headers; - this.mediaDataSourceFactory = - DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter, - this.requestHeaders); - - if (!isOriginalSourceNull && !isSourceEqual) { - reloadSource(); - } - } - } - - public void setProgressUpdateInterval(final float progressUpdateInterval) { - mProgressUpdateInterval = progressUpdateInterval; - } - - public void setReportBandwidth(boolean reportBandwidth) { - mReportBandwidth = reportBandwidth; - } - - public void setAdTagUrl(final Uri uri) { - adTagUrl = uri; - adsLoader = new ImaAdsLoader(this.themedReactContext, adTagUrl); - adsLoader = new ImaAdsLoader.Builder(this.themedReactContext).setAdEventListener(this).buildForAdTag(adTagUrl); - - } - - public void setRawSrc(final Uri uri, final String extension) { - if (uri != null) { - boolean isOriginalSourceNull = srcUri == null; - boolean isSourceEqual = uri.equals(srcUri); - - this.srcUri = uri; - this.extension = extension; - this.mediaDataSourceFactory = buildDataSourceFactory(true); - - if (!isOriginalSourceNull && !isSourceEqual) { - reloadSource(); - } - } - } - - public void setTextTracks(ReadableArray textTracks) { - this.textTracks = textTracks; - reloadSource(); - } - - private void reloadSource() { - playerNeedsSource = true; - initializePlayer(); - } - - public void setResizeModeModifier(@ResizeMode.Mode int resizeMode) { - exoPlayerView.setResizeMode(resizeMode); - } - - private void applyModifiers() { - setRepeatModifier(repeat); - setMutedModifier(muted); - } - - public void setRepeatModifier(boolean repeat) { - if (player != null) { - if (repeat) { - player.setRepeatMode(Player.REPEAT_MODE_ONE); - } else { - player.setRepeatMode(Player.REPEAT_MODE_OFF); - } - } - this.repeat = repeat; - } - - public void setPreventsDisplaySleepDuringVideoPlayback(boolean preventsDisplaySleepDuringVideoPlayback) { - this.preventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback; - } - - public void setSelectedTrack(int trackType, String type, Dynamic value) { - if (player == null) return; - int rendererIndex = getTrackRendererIndex(trackType); - if (rendererIndex == C.INDEX_UNSET) { - return; - } - MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); - if (info == null) { - return; - } - - TrackGroupArray groups = info.getTrackGroups(rendererIndex); - int groupIndex = C.INDEX_UNSET; - int[] tracks = {0} ; - - if (TextUtils.isEmpty(type)) { - type = "default"; - } - - DefaultTrackSelector.Parameters disableParameters = trackSelector.getParameters() - .buildUpon() - .setRendererDisabled(rendererIndex, true) - .build(); - - if (type.equals("disabled")) { - trackSelector.setParameters(disableParameters); - return; - } else if (type.equals("language")) { - for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - if (format.language != null && format.language.equals(value.asString())) { - groupIndex = i; - break; - } - } - } else if (type.equals("title")) { - for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - if (format.id != null && format.id.equals(value.asString())) { - groupIndex = i; - break; - } - } - } else if (type.equals("index")) { - if (value.asInt() < groups.length) { - groupIndex = value.asInt(); - } - } else if (type.equals("resolution")) { - int height = value.asInt(); - for (int i = 0; i < groups.length; ++i) { // Search for the exact height - TrackGroup group = groups.get(i); - for (int j = 0; j < group.length; j++) { - Format format = group.getFormat(j); - if (format.height == height) { - groupIndex = i; - tracks[0] = j; - break; - } - } - } - } else if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default - // Use system settings if possible - CaptioningManager captioningManager - = (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE); - if (captioningManager != null && captioningManager.isEnabled()) { - groupIndex = getGroupIndexForDefaultLocale(groups); - } - } else if (rendererIndex == C.TRACK_TYPE_AUDIO) { // Audio default - groupIndex = getGroupIndexForDefaultLocale(groups); - } - - if (groupIndex == C.INDEX_UNSET && trackType == C.TRACK_TYPE_VIDEO && groups.length != 0) { // Video auto - // Add all tracks as valid options for ABR to choose from - TrackGroup group = groups.get(0); - tracks = new int[group.length]; - groupIndex = 0; - for (int j = 0; j < group.length; j++) { - tracks[j] = j; - } - } - - if (groupIndex == C.INDEX_UNSET) { - trackSelector.setParameters(disableParameters); - return; - } - - DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters() - .buildUpon() - .setRendererDisabled(rendererIndex, false) - .setSelectionOverride(rendererIndex, groups, - new DefaultTrackSelector.SelectionOverride(groupIndex, tracks)) - .build(); - trackSelector.setParameters(selectionParameters); - } - - private int getGroupIndexForDefaultLocale(TrackGroupArray groups) { - if (groups.length == 0){ - return C.INDEX_UNSET; - } - - int groupIndex = 0; // default if no match - String locale2 = Locale.getDefault().getLanguage(); // 2 letter code - String locale3 = Locale.getDefault().getISO3Language(); // 3 letter code - for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - String language = format.language; - if (language != null && (language.equals(locale2) || language.equals(locale3))) { - groupIndex = i; - break; - } - } - return groupIndex; - } - - public void setSelectedVideoTrack(String type, Dynamic value) { - videoTrackType = type; - videoTrackValue = value; - setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue); - } - - public void setSelectedAudioTrack(String type, Dynamic value) { - audioTrackType = type; - audioTrackValue = value; - setSelectedTrack(C.TRACK_TYPE_AUDIO, audioTrackType, audioTrackValue); - } - - public void setSelectedTextTrack(String type, Dynamic value) { - textTrackType = type; - textTrackValue = value; - setSelectedTrack(C.TRACK_TYPE_TEXT, textTrackType, textTrackValue); - } - - public void setPausedModifier(boolean paused) { - isPaused = paused; - if (player != null) { - if (!paused) { - startPlayback(); - } else { - pausePlayback(); - } - } - } - - public void setMutedModifier(boolean muted) { - this.muted = muted; - audioVolume = muted ? 0.f : 1.f; - if (player != null) { - player.setVolume(audioVolume); - } - } - - - public void setVolumeModifier(float volume) { - audioVolume = volume; - if (player != null) { - player.setVolume(audioVolume); - } - } - - public void seekTo(long positionMs) { - if (player != null) { - seekTime = positionMs; - player.seekTo(positionMs); - } - } - - public void setRateModifier(float newRate) { - rate = newRate; - - if (player != null) { - PlaybackParameters params = new PlaybackParameters(rate, 1f); - player.setPlaybackParameters(params); - } - } - - public void setMaxBitRateModifier(int newMaxBitRate) { - maxBitRate = newMaxBitRate; - if (player != null) { - trackSelector.setParameters(trackSelector.buildUponParameters() - .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); - } - } - - public void setMinLoadRetryCountModifier(int newMinLoadRetryCount) { - minLoadRetryCount = newMinLoadRetryCount; - releasePlayer(); - initializePlayer(); - } - - public void setPlayInBackground(boolean playInBackground) { - this.playInBackground = playInBackground; - } - - public void setDisableFocus(boolean disableFocus) { - this.disableFocus = disableFocus; - } - - public void setFullscreen(boolean fullscreen) { - if (fullscreen == isFullscreen) { - return; // Avoid generating events when nothing is changing - } - isFullscreen = fullscreen; - - Activity activity = themedReactContext.getCurrentActivity(); - if (activity == null) { - return; - } - Window window = activity.getWindow(); - View decorView = window.getDecorView(); - int uiOptions; - if (isFullscreen) { - if (Util.SDK_INT >= 19) { // 4.4+ - uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION - | SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | SYSTEM_UI_FLAG_FULLSCREEN; - } else { - uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION - | SYSTEM_UI_FLAG_FULLSCREEN; - } - eventEmitter.fullscreenWillPresent(); - decorView.setSystemUiVisibility(uiOptions); - eventEmitter.fullscreenDidPresent(); - } else { - uiOptions = View.SYSTEM_UI_FLAG_VISIBLE; - eventEmitter.fullscreenWillDismiss(); - decorView.setSystemUiVisibility(uiOptions); - eventEmitter.fullscreenDidDismiss(); - } - } - - public void setUseTextureView(boolean useTextureView) { - boolean finallyUseTextureView = useTextureView && this.drmUUID == null; - exoPlayerView.setUseTextureView(finallyUseTextureView); - } - - public void setHideShutterView(boolean hideShutterView) { - exoPlayerView.setHideShutterView(hideShutterView); - } - - public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) { - minBufferMs = newMinBufferMs; - maxBufferMs = newMaxBufferMs; - bufferForPlaybackMs = newBufferForPlaybackMs; - bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs; - releasePlayer(); - initializePlayer(); - } - - public void setDrmType(UUID drmType) { - this.drmUUID = drmType; - } - - public void setDrmLicenseUrl(String licenseUrl){ - this.drmLicenseUrl = licenseUrl; - } - - public void setDrmLicenseHeader(String[] header){ - this.drmLicenseHeader = header; - } - - - @Override - public void onDrmKeysLoaded() { - Log.d("DRM Info", "onDrmKeysLoaded"); - } - - @Override - public void onDrmSessionManagerError(Exception e) { - Log.d("DRM Info", "onDrmSessionManagerError"); - eventEmitter.error("onDrmSessionManagerError", e); - } - - @Override - public void onDrmKeysRestored() { - Log.d("DRM Info", "onDrmKeysRestored"); - } - - @Override - public void onDrmKeysRemoved() { - Log.d("DRM Info", "onDrmKeysRemoved"); - } - - /** - * Handling controls prop - * - * @param controls Controls prop, if true enable controls, if false disable them - */ - public void setControls(boolean controls) { - this.controls = controls; - if (player == null || exoPlayerView == null) return; - if (controls) { - addPlayerControl(); - } else { - int indexOfPC = indexOfChild(playerControlView); - if (indexOfPC != -1) { - removeViewAt(indexOfPC); - } - } - } - - @Override - public void onAdEvent(AdEvent adEvent) { - eventEmitter.receiveAdEvent(adEvent.getType().name()); - } - -} diff --git a/android/build.gradle b/android/build.gradle index ebacc8e9..17b1d682 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -45,5 +45,7 @@ dependencies { implementation('com.google.android.exoplayer:extension-okhttp:2.18.1') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } + implementation 'com.google.android.exoplayer:extension-ima:2.11.4' + implementation "com.squareup.okhttp3:okhttp:" + '$OKHTTP_VERSION' } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index 462d610c..b29389ab 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -19,16 +19,19 @@ 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.source.ads.AdsLoader; 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.SubtitleView; +import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.video.VideoSize; import java.util.List; +import java.util.ArrayList; @TargetApi(16) -public final class ExoPlayerView extends FrameLayout { +public final class ExoPlayerView extends FrameLayout implements AdsLoader.AdViewProvider { private View surfaceView; private final View shutterView; @@ -38,6 +41,7 @@ public final class ExoPlayerView extends FrameLayout { private ExoPlayer player; private Context context; private ViewGroup.LayoutParams layoutParams; + private final FrameLayout adOverlayFrameLayout; private boolean useTextureView = true; private boolean useSecureView = false; @@ -83,7 +87,10 @@ public final class ExoPlayerView extends FrameLayout { layout.addView(shutterView, 1, layoutParams); layout.addView(subtitleLayout, 2, layoutParams); + adOverlayFrameLayout = new FrameLayout(context); + addViewInLayout(layout, 0, aspectRatioParams); + addViewInLayout(adOverlayFrameLayout, 1, layoutParams); } private void clearVideoView() { @@ -139,6 +146,28 @@ public final class ExoPlayerView extends FrameLayout { shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE); } + @Override + public void requestLayout() { + super.requestLayout(); + post(measureAndLayout); + } + + // AdsLoader.AdViewProvider implementation. + + @Override + public ViewGroup getAdViewGroup() { + return Assertions.checkNotNull(adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback"); + } + + @Override + public View[] getAdOverlayViews() { + ArrayList overlayViews = new ArrayList<>(); + if (adOverlayFrameLayout != null) { + overlayViews.add(adOverlayFrameLayout); + } + return overlayViews.toArray(new View[0]); + } + /** * Set the {@link ExoPlayer} to use. The {@link ExoPlayer#addListener} method of the * player will be called and previous diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 6d94dc6c..5c73be67 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -31,6 +31,7 @@ import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.util.RNLog; +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; @@ -93,9 +94,13 @@ import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet; import com.google.android.exoplayer2.source.dash.manifest.Representation; import com.google.android.exoplayer2.source.dash.manifest.Descriptor; +import com.google.android.exoplayer2.ext.ima.ImaAdsLoader; +import com.google.android.exoplayer2.source.ads.AdsMediaSource; + import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; +import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -119,7 +124,8 @@ class ReactExoplayerView extends FrameLayout implements BandwidthMeter.EventListener, BecomingNoisyListener, AudioManager.OnAudioFocusChangeListener, - DrmSessionEventListener { + DrmSessionEventListener, + AdEvent.AdEventListener { public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1; public static final double DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE = 0; @@ -144,6 +150,7 @@ class ReactExoplayerView extends FrameLayout implements private ExoPlayerView exoPlayerView; private FullScreenPlayerView fullScreenPlayerView; + private ImaAdsLoader adsLoader; private DataSource.Factory mediaDataSourceFactory; private ExoPlayer player; @@ -203,6 +210,7 @@ class ReactExoplayerView extends FrameLayout implements private String drmLicenseUrl = null; private String[] drmLicenseHeader = null; private boolean controls; + private Uri adTagUrl; // \ End props // React @@ -221,6 +229,9 @@ class ReactExoplayerView extends FrameLayout implements switch (msg.what) { case SHOW_PROGRESS: if (player != null) { + if (isPlayingAd()) { + playerControlView.hide(); + } long pos = player.getCurrentPosition(); long bufferedDuration = player.getBufferedPercentage() * player.getDuration() / 100; long duration = player.getDuration(); @@ -256,6 +267,8 @@ class ReactExoplayerView extends FrameLayout implements this.config = config; this.bandwidthMeter = config.getBandwidthMeter(); + adsLoader = new ImaAdsLoader(this.themedReactContext, Uri.EMPTY); + createViews(); audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); @@ -263,6 +276,9 @@ class ReactExoplayerView extends FrameLayout implements audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext); } + private boolean isPlayingAd() { + return player != null && player.isPlayingAd() && player.getPlayWhenReady(); + } @Override public void setId(int id) { @@ -380,7 +396,9 @@ class ReactExoplayerView extends FrameLayout implements exoPlayerView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - togglePlayerControlVisibility(); + if (!isPlayingAd()) { + togglePlayerControlVisibility(); + } } }); @@ -604,6 +622,7 @@ class ReactExoplayerView extends FrameLayout implements .setLoadControl(loadControl) .build(); player.addListener(self); + adsLoader.setPlayer(player); exoPlayerView.setPlayer(player); audioBecomingNoisyReceiver.setListener(self); bandwidthMeter.addEventListener(new Handler(), self); @@ -634,11 +653,12 @@ class ReactExoplayerView extends FrameLayout implements private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) { ArrayList mediaSourceList = buildTextSources(); MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager); + MediaSource mediaSourceWithAds = new AdsMediaSource(videoSource, mediaDataSourceFactory, adsLoader, exoPlayerView); MediaSource mediaSource; if (mediaSourceList.size() == 0) { - mediaSource = videoSource; + mediaSource = mediaSourceWithAds; } else { - mediaSourceList.add(0, videoSource); + mediaSourceList.add(0, mediaSourceWithAds); MediaSource[] textSourceArray = mediaSourceList.toArray( new MediaSource[mediaSourceList.size()] ); @@ -816,6 +836,7 @@ class ReactExoplayerView extends FrameLayout implements trackSelector = null; player = null; } + adsLoader.release(); progressHandler.removeMessages(SHOW_PROGRESS); themedReactContext.removeLifecycleEventListener(this); audioBecomingNoisyReceiver.removeListener(); @@ -938,7 +959,6 @@ class ReactExoplayerView extends FrameLayout implements case AudioManager.AUDIOFOCUS_LOSS: this.hasAudioFocus = false; eventEmitter.audioFocusChanged(false); - pausePlayback(); audioManager.abandonAudioFocus(this); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: @@ -1046,7 +1066,7 @@ class ReactExoplayerView extends FrameLayout implements } private void videoLoaded() { - if (loadVideoStarted) { + if (!player.isPlayingAd() && loadVideoStarted) { loadVideoStarted = false; if (audioTrackType != null) { setSelectedAudioTrack(audioTrackType, audioTrackValue); @@ -1416,6 +1436,12 @@ class ReactExoplayerView extends FrameLayout implements mReportBandwidth = reportBandwidth; } + public void setAdTagUrl(final Uri uri) { + adTagUrl = uri; + adsLoader = new ImaAdsLoader(this.themedReactContext, adTagUrl); + adsLoader = new ImaAdsLoader.Builder(this.themedReactContext).setAdEventListener(this).buildForAdTag(adTagUrl); + } + public void setRawSrc(final Uri uri, final String extension) { if (uri != null) { boolean isSourceEqual = uri.equals(srcUri); @@ -1905,4 +1931,9 @@ class ReactExoplayerView extends FrameLayout implements public void setSubtitleStyle(SubtitleStyle style) { exoPlayerView.setSubtitleStyle(style); } + + @Override + public void onAdEvent(AdEvent adEvent) { + eventEmitter.receiveAdEvent(adEvent.getType().name()); + } }