Allow delivery to multiple messages in a single message.

Where multiple messages are required to be sent in order
to perform a player reconfiguration, it will usually be
desirable to process all messages in a single "transaction"
(i.e. without any rendering happening when only some of
the messages have been applied).
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123228334
This commit is contained in:
olly 2016-05-25 10:36:34 -07:00 committed by Oliver Woodman
parent ff7819e86a
commit 10329eb111
7 changed files with 100 additions and 50 deletions

View File

@ -104,8 +104,9 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
false), false),
new DefaultAllocator(BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT, new DefaultAllocator(BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT,
new Extractor[] {new MatroskaExtractor()}); new Extractor[] {new MatroskaExtractor()});
player.sendMessage(videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER, player.sendMessages(new ExoPlayer.ExoPlayerMessage(videoRenderer,
new VpxVideoSurfaceView(context)); LibvpxVideoTrackRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER,
new VpxVideoSurfaceView(context)));
player.setSource(sampleSource); player.setSource(sampleSource);
player.setPlayWhenReady(true); player.setPlayWhenReady(true);
Looper.loop(); Looper.loop();

View File

@ -41,10 +41,10 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
/** /**
* The type of a message that can be passed to an instance of this class via * The type of a message that can be passed to an instance of this class via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object * {@link ExoPlayer#sendMessages} or {@link ExoPlayer#blockingSendMessages}. The message object
* should be the target {@link VpxOutputBufferRenderer}, or null. * should be the target {@link VpxOutputBufferRenderer}, or null.
*/ */
public static final int MSG_SET_OUTPUT_BUFFER_RENDERER = 2; public static final int MSG_SET_OUTPUT_BUFFER_RENDERER = C.MSG_CUSTOM_BASE;
/** /**
* The number of input buffers and the number of output buffers. The track renderer may limit the * The number of input buffers and the number of output buffers. The track renderer may limit the

View File

@ -231,25 +231,30 @@ public interface C {
/** /**
* The type of a message that can be passed to a video {@link TrackRenderer} via * The type of a message that can be passed to a video {@link TrackRenderer} via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object * {@link ExoPlayer#sendMessages} or {@link ExoPlayer#blockingSendMessages}. The message object
* should be the target {@link Surface}, or null. * should be the target {@link Surface}, or null.
*/ */
int MSG_SET_SURFACE = 1; int MSG_SET_SURFACE = 1;
/** /**
* The type of a message that can be passed to an audio {@link TrackRenderer} via * The type of a message that can be passed to an audio {@link TrackRenderer} via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object * {@link ExoPlayer#sendMessages} or {@link ExoPlayer#blockingSendMessages}. The message object
* should be a {@link Float} with 0 being silence and 1 being unity gain. * should be a {@link Float} with 0 being silence and 1 being unity gain.
*/ */
int MSG_SET_VOLUME = 2; int MSG_SET_VOLUME = 2;
/** /**
* The type of a message that can be passed to an audio {@link TrackRenderer} via * The type of a message that can be passed to an audio {@link TrackRenderer} via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object * {@link ExoPlayer#sendMessages} or {@link ExoPlayer#blockingSendMessages}. The message object
* should be a {@link android.media.PlaybackParams}, which will be used to configure the * should be a {@link android.media.PlaybackParams}, which will be used to configure the
* underlying {@link android.media.AudioTrack}. The message object should not be modified by the * underlying {@link android.media.AudioTrack}. The message object should not be modified by the
* caller after it has been passed * caller after it has been passed
*/ */
int MSG_SET_PLAYBACK_PARAMS = 3; int MSG_SET_PLAYBACK_PARAMS = 3;
/**
* A minimum value for custom {@link TrackRenderer} message types.
*/
int MSG_CUSTOM_BASE = 10000;
} }

View File

@ -132,8 +132,8 @@ public interface ExoPlayer {
/** /**
* A component of an {@link ExoPlayer} that can receive messages on the playback thread. * A component of an {@link ExoPlayer} that can receive messages on the playback thread.
* <p> * <p>
* Messages can be delivered to a component via {@link ExoPlayer#sendMessage} and * Messages can be delivered to a component via {@link ExoPlayer#sendMessages} and
* {@link ExoPlayer#blockingSendMessage}. * {@link ExoPlayer#blockingSendMessages}.
*/ */
interface ExoPlayerComponent { interface ExoPlayerComponent {
@ -148,6 +148,28 @@ public interface ExoPlayer {
} }
/**
* Defines a message and a target {@link ExoPlayerComponent} to receive it.
*/
final class ExoPlayerMessage {
public final ExoPlayerComponent target;
public final int messageType;
public final Object message;
/**
* @param target The target of the message.
* @param messageType An integer identifying the type of message.
* @param message The message object.
*/
public ExoPlayerMessage(ExoPlayerComponent target, int messageType, Object message) {
this.target = target;
this.messageType = messageType;
this.message = message;
}
}
/** /**
* The player does not have a source to load, so it is neither buffering nor ready to play. * The player does not have a source to load, so it is neither buffering nor ready to play.
*/ */
@ -256,25 +278,21 @@ public interface ExoPlayer {
void release(); void release();
/** /**
* Sends a message to a specified component. The message is delivered to the component on the * Sends messages to their target components. The messages are delivered on the playback thread.
* playback thread. If the component throws a {@link ExoPlaybackException}, then it is * If a component throws an {@link ExoPlaybackException} then it is propagated out of the player
* propagated out of the player as an error. * as an error.
* *
* @param target The target to which the message should be delivered. * @param messages The messages to be sent.
* @param messageType An integer that can be used to identify the type of the message.
* @param message The message object.
*/ */
void sendMessage(ExoPlayerComponent target, int messageType, Object message); void sendMessages(ExoPlayerMessage... messages);
/** /**
* Blocking variant of {@link #sendMessage(ExoPlayerComponent, int, Object)} that does not return * Variant of {@link #sendMessages(ExoPlayerMessage...)} that blocks until after the messages have
* until after the message has been delivered. * been delivered.
* *
* @param target The target to which the message should be delivered. * @param messages The messages to be sent.
* @param messageType An integer that can be used to identify the type of the message.
* @param message The message object.
*/ */
void blockingSendMessage(ExoPlayerComponent target, int messageType, Object message); void blockingSendMessages(ExoPlayerMessage... messages);
/** /**
* Gets the duration of the track in milliseconds. * Gets the duration of the track in milliseconds.

View File

@ -129,13 +129,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
@Override @Override
public void sendMessage(ExoPlayerComponent target, int messageType, Object message) { public void sendMessages(ExoPlayerMessage... messages) {
internalPlayer.sendMessage(target, messageType, message); internalPlayer.sendMessages(messages);
} }
@Override @Override
public void blockingSendMessage(ExoPlayerComponent target, int messageType, Object message) { public void blockingSendMessages(ExoPlayerMessage... messages) {
internalPlayer.blockingSendMessage(target, messageType, message); internalPlayer.blockingSendMessages(messages);
} }
@Override @Override

View File

@ -15,7 +15,7 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.ExoPlayer.ExoPlayerComponent; import com.google.android.exoplayer.ExoPlayer.ExoPlayerMessage;
import com.google.android.exoplayer.TrackSelector.InvalidationListener; import com.google.android.exoplayer.TrackSelector.InvalidationListener;
import com.google.android.exoplayer.util.PriorityHandlerThread; import com.google.android.exoplayer.util.PriorityHandlerThread;
import com.google.android.exoplayer.util.TraceUtil; import com.google.android.exoplayer.util.TraceUtil;
@ -154,19 +154,22 @@ import java.util.concurrent.atomic.AtomicInteger;
handler.sendEmptyMessage(MSG_STOP); handler.sendEmptyMessage(MSG_STOP);
} }
public void sendMessage(ExoPlayerComponent target, int messageType, Object message) { public void sendMessages(ExoPlayerMessage... messages) {
if (released) {
Log.w(TAG, "Ignoring messages sent after release.");
return;
}
customMessagesSent++; customMessagesSent++;
handler.obtainMessage(MSG_CUSTOM, messageType, 0, Pair.create(target, message)).sendToTarget(); handler.obtainMessage(MSG_CUSTOM, messages).sendToTarget();
} }
public synchronized void blockingSendMessage(ExoPlayerComponent target, int messageType, public synchronized void blockingSendMessages(ExoPlayerMessage... messages) {
Object message) {
if (released) { if (released) {
Log.w(TAG, "Sent message(" + messageType + ") after release. Message ignored."); Log.w(TAG, "Ignoring messages sent after release.");
return; return;
} }
int messageNumber = customMessagesSent++; int messageNumber = customMessagesSent++;
handler.obtainMessage(MSG_CUSTOM, messageType, 0, Pair.create(target, message)).sendToTarget(); handler.obtainMessage(MSG_CUSTOM, messages).sendToTarget();
while (customMessagesProcessed <= messageNumber) { while (customMessagesProcessed <= messageNumber) {
try { try {
wait(); wait();
@ -225,7 +228,7 @@ import java.util.concurrent.atomic.AtomicInteger;
return true; return true;
} }
case MSG_CUSTOM: { case MSG_CUSTOM: {
sendMessageInternal(msg.arg1, msg.obj); sendMessagesInternal((ExoPlayerMessage[]) msg.obj);
return true; return true;
} }
case MSG_TRACK_SELECTION_INVALIDATED: { case MSG_TRACK_SELECTION_INVALIDATED: {
@ -517,12 +520,11 @@ import java.util.concurrent.atomic.AtomicInteger;
} }
} }
private <T> void sendMessageInternal(int what, Object obj) private void sendMessagesInternal(ExoPlayerMessage[] messages) throws ExoPlaybackException {
throws ExoPlaybackException {
try { try {
@SuppressWarnings("unchecked") for (ExoPlayerMessage message : messages) {
Pair<ExoPlayerComponent, Object> targetAndMessage = (Pair<ExoPlayerComponent, Object>) obj; message.target.handleMessage(message.messageType, message.message);
targetAndMessage.first.handleMessage(what, targetAndMessage.second); }
if (preparedSource) { if (preparedSource) {
// The message may have caused something to change that now requires us to do work. // The message may have caused something to change that now requires us to do work.
handler.sendEmptyMessage(MSG_DO_SOME_WORK); handler.sendEmptyMessage(MSG_DO_SOME_WORK);

View File

@ -89,6 +89,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
private final TrackRenderer[] renderers; private final TrackRenderer[] renderers;
private final ComponentListener componentListener; private final ComponentListener componentListener;
private final Handler mainHandler; private final Handler mainHandler;
private final int videoRendererCount;
private final int audioRendererCount;
private Format videoFormat; private Format videoFormat;
private Format audioFormat; private Format audioFormat;
@ -115,6 +117,22 @@ public final class SimpleExoPlayer implements ExoPlayer {
buildRenderers(context, drmSessionManager, renderersList); buildRenderers(context, drmSessionManager, renderersList);
renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]); renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]);
// Obtain counts of video and audio renderers.
int videoRendererCount = 0;
int audioRendererCount = 0;
for (TrackRenderer renderer : renderers) {
switch (renderer.getTrackType()) {
case C.TRACK_TYPE_VIDEO:
videoRendererCount++;
break;
case C.TRACK_TYPE_AUDIO:
audioRendererCount++;
break;
}
}
this.videoRendererCount = videoRendererCount;
this.audioRendererCount = audioRendererCount;
// Build the player and associated objects. // Build the player and associated objects.
player = new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs); player = new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs);
} }
@ -145,16 +163,19 @@ public final class SimpleExoPlayer implements ExoPlayer {
* @param surface The {@link Surface}. * @param surface The {@link Surface}.
*/ */
public void setSurface(Surface surface) { public void setSurface(Surface surface) {
ExoPlayerMessage[] messages = new ExoPlayerMessage[videoRendererCount];
int count = 0;
for (TrackRenderer renderer : renderers) { for (TrackRenderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) {
if (surface == null) { messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_SURFACE, surface);
// Block to ensure that the surface is not accessed after the method returns.
player.blockingSendMessage(renderer, C.MSG_SET_SURFACE, null);
} else {
player.sendMessage(renderer, C.MSG_SET_SURFACE, surface);
}
} }
} }
if (surface == null) {
// Block to ensure that the surface is not accessed after the method returns.
player.blockingSendMessages(messages);
} else {
player.sendMessages(messages);
}
} }
/** /**
@ -163,11 +184,14 @@ public final class SimpleExoPlayer implements ExoPlayer {
* @param volume The volume. * @param volume The volume.
*/ */
public void setVolume(float volume) { public void setVolume(float volume) {
ExoPlayerMessage[] messages = new ExoPlayerMessage[audioRendererCount];
int count = 0;
for (TrackRenderer renderer : renderers) { for (TrackRenderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
player.sendMessage(renderer, C.MSG_SET_VOLUME, volume); messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_VOLUME, volume);
} }
} }
player.sendMessages(messages);
} }
/** /**
@ -296,13 +320,13 @@ public final class SimpleExoPlayer implements ExoPlayer {
} }
@Override @Override
public void sendMessage(ExoPlayerComponent target, int messageType, Object message) { public void sendMessages(ExoPlayerMessage... messages) {
player.sendMessage(target, messageType, message); player.sendMessages(messages);
} }
@Override @Override
public void blockingSendMessage(ExoPlayerComponent target, int messageType, Object message) { public void blockingSendMessages(ExoPlayerMessage... messages) {
player.blockingSendMessage(target, messageType, message); player.blockingSendMessages(messages);
} }
@Override @Override