mirror of
https://github.com/androidx/media.git
synced 2025-05-07 15:40:37 +08:00
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
This commit is contained in:
parent
5742c87755
commit
60a3eda1e0
@ -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 final class C {
|
||||
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;
|
||||
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 android.view.Surface;
|
||||
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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
pendingPixelWidthHeightRatio = Format.NO_VALUE;
|
||||
clearLastReportedVideoSize();
|
||||
frameReleaseTimeHelper.disable();
|
||||
tunnelingOnFrameRenderedListener = null;
|
||||
try {
|
||||
super.onDisabled();
|
||||
} finally {
|
||||
@ -288,11 +298,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
|
||||
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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
TraceUtil.endSection();
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
if (!renderedFirstFrame) {
|
||||
renderedFirstFrame = true;
|
||||
eventDispatcher.renderedFirstFrame(surface);
|
||||
}
|
||||
maybeNotifyRenderedFirstFrame();
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
@ -493,6 +511,25 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
|
||||
@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 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
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 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user