Commit 60a3eda1 by olly Committed by Oliver Woodman

Support tunneling in video renderer

At this point the renderers all have TODOs in enable(),
and turning on tunneling is reduced to the problem of
propagating a tunneling ID to these points.

Issue: #1688

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=144619393
parent 5742c877
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.AudioFormat; import android.media.AudioFormat;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaCodec; import android.media.MediaCodec;
...@@ -550,4 +552,13 @@ public final class C { ...@@ -550,4 +552,13 @@ public final class C {
return timeMs == TIME_UNSET ? TIME_UNSET : (timeMs * 1000); return timeMs == TIME_UNSET ? TIME_UNSET : (timeMs * 1000);
} }
/**
* Returns a newly generated {@link android.media.AudioTrack} session identifier.
*/
@TargetApi(21)
public static int generateAudioSessionIdV21(Context context) {
return ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE))
.generateAudioSessionId();
}
} }
...@@ -19,6 +19,7 @@ import android.annotation.SuppressLint; ...@@ -19,6 +19,7 @@ import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.os.Handler; import android.os.Handler;
...@@ -28,6 +29,7 @@ import android.view.Surface; ...@@ -28,6 +29,7 @@ import android.view.Surface;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
...@@ -83,6 +85,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -83,6 +85,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private int lastReportedUnappliedRotationDegrees; private int lastReportedUnappliedRotationDegrees;
private float lastReportedPixelWidthHeightRatio; private float lastReportedPixelWidthHeightRatio;
private boolean tunneling;
private int tunnelingAudioSessionId;
/* package */ OnFrameRenderedListenerV23 tunnelingOnFrameRenderedListener;
/** /**
* @param context A context. * @param context A context.
* @param mediaCodecSelector A decoder selector. * @param mediaCodecSelector A decoder selector.
...@@ -204,6 +210,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -204,6 +210,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Override @Override
protected void onEnabled(boolean joining) throws ExoPlaybackException { protected void onEnabled(boolean joining) throws ExoPlaybackException {
super.onEnabled(joining); super.onEnabled(joining);
// TODO: Allow this to be set.
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
tunneling = tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET;
eventDispatcher.enabled(decoderCounters); eventDispatcher.enabled(decoderCounters);
frameReleaseTimeHelper.enable(); frameReleaseTimeHelper.enable();
} }
...@@ -217,7 +226,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -217,7 +226,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Override @Override
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
super.onPositionReset(positionUs, joining); super.onPositionReset(positionUs, joining);
renderedFirstFrame = false; clearRenderedFirstFrame();
consecutiveDroppedFrameCount = 0; consecutiveDroppedFrameCount = 0;
joiningDeadlineMs = joining && allowedJoiningTimeMs > 0 joiningDeadlineMs = joining && allowedJoiningTimeMs > 0
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET; ? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
...@@ -264,6 +273,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -264,6 +273,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
pendingPixelWidthHeightRatio = Format.NO_VALUE; pendingPixelWidthHeightRatio = Format.NO_VALUE;
clearLastReportedVideoSize(); clearLastReportedVideoSize();
frameReleaseTimeHelper.disable(); frameReleaseTimeHelper.disable();
tunnelingOnFrameRenderedListener = null;
try { try {
super.onDisabled(); super.onDisabled();
} finally { } finally {
...@@ -288,11 +298,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -288,11 +298,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
private void setSurface(Surface surface) throws ExoPlaybackException { private void setSurface(Surface surface) throws ExoPlaybackException {
// Clear state so that we always call the event listener with the video size and when a frame // We only need to release and reinitialize the codec if the surface has changed.
// is rendered, even if the surface hasn't changed.
renderedFirstFrame = false;
clearLastReportedVideoSize();
// We only need to actually release and reinitialize the codec if the surface has changed.
if (this.surface != surface) { if (this.surface != surface) {
this.surface = surface; this.surface = surface;
int state = getState(); int state = getState();
...@@ -301,6 +307,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -301,6 +307,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
maybeInitCodec(); maybeInitCodec();
} }
} }
// Clear state so that we always call the event listener with the video size and when a frame
// is rendered, even if the surface hasn't changed.
clearRenderedFirstFrame();
clearLastReportedVideoSize();
} }
@Override @Override
...@@ -311,8 +321,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -311,8 +321,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Override @Override
protected void configureCodec(MediaCodec codec, Format format, MediaCrypto crypto) { protected void configureCodec(MediaCodec codec, Format format, MediaCrypto crypto) {
codecMaxValues = getCodecMaxValues(format, streamFormats); codecMaxValues = getCodecMaxValues(format, streamFormats);
MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround); MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround,
tunnelingAudioSessionId);
codec.configure(mediaFormat, surface, crypto, 0); codec.configure(mediaFormat, surface, crypto, 0);
if (Util.SDK_INT >= 23 && tunneling) {
tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec);
}
} }
@Override @Override
...@@ -330,6 +344,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -330,6 +344,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
@Override @Override
protected void onQueueInputBuffer(DecoderInputBuffer buffer) {
if (Util.SDK_INT < 23 && tunneling) {
maybeNotifyRenderedFirstFrame();
}
}
@Override
protected void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat outputFormat) { protected void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat outputFormat) {
boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT) boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT)
&& outputFormat.containsKey(KEY_CROP_LEFT) && outputFormat.containsKey(KEY_CROP_BOTTOM) && outputFormat.containsKey(KEY_CROP_LEFT) && outputFormat.containsKey(KEY_CROP_BOTTOM)
...@@ -479,10 +500,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -479,10 +500,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
TraceUtil.endSection(); TraceUtil.endSection();
decoderCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
consecutiveDroppedFrameCount = 0; consecutiveDroppedFrameCount = 0;
if (!renderedFirstFrame) { maybeNotifyRenderedFirstFrame();
renderedFirstFrame = true;
eventDispatcher.renderedFirstFrame(surface);
}
} }
@TargetApi(21) @TargetApi(21)
...@@ -493,6 +511,25 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -493,6 +511,25 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
TraceUtil.endSection(); TraceUtil.endSection();
decoderCounters.renderedOutputBufferCount++; decoderCounters.renderedOutputBufferCount++;
consecutiveDroppedFrameCount = 0; consecutiveDroppedFrameCount = 0;
maybeNotifyRenderedFirstFrame();
}
private void clearRenderedFirstFrame() {
renderedFirstFrame = false;
// The first frame notification is triggered by renderOutputBuffer or renderOutputBufferV21 for
// non-tunneled playback, onQueueInputBuffer for tunneled playback prior to API level 23, and
// OnFrameRenderedListenerV23.onFrameRenderedListener for tunneled playback on API level 23 and
// above.
if (Util.SDK_INT >= 23 && tunneling) {
MediaCodec codec = getCodec();
// If codec is null then the listener will be instantiated in configureCodec.
if (codec != null) {
tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec);
}
}
}
/* package */ void maybeNotifyRenderedFirstFrame() {
if (!renderedFirstFrame) { if (!renderedFirstFrame) {
renderedFirstFrame = true; renderedFirstFrame = true;
eventDispatcher.renderedFirstFrame(surface); eventDispatcher.renderedFirstFrame(surface);
...@@ -531,7 +568,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -531,7 +568,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
private static MediaFormat getMediaFormat(Format format, CodecMaxValues codecMaxValues, private static MediaFormat getMediaFormat(Format format, CodecMaxValues codecMaxValues,
boolean deviceNeedsAutoFrcWorkaround) { boolean deviceNeedsAutoFrcWorkaround, int tunnelingAudioSessionId) {
MediaFormat frameworkMediaFormat = format.getFrameworkMediaFormatV16(); MediaFormat frameworkMediaFormat = format.getFrameworkMediaFormatV16();
// Set the maximum adaptive video dimensions. // Set the maximum adaptive video dimensions.
frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, codecMaxValues.width); frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, codecMaxValues.width);
...@@ -544,6 +581,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -544,6 +581,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
if (deviceNeedsAutoFrcWorkaround) { if (deviceNeedsAutoFrcWorkaround) {
frameworkMediaFormat.setInteger("auto-frc", 0); frameworkMediaFormat.setInteger("auto-frc", 0);
} }
// Configure tunneling if enabled.
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
frameworkMediaFormat.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true);
frameworkMediaFormat.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, tunnelingAudioSessionId);
}
return frameworkMediaFormat; return frameworkMediaFormat;
} }
...@@ -682,4 +724,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -682,4 +724,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
@TargetApi(23)
private final class OnFrameRenderedListenerV23 implements MediaCodec.OnFrameRenderedListener {
private OnFrameRenderedListenerV23(MediaCodec codec) {
codec.setOnFrameRenderedListener(this, new Handler());
}
@Override
public void onFrameRendered(MediaCodec codec, long presentationTimeUs, long nanoTime) {
if (this != tunnelingOnFrameRenderedListener) {
// Stale event.
return;
}
maybeNotifyRenderedFirstFrame();
}
}
} }
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