Skip to content

Commit

Permalink
[Refactor - Step #5] Introduce TrackSelector
Browse files Browse the repository at this point in the history
Notes:

- The way this works is that every time the player needs to
  select some tracks it invokes the TrackSelector. When a
  track selection is actually activated (i.e. "hits the
  screen") it gets passed back to the TrackSelector, which
  allows it to expose the current tracks through an API that
  it may choose to define. Since playlist support doesn't exist
  yet, it's currently the case that the pass-back always occurs
  immediately.
- A TrackSelector can invalidate its previous selections if its
  selection criteria changes. This will force the player to invoke
  it again to make a new selection. If the new selection is the
  same as the previous one for a renderer then the player handles
  this efficiently (i.e. turns it into a no-op).
- DefaultTrackSelector supports disabling/enabling of renderers.
  Separately, it supports overrides to select specific formats.
  Since formats may change (playlists/periods), overrides are
  specific to not only the renderer but also the set of formats
  that are available to it. If the formats available to a renderer
  change then the override will no longer apply. If the same set
  of formats become available at some point later, it will apply
  once more. This will nicely handle cases like ad-insertion where
  the ads have different formats, but all segments of main content
  use the same set of formats.
- In general, in  multi-period or playlist cases, the preferred
  way of selecting formats will be via constraints (e.g. "don't play
  HD", "prefer higher quality audio") rather than explicit format
  selections. The ability to set various constraints on
  DefaultTrackSelector is future work.

Note about the demo app:

- I've removed the verbose log toggle. I doubt anyone has
  ever used it! I've also removed the background audio option.
  Without using a service it can't be considered a reference
  implementation, so it's probably best to leave developers to
  figure this one out. Finally, listening to AudioCapabilities
  has also gone. This will be replaced by having the player
  detect and handle the capabilities change internally in a
  future CL. This will work by allowing a renderer to invalidate
  the track selections when its capabilities change, much like
  how a selector is able to invalidate the track selections in
  this CL.
- It's now possible to enable ABR with an arbitrary subset of
  tracks.
- Unsupported tracks are shown grayed out in the UI. I'm not
  showing tracks that aren't associated to any renderer, but we
  could optionally add that later.
- Every time the tracks change, there's logcat output showing
  all of the tracks and which ones are enabled. Unassociated
  tracks are displayed in this output.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=117122202
  • Loading branch information
ojw28 committed Jun 15, 2016
1 parent 9c98c4b commit d3d6310
Show file tree
Hide file tree
Showing 18 changed files with 1,352 additions and 592 deletions.
153 changes: 132 additions & 21 deletions demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
*/
package com.google.android.exoplayer.demo;

import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
import com.google.android.exoplayer.TimeRange;
import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.TrackSelection;
import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.demo.player.DemoPlayer;
import com.google.android.exoplayer.util.VerboseLogUtil;

import android.media.MediaCodec.CryptoException;
import android.os.SystemClock;
Expand All @@ -46,13 +50,8 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
}

private long sessionStartTimeMs;
private long[] loadStartTimeMs;
private long[] availableRangeValuesUs;

public EventLogger() {
loadStartTimeMs = new long[DemoPlayer.RENDERER_COUNT];
}

public void startSession() {
sessionStartTimeMs = SystemClock.elapsedRealtime();
Log.d(TAG, "start [0]");
Expand Down Expand Up @@ -82,6 +81,54 @@ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegre
+ ", " + pixelWidthHeightRatio + "]");
}

@Override
public void onTracksChanged(TrackInfo trackInfo) {
Log.d(TAG, "Tracks [");
// Log tracks associated to renderers.
for (int rendererIndex = 0; rendererIndex < trackInfo.rendererCount; rendererIndex++) {
TrackGroupArray trackGroups = trackInfo.getTrackGroups(rendererIndex);
TrackSelection trackSelection = trackInfo.getTrackSelection(rendererIndex);
if (trackGroups.length > 0) {
Log.d(TAG, " Renderer:" + rendererIndex + " [");
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup trackGroup = trackGroups.get(groupIndex);
String adaptiveSupport = getAdaptiveSupportString(
trackGroup.length, trackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false));
Log.d(TAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " [");
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
String status = getTrackStatusString(trackSelection, groupIndex, trackIndex);
String formatSupport = getFormatSupportString(
trackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex));
Log.d(TAG, " " + status + " Track:" + trackIndex + ", "
+ getFormatString(trackGroup.getFormat(trackIndex))
+ ", supported=" + formatSupport);
}
Log.d(TAG, " ]");
}
Log.d(TAG, " ]");
}
}
// Log tracks not associated with a renderer.
TrackGroupArray trackGroups = trackInfo.getUnassociatedTrackGroups();
if (trackGroups.length > 0) {
Log.d(TAG, " Renderer:None [");
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
Log.d(TAG, " Group:" + groupIndex + " [");
TrackGroup trackGroup = trackGroups.get(groupIndex);
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
String status = getTrackStatusString(false);
String formatSupport = getFormatSupportString(TrackRenderer.FORMAT_UNSUPPORTED_TYPE);
Log.d(TAG, " " + status + " Track:" + trackIndex + ", "
+ getFormatString(trackGroup.getFormat(trackIndex))
+ ", supported=" + formatSupport);
}
Log.d(TAG, " ]");
}
Log.d(TAG, " ]");
}
Log.d(TAG, "]");
}

