Commit ced7de15 by olly Committed by Oliver Woodman

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
parent 075e095c
Showing with 418 additions and 231 deletions
...@@ -16,16 +16,17 @@ ...@@ -16,16 +16,17 @@
package com.google.android.exoplayer.demo; package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo; import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.SimpleExoPlayer;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray; import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackSelection;
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener; 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.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.ExtractorSampleSource;
...@@ -39,9 +40,9 @@ import java.util.Locale; ...@@ -39,9 +40,9 @@ import java.util.Locale;
/** /**
* Logs player events using {@link Log}. * 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, ChunkTrackStreamEventListener, ExtractorSampleSource.EventListener,
StreamingDrmSessionManager.EventListener { StreamingDrmSessionManager.EventListener, DefaultTrackSelector.EventListener {
private static final String TAG = "EventLogger"; private static final String TAG = "EventLogger";
private static final NumberFormat TIME_FORMAT; private static final NumberFormat TIME_FORMAT;
...@@ -62,26 +63,26 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener ...@@ -62,26 +63,26 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
Log.d(TAG, "end [" + getSessionTimeString() + "]"); Log.d(TAG, "end [" + getSessionTimeString() + "]");
} }
// DemoPlayer.Listener // ExoPlayer.EventListener
@Override @Override
public void onStateChanged(boolean playWhenReady, int state) { public void onPlayerStateChanged(boolean playWhenReady, int state) {
Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", " Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", "
+ getStateString(state) + "]"); + getStateString(state) + "]");
} }
@Override @Override
public void onError(ExoPlaybackException e) { public void onPlayWhenReadyCommitted() {
Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e); // Do nothing.
} }
@Override @Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, public void onPlayerError(ExoPlaybackException e) {
float pixelWidthHeightRatio) { Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e);
Log.d(TAG, "videoSizeChanged [" + width + ", " + height + ", " + unappliedRotationDegrees
+ ", " + pixelWidthHeightRatio + "]");
} }
// DefaultTrackSelector.EventListener
@Override @Override
public void onTracksChanged(TrackInfo trackInfo) { public void onTracksChanged(TrackInfo trackInfo) {
Log.d(TAG, "Tracks ["); Log.d(TAG, "Tracks [");
...@@ -130,7 +131,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener ...@@ -130,7 +131,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
Log.d(TAG, "]"); Log.d(TAG, "]");
} }
// DemoPlayer.InfoListener // SimpleExoPlayer.DebugListener
@Override @Override
public void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs, public void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
......
...@@ -17,15 +17,18 @@ package com.google.android.exoplayer.demo; ...@@ -17,15 +17,18 @@ package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.AspectRatioFrameLayout; import com.google.android.exoplayer.AspectRatioFrameLayout;
import com.google.android.exoplayer.C; 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.DefaultTrackSelector.TrackInfo;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.ExoPlayerFactory;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SimpleExoPlayer;
import com.google.android.exoplayer.TrackGroupArray; import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.dash.DashSampleSource; 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.demo.ui.TrackSelectionHelper;
import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
...@@ -46,6 +49,7 @@ import com.google.android.exoplayer.upstream.DataSourceFactory; ...@@ -46,6 +49,7 @@ import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator; import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer.util.DebugTextViewHelper; import com.google.android.exoplayer.util.DebugTextViewHelper;
import com.google.android.exoplayer.util.PlayerControl;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.Manifest.permission; import android.Manifest.permission;
...@@ -61,6 +65,7 @@ import android.text.TextUtils; ...@@ -61,6 +65,7 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.View; import android.view.View;
...@@ -81,10 +86,11 @@ import java.util.List; ...@@ -81,10 +86,11 @@ import java.util.List;
import java.util.UUID; 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, 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. // For use within demo app code.
public static final String CONTENT_TYPE_EXTRA = "content_type"; public static final String CONTENT_TYPE_EXTRA = "content_type";
...@@ -114,12 +120,12 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -114,12 +120,12 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
private AspectRatioFrameLayout videoFrame; private AspectRatioFrameLayout videoFrame;
private SurfaceView surfaceView; private SurfaceView surfaceView;
private TextView debugTextView; private TextView debugTextView;
private TextView playerStateTextView;
private SubtitleLayout subtitleLayout; private SubtitleLayout subtitleLayout;
private Button retryButton; private Button retryButton;
private DataSourceFactory dataSourceFactory; private DataSourceFactory dataSourceFactory;
private DemoPlayer player; private SimpleExoPlayer player;
private DefaultTrackSelector trackSelector;
private TrackSelectionHelper trackSelectionHelper; private TrackSelectionHelper trackSelectionHelper;
private DebugTextViewHelper debugViewHelper; private DebugTextViewHelper debugViewHelper;
private boolean playerNeedsSource; private boolean playerNeedsSource;
...@@ -163,10 +169,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -163,10 +169,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
surfaceView = (SurfaceView) findViewById(R.id.surface_view); surfaceView = (SurfaceView) findViewById(R.id.surface_view);
surfaceView.getHolder().addCallback(this); surfaceView.getHolder().addCallback(this);
debugTextView = (TextView) findViewById(R.id.debug_text_view); debugTextView = (TextView) findViewById(R.id.debug_text_view);
playerStateTextView = (TextView) findViewById(R.id.player_state_view);
subtitleLayout = (SubtitleLayout) findViewById(R.id.subtitles); subtitleLayout = (SubtitleLayout) findViewById(R.id.subtitles);
mediaController = new KeyCompatibleMediaController(this); mediaController = new KeyCompatibleMediaController(this);
retryButton = (Button) findViewById(R.id.retry_button); retryButton = (Button) findViewById(R.id.retry_button);
retryButton.setOnClickListener(this); retryButton.setOnClickListener(this);
...@@ -226,7 +229,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -226,7 +229,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
initializePlayer(); initializePlayer();
} else if (view.getParent() == debugRootView) { } else if (view.getParent() == debugRootView) {
trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(), 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, ...@@ -279,6 +282,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
int type = intent.getIntExtra(CONTENT_TYPE_EXTRA, int type = intent.getIntExtra(CONTENT_TYPE_EXTRA,
inferContentType(uri, intent.getStringExtra(CONTENT_EXT_EXTRA))); inferContentType(uri, intent.getStringExtra(CONTENT_EXT_EXTRA)));
if (player == null) { if (player == null) {
boolean useExtensionDecoders = intent.getBooleanExtra(USE_EXTENSION_DECODERS, false);
UUID drmSchemeUuid = (UUID) intent.getSerializableExtra(DRM_SCHEME_UUID_EXTRA); UUID drmSchemeUuid = (UUID) intent.getSerializableExtra(DRM_SCHEME_UUID_EXTRA);
DrmSessionManager drmSessionManager = null; DrmSessionManager drmSessionManager = null;
if (drmSchemeUuid != null) { if (drmSchemeUuid != null) {
...@@ -291,20 +295,24 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -291,20 +295,24 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
return; return;
} }
} }
boolean useExtensionDecoders = intent.getBooleanExtra(USE_EXTENSION_DECODERS, false);
eventLogger = new EventLogger(); eventLogger = new EventLogger();
eventLogger.startSession(); 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(this);
player.addListener(eventLogger); player.addListener(eventLogger);
player.setInfoListener(eventLogger); player.setDebugListener(eventLogger);
player.setVideoListener(this);
player.setCaptionListener(this); player.setCaptionListener(this);
player.setMetadataListener(this); player.setMetadataListener(this);
player.seekTo(playerPosition); player.seekTo(playerPosition);
player.setSurface(surfaceView.getHolder().getSurface()); player.setSurface(surfaceView.getHolder().getSurface());
player.setPlayWhenReady(true); player.setPlayWhenReady(true);
trackSelectionHelper = new TrackSelectionHelper(player.getTrackSelector()); mediaController.setMediaPlayer(new PlayerControl(player));
mediaController.setMediaPlayer(player.getPlayerControl());
mediaController.setAnchorView(rootView); mediaController.setAnchorView(rootView);
debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper = new DebugTextViewHelper(player, debugTextView);
debugViewHelper.start(); debugViewHelper.start();
...@@ -373,47 +381,30 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -373,47 +381,30 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
playerPosition = player.getCurrentPosition(); playerPosition = player.getCurrentPosition();
player.release(); player.release();
player = null; player = null;
trackSelector = null;
trackSelectionHelper = null;
eventLogger.endSession(); eventLogger.endSession();
eventLogger = null; eventLogger = null;
} }
} }
// DemoPlayer.Listener implementation // ExoPlayer.EventListener implementation
@Override
public void onTracksChanged(TrackInfo trackSet) {
updateButtonVisibilities();
}
@Override @Override
public void onStateChanged(boolean playWhenReady, int playbackState) { public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ExoPlayer.STATE_ENDED) { if (playbackState == ExoPlayer.STATE_ENDED) {
showControls(); 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(); updateButtonVisibilities();
} }
@Override @Override
public void onError(ExoPlaybackException e) { public void onPlayWhenReadyCommitted() {
// Do nothing.
}
@Override
public void onPlayerError(ExoPlaybackException e) {
String errorString = null; String errorString = null;
if (e.type == ExoPlaybackException.TYPE_RENDERER) { if (e.type == ExoPlaybackException.TYPE_RENDERER) {
Exception cause = e.getRendererException(); Exception cause = e.getRendererException();
...@@ -445,14 +436,27 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -445,14 +436,27 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
showControls(); showControls();
} }
// SimpleExoPlayer.VideoListener implementation
@Override @Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthAspectRatio) { float pixelWidthAspectRatio) {
shutterView.setVisibility(View.GONE);
videoFrame.setAspectRatio( videoFrame.setAspectRatio(
height == 0 ? 1 : (width * pixelWidthAspectRatio) / height); 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 // User controls
private void updateButtonVisibilities() { private void updateButtonVisibilities() {
...@@ -461,8 +465,12 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -461,8 +465,12 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
retryButton.setVisibility(playerNeedsSource ? View.VISIBLE : View.GONE); retryButton.setVisibility(playerNeedsSource ? View.VISIBLE : View.GONE);
debugRootView.addView(retryButton); debugRootView.addView(retryButton);
TrackInfo trackInfo; if (player == null) {
if (player == null || (trackInfo = player.getTrackInfo()) == null) { return;
}
TrackInfo trackInfo = trackSelector.getTrackInfo();
if (trackInfo == null) {
return; return;
} }
...@@ -500,14 +508,14 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -500,14 +508,14 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
debugRootView.setVisibility(View.VISIBLE); debugRootView.setVisibility(View.VISIBLE);
} }
// DemoPlayer.CaptionListener implementation // SimpleExoPlayer.CaptionListener implementation
@Override @Override
public void onCues(List<Cue> cues) { public void onCues(List<Cue> cues) {
subtitleLayout.setCues(cues); subtitleLayout.setCues(cues);
} }
// DemoPlayer.MetadataListener implementation // SimpleExoPlayer.MetadataListener implementation
@Override @Override
public void onId3Metadata(List<Id3Frame> id3Frames) { public void onId3Metadata(List<Id3Frame> id3Frames) {
......
...@@ -48,14 +48,6 @@ ...@@ -48,14 +48,6 @@
android:background="#88000000" android:background="#88000000"
android:orientation="vertical"> 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" <TextView android:id="@+id/debug_text_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
......
...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy; ...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.ExoPlayerFactory;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.ExtractorSampleSource;
...@@ -57,7 +58,7 @@ public class FlacPlaybackTest extends InstrumentationTestCase { ...@@ -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_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 16; private static final int BUFFER_SEGMENT_COUNT = 16;
...@@ -77,9 +78,9 @@ public class FlacPlaybackTest extends InstrumentationTestCase { ...@@ -77,9 +78,9 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibflacAudioTrackRenderer audioRenderer = new LibflacAudioTrackRenderer(); LibflacAudioTrackRenderer audioRenderer = new LibflacAudioTrackRenderer();
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null, DefaultTrackSelector trackSelector = new DefaultTrackSelector(
new DefaultTrackSelectionPolicy()); new DefaultTrackSelectionPolicy(), null);
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector); player = ExoPlayerFactory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorSampleSource sampleSource = new ExtractorSampleSource( ExtractorSampleSource sampleSource = new ExtractorSampleSource(
uri, uri,
......
...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy; ...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.ExoPlayerFactory;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.ExtractorSampleSource;
...@@ -57,7 +58,7 @@ public class OpusPlaybackTest extends InstrumentationTestCase { ...@@ -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_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 16; private static final int BUFFER_SEGMENT_COUNT = 16;
...@@ -77,9 +78,9 @@ public class OpusPlaybackTest extends InstrumentationTestCase { ...@@ -77,9 +78,9 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibopusAudioTrackRenderer audioRenderer = new LibopusAudioTrackRenderer(); LibopusAudioTrackRenderer audioRenderer = new LibopusAudioTrackRenderer();
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null, DefaultTrackSelector trackSelector = new DefaultTrackSelector(
new DefaultTrackSelectionPolicy()); new DefaultTrackSelectionPolicy(), null);
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector); player = ExoPlayerFactory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorSampleSource sampleSource = new ExtractorSampleSource( ExtractorSampleSource sampleSource = new ExtractorSampleSource(
uri, uri,
......
...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy; ...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.ExoPlayerFactory;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.ExtractorSampleSource;
...@@ -73,7 +74,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase { ...@@ -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_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 16; private static final int BUFFER_SEGMENT_COUNT = 16;
...@@ -93,9 +94,9 @@ public class VpxPlaybackTest extends InstrumentationTestCase { ...@@ -93,9 +94,9 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(true); LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(true);
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null, DefaultTrackSelector trackSelector = new DefaultTrackSelector(
new DefaultTrackSelectionPolicy()); new DefaultTrackSelectionPolicy(), null);
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector); player = ExoPlayerFactory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorSampleSource sampleSource = new ExtractorSampleSource( ExtractorSampleSource sampleSource = new ExtractorSampleSource(
uri, uri,
......
...@@ -333,7 +333,8 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -333,7 +333,8 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
eventDispatcher.codecCounters(codecCounters); codecCounters.reset();
eventDispatcher.enabled(codecCounters);
} }
@Override @Override
...@@ -360,6 +361,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -360,6 +361,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
} }
} finally { } finally {
super.onDisabled(); super.onDisabled();
eventDispatcher.disabled();
} }
} }
......
...@@ -27,11 +27,11 @@ import android.os.SystemClock; ...@@ -27,11 +27,11 @@ import android.os.SystemClock;
public interface AudioTrackRendererEventListener { 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. * Invoked when a decoder is created.
...@@ -63,6 +63,11 @@ public interface AudioTrackRendererEventListener { ...@@ -63,6 +63,11 @@ public interface AudioTrackRendererEventListener {
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
/** /**
* Invoked when the renderer is disabled.
*/
void onAudioDisabled();
/**
* Dispatches events to a {@link AudioTrackRendererEventListener}. * Dispatches events to a {@link AudioTrackRendererEventListener}.
*/ */
final class EventDispatcher { final class EventDispatcher {
...@@ -75,12 +80,12 @@ public interface AudioTrackRendererEventListener { ...@@ -75,12 +80,12 @@ public interface AudioTrackRendererEventListener {
this.listener = listener; this.listener = listener;
} }
public void codecCounters(final CodecCounters codecCounters) { public void enabled(final CodecCounters codecCounters) {
if (listener != null) { if (listener != null) {
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
listener.onAudioCodecCounters(codecCounters); listener.onAudioEnabled(codecCounters);
} }
}); });
} }
...@@ -122,6 +127,17 @@ public interface AudioTrackRendererEventListener { ...@@ -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 { ...@@ -70,15 +70,17 @@ public final class CodecCounters {
// call this method. // call this method.
} }
public String getDebugString() { /**
ensureUpdated(); * Resets all counters to zero.
return "ic:" + codecInitCount */
+ " rc:" + codecReleaseCount public void reset() {
+ " ib:" + inputBufferCount codecInitCount = 0;
+ " rb:" + renderedOutputBufferCount codecReleaseCount = 0;
+ " sb:" + skippedOutputBufferCount inputBufferCount = 0;
+ " db:" + droppedOutputBufferCount renderedOutputBufferCount = 0;
+ " mcdb:" + maxConsecutiveDroppedOutputBufferCount; skippedOutputBufferCount = 0;
droppedOutputBufferCount = 0;
maxConsecutiveDroppedOutputBufferCount = 0;
} }
} }
...@@ -26,11 +26,12 @@ import android.util.SparseBooleanArray; ...@@ -26,11 +26,12 @@ import android.util.SparseBooleanArray;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
/** /**
* A {@link TrackSelector} suitable for a wide range of use cases. * 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{ TrackSelectionPolicy.InvalidationListener{
/** /**
...@@ -48,7 +49,7 @@ public class DefaultTrackSelector extends TrackSelector implements ...@@ -48,7 +49,7 @@ public class DefaultTrackSelector extends TrackSelector implements
} }
private final Handler eventHandler; private final Handler eventHandler;
private final EventListener eventListener; private final CopyOnWriteArraySet<EventListener> listeners;
private final SparseArray<Map<TrackGroupArray, TrackSelection>> trackSelectionOverrides; private final SparseArray<Map<TrackGroupArray, TrackSelection>> trackSelectionOverrides;
private final SparseBooleanArray rendererDisabledFlags; private final SparseBooleanArray rendererDisabledFlags;
private final TrackSelectionPolicy trackSelectionPolicy; private final TrackSelectionPolicy trackSelectionPolicy;
...@@ -56,19 +57,37 @@ public class DefaultTrackSelector extends TrackSelector implements ...@@ -56,19 +57,37 @@ public class DefaultTrackSelector extends TrackSelector implements
private TrackInfo activeTrackInfo; 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 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, public DefaultTrackSelector(TrackSelectionPolicy trackSelectionPolicy, Handler eventHandler) {
TrackSelectionPolicy trackSelectionPolicy) { this.trackSelectionPolicy = Assertions.checkNotNull(trackSelectionPolicy);
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.listeners = new CopyOnWriteArraySet<>();
trackSelectionOverrides = new SparseArray<>(); trackSelectionOverrides = new SparseArray<>();
rendererDisabledFlags = new SparseBooleanArray(); rendererDisabledFlags = new SparseBooleanArray();
this.trackSelectionPolicy = Assertions.checkNotNull(trackSelectionPolicy); trackSelectionPolicy.init(this);
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 ...@@ -391,11 +410,13 @@ public class DefaultTrackSelector extends TrackSelector implements
} }
private void notifyTrackInfoChanged(final TrackInfo trackInfo) { private void notifyTrackInfoChanged(final TrackInfo trackInfo) {
if (eventHandler != null && eventListener != null) { if (eventHandler != null) {
eventHandler.post(new Runnable() { eventHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
eventListener.onTracksChanged(trackInfo); for (EventListener listener : listeners) {
listener.onTracksChanged(trackInfo);
}
} }
}); });
} }
......
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import android.os.Looper;
/** /**
* An extensible media player exposing traditional high-level media player functionality, such as * An extensible media player exposing traditional high-level media player functionality, such as
* the ability to buffer media, play, pause and seek. * the ability to buffer media, play, pause and seek.
...@@ -62,8 +60,8 @@ import android.os.Looper; ...@@ -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 * discouraged, however if an application does wish to do this then it may do so provided that it
* ensures accesses are synchronized. * ensures accesses are synchronized.
* </li> * </li>
* <li>Registered {@link Listener}s are invoked on the thread that created the {@link ExoPlayer} * <li>Registered {@link EventListener}s are invoked on the thread that created the
* instance.</li> * {@link ExoPlayer} instance.</li>
* <li>An internal playback thread is responsible for managing playback and invoking the * <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> * {@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 * <li>{@link TrackRenderer} implementations (or any upstream components that they depend on) may
...@@ -94,62 +92,10 @@ import android.os.Looper; ...@@ -94,62 +92,10 @@ import android.os.Looper;
public interface ExoPlayer { 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 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 * Invoked when the value returned from either {@link ExoPlayer#getPlayWhenReady()} or
* {@link ExoPlayer#getPlaybackState()} changes. * {@link ExoPlayer#getPlaybackState()} changes.
...@@ -159,6 +105,7 @@ public interface ExoPlayer { ...@@ -159,6 +105,7 @@ public interface ExoPlayer {
* interface. * interface.
*/ */
void onPlayerStateChanged(boolean playWhenReady, int playbackState); void onPlayerStateChanged(boolean playWhenReady, int playbackState);
/** /**
* Invoked when the current value of {@link ExoPlayer#getPlayWhenReady()} has been reflected * Invoked when the current value of {@link ExoPlayer#getPlayWhenReady()} has been reflected
* by the internal playback thread. * by the internal playback thread.
...@@ -169,6 +116,7 @@ public interface ExoPlayer { ...@@ -169,6 +116,7 @@ public interface ExoPlayer {
* has been reflected. * has been reflected.
*/ */
void onPlayWhenReadyCommitted(); void onPlayWhenReadyCommitted();
/** /**
* Invoked when an error occurs. The playback state will transition to * Invoked when an error occurs. The playback state will transition to
* {@link ExoPlayer#STATE_IDLE} immediately after this method is invoked. The player instance * {@link ExoPlayer#STATE_IDLE} immediately after this method is invoked. The player instance
...@@ -178,6 +126,7 @@ public interface ExoPlayer { ...@@ -178,6 +126,7 @@ public interface ExoPlayer {
* @param error The error. * @param error The error.
*/ */
void onPlayerError(ExoPlaybackException error); void onPlayerError(ExoPlaybackException error);
} }
/** /**
...@@ -230,14 +179,14 @@ public interface ExoPlayer { ...@@ -230,14 +179,14 @@ public interface ExoPlayer {
* *
* @param listener The listener to register. * @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. * Unregister a listener. The listener will no longer receive events from the player.
* *
* @param listener The listener to unregister. * @param listener The listener to unregister.
*/ */
void removeListener(Listener listener); void removeListener(EventListener listener);
/** /**
* Returns the current state of the player. * Returns the current state of the player.
......
/*
* 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; ...@@ -34,7 +34,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private final Handler eventHandler; private final Handler eventHandler;
private final ExoPlayerImplInternal internalPlayer; private final ExoPlayerImplInternal internalPlayer;
private final CopyOnWriteArraySet<Listener> listeners; private final CopyOnWriteArraySet<EventListener> listeners;
private boolean playWhenReady; private boolean playWhenReady;
private int playbackState; private int playbackState;
...@@ -71,12 +71,12 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -71,12 +71,12 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
@Override @Override
public void addListener(Listener listener) { public void addListener(EventListener listener) {
listeners.add(listener); listeners.add(listener);
} }
@Override @Override
public void removeListener(Listener listener) { public void removeListener(EventListener listener) {
listeners.remove(listener); listeners.remove(listener);
} }
...@@ -96,7 +96,7 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -96,7 +96,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
this.playWhenReady = playWhenReady; this.playWhenReady = playWhenReady;
pendingPlayWhenReadyAcks++; pendingPlayWhenReadyAcks++;
internalPlayer.setPlayWhenReady(playWhenReady); internalPlayer.setPlayWhenReady(playWhenReady);
for (Listener listener : listeners) { for (EventListener listener : listeners) {
listener.onPlayerStateChanged(playWhenReady, playbackState); listener.onPlayerStateChanged(playWhenReady, playbackState);
} }
} }
...@@ -166,7 +166,7 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -166,7 +166,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
switch (msg.what) { switch (msg.what) {
case ExoPlayerImplInternal.MSG_STATE_CHANGED: { case ExoPlayerImplInternal.MSG_STATE_CHANGED: {
playbackState = msg.arg1; playbackState = msg.arg1;
for (Listener listener : listeners) { for (EventListener listener : listeners) {
listener.onPlayerStateChanged(playWhenReady, playbackState); listener.onPlayerStateChanged(playWhenReady, playbackState);
} }
break; break;
...@@ -174,7 +174,7 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -174,7 +174,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
case ExoPlayerImplInternal.MSG_SET_PLAY_WHEN_READY_ACK: { case ExoPlayerImplInternal.MSG_SET_PLAY_WHEN_READY_ACK: {
pendingPlayWhenReadyAcks--; pendingPlayWhenReadyAcks--;
if (pendingPlayWhenReadyAcks == 0) { if (pendingPlayWhenReadyAcks == 0) {
for (Listener listener : listeners) { for (EventListener listener : listeners) {
listener.onPlayWhenReadyCommitted(); listener.onPlayWhenReadyCommitted();
} }
} }
...@@ -182,7 +182,7 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -182,7 +182,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
case ExoPlayerImplInternal.MSG_ERROR: { case ExoPlayerImplInternal.MSG_ERROR: {
ExoPlaybackException exception = (ExoPlaybackException) msg.obj; ExoPlaybackException exception = (ExoPlaybackException) msg.obj;
for (Listener listener : listeners) { for (EventListener listener : listeners) {
listener.onPlayerError(exception); listener.onPlayerError(exception);
} }
break; break;
......
...@@ -272,8 +272,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -272,8 +272,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
codecCounters.reset();
eventDispatcher.enabled(codecCounters);
super.onEnabled(formats, joining); super.onEnabled(formats, joining);
eventDispatcher.codecCounters(codecCounters);
} }
@Override @Override
...@@ -290,6 +291,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -290,6 +291,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
@Override @Override
protected void onDisabled() { protected void onDisabled() {
eventDispatcher.disabled();
audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
try { try {
audioTrack.release(); audioTrack.release();
......
...@@ -210,6 +210,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -210,6 +210,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
codecCounters.reset();
eventDispatcher.enabled(codecCounters);
super.onEnabled(formats, joining); super.onEnabled(formats, joining);
adaptiveMaxWidth = Format.NO_VALUE; adaptiveMaxWidth = Format.NO_VALUE;
adaptiveMaxHeight = Format.NO_VALUE; adaptiveMaxHeight = Format.NO_VALUE;
...@@ -228,7 +230,6 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -228,7 +230,6 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
joiningDeadlineMs = SystemClock.elapsedRealtime() + allowedJoiningTimeMs; joiningDeadlineMs = SystemClock.elapsedRealtime() + allowedJoiningTimeMs;
} }
frameReleaseTimeHelper.enable(); frameReleaseTimeHelper.enable();
eventDispatcher.codecCounters(codecCounters);
} }
@Override @Override
...@@ -274,6 +275,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -274,6 +275,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
@Override @Override
protected void onDisabled() { protected void onDisabled() {
eventDispatcher.disabled();
currentWidth = -1; currentWidth = -1;
currentHeight = -1; currentHeight = -1;
currentPixelWidthHeightRatio = -1; currentPixelWidthHeightRatio = -1;
......
...@@ -28,11 +28,11 @@ import android.view.TextureView; ...@@ -28,11 +28,11 @@ import android.view.TextureView;
public interface VideoTrackRendererEventListener { 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. * Invoked when a decoder is created.
...@@ -93,6 +93,11 @@ public interface VideoTrackRendererEventListener { ...@@ -93,6 +93,11 @@ public interface VideoTrackRendererEventListener {
void onDrawnToSurface(Surface surface); void onDrawnToSurface(Surface surface);
/** /**
* Invoked when the renderer is disabled.
*/
void onVideoDisabled();
/**
* Dispatches events to a {@link VideoTrackRendererEventListener}. * Dispatches events to a {@link VideoTrackRendererEventListener}.
*/ */
final class EventDispatcher { final class EventDispatcher {
...@@ -105,12 +110,12 @@ public interface VideoTrackRendererEventListener { ...@@ -105,12 +110,12 @@ public interface VideoTrackRendererEventListener {
this.listener = listener; this.listener = listener;
} }
public void codecCounters(final CodecCounters codecCounters) { public void enabled(final CodecCounters codecCounters) {
if (listener != null) { if (listener != null) {
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
listener.onVideoCodecCounters(codecCounters); listener.onVideoEnabled(codecCounters);
} }
}); });
} }
...@@ -175,6 +180,17 @@ public interface VideoTrackRendererEventListener { ...@@ -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 ...@@ -306,7 +306,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
eventDispatcher.codecCounters(codecCounters); codecCounters.reset();
eventDispatcher.enabled(codecCounters);
} }
@Override @Override
...@@ -321,6 +322,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -321,6 +322,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
@Override @Override
protected void onDisabled() { protected void onDisabled() {
eventDispatcher.disabled();
inputBuffer = null; inputBuffer = null;
outputBuffer = null; outputBuffer = null;
inputFormat = null; inputFormat = null;
......
...@@ -16,54 +16,33 @@ ...@@ -16,54 +16,33 @@
package com.google.android.exoplayer.util; package com.google.android.exoplayer.util;
import com.google.android.exoplayer.CodecCounters; 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.Format;
import com.google.android.exoplayer.SimpleExoPlayer;
import com.google.android.exoplayer.upstream.BandwidthMeter; import com.google.android.exoplayer.upstream.BandwidthMeter;
import android.widget.TextView; 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 { public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListener {
/**
* 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();
}
private static final int REFRESH_INTERVAL_MS = 1000; private static final int REFRESH_INTERVAL_MS = 1000;
private final SimpleExoPlayer player;
private final TextView textView; 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. * @param textView The {@link TextView} that should be updated to display the information.
*/ */
public DebugTextViewHelper(Provider debuggable, TextView textView) { public DebugTextViewHelper(SimpleExoPlayer player, TextView textView) {
this.debuggable = debuggable; this.player = player;
this.textView = textView; this.textView = textView;
} }
...@@ -73,7 +52,11 @@ public final class DebugTextViewHelper implements Runnable { ...@@ -73,7 +52,11 @@ public final class DebugTextViewHelper implements Runnable {
* Should be called from the application's main thread. * Should be called from the application's main thread.
*/ */
public void start() { public void start() {
stop(); if (started) {
return;
}
started = true;
player.addListener(this);
run(); run();
} }
...@@ -83,43 +66,101 @@ public final class DebugTextViewHelper implements Runnable { ...@@ -83,43 +66,101 @@ public final class DebugTextViewHelper implements Runnable {
* Should be called from the application's main thread. * Should be called from the application's main thread.
*/ */
public void stop() { public void stop() {
if (!started) {
return;
}
started = false;
player.removeListener(this);
textView.removeCallbacks(this); textView.removeCallbacks(this);
} }
@Override @Override
public void run() { public void run() {
textView.setText(getRenderString()); updateTextView();
textView.postDelayed(this, REFRESH_INTERVAL_MS); textView.postDelayed(this, REFRESH_INTERVAL_MS);
} }
private String getRenderString() { private void updateTextView() {
return getTimeString() + " " + getQualityString() + " " + getBandwidthString() + " " textView.setText(getPlayerStateString() + getBandwidthString() + getVideoString()
+ getVideoCodecCountersString(); + getAudioString());
} }
private String getTimeString() { public String getPlayerStateString() {
return "ms(" + debuggable.getCurrentPosition() + ")"; 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;
} }
return text;
private String getQualityString() {
Format format = debuggable.getFormat();
return format == null ? "id:? br:? h:?"
: "id:" + format.id + " br:" + format.bitrate + " h:" + format.height;
} }
private String getBandwidthString() { private String getBandwidthString() {
BandwidthMeter bandwidthMeter = debuggable.getBandwidthMeter(); BandwidthMeter bandwidthMeter = player.getBandwidthMeter();
if (bandwidthMeter == null if (bandwidthMeter == null
|| bandwidthMeter.getBitrateEstimate() == BandwidthMeter.NO_ESTIMATE) { || bandwidthMeter.getBitrateEstimate() == BandwidthMeter.NO_ESTIMATE) {
return "bw:?"; return " bw:?";
} else { } else {
return "bw:" + (bandwidthMeter.getBitrateEstimate() / 1000); return " bw:" + (bandwidthMeter.getBitrateEstimate() / 1000);
}
}
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 getVideoCodecCountersString() { private String getAudioString() {
CodecCounters codecCounters = debuggable.getCodecCounters(); Format format = player.getAudioFormat();
return codecCounters == null ? "" : codecCounters.getDebugString(); 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.
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment