Skip to content

Commit

Permalink
[expo-av][Android] Fix adaptive streaming for exoplayer in expo-av. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mczernek committed May 22, 2020
1 parent 34808ad commit 59a5775
Show file tree
Hide file tree
Showing 9 changed files with 39 additions and 26 deletions.
Expand Up @@ -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;

Expand All @@ -12,9 +13,9 @@
public class SharedCookiesDataSourceFactory implements DataSource.Factory {
private final DataSource.Factory mDataSourceFactory;

public SharedCookiesDataSourceFactory(ReactContext reactApplicationContext, String userAgent, Map<String, Object> requestHeaders) {
public SharedCookiesDataSourceFactory(ReactContext reactApplicationContext, String userAgent, Map<String, Object> 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
Expand Down
Expand Up @@ -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;

Expand All @@ -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<String, Object> requestHeaders) {
public DataSource.Factory createFactory(Context context, ModuleRegistry moduleRegistry, String userAgent, Map<String, Object> 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);
}
}
1 change: 1 addition & 0 deletions packages/expo-av/CHANGELOG.md
Expand Up @@ -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))
@@ -1,6 +1,5 @@
package expo.modules.av.player;


import android.content.Context;
import android.media.MediaPlayer;
import android.media.PlaybackParams;
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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";

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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!");
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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:
Expand Down
Expand Up @@ -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<String, Object> requestHeaders) {
super();
mCallFactory = callFactory;
mUserAgent = userAgent;
mListener = null;
mCacheControl = null;
updateRequestProperties(getDefaultRequestProperties(), requestHeaders);
}
Expand Down
Expand Up @@ -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<String, Object> requestHeaders);
DataSource.Factory createFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map<String, Object> requestHeaders, TransferListener transferListener);
}
Expand Up @@ -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;
Expand All @@ -15,14 +16,14 @@
public class SharedCookiesDataSourceFactory implements DataSource.Factory {
private final DataSource.Factory mDataSourceFactory;

public SharedCookiesDataSourceFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map<String, Object> requestHeaders) {
public SharedCookiesDataSourceFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map<String, Object> 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
Expand Down
Expand Up @@ -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;
Expand All @@ -18,7 +19,7 @@ public List<Class> getExportedInterfaces() {
}

@Override
public DataSource.Factory createFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map<String, Object> requestHeaders) {
return new SharedCookiesDataSourceFactory(reactApplicationContext, moduleRegistry, userAgent, requestHeaders);
public DataSource.Factory createFactory(Context reactApplicationContext, ModuleRegistry moduleRegistry, String userAgent, Map<String, Object> requestHeaders, TransferListener transferListener) {
return new SharedCookiesDataSourceFactory(reactApplicationContext, moduleRegistry, userAgent, requestHeaders, transferListener);
}
}

0 comments on commit 59a5775

Please sign in to comment.