/*
* Copyright (C) 2014 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.exoplayer;
import android.os.Looper;
/**
* An extensible media player exposing traditional high-level media player functionality, such as
* the ability to prepare, play, pause and seek.
*
* <p>Topics covered here are:
* <ol>
* <li><a href="#Assumptions">Assumptions and player composition</a>
* <li><a href="#Threading">Threading model</a>
* <li><a href="#State">Player state</a>
* </ol>
*
* <a name="Assumptions"></a>
* <h3>Assumptions and player construction</h3>
*
* <p>The implementation is designed make no assumptions about (and hence impose no restrictions
* on) the type of the media being played, how and where it is stored, or how it is rendered.
* Rather than implementing the loading and rendering of media directly, {@link ExoPlayer} instead
* delegates this work to one or more {@link TrackRenderer}s, which are injected when the player
* is prepared. Hence {@link ExoPlayer} is capable of loading and playing any media for which a
* {@link TrackRenderer} implementation can be provided.
*
* <p>{@link MediaCodecAudioTrackRenderer} and {@link MediaCodecVideoTrackRenderer} can be used for
* the common cases of rendering audio and video. These components in turn require an
* <i>upstream</i> {@link SampleSource} to be injected through their constructors, where upstream
* is defined to denote a component that is closer to the source of the media. This pattern of
* upstream dependency injection is actively encouraged, since it means that the functionality of
* the player is built up through the composition of components that can easily be exchanged for
* alternate implementations. For example a {@link SampleSource} implementation may require a
* further upstream data loading component to be injected through its constructor, with different
* implementations enabling the loading of data from various sources.
*
* <a name="Threading"></a>
* <h3>Threading model</h3>
*
* <p>The figure below shows the {@link ExoPlayer} threading model.</p>
* <p align="center"><img src="../../../../../doc_src/images/exoplayer_threading_model.png"
* alt="MediaPlayer state diagram"
* border="0"/></p>
*
* <ul>
* <li>It is recommended that instances are created and accessed from a single application thread.
* An application's main thread is ideal. Accessing an instance from multiple threads is
* discouraged, however if an application does wish to do this then it may do so provided that it
* ensures accesses are synchronized.
* </li>
* <li>Registered {@link Listener}s are invoked on the thread that created the {@link ExoPlayer}
* instance.</li>
* <li>An internal playback thread is responsible for managing playback and invoking the
* {@link TrackRenderer}s in order to load and play the media.</li>
* <li>{@link TrackRenderer} implementations (or any upstream components that they depend on) may
* use additional background threads (e.g. to load data). These are implementation specific.</li>
* </ul>
*
* <a name="State"></a>
* <h3>Player state</h3>
*
* <p>The components of an {@link ExoPlayer}'s state can be divided into two distinct groups. State
* accessed by {@link #getRendererEnabled(int)} and {@link #getPlayWhenReady()} are only ever
* changed by invoking the player's methods, and are never changed as a result of operations that
* have been performed asynchronously by the playback thread. In contrast, the playback state
* accessed by {@link #getPlaybackState()} is only ever changed as a result of operations
* completing on the playback thread, as illustrated below.</p>
* <p align="center"><img src="../../../../../doc_src/images/exoplayer_state.png"
* alt="ExoPlayer state"
* border="0"/></p>
*
* <p>The possible playback state transitions are shown below. Transitions can be triggered either
* by changes in the state of the {@link TrackRenderer}s being used, or as a result of
* {@link #prepare(TrackRenderer[])}, {@link #stop()} or {@link #release()} being invoked.</p>
* <p align="center"><img src="../../../../../doc_src/images/exoplayer_playbackstate.png"
* alt="ExoPlayer playback state transitions"
* border="0"/></p>
*/
public interface ExoPlayer {
/**
* A factory for instantiating ExoPlayer instances.
*/
public static final class Factory {
/**
* The default minimum duration of data that must be buffered for playback to start or resume
* following a user action such as a seek.
*/
public static final int DEFAULT_MIN_BUFFER_MS = 500;
/**
* The default minimum duration of data that must be buffered for playback to resume
* after a player invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
* not due to a user action such as starting playback or seeking).
*/
public static final int DEFAULT_MIN_REBUFFER_MS = 5000;
private Factory() {}
/**
* Obtains an {@link ExoPlayer} instance.
* <p>
* Must be invoked from a thread that has an associated {@link Looper}.
*
* @param rendererCount The number of {@link TrackRenderer}s that will be passed to
* {@link #prepare(TrackRenderer[])}.
* @param minBufferMs A minimum duration of data that must be buffered for playback to start
* or resume following a user action such as a seek.
* @param minRebufferMs A minimum duration of data that must be buffered for playback to resume
* after a player invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
* not due to a user action such as starting playback or seeking).
*/
public static ExoPlayer newInstance(int rendererCount, int minBufferMs, int minRebufferMs) {
return new ExoPlayerImpl(rendererCount, minBufferMs, minRebufferMs);
}
/**
* Obtains an {@link ExoPlayer} instance.
* <p>
* Must be invoked from a thread that has an associated {@link Looper}.
*
* @param rendererCount The number of {@link TrackRenderer}s that will be passed to
* {@link #prepare(TrackRenderer[])}.
*/
public static ExoPlayer newInstance(int rendererCount) {
return new ExoPlayerImpl(rendererCount, DEFAULT_MIN_BUFFER_MS, DEFAULT_MIN_REBUFFER_MS);
}
/**
* @deprecated Please use {@link #newInstance(int, int, int)}.
*/
@Deprecated
public static ExoPlayer newInstance(int rendererCount, int minRebufferMs) {
return new ExoPlayerImpl(rendererCount, DEFAULT_MIN_BUFFER_MS, minRebufferMs);
}
}
/**
* Interface definition for a callback to be notified of changes in player state.
*/
public interface Listener {
/**
* Invoked when the value returned from either {@link ExoPlayer#getPlayWhenReady()} or
* {@link ExoPlayer#getPlaybackState()} changes.
*
* @param playWhenReady Whether playback will proceed when ready.
* @param playbackState One of the {@code STATE} constants defined in this class.
*/
void onPlayerStateChanged(boolean playWhenReady, int playbackState);
/**
* Invoked when the current value of {@link ExoPlayer#getPlayWhenReady()} has been reflected
* by the internal playback thread.
* <p>
* An invocation of this method will shortly follow any call to
* {@link ExoPlayer#setPlayWhenReady(boolean)} that changes the state. If multiple calls are
* made in rapid succession, then this method will be invoked only once, after the final state
* has been reflected.
*/
void onPlayWhenReadyCommitted();
/**
* Invoked when an error occurs. The playback state will transition to
* {@link ExoPlayer#STATE_IDLE} immediately after this method is invoked. The player instance
* can still be used, and {@link ExoPlayer#release()} must still be called on the player should
* it no longer be required.
*
* @param error The error.
*/
void onPlayerError(ExoPlaybackException error);
}
/**
* A component of an {@link ExoPlayer} that can receive messages on the playback thread.
* <p>
* Messages can be delivered to a component via {@link ExoPlayer#sendMessage} and
* {@link ExoPlayer#blockingSendMessage}.
*/
public interface ExoPlayerComponent {
/**
* Handles a message delivered to the component. Invoked on the playback thread.
*
* @param messageType An integer identifying the type of message.
* @param message The message object.
* @throws ExoPlaybackException If an error occurred whilst handling the message.
*/
void handleMessage(int messageType, Object message) throws ExoPlaybackException;
}
/**
* The player is neither prepared or being prepared.
*/
public static final int STATE_IDLE = 1;
/**
* The player is being prepared.
*/
public static final int STATE_PREPARING = 2;
/**
* The player is prepared but not able to immediately play from the current position. The cause
* is {@link TrackRenderer} specific, but this state typically occurs when more data needs
* to be buffered for playback to start.
*/
public static final int STATE_BUFFERING = 3;
/**
* The player is prepared and able to immediately play from the current position. The player will
* be playing if {@link #setPlayWhenReady(boolean)} returns true, and paused otherwise.
*/
public static final int STATE_READY = 4;
/**
* The player has finished playing the media.
*/
public static final int STATE_ENDED = 5;
/**
* Represents an unknown time or duration.
*/
public static final int UNKNOWN_TIME = -1;
/**
* Gets the {@link Looper} associated with the playback thread.
*
* @return The {@link Looper} associated with the playback thread.
*/
public Looper getPlaybackLooper();
/**
* Register a listener to receive events from the player. The listener's methods will be invoked
* on the thread that was used to construct the player.
*
* @param listener The listener to register.
*/
public void addListener(Listener listener);
/**
* Unregister a listener. The listener will no longer receive events from the player.
*
* @param listener The listener to unregister.
*/
public void removeListener(Listener listener);
/**
* Returns the current state of the player.
*
* @return One of the {@code STATE} constants defined in this class.
*/
public int getPlaybackState();
/**
* Prepares the player for playback.
*
* @param renderers The {@link TrackRenderer}s to use. The number of renderers must match the
* value that was passed to the {@link ExoPlayer.Factory#newInstance} method.
*/
public void prepare(TrackRenderer... renderers);
/**
* Sets whether the renderer at the given index is enabled.
*
* @param index The index of the renderer.
* @param enabled Whether the renderer at the given index should be enabled.
*/
public void setRendererEnabled(int index, boolean enabled);
/**
* Whether the renderer at the given index is enabled.
*
* @param index The index of the renderer.
* @return Whether the renderer is enabled.
*/
public boolean getRendererEnabled(int index);
/**
* Sets whether playback should proceed when {@link #getPlaybackState()} == {@link #STATE_READY}.
* If the player is already in this state, then this method can be used to pause and resume
* playback.
*
* @param playWhenReady Whether playback should proceed when ready.
*/
public void setPlayWhenReady(boolean playWhenReady);
/**
* Whether playback will proceed when {@link #getPlaybackState()} == {@link #STATE_READY}.
*
* @return Whether playback will proceed when ready.
*/
public boolean getPlayWhenReady();
/**
* Whether the current value of {@link ExoPlayer#getPlayWhenReady()} has been reflected by the
* internal playback thread.
*
* @return True if the current value has been reflected. False otherwise.
*/
public boolean isPlayWhenReadyCommitted();
/**
* Seeks to a position specified in milliseconds.
*
* @param positionMs The seek position.
*/
public void seekTo(int positionMs);
/**
* Stops playback. Use {@code setPlayWhenReady(false)} rather than this method if the intention
* is to pause playback.
* <p>
* Calling this method will cause the playback state to transition to
* {@link ExoPlayer#STATE_IDLE}. The player instance can still be used, and
* {@link ExoPlayer#release()} must still be called on the player if it's no longer required.
* <p>
* Calling this method does not reset the playback position. If this player instance will be used
* to play another video from its start, then {@code seekTo(0)} should be called after stopping
* the player and before preparing it for the next video.
*/
public void stop();
/**
* Releases the player. This method must be called when the player is no longer required.
* <p>
* The player must not be used after calling this method.
*/
public void release();
/**
* Sends a message to a specified component. The message is delivered to the component on the
* playback thread. If the component throws a {@link ExoPlaybackException}, then it is
* propagated out of the player as an error.
*
* @param target The target to which the message should be delivered.
* @param messageType An integer that can be used to identify the type of the message.
* @param message The message object.
*/
public void sendMessage(ExoPlayerComponent target, int messageType, Object message);
/**
* Blocking variant of {@link #sendMessage(ExoPlayerComponent, int, Object)} that does not return
* until after the message has been delivered.
*
* @param target The target to which the message should be delivered.
* @param messageType An integer that can be used to identify the type of the message.
* @param message The message object.
*/
public void blockingSendMessage(ExoPlayerComponent target, int messageType, Object message);
/**
* Gets the duration of the track in milliseconds.
*
* @return The duration of the track in milliseconds, or {@link ExoPlayer#UNKNOWN_TIME} if the
* duration is not known.
*/
public int getDuration();
/**
* Gets the current playback position in milliseconds.
*
* @return The current playback position in milliseconds.
*/
public int getCurrentPosition();
/**
* Gets an estimate of the absolute position in milliseconds up to which data is buffered.
*
* @return An estimate of the absolute position in milliseconds up to which data is buffered,
* or {@link ExoPlayer#UNKNOWN_TIME} if no estimate is available.
*/
public int getBufferedPosition();
/**
* Gets an estimate of the percentage into the media up to which data is buffered.
*
* @return An estimate of the percentage into the media up to which data is buffered. 0 if the
* duration of the media is not known or if no estimate is available.
*/
public int getBufferedPercentage();
/**
* Sets playback speed for the video and audio.
* @param speed - float This value should be in the range 0.5 to 2.0
*/
public void setPlaybackSpeed(float speed);
/**
* Returns current playback speed.
* @return
*/
public float getPlaybackSpeed();
}