mirror of
https://github.com/samsonjs/media.git
synced 2026-04-14 12:45:47 +00:00
Allow to specify player looper at the time of ExoPlayer creation.
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
This commit is contained in:
parent
01ce549e8f
commit
4f2b596062
11 changed files with 215 additions and 85 deletions
|
|
@ -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 ###
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import android.graphics.Bitmap;
|
|||
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;
|
||||
|
|
@ -39,6 +38,7 @@ import com.google.android.exoplayer2.Player;
|
|||
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;
|
||||
|
|
@ -323,7 +323,6 @@ public final class MediaSessionConnector {
|
|||
public final MediaSessionCompat mediaSession;
|
||||
|
||||
private final MediaControllerCompat mediaController;
|
||||
private final Handler handler;
|
||||
private final boolean doMaintainMetadata;
|
||||
private final ExoPlayerEventListener exoPlayerEventListener;
|
||||
private final MediaSessionCallback mediaSessionCallback;
|
||||
|
|
@ -341,10 +340,9 @@ public final class MediaSessionConnector {
|
|||
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.
|
||||
*/
|
||||
|
|
@ -353,8 +351,7 @@ public final class 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.
|
||||
*
|
||||
* <p>Equivalent to {@code MediaSessionConnector(mediaSession, playbackController, true, null)}.
|
||||
*
|
||||
|
|
@ -367,8 +364,7 @@ public final class 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
|
||||
|
|
@ -388,8 +384,6 @@ public final class 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();
|
||||
|
|
@ -401,7 +395,8 @@ public final class 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.
|
||||
|
|
@ -428,6 +423,7 @@ public final class MediaSessionConnector {
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,12 +89,13 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
|
|||
* 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.
|
||||
|
|
@ -178,13 +179,15 @@ public interface ExoPlayer extends Player {
|
|||
@Deprecated
|
||||
@RepeatMode int REPEAT_MODE_ALL = Player.REPEAT_MODE_ALL;
|
||||
|
||||
/**
|
||||
* 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 playback thread. */
|
||||
Looper getPlaybackLooper();
|
||||
|
||||
/**
|
||||
* Returns the {@link Looper} associated with the application thread that's used to access the
|
||||
* player and on which player events are received.
|
||||
*/
|
||||
Looper getApplicationLooper();
|
||||
|
||||
/**
|
||||
* Prepares the player to play the provided {@link MediaSource}. Equivalent to
|
||||
* {@code prepare(mediaSource, true, true)}.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -156,7 +158,11 @@ public final class ExoPlayerFactory {
|
|||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -173,7 +179,8 @@ public final class ExoPlayerFactory {
|
|||
TrackSelector trackSelector,
|
||||
LoadControl loadControl,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
|
||||
return new SimpleExoPlayer(renderersFactory, trackSelector, loadControl, drmSessionManager);
|
||||
return new SimpleExoPlayer(
|
||||
renderersFactory, trackSelector, loadControl, drmSessionManager, Util.getLooper());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -194,7 +201,62 @@ public final class ExoPlayerFactory {
|
|||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -216,7 +278,20 @@ public final class ExoPlayerFactory {
|
|||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,10 +81,16 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
* @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);
|
||||
|
|
@ -102,13 +108,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
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,
|
||||
|
|
@ -146,6 +152,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
return internalPlayer.getPlaybackLooper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Looper getApplicationLooper() {
|
||||
return eventHandler.getLooper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Player.EventListener listener) {
|
||||
listeners.add(listener);
|
||||
|
|
|
|||
|
|
@ -98,23 +98,40 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
|
|||
private List<Cue> currentCues;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @deprecated Use {@link #SimpleExoPlayer(RenderersFactory, TrackSelector, LoadControl,
|
||||
* DrmSessionManager, Looper)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected SimpleExoPlayer(
|
||||
RenderersFactory renderersFactory,
|
||||
TrackSelector trackSelector,
|
||||
LoadControl loadControl,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
|
||||
this(renderersFactory, trackSelector, loadControl, drmSessionManager, Util.getLooper());
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
protected SimpleExoPlayer(
|
||||
RenderersFactory renderersFactory,
|
||||
TrackSelector trackSelector,
|
||||
LoadControl loadControl,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
||||
Looper looper) {
|
||||
this(
|
||||
renderersFactory,
|
||||
trackSelector,
|
||||
loadControl,
|
||||
drmSessionManager,
|
||||
new AnalyticsCollector.Factory());
|
||||
new AnalyticsCollector.Factory(),
|
||||
looper);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -125,20 +142,24 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
|
|||
* 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.
|
||||
*/
|
||||
protected SimpleExoPlayer(
|
||||
RenderersFactory renderersFactory,
|
||||
TrackSelector trackSelector,
|
||||
LoadControl loadControl,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
||||
AnalyticsCollector.Factory analyticsCollectorFactory) {
|
||||
AnalyticsCollector.Factory analyticsCollectorFactory,
|
||||
Looper looper) {
|
||||
this(
|
||||
renderersFactory,
|
||||
trackSelector,
|
||||
loadControl,
|
||||
drmSessionManager,
|
||||
analyticsCollectorFactory,
|
||||
Clock.DEFAULT);
|
||||
Clock.DEFAULT,
|
||||
looper);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -151,6 +172,8 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
|
|||
* will collect and forward all player events.
|
||||
* @param clock The {@link Clock} that will be used by the instance. Should always be {@link
|
||||
* Clock#DEFAULT}, unless the player is being used from a test.
|
||||
* @param looper The {@link Looper} which must be used for all calls to the player and which is
|
||||
* used to call listeners on.
|
||||
*/
|
||||
protected SimpleExoPlayer(
|
||||
RenderersFactory renderersFactory,
|
||||
|
|
@ -158,15 +181,15 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
|
|||
LoadControl loadControl,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
||||
AnalyticsCollector.Factory analyticsCollectorFactory,
|
||||
Clock clock) {
|
||||
Clock clock,
|
||||
Looper looper) {
|
||||
componentListener = new ComponentListener();
|
||||
videoListeners = new CopyOnWriteArraySet<>();
|
||||
textOutputs = new CopyOnWriteArraySet<>();
|
||||
metadataOutputs = new CopyOnWriteArraySet<>();
|
||||
videoDebugListeners = new CopyOnWriteArraySet<>();
|
||||
audioDebugListeners = new CopyOnWriteArraySet<>();
|
||||
Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper();
|
||||
eventHandler = new Handler(eventLooper);
|
||||
eventHandler = new Handler(looper);
|
||||
renderers =
|
||||
renderersFactory.createRenderers(
|
||||
eventHandler,
|
||||
|
|
@ -184,7 +207,7 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
|
|||
currentCues = Collections.emptyList();
|
||||
|
||||
// Build the player and associated objects.
|
||||
player = createExoPlayerImpl(renderers, trackSelector, loadControl, clock);
|
||||
player = createExoPlayerImpl(renderers, trackSelector, loadControl, clock, looper);
|
||||
analyticsCollector = analyticsCollectorFactory.createAnalyticsCollector(player, clock);
|
||||
addListener(analyticsCollector);
|
||||
videoDebugListeners.add(analyticsCollector);
|
||||
|
|
@ -671,6 +694,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
|
|||
return player.getPlaybackLooper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Looper getApplicationLooper() {
|
||||
return player.getApplicationLooper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Player.EventListener listener) {
|
||||
player.addListener(listener);
|
||||
|
|
@ -954,11 +982,17 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player
|
|||
* @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 this instance.
|
||||
* @param looper The {@link Looper} which must be used for all calls to the player and which is
|
||||
* used to call listeners on.
|
||||
* @return A new {@link ExoPlayer} instance.
|
||||
*/
|
||||
protected ExoPlayer createExoPlayerImpl(
|
||||
Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, Clock clock) {
|
||||
return new ExoPlayerImpl(renderers, trackSelector, loadControl, clock);
|
||||
Renderer[] renderers,
|
||||
TrackSelector trackSelector,
|
||||
LoadControl loadControl,
|
||||
Clock clock,
|
||||
Looper looper) {
|
||||
return new ExoPlayerImpl(renderers, trackSelector, loadControl, clock, looper);
|
||||
}
|
||||
|
||||
private void removeSurfaceCallbacks() {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
|
|
@ -61,13 +60,14 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
private final List<MediaSourceHolder> mediaSourceHolders;
|
||||
private final MediaSourceHolder query;
|
||||
private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
|
||||
private final List<EventDispatcher> pendingOnCompletionActions;
|
||||
private final List<Runnable> pendingOnCompletionActions;
|
||||
private final boolean isAtomic;
|
||||
private final boolean useLazyPreparation;
|
||||
private final Timeline.Window window;
|
||||
private final Timeline.Period period;
|
||||
|
||||
private @Nullable ExoPlayer player;
|
||||
private @Nullable Handler playerApplicationHandler;
|
||||
private boolean listenerNotificationScheduled;
|
||||
private ShuffleOrder shuffleOrder;
|
||||
private int windowCount;
|
||||
|
|
@ -351,11 +351,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
public final synchronized void clear(@Nullable Runnable actionOnCompletion) {
|
||||
mediaSourcesPublic.clear();
|
||||
if (player != null) {
|
||||
player
|
||||
.createMessage(this)
|
||||
.setType(MSG_CLEAR)
|
||||
.setPayload(actionOnCompletion != null ? new EventDispatcher(actionOnCompletion) : null)
|
||||
.send();
|
||||
player.createMessage(this).setType(MSG_CLEAR).setPayload(actionOnCompletion).send();
|
||||
} else if (actionOnCompletion != null) {
|
||||
actionOnCompletion.run();
|
||||
}
|
||||
|
|
@ -380,6 +376,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
public final synchronized void prepareSourceInternal(ExoPlayer player, boolean isTopLevelSource) {
|
||||
super.prepareSourceInternal(player, isTopLevelSource);
|
||||
this.player = player;
|
||||
playerApplicationHandler = new Handler(player.getApplicationLooper());
|
||||
if (mediaSourcesPublic.isEmpty()) {
|
||||
notifyListener();
|
||||
} else {
|
||||
|
|
@ -424,6 +421,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
super.releaseSourceInternal();
|
||||
mediaSourceHolders.clear();
|
||||
player = null;
|
||||
playerApplicationHandler = null;
|
||||
shuffleOrder = shuffleOrder.cloneAndClear();
|
||||
windowCount = 0;
|
||||
periodCount = 0;
|
||||
|
|
@ -462,6 +460,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void handleMessage(int messageType, Object message) throws ExoPlaybackException {
|
||||
if (player == null) {
|
||||
// Stale event.
|
||||
return;
|
||||
}
|
||||
switch (messageType) {
|
||||
case MSG_ADD:
|
||||
MessageData<MediaSourceHolder> addMessage = (MessageData<MediaSourceHolder>) message;
|
||||
|
|
@ -493,15 +495,16 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
break;
|
||||
case MSG_CLEAR:
|
||||
clearInternal();
|
||||
scheduleListenerNotification((EventDispatcher) message);
|
||||
scheduleListenerNotification((Runnable) message);
|
||||
break;
|
||||
case MSG_NOTIFY_LISTENER:
|
||||
notifyListener();
|
||||
break;
|
||||
case MSG_ON_COMPLETION:
|
||||
List<EventDispatcher> actionsOnCompletion = ((List<EventDispatcher>) message);
|
||||
List<Runnable> actionsOnCompletion = ((List<Runnable>) message);
|
||||
Handler handler = Assertions.checkNotNull(playerApplicationHandler);
|
||||
for (int i = 0; i < actionsOnCompletion.size(); i++) {
|
||||
actionsOnCompletion.get(i).dispatchEvent();
|
||||
handler.post(actionsOnCompletion.get(i));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
@ -509,7 +512,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
}
|
||||
}
|
||||
|
||||
private void scheduleListenerNotification(@Nullable EventDispatcher actionOnCompletion) {
|
||||
private void scheduleListenerNotification(@Nullable Runnable actionOnCompletion) {
|
||||
if (!listenerNotificationScheduled) {
|
||||
Assertions.checkNotNull(player).createMessage(this).setType(MSG_NOTIFY_LISTENER).send();
|
||||
listenerNotificationScheduled = true;
|
||||
|
|
@ -521,9 +524,9 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
|
||||
private void notifyListener() {
|
||||
listenerNotificationScheduled = false;
|
||||
List<EventDispatcher> actionsOnCompletion =
|
||||
List<Runnable> actionsOnCompletion =
|
||||
pendingOnCompletionActions.isEmpty()
|
||||
? Collections.<EventDispatcher>emptyList()
|
||||
? Collections.<Runnable>emptyList()
|
||||
: new ArrayList<>(pendingOnCompletionActions);
|
||||
pendingOnCompletionActions.clear();
|
||||
refreshSourceInfo(
|
||||
|
|
@ -698,34 +701,16 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||
}
|
||||
}
|
||||
|
||||
/** Can be used to dispatch a runnable on the thread the object was created on. */
|
||||
private static final class EventDispatcher {
|
||||
|
||||
public final Handler eventHandler;
|
||||
public final Runnable runnable;
|
||||
|
||||
public EventDispatcher(Runnable runnable) {
|
||||
this.runnable = runnable;
|
||||
this.eventHandler =
|
||||
new Handler(Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper());
|
||||
}
|
||||
|
||||
public void dispatchEvent() {
|
||||
eventHandler.post(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/** Message used to post actions from app thread to playback thread. */
|
||||
private static final class MessageData<T> {
|
||||
|
||||
public final int index;
|
||||
public final T customData;
|
||||
public final @Nullable EventDispatcher actionOnCompletion;
|
||||
public final @Nullable Runnable actionOnCompletion;
|
||||
|
||||
public MessageData(int index, T customData, @Nullable Runnable actionOnCompletion) {
|
||||
this.index = index;
|
||||
this.actionOnCompletion =
|
||||
actionOnCompletion != null ? new EventDispatcher(actionOnCompletion) : null;
|
||||
this.actionOnCompletion = actionOnCompletion;
|
||||
this.customData = customData;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,11 +252,14 @@ public final class Util {
|
|||
* assumption that the Handler won't be used to send messages until the callback is fully
|
||||
* initialized.
|
||||
*
|
||||
* <p>If the current thread doesn't have a {@link Looper}, the application's main thread {@link
|
||||
* Looper} is used.
|
||||
*
|
||||
* @param callback A {@link Handler.Callback}. May be a partially initialized class.
|
||||
* @return A {@link Handler} with the specified callback on the current {@link Looper} thread.
|
||||
*/
|
||||
public static Handler createHandler(Handler.@UnknownInitialization Callback callback) {
|
||||
return createHandler(Looper.myLooper(), callback);
|
||||
return createHandler(getLooper(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -275,6 +278,15 @@ public final class Util {
|
|||
return new Handler(looper, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Looper} associated with the current thread, or the {@link Looper} of the
|
||||
* application's main thread if the current thread doesn't have a {@link Looper}.
|
||||
*/
|
||||
public static Looper getLooper() {
|
||||
Looper myLooper = Looper.myLooper();
|
||||
return myLooper != null ? myLooper : Looper.getMainLooper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new single threaded executor whose thread has the specified name.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.testutil;
|
|||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
|
|
@ -663,7 +664,8 @@ public final class ExoPlayerTestRunner extends Player.DefaultEventListener
|
|||
loadControl,
|
||||
/* drmSessionManager= */ null,
|
||||
new AnalyticsCollector.Factory(),
|
||||
clock);
|
||||
clock,
|
||||
Looper.myLooper());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -442,6 +442,11 @@ public class MediaSourceTestRunner {
|
|||
this.handler = new Handler(looper, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Looper getApplicationLooper() {
|
||||
return handler.getLooper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerMessage createMessage(PlayerMessage.Target target) {
|
||||
return new PlayerMessage(
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ public abstract class StubExoPlayer implements ExoPlayer {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Looper getApplicationLooper() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Player.EventListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
|||
Loading…
Reference in a new issue