Promote DemoPlayer to library as SimpleExoPlayer.
DemoPlayer moves into core library as SimpleExoPlayer, which implements ExoPlayer. Issue: #383 Issue: #592 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=123090184
This commit is contained in:
parent
075e095cba
commit
ced7de15a9
@ -16,16 +16,17 @@
|
||||
package com.google.android.exoplayer.demo;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer.TrackGroup;
|
||||
import com.google.android.exoplayer.TrackGroupArray;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.TrackSelection;
|
||||
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
|
||||
import com.google.android.exoplayer.demo.player.DemoPlayer;
|
||||
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
|
||||
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
|
||||
|
||||
@ -39,9 +40,9 @@ import java.util.Locale;
|
||||
/**
|
||||
* Logs player events using {@link Log}.
|
||||
*/
|
||||
public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener,
|
||||
public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.DebugListener,
|
||||
ChunkTrackStreamEventListener, ExtractorSampleSource.EventListener,
|
||||
StreamingDrmSessionManager.EventListener {
|
||||
StreamingDrmSessionManager.EventListener, DefaultTrackSelector.EventListener {
|
||||
|
||||
private static final String TAG = "EventLogger";
|
||||
private static final NumberFormat TIME_FORMAT;
|
||||
@ -62,26 +63,26 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
|
||||
Log.d(TAG, "end [" + getSessionTimeString() + "]");
|
||||
}
|
||||
|
||||
// DemoPlayer.Listener
|
||||
// ExoPlayer.EventListener
|
||||
|
||||
@Override
|
||||
public void onStateChanged(boolean playWhenReady, int state) {
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int state) {
|
||||
Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", "
|
||||
+ getStateString(state) + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ExoPlaybackException e) {
|
||||
Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e);
|
||||
public void onPlayWhenReadyCommitted() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||
float pixelWidthHeightRatio) {
|
||||
Log.d(TAG, "videoSizeChanged [" + width + ", " + height + ", " + unappliedRotationDegrees
|
||||
+ ", " + pixelWidthHeightRatio + "]");
|
||||
public void onPlayerError(ExoPlaybackException e) {
|
||||
Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e);
|
||||
}
|
||||
|
||||
// DefaultTrackSelector.EventListener
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackInfo trackInfo) {
|
||||
Log.d(TAG, "Tracks [");
|
||||
@ -130,7 +131,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
|
||||
Log.d(TAG, "]");
|
||||
}
|
||||
|
||||
// DemoPlayer.InfoListener
|
||||
// SimpleExoPlayer.DebugListener
|
||||
|
||||
@Override
|
||||
public void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||
|
@ -17,15 +17,18 @@ package com.google.android.exoplayer.demo;
|
||||
|
||||
import com.google.android.exoplayer.AspectRatioFrameLayout;
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
|
||||
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||
import com.google.android.exoplayer.SampleSource;
|
||||
import com.google.android.exoplayer.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer.TrackGroupArray;
|
||||
import com.google.android.exoplayer.dash.DashSampleSource;
|
||||
import com.google.android.exoplayer.demo.player.DemoPlayer;
|
||||
import com.google.android.exoplayer.demo.ui.TrackSelectionHelper;
|
||||
import com.google.android.exoplayer.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
|
||||
@ -46,6 +49,7 @@ import com.google.android.exoplayer.upstream.DataSourceFactory;
|
||||
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||
import com.google.android.exoplayer.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer.util.DebugTextViewHelper;
|
||||
import com.google.android.exoplayer.util.PlayerControl;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.Manifest.permission;
|
||||
@ -61,6 +65,7 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
@ -81,10 +86,11 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* An activity that plays media using {@link DemoPlayer}.
|
||||
* An activity that plays media using {@link SimpleExoPlayer}.
|
||||
*/
|
||||
public class PlayerActivity extends Activity implements SurfaceHolder.Callback, OnClickListener,
|
||||
DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener {
|
||||
ExoPlayer.EventListener, SimpleExoPlayer.VideoListener, SimpleExoPlayer.CaptionListener,
|
||||
SimpleExoPlayer.Id3MetadataListener, DefaultTrackSelector.EventListener {
|
||||
|
||||
// For use within demo app code.
|
||||
public static final String CONTENT_TYPE_EXTRA = "content_type";
|
||||
@ -114,12 +120,12 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
private AspectRatioFrameLayout videoFrame;
|
||||
private SurfaceView surfaceView;
|
||||
private TextView debugTextView;
|
||||
private TextView playerStateTextView;
|
||||
private SubtitleLayout subtitleLayout;
|
||||
private Button retryButton;
|
||||
|
||||
private DataSourceFactory dataSourceFactory;
|
||||
private DemoPlayer player;
|
||||
private SimpleExoPlayer player;
|
||||
private DefaultTrackSelector trackSelector;
|
||||
private TrackSelectionHelper trackSelectionHelper;
|
||||
private DebugTextViewHelper debugViewHelper;
|
||||
private boolean playerNeedsSource;
|
||||
@ -163,10 +169,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
surfaceView = (SurfaceView) findViewById(R.id.surface_view);
|
||||
surfaceView.getHolder().addCallback(this);
|
||||
debugTextView = (TextView) findViewById(R.id.debug_text_view);
|
||||
|
||||
playerStateTextView = (TextView) findViewById(R.id.player_state_view);
|
||||
subtitleLayout = (SubtitleLayout) findViewById(R.id.subtitles);
|
||||
|
||||
mediaController = new KeyCompatibleMediaController(this);
|
||||
retryButton = (Button) findViewById(R.id.retry_button);
|
||||
retryButton.setOnClickListener(this);
|
||||
@ -226,7 +229,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
initializePlayer();
|
||||
} else if (view.getParent() == debugRootView) {
|
||||
trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(),
|
||||
player.getTrackInfo(), (int) view.getTag());
|
||||
trackSelector.getTrackInfo(), (int) view.getTag());
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,6 +282,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
int type = intent.getIntExtra(CONTENT_TYPE_EXTRA,
|
||||
inferContentType(uri, intent.getStringExtra(CONTENT_EXT_EXTRA)));
|
||||
if (player == null) {
|
||||
boolean useExtensionDecoders = intent.getBooleanExtra(USE_EXTENSION_DECODERS, false);
|
||||
UUID drmSchemeUuid = (UUID) intent.getSerializableExtra(DRM_SCHEME_UUID_EXTRA);
|
||||
DrmSessionManager drmSessionManager = null;
|
||||
if (drmSchemeUuid != null) {
|
||||
@ -291,20 +295,24 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
return;
|
||||
}
|
||||
}
|
||||
boolean useExtensionDecoders = intent.getBooleanExtra(USE_EXTENSION_DECODERS, false);
|
||||
eventLogger = new EventLogger();
|
||||
eventLogger.startSession();
|
||||
player = new DemoPlayer(this, drmSessionManager, useExtensionDecoders);
|
||||
trackSelector = new DefaultTrackSelector(new DefaultTrackSelectionPolicy(), mainHandler);
|
||||
trackSelector.addListener(this);
|
||||
trackSelector.addListener(eventLogger);
|
||||
trackSelectionHelper = new TrackSelectionHelper(trackSelector);
|
||||
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, drmSessionManager,
|
||||
useExtensionDecoders);
|
||||
player.addListener(this);
|
||||
player.addListener(eventLogger);
|
||||
player.setInfoListener(eventLogger);
|
||||
player.setDebugListener(eventLogger);
|
||||
player.setVideoListener(this);
|
||||
player.setCaptionListener(this);
|
||||
player.setMetadataListener(this);
|
||||
player.seekTo(playerPosition);
|
||||
player.setSurface(surfaceView.getHolder().getSurface());
|
||||
player.setPlayWhenReady(true);
|
||||
trackSelectionHelper = new TrackSelectionHelper(player.getTrackSelector());
|
||||
mediaController.setMediaPlayer(player.getPlayerControl());
|
||||
mediaController.setMediaPlayer(new PlayerControl(player));
|
||||
mediaController.setAnchorView(rootView);
|
||||
debugViewHelper = new DebugTextViewHelper(player, debugTextView);
|
||||
debugViewHelper.start();
|
||||
@ -373,47 +381,30 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
playerPosition = player.getCurrentPosition();
|
||||
player.release();
|
||||
player = null;
|
||||
trackSelector = null;
|
||||
trackSelectionHelper = null;
|
||||
eventLogger.endSession();
|
||||
eventLogger = null;
|
||||
}
|
||||
}
|
||||
|
||||
// DemoPlayer.Listener implementation
|
||||
// ExoPlayer.EventListener implementation
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackInfo trackSet) {
|
||||
updateButtonVisibilities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(boolean playWhenReady, int playbackState) {
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
if (playbackState == ExoPlayer.STATE_ENDED) {
|
||||
showControls();
|
||||
}
|
||||
String text = "playWhenReady=" + playWhenReady + ", playbackState=";
|
||||
switch(playbackState) {
|
||||
case ExoPlayer.STATE_BUFFERING:
|
||||
text += "buffering";
|
||||
break;
|
||||
case ExoPlayer.STATE_ENDED:
|
||||
text += "ended";
|
||||
break;
|
||||
case ExoPlayer.STATE_IDLE:
|
||||
text += "idle";
|
||||
break;
|
||||
case ExoPlayer.STATE_READY:
|
||||
text += "ready";
|
||||
break;
|
||||
default:
|
||||
text += "unknown";
|
||||
break;
|
||||
}
|
||||
playerStateTextView.setText(text);
|
||||
updateButtonVisibilities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ExoPlaybackException e) {
|
||||
public void onPlayWhenReadyCommitted() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException e) {
|
||||
String errorString = null;
|
||||
if (e.type == ExoPlaybackException.TYPE_RENDERER) {
|
||||
Exception cause = e.getRendererException();
|
||||
@ -445,14 +436,27 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
showControls();
|
||||
}
|
||||
|
||||
// SimpleExoPlayer.VideoListener implementation
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||
float pixelWidthAspectRatio) {
|
||||
shutterView.setVisibility(View.GONE);
|
||||
videoFrame.setAspectRatio(
|
||||
height == 0 ? 1 : (width * pixelWidthAspectRatio) / height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawnToSurface(Surface surface) {
|
||||
shutterView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// DefaultTrackSelector.EventListener implementation
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackInfo trackSet) {
|
||||
updateButtonVisibilities();
|
||||
}
|
||||
|
||||
// User controls
|
||||
|
||||
private void updateButtonVisibilities() {
|
||||
@ -461,8 +465,12 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
retryButton.setVisibility(playerNeedsSource ? View.VISIBLE : View.GONE);
|
||||
debugRootView.addView(retryButton);
|
||||
|
||||
TrackInfo trackInfo;
|
||||
if (player == null || (trackInfo = player.getTrackInfo()) == null) {
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TrackInfo trackInfo = trackSelector.getTrackInfo();
|
||||
if (trackInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -500,14 +508,14 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
debugRootView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// DemoPlayer.CaptionListener implementation
|
||||
// SimpleExoPlayer.CaptionListener implementation
|
||||
|
||||
@Override
|
||||
public void onCues(List<Cue> cues) {
|
||||
subtitleLayout.setCues(cues);
|
||||
}
|
||||
|
||||
// DemoPlayer.MetadataListener implementation
|
||||
// SimpleExoPlayer.MetadataListener implementation
|
||||
|
||||
@Override
|
||||
public void onId3Metadata(List<Id3Frame> id3Frames) {
|
||||
|
@ -48,14 +48,6 @@
|
||||
android:background="#88000000"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView android:id="@+id/player_state_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp"/>
|
||||
|
||||
<TextView android:id="@+id/debug_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
|
||||
@ -57,7 +58,7 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestPlaybackThread extends Thread implements ExoPlayer.Listener {
|
||||
private static class TestPlaybackThread extends Thread implements ExoPlayer.EventListener {
|
||||
|
||||
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
|
||||
private static final int BUFFER_SEGMENT_COUNT = 16;
|
||||
@ -77,9 +78,9 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
LibflacAudioTrackRenderer audioRenderer = new LibflacAudioTrackRenderer();
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null,
|
||||
new DefaultTrackSelectionPolicy());
|
||||
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector);
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(
|
||||
new DefaultTrackSelectionPolicy(), null);
|
||||
player = ExoPlayerFactory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector);
|
||||
player.addListener(this);
|
||||
ExtractorSampleSource sampleSource = new ExtractorSampleSource(
|
||||
uri,
|
||||
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
|
||||
@ -57,7 +58,7 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestPlaybackThread extends Thread implements ExoPlayer.Listener {
|
||||
private static class TestPlaybackThread extends Thread implements ExoPlayer.EventListener {
|
||||
|
||||
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
|
||||
private static final int BUFFER_SEGMENT_COUNT = 16;
|
||||
@ -77,9 +78,9 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
LibopusAudioTrackRenderer audioRenderer = new LibopusAudioTrackRenderer();
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null,
|
||||
new DefaultTrackSelectionPolicy());
|
||||
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector);
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(
|
||||
new DefaultTrackSelectionPolicy(), null);
|
||||
player = ExoPlayerFactory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector);
|
||||
player.addListener(this);
|
||||
ExtractorSampleSource sampleSource = new ExtractorSampleSource(
|
||||
uri,
|
||||
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
|
||||
@ -73,7 +74,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestPlaybackThread extends Thread implements ExoPlayer.Listener {
|
||||
private static class TestPlaybackThread extends Thread implements ExoPlayer.EventListener {
|
||||
|
||||
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
|
||||
private static final int BUFFER_SEGMENT_COUNT = 16;
|
||||
@ -93,9 +94,9 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(true);
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null,
|
||||
new DefaultTrackSelectionPolicy());
|
||||
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector);
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(
|
||||
new DefaultTrackSelectionPolicy(), null);
|
||||
player = ExoPlayerFactory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector);
|
||||
player.addListener(this);
|
||||
ExtractorSampleSource sampleSource = new ExtractorSampleSource(
|
||||
uri,
|
||||
|
@ -333,7 +333,8 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||
|
||||
@Override
|
||||
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
||||
eventDispatcher.codecCounters(codecCounters);
|
||||
codecCounters.reset();
|
||||
eventDispatcher.enabled(codecCounters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -360,6 +361,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||
}
|
||||
} finally {
|
||||
super.onDisabled();
|
||||
eventDispatcher.disabled();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,11 +27,11 @@ import android.os.SystemClock;
|
||||
public interface AudioTrackRendererEventListener {
|
||||
|
||||
/**
|
||||
* Invoked to pass the codec counters when the renderer is enabled.
|
||||
* Invoked when the renderer is enabled.
|
||||
*
|
||||
* @param counters CodecCounters object used by the renderer.
|
||||
* @param counters {@link CodecCounters} that will be updated by the renderer.
|
||||
*/
|
||||
void onAudioCodecCounters(CodecCounters counters);
|
||||
void onAudioEnabled(CodecCounters counters);
|
||||
|
||||
/**
|
||||
* Invoked when a decoder is created.
|
||||
@ -62,6 +62,11 @@ public interface AudioTrackRendererEventListener {
|
||||
*/
|
||||
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
|
||||
|
||||
/**
|
||||
* Invoked when the renderer is disabled.
|
||||
*/
|
||||
void onAudioDisabled();
|
||||
|
||||
/**
|
||||
* Dispatches events to a {@link AudioTrackRendererEventListener}.
|
||||
*/
|
||||
@ -75,12 +80,12 @@ public interface AudioTrackRendererEventListener {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void codecCounters(final CodecCounters codecCounters) {
|
||||
public void enabled(final CodecCounters codecCounters) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onAudioCodecCounters(codecCounters);
|
||||
listener.onAudioEnabled(codecCounters);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -122,6 +127,17 @@ public interface AudioTrackRendererEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void disabled() {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onAudioDisabled();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -70,15 +70,17 @@ public final class CodecCounters {
|
||||
// call this method.
|
||||
}
|
||||
|
||||
public String getDebugString() {
|
||||
ensureUpdated();
|
||||
return "ic:" + codecInitCount
|
||||
+ " rc:" + codecReleaseCount
|
||||
+ " ib:" + inputBufferCount
|
||||
+ " rb:" + renderedOutputBufferCount
|
||||
+ " sb:" + skippedOutputBufferCount
|
||||
+ " db:" + droppedOutputBufferCount
|
||||
+ " mcdb:" + maxConsecutiveDroppedOutputBufferCount;
|
||||
/**
|
||||
* Resets all counters to zero.
|
||||
*/
|
||||
public void reset() {
|
||||
codecInitCount = 0;
|
||||
codecReleaseCount = 0;
|
||||
inputBufferCount = 0;
|
||||
renderedOutputBufferCount = 0;
|
||||
skippedOutputBufferCount = 0;
|
||||
droppedOutputBufferCount = 0;
|
||||
maxConsecutiveDroppedOutputBufferCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,11 +26,12 @@ import android.util.SparseBooleanArray;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* A {@link TrackSelector} suitable for a wide range of use cases.
|
||||
*/
|
||||
public class DefaultTrackSelector extends TrackSelector implements
|
||||
public final class DefaultTrackSelector extends TrackSelector implements
|
||||
TrackSelectionPolicy.InvalidationListener{
|
||||
|
||||
/**
|
||||
@ -48,7 +49,7 @@ public class DefaultTrackSelector extends TrackSelector implements
|
||||
}
|
||||
|
||||
private final Handler eventHandler;
|
||||
private final EventListener eventListener;
|
||||
private final CopyOnWriteArraySet<EventListener> listeners;
|
||||
private final SparseArray<Map<TrackGroupArray, TrackSelection>> trackSelectionOverrides;
|
||||
private final SparseBooleanArray rendererDisabledFlags;
|
||||
private final TrackSelectionPolicy trackSelectionPolicy;
|
||||
@ -56,19 +57,37 @@ public class DefaultTrackSelector extends TrackSelector implements
|
||||
private TrackInfo activeTrackInfo;
|
||||
|
||||
/**
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
* @param trackSelectionPolicy Defines the policy for track selection.
|
||||
* @param eventHandler A handler to use when delivering events to listeners added via
|
||||
* {@link #addListener(EventListener)}.
|
||||
*/
|
||||
public DefaultTrackSelector(Handler eventHandler, EventListener eventListener,
|
||||
TrackSelectionPolicy trackSelectionPolicy) {
|
||||
public DefaultTrackSelector(TrackSelectionPolicy trackSelectionPolicy, Handler eventHandler) {
|
||||
this.trackSelectionPolicy = Assertions.checkNotNull(trackSelectionPolicy);
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
this.listeners = new CopyOnWriteArraySet<>();
|
||||
trackSelectionOverrides = new SparseArray<>();
|
||||
rendererDisabledFlags = new SparseBooleanArray();
|
||||
this.trackSelectionPolicy = Assertions.checkNotNull(trackSelectionPolicy);
|
||||
this.trackSelectionPolicy.init(this);
|
||||
trackSelectionPolicy.init(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener to receive events from the selector. The listener's methods will be invoked
|
||||
* using the {@link Handler} that was passed to the constructor.
|
||||
*
|
||||
* @param listener The listener to register.
|
||||
*/
|
||||
public void addListener(EventListener listener) {
|
||||
Assertions.checkState(eventHandler != null);
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a listener. The listener will no longer receive events from the selector.
|
||||
*
|
||||
* @param listener The listener to unregister.
|
||||
*/
|
||||
public void removeListener(EventListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -391,11 +410,13 @@ public class DefaultTrackSelector extends TrackSelector implements
|
||||
}
|
||||
|
||||
private void notifyTrackInfoChanged(final TrackInfo trackInfo) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
if (eventHandler != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onTracksChanged(trackInfo);
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onTracksChanged(trackInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -15,8 +15,6 @@
|
||||
*/
|
||||
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 buffer media, play, pause and seek.
|
||||
@ -62,8 +60,8 @@ import android.os.Looper;
|
||||
* 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>Registered {@link EventListener}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
|
||||
@ -93,63 +91,11 @@ import android.os.Looper;
|
||||
*/
|
||||
public interface ExoPlayer {
|
||||
|
||||
/**
|
||||
* A factory for instantiating ExoPlayer instances.
|
||||
*/
|
||||
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 = 2500;
|
||||
|
||||
/**
|
||||
* 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 renderers The {@link TrackRenderer}s that will be used by the instance.
|
||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||
* @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(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||
int minBufferMs, int minRebufferMs) {
|
||||
return new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an {@link ExoPlayer} instance.
|
||||
* <p>
|
||||
* Must be invoked from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param renderers The {@link TrackRenderer}s that will be used by the instance.
|
||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||
*/
|
||||
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector) {
|
||||
return new ExoPlayerImpl(renderers, trackSelector, DEFAULT_MIN_BUFFER_MS,
|
||||
DEFAULT_MIN_REBUFFER_MS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of changes in player state.
|
||||
*/
|
||||
interface Listener {
|
||||
interface EventListener {
|
||||
|
||||
/**
|
||||
* Invoked when the value returned from either {@link ExoPlayer#getPlayWhenReady()} or
|
||||
* {@link ExoPlayer#getPlaybackState()} changes.
|
||||
@ -159,6 +105,7 @@ public interface ExoPlayer {
|
||||
* interface.
|
||||
*/
|
||||
void onPlayerStateChanged(boolean playWhenReady, int playbackState);
|
||||
|
||||
/**
|
||||
* Invoked when the current value of {@link ExoPlayer#getPlayWhenReady()} has been reflected
|
||||
* by the internal playback thread.
|
||||
@ -169,6 +116,7 @@ public interface ExoPlayer {
|
||||
* 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
|
||||
@ -178,6 +126,7 @@ public interface ExoPlayer {
|
||||
* @param error The error.
|
||||
*/
|
||||
void onPlayerError(ExoPlaybackException error);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,14 +179,14 @@ public interface ExoPlayer {
|
||||
*
|
||||
* @param listener The listener to register.
|
||||
*/
|
||||
void addListener(Listener listener);
|
||||
void addListener(EventListener listener);
|
||||
|
||||
/**
|
||||
* Unregister a listener. The listener will no longer receive events from the player.
|
||||
*
|
||||
* @param listener The listener to unregister.
|
||||
*/
|
||||
void removeListener(Listener listener);
|
||||
void removeListener(EventListener listener);
|
||||
|
||||
/**
|
||||
* Returns the current state of the player.
|
||||
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 com.google.android.exoplayer.drm.DrmSessionManager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* A factory for instantiating {@link ExoPlayer} instances.
|
||||
*/
|
||||
public final class ExoPlayerFactory {
|
||||
|
||||
/**
|
||||
* 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 = 2500;
|
||||
|
||||
/**
|
||||
* 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 ExoPlayerFactory() {}
|
||||
|
||||
/**
|
||||
* Obtains a {@link SimpleExoPlayer} instance.
|
||||
* <p>
|
||||
* Must be called from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||
*/
|
||||
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector) {
|
||||
return newSimpleInstance(context, trackSelector, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a {@link SimpleExoPlayer} instance.
|
||||
* <p>
|
||||
* Must be called from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param trackSelector The {@link TrackSelector} 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 useExtensionDecoders True to include {@link TrackRenderer} instances defined in
|
||||
* available extensions. Note that the required extensions must be included in the application
|
||||
* build for setting this flag to have any effect.
|
||||
*/
|
||||
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
||||
DrmSessionManager drmSessionManager, boolean useExtensionDecoders) {
|
||||
return newSimpleInstance(context, trackSelector, drmSessionManager, useExtensionDecoders,
|
||||
DEFAULT_MIN_BUFFER_MS, DEFAULT_MIN_REBUFFER_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a {@link SimpleExoPlayer} instance.
|
||||
* <p>
|
||||
* Must be called from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param trackSelector The {@link TrackSelector} 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 useExtensionDecoders True to include {@link TrackRenderer} instances defined in
|
||||
* available extensions. Note that the required extensions must be included in the application
|
||||
* build for setting this flag to have any effect.
|
||||
* @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 SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
||||
DrmSessionManager drmSessionManager, boolean useExtensionDecoders, int minBufferMs,
|
||||
int minRebufferMs) {
|
||||
return new SimpleExoPlayer(context, trackSelector, drmSessionManager, useExtensionDecoders,
|
||||
minBufferMs, minRebufferMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an {@link ExoPlayer} instance.
|
||||
* <p>
|
||||
* Must be called from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param renderers The {@link TrackRenderer}s that will be used by the instance.
|
||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||
* @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(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||
int minBufferMs, int minRebufferMs) {
|
||||
return new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an {@link ExoPlayer} instance.
|
||||
* <p>
|
||||
* Must be called from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param renderers The {@link TrackRenderer}s that will be used by the instance.
|
||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||
*/
|
||||
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector) {
|
||||
return new ExoPlayerImpl(renderers, trackSelector, DEFAULT_MIN_BUFFER_MS,
|
||||
DEFAULT_MIN_REBUFFER_MS);
|
||||
}
|
||||
|
||||
}
|
@ -34,7 +34,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
private final Handler eventHandler;
|
||||
private final ExoPlayerImplInternal internalPlayer;
|
||||
private final CopyOnWriteArraySet<Listener> listeners;
|
||||
private final CopyOnWriteArraySet<EventListener> listeners;
|
||||
|
||||
private boolean playWhenReady;
|
||||
private int playbackState;
|
||||
@ -71,12 +71,12 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
public void addListener(EventListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener listener) {
|
||||
public void removeListener(EventListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
this.playWhenReady = playWhenReady;
|
||||
pendingPlayWhenReadyAcks++;
|
||||
internalPlayer.setPlayWhenReady(playWhenReady);
|
||||
for (Listener listener : listeners) {
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
}
|
||||
}
|
||||
@ -166,7 +166,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
switch (msg.what) {
|
||||
case ExoPlayerImplInternal.MSG_STATE_CHANGED: {
|
||||
playbackState = msg.arg1;
|
||||
for (Listener listener : listeners) {
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
}
|
||||
break;
|
||||
@ -174,7 +174,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
case ExoPlayerImplInternal.MSG_SET_PLAY_WHEN_READY_ACK: {
|
||||
pendingPlayWhenReadyAcks--;
|
||||
if (pendingPlayWhenReadyAcks == 0) {
|
||||
for (Listener listener : listeners) {
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onPlayWhenReadyCommitted();
|
||||
}
|
||||
}
|
||||
@ -182,7 +182,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_ERROR: {
|
||||
ExoPlaybackException exception = (ExoPlaybackException) msg.obj;
|
||||
for (Listener listener : listeners) {
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onPlayerError(exception);
|
||||
}
|
||||
break;
|
||||
|
@ -272,8 +272,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
|
||||
@Override
|
||||
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
||||
codecCounters.reset();
|
||||
eventDispatcher.enabled(codecCounters);
|
||||
super.onEnabled(formats, joining);
|
||||
eventDispatcher.codecCounters(codecCounters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -290,6 +291,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
|
||||
@Override
|
||||
protected void onDisabled() {
|
||||
eventDispatcher.disabled();
|
||||
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||
try {
|
||||
audioTrack.release();
|
||||
|
@ -210,6 +210,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
|
||||
@Override
|
||||
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
||||
codecCounters.reset();
|
||||
eventDispatcher.enabled(codecCounters);
|
||||
super.onEnabled(formats, joining);
|
||||
adaptiveMaxWidth = Format.NO_VALUE;
|
||||
adaptiveMaxHeight = Format.NO_VALUE;
|
||||
@ -228,7 +230,6 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
joiningDeadlineMs = SystemClock.elapsedRealtime() + allowedJoiningTimeMs;
|
||||
}
|
||||
frameReleaseTimeHelper.enable();
|
||||
eventDispatcher.codecCounters(codecCounters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -274,6 +275,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
|
||||
@Override
|
||||
protected void onDisabled() {
|
||||
eventDispatcher.disabled();
|
||||
currentWidth = -1;
|
||||
currentHeight = -1;
|
||||
currentPixelWidthHeightRatio = -1;
|
||||
|
@ -13,23 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.demo.player;
|
||||
package com.google.android.exoplayer;
|
||||
|
||||
import com.google.android.exoplayer.AudioTrackRendererEventListener;
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.CodecCounters;
|
||||
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
||||
import com.google.android.exoplayer.MediaCodecSelector;
|
||||
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
||||
import com.google.android.exoplayer.SampleSource;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.VideoTrackRendererEventListener;
|
||||
import com.google.android.exoplayer.audio.AudioCapabilities;
|
||||
import com.google.android.exoplayer.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
|
||||
@ -41,8 +26,6 @@ import com.google.android.exoplayer.text.TextRenderer;
|
||||
import com.google.android.exoplayer.text.TextTrackRenderer;
|
||||
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
||||
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
|
||||
import com.google.android.exoplayer.util.DebugTextViewHelper;
|
||||
import com.google.android.exoplayer.util.PlayerControl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
@ -54,30 +37,27 @@ import android.view.Surface;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* A wrapper around {@link ExoPlayer} that provides a higher level interface.
|
||||
* An {@link ExoPlayer} that uses default {@link TrackRenderer} components.
|
||||
* <p>
|
||||
* Instances of this class can be obtained from {@link ExoPlayerFactory}.
|
||||
*/
|
||||
public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.EventListener,
|
||||
VideoTrackRendererEventListener, AudioTrackRendererEventListener, TextRenderer,
|
||||
MetadataRenderer<List<Id3Frame>>, DebugTextViewHelper.Provider {
|
||||
public final class SimpleExoPlayer implements ExoPlayer {
|
||||
|
||||
/**
|
||||
* A listener for core events.
|
||||
* A listener for video rendering information.
|
||||
*/
|
||||
public interface Listener {
|
||||
void onStateChanged(boolean playWhenReady, int playbackState);
|
||||
void onError(ExoPlaybackException e);
|
||||
void onTracksChanged(TrackInfo trackInfo);
|
||||
public interface VideoListener {
|
||||
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||
float pixelWidthHeightRatio);
|
||||
void onDrawnToSurface(Surface surface);
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener for debugging information.
|
||||
*/
|
||||
public interface InfoListener {
|
||||
public interface DebugListener {
|
||||
void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||
long initializationDurationMs);
|
||||
void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||
@ -100,30 +80,31 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
||||
void onId3Metadata(List<Id3Frame> id3Frames);
|
||||
}
|
||||
|
||||
private static final String TAG = "DemoPlayer";
|
||||
private static final String TAG = "SimpleExoPlayer";
|
||||
|
||||
private final ExoPlayer player;
|
||||
private final DefaultTrackSelector trackSelector;
|
||||
private final BandwidthMeter bandwidthMeter;
|
||||
private final TrackRenderer[] renderers;
|
||||
private final PlayerControl playerControl;
|
||||
private final ComponentListener componentListener;
|
||||
private final Handler mainHandler;
|
||||
private final CopyOnWriteArrayList<Listener> listeners;
|
||||
|
||||
private Surface surface;
|
||||
private Format videoFormat;
|
||||
private TrackInfo trackInfo;
|
||||
private Format audioFormat;
|
||||
|
||||
private CaptionListener captionListener;
|
||||
private Id3MetadataListener id3MetadataListener;
|
||||
private InfoListener infoListener;
|
||||
private VideoListener videoListener;
|
||||
private DebugListener debugListener;
|
||||
private CodecCounters videoCodecCounters;
|
||||
private CodecCounters audioCodecCounters;
|
||||
|
||||
public DemoPlayer(Context context, DrmSessionManager drmSessionManager,
|
||||
boolean useExtensionDecoders) {
|
||||
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
|
||||
DrmSessionManager drmSessionManager, boolean useExtensionDecoders, int minBufferMs,
|
||||
int minRebufferMs) {
|
||||
mainHandler = new Handler();
|
||||
bandwidthMeter = new DefaultBandwidthMeter();
|
||||
listeners = new CopyOnWriteArrayList<>();
|
||||
componentListener = new ComponentListener();
|
||||
|
||||
// Build the renderers.
|
||||
ArrayList<TrackRenderer> renderersList = new ArrayList<>();
|
||||
@ -134,88 +115,186 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
||||
renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]);
|
||||
|
||||
// Build the player and associated objects.
|
||||
trackSelector = new DefaultTrackSelector(mainHandler, this, new DefaultTrackSelectionPolicy());
|
||||
player = ExoPlayer.Factory.newInstance(renderers, trackSelector, 1000, 5000);
|
||||
player.addListener(this);
|
||||
playerControl = new PlayerControl(player);
|
||||
player = new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs);
|
||||
}
|
||||
|
||||
public DefaultTrackSelector getTrackSelector() {
|
||||
return trackSelector;
|
||||
/**
|
||||
* Returns the number of renderers.
|
||||
*
|
||||
* @return The number of renderers.
|
||||
*/
|
||||
public int getRendererCount() {
|
||||
return renderers.length;
|
||||
}
|
||||
|
||||
public PlayerControl getPlayerControl() {
|
||||
return playerControl;
|
||||
}
|
||||
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void setInfoListener(InfoListener listener) {
|
||||
infoListener = listener;
|
||||
}
|
||||
|
||||
public void setCaptionListener(CaptionListener listener) {
|
||||
captionListener = listener;
|
||||
}
|
||||
|
||||
public void setMetadataListener(Id3MetadataListener listener) {
|
||||
id3MetadataListener = listener;
|
||||
/**
|
||||
* Returns the track type that the renderer at a given index handles.
|
||||
*
|
||||
* @see {@link TrackRenderer#getTrackType()}.
|
||||
* @param index The index of the renderer.
|
||||
* @return One of the TRACK_TYPE_* constants defined in {@link C}.
|
||||
*/
|
||||
public int getRendererType(int index) {
|
||||
return renderers[index].getTrackType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Surface} onto which video will be rendered.
|
||||
*
|
||||
* @param surface The {@link Surface}.
|
||||
*/
|
||||
public void setSurface(Surface surface) {
|
||||
this.surface = surface;
|
||||
pushSurface(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the {@link Surface} onto which video will be rendered.
|
||||
*/
|
||||
public void blockingClearSurface() {
|
||||
surface = null;
|
||||
pushSurface(true);
|
||||
}
|
||||
|
||||
public TrackInfo getTrackInfo() {
|
||||
return trackInfo;
|
||||
/**
|
||||
* @return The {@link BandwidthMeter} being used by the player.
|
||||
*/
|
||||
public BandwidthMeter getBandwidthMeter() {
|
||||
return bandwidthMeter;
|
||||
}
|
||||
|
||||
public void setSource(SampleSource source) {
|
||||
player.setSource(source);
|
||||
/**
|
||||
* @return The video format currently being played, or null if there is no video component to the
|
||||
* current media.
|
||||
*/
|
||||
public Format getVideoFormat() {
|
||||
return videoFormat;
|
||||
}
|
||||
|
||||
public void setPlayWhenReady(boolean playWhenReady) {
|
||||
player.setPlayWhenReady(playWhenReady);
|
||||
/**
|
||||
* @return The audio format currently being played, or null if there is no audio component to the
|
||||
* current media.
|
||||
*/
|
||||
public Format getAudioFormat() {
|
||||
return audioFormat;
|
||||
}
|
||||
|
||||
public void seekTo(long positionMs) {
|
||||
player.seekTo(positionMs);
|
||||
/**
|
||||
* @return The {@link CodecCounters} for video, or null if there is no video component to the
|
||||
* current media.
|
||||
*/
|
||||
public CodecCounters getVideoCodecCounters() {
|
||||
return videoCodecCounters;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
surface = null;
|
||||
player.release();
|
||||
/**
|
||||
* @return The {@link CodecCounters} for audio, or null if there is no audio component to the
|
||||
* current media.
|
||||
*/
|
||||
public CodecCounters getAudioCodecCounters() {
|
||||
return audioCodecCounters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to receive video events.
|
||||
*
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void setVideoListener(VideoListener listener) {
|
||||
videoListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to receive debug events.
|
||||
*
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void setDebugListener(DebugListener listener) {
|
||||
debugListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to receive caption events.
|
||||
*
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void setCaptionListener(CaptionListener listener) {
|
||||
captionListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to receive metadata events.
|
||||
*
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void setMetadataListener(Id3MetadataListener listener) {
|
||||
id3MetadataListener = listener;
|
||||
}
|
||||
|
||||
// ExoPlayer implementation
|
||||
|
||||
public void addListener(EventListener listener) {
|
||||
player.addListener(listener);
|
||||
}
|
||||
|
||||
public void removeListener(EventListener listener) {
|
||||
player.removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlaybackState() {
|
||||
return player.getPlaybackState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Format getFormat() {
|
||||
return videoFormat;
|
||||
public void setSource(SampleSource source) {
|
||||
player.setSource(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BandwidthMeter getBandwidthMeter() {
|
||||
return bandwidthMeter;
|
||||
public void setPlayWhenReady(boolean playWhenReady) {
|
||||
player.setPlayWhenReady(playWhenReady);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodecCounters getCodecCounters() {
|
||||
return videoCodecCounters;
|
||||
public boolean getPlayWhenReady() {
|
||||
return player.getPlayWhenReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayWhenReadyCommitted() {
|
||||
return player.isPlayWhenReadyCommitted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(long positionMs) {
|
||||
player.seekTo(positionMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
player.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
surface = null;
|
||||
player.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(ExoPlayerComponent target, int messageType, Object message) {
|
||||
player.sendMessage(target, messageType, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockingSendMessage(ExoPlayerComponent target, int messageType, Object message) {
|
||||
player.blockingSendMessage(target, messageType, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return player.getDuration();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -223,127 +302,17 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
||||
return player.getCurrentPosition();
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return player.getDuration();
|
||||
@Override
|
||||
public long getBufferedPosition() {
|
||||
return player.getBufferedPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferedPercentage() {
|
||||
return player.getBufferedPercentage();
|
||||
}
|
||||
|
||||
public boolean getPlayWhenReady() {
|
||||
return player.getPlayWhenReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int state) {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onStateChanged(playWhenReady, state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException exception) {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onError(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackInfo trackInfo) {
|
||||
this.trackInfo = trackInfo;
|
||||
for (Listener listener : listeners) {
|
||||
listener.onTracksChanged(trackInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||
float pixelWidthHeightRatio) {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDroppedFrames(int count, long elapsed) {
|
||||
if (infoListener != null) {
|
||||
infoListener.onDroppedFrames(count, elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||
if (infoListener != null) {
|
||||
infoListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioCodecCounters(CodecCounters counters) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs,
|
||||
long initializationDurationMs) {
|
||||
if (infoListener != null) {
|
||||
infoListener.onAudioDecoderInitialized(decoderName, initializedTimestampMs,
|
||||
initializationDurationMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioInputFormatChanged(Format format) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCues(List<Cue> cues) {
|
||||
if (captionListener != null) {
|
||||
captionListener.onCues(cues);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMetadata(List<Id3Frame> id3Frames) {
|
||||
if (id3MetadataListener != null) {
|
||||
id3MetadataListener.onId3Metadata(id3Frames);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyCommitted() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawnToSurface(Surface surface) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoCodecCounters(CodecCounters counters) {
|
||||
this.videoCodecCounters = counters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDecoderInitialized(String decoderName, long initializedTimestampMs,
|
||||
long initializationDurationMs) {
|
||||
if (infoListener != null) {
|
||||
infoListener.onVideoDecoderInitialized(decoderName, initializedTimestampMs,
|
||||
initializationDurationMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoInputFormatChanged(Format format) {
|
||||
videoFormat = format;
|
||||
}
|
||||
|
||||
public int getRendererType(int index) {
|
||||
return renderers[index].getTrackType();
|
||||
}
|
||||
// Internal methods.
|
||||
|
||||
private void pushSurface(boolean blockForSurfacePush) {
|
||||
for (TrackRenderer renderer : renderers) {
|
||||
@ -361,19 +330,19 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
||||
ArrayList<TrackRenderer> renderersList) {
|
||||
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
|
||||
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
|
||||
drmSessionManager, false, mainHandler, this, 50);
|
||||
drmSessionManager, false, mainHandler, componentListener, 50);
|
||||
renderersList.add(videoRenderer);
|
||||
|
||||
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT,
|
||||
drmSessionManager, true, mainHandler, this, AudioCapabilities.getCapabilities(context),
|
||||
AudioManager.STREAM_MUSIC);
|
||||
drmSessionManager, true, mainHandler, componentListener,
|
||||
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
|
||||
renderersList.add(audioRenderer);
|
||||
|
||||
TrackRenderer textRenderer = new TextTrackRenderer(this, mainHandler.getLooper());
|
||||
TrackRenderer textRenderer = new TextTrackRenderer(componentListener, mainHandler.getLooper());
|
||||
renderersList.add(textRenderer);
|
||||
|
||||
MetadataTrackRenderer<List<Id3Frame>> id3Renderer = new MetadataTrackRenderer<>(new Id3Parser(),
|
||||
this, mainHandler.getLooper());
|
||||
componentListener, mainHandler.getLooper());
|
||||
renderersList.add(id3Renderer);
|
||||
}
|
||||
|
||||
@ -386,7 +355,8 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
||||
Class.forName("com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer");
|
||||
Constructor<?> constructor = clazz.getConstructor(boolean.class, Handler.class,
|
||||
VideoTrackRendererEventListener.class, int.class);
|
||||
renderersList.add((TrackRenderer) constructor.newInstance(true, mainHandler, this, 50));
|
||||
renderersList.add((TrackRenderer) constructor.newInstance(true, mainHandler,
|
||||
componentListener, 50));
|
||||
Log.i(TAG, "Loaded LibvpxVideoTrackRenderer.");
|
||||
} catch (Exception e) {
|
||||
// Expected if the app was built without the extension.
|
||||
@ -395,7 +365,9 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
||||
try {
|
||||
Class<?> clazz =
|
||||
Class.forName("com.google.android.exoplayer.ext.opus.LibopusAudioTrackRenderer");
|
||||
renderersList.add((TrackRenderer) clazz.newInstance());
|
||||
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
||||
AudioTrackRendererEventListener.class);
|
||||
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
||||
Log.i(TAG, "Loaded LibopusAudioTrackRenderer.");
|
||||
} catch (Exception e) {
|
||||
// Expected if the app was built without the extension.
|
||||
@ -404,7 +376,9 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
||||
try {
|
||||
Class<?> clazz =
|
||||
Class.forName("com.google.android.exoplayer.ext.flac.LibflacAudioTrackRenderer");
|
||||
renderersList.add((TrackRenderer) clazz.newInstance());
|
||||
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
||||
AudioTrackRendererEventListener.class);
|
||||
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
||||
Log.i(TAG, "Loaded LibflacAudioTrackRenderer.");
|
||||
} catch (Exception e) {
|
||||
// Expected if the app was built without the extension.
|
||||
@ -413,11 +387,121 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
||||
try {
|
||||
Class<?> clazz =
|
||||
Class.forName("com.google.android.exoplayer.ext.ffmpeg.FfmpegAudioTrackRenderer");
|
||||
renderersList.add((TrackRenderer) clazz.newInstance());
|
||||
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
||||
AudioTrackRendererEventListener.class);
|
||||
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
||||
Log.i(TAG, "Loaded FfmpegAudioTrackRenderer.");
|
||||
} catch (Exception e) {
|
||||
// Expected if the app was built without the extension.
|
||||
}
|
||||
}
|
||||
|
||||
private final class ComponentListener implements VideoTrackRendererEventListener,
|
||||
AudioTrackRendererEventListener, TextRenderer, MetadataRenderer<List<Id3Frame>> {
|
||||
|
||||
// VideoTrackRendererEventListener implementation
|
||||
|
||||
@Override
|
||||
public void onVideoEnabled(CodecCounters counters) {
|
||||
videoCodecCounters = counters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDecoderInitialized(String decoderName, long initializedTimestampMs,
|
||||
long initializationDurationMs) {
|
||||
if (debugListener != null) {
|
||||
debugListener.onVideoDecoderInitialized(decoderName, initializedTimestampMs,
|
||||
initializationDurationMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoInputFormatChanged(Format format) {
|
||||
videoFormat = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDroppedFrames(int count, long elapsed) {
|
||||
if (debugListener != null) {
|
||||
debugListener.onDroppedFrames(count, elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||
float pixelWidthHeightRatio) {
|
||||
if (videoListener != null) {
|
||||
videoListener.onVideoSizeChanged(width, height, unappliedRotationDegrees,
|
||||
pixelWidthHeightRatio);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawnToSurface(Surface surface) {
|
||||
if (videoListener != null) {
|
||||
videoListener.onDrawnToSurface(surface);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoDisabled() {
|
||||
videoFormat = null;
|
||||
videoCodecCounters = null;
|
||||
}
|
||||
|
||||
// AudioTrackRendererEventListener implementation
|
||||
|
||||
@Override
|
||||
public void onAudioEnabled(CodecCounters counters) {
|
||||
audioCodecCounters = counters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs,
|
||||
long initializationDurationMs) {
|
||||
if (debugListener != null) {
|
||||
debugListener.onAudioDecoderInitialized(decoderName, initializedTimestampMs,
|
||||
initializationDurationMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioInputFormatChanged(Format format) {
|
||||
audioFormat = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs,
|
||||
long elapsedSinceLastFeedMs) {
|
||||
if (debugListener != null) {
|
||||
debugListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDisabled() {
|
||||
audioFormat = null;
|
||||
audioCodecCounters = null;
|
||||
}
|
||||
|
||||
// TextRenderer implementation
|
||||
|
||||
@Override
|
||||
public void onCues(List<Cue> cues) {
|
||||
if (captionListener != null) {
|
||||
captionListener.onCues(cues);
|
||||
}
|
||||
}
|
||||
|
||||
// MetadataRenderer implementation
|
||||
|
||||
@Override
|
||||
public void onMetadata(List<Id3Frame> id3Frames) {
|
||||
if (id3MetadataListener != null) {
|
||||
id3MetadataListener.onId3Metadata(id3Frames);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -28,11 +28,11 @@ import android.view.TextureView;
|
||||
public interface VideoTrackRendererEventListener {
|
||||
|
||||
/**
|
||||
* Invoked to pass the codec counters when the renderer is enabled.
|
||||
* Invoked when the renderer is enabled.
|
||||
*
|
||||
* @param counters CodecCounters object used by the renderer.
|
||||
* @param counters {@link CodecCounters} that will be updated by the renderer.
|
||||
*/
|
||||
void onVideoCodecCounters(CodecCounters counters);
|
||||
void onVideoEnabled(CodecCounters counters);
|
||||
|
||||
/**
|
||||
* Invoked when a decoder is created.
|
||||
@ -92,6 +92,11 @@ public interface VideoTrackRendererEventListener {
|
||||
*/
|
||||
void onDrawnToSurface(Surface surface);
|
||||
|
||||
/**
|
||||
* Invoked when the renderer is disabled.
|
||||
*/
|
||||
void onVideoDisabled();
|
||||
|
||||
/**
|
||||
* Dispatches events to a {@link VideoTrackRendererEventListener}.
|
||||
*/
|
||||
@ -105,12 +110,12 @@ public interface VideoTrackRendererEventListener {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void codecCounters(final CodecCounters codecCounters) {
|
||||
public void enabled(final CodecCounters codecCounters) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onVideoCodecCounters(codecCounters);
|
||||
listener.onVideoEnabled(codecCounters);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -175,6 +180,17 @@ public interface VideoTrackRendererEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void disabled() {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onVideoDisabled();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -306,7 +306,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
||||
|
||||
@Override
|
||||
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
||||
eventDispatcher.codecCounters(codecCounters);
|
||||
codecCounters.reset();
|
||||
eventDispatcher.enabled(codecCounters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -321,6 +322,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
||||
|
||||
@Override
|
||||
protected void onDisabled() {
|
||||
eventDispatcher.disabled();
|
||||
inputBuffer = null;
|
||||
outputBuffer = null;
|
||||
inputFormat = null;
|
||||
|
@ -16,54 +16,33 @@
|
||||
package com.google.android.exoplayer.util;
|
||||
|
||||
import com.google.android.exoplayer.CodecCounters;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
||||
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A helper class for periodically updating debug information displayed by a {@link TextView}.
|
||||
* A helper class for periodically updating a {@link TextView} with debug information obtained from
|
||||
* a {@link SimpleExoPlayer}.
|
||||
*/
|
||||
public final class DebugTextViewHelper implements Runnable {
|
||||
|
||||
/**
|
||||
* Provides debug information about an ongoing playback.
|
||||
*/
|
||||
public interface Provider {
|
||||
|
||||
/**
|
||||
* Returns the current playback position, in milliseconds.
|
||||
*/
|
||||
long getCurrentPosition();
|
||||
|
||||
/**
|
||||
* Returns a format whose information should be displayed, or null.
|
||||
*/
|
||||
Format getFormat();
|
||||
|
||||
/**
|
||||
* Returns a {@link BandwidthMeter} whose estimate should be displayed, or null.
|
||||
*/
|
||||
BandwidthMeter getBandwidthMeter();
|
||||
|
||||
/**
|
||||
* Returns a {@link CodecCounters} whose information should be displayed, or null.
|
||||
*/
|
||||
CodecCounters getCodecCounters();
|
||||
|
||||
}
|
||||
public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListener {
|
||||
|
||||
private static final int REFRESH_INTERVAL_MS = 1000;
|
||||
|
||||
private final SimpleExoPlayer player;
|
||||
private final TextView textView;
|
||||
private final Provider debuggable;
|
||||
|
||||
private boolean started;
|
||||
|
||||
/**
|
||||
* @param debuggable The {@link Provider} from which debug information should be obtained.
|
||||
* @param player The {@link SimpleExoPlayer} from which debug information should be obtained.
|
||||
* @param textView The {@link TextView} that should be updated to display the information.
|
||||
*/
|
||||
public DebugTextViewHelper(Provider debuggable, TextView textView) {
|
||||
this.debuggable = debuggable;
|
||||
public DebugTextViewHelper(SimpleExoPlayer player, TextView textView) {
|
||||
this.player = player;
|
||||
this.textView = textView;
|
||||
}
|
||||
|
||||
@ -73,7 +52,11 @@ public final class DebugTextViewHelper implements Runnable {
|
||||
* Should be called from the application's main thread.
|
||||
*/
|
||||
public void start() {
|
||||
stop();
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
started = true;
|
||||
player.addListener(this);
|
||||
run();
|
||||
}
|
||||
|
||||
@ -83,32 +66,49 @@ public final class DebugTextViewHelper implements Runnable {
|
||||
* Should be called from the application's main thread.
|
||||
*/
|
||||
public void stop() {
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
started = false;
|
||||
player.removeListener(this);
|
||||
textView.removeCallbacks(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
textView.setText(getRenderString());
|
||||
updateTextView();
|
||||
textView.postDelayed(this, REFRESH_INTERVAL_MS);
|
||||
}
|
||||
|
||||
private String getRenderString() {
|
||||
return getTimeString() + " " + getQualityString() + " " + getBandwidthString() + " "
|
||||
+ getVideoCodecCountersString();
|
||||
private void updateTextView() {
|
||||
textView.setText(getPlayerStateString() + getBandwidthString() + getVideoString()
|
||||
+ getAudioString());
|
||||
}
|
||||
|
||||
private String getTimeString() {
|
||||
return "ms(" + debuggable.getCurrentPosition() + ")";
|
||||
public String getPlayerStateString() {
|
||||
String text = "playWhenReady:" + player.getPlayWhenReady() + " playbackState:";
|
||||
switch(player.getPlaybackState()) {
|
||||
case ExoPlayer.STATE_BUFFERING:
|
||||
text += "buffering";
|
||||
break;
|
||||
case ExoPlayer.STATE_ENDED:
|
||||
text += "ended";
|
||||
break;
|
||||
case ExoPlayer.STATE_IDLE:
|
||||
text += "idle";
|
||||
break;
|
||||
case ExoPlayer.STATE_READY:
|
||||
text += "ready";
|
||||
break;
|
||||
default:
|
||||
text += "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
private String getQualityString() {
|
||||
Format format = debuggable.getFormat();
|
||||
return format == null ? "id:? br:? h:?"
|
||||
: "id:" + format.id + " br:" + format.bitrate + " h:" + format.height;
|
||||
return text;
|
||||
}
|
||||
|
||||
private String getBandwidthString() {
|
||||
BandwidthMeter bandwidthMeter = debuggable.getBandwidthMeter();
|
||||
BandwidthMeter bandwidthMeter = player.getBandwidthMeter();
|
||||
if (bandwidthMeter == null
|
||||
|| bandwidthMeter.getBitrateEstimate() == BandwidthMeter.NO_ESTIMATE) {
|
||||
return " bw:?";
|
||||
@ -117,9 +117,50 @@ public final class DebugTextViewHelper implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private String getVideoCodecCountersString() {
|
||||
CodecCounters codecCounters = debuggable.getCodecCounters();
|
||||
return codecCounters == null ? "" : codecCounters.getDebugString();
|
||||
private String getVideoString() {
|
||||
Format format = player.getVideoFormat();
|
||||
if (format == null) {
|
||||
return "";
|
||||
}
|
||||
return "\n" + format.sampleMimeType + "(r:" + format.width + "x" + format.height
|
||||
+ getCodecCounterBufferCountString(player.getVideoCodecCounters()) + ")";
|
||||
}
|
||||
|
||||
private String getAudioString() {
|
||||
Format format = player.getAudioFormat();
|
||||
if (format == null) {
|
||||
return "";
|
||||
}
|
||||
return "\n" + format.sampleMimeType + "(hz:" + format.sampleRate + " ch:" + format.channelCount
|
||||
+ getCodecCounterBufferCountString(player.getAudioCodecCounters()) + ")";
|
||||
}
|
||||
|
||||
private static String getCodecCounterBufferCountString(CodecCounters counters) {
|
||||
if (counters == null) {
|
||||
return "";
|
||||
}
|
||||
counters.ensureUpdated();
|
||||
return " rb:" + counters.renderedOutputBufferCount
|
||||
+ " sb:" + counters.skippedOutputBufferCount
|
||||
+ " db:" + counters.droppedOutputBufferCount
|
||||
+ " mcdb:" + counters.maxConsecutiveDroppedOutputBufferCount;
|
||||
}
|
||||
|
||||
// ExoPlayer.EventListener implementation
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
updateTextView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyCommitted() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user