Commit 3b633f81 by tiffanywong Committed by Oliver Woodman

Automated g4 rollback of changelist 179563355.

*** Original change description ***

Add possiblity to send messages at playback position.

This adds options to ExoPlayer.sendMessages which allow to specify a window index
and position at which the message should be sent. Additionally, the options can be
configured to use a custom Handler for the messages and whether the message should
be repeated when playback reaches the same position again.

The internal player converts these window positions to period index and position
at the earliest possibility. The internal player also at...

***

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=179666357
parent 6c2d1e19
...@@ -6,10 +6,6 @@ ...@@ -6,10 +6,6 @@
* Add optional parameter to `stop` to reset the player when stopping. * Add optional parameter to `stop` to reset the player when stopping.
* Add a reason to `EventListener.onTimelineChanged` to distinguish between * Add a reason to `EventListener.onTimelineChanged` to distinguish between
initial preparation, reset and dynamic updates. initial preparation, reset and dynamic updates.
* Replaced `ExoPlayer.sendMessages` with `ExoPlayer.createMessage` to allow
more customization of the message. Now supports setting a message delivery
playback position and/or a delivery handler.
([#2189](https://github.com/google/ExoPlayer/issues/2189)).
* Buffering: * Buffering:
* Allow a back-buffer of media to be retained behind the current playback * Allow a back-buffer of media to be retained behind the current playback
position, for fast backward seeking. The back-buffer can be configured by position, for fast backward seeking. The back-buffer can be configured by
......
...@@ -119,11 +119,9 @@ public class VpxPlaybackTest extends InstrumentationTestCase { ...@@ -119,11 +119,9 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
new DefaultDataSourceFactory(context, "ExoPlayerExtVp9Test")) new DefaultDataSourceFactory(context, "ExoPlayerExtVp9Test"))
.setExtractorsFactory(MatroskaExtractor.FACTORY) .setExtractorsFactory(MatroskaExtractor.FACTORY)
.createMediaSource(uri); .createMediaSource(uri);
player player.sendMessages(new ExoPlayer.ExoPlayerMessage(videoRenderer,
.createMessage(videoRenderer) LibvpxVideoRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER,
.setType(LibvpxVideoRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER) new VpxVideoSurfaceView(context)));
.setMessage(new VpxVideoSurfaceView(context))
.send();
player.prepare(mediaSource); player.prepare(mediaSource);
player.setPlayWhenReady(true); player.setPlayWhenReady(true);
Looper.loop(); Looper.loop();
......
...@@ -157,7 +157,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { ...@@ -157,7 +157,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
return ADAPTIVE_NOT_SUPPORTED; return ADAPTIVE_NOT_SUPPORTED;
} }
// PlayerMessage.Target implementation. // ExoPlayerComponent implementation.
@Override @Override
public void handleMessage(int what, Object object) throws ExoPlaybackException { public void handleMessage(int what, Object object) throws ExoPlaybackException {
......
...@@ -22,7 +22,6 @@ import android.os.Message; ...@@ -22,7 +22,6 @@ import android.os.Message;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
...@@ -32,8 +31,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelector; ...@@ -32,8 +31,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult; import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
/** /**
...@@ -48,7 +45,6 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -48,7 +45,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
private final TrackSelectorResult emptyTrackSelectorResult; private final TrackSelectorResult emptyTrackSelectorResult;
private final Handler eventHandler; private final Handler eventHandler;
private final ExoPlayerImplInternal internalPlayer; private final ExoPlayerImplInternal internalPlayer;
private final Handler internalPlayerHandler;
private final CopyOnWriteArraySet<Player.EventListener> listeners; private final CopyOnWriteArraySet<Player.EventListener> listeners;
private final Timeline.Window window; private final Timeline.Window window;
private final Timeline.Period period; private final Timeline.Period period;
...@@ -117,7 +113,6 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -117,7 +113,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
shuffleModeEnabled, shuffleModeEnabled,
eventHandler, eventHandler,
this); this);
internalPlayerHandler = new Handler(internalPlayer.getPlaybackLooper());
} }
@Override @Override
...@@ -331,47 +326,12 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -331,47 +326,12 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override @Override
public void sendMessages(ExoPlayerMessage... messages) { public void sendMessages(ExoPlayerMessage... messages) {
for (ExoPlayerMessage message : messages) { internalPlayer.sendMessages(messages);
createMessage(message.target).setType(message.messageType).setMessage(message.message).send();
}
}
@Override
public PlayerMessage createMessage(Target target) {
return new PlayerMessage(
internalPlayer,
target,
playbackInfo.timeline,
getCurrentWindowIndex(),
internalPlayerHandler);
} }
@Override @Override
public void blockingSendMessages(ExoPlayerMessage... messages) { public void blockingSendMessages(ExoPlayerMessage... messages) {
List<PlayerMessage> playerMessages = new ArrayList<>(); internalPlayer.blockingSendMessages(messages);
for (ExoPlayerMessage message : messages) {
playerMessages.add(
createMessage(message.target)
.setType(message.messageType)
.setMessage(message.message)
.send());
}
boolean wasInterrupted = false;
for (PlayerMessage message : playerMessages) {
boolean blockMessage = true;
while (blockMessage) {
try {
message.blockUntilDelivered();
blockMessage = false;
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}
if (wasInterrupted) {
// Restore the interrupted status.
Thread.currentThread().interrupt();
}
} }
@Override @Override
......
...@@ -179,7 +179,7 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities ...@@ -179,7 +179,7 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities
return ADAPTIVE_NOT_SUPPORTED; return ADAPTIVE_NOT_SUPPORTED;
} }
// PlayerMessage.Target implementation. // ExoPlayerComponent implementation.
@Override @Override
public void handleMessage(int what, Object object) throws ExoPlaybackException { public void handleMessage(int what, Object object) throws ExoPlaybackException {
......
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
import android.os.Handler;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions;
/**
* Defines a player message which can be sent with a {@link Sender} and received by a {@link
* Target}.
*/
public final class PlayerMessage {
/** A target for messages. */
public interface Target {
/**
* Handles a message delivered to the target.
*
* @param messageType The message type.
* @param message The message.
* @throws ExoPlaybackException If an error occurred whilst handling the message.
*/
void handleMessage(int messageType, Object message) throws ExoPlaybackException;
}
/** A sender for messages. */
public interface Sender {
/** A listener for message events triggered by the sender. */
interface Listener {
/** Called when the message has been delivered. */
void onMessageDelivered();
/** Called when the message has been deleted. */
void onMessageDeleted();
}
/**
* Sends a message.
*
* @param message The message to be sent.
* @param listener The listener to listen to message events.
*/
void sendMessage(PlayerMessage message, Listener listener);
}
private final Target target;
private final Sender sender;
private final Timeline timeline;
private int type;
private Object message;
private Handler handler;
private int windowIndex;
private long positionMs;
private boolean deleteAfterDelivery;
private boolean isSent;
private boolean isDelivered;
private boolean isDeleted;
/**
* Creates a new message.
*
* @param sender The {@link Sender} used to send the message.
* @param target The {@link Target} the message is sent to.
* @param timeline The timeline used when setting the position with {@link #setPosition(long)}. If
* set to {@link Timeline#EMPTY}, any position can be specified.
* @param defaultWindowIndex The default window index in the {@code timeline} when no other window
* index is specified.
* @param defaultHandler The default handler to send the message on when no other handler is
* specified.
*/
public PlayerMessage(
Sender sender,
Target target,
Timeline timeline,
int defaultWindowIndex,
Handler defaultHandler) {
this.sender = sender;
this.target = target;
this.timeline = timeline;
this.handler = defaultHandler;
this.windowIndex = defaultWindowIndex;
this.positionMs = C.TIME_UNSET;
this.deleteAfterDelivery = true;
}
/** Returns the timeline used for setting the position with {@link #setPosition(long)}. */
public Timeline getTimeline() {
return timeline;
}
/** Returns the target the message is sent to. */
public Target getTarget() {
return target;
}
/**
* Sets a custom message type forwarded to the {@link Target#handleMessage(int, Object)}.
*
* @param messageType The custom message type.
* @return This message.
* @throws IllegalStateException If {@link #send()} has already been called.
*/
public PlayerMessage setType(int messageType) {
Assertions.checkState(!isSent);
this.type = messageType;
return this;
}
/** Returns custom message type forwarded to the {@link Target#handleMessage(int, Object)}. */
public int getType() {
return type;
}
/**
* Sets a custom message forwarded to the {@link Target#handleMessage(int, Object)}.
*
* @param message The custom message.
* @return This message.
* @throws IllegalStateException If {@link #send()} has already been called.
*/
public PlayerMessage setMessage(@Nullable Object message) {
Assertions.checkState(!isSent);
this.message = message;
return this;
}
/** Returns custom message forwarded to the {@link Target#handleMessage(int, Object)}. */
public Object getMessage() {
return message;
}
/**
* Sets the handler the message is delivered on.
*
* @param handler A {@link Handler}.
* @return This message.
* @throws IllegalStateException If {@link #send()} has already been called.
*/
public PlayerMessage setHandler(Handler handler) {
Assertions.checkState(!isSent);
this.handler = handler;
return this;
}
/** Returns the handler the message is delivered on. */
public Handler getHandler() {
return handler;
}
/**
* Sets a position in the current window at which the message will be delivered.
*
* @param positionMs The position in the current window at which the message will be sent, in
* milliseconds.
* @return This message.
* @throws IllegalStateException If {@link #send()} has already been called.
*/
public PlayerMessage setPosition(long positionMs) {
Assertions.checkState(!isSent);
this.positionMs = positionMs;
return this;
}
/**
* Returns position in window at {@link #getWindowIndex()} at which the message will be delivered,
* in milliseconds. If {@link C#TIME_UNSET}, the message will be delivered immediately.
*/
public long getPositionMs() {
return positionMs;
}
/**
* Sets a position in a window at which the message will be delivered.
*
* @param windowIndex The index of the window at which the message will be sent.
* @param positionMs The position in the window with index {@code windowIndex} at which the
* message will be sent, in milliseconds.
* @return This message.
* @throws IllegalSeekPositionException If the timeline returned by {@link #getTimeline()} is not
* empty and the provided window index is not within the bounds of the timeline.
* @throws IllegalStateException If {@link #send()} has already been called.
*/
public PlayerMessage setPosition(int windowIndex, long positionMs) {
Assertions.checkState(!isSent);
Assertions.checkArgument(positionMs != C.TIME_UNSET);
if (windowIndex < 0 || (!timeline.isEmpty() && windowIndex >= timeline.getWindowCount())) {
throw new IllegalSeekPositionException(timeline, windowIndex, positionMs);
}
this.windowIndex = windowIndex;
this.positionMs = positionMs;
return this;
}
/** Returns window index at which the message will be delivered. */
public int getWindowIndex() {
return windowIndex;
}
/**
* Sets whether the message will be deleted after delivery. If false, the message will be resent
* if playback reaches the specified position again. Only allowed to be false if a position is set
* with {@link #setPosition(long)}.
*
* @param deleteAfterDelivery Whether the message is deleted after delivery.
* @return This message.
* @throws IllegalStateException If {@link #send()} has already been called.
*/
public PlayerMessage setDeleteAfterDelivery(boolean deleteAfterDelivery) {
Assertions.checkState(!isSent);
this.deleteAfterDelivery = deleteAfterDelivery;
return this;
}
/** Returns whether the message will be deleted after delivery. */
public boolean getDeleteAfterDelivery() {
return deleteAfterDelivery;
}
/**
* Sends the message. If the target throws an {@link ExoPlaybackException} then it is propagated
* out of the player as an error using {@link
* Player.EventListener#onPlayerError(ExoPlaybackException)}.
*
* @return This message.
* @throws IllegalStateException If {@link #send()} has already been called.
*/
public PlayerMessage send() {
Assertions.checkState(!isSent);
if (positionMs == C.TIME_UNSET) {
Assertions.checkArgument(deleteAfterDelivery);
}
isSent = true;
sender.sendMessage(
this,
new Sender.Listener() {
@Override
public void onMessageDelivered() {
synchronized (PlayerMessage.this) {
isDelivered = true;
PlayerMessage.this.notifyAll();
}
}
@Override
public void onMessageDeleted() {
synchronized (PlayerMessage.this) {
isDeleted = true;
PlayerMessage.this.notifyAll();
}
}
});
return this;
}
/**
* Blocks until after the message has been delivered or the player is no longer able to deliver
* the message.
*
* <p>Note that this method can't be called if the current thread is the same thread used by the
* message handler set with {@link #setHandler(Handler)} as it would cause a deadlock.
*
* @return Whether the message was delivered successfully.
* @throws IllegalStateException If this method is called before {@link #send()}.
* @throws IllegalStateException If this method is called on the same thread used by the message
* handler set with {@link #setHandler(Handler)}.
* @throws InterruptedException If the current thread is interrupted while waiting for the message
* to be delivered.
*/
public synchronized boolean blockUntilDelivered() throws InterruptedException {
Assertions.checkState(!isSent);
Assertions.checkState(handler.getLooper().getThread() != Thread.currentThread());
while (!isDelivered && !isDeleted) {
wait();
}
return isDelivered;
}
}
...@@ -15,20 +15,22 @@ ...@@ -15,20 +15,22 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import com.google.android.exoplayer2.ExoPlayer.ExoPlayerComponent;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.util.MediaClock; import com.google.android.exoplayer2.util.MediaClock;
import java.io.IOException; import java.io.IOException;
/** /**
* Renders media read from a {@link SampleStream}. * Renders media read from a {@link SampleStream}.
* * <p>
* <p>Internally, a renderer's lifecycle is managed by the owning {@link ExoPlayer}. The renderer is * Internally, a renderer's lifecycle is managed by the owning {@link ExoPlayer}. The renderer is
* transitioned through various states as the overall playback state changes. The valid state * transitioned through various states as the overall playback state changes. The valid state
* transitions are shown below, annotated with the methods that are called during each transition. * transitions are shown below, annotated with the methods that are called during each transition.
* * <p align="center">
* <p align="center"><img src="doc-files/renderer-states.svg" alt="Renderer state transitions"> * <img src="doc-files/renderer-states.svg" alt="Renderer state transitions">
* </p>
*/ */
public interface Renderer extends PlayerMessage.Target { public interface Renderer extends ExoPlayerComponent {
/** /**
* The renderer is disabled. * The renderer is disabled.
......
...@@ -93,6 +93,8 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -93,6 +93,8 @@ public class SimpleExoPlayer implements ExoPlayer {
private final CopyOnWriteArraySet<MetadataOutput> metadataOutputs; private final CopyOnWriteArraySet<MetadataOutput> metadataOutputs;
private final CopyOnWriteArraySet<VideoRendererEventListener> videoDebugListeners; private final CopyOnWriteArraySet<VideoRendererEventListener> videoDebugListeners;
private final CopyOnWriteArraySet<AudioRendererEventListener> audioDebugListeners; private final CopyOnWriteArraySet<AudioRendererEventListener> audioDebugListeners;
private final int videoRendererCount;
private final int audioRendererCount;
private Format videoFormat; private Format videoFormat;
private Format audioFormat; private Format audioFormat;
...@@ -122,6 +124,25 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -122,6 +124,25 @@ public class SimpleExoPlayer implements ExoPlayer {
renderers = renderersFactory.createRenderers(eventHandler, componentListener, componentListener, renderers = renderersFactory.createRenderers(eventHandler, componentListener, componentListener,
componentListener, componentListener); componentListener, componentListener);
// Obtain counts of video and audio renderers.
int videoRendererCount = 0;
int audioRendererCount = 0;
for (Renderer renderer : renderers) {
switch (renderer.getTrackType()) {
case C.TRACK_TYPE_VIDEO:
videoRendererCount++;
break;
case C.TRACK_TYPE_AUDIO:
audioRendererCount++;
break;
default:
// Don't count other track types.
break;
}
}
this.videoRendererCount = videoRendererCount;
this.audioRendererCount = audioRendererCount;
// Set initial values. // Set initial values.
audioVolume = 1; audioVolume = 1;
audioSessionId = C.AUDIO_SESSION_ID_UNSET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
...@@ -142,15 +163,15 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -142,15 +163,15 @@ public class SimpleExoPlayer implements ExoPlayer {
*/ */
public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) {
this.videoScalingMode = videoScalingMode; this.videoScalingMode = videoScalingMode;
ExoPlayerMessage[] messages = new ExoPlayerMessage[videoRendererCount];
int count = 0;
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) {
player messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_SCALING_MODE,
.createMessage(renderer) videoScalingMode);
.setType(C.MSG_SET_SCALING_MODE)
.setMessage(videoScalingMode)
.send();
} }
} }
player.sendMessages(messages);
} }
/** /**
...@@ -331,15 +352,15 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -331,15 +352,15 @@ public class SimpleExoPlayer implements ExoPlayer {
*/ */
public void setAudioAttributes(AudioAttributes audioAttributes) { public void setAudioAttributes(AudioAttributes audioAttributes) {
this.audioAttributes = audioAttributes; this.audioAttributes = audioAttributes;
ExoPlayerMessage[] messages = new ExoPlayerMessage[audioRendererCount];
int count = 0;
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_AUDIO_ATTRIBUTES,
.createMessage(renderer) audioAttributes);
.setType(C.MSG_SET_AUDIO_ATTRIBUTES)
.setMessage(audioAttributes)
.send();
} }
} }
player.sendMessages(messages);
} }
/** /**
...@@ -356,11 +377,14 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -356,11 +377,14 @@ public class SimpleExoPlayer implements ExoPlayer {
*/ */
public void setVolume(float audioVolume) { public void setVolume(float audioVolume) {
this.audioVolume = audioVolume; this.audioVolume = audioVolume;
ExoPlayerMessage[] messages = new ExoPlayerMessage[audioRendererCount];
int count = 0;
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player.createMessage(renderer).setType(C.MSG_SET_VOLUME).setMessage(audioVolume).send(); messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_VOLUME, audioVolume);
} }
} }
player.sendMessages(messages);
} }
/** /**
...@@ -747,11 +771,6 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -747,11 +771,6 @@ public class SimpleExoPlayer implements ExoPlayer {
} }
@Override @Override
public PlayerMessage createMessage(PlayerMessage.Target target) {
return player.createMessage(target);
}
@Override
public void blockingSendMessages(ExoPlayerMessage... messages) { public void blockingSendMessages(ExoPlayerMessage... messages) {
player.blockingSendMessages(messages); player.blockingSendMessages(messages);
} }
...@@ -889,25 +908,22 @@ public class SimpleExoPlayer implements ExoPlayer { ...@@ -889,25 +908,22 @@ public class SimpleExoPlayer implements ExoPlayer {
private void setVideoSurfaceInternal(Surface surface, boolean ownsSurface) { private void setVideoSurfaceInternal(Surface surface, boolean ownsSurface) {
// Note: We don't turn this method into a no-op if the surface is being replaced with itself // Note: We don't turn this method into a no-op if the surface is being replaced with itself
// so as to ensure onRenderedFirstFrame callbacks are still called in this case. // so as to ensure onRenderedFirstFrame callbacks are still called in this case.
boolean surfaceReplaced = this.surface != null && this.surface != surface; ExoPlayerMessage[] messages = new ExoPlayerMessage[videoRendererCount];
int count = 0;
for (Renderer renderer : renderers) { for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) {
PlayerMessage message = messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_SURFACE, surface);
player.createMessage(renderer).setType(C.MSG_SET_SURFACE).setMessage(surface).send();
if (surfaceReplaced) {
try {
message.blockUntilDelivered();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
} }
} }
if (surfaceReplaced) { if (this.surface != null && this.surface != surface) {
// We're replacing a surface. Block to ensure that it's not accessed after the method returns.
player.blockingSendMessages(messages);
// If we created the previous surface, we are responsible for releasing it. // If we created the previous surface, we are responsible for releasing it.
if (this.ownsSurface) { if (this.ownsSurface) {
this.surface.release(); this.surface.release();
} }
} else {
player.sendMessages(messages);
} }
this.surface = surface; this.surface = surface;
this.ownsSurface = ownsSurface; this.ownsSurface = ownsSurface;
......
...@@ -23,7 +23,8 @@ import android.util.SparseIntArray; ...@@ -23,7 +23,8 @@ import android.util.SparseIntArray;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlayerMessage; import com.google.android.exoplayer2.ExoPlayer.ExoPlayerComponent;
import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder; import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
...@@ -41,7 +42,7 @@ import java.util.Map; ...@@ -41,7 +42,7 @@ import java.util.Map;
* Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified * Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified
* during playback. Access to this class is thread-safe. * during playback. Access to this class is thread-safe.
*/ */
public final class DynamicConcatenatingMediaSource implements MediaSource, PlayerMessage.Target { public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPlayerComponent {
private static final int MSG_ADD = 0; private static final int MSG_ADD = 0;
private static final int MSG_ADD_MULTIPLE = 1; private static final int MSG_ADD_MULTIPLE = 1;
...@@ -146,11 +147,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe ...@@ -146,11 +147,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
Assertions.checkArgument(!mediaSourcesPublic.contains(mediaSource)); Assertions.checkArgument(!mediaSourcesPublic.contains(mediaSource));
mediaSourcesPublic.add(index, mediaSource); mediaSourcesPublic.add(index, mediaSource);
if (player != null) { if (player != null) {
player player.sendMessages(new ExoPlayerMessage(this, MSG_ADD,
.createMessage(this) new MessageData<>(index, mediaSource, actionOnCompletion)));
.setType(MSG_ADD)
.setMessage(new MessageData<>(index, mediaSource, actionOnCompletion))
.send();
} else if (actionOnCompletion != null) { } else if (actionOnCompletion != null) {
actionOnCompletion.run(); actionOnCompletion.run();
} }
...@@ -222,11 +220,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe ...@@ -222,11 +220,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
} }
mediaSourcesPublic.addAll(index, mediaSources); mediaSourcesPublic.addAll(index, mediaSources);
if (player != null && !mediaSources.isEmpty()) { if (player != null && !mediaSources.isEmpty()) {
player player.sendMessages(new ExoPlayerMessage(this, MSG_ADD_MULTIPLE,
.createMessage(this) new MessageData<>(index, mediaSources, actionOnCompletion)));
.setType(MSG_ADD_MULTIPLE)
.setMessage(new MessageData<>(index, mediaSources, actionOnCompletion))
.send();
} else if (actionOnCompletion != null){ } else if (actionOnCompletion != null){
actionOnCompletion.run(); actionOnCompletion.run();
} }
...@@ -261,11 +256,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe ...@@ -261,11 +256,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
public synchronized void removeMediaSource(int index, @Nullable Runnable actionOnCompletion) { public synchronized void removeMediaSource(int index, @Nullable Runnable actionOnCompletion) {
mediaSourcesPublic.remove(index); mediaSourcesPublic.remove(index);
if (player != null) { if (player != null) {
player player.sendMessages(new ExoPlayerMessage(this, MSG_REMOVE,
.createMessage(this) new MessageData<>(index, null, actionOnCompletion)));
.setType(MSG_REMOVE)
.setMessage(new MessageData<>(index, null, actionOnCompletion))
.send();
} else if (actionOnCompletion != null) { } else if (actionOnCompletion != null) {
actionOnCompletion.run(); actionOnCompletion.run();
} }
...@@ -301,11 +293,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe ...@@ -301,11 +293,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
} }
mediaSourcesPublic.add(newIndex, mediaSourcesPublic.remove(currentIndex)); mediaSourcesPublic.add(newIndex, mediaSourcesPublic.remove(currentIndex));
if (player != null) { if (player != null) {
player player.sendMessages(new ExoPlayerMessage(this, MSG_MOVE,
.createMessage(this) new MessageData<>(currentIndex, newIndex, actionOnCompletion)));
.setType(MSG_MOVE)
.setMessage(new MessageData<>(currentIndex, newIndex, actionOnCompletion))
.send();
} else if (actionOnCompletion != null) { } else if (actionOnCompletion != null) {
actionOnCompletion.run(); actionOnCompletion.run();
} }
...@@ -438,7 +427,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe ...@@ -438,7 +427,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
new ConcatenatedTimeline(mediaSourceHolders, windowCount, periodCount, shuffleOrder), new ConcatenatedTimeline(mediaSourceHolders, windowCount, periodCount, shuffleOrder),
null); null);
if (actionOnCompletion != null) { if (actionOnCompletion != null) {
player.createMessage(this).setType(MSG_ON_COMPLETION).setMessage(actionOnCompletion).send(); player.sendMessages(
new ExoPlayerMessage(this, MSG_ON_COMPLETION, actionOnCompletion));
} }
} }
} }
......
...@@ -562,18 +562,6 @@ public final class Util { ...@@ -562,18 +562,6 @@ public final class Util {
} }
/** /**
* Compares two long values and returns the same value as {@code Long.compare(long, long)}.
*
* @param left The left operand.
* @param right The right operand.
* @return 0, if left == right, a negative value if left &lt; right, or a positive value if left
* &gt; right.
*/
public static int compareLong(long left, long right) {
return left < right ? -1 : left == right ? 0 : 1;
}
/**
* Parses an xs:duration attribute value, returning the parsed duration in milliseconds. * Parses an xs:duration attribute value, returning the parsed duration in milliseconds.
* *
* @param value The attribute value to decode. * @param value The attribute value to decode.
......
...@@ -18,17 +18,13 @@ package com.google.android.exoplayer2.testutil; ...@@ -18,17 +18,13 @@ package com.google.android.exoplayer2.testutil;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.testutil.ActionSchedule.ActionNode; import com.google.android.exoplayer2.testutil.ActionSchedule.ActionNode;
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerTarget;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
/** /**
...@@ -349,63 +345,7 @@ public abstract class Action { ...@@ -349,63 +345,7 @@ public abstract class Action {
Surface surface) { Surface surface) {
player.setShuffleModeEnabled(shuffleModeEnabled); player.setShuffleModeEnabled(shuffleModeEnabled);
} }
}
/** Calls {@link ExoPlayer#createMessage(Target)} and {@link PlayerMessage#send()}. */
public static final class SendMessages extends Action {
private final Target target;
private final int windowIndex;
private final long positionMs;
private final boolean deleteAfterDelivery;
/**
* @param tag A tag to use for logging.
* @param target A message target.
* @param positionMs The position at which the message should be sent, in milliseconds.
*/
public SendMessages(String tag, Target target, long positionMs) {
this(
tag,
target,
/* windowIndex= */ C.INDEX_UNSET,
positionMs,
/* deleteAfterDelivery= */ true);
}
/**
* @param tag A tag to use for logging.
* @param target A message target.
* @param windowIndex The window index at which the message should be sent, or {@link
* C#INDEX_UNSET} for the current window.
* @param positionMs The position at which the message should be sent, in milliseconds.
* @param deleteAfterDelivery Whether the message will be deleted after delivery.
*/
public SendMessages(
String tag, Target target, int windowIndex, long positionMs, boolean deleteAfterDelivery) {
super(tag, "SendMessages");
this.target = target;
this.windowIndex = windowIndex;
this.positionMs = positionMs;
this.deleteAfterDelivery = deleteAfterDelivery;
}
@Override
protected void doActionImpl(
final SimpleExoPlayer player, MappingTrackSelector trackSelector, Surface surface) {
if (target instanceof PlayerTarget) {
((PlayerTarget) target).setPlayer(player);
}
PlayerMessage message = player.createMessage(target);
if (windowIndex != C.INDEX_UNSET) {
message.setPosition(windowIndex, positionMs);
} else {
message.setPosition(positionMs);
}
message.setHandler(new Handler());
message.setDeleteAfterDelivery(deleteAfterDelivery);
message.send();
}
} }
/** /**
......
...@@ -20,11 +20,8 @@ import android.os.Looper; ...@@ -20,11 +20,8 @@ import android.os.Looper;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.view.Surface; import android.view.Surface;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
...@@ -32,7 +29,6 @@ import com.google.android.exoplayer2.testutil.Action.ClearVideoSurface; ...@@ -32,7 +29,6 @@ import com.google.android.exoplayer2.testutil.Action.ClearVideoSurface;
import com.google.android.exoplayer2.testutil.Action.ExecuteRunnable; import com.google.android.exoplayer2.testutil.Action.ExecuteRunnable;
import com.google.android.exoplayer2.testutil.Action.PrepareSource; import com.google.android.exoplayer2.testutil.Action.PrepareSource;
import com.google.android.exoplayer2.testutil.Action.Seek; import com.google.android.exoplayer2.testutil.Action.Seek;
import com.google.android.exoplayer2.testutil.Action.SendMessages;
import com.google.android.exoplayer2.testutil.Action.SetPlayWhenReady; import com.google.android.exoplayer2.testutil.Action.SetPlayWhenReady;
import com.google.android.exoplayer2.testutil.Action.SetPlaybackParameters; import com.google.android.exoplayer2.testutil.Action.SetPlaybackParameters;
import com.google.android.exoplayer2.testutil.Action.SetRendererDisabled; import com.google.android.exoplayer2.testutil.Action.SetRendererDisabled;
...@@ -320,44 +316,6 @@ public final class ActionSchedule { ...@@ -320,44 +316,6 @@ public final class ActionSchedule {
} }
/** /**
* Schedules sending a {@link PlayerMessage}.
*
* @param positionMs The position in the current window at which the message should be sent, in
* milliseconds.
* @return The builder, for convenience.
*/
public Builder sendMessage(Target target, long positionMs) {
return apply(new SendMessages(tag, target, positionMs));
}
/**
* Schedules sending a {@link PlayerMessage}.
*
* @param target A message target.
* @param windowIndex The window index at which the message should be sent.
* @param positionMs The position at which the message should be sent, in milliseconds.
* @return The builder, for convenience.
*/
public Builder sendMessage(Target target, int windowIndex, long positionMs) {
return apply(
new SendMessages(tag, target, windowIndex, positionMs, /* deleteAfterDelivery= */ true));
}
/**
* Schedules to send a {@link PlayerMessage}.
*
* @param target A message target.
* @param windowIndex The window index at which the message should be sent.
* @param positionMs The position at which the message should be sent, in milliseconds.
* @param deleteAfterDelivery Whether the message will be deleted after delivery.
* @return The builder, for convenience.
*/
public Builder sendMessage(
Target target, int windowIndex, long positionMs, boolean deleteAfterDelivery) {
return apply(new SendMessages(tag, target, windowIndex, positionMs, deleteAfterDelivery));
}
/**
* Schedules a delay until the timeline changed to a specified expected timeline. * Schedules a delay until the timeline changed to a specified expected timeline.
* *
* @param expectedTimeline The expected timeline to wait for. * @param expectedTimeline The expected timeline to wait for.
...@@ -407,28 +365,7 @@ public final class ActionSchedule { ...@@ -407,28 +365,7 @@ public final class ActionSchedule {
currentDelayMs = 0; currentDelayMs = 0;
return this; return this;
} }
}
/**
* Provides a wrapper for a {@link Target} which has access to the player when handling messages.
* Can be used with {@link Builder#sendMessage(Target, long)}.
*/
public abstract static class PlayerTarget implements Target {
private SimpleExoPlayer player;
/** Handles the message send to the component and additionally provides access to the player. */
public abstract void handleMessage(SimpleExoPlayer player, int messageType, Object message);
/** Sets the player to be passed to {@link #handleMessage(SimpleExoPlayer, int, Object)}. */
/* package */ void setPlayer(SimpleExoPlayer player) {
this.player = player;
}
@Override
public final void handleMessage(int messageType, Object message) throws ExoPlaybackException {
handleMessage(player, messageType, message);
}
} }
/** /**
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer2.testutil; package com.google.android.exoplayer2.testutil;
import android.util.Pair;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
...@@ -171,7 +170,7 @@ public final class FakeTimeline extends Timeline { ...@@ -171,7 +170,7 @@ public final class FakeTimeline extends Timeline {
int windowPeriodIndex = periodIndex - periodOffsets[windowIndex]; int windowPeriodIndex = periodIndex - periodOffsets[windowIndex];
TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex];
Object id = setIds ? windowPeriodIndex : null; Object id = setIds ? windowPeriodIndex : null;
Object uid = setIds ? Pair.create(windowDefinition.id, windowPeriodIndex) : null; Object uid = setIds ? periodIndex : null;
long periodDurationUs = windowDefinition.durationUs / windowDefinition.periodCount; long periodDurationUs = windowDefinition.durationUs / windowDefinition.periodCount;
long positionInWindowUs = periodDurationUs * windowPeriodIndex; long positionInWindowUs = periodDurationUs * windowPeriodIndex;
if (windowDefinition.adGroupsPerPeriodCount == 0) { if (windowDefinition.adGroupsPerPeriodCount == 0) {
...@@ -199,13 +198,11 @@ public final class FakeTimeline extends Timeline { ...@@ -199,13 +198,11 @@ public final class FakeTimeline extends Timeline {
@Override @Override
public int getIndexOfPeriod(Object uid) { public int getIndexOfPeriod(Object uid) {
Period period = new Period(); if (!(uid instanceof Integer)) {
for (int i = 0; i < getPeriodCount(); i++) { return C.INDEX_UNSET;
if (getPeriod(i, period, true).uid.equals(uid)) {
return i;
}
} }
return C.INDEX_UNSET; int index = (Integer) uid;
return index >= 0 && index < getPeriodCount() ? index : C.INDEX_UNSET;
} }
private static TimelineWindowDefinition[] createDefaultWindowDefinitions(int windowCount) { private static TimelineWindowDefinition[] createDefaultWindowDefinitions(int windowCount) {
......
...@@ -24,9 +24,7 @@ import android.os.Handler; ...@@ -24,9 +24,7 @@ import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.util.Pair;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
...@@ -283,8 +281,7 @@ public class MediaSourceTestRunner { ...@@ -283,8 +281,7 @@ public class MediaSourceTestRunner {
} }
private static class EventHandlingExoPlayer extends StubExoPlayer private static class EventHandlingExoPlayer extends StubExoPlayer implements Handler.Callback {
implements Handler.Callback, PlayerMessage.Sender {
private final Handler handler; private final Handler handler;
...@@ -293,33 +290,23 @@ public class MediaSourceTestRunner { ...@@ -293,33 +290,23 @@ public class MediaSourceTestRunner {
} }
@Override @Override
public PlayerMessage createMessage(PlayerMessage.Target target) { public void sendMessages(ExoPlayerMessage... messages) {
return new PlayerMessage( handler.obtainMessage(0, messages).sendToTarget();
/* sender= */ this, target, Timeline.EMPTY, /* defaultWindowIndex= */ 0, handler);
} }
@Override @Override
public void sendMessage(PlayerMessage message, Listener listener) {
handler.obtainMessage(0, Pair.create(message, listener)).sendToTarget();
}
@Override
@SuppressWarnings("unchecked")
public boolean handleMessage(Message msg) { public boolean handleMessage(Message msg) {
Pair<PlayerMessage, Listener> messageAndListener = (Pair<PlayerMessage, Listener>) msg.obj; ExoPlayerMessage[] messages = (ExoPlayerMessage[]) msg.obj;
try { for (ExoPlayerMessage message : messages) {
messageAndListener try {
.first message.target.handleMessage(message.messageType, message.message);
.getTarget() } catch (ExoPlaybackException e) {
.handleMessage( fail("Unexpected ExoPlaybackException.");
messageAndListener.first.getType(), messageAndListener.first.getMessage()); }
messageAndListener.second.onMessageDelivered();
messageAndListener.second.onMessageDeleted();
} catch (ExoPlaybackException e) {
fail("Unexpected ExoPlaybackException.");
} }
return true; return true;
} }
} }
} }
...@@ -19,7 +19,6 @@ import android.os.Looper; ...@@ -19,7 +19,6 @@ import android.os.Looper;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
...@@ -148,11 +147,6 @@ public abstract class StubExoPlayer implements ExoPlayer { ...@@ -148,11 +147,6 @@ public abstract class StubExoPlayer implements ExoPlayer {
} }
@Override @Override
public PlayerMessage createMessage(PlayerMessage.Target target) {
throw new UnsupportedOperationException();
}
@Override
public void sendMessages(ExoPlayerMessage... messages) { public void sendMessages(ExoPlayerMessage... messages) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment