Skip to content

Commit

Permalink
Allow to specify player looper at the time of ExoPlayer creation.
Browse files Browse the repository at this point in the history
Currently, the looper of the thread the player is created on is used (or the
main looper if this thread doesn't have a looper). To allow more control over
the threading, this change lets users specificy the looper which must be used
to call player methods and which is used for event callbacks.

Issue:#4278

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=201331564
  • Loading branch information
tonihei authored and ojw28 committed Jun 20, 2018
1 parent 01ce549 commit 4f2b596
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 80 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
([#4385](https://github.com/google/ExoPlayer/issues/4385)).
* Expose all internal ID3 data stored in MP4 udta boxes, and switch from using
CommentFrame to InternalFrame for frames with gapless metadata in MP4.
* Allow setting the `Looper`, which is used to access the player, in
`ExoPlayerFactory` ([#4278](https://github.com/google/ExoPlayer/issues/4278)).

### 2.8.2 ###

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.support.annotation.NonNull;
Expand All @@ -39,6 +38,7 @@
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.ErrorMessageProvider;
import com.google.android.exoplayer2.util.RepeatModeUtil;
import com.google.android.exoplayer2.util.Util;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -323,7 +323,6 @@ public interface CustomActionProvider {
public final MediaSessionCompat mediaSession;

private final MediaControllerCompat mediaController;
private final Handler handler;
private final boolean doMaintainMetadata;
private final ExoPlayerEventListener exoPlayerEventListener;
private final MediaSessionCallback mediaSessionCallback;
Expand All @@ -341,10 +340,9 @@ public interface CustomActionProvider {
private RatingCallback ratingCallback;

/**
* Creates an instance. Must be called on the same thread that is used to construct the player
* instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}.
* <p>
* Equivalent to {@code MediaSessionConnector(mediaSession, new DefaultPlaybackController())}.
* Creates an instance.
*
* <p>Equivalent to {@code MediaSessionConnector(mediaSession, new DefaultPlaybackController())}.
*
* @param mediaSession The {@link MediaSessionCompat} to connect to.
*/
Expand All @@ -353,8 +351,7 @@ public MediaSessionConnector(MediaSessionCompat mediaSession) {
}

/**
* Creates an instance. Must be called on the same thread that is used to construct the player
* instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}.
* Creates an instance.
*
* <p>Equivalent to {@code MediaSessionConnector(mediaSession, playbackController, true, null)}.
*
Expand All @@ -367,8 +364,7 @@ public MediaSessionConnector(
}

/**
* Creates an instance. Must be called on the same thread that is used to construct the player
* instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}.
* Creates an instance.
*
* @param mediaSession The {@link MediaSessionCompat} to connect to.
* @param playbackController A {@link PlaybackController} for handling playback actions, or {@code
Expand All @@ -388,8 +384,6 @@ public MediaSessionConnector(
this.playbackController = playbackController != null ? playbackController
: new DefaultPlaybackController();
this.metadataExtrasPrefix = metadataExtrasPrefix != null ? metadataExtrasPrefix : "";
this.handler = new Handler(Looper.myLooper() != null ? Looper.myLooper()
: Looper.getMainLooper());
this.doMaintainMetadata = doMaintainMetadata;
mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS);
mediaController = mediaSession.getController();
Expand All @@ -401,7 +395,8 @@ public MediaSessionConnector(
}

/**
* Sets the player to be connected to the media session.
* Sets the player to be connected to the media session. Must be called on the same thread that is
* used to access the player.
*
* <p>The order in which any {@link CustomActionProvider}s are passed determines the order of the
* actions published with the playback state of the session.
Expand All @@ -428,6 +423,7 @@ public void setPlayer(
this.customActionProviders = (player != null && customActionProviders != null)
? customActionProviders : new CustomActionProvider[0];
if (player != null) {
Handler handler = new Handler(Util.getLooper());
mediaSession.setCallback(mediaSessionCallback, handler);
player.addListener(exoPlayerEventListener);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,13 @@
* model">
*
* <ul>
* <li>ExoPlayer instances must be accessed from a single application thread. This must be the
* thread the player is created on if that thread has a {@link Looper}, or the application's
* main thread otherwise.
* <li>Registered listeners are called on the thread the player is created on if that thread has a
* {@link Looper}, or the application's main thread otherwise. Note that this means registered
* listeners are called on the same thread which must be used to access the player.
* <li>ExoPlayer instances must be accessed from the thread associated with {@link
* #getApplicationLooper()}. This Looper can be specified when creating the player, or this is
* the Looper of the thread the player is created on, or the Looper of the application's main
* thread if the player is created on a thread without Looper.
* <li>Registered listeners are called on the thread thread associated with {@link
* #getApplicationLooper()}. Note that this means registered listeners are called on the same
* thread which must be used to access the player.
* <li>An internal playback thread is responsible for playback. Injected player components such as
* Renderers, MediaSources, TrackSelectors and LoadControls are called by the player on this
* thread.
Expand Down Expand Up @@ -178,12 +179,14 @@ public ExoPlayerMessage(PlayerMessage.Target target, int messageType, Object mes
@Deprecated
@RepeatMode int REPEAT_MODE_ALL = Player.REPEAT_MODE_ALL;

/** Returns the {@link Looper} associated with the playback thread. */
Looper getPlaybackLooper();

/**
* Gets the {@link Looper} associated with the playback thread.
*
* @return The {@link Looper} associated with the playback thread.
* Returns the {@link Looper} associated with the application thread that's used to access the
* player and on which player events are received.
*/
Looper getPlaybackLooper();
Looper getApplicationLooper();

/**
* Prepares the player to play the provided {@link MediaSource}. Equivalent to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
package com.google.android.exoplayer2;

import android.content.Context;
import android.os.Looper;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util;

/**
* A factory for {@link ExoPlayer} instances.
Expand Down Expand Up @@ -156,7 +158,11 @@ public static SimpleExoPlayer newSimpleInstance(
public static SimpleExoPlayer newSimpleInstance(RenderersFactory renderersFactory,
TrackSelector trackSelector, LoadControl loadControl) {
return new SimpleExoPlayer(
renderersFactory, trackSelector, loadControl, /* drmSessionManager= */ null);
renderersFactory,
trackSelector,
loadControl,
/* drmSessionManager= */ null,
Util.getLooper());
}

/**
Expand All @@ -173,7 +179,8 @@ public static SimpleExoPlayer newSimpleInstance(
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
return new SimpleExoPlayer(renderersFactory, trackSelector, loadControl, drmSessionManager);
return new SimpleExoPlayer(
renderersFactory, trackSelector, loadControl, drmSessionManager, Util.getLooper());
}

/**
Expand All @@ -194,7 +201,62 @@ public static SimpleExoPlayer newSimpleInstance(
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
AnalyticsCollector.Factory analyticsCollectorFactory) {
return new SimpleExoPlayer(
renderersFactory, trackSelector, loadControl, drmSessionManager, analyticsCollectorFactory);
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
analyticsCollectorFactory,
Util.getLooper());
}

/**
* Creates a {@link SimpleExoPlayer} instance.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
public static SimpleExoPlayer newSimpleInstance(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
Looper looper) {
return new SimpleExoPlayer(
renderersFactory, trackSelector, loadControl, drmSessionManager, looper);
}

/**
* Creates a {@link SimpleExoPlayer} instance.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @param analyticsCollectorFactory A factory for creating the {@link AnalyticsCollector} that
* will collect and forward all player events.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
public static SimpleExoPlayer newSimpleInstance(
RenderersFactory renderersFactory,
TrackSelector trackSelector,
LoadControl loadControl,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
AnalyticsCollector.Factory analyticsCollectorFactory,
Looper looper) {
return new SimpleExoPlayer(
renderersFactory,
trackSelector,
loadControl,
drmSessionManager,
analyticsCollectorFactory,
looper);
}

/**
Expand All @@ -216,7 +278,20 @@ public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSel
*/
public static ExoPlayer newInstance(Renderer[] renderers, TrackSelector trackSelector,
LoadControl loadControl) {
return new ExoPlayerImpl(renderers, trackSelector, loadControl, Clock.DEFAULT);
return newInstance(renderers, trackSelector, loadControl, Util.getLooper());
}

/**
* Creates an {@link ExoPlayer} instance.
*
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
public static ExoPlayer newInstance(
Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, Looper looper) {
return new ExoPlayerImpl(renderers, trackSelector, loadControl, Clock.DEFAULT, looper);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,16 @@
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param clock The {@link Clock} that will be used by the instance.
* @param looper The {@link Looper} which must be used for all calls to the player and which is
* used to call listeners on.
*/
@SuppressLint("HandlerLeak")
public ExoPlayerImpl(
Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, Clock clock) {
Renderer[] renderers,
TrackSelector trackSelector,
LoadControl loadControl,
Clock clock,
Looper looper) {
Log.i(TAG, "Init " + Integer.toHexString(System.identityHashCode(this)) + " ["
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "]");
Assertions.checkState(renderers.length > 0);
Expand All @@ -102,13 +108,13 @@ public ExoPlayerImpl(
window = new Timeline.Window();
period = new Timeline.Period();
playbackParameters = PlaybackParameters.DEFAULT;
Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper();
eventHandler = new Handler(eventLooper) {
@Override
public void handleMessage(Message msg) {
ExoPlayerImpl.this.handleEvent(msg);
}
};
eventHandler =
new Handler(looper) {
@Override
public void handleMessage(Message msg) {
ExoPlayerImpl.this.handleEvent(msg);
}
};
playbackInfo =
new PlaybackInfo(
Timeline.EMPTY,
Expand Down Expand Up @@ -146,6 +152,11 @@ public Looper getPlaybackLooper() {
return internalPlayer.getPlaybackLooper();
}

@Override
public Looper getApplicationLooper() {
return eventHandler.getLooper();
}

@Override
public void addListener(Player.EventListener listener) {
listeners.add(listener);
Expand Down
Loading

0 comments on commit 4f2b596

Please sign in to comment.