Commit 642b2009 by eguven Committed by Oliver Woodman

Add extensions to v2 demo app.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=121674924
parent cf0a31a7
Showing with 439 additions and 310 deletions
...@@ -26,13 +26,25 @@ android { ...@@ -26,13 +26,25 @@ android {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
} }
debug {
jniDebuggable = true
debuggable = true
}
} }
lintOptions { lintOptions {
abortOnError false abortOnError false
} }
productFlavors {
demo
demo_ext
}
} }
dependencies { dependencies {
compile project(':library') compile project(':library')
demo_extCompile project(path: ':extension-opus')
demo_extCompile project(path: ':extension-flac')
demo_extCompile project(path: ':extension-vp9')
} }
...@@ -18,7 +18,6 @@ package com.google.android.exoplayer.demo; ...@@ -18,7 +18,6 @@ package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo; import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
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.MediaCodecTrackRenderer.DecoderInitializationException;
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;
...@@ -182,7 +181,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener ...@@ -182,7 +181,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
} }
@Override @Override
public void onDecoderInitializationError(DecoderInitializationException e) { public void onDecoderInitializationError(Exception e) {
printInternalError("decoderInitializationError", e); printInternalError("decoderInitializationError", e);
} }
......
...@@ -16,12 +16,14 @@ ...@@ -16,12 +16,14 @@
package com.google.android.exoplayer.demo; 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.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.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.TrackGroupArray;
import com.google.android.exoplayer.demo.player.DemoPlayer; import com.google.android.exoplayer.demo.player.DemoPlayer;
import com.google.android.exoplayer.demo.player.SourceBuilder; import com.google.android.exoplayer.demo.player.SourceBuilder;
import com.google.android.exoplayer.demo.ui.TrackSelectionHelper; import com.google.android.exoplayer.demo.ui.TrackSelectionHelper;
...@@ -58,6 +60,7 @@ import android.view.View.OnKeyListener; ...@@ -58,6 +60,7 @@ import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener; import android.view.View.OnTouchListener;
import android.view.accessibility.CaptioningManager; import android.view.accessibility.CaptioningManager;
import android.widget.Button; import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.MediaController; import android.widget.MediaController;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
...@@ -77,6 +80,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -77,6 +80,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
public static final String CONTENT_ID_EXTRA = "content_id"; public static final String CONTENT_ID_EXTRA = "content_id";
public static final String CONTENT_TYPE_EXTRA = "content_type"; public static final String CONTENT_TYPE_EXTRA = "content_type";
public static final String PROVIDER_EXTRA = "provider"; public static final String PROVIDER_EXTRA = "provider";
public static final String USE_EXTENSION_DECODERS = "use_extension_decoders";
// For use when launching the demo app using adb. // For use when launching the demo app using adb.
private static final String CONTENT_EXT_EXTRA = "type"; private static final String CONTENT_EXT_EXTRA = "type";
...@@ -84,6 +88,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -84,6 +88,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
private static final String TAG = "PlayerActivity"; private static final String TAG = "PlayerActivity";
private static final CookieManager defaultCookieManager; private static final CookieManager defaultCookieManager;
static { static {
defaultCookieManager = new CookieManager(); defaultCookieManager = new CookieManager();
defaultCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); defaultCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
...@@ -91,16 +96,13 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -91,16 +96,13 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
private EventLogger eventLogger; private EventLogger eventLogger;
private MediaController mediaController; private MediaController mediaController;
private View debugRootView; private LinearLayout debugRootView;
private View shutterView; private View shutterView;
private AspectRatioFrameLayout videoFrame; private AspectRatioFrameLayout videoFrame;
private SurfaceView surfaceView; private SurfaceView surfaceView;
private TextView debugTextView; private TextView debugTextView;
private TextView playerStateTextView; private TextView playerStateTextView;
private SubtitleLayout subtitleLayout; private SubtitleLayout subtitleLayout;
private Button videoButton;
private Button audioButton;
private Button textButton;
private Button retryButton; private Button retryButton;
private DataSourceFactory dataSourceFactory; private DataSourceFactory dataSourceFactory;
...@@ -141,7 +143,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -141,7 +143,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
}); });
shutterView = findViewById(R.id.shutter); shutterView = findViewById(R.id.shutter);
debugRootView = findViewById(R.id.controls_root); debugRootView = (LinearLayout) findViewById(R.id.controls_root);
videoFrame = (AspectRatioFrameLayout) findViewById(R.id.video_frame); videoFrame = (AspectRatioFrameLayout) findViewById(R.id.video_frame);
surfaceView = (SurfaceView) findViewById(R.id.surface_view); surfaceView = (SurfaceView) findViewById(R.id.surface_view);
...@@ -155,9 +157,6 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -155,9 +157,6 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
mediaController.setAnchorView(root); mediaController.setAnchorView(root);
retryButton = (Button) findViewById(R.id.retry_button); retryButton = (Button) findViewById(R.id.retry_button);
retryButton.setOnClickListener(this); retryButton.setOnClickListener(this);
videoButton = (Button) findViewById(R.id.video_controls);
audioButton = (Button) findViewById(R.id.audio_controls);
textButton = (Button) findViewById(R.id.text_controls);
CookieHandler currentHandler = CookieHandler.getDefault(); CookieHandler currentHandler = CookieHandler.getDefault();
if (currentHandler != defaultCookieManager) { if (currentHandler != defaultCookieManager) {
...@@ -212,6 +211,9 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -212,6 +211,9 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
public void onClick(View view) { public void onClick(View view) {
if (view == retryButton) { if (view == retryButton) {
initializePlayer(); initializePlayer();
} else if (view.getParent() == debugRootView) {
trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(),
player.getTrackInfo(), (int) view.getTag());
} }
} }
...@@ -277,7 +279,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -277,7 +279,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
private void initializePlayer() { private void initializePlayer() {
if (player == null) { if (player == null) {
player = new DemoPlayer(this); boolean useExtensionDecoders = getIntent().getBooleanExtra(USE_EXTENSION_DECODERS, false);
player = new DemoPlayer(this, useExtensionDecoders);
player.addListener(this); player.addListener(this);
player.setCaptionListener(this); player.setCaptionListener(this);
player.setMetadataListener(this); player.setMetadataListener(this);
...@@ -408,33 +411,34 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -408,33 +411,34 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
// User controls // User controls
private void updateButtonVisibilities() { private void updateButtonVisibilities() {
debugRootView.removeAllViews();
retryButton.setVisibility(playerNeedsSource ? View.VISIBLE : View.GONE); retryButton.setVisibility(playerNeedsSource ? View.VISIBLE : View.GONE);
videoButton.setVisibility(haveTracks(DemoPlayer.RENDERER_INDEX_VIDEO) ? View.VISIBLE debugRootView.addView(retryButton);
: View.GONE);
audioButton.setVisibility(haveTracks(DemoPlayer.RENDERER_INDEX_AUDIO) ? View.VISIBLE
: View.GONE);
textButton.setVisibility(haveTracks(DemoPlayer.RENDERER_INDEX_TEXT) ? View.VISIBLE
: View.GONE);
}
private boolean haveTracks(int rendererIndex) { TrackInfo trackInfo;
TrackInfo trackInfo = player == null ? null : player.getTrackInfo(); if (player == null || (trackInfo = player.getTrackInfo()) == null) {
return trackInfo != null && trackInfo.getTrackGroups(rendererIndex).length != 0; return;
} }
public void showVideoPopup(@SuppressWarnings("unused") View v) { int rendererCount = trackInfo.rendererCount;
trackSelectionHelper.showSelectionDialog(this, R.string.video, player.getTrackInfo(), for (int i = 0; i < rendererCount; i++) {
DemoPlayer.RENDERER_INDEX_VIDEO); TrackGroupArray trackGroups = trackInfo.getTrackGroups(i);
if (trackGroups.length != 0) {
Button button = new Button(this);
int label;
switch (player.getRendererType(i)) {
case C.TRACK_TYPE_AUDIO: label = R.string.audio; break;
case C.TRACK_TYPE_VIDEO: label = R.string.video; break;
case C.TRACK_TYPE_TEXT: label = R.string.text; break;
default: continue;
}
button.setText(label);
button.setTag(i);
button.setOnClickListener(this);
debugRootView.addView(button);
} }
public void showAudioPopup(@SuppressWarnings("unused") View v) {
trackSelectionHelper.showSelectionDialog(this, R.string.audio, player.getTrackInfo(),
DemoPlayer.RENDERER_INDEX_AUDIO);
} }
public void showTextPopup(@SuppressWarnings("unused") View v) {
trackSelectionHelper.showSelectionDialog(this, R.string.text, player.getTrackInfo(),
DemoPlayer.RENDERER_INDEX_TEXT);
} }
private void toggleControlsVisibility() { private void toggleControlsVisibility() {
......
...@@ -75,6 +75,11 @@ public class SampleChooserActivity extends Activity { ...@@ -75,6 +75,11 @@ public class SampleChooserActivity extends Activity {
group = new SampleGroup("Misc"); group = new SampleGroup("Misc");
group.addAll(Samples.MISC); group.addAll(Samples.MISC);
sampleGroups.add(group); sampleGroups.add(group);
group = new SampleGroup("Extensions");
group.addAll(Samples.VP9_EXTENSION_SAMPLES);
group.addAll(Samples.VP9_OPUS_EXTENSION_SAMPLES);
sampleGroups.add(group);
ExpandableListView sampleList = (ExpandableListView) findViewById(R.id.sample_list); ExpandableListView sampleList = (ExpandableListView) findViewById(R.id.sample_list);
sampleList.setAdapter(new SampleAdapter(this, sampleGroups)); sampleList.setAdapter(new SampleAdapter(this, sampleGroups));
sampleList.setOnChildClickListener(new OnChildClickListener() { sampleList.setOnChildClickListener(new OnChildClickListener() {
...@@ -92,7 +97,8 @@ public class SampleChooserActivity extends Activity { ...@@ -92,7 +97,8 @@ public class SampleChooserActivity extends Activity {
.setData(Uri.parse(sample.uri)) .setData(Uri.parse(sample.uri))
.putExtra(PlayerActivity.CONTENT_ID_EXTRA, sample.contentId) .putExtra(PlayerActivity.CONTENT_ID_EXTRA, sample.contentId)
.putExtra(PlayerActivity.CONTENT_TYPE_EXTRA, sample.type) .putExtra(PlayerActivity.CONTENT_TYPE_EXTRA, sample.type)
.putExtra(PlayerActivity.PROVIDER_EXTRA, sample.provider); .putExtra(PlayerActivity.PROVIDER_EXTRA, sample.provider)
.putExtra(PlayerActivity.USE_EXTENSION_DECODERS, sample.useExtensionDecoders);
startActivity(mpdIntent); startActivity(mpdIntent);
} }
......
...@@ -22,7 +22,7 @@ import java.util.Locale; ...@@ -22,7 +22,7 @@ import java.util.Locale;
/** /**
* Holds statically defined sample definitions. * Holds statically defined sample definitions.
*/ */
/* package */ class Samples { /* package */ final class Samples {
public static class Sample { public static class Sample {
...@@ -31,17 +31,29 @@ import java.util.Locale; ...@@ -31,17 +31,29 @@ import java.util.Locale;
public final String provider; public final String provider;
public final String uri; public final String uri;
public final int type; public final int type;
public final boolean useExtensionDecoders;
public Sample(String name, String uri, int type) { public Sample(String name, String uri, int type) {
this(name, name.toLowerCase(Locale.US).replaceAll("\\s", ""), "", uri, type); this(name, uri, type, false);
}
public Sample(String name, String uri, int type, boolean useExtensionDecoders) {
this(name, name.toLowerCase(Locale.US).replaceAll("\\s", ""), "", uri, type,
useExtensionDecoders);
} }
public Sample(String name, String contentId, String provider, String uri, int type) { public Sample(String name, String contentId, String provider, String uri, int type) {
this(name, contentId, provider, uri, type, false);
}
public Sample(String name, String contentId, String provider, String uri, int type,
boolean useExtensionDecoders) {
this.name = name; this.name = name;
this.contentId = contentId; this.contentId = contentId;
this.provider = provider; this.provider = provider;
this.uri = uri; this.uri = uri;
this.type = type; this.type = type;
this.useExtensionDecoders = useExtensionDecoders;
} }
} }
...@@ -250,6 +262,21 @@ import java.util.Locale; ...@@ -250,6 +262,21 @@ import java.util.Locale;
"http://vod.leasewebcdn.com/bbb.flv?ri=1024&rs=150&start=0", Util.TYPE_OTHER), "http://vod.leasewebcdn.com/bbb.flv?ri=1024&rs=150&start=0", Util.TYPE_OTHER),
}; };
public static final Sample[] VP9_EXTENSION_SAMPLES = new Sample[] {
new Sample("Google Glass DASH - VP9 Only",
"http://demos.webmproject.org/dash/201410/vp9_glass/manifest_vp9.mpd",
Util.TYPE_DASH, true),
new Sample("Google Glass DASH - VP9 and Vorbis",
"http://demos.webmproject.org/dash/201410/vp9_glass/manifest_vp9_vorbis.mpd",
Util.TYPE_DASH, true),
};
public static final Sample[] VP9_OPUS_EXTENSION_SAMPLES = new Sample[] {
new Sample("Google Glass DASH - VP9 and Opus",
"http://demos.webmproject.org/dash/201410/vp9_glass/manifest_vp9_opus.mpd",
Util.TYPE_DASH, true),
};
private Samples() {} private Samples() {}
} }
...@@ -24,11 +24,11 @@ import com.google.android.exoplayer.ExoPlayer; ...@@ -24,11 +24,11 @@ import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector; import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.SingleSampleSource; import com.google.android.exoplayer.SingleSampleSource;
import com.google.android.exoplayer.TrackRenderer; 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.audio.AudioTrack; import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener; import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
...@@ -51,9 +51,12 @@ import android.media.AudioManager; ...@@ -51,9 +51,12 @@ import android.media.AudioManager;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCodec.CryptoException; import android.media.MediaCodec.CryptoException;
import android.os.Handler; import android.os.Handler;
import android.util.Log;
import android.view.Surface; import android.view.Surface;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
...@@ -91,7 +94,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -91,7 +94,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
void onAudioTrackInitializationError(AudioTrack.InitializationException e); void onAudioTrackInitializationError(AudioTrack.InitializationException e);
void onAudioTrackWriteError(AudioTrack.WriteException e); void onAudioTrackWriteError(AudioTrack.WriteException e);
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
void onDecoderInitializationError(DecoderInitializationException e); void onDecoderInitializationError(Exception e);
void onCryptoError(CryptoException e); void onCryptoError(CryptoException e);
void onLoadError(int sourceId, IOException e); void onLoadError(int sourceId, IOException e);
void onDrmSessionManagerError(Exception e); void onDrmSessionManagerError(Exception e);
...@@ -133,16 +136,12 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -133,16 +136,12 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
public static final int STATE_READY = ExoPlayer.STATE_READY; public static final int STATE_READY = ExoPlayer.STATE_READY;
public static final int STATE_ENDED = ExoPlayer.STATE_ENDED; public static final int STATE_ENDED = ExoPlayer.STATE_ENDED;
public static final int RENDERER_COUNT = 4; private static final String TAG = "DemoPlayer";
public static final int RENDERER_INDEX_VIDEO = 0;
public static final int RENDERER_INDEX_AUDIO = 1;
public static final int RENDERER_INDEX_TEXT = 2;
public static final int RENDERER_INDEX_METADATA = 3;
private final ExoPlayer player; private final ExoPlayer player;
private final DefaultTrackSelector trackSelector; private final DefaultTrackSelector trackSelector;
private final BandwidthMeter bandwidthMeter; private final BandwidthMeter bandwidthMeter;
private final MediaCodecVideoTrackRenderer videoRenderer; private final TrackRenderer[] renderers;
private final PlayerControl playerControl; private final PlayerControl playerControl;
private final Handler mainHandler; private final Handler mainHandler;
private final CopyOnWriteArrayList<Listener> listeners; private final CopyOnWriteArrayList<Listener> listeners;
...@@ -155,23 +154,20 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -155,23 +154,20 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
private Id3MetadataListener id3MetadataListener; private Id3MetadataListener id3MetadataListener;
private InternalErrorListener internalErrorListener; private InternalErrorListener internalErrorListener;
private InfoListener infoListener; private InfoListener infoListener;
private CodecCounters videoCodecCounters;
public DemoPlayer(Context context) { public DemoPlayer(Context context, boolean useExtensionDecoders) {
mainHandler = new Handler(); mainHandler = new Handler();
bandwidthMeter = new DefaultBandwidthMeter(); bandwidthMeter = new DefaultBandwidthMeter();
listeners = new CopyOnWriteArrayList<>(); listeners = new CopyOnWriteArrayList<>();
// Build the renderers. // Build the renderers.
videoRenderer = new MediaCodecVideoTrackRenderer(context, MediaCodecSelector.DEFAULT, ArrayList<TrackRenderer> renderersList = new ArrayList<>();
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, this, 50); buildRenderers(context, renderersList);
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT, null, if (useExtensionDecoders) {
true, mainHandler, this, AudioCapabilities.getCapabilities(context), buildExtensionRenderers(renderersList);
AudioManager.STREAM_MUSIC); }
TrackRenderer textRenderer = new TextTrackRenderer(this, mainHandler.getLooper()); renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]);
MetadataTrackRenderer<List<Id3Frame>> id3Renderer = new MetadataTrackRenderer<>(new Id3Parser(),
this, mainHandler.getLooper());
TrackRenderer[] renderers = new TrackRenderer[] {videoRenderer, audioRenderer, textRenderer,
id3Renderer};
// Build the player and associated objects. // Build the player and associated objects.
trackSelector = new DefaultTrackSelector(mainHandler, this); trackSelector = new DefaultTrackSelector(mainHandler, this);
...@@ -263,7 +259,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -263,7 +259,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
@Override @Override
public CodecCounters getCodecCounters() { public CodecCounters getCodecCounters() {
return videoRenderer.codecCounters; return videoCodecCounters;
} }
@Override @Override
...@@ -358,7 +354,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -358,7 +354,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
} }
@Override @Override
public void onDecoderInitializationError(DecoderInitializationException e) { public void onDecoderInitializationError(Exception e) {
if (internalErrorListener != null) { if (internalErrorListener != null) {
internalErrorListener.onDecoderInitializationError(e); internalErrorListener.onDecoderInitializationError(e);
} }
...@@ -386,6 +382,11 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -386,6 +382,11 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
} }
@Override @Override
public void onAudioCodecCounters(CodecCounters counters) {
// do nothing
}
@Override
public void onCryptoError(CryptoException e) { public void onCryptoError(CryptoException e) {
if (internalErrorListener != null) { if (internalErrorListener != null) {
internalErrorListener.onCryptoError(e); internalErrorListener.onCryptoError(e);
...@@ -432,6 +433,11 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -432,6 +433,11 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
} }
@Override @Override
public void onVideoCodecCounters(CodecCounters counters) {
this.videoCodecCounters = counters;
}
@Override
public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
long mediaStartTimeMs, long mediaEndTimeMs) { long mediaStartTimeMs, long mediaEndTimeMs) {
if (infoListener != null) { if (infoListener != null) {
...@@ -459,14 +465,70 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -459,14 +465,70 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
// Do nothing. // Do nothing.
} }
public int getRendererType(int index) {
return renderers[index].getTrackType();
}
private void pushSurface(boolean blockForSurfacePush) { private void pushSurface(boolean blockForSurfacePush) {
for (TrackRenderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) {
if (blockForSurfacePush) { if (blockForSurfacePush) {
player.blockingSendMessage( player.blockingSendMessage(renderer, C.MSG_SET_SURFACE, surface);
videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface);
} else { } else {
player.sendMessage( player.sendMessage(renderer, C.MSG_SET_SURFACE, surface);
videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); }
}
} }
} }
private void buildRenderers(Context context, ArrayList<TrackRenderer> renderersList) {
MediaCodecVideoTrackRenderer videoRenderer =
new MediaCodecVideoTrackRenderer(context, MediaCodecSelector.DEFAULT,
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, this, 50);
renderersList.add(videoRenderer);
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT, null,
true, mainHandler, this, AudioCapabilities.getCapabilities(context),
AudioManager.STREAM_MUSIC);
renderersList.add(audioRenderer);
TrackRenderer textRenderer = new TextTrackRenderer(this, mainHandler.getLooper());
renderersList.add(textRenderer);
MetadataTrackRenderer<List<Id3Frame>> id3Renderer = new MetadataTrackRenderer<>(new Id3Parser(),
this, mainHandler.getLooper());
renderersList.add(id3Renderer);
}
private void buildExtensionRenderers(ArrayList<TrackRenderer> renderersList) {
// Load extension renderers using reflection so that demo app doesn't depend on them.
// Class.forName(<class name>) appears for each renderer so that automated tools like proguard
// can detect the use of reflection (see http://proguard.sourceforge.net/FAQ.html#forname).
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer");
Constructor<?> constructor = clazz.getConstructor(boolean.class, Handler.class,
VideoTrackRendererEventListener.class, int.class);
Object renderer = constructor.newInstance(true, mainHandler, this, 50);
renderersList.add(0, (TrackRenderer) renderer);
} catch (Exception e) {
Log.i(TAG, "can't load LibvpxVideoTrackRenderer.");
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer.ext.opus.LibopusAudioTrackRenderer");
renderersList.add(1, (TrackRenderer) clazz.newInstance());
} catch (Exception e) {
Log.i(TAG, "can't load LibopusAudioTrackRenderer.");
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer.ext.flac.LibflacAudioTrackRenderer");
renderersList.add(2, (TrackRenderer) clazz.newInstance());
} catch (Exception e) {
Log.i(TAG, "can't load LibflacAudioTrackRenderer.");
}
}
} }
...@@ -68,11 +68,11 @@ public class TrackSelectionHelper implements View.OnClickListener, DialogInterfa ...@@ -68,11 +68,11 @@ public class TrackSelectionHelper implements View.OnClickListener, DialogInterfa
* Shows the selection dialog for a given renderer. * Shows the selection dialog for a given renderer.
* *
* @param activity The parent activity. * @param activity The parent activity.
* @param titleId The dialog's title. * @param title The dialog's title.
* @param trackInfo The current track information. * @param trackInfo The current track information.
* @param rendererIndex The index of the renderer. * @param rendererIndex The index of the renderer.
*/ */
public void showSelectionDialog(Activity activity, int titleId, TrackInfo trackInfo, public void showSelectionDialog(Activity activity, CharSequence title, TrackInfo trackInfo,
int rendererIndex) { int rendererIndex) {
this.trackInfo = trackInfo; this.trackInfo = trackInfo;
this.rendererIndex = rendererIndex; this.rendererIndex = rendererIndex;
...@@ -88,7 +88,7 @@ public class TrackSelectionHelper implements View.OnClickListener, DialogInterfa ...@@ -88,7 +88,7 @@ public class TrackSelectionHelper implements View.OnClickListener, DialogInterfa
? trackInfo.getTrackSelection(rendererIndex) : null; ? trackInfo.getTrackSelection(rendererIndex) : null;
AlertDialog.Builder builder = new AlertDialog.Builder(activity); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(titleId) builder.setTitle(title)
.setView(buildView(LayoutInflater.from(builder.getContext()))) .setView(buildView(LayoutInflater.from(builder.getContext())))
.setPositiveButton(android.R.string.ok, this) .setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
......
...@@ -70,30 +70,6 @@ ...@@ -70,30 +70,6 @@
android:orientation="horizontal" android:orientation="horizontal"
android:visibility="gone"> android:visibility="gone">
<Button android:id="@+id/video_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video"
style="@style/DemoButton"
android:visibility="gone"
android:onClick="showVideoPopup"/>
<Button android:id="@+id/audio_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio"
style="@style/DemoButton"
android:visibility="gone"
android:onClick="showAudioPopup"/>
<Button android:id="@+id/text_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text"
style="@style/DemoButton"
android:visibility="gone"
android:onClick="showTextPopup"/>
<Button android:id="@+id/retry_button" <Button android:id="@+id/retry_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.ext.flac; package com.google.android.exoplayer.ext.flac;
import com.google.android.exoplayer.AudioTrackRendererEventListener;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.extensions.AudioDecoderTrackRenderer; import com.google.android.exoplayer.util.extensions.AudioDecoderTrackRenderer;
...@@ -47,7 +48,7 @@ public class LibflacAudioTrackRenderer extends AudioDecoderTrackRenderer { ...@@ -47,7 +48,7 @@ public class LibflacAudioTrackRenderer extends AudioDecoderTrackRenderer {
* @param eventListener A listener of events. 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.
*/ */
public LibflacAudioTrackRenderer(Handler eventHandler, public LibflacAudioTrackRenderer(Handler eventHandler,
AudioDecoderTrackRenderer.EventListener eventListener) { AudioTrackRendererEventListener eventListener) {
super(eventHandler, eventListener); super(eventHandler, eventListener);
} }
@Override @Override
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.ext.opus; package com.google.android.exoplayer.ext.opus;
import com.google.android.exoplayer.AudioTrackRendererEventListener;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.extensions.AudioDecoderTrackRenderer; import com.google.android.exoplayer.util.extensions.AudioDecoderTrackRenderer;
...@@ -54,7 +55,8 @@ public final class LibopusAudioTrackRenderer extends AudioDecoderTrackRenderer { ...@@ -54,7 +55,8 @@ public final class LibopusAudioTrackRenderer extends AudioDecoderTrackRenderer {
* null if delivery of events is not required. * 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 eventListener A listener of events. May be null if delivery of events is not required.
*/ */
public LibopusAudioTrackRenderer(Handler eventHandler, EventListener eventListener) { public LibopusAudioTrackRenderer(Handler eventHandler,
AudioTrackRendererEventListener eventListener) {
super(eventHandler, eventListener); super(eventHandler, eventListener);
} }
......
...@@ -24,6 +24,7 @@ import com.google.android.exoplayer.Format; ...@@ -24,6 +24,7 @@ import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.VideoTrackRendererEventListener;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import android.graphics.Bitmap; import android.graphics.Bitmap;
...@@ -38,67 +39,6 @@ import android.view.Surface; ...@@ -38,67 +39,6 @@ import android.view.Surface;
public final class LibvpxVideoTrackRenderer extends TrackRenderer { public final class LibvpxVideoTrackRenderer extends TrackRenderer {
/** /**
* Interface definition for a callback to be notified of {@link LibvpxVideoTrackRenderer} events.
*/
public interface EventListener {
/**
* Invoked to report the number of frames dropped by the renderer. Dropped frames are reported
* whenever the renderer is stopped having dropped frames, and optionally, whenever the count
* reaches a specified threshold whilst the renderer is started.
*
* @param count The number of dropped frames.
* @param elapsed The duration in milliseconds over which the frames were dropped. This
* duration is timed from when the renderer was started or from when dropped frames were
* last reported (whichever was more recent), and not from when the first of the reported
* drops occurred.
*/
void onDroppedFrames(int count, long elapsed);
/**
* Invoked each time there's a change in the size of the video being rendered.
*
* @param width The video width in pixels.
* @param height The video height in pixels.
*/
void onVideoSizeChanged(int width, int height);
/**
* Invoked when a frame is rendered to a surface for the first time following that surface
* having been set as the target for the renderer.
*
* @param surface The surface to which a first frame has been rendered.
*/
void onDrawnToSurface(Surface surface);
/**
* Invoked when one of the following happens: libvpx initialization failure, decoder error,
* renderer error.
*
* @param e The corresponding exception.
*/
void onDecoderError(VpxDecoderException e);
/**
* Invoked when a decoder is successfully created.
*
* @param decoderName The decoder that was configured and created.
* @param elapsedRealtimeMs {@code elapsedRealtime} timestamp of when the initialization
* finished.
* @param initializationDurationMs Amount of time taken to initialize the decoder.
*/
void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
long initializationDurationMs);
}
/**
* The type of a message that can be passed to an instance of this class via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
* should be the target {@link Surface}, or null.
*/
public static final int MSG_SET_SURFACE = 1;
/**
* The type of a message that can be passed to an instance of this class via * The type of a message that can be passed to an instance of this class via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object * {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
* should be the target {@link VpxOutputBufferRenderer}, or null. * should be the target {@link VpxOutputBufferRenderer}, or null.
...@@ -117,7 +57,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -117,7 +57,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
private final boolean scaleToFit; private final boolean scaleToFit;
private final Handler eventHandler; private final Handler eventHandler;
private final EventListener eventListener; private final VideoTrackRendererEventListener eventListener;
private final int maxDroppedFrameCountToNotify; private final int maxDroppedFrameCountToNotify;
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
...@@ -157,10 +97,10 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -157,10 +97,10 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
* null if delivery of events is not required. * 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 eventListener A listener of events. May be null if delivery of events is not required.
* @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between * @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between
* invocations of {@link EventListener#onDroppedFrames(int, long)}. * invocations of {@link VideoTrackRendererEventListener#onDroppedFrames(int, long)}.
*/ */
public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler, public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler,
EventListener eventListener, int maxDroppedFrameCountToNotify) { VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
this.scaleToFit = scaleToFit; this.scaleToFit = scaleToFit;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
...@@ -186,7 +126,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -186,7 +126,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected int getTrackType() { public int getTrackType() {
return C.TRACK_TYPE_VIDEO; return C.TRACK_TYPE_VIDEO;
} }
...@@ -220,7 +160,6 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -220,7 +160,6 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
while (processOutputBuffer(positionUs)) {} while (processOutputBuffer(positionUs)) {}
while (feedInputBuffer()) {} while (feedInputBuffer()) {}
} catch (VpxDecoderException e) { } catch (VpxDecoderException e) {
notifyDecoderError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
codecCounters.ensureUpdated(); codecCounters.ensureUpdated();
...@@ -381,6 +320,11 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -381,6 +320,11 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
notifyVideoCodecCounters();
}
@Override
protected void onStarted() { protected void onStarted() {
droppedFrameCount = 0; droppedFrameCount = 0;
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime(); droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
...@@ -418,7 +362,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -418,7 +362,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
@Override @Override
public void handleMessage(int messageType, Object message) throws ExoPlaybackException { public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
if (messageType == MSG_SET_SURFACE) { if (messageType == C.MSG_SET_SURFACE) {
setSurface((Surface) message); setSurface((Surface) message);
} else if (messageType == MSG_SET_OUTPUT_BUFFER_RENDERER) { } else if (messageType == MSG_SET_OUTPUT_BUFFER_RENDERER) {
setOutputBufferRenderer((VpxOutputBufferRenderer) message); setOutputBufferRenderer((VpxOutputBufferRenderer) message);
...@@ -462,7 +406,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -462,7 +406,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
eventHandler.post(new Runnable() { eventHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
eventListener.onVideoSizeChanged(outputBuffer.width, outputBuffer.height); eventListener.onVideoSizeChanged(outputBuffer.width, outputBuffer.height, 0, 1);
} }
}); });
} }
...@@ -496,25 +440,25 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -496,25 +440,25 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
} }
} }
private void notifyDecoderError(final VpxDecoderException e) { private void notifyDecoderInitialized(
final long startElapsedRealtimeMs, final long finishElapsedRealtimeMs) {
if (eventHandler != null && eventListener != null) { if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() { eventHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
eventListener.onDecoderError(e); eventListener.onDecoderInitialized("libvpx" + getLibvpxVersion(),
finishElapsedRealtimeMs, finishElapsedRealtimeMs - startElapsedRealtimeMs);
} }
}); });
} }
} }
private void notifyDecoderInitialized( private void notifyVideoCodecCounters() {
final long startElapsedRealtimeMs, final long finishElapsedRealtimeMs) {
if (eventHandler != null && eventListener != null) { if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() { eventHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
eventListener.onDecoderInitialized("libvpx" + getLibvpxVersion(), eventListener.onVideoCodecCounters(codecCounters);
finishElapsedRealtimeMs, finishElapsedRealtimeMs - startElapsedRealtimeMs);
} }
}); });
} }
......
/*
* Copyright (C) 2016 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.audio.AudioTrack;
/**
* Optional interface definition for a callback to be notified of audio {@link TrackRenderer}
* events.
*/
public interface AudioTrackRendererEventListener extends TrackRendererEventListener {
/**
* Invoked when an {@link AudioTrack} fails to initialize.
*
* @param e The corresponding exception.
*/
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
/**
* Invoked when an {@link AudioTrack} write fails.
*
* @param e The corresponding exception.
*/
void onAudioTrackWriteError(AudioTrack.WriteException e);
/**
* Invoked when an {@link AudioTrack} underrun occurs.
*
* @param bufferSize The size of the {@link AudioTrack}'s buffer, in bytes.
* @param bufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds, if it is
* configured for PCM output. -1 if it is configured for passthrough output, as the buffered
* media can have a variable bitrate so the duration may be unknown.
* @param elapsedSinceLastFeedMs The time since the {@link AudioTrack} was last fed data.
*/
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
/**
* Invoked to pass the codec counters when the renderer is enabled.
*
* @param counters CodecCounters object used by the renderer.
*/
void onAudioCodecCounters(CodecCounters counters);
}
...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.util.Util; ...@@ -19,6 +19,7 @@ import com.google.android.exoplayer.util.Util;
import android.media.AudioFormat; import android.media.AudioFormat;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.view.Surface;
import java.util.UUID; import java.util.UUID;
...@@ -183,6 +184,13 @@ public final class C { ...@@ -183,6 +184,13 @@ public final class C {
*/ */
public static final UUID UUID_NIL = new UUID(0L, 0L); public static final UUID UUID_NIL = new UUID(0L, 0L);
/**
* The type of a message that can be passed to an video {@link TrackRenderer} via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
* should be the target {@link Surface}, or null.
*/
public static final int MSG_SET_SURFACE = 1;
private C() {} private C() {}
} }
...@@ -44,33 +44,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -44,33 +44,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
* Interface definition for a callback to be notified of {@link MediaCodecAudioTrackRenderer} * Interface definition for a callback to be notified of {@link MediaCodecAudioTrackRenderer}
* events. * events.
*/ */
public interface EventListener extends MediaCodecTrackRenderer.EventListener { public interface EventListener extends MediaCodecTrackRenderer.EventListener,
AudioTrackRendererEventListener {
/** // No extra methods
* Invoked when an {@link AudioTrack} fails to initialize.
*
* @param e The corresponding exception.
*/
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
/**
* Invoked when an {@link AudioTrack} write fails.
*
* @param e The corresponding exception.
*/
void onAudioTrackWriteError(AudioTrack.WriteException e);
/**
* Invoked when an {@link AudioTrack} underrun occurs.
*
* @param bufferSize The size of the {@link AudioTrack}'s buffer, in bytes.
* @param bufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds, if it is
* configured for PCM output. -1 if it is configured for passthrough output, as the buffered
* media can have a variable bitrate so the duration may be unknown.
* @param elapsedSinceLastFeedMs The time since the {@link AudioTrack} was last fed data.
*/
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
} }
/** /**
...@@ -182,7 +158,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -182,7 +158,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
@Override @Override
protected int getTrackType() { public int getTrackType() {
return C.TRACK_TYPE_AUDIO; return C.TRACK_TYPE_AUDIO;
} }
...@@ -281,6 +257,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -281,6 +257,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
super.onEnabled(formats, joining);
notifyAudioCodecCounters();
}
@Override
protected void onStarted() { protected void onStarted() {
super.onStarted(); super.onStarted();
audioTrack.play(); audioTrack.play();
...@@ -293,6 +275,16 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -293,6 +275,16 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
@Override @Override
protected void onDisabled() {
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
try {
audioTrack.release();
} finally {
super.onDisabled();
}
}
@Override
protected boolean isEnded() { protected boolean isEnded() {
return super.isEnded() && !audioTrack.hasPendingData(); return super.isEnded() && !audioTrack.hasPendingData();
} }
...@@ -314,16 +306,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -314,16 +306,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
@Override @Override
protected void onDisabled() {
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
try {
audioTrack.release();
} finally {
super.onDisabled();
}
}
@Override
protected void reset(long positionUs) throws ExoPlaybackException { protected void reset(long positionUs) throws ExoPlaybackException {
super.reset(positionUs); super.reset(positionUs);
audioTrack.reset(); audioTrack.reset();
...@@ -460,4 +442,15 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -460,4 +442,15 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
} }
private void notifyAudioCodecCounters() {
if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() {
@Override
public void run() {
eventListener.onAudioCodecCounters(codecCounters);
}
});
}
}
} }
...@@ -44,14 +44,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -44,14 +44,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
/** /**
* Interface definition for a callback to be notified of {@link MediaCodecTrackRenderer} events. * Interface definition for a callback to be notified of {@link MediaCodecTrackRenderer} events.
*/ */
public interface EventListener { public interface EventListener extends TrackRendererEventListener {
/**
* Invoked when a decoder fails to initialize.
*
* @param e The corresponding exception.
*/
void onDecoderInitializationError(DecoderInitializationException e);
/** /**
* Invoked when a decoder operation raises a {@link CryptoException}. * Invoked when a decoder operation raises a {@link CryptoException}.
...@@ -60,17 +53,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -60,17 +53,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
*/ */
void onCryptoError(CryptoException e); void onCryptoError(CryptoException e);
/**
* Invoked when a decoder is successfully created.
*
* @param decoderName The decoder that was configured and created.
* @param elapsedRealtimeMs {@code elapsedRealtime} timestamp of when the initialization
* finished.
* @param initializationDurationMs Amount of time taken to initialize the decoder.
*/
void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
long initializationDurationMs);
} }
/** /**
......
...@@ -31,7 +31,6 @@ import android.os.Handler; ...@@ -31,7 +31,6 @@ import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
import android.view.TextureView;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -45,48 +44,9 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -45,48 +44,9 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
* Interface definition for a callback to be notified of {@link MediaCodecVideoTrackRenderer} * Interface definition for a callback to be notified of {@link MediaCodecVideoTrackRenderer}
* events. * events.
*/ */
public interface EventListener extends MediaCodecTrackRenderer.EventListener { public interface EventListener extends MediaCodecTrackRenderer.EventListener,
VideoTrackRendererEventListener {
/** // No extra methods
* Invoked to report the number of frames dropped by the renderer. Dropped frames are reported
* whenever the renderer is stopped having dropped frames, and optionally, whenever the count
* reaches a specified threshold whilst the renderer is started.
*
* @param count The number of dropped frames.
* @param elapsed The duration in milliseconds over which the frames were dropped. This
* duration is timed from when the renderer was started or from when dropped frames were
* last reported (whichever was more recent), and not from when the first of the reported
* drops occurred.
*/
void onDroppedFrames(int count, long elapsed);
/**
* Invoked each time there's a change in the size of the video being rendered.
*
* @param width The video width in pixels.
* @param height The video height in pixels.
* @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise
* rotation in degrees that the application should apply for the video for it to be rendered
* in the correct orientation. This value will always be zero on API levels 21 and above,
* since the renderer will apply all necessary rotations internally. On earlier API levels
* this is not possible. Applications that use {@link TextureView} can apply the rotation by
* calling {@link TextureView#setTransform}. Applications that do not expect to encounter
* rotated videos can safely ignore this parameter.
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case
* of square pixels this will be equal to 1.0. Different values are indicative of anamorphic
* content.
*/
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio);
/**
* Invoked when a frame is rendered to a surface for the first time following that surface
* having been set as the target for the renderer.
*
* @param surface The surface to which a first frame has been rendered.
*/
void onDrawnToSurface(Surface surface);
} }
private static final String TAG = "MediaCodecVideoTrackRenderer"; private static final String TAG = "MediaCodecVideoTrackRenderer";
...@@ -98,13 +58,6 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -98,13 +58,6 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
private static final String KEY_CROP_BOTTOM = "crop-bottom"; private static final String KEY_CROP_BOTTOM = "crop-bottom";
private static final String KEY_CROP_TOP = "crop-top"; private static final String KEY_CROP_TOP = "crop-top";
/**
* The type of a message that can be passed to an instance of this class via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
* should be the target {@link Surface}, or null.
*/
public static final int MSG_SET_SURFACE = 1;
private final VideoFrameReleaseTimeHelper frameReleaseTimeHelper; private final VideoFrameReleaseTimeHelper frameReleaseTimeHelper;
private final EventListener eventListener; private final EventListener eventListener;
private final long allowedJoiningTimeMs; private final long allowedJoiningTimeMs;
...@@ -221,7 +174,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -221,7 +174,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
} }
@Override @Override
protected int getTrackType() { public int getTrackType() {
return C.TRACK_TYPE_VIDEO; return C.TRACK_TYPE_VIDEO;
} }
...@@ -285,6 +238,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -285,6 +238,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
joiningDeadlineMs = SystemClock.elapsedRealtime() + allowedJoiningTimeMs; joiningDeadlineMs = SystemClock.elapsedRealtime() + allowedJoiningTimeMs;
} }
frameReleaseTimeHelper.enable(); frameReleaseTimeHelper.enable();
notifyVideoCodecCounters();
} }
@Override @Override
...@@ -343,7 +297,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -343,7 +297,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
@Override @Override
public void handleMessage(int messageType, Object message) throws ExoPlaybackException { public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
if (messageType == MSG_SET_SURFACE) { if (messageType == C.MSG_SET_SURFACE) {
setSurface((Surface) message); setSurface((Surface) message);
} else { } else {
super.handleMessage(messageType, message); super.handleMessage(messageType, message);
...@@ -686,4 +640,15 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { ...@@ -686,4 +640,15 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
return Util.SDK_INT <= 22 && "foster".equals(Util.DEVICE) && "NVIDIA".equals(Util.MANUFACTURER); return Util.SDK_INT <= 22 && "foster".equals(Util.DEVICE) && "NVIDIA".equals(Util.MANUFACTURER);
} }
private void notifyVideoCodecCounters() {
if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() {
@Override
public void run() {
eventListener.onVideoCodecCounters(codecCounters);
}
});
}
}
} }
...@@ -301,7 +301,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent { ...@@ -301,7 +301,7 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
* *
* @return One of the TRACK_TYPE_* constants defined in {@link C}. * @return One of the TRACK_TYPE_* constants defined in {@link C}.
*/ */
protected abstract int getTrackType(); public abstract int getTrackType();
/** /**
* Returns the extent to which the renderer supports a given format. * Returns the extent to which the renderer supports a given format.
......
/*
* 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;
/**
* Optional interface definition for a callback to be notified of {@link TrackRenderer} events.
*/
public interface TrackRendererEventListener {
/**
* Invoked when a decoder fails to initialize.
*
* @param e The corresponding exception.
*/
void onDecoderInitializationError(Exception e);
/**
* Invoked when a decoder is successfully created.
*
* @param decoderName The decoder that was configured and created.
* @param elapsedRealtimeMs {@code elapsedRealtime} timestamp of when the initialization
* finished.
* @param initializationDurationMs Amount of time taken to initialize the decoder.
*/
void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
long initializationDurationMs);
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer;
import android.view.Surface;
import android.view.TextureView;
/**
* Optional interface definition for a callback to be notified of video {@link TrackRenderer}
* events.
*/
public interface VideoTrackRendererEventListener extends TrackRendererEventListener {
/**
* Invoked to report the number of frames dropped by the renderer. Dropped frames are reported
* whenever the renderer is stopped having dropped frames, and optionally, whenever the count
* reaches a specified threshold whilst the renderer is started.
*
* @param count The number of dropped frames.
* @param elapsed The duration in milliseconds over which the frames were dropped. This
* duration is timed from when the renderer was started or from when dropped frames were
* last reported (whichever was more recent), and not from when the first of the reported
* drops occurred.
*/
void onDroppedFrames(int count, long elapsed);
/**
* Invoked each time there's a change in the size of the video being rendered.
*
* @param width The video width in pixels.
* @param height The video height in pixels.
* @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise
* rotation in degrees that the application should apply for the video for it to be rendered
* in the correct orientation. This value will always be zero on API levels 21 and above,
* since the renderer will apply all necessary rotations internally. On earlier API levels
* this is not possible. Applications that use {@link TextureView} can apply the rotation by
* calling {@link TextureView#setTransform}. Applications that do not expect to encounter
* rotated videos can safely ignore this parameter.
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case
* of square pixels this will be equal to 1.0. Different values are indicative of anamorphic
* content.
*/
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio);
/**
* Invoked when a frame is rendered to a surface for the first time following that surface
* having been set as the target for the renderer.
*
* @param surface The surface to which a first frame has been rendered.
*/
void onDrawnToSurface(Surface surface);
/**
* Invoked to pass the codec counters when the renderer is enabled.
*
* @param counters CodecCounters object used by the renderer.
*/
void onVideoCodecCounters(CodecCounters counters);
}
...@@ -86,7 +86,7 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal ...@@ -86,7 +86,7 @@ public final class MetadataTrackRenderer<T> extends TrackRenderer implements Cal
} }
@Override @Override
protected int getTrackType() { public int getTrackType() {
return C.TRACK_TYPE_METADATA; return C.TRACK_TYPE_METADATA;
} }
......
...@@ -92,7 +92,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback { ...@@ -92,7 +92,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
} }
@Override @Override
protected int getTrackType() { public int getTrackType() {
return C.TRACK_TYPE_TEXT; return C.TRACK_TYPE_TEXT;
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.util.extensions; package com.google.android.exoplayer.util.extensions;
import com.google.android.exoplayer.AudioTrackRendererEventListener;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.CodecCounters; import com.google.android.exoplayer.CodecCounters;
import com.google.android.exoplayer.DecoderInputBuffer; import com.google.android.exoplayer.DecoderInputBuffer;
...@@ -37,34 +38,6 @@ import java.util.List; ...@@ -37,34 +38,6 @@ import java.util.List;
public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements MediaClock { public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements MediaClock {
/** /**
* Interface definition for a callback to be notified of {@link AudioDecoderTrackRenderer} events.
*/
public interface EventListener {
/**
* Invoked when the {@link AudioTrack} fails to initialize.
*
* @param e The corresponding exception.
*/
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
/**
* Invoked when an {@link AudioTrack} write fails.
*
* @param e The corresponding exception.
*/
void onAudioTrackWriteError(AudioTrack.WriteException e);
/**
* Invoked when decoding fails.
*
* @param e The corresponding exception.
*/
void onDecoderError(AudioDecoderException e);
}
/**
* The type of a message that can be passed to an instance of this class via * The type of a message that can be passed to an instance of this class via
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object * {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
* should be a {@link Float} with 0 being silence and 1 being unity gain. * should be a {@link Float} with 0 being silence and 1 being unity gain.
...@@ -74,7 +47,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -74,7 +47,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
public final CodecCounters codecCounters = new CodecCounters(); public final CodecCounters codecCounters = new CodecCounters();
private final Handler eventHandler; private final Handler eventHandler;
private final EventListener eventListener; private final AudioTrackRendererEventListener eventListener;
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
private Format format; private Format format;
...@@ -100,7 +73,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -100,7 +73,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
* null if delivery of events is not required. * 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 eventListener A listener of events. May be null if delivery of events is not required.
*/ */
public AudioDecoderTrackRenderer(Handler eventHandler, EventListener eventListener) { public AudioDecoderTrackRenderer(Handler eventHandler,
AudioTrackRendererEventListener eventListener) {
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET; this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
...@@ -130,7 +104,6 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -130,7 +104,6 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
try { try {
decoder = createDecoder(format.initializationData); decoder = createDecoder(format.initializationData);
} catch (AudioDecoderException e) { } catch (AudioDecoderException e) {
notifyDecoderError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
codecCounters.codecInitCount++; codecCounters.codecInitCount++;
...@@ -147,14 +120,13 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -147,14 +120,13 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
notifyAudioTrackWriteError(e); notifyAudioTrackWriteError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} catch (AudioDecoderException e) { } catch (AudioDecoderException e) {
notifyDecoderError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
codecCounters.ensureUpdated(); codecCounters.ensureUpdated();
} }
@Override @Override
protected int getTrackType() { public int getTrackType() {
return C.TRACK_TYPE_AUDIO; return C.TRACK_TYPE_AUDIO;
} }
...@@ -283,6 +255,11 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -283,6 +255,11 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
} }
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
notifyAudioCodecCounters();
}
@Override
protected void onStarted() { protected void onStarted() {
audioTrack.play(); audioTrack.play();
} }
...@@ -351,12 +328,12 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -351,12 +328,12 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
} }
} }
private void notifyDecoderError(final AudioDecoderException e) { private void notifyAudioCodecCounters() {
if (eventHandler != null && eventListener != null) { if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() { eventHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
eventListener.onDecoderError(e); eventListener.onAudioCodecCounters(codecCounters);
} }
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment