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;
|
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;
|
|||||||
/**
|
/**
|
||||||
* 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
|
|||||||
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
|
|||||||
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;
|
|||||||
|
|
||||||
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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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
|
@Override
|
||||||
public void onTracksChanged(TrackInfo trackSet) {
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
updateButtonVisibilities();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStateChanged(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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
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;
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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;
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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;
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
@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 {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
super.onDisabled();
|
super.onDisabled();
|
||||||
|
eventDispatcher.disabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
@ -62,6 +62,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}.
|
||||||
*/
|
*/
|
||||||
@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// 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;
|
|||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 @@
|
|||||||
*/
|
*/
|
||||||
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;
|
|||||||
* 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
|
||||||
@ -93,63 +91,11 @@ 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 {
|
|||||||
* 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 {
|
|||||||
* 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 {
|
|||||||
* @param error The error.
|
* @param error The error.
|
||||||
*/
|
*/
|
||||||
void onPlayerError(ExoPlaybackException error);
|
void onPlayerError(ExoPlaybackException error);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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.
|
||||||
|
@ -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 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;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
}
|
}
|
||||||
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
|
|||||||
|
|
||||||
@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
|
|||||||
|
|
||||||
@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 {
|
|||||||
|
|
||||||
@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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDisabled() {
|
protected void onDisabled() {
|
||||||
|
eventDispatcher.disabled();
|
||||||
currentWidth = -1;
|
currentWidth = -1;
|
||||||
currentHeight = -1;
|
currentHeight = -1;
|
||||||
currentPixelWidthHeightRatio = -1;
|
currentPixelWidthHeightRatio = -1;
|
||||||
|
@ -13,23 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.audio.AudioCapabilities;
|
||||||
import com.google.android.exoplayer.drm.DrmSessionManager;
|
import com.google.android.exoplayer.drm.DrmSessionManager;
|
||||||
import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
|
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.text.TextTrackRenderer;
|
||||||
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
import com.google.android.exoplayer.upstream.BandwidthMeter;
|
||||||
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
|
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.content.Context;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
@ -54,30 +37,27 @@ import android.view.Surface;
|
|||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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,
|
public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
VideoTrackRendererEventListener, AudioTrackRendererEventListener, TextRenderer,
|
|
||||||
MetadataRenderer<List<Id3Frame>>, DebugTextViewHelper.Provider {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener for core events.
|
* A listener for video rendering information.
|
||||||
*/
|
*/
|
||||||
public interface Listener {
|
public interface VideoListener {
|
||||||
void onStateChanged(boolean playWhenReady, int playbackState);
|
|
||||||
void onError(ExoPlaybackException e);
|
|
||||||
void onTracksChanged(TrackInfo trackInfo);
|
|
||||||
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||||
float pixelWidthHeightRatio);
|
float pixelWidthHeightRatio);
|
||||||
|
void onDrawnToSurface(Surface surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener for debugging information.
|
* A listener for debugging information.
|
||||||
*/
|
*/
|
||||||
public interface InfoListener {
|
public interface DebugListener {
|
||||||
void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||||
long initializationDurationMs);
|
long initializationDurationMs);
|
||||||
void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||||
@ -100,30 +80,31 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
void onId3Metadata(List<Id3Frame> id3Frames);
|
void onId3Metadata(List<Id3Frame> id3Frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String TAG = "DemoPlayer";
|
private static final String TAG = "SimpleExoPlayer";
|
||||||
|
|
||||||
private final ExoPlayer player;
|
private final ExoPlayer player;
|
||||||
private final DefaultTrackSelector trackSelector;
|
|
||||||
private final BandwidthMeter bandwidthMeter;
|
private final BandwidthMeter bandwidthMeter;
|
||||||
private final TrackRenderer[] renderers;
|
private final TrackRenderer[] renderers;
|
||||||
private final PlayerControl playerControl;
|
private final ComponentListener componentListener;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
private final CopyOnWriteArrayList<Listener> listeners;
|
|
||||||
|
|
||||||
private Surface surface;
|
private Surface surface;
|
||||||
private Format videoFormat;
|
private Format videoFormat;
|
||||||
private TrackInfo trackInfo;
|
private Format audioFormat;
|
||||||
|
|
||||||
private CaptionListener captionListener;
|
private CaptionListener captionListener;
|
||||||
private Id3MetadataListener id3MetadataListener;
|
private Id3MetadataListener id3MetadataListener;
|
||||||
private InfoListener infoListener;
|
private VideoListener videoListener;
|
||||||
|
private DebugListener debugListener;
|
||||||
private CodecCounters videoCodecCounters;
|
private CodecCounters videoCodecCounters;
|
||||||
|
private CodecCounters audioCodecCounters;
|
||||||
|
|
||||||
public DemoPlayer(Context context, DrmSessionManager drmSessionManager,
|
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
|
||||||
boolean useExtensionDecoders) {
|
DrmSessionManager drmSessionManager, boolean useExtensionDecoders, int minBufferMs,
|
||||||
|
int minRebufferMs) {
|
||||||
mainHandler = new Handler();
|
mainHandler = new Handler();
|
||||||
bandwidthMeter = new DefaultBandwidthMeter();
|
bandwidthMeter = new DefaultBandwidthMeter();
|
||||||
listeners = new CopyOnWriteArrayList<>();
|
componentListener = new ComponentListener();
|
||||||
|
|
||||||
// Build the renderers.
|
// Build the renderers.
|
||||||
ArrayList<TrackRenderer> renderersList = new ArrayList<>();
|
ArrayList<TrackRenderer> renderersList = new ArrayList<>();
|
||||||
@ -134,88 +115,186 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]);
|
renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]);
|
||||||
|
|
||||||
// Build the player and associated objects.
|
// Build the player and associated objects.
|
||||||
trackSelector = new DefaultTrackSelector(mainHandler, this, new DefaultTrackSelectionPolicy());
|
player = new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs);
|
||||||
player = ExoPlayer.Factory.newInstance(renderers, trackSelector, 1000, 5000);
|
|
||||||
player.addListener(this);
|
|
||||||
playerControl = new PlayerControl(player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
* Returns the track type that the renderer at a given index handles.
|
||||||
}
|
*
|
||||||
|
* @see {@link TrackRenderer#getTrackType()}.
|
||||||
public void addListener(Listener listener) {
|
* @param index The index of the renderer.
|
||||||
listeners.add(listener);
|
* @return One of the TRACK_TYPE_* constants defined in {@link C}.
|
||||||
}
|
*/
|
||||||
|
public int getRendererType(int index) {
|
||||||
public void removeListener(Listener listener) {
|
return renderers[index].getTrackType();
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInfoListener(InfoListener listener) {
|
|
||||||
infoListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCaptionListener(CaptionListener listener) {
|
|
||||||
captionListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMetadataListener(Id3MetadataListener listener) {
|
|
||||||
id3MetadataListener = listener;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Surface} onto which video will be rendered.
|
||||||
|
*
|
||||||
|
* @param surface The {@link Surface}.
|
||||||
|
*/
|
||||||
public void setSurface(Surface surface) {
|
public void setSurface(Surface surface) {
|
||||||
this.surface = surface;
|
this.surface = surface;
|
||||||
pushSurface(false);
|
pushSurface(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the {@link Surface} onto which video will be rendered.
|
||||||
|
*/
|
||||||
public void blockingClearSurface() {
|
public void blockingClearSurface() {
|
||||||
surface = null;
|
surface = null;
|
||||||
pushSurface(true);
|
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;
|
* @return The {@link CodecCounters} for audio, or null if there is no audio component to the
|
||||||
player.release();
|
* 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() {
|
public int getPlaybackState() {
|
||||||
return player.getPlaybackState();
|
return player.getPlaybackState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Format getFormat() {
|
public void setSource(SampleSource source) {
|
||||||
return videoFormat;
|
player.setSource(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BandwidthMeter getBandwidthMeter() {
|
public void setPlayWhenReady(boolean playWhenReady) {
|
||||||
return bandwidthMeter;
|
player.setPlayWhenReady(playWhenReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodecCounters getCodecCounters() {
|
public boolean getPlayWhenReady() {
|
||||||
return videoCodecCounters;
|
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
|
@Override
|
||||||
@ -223,127 +302,17 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
return player.getCurrentPosition();
|
return player.getCurrentPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDuration() {
|
@Override
|
||||||
return player.getDuration();
|
public long getBufferedPosition() {
|
||||||
|
return player.getBufferedPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getBufferedPercentage() {
|
public int getBufferedPercentage() {
|
||||||
return player.getBufferedPercentage();
|
return player.getBufferedPercentage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getPlayWhenReady() {
|
// Internal methods.
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pushSurface(boolean blockForSurfacePush) {
|
private void pushSurface(boolean blockForSurfacePush) {
|
||||||
for (TrackRenderer renderer : renderers) {
|
for (TrackRenderer renderer : renderers) {
|
||||||
@ -361,19 +330,19 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
ArrayList<TrackRenderer> renderersList) {
|
ArrayList<TrackRenderer> renderersList) {
|
||||||
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
|
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
|
||||||
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
|
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
|
||||||
drmSessionManager, false, mainHandler, this, 50);
|
drmSessionManager, false, mainHandler, componentListener, 50);
|
||||||
renderersList.add(videoRenderer);
|
renderersList.add(videoRenderer);
|
||||||
|
|
||||||
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT,
|
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT,
|
||||||
drmSessionManager, true, mainHandler, this, AudioCapabilities.getCapabilities(context),
|
drmSessionManager, true, mainHandler, componentListener,
|
||||||
AudioManager.STREAM_MUSIC);
|
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
|
||||||
renderersList.add(audioRenderer);
|
renderersList.add(audioRenderer);
|
||||||
|
|
||||||
TrackRenderer textRenderer = new TextTrackRenderer(this, mainHandler.getLooper());
|
TrackRenderer textRenderer = new TextTrackRenderer(componentListener, mainHandler.getLooper());
|
||||||
renderersList.add(textRenderer);
|
renderersList.add(textRenderer);
|
||||||
|
|
||||||
MetadataTrackRenderer<List<Id3Frame>> id3Renderer = new MetadataTrackRenderer<>(new Id3Parser(),
|
MetadataTrackRenderer<List<Id3Frame>> id3Renderer = new MetadataTrackRenderer<>(new Id3Parser(),
|
||||||
this, mainHandler.getLooper());
|
componentListener, mainHandler.getLooper());
|
||||||
renderersList.add(id3Renderer);
|
renderersList.add(id3Renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +355,8 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
Class.forName("com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer");
|
Class.forName("com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer");
|
||||||
Constructor<?> constructor = clazz.getConstructor(boolean.class, Handler.class,
|
Constructor<?> constructor = clazz.getConstructor(boolean.class, Handler.class,
|
||||||
VideoTrackRendererEventListener.class, int.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.");
|
Log.i(TAG, "Loaded LibvpxVideoTrackRenderer.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Expected if the app was built without the extension.
|
// Expected if the app was built without the extension.
|
||||||
@ -395,7 +365,9 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
try {
|
try {
|
||||||
Class<?> clazz =
|
Class<?> clazz =
|
||||||
Class.forName("com.google.android.exoplayer.ext.opus.LibopusAudioTrackRenderer");
|
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.");
|
Log.i(TAG, "Loaded LibopusAudioTrackRenderer.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Expected if the app was built without the extension.
|
// Expected if the app was built without the extension.
|
||||||
@ -404,7 +376,9 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
try {
|
try {
|
||||||
Class<?> clazz =
|
Class<?> clazz =
|
||||||
Class.forName("com.google.android.exoplayer.ext.flac.LibflacAudioTrackRenderer");
|
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.");
|
Log.i(TAG, "Loaded LibflacAudioTrackRenderer.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Expected if the app was built without the extension.
|
// Expected if the app was built without the extension.
|
||||||
@ -413,11 +387,121 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
try {
|
try {
|
||||||
Class<?> clazz =
|
Class<?> clazz =
|
||||||
Class.forName("com.google.android.exoplayer.ext.ffmpeg.FfmpegAudioTrackRenderer");
|
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.");
|
Log.i(TAG, "Loaded FfmpegAudioTrackRenderer.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Expected if the app was built without the extension.
|
// 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 {
|
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.
|
||||||
@ -92,6 +92,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}.
|
||||||
*/
|
*/
|
||||||
@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@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
|
|||||||
|
|
||||||
@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 @@
|
|||||||
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 {
|
|||||||
* 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,32 +66,49 @@ 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:?";
|
||||||
@ -117,9 +117,50 @@ public final class DebugTextViewHelper implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getVideoCodecCountersString() {
|
private String getVideoString() {
|
||||||
CodecCounters codecCounters = debuggable.getCodecCounters();
|
Format format = player.getVideoFormat();
|
||||||
return codecCounters == null ? "" : codecCounters.getDebugString();
|
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