From 59a57759f3f77105cba58d3d3e2c3eb55ce61312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Czernek?= Date: Fri, 22 May 2020 17:47:44 +0200 Subject: [PATCH] [expo-av][Android] Fix adaptive streaming for exoplayer in expo-av. (#8380) --- .../av/SharedCookiesDataSourceFactory.java | 5 ++- ...haredCookiesDataSourceFactoryProvider.java | 5 ++- packages/expo-av/CHANGELOG.md | 1 + .../modules/av/player/MediaPlayerData.java | 1 - .../av/player/SimpleExoPlayerData.java | 37 ++++++++++++------- .../CustomHeadersOkHttpDataSourceFactory.java | 3 -- .../datasource/DataSourceFactoryProvider.java | 3 +- .../SharedCookiesDataSourceFactory.java | 5 ++- ...haredCookiesDataSourceFactoryProvider.java | 5 ++- 9 files changed, 39 insertions(+), 26 deletions(-) diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/universal/av/SharedCookiesDataSourceFactory.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/universal/av/SharedCookiesDataSourceFactory.java index 2b015742fbabe..5b55d3ac8aade 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/universal/av/SharedCookiesDataSourceFactory.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/universal/av/SharedCookiesDataSourceFactory.java @@ -4,6 +4,7 @@ import com.facebook.react.modules.network.NetworkingModule; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.upstream.TransferListener; import java.util.Map; @@ -12,9 +13,9 @@ public class SharedCookiesDataSourceFactory implements DataSource.Factory { private final DataSource.Factory mDataSourceFactory; - public SharedCookiesDataSourceFactory(ReactContext reactApplicationContext, String userAgent, Map requestHeaders) { + public SharedCookiesDataSourceFactory(ReactContext reactApplicationContext, String userAgent, Map requestHeaders, TransferListener transferListener) { OkHttpClient reactNativeOkHttpClient = reactApplicationContext.getNativeModule(NetworkingModule.class).mClient; - mDataSourceFactory = new DefaultDataSourceFactory(reactApplicationContext, null, new CustomHeadersOkHttpDataSourceFactory(reactNativeOkHttpClient, userAgent, requestHeaders)); + mDataSourceFactory = new DefaultDataSourceFactory(reactApplicationContext, transferListener, new CustomHeadersOkHttpDataSourceFactory(reactNativeOkHttpClient, userAgent, requestHeaders)); } @Override diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/universal/av/SharedCookiesDataSourceFactoryProvider.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/universal/av/SharedCookiesDataSourceFactoryProvider.java index 180b8747c3cd9..f2f7a987e70d9 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/universal/av/SharedCookiesDataSourceFactoryProvider.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/universal/av/SharedCookiesDataSourceFactoryProvider.java @@ -4,6 +4,7 @@ import com.facebook.react.bridge.ReactContext; import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.TransferListener; import java.util.Map; @@ -12,13 +13,13 @@ public class SharedCookiesDataSourceFactoryProvider extends expo.modules.av.player.datasource.SharedCookiesDataSourceFactoryProvider { @Override - public DataSource.Factory createFactory(Context context, ModuleRegistry moduleRegistry, String userAgent, Map requestHeaders) { + public DataSource.Factory createFactory(Context context, ModuleRegistry moduleRegistry, String userAgent, Map requestHeaders, TransferListener transferListener) { ReactContext reactContext = null; if (context instanceof ReactContext) { reactContext = (ReactContext) context; } else if (context instanceof ScopedContext) { reactContext = (ReactContext) ((ScopedContext) context).getContext(); } - return new SharedCookiesDataSourceFactory(reactContext, userAgent, requestHeaders); + return new SharedCookiesDataSourceFactory(reactContext, userAgent, requestHeaders, transferListener); } } diff --git a/packages/expo-av/CHANGELOG.md b/packages/expo-av/CHANGELOG.md index 6842921fe3ca1..376bc9eabd40a 100644 --- a/packages/expo-av/CHANGELOG.md +++ b/packages/expo-av/CHANGELOG.md @@ -11,3 +11,4 @@ - Fix unable to call presentFullScreenPlayer twice. ([#8343](https://github.com/expo/expo/pull/8343) by [@IjzerenHein](https://github.com/IjzerenHein)) - Fixed multiplied callbacks in `expo-av` after replaying ([#7193](https://github.com/expo/expo/pull/7193) by [@mczernek](https://github.com/mczernek)) - Fixed `Plaback.loadAsync()` return type. ([#7559](https://github.com/expo/expo/pull/7559) by [@awinograd](https://github.com/awinograd)) +- Fixed the adaptive streaming for exoplayer on android. ([#8380](https://github.com/expo/expo/pull/8363) by [@watchinharrison](https://github.com/watchinharrison)) diff --git a/packages/expo-av/android/src/main/java/expo/modules/av/player/MediaPlayerData.java b/packages/expo-av/android/src/main/java/expo/modules/av/player/MediaPlayerData.java index 5cac83e43186a..ddb403eafce22 100644 --- a/packages/expo-av/android/src/main/java/expo/modules/av/player/MediaPlayerData.java +++ b/packages/expo-av/android/src/main/java/expo/modules/av/player/MediaPlayerData.java @@ -1,6 +1,5 @@ package expo.modules.av.player; - import android.content.Context; import android.media.MediaPlayer; import android.media.PlaybackParams; diff --git a/packages/expo-av/android/src/main/java/expo/modules/av/player/SimpleExoPlayerData.java b/packages/expo-av/android/src/main/java/expo/modules/av/player/SimpleExoPlayerData.java index 1ca5a7579db1b..f5fabc66987a5 100644 --- a/packages/expo-av/android/src/main/java/expo/modules/av/player/SimpleExoPlayerData.java +++ b/packages/expo-av/android/src/main/java/expo/modules/av/player/SimpleExoPlayerData.java @@ -4,12 +4,16 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; + import androidx.annotation.Nullable; + import android.text.TextUtils; import android.util.Pair; import android.view.Surface; 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; @@ -43,10 +47,11 @@ import expo.modules.av.AVManagerInterface; import expo.modules.av.AudioFocusNotAcquiredException; +import expo.modules.av.player.datasource.CustomHeadersOkHttpDataSourceFactory; import expo.modules.av.player.datasource.DataSourceFactoryProvider; class SimpleExoPlayerData extends PlayerData - implements Player.EventListener, ExtractorMediaSource.EventListener, SimpleExoPlayer.VideoListener, AdaptiveMediaSourceEventListener { + implements Player.EventListener, ExtractorMediaSource.EventListener, SimpleExoPlayer.VideoListener, AdaptiveMediaSourceEventListener { private static final String IMPLEMENTATION_NAME = "SimpleExoPlayer"; @@ -83,16 +88,22 @@ public void load(final Bundle status, final LoadCompletionListener loadCompletio final Handler mainHandler = new Handler(); // Measures bandwidth during playback. Can be null if not required. final BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); - final TrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); + final TrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(); final TrackSelector trackSelector = new DefaultTrackSelector(trackSelectionFactory); // Create the player - mSimpleExoPlayer = ExoPlayerFactory.newSimpleInstance(mAVModule.getContext(), trackSelector); + mSimpleExoPlayer = ExoPlayerFactory.newSimpleInstance( + mAVModule.getContext(), + new DefaultRenderersFactory(mAVModule.getContext()), + trackSelector, + new DefaultLoadControl(), + null, + bandwidthMeter); mSimpleExoPlayer.addListener(this); mSimpleExoPlayer.addVideoListener(this); // Produces DataSource instances through which media data is loaded. - final DataSource.Factory dataSourceFactory = mAVModule.getModuleRegistry().getModule(DataSourceFactoryProvider.class).createFactory(mReactContext, mAVModule.getModuleRegistry(), Util.getUserAgent(mAVModule.getContext(), "yourApplicationName"), mRequestHeaders); + final DataSource.Factory dataSourceFactory = mAVModule.getModuleRegistry().getModule(DataSourceFactoryProvider.class).createFactory(mReactContext, mAVModule.getModuleRegistry(), Util.getUserAgent(mAVModule.getContext(), "yourApplicationName"), mRequestHeaders, bandwidthMeter.getTransferListener()); try { // This is the MediaSource representing the media to be played. final MediaSource source = buildMediaSource(mUri, mOverridingExtension, mainHandler, dataSourceFactory); @@ -141,7 +152,7 @@ void playPlayerWithRateAndMuteIfNecessary() throws AudioFocusNotAcquiredExceptio @Override void applyNewStatus(final Integer newPositionMillis, final Boolean newIsLooping) - throws AudioFocusNotAcquiredException, IllegalStateException { + throws AudioFocusNotAcquiredException, IllegalStateException { if (mSimpleExoPlayer == null) { throw new IllegalStateException("mSimpleExoPlayer is null!"); } @@ -188,14 +199,14 @@ void getExtraStatusFields(final Bundle map) { final int duration = (int) mSimpleExoPlayer.getDuration(); map.putInt(STATUS_DURATION_MILLIS_KEY_PATH, duration); map.putInt(STATUS_POSITION_MILLIS_KEY_PATH, - getClippedIntegerForValue((int) mSimpleExoPlayer.getCurrentPosition(), 0, duration)); + getClippedIntegerForValue((int) mSimpleExoPlayer.getCurrentPosition(), 0, duration)); map.putInt(STATUS_PLAYABLE_DURATION_MILLIS_KEY_PATH, - getClippedIntegerForValue((int) mSimpleExoPlayer.getBufferedPosition(), 0, duration)); + getClippedIntegerForValue((int) mSimpleExoPlayer.getBufferedPosition(), 0, duration)); map.putBoolean(STATUS_IS_PLAYING_KEY_PATH, - mSimpleExoPlayer.getPlayWhenReady() && mSimpleExoPlayer.getPlaybackState() == Player.STATE_READY); + mSimpleExoPlayer.getPlayWhenReady() && mSimpleExoPlayer.getPlaybackState() == Player.STATE_READY); map.putBoolean(STATUS_IS_BUFFERING_KEY_PATH, - mIsLoading || mSimpleExoPlayer.getPlaybackState() == Player.STATE_BUFFERING); + mIsLoading || mSimpleExoPlayer.getPlaybackState() == Player.STATE_BUFFERING); map.putBoolean(STATUS_IS_LOOPING_KEY_PATH, mIsLooping); } @@ -284,8 +295,8 @@ public void onPlayerStateChanged(final boolean playWhenReady, final int playback } if (mLastPlaybackState != null - && playbackState != mLastPlaybackState - && playbackState == Player.STATE_ENDED) { + && playbackState != mLastPlaybackState + && playbackState == Player.STATE_ENDED) { callStatusUpdateListenerWithDidJustFinish(); } else { callStatusUpdateListener(); @@ -360,10 +371,10 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension, Handler switch (type) { case C.TYPE_SS: return new SsMediaSource(uri, factory, - new DefaultSsChunkSource.Factory(factory), mainHandler, this); + new DefaultSsChunkSource.Factory(factory), mainHandler, this); case C.TYPE_DASH: return new DashMediaSource(uri, factory, - new DefaultDashChunkSource.Factory(factory), mainHandler, this); + new DefaultDashChunkSource.Factory(factory), mainHandler, this); case C.TYPE_HLS: return new HlsMediaSource(uri, factory, mainHandler, this); case C.TYPE_OTHER: diff --git a/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/CustomHeadersOkHttpDataSourceFactory.java b/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/CustomHeadersOkHttpDataSourceFactory.java index 5d634b1677293..87d5ef5f9265c 100644 --- a/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/CustomHeadersOkHttpDataSourceFactory.java +++ b/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/CustomHeadersOkHttpDataSourceFactory.java @@ -21,15 +21,12 @@ public class CustomHeadersOkHttpDataSourceFactory extends HttpDataSource.BaseFac @Nullable private final String mUserAgent; @Nullable - private final TransferListener mListener; - @Nullable private final CacheControl mCacheControl; public CustomHeadersOkHttpDataSourceFactory(@NonNull Call.Factory callFactory, @Nullable String userAgent, @Nullable Map requestHeaders) { super(); mCallFactory = callFactory; mUserAgent = userAgent; - mListener = null; mCacheControl = null; updateRequestProperties(getDefaultRequestProperties(), requestHeaders); } diff --git a/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/DataSourceFactoryProvider.java b/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/DataSourceFactoryProvider.java index 7c27a655d0603..3e89a89bde0b4 100644 --- a/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/DataSourceFactoryProvider.java +++ b/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/DataSourceFactoryProvider.java @@ -3,11 +3,12 @@ import android.content.Context; import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.TransferListener; import java.util.Map; import org.unimodules.core.ModuleRegistry; public interface DataSourceFactoryProvider { - DataSource.Factory createFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map requestHeaders); + DataSource.Factory createFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map requestHeaders, TransferListener transferListener); } diff --git a/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/SharedCookiesDataSourceFactory.java b/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/SharedCookiesDataSourceFactory.java index ec2e9aaba0485..9d26d807a22db 100644 --- a/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/SharedCookiesDataSourceFactory.java +++ b/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/SharedCookiesDataSourceFactory.java @@ -4,6 +4,7 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.upstream.TransferListener; import java.net.CookieHandler; import java.util.Map; @@ -15,14 +16,14 @@ public class SharedCookiesDataSourceFactory implements DataSource.Factory { private final DataSource.Factory mDataSourceFactory; - public SharedCookiesDataSourceFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map requestHeaders) { + public SharedCookiesDataSourceFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map requestHeaders, TransferListener transferListener) { CookieHandler cookieHandler = moduleRegistry.getModule(CookieHandler.class); OkHttpClient.Builder builder = new OkHttpClient.Builder(); if (cookieHandler != null) { builder.cookieJar(new JavaNetCookieJar(cookieHandler)); } OkHttpClient client = builder.build(); - mDataSourceFactory = new DefaultDataSourceFactory(reactApplicationContext, null, new CustomHeadersOkHttpDataSourceFactory(client, userAgent, requestHeaders)); + mDataSourceFactory = new DefaultDataSourceFactory(reactApplicationContext, transferListener, new CustomHeadersOkHttpDataSourceFactory(client, userAgent, requestHeaders)); } @Override diff --git a/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/SharedCookiesDataSourceFactoryProvider.java b/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/SharedCookiesDataSourceFactoryProvider.java index 8824197e71701..5835755fb4258 100644 --- a/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/SharedCookiesDataSourceFactoryProvider.java +++ b/packages/expo-av/android/src/main/java/expo/modules/av/player/datasource/SharedCookiesDataSourceFactoryProvider.java @@ -3,6 +3,7 @@ import android.content.Context; import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.TransferListener; import java.util.Collections; import java.util.List; @@ -18,7 +19,7 @@ public List getExportedInterfaces() { } @Override - public DataSource.Factory createFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map requestHeaders) { - return new SharedCookiesDataSourceFactory(reactApplicationContext, moduleRegistry, userAgent, requestHeaders); + public DataSource.Factory createFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map requestHeaders, TransferListener transferListener) { + return new SharedCookiesDataSourceFactory(reactApplicationContext, moduleRegistry, userAgent, requestHeaders, transferListener); } }