mirror of
https://github.com/androidx/media.git
synced 2025-05-08 08:00:49 +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;
|
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 {
|
|||||||
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;
|
|||||||
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;
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
@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 {
|
|||||||
@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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
@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
|
||||||
@ -329,6 +343,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
pendingRotationDegrees = getRotationDegrees(newFormat);
|
pendingRotationDegrees = getRotationDegrees(newFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onQueueInputBuffer(DecoderInputBuffer buffer) {
|
||||||
|
if (Util.SDK_INT < 23 && tunneling) {
|
||||||
|
maybeNotifyRenderedFirstFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@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)
|
||||||
@ -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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
@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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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