Skip to content
Permalink
Browse files

Enable tunneling end-to-end

- Tunneling can be enabled by calling:
  trackSelector.setTunnelingAudioSessionId(
      C.generateAudioSessionIdV21(this));

- If enabled, tunneling is automatically used when the renderers
  and track selection combination support it. Tunneling is
  automatically turned on and off through playlists if the support
  changes.

Issue: #1688
  • Loading branch information
ojw28 committed Jan 17, 2017
1 parent c828d9b commit f1e3d3f244ff90db6ea53a168c99fb710860e722
@@ -28,6 +28,7 @@

private final int trackType;

private RendererConfiguration configuration;
private int index;
private int state;
private SampleStream stream;
@@ -70,9 +71,11 @@ public final int getState() {
}

@Override
public final void enable(Format[] formats, SampleStream stream, long positionUs, boolean joining,
long offsetUs) throws ExoPlaybackException {
public final void enable(RendererConfiguration configuration, Format[] formats,
SampleStream stream, long positionUs, boolean joining, long offsetUs)
throws ExoPlaybackException {
Assertions.checkState(state == STATE_DISABLED);
this.configuration = configuration;
state = STATE_ENABLED;
onEnabled(joining);
replaceStream(formats, stream, offsetUs);
@@ -237,10 +240,15 @@ protected void onDisabled() {

// Methods to be called by subclasses.

/**
* Returns the configuration set when the renderer was most recently enabled.
*/
protected final RendererConfiguration getConfiguration() {
return configuration;
}

/**
* Returns the index of the renderer within the player.
*
* @return The index of the renderer within the player.
*/
protected final int getIndex() {
return index;
@@ -35,7 +35,6 @@
import com.google.android.exoplayer2.util.PriorityHandlerThread;
import com.google.android.exoplayer2.util.StandaloneMediaClock;
import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;

/**
@@ -1149,35 +1148,38 @@ private void updatePeriods() throws ExoPlaybackException, IOException {
}

if (readingPeriodHolder.next != null && readingPeriodHolder.next.prepared) {
TrackSelectionArray oldTrackSelections = readingPeriodHolder.trackSelectorResult.selections;
TrackSelectorResult oldTrackSelectorResult = readingPeriodHolder.trackSelectorResult;
readingPeriodHolder = readingPeriodHolder.next;
TrackSelectionArray newTrackSelections = readingPeriodHolder.trackSelectorResult.selections;
TrackSelectorResult newTrackSelectorResult = readingPeriodHolder.trackSelectorResult;

boolean initialDiscontinuity =
readingPeriodHolder.mediaPeriod.readDiscontinuity() != C.TIME_UNSET;
for (int i = 0; i < renderers.length; i++) {
Renderer renderer = renderers[i];
TrackSelection oldSelection = oldTrackSelections.get(i);
TrackSelection newSelection = newTrackSelections.get(i);
TrackSelection oldSelection = oldTrackSelectorResult.selections.get(i);
if (oldSelection == null) {
// The renderer has no current stream and will be enabled when we play the next period.
} else if (initialDiscontinuity) {
// The new period starts with a discontinuity, so the renderer will play out all data then
// be disabled and re-enabled when it starts playing the next period.
renderer.setCurrentStreamFinal();
} else if (!renderer.isCurrentStreamFinal()) {
if (newSelection != null) {
// Replace the renderer's SampleStream so the transition to playing the next period
// can be seamless.
TrackSelection newSelection = newTrackSelectorResult.selections.get(i);
RendererConfiguration oldConfig = oldTrackSelectorResult.rendererConfigurations[i];
RendererConfiguration newConfig = newTrackSelectorResult.rendererConfigurations[i];
if (newSelection != null && newConfig.equals(oldConfig)) {
// Replace the renderer's SampleStream so the transition to playing the next period can
// be seamless.
Format[] formats = new Format[newSelection.length()];
for (int j = 0; j < formats.length; j++) {
formats[j] = newSelection.getFormat(j);
}
renderer.replaceStream(formats, readingPeriodHolder.sampleStreams[i],
readingPeriodHolder.getRendererOffset());
} else {
// The renderer will be disabled when transitioning to playing the next period. Mark the
// SampleStream as final to play out any remaining data.
// The renderer will be disabled when transitioning to playing the next period, either
// because there's no new selection or because a configuration change is required. Mark
// the SampleStream as final to play out any remaining data.
renderer.setCurrentStreamFinal();
}
}
@@ -1354,6 +1356,8 @@ private void enableRenderers(boolean[] rendererWasEnabledFlags, int enabledRende
if (newSelection != null) {
enabledRenderers[enabledRendererCount++] = renderer;
if (renderer.getState() == Renderer.STATE_DISABLED) {
RendererConfiguration rendererConfiguration =
playingPeriodHolder.trackSelectorResult.rendererConfigurations[i];
// The renderer needs enabling with its new track selection.
boolean playing = playWhenReady && state == ExoPlayer.STATE_READY;
// Consider as joining only if the renderer was previously disabled.
@@ -1364,8 +1368,8 @@ private void enableRenderers(boolean[] rendererWasEnabledFlags, int enabledRende
formats[j] = newSelection.getFormat(j);
}
// Enable the renderer.
renderer.enable(formats, playingPeriodHolder.sampleStreams[i], rendererPositionUs,
joining, playingPeriodHolder.getRendererOffset());
renderer.enable(rendererConfiguration, formats, playingPeriodHolder.sampleStreams[i],
rendererPositionUs, joining, playingPeriodHolder.getRendererOffset());
MediaClock mediaClock = renderer.getMediaClock();
if (mediaClock != null) {
if (rendererMediaClock != null) {
@@ -1410,7 +1414,7 @@ private void enableRenderers(boolean[] rendererWasEnabledFlags, int enabledRende
private final LoadControl loadControl;
private final MediaSource mediaSource;

private TrackSelectionArray periodTrackSelections;
private TrackSelectorResult periodTrackSelectorResult;

public MediaPeriodHolder(Renderer[] renderers, RendererCapabilities[] rendererCapabilities,
long rendererPositionOffsetUs, TrackSelector trackSelector, LoadControl loadControl,
@@ -1463,8 +1467,7 @@ public void handlePrepared() throws ExoPlaybackException {
public boolean selectTracks() throws ExoPlaybackException {
TrackSelectorResult selectorResult = trackSelector.selectTracks(rendererCapabilities,
mediaPeriod.getTrackGroups());
TrackSelectionArray newTrackSelections = selectorResult.selections;
if (newTrackSelections.equals(periodTrackSelections)) {
if (selectorResult.isEquivalent(periodTrackSelectorResult)) {
return false;
}
trackSelectorResult = selectorResult;
@@ -1481,14 +1484,13 @@ public long updatePeriodTrackSelection(long positionUs, boolean forceRecreateStr
TrackSelectionArray trackSelections = trackSelectorResult.selections;
for (int i = 0; i < trackSelections.length; i++) {
mayRetainStreamFlags[i] = !forceRecreateStreams
&& Util.areEqual(periodTrackSelections == null ? null : periodTrackSelections.get(i),
trackSelections.get(i));
&& trackSelectorResult.isEquivalent(periodTrackSelectorResult, i);
}

// Disable streams on the period and get new streams for updated/newly-enabled tracks.
positionUs = mediaPeriod.selectTracks(trackSelections.getAll(), mayRetainStreamFlags,
sampleStreams, streamResetFlags, positionUs);
periodTrackSelections = trackSelections;
periodTrackSelectorResult = trackSelectorResult;

// Update whether we have enabled tracks and sanity check the expected streams are non-null.
hasEnabledTracks = false;
@@ -183,10 +183,8 @@
*/
public final int accessibilityChannel;

// Lazily initialized hashcode and framework media format.

// Lazily initialized hashcode.
private int hashCode;
private MediaFormat frameworkMediaFormat;

// Video.

@@ -495,25 +493,22 @@ public int getPixelCount() {
@SuppressLint("InlinedApi")
@TargetApi(16)
public final MediaFormat getFrameworkMediaFormatV16() {
if (frameworkMediaFormat == null) {
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, sampleMimeType);
maybeSetStringV16(format, MediaFormat.KEY_LANGUAGE, language);
maybeSetIntegerV16(format, MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize);
maybeSetIntegerV16(format, MediaFormat.KEY_WIDTH, width);
maybeSetIntegerV16(format, MediaFormat.KEY_HEIGHT, height);
maybeSetFloatV16(format, MediaFormat.KEY_FRAME_RATE, frameRate);
maybeSetIntegerV16(format, "rotation-degrees", rotationDegrees);
maybeSetIntegerV16(format, MediaFormat.KEY_CHANNEL_COUNT, channelCount);
maybeSetIntegerV16(format, MediaFormat.KEY_SAMPLE_RATE, sampleRate);
maybeSetIntegerV16(format, "encoder-delay", encoderDelay);
maybeSetIntegerV16(format, "encoder-padding", encoderPadding);
for (int i = 0; i < initializationData.size(); i++) {
format.setByteBuffer("csd-" + i, ByteBuffer.wrap(initializationData.get(i)));
}
frameworkMediaFormat = format;
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, sampleMimeType);
maybeSetStringV16(format, MediaFormat.KEY_LANGUAGE, language);
maybeSetIntegerV16(format, MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize);
maybeSetIntegerV16(format, MediaFormat.KEY_WIDTH, width);
maybeSetIntegerV16(format, MediaFormat.KEY_HEIGHT, height);
maybeSetFloatV16(format, MediaFormat.KEY_FRAME_RATE, frameRate);
maybeSetIntegerV16(format, "rotation-degrees", rotationDegrees);
maybeSetIntegerV16(format, MediaFormat.KEY_CHANNEL_COUNT, channelCount);
maybeSetIntegerV16(format, MediaFormat.KEY_SAMPLE_RATE, sampleRate);
maybeSetIntegerV16(format, "encoder-delay", encoderDelay);
maybeSetIntegerV16(format, "encoder-padding", encoderPadding);
for (int i = 0; i < initializationData.size(); i++) {
format.setByteBuffer("csd-" + i, ByteBuffer.wrap(initializationData.get(i)));
}
return frameworkMediaFormat;
return format;
}

@Override
@@ -92,6 +92,7 @@
* This method may be called when the renderer is in the following states:
* {@link #STATE_DISABLED}.
*
* @param configuration The renderer configuration.
* @param formats The enabled formats.
* @param stream The {@link SampleStream} from which the renderer should consume.
* @param positionUs The player's current position.
@@ -100,8 +101,8 @@
* before they are rendered.
* @throws ExoPlaybackException If an error occurs.
*/
void enable(Format[] formats, SampleStream stream, long positionUs, boolean joining,
long offsetUs) throws ExoPlaybackException;
void enable(RendererConfiguration configuration, Format[] formats, SampleStream stream,
long positionUs, boolean joining, long offsetUs) throws ExoPlaybackException;

/**
* Starts the renderer, meaning that calls to {@link #render(long, long)} will cause media to be
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2017 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.exoplayer2;

/**
* The configuration of a {@link Renderer}.
*/
public final class RendererConfiguration {

/**
* The default configuration.
*/
public static final RendererConfiguration DEFAULT =
new RendererConfiguration(C.AUDIO_SESSION_ID_UNSET);

/**
* The audio session id to use for tunneling, or {@link C#AUDIO_SESSION_ID_UNSET} if tunneling
* should not be enabled.
*/
public final int tunnelingAudioSessionId;

/**
* @param tunnelingAudioSessionId The audio session id to use for tunneling, or
* {@link C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled.
*/
public RendererConfiguration(int tunnelingAudioSessionId) {
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
RendererConfiguration other = (RendererConfiguration) obj;
return tunnelingAudioSessionId == other.tunnelingAudioSessionId;
}

@Override
public int hashCode() {
return tunnelingAudioSessionId;
}

}
@@ -259,8 +259,7 @@ protected void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs,
protected void onEnabled(boolean joining) throws ExoPlaybackException {
super.onEnabled(joining);
eventDispatcher.enabled(decoderCounters);
// TODO: Allow this to be set.
int tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
int tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId;
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
audioTrack.enableTunnelingV21(tunnelingAudioSessionId);
} else {
@@ -407,8 +407,7 @@ public long getPositionUs() {
protected void onEnabled(boolean joining) throws ExoPlaybackException {
decoderCounters = new DecoderCounters();
eventDispatcher.enabled(decoderCounters);
// TODO: Allow this to be set.
int tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
int tunnelingAudioSessionId = getConfiguration().tunnelingAudioSessionId;
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
audioTrack.enableTunnelingV21(tunnelingAudioSessionId);
} else {

0 comments on commit f1e3d3f

Please sign in to comment.
You can’t perform that action at this time.