// DemoPlayer.InfoListener

@Override
Expand All @@ -98,21 +145,13 @@ public void onDroppedFrames(int count, long elapsed) {
@Override
public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
long mediaStartTimeMs, long mediaEndTimeMs) {
loadStartTimeMs[sourceId] = SystemClock.elapsedRealtime();
if (VerboseLogUtil.isTagEnabled(TAG)) {
Log.v(TAG, "loadStart [" + getSessionTimeString() + ", " + sourceId + ", " + type
+ ", " + mediaStartTimeMs + ", " + mediaEndTimeMs + "]");
}
// Do nothing.
}

@Override
public void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format,
long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs) {
if (VerboseLogUtil.isTagEnabled(TAG)) {
long downloadTime = SystemClock.elapsedRealtime() - loadStartTimeMs[sourceId];
Log.v(TAG, "loadEnd [" + getSessionTimeString() + ", " + sourceId + ", " + downloadTime
+ "]");
}
// Do nothing.
}

@Override
Expand Down Expand Up @@ -187,7 +226,15 @@ private void printInternalError(String type, Exception e) {
Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e);
}

private String getStateString(int state) {
private String getSessionTimeString() {
return getTimeString(SystemClock.elapsedRealtime() - sessionStartTimeMs);
}

private static String getTimeString(long timeMs) {
return TIME_FORMAT.format((timeMs) / 1000f);
}

private static String getStateString(int state) {
switch (state) {
case ExoPlayer.STATE_BUFFERING:
return "B";
Expand All @@ -204,12 +251,76 @@ private String getStateString(int state) {
}
}

private String getSessionTimeString() {
return getTimeString(SystemClock.elapsedRealtime() - sessionStartTimeMs);
private static String getFormatSupportString(int formatSupport) {
switch (formatSupport) {
case TrackRenderer.FORMAT_HANDLED:
return "YES";
case TrackRenderer.FORMAT_EXCEEDS_CAPABILITIES:
return "NO_EXCEEDS_CAPABILITIES";
case TrackRenderer.FORMAT_UNSUPPORTED_SUBTYPE:
return "NO_UNSUPPORTED_TYPE";
case TrackRenderer.FORMAT_UNSUPPORTED_TYPE:
return "NO";
default:
return "?";
}
}

private String getTimeString(long timeMs) {
return TIME_FORMAT.format((timeMs) / 1000f);
private static String getAdaptiveSupportString(int trackCount, int adaptiveSupport) {
if (trackCount < 2) {
return "N/A";
}
switch (adaptiveSupport) {
case TrackRenderer.ADAPTIVE_SEAMLESS:
return "YES";
case TrackRenderer.ADAPTIVE_NOT_SEAMLESS:
return "YES_NOT_SEAMLESS";
case TrackRenderer.ADAPTIVE_NOT_SUPPORTED:
return "NO";
default:
return "?";
}
}

private static String getFormatString(Format format) {
StringBuilder builder = new StringBuilder();
builder.append("id=").append(format.id).append(", mimeType=").append(format.sampleMimeType);
if (format.bitrate != Format.NO_VALUE) {
builder.append(", bitrate=").append(format.bitrate);
}
if (format.width != -1 && format.height != -1) {
builder.append(", res=").append(format.width).append("x").append(format.height);
}
if (format.frameRate != -1) {
builder.append(", fps=").append(format.frameRate);
}
if (format.channelCount != -1) {
builder.append(", channels=").append(format.channelCount);
}
if (format.sampleRate != -1) {
builder.append(", sample_rate=").append(format.sampleRate);
}
if (format.language != null) {
builder.append(", language=").append(format.language);
}
return builder.toString();
}

private static String getTrackStatusString(TrackSelection selection, int groupIndex,
int trackIndex) {
boolean groupEnabled = selection != null && selection.group == groupIndex;
if (groupEnabled) {
for (int i = 0; i < selection.length; i++) {
if (selection.getTrack(i) == trackIndex) {
return getTrackStatusString(true);
}
}
}
return getTrackStatusString(false);
}

private static String getTrackStatusString(boolean enabled) {
return enabled ? "[X]" : "[ ]";
}

}
Loading

0 comments on commit d3d6310

Please sign in to comment.