Implement ExoPlayerPool for better reuse and performance.

This commit is contained in:
Alex Hart
2021-09-24 13:10:48 -03:00
committed by GitHub
parent a5c51ff801
commit 5c1b57e4ba
12 changed files with 478 additions and 154 deletions

View File

@@ -1,46 +0,0 @@
package org.thoughtcrime.securesms.giph.mp4;
import android.content.Context;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DataSource;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.net.ContentProxySelector;
import org.thoughtcrime.securesms.video.exo.SignalDataSource;
import okhttp3.OkHttpClient;
/**
* Provider which creates ExoPlayer instances for displaying Giphy content.
*/
final class GiphyMp4ExoPlayerProvider implements DefaultLifecycleObserver {
private final Context context;
private final OkHttpClient okHttpClient = ApplicationDependencies.getOkHttpClient().newBuilder().proxySelector(new ContentProxySelector()).build();
private final DataSource.Factory dataSourceFactory = new SignalDataSource.Factory(ApplicationDependencies.getApplication(), okHttpClient, null);
private final MediaSourceFactory mediaSourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);
GiphyMp4ExoPlayerProvider(@NonNull Context context) {
this.context = context;
}
@MainThread final @NonNull ExoPlayer create() {
SimpleExoPlayer exoPlayer = new SimpleExoPlayer.Builder(context)
.setMediaSourceFactory(mediaSourceFactory)
.build();
exoPlayer.setRepeatMode(Player.REPEAT_MODE_ALL);
exoPlayer.setVolume(0f);
return exoPlayer;
}
}

View File

@@ -1,11 +1,5 @@
package org.thoughtcrime.securesms.giph.mp4;
import android.os.Build;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
import com.google.android.exoplayer2.util.MimeTypes;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.DeviceProperties;
import org.thoughtcrime.securesms.util.FeatureFlags;
@@ -17,10 +11,6 @@ import java.util.concurrent.TimeUnit;
*/
public final class GiphyMp4PlaybackPolicy {
private static final int MAXIMUM_SUPPORTED_PLAYBACK_PRE_23 = 6;
private static final int MAXIMUM_SUPPORTED_PLAYBACK_PRE_23_LOW_MEM = 3;
private static final float SEARCH_RESULT_RATIO = 0.75f;
private GiphyMp4PlaybackPolicy() { }
public static boolean sendAsMp4() {
@@ -39,35 +29,11 @@ public final class GiphyMp4PlaybackPolicy {
return TimeUnit.SECONDS.toMillis(8);
}
public static int maxSimultaneousPlaybackInConversation() {
return Build.VERSION.SDK_INT >= 23 ? maxSimultaneousPlaybackWithRatio(1f - SEARCH_RESULT_RATIO) : 0;
}
public static int maxSimultaneousPlaybackInSearchResults() {
return maxSimultaneousPlaybackWithRatio(SEARCH_RESULT_RATIO);
return 12;
}
private static int maxSimultaneousPlaybackWithRatio(float ratio) {
int maxInstances = 0;
try {
MediaCodecInfo info = MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H264, false, false);
if (info != null && info.getMaxSupportedInstances() > 0) {
maxInstances = (int) (info.getMaxSupportedInstances() * ratio);
}
} catch (MediaCodecUtil.DecoderQueryException ignored) {
}
if (maxInstances > 0) {
return maxInstances;
}
if (DeviceProperties.isLowMemoryDevice(ApplicationDependencies.getApplication())) {
return (int) (MAXIMUM_SUPPORTED_PLAYBACK_PRE_23_LOW_MEM * ratio);
} else {
return (int) (MAXIMUM_SUPPORTED_PLAYBACK_PRE_23 * ratio);
}
public static int maxSimultaneousPlaybackInConversation() {
return 4;
}
}

View File

@@ -8,15 +8,20 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.video.exo.ExoPlayerKt;
import java.util.ArrayList;
import java.util.List;
@@ -24,7 +29,9 @@ import java.util.List;
/**
* Object which holds on to an injected video player.
*/
public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener {
public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener, DefaultLifecycleObserver {
private static final String TAG = Log.tag(GiphyMp4ProjectionPlayerHolder.class);
private final FrameLayout container;
private final GiphyMp4VideoPlayer player;
@@ -45,6 +52,20 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener {
this.mediaItem = mediaItem;
this.policyEnforcer = policyEnforcer;
if (player.getExoPlayer() == null) {
SimpleExoPlayer fromPool = ApplicationDependencies.getExoPlayerPool().get();
if (fromPool == null) {
Log.i(TAG, "Could not get exoplayer from pool.");
return;
} else {
ExoPlayerKt.configureForGifPlayback(fromPool);
fromPool.addListener(this);
}
player.setExoPlayer(fromPool);
}
player.setVideoItem(mediaItem);
player.play();
}
@@ -52,7 +73,14 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener {
public void clearMedia() {
this.mediaItem = null;
this.policyEnforcer = null;
player.stop();
SimpleExoPlayer exoPlayer = player.getExoPlayer();
if (exoPlayer != null) {
player.stop();
player.setExoPlayer(null);
exoPlayer.removeListener(this);
ApplicationDependencies.getExoPlayerPool().pool(exoPlayer);
}
}
public @Nullable MediaItem getMediaItem() {
@@ -98,25 +126,46 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener {
}
}
@Override
public void onResume(@NonNull LifecycleOwner owner) {
if (mediaItem != null) {
SimpleExoPlayer fromPool = ApplicationDependencies.getExoPlayerPool().get();
if (fromPool != null) {
ExoPlayerKt.configureForGifPlayback(fromPool);
fromPool.addListener(this);
player.setExoPlayer(fromPool);
player.setVideoItem(mediaItem);
player.play();
}
}
}
@Override
public void onPause(@NonNull LifecycleOwner owner) {
if (player.getExoPlayer() != null) {
player.getExoPlayer().stop();
player.getExoPlayer().clearMediaItems();
player.getExoPlayer().removeListener(this);
ApplicationDependencies.getExoPlayerPool().pool(player.getExoPlayer());
player.setExoPlayer(null);
}
}
public static @NonNull List<GiphyMp4ProjectionPlayerHolder> injectVideoViews(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull ViewGroup viewGroup,
int nPlayers)
{
List<GiphyMp4ProjectionPlayerHolder> holders = new ArrayList<>(nPlayers);
GiphyMp4ExoPlayerProvider playerProvider = new GiphyMp4ExoPlayerProvider(context);
List<GiphyMp4ProjectionPlayerHolder> holders = new ArrayList<>(nPlayers);
for (int i = 0; i < nPlayers; i++) {
FrameLayout container = (FrameLayout) LayoutInflater.from(context)
.inflate(R.layout.giphy_mp4_player, viewGroup, false);
GiphyMp4VideoPlayer player = container.findViewById(R.id.video_player);
ExoPlayer exoPlayer = playerProvider.create();
GiphyMp4ProjectionPlayerHolder holder = new GiphyMp4ProjectionPlayerHolder(container, player);
GiphyMp4VideoPlayer player = container.findViewById(R.id.video_player);
GiphyMp4ProjectionPlayerHolder holder = new GiphyMp4ProjectionPlayerHolder(container, player);
lifecycle.addObserver(player);
player.setExoPlayer(exoPlayer);
lifecycle.addObserver(holder);
player.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL);
exoPlayer.addListener(holder);
holders.add(holder);
viewGroup.addView(container);

View File

@@ -13,13 +13,16 @@ import androidx.lifecycle.LifecycleOwner;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerView;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.CornerMask;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.video.exo.ExoPlayerKt;
/**
* Video Player class specifically created for the GiphyMp4Fragment.
@@ -29,9 +32,10 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
@SuppressWarnings("unused")
private static final String TAG = Log.tag(GiphyMp4VideoPlayer.class);
private final PlayerView exoView;
private ExoPlayer exoPlayer;
private CornerMask cornerMask;
private final PlayerView exoView;
private SimpleExoPlayer exoPlayer;
private CornerMask cornerMask;
private MediaItem mediaItem;
public GiphyMp4VideoPlayer(Context context) {
this(context, null);
@@ -64,16 +68,25 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
}
}
void setExoPlayer(@NonNull ExoPlayer exoPlayer) {
@Nullable SimpleExoPlayer getExoPlayer() {
return exoPlayer;
}
void setExoPlayer(@Nullable SimpleExoPlayer exoPlayer) {
exoView.setPlayer(exoPlayer);
this.exoPlayer = exoPlayer;
}
int getPlaybackState() {
return exoPlayer.getPlaybackState();
if (exoPlayer != null) {
return exoPlayer.getPlaybackState();
} else {
return -1;
}
}
void setVideoItem(@NonNull MediaItem mediaItem) {
this.mediaItem = mediaItem;
exoPlayer.setMediaItem(mediaItem);
exoPlayer.prepare();
}
@@ -98,6 +111,7 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
if (exoPlayer != null) {
exoPlayer.stop();
exoPlayer.clearMediaItems();
mediaItem = null;
}
}
@@ -112,11 +126,4 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
void setResizeMode(@AspectRatioFrameLayout.ResizeMode int resizeMode) {
exoView.setResizeMode(resizeMode);
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
if (exoPlayer != null) {
exoPlayer.release();
}
}
}