From 60a3eda1e02e53b8ef8b977b70da4168695541fa Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 16 Jan 2017 03:29:56 -0800 Subject: [PATCH] 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 --- .../java/com/google/android/exoplayer2/C.java | 11 +++ .../video/MediaCodecVideoRenderer.java | 84 ++++++++++++++++--- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/C.java b/library/src/main/java/com/google/android/exoplayer2/C.java index 5cef177517f..0b1c33bfc9f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/src/main/java/com/google/android/exoplayer2/C.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2; +import android.annotation.TargetApi; +import android.content.Context; import android.media.AudioFormat; import android.media.AudioManager; import android.media.MediaCodec; @@ -550,4 +552,13 @@ public static long msToUs(long timeMs) { 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(); + } + } diff --git a/library/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index f0563621e94..a6fb7721101 100644 --- a/library/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -19,6 +19,7 @@ import android.annotation.TargetApi; import android.content.Context; import android.media.MediaCodec; +import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCrypto; import android.media.MediaFormat; import android.os.Handler; @@ -28,6 +29,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; 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.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; @@ -83,6 +85,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { private int lastReportedUnappliedRotationDegrees; private float lastReportedPixelWidthHeightRatio; + private boolean tunneling; + private int tunnelingAudioSessionId; + /* package */ OnFrameRenderedListenerV23 tunnelingOnFrameRenderedListener; + /** * @param context A context. * @param mediaCodecSelector A decoder selector. @@ -204,6 +210,9 @@ protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format forma @Override protected void onEnabled(boolean joining) throws ExoPlaybackException { 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); frameReleaseTimeHelper.enable(); } @@ -217,7 +226,7 @@ protected void onStreamChanged(Format[] formats) throws ExoPlaybackException { @Override protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { super.onPositionReset(positionUs, joining); - renderedFirstFrame = false; + clearRenderedFirstFrame(); consecutiveDroppedFrameCount = 0; joiningDeadlineMs = joining && allowedJoiningTimeMs > 0 ? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET; @@ -264,6 +273,7 @@ protected void onDisabled() { pendingPixelWidthHeightRatio = Format.NO_VALUE; clearLastReportedVideoSize(); frameReleaseTimeHelper.disable(); + tunnelingOnFrameRenderedListener = null; try { super.onDisabled(); } finally { @@ -288,11 +298,7 @@ public void handleMessage(int messageType, Object message) throws ExoPlaybackExc } 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 - // 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. + // We only need to release and reinitialize the codec if the surface has changed. if (this.surface != surface) { this.surface = surface; int state = getState(); @@ -301,6 +307,10 @@ private void setSurface(Surface surface) throws ExoPlaybackException { 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 @@ -311,8 +321,12 @@ protected boolean shouldInitCodec() { @Override protected void configureCodec(MediaCodec codec, Format format, MediaCrypto crypto) { codecMaxValues = getCodecMaxValues(format, streamFormats); - MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround); + MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround, + tunnelingAudioSessionId); codec.configure(mediaFormat, surface, crypto, 0); + if (Util.SDK_INT >= 23 && tunneling) { + tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec); + } } @Override @@ -329,6 +343,13 @@ protected void onInputFormatChanged(Format newFormat) throws ExoPlaybackExceptio pendingRotationDegrees = getRotationDegrees(newFormat); } + @Override + protected void onQueueInputBuffer(DecoderInputBuffer buffer) { + if (Util.SDK_INT < 23 && tunneling) { + maybeNotifyRenderedFirstFrame(); + } + } + @Override protected void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat outputFormat) { boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT) @@ -479,10 +500,7 @@ private void renderOutputBuffer(MediaCodec codec, int bufferIndex) { TraceUtil.endSection(); decoderCounters.renderedOutputBufferCount++; consecutiveDroppedFrameCount = 0; - if (!renderedFirstFrame) { - renderedFirstFrame = true; - eventDispatcher.renderedFirstFrame(surface); - } + maybeNotifyRenderedFirstFrame(); } @TargetApi(21) @@ -493,6 +511,25 @@ private void renderOutputBufferV21(MediaCodec codec, int bufferIndex, long relea TraceUtil.endSection(); decoderCounters.renderedOutputBufferCount++; 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) { renderedFirstFrame = true; eventDispatcher.renderedFirstFrame(surface); @@ -531,7 +568,7 @@ private void maybeNotifyDroppedFrames() { @SuppressLint("InlinedApi") private static MediaFormat getMediaFormat(Format format, CodecMaxValues codecMaxValues, - boolean deviceNeedsAutoFrcWorkaround) { + boolean deviceNeedsAutoFrcWorkaround, int tunnelingAudioSessionId) { MediaFormat frameworkMediaFormat = format.getFrameworkMediaFormatV16(); // Set the maximum adaptive video dimensions. frameworkMediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, codecMaxValues.width); @@ -544,6 +581,11 @@ private static MediaFormat getMediaFormat(Format format, CodecMaxValues codecMax if (deviceNeedsAutoFrcWorkaround) { 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; } @@ -682,4 +724,22 @@ public CodecMaxValues(int width, int height, int inputSize) { } + @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(); + } + + } + }