mirror of
https://github.com/androidx/media.git
synced 2025-05-05 06:30:24 +08:00
Add support for DTS passthrough on supporting devices before API 23.
NVIDIA Shield before API 23 supports DTS passthrough, so this change inlines the constant value.
This commit is contained in:
parent
a5ebb49a1a
commit
a764b359e8
@ -68,6 +68,11 @@ public final class C {
|
|||||||
@SuppressWarnings("InlinedApi")
|
@SuppressWarnings("InlinedApi")
|
||||||
public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3;
|
public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3;
|
||||||
|
|
||||||
|
// TODO: Switch these constants to use AudioFormat fields when the target API version is >= 23.
|
||||||
|
// The inlined values here are for NVIDIA Shield devices which support DTS on earlier versions.
|
||||||
|
public static final int ENCODING_DTS = 7;
|
||||||
|
public static final int ENCODING_DTS_HD = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see MediaExtractor#SAMPLE_FLAG_SYNC
|
* @see MediaExtractor#SAMPLE_FLAG_SYNC
|
||||||
*/
|
*/
|
||||||
|
@ -202,7 +202,7 @@ public final class AudioTrack {
|
|||||||
private int temporaryBufferSize;
|
private int temporaryBufferSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bitrate measured in kilobits per second, if {@link #isPassthrough()} returns true.
|
* Bitrate measured in kilobits per second, if using passthrough.
|
||||||
*/
|
*/
|
||||||
private int passthroughBitrate;
|
private int passthroughBitrate;
|
||||||
|
|
||||||
@ -359,7 +359,7 @@ public final class AudioTrack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audioTrackUtil.reconfigure(audioTrack, isPassthrough());
|
audioTrackUtil.reconfigure(audioTrack, needsPassthroughWorkarounds());
|
||||||
setAudioTrackVolume();
|
setAudioTrackVolume();
|
||||||
|
|
||||||
return sessionId;
|
return sessionId;
|
||||||
@ -472,8 +472,7 @@ public final class AudioTrack {
|
|||||||
return RESULT_BUFFER_CONSUMED;
|
return RESULT_BUFFER_CONSUMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workarounds for issues with AC-3 passthrough AudioTracks on API versions 21/22:
|
if (needsPassthroughWorkarounds()) {
|
||||||
if (Util.SDK_INT <= 22 && isPassthrough()) {
|
|
||||||
// An AC-3 audio track continues to play data written while it is paused. Stop writing so its
|
// An AC-3 audio track continues to play data written while it is paused. Stop writing so its
|
||||||
// buffer empties. See [Internal: b/18899620].
|
// buffer empties. See [Internal: b/18899620].
|
||||||
if (audioTrack.getPlayState() == android.media.AudioTrack.PLAYSTATE_PAUSED) {
|
if (audioTrack.getPlayState() == android.media.AudioTrack.PLAYSTATE_PAUSED) {
|
||||||
@ -491,8 +490,14 @@ public final class AudioTrack {
|
|||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
if (temporaryBufferSize == 0) {
|
if (temporaryBufferSize == 0) {
|
||||||
if (isPassthrough() && passthroughBitrate == UNKNOWN_BITRATE) {
|
if (passthroughBitrate == UNKNOWN_BITRATE) {
|
||||||
passthroughBitrate = Ac3Util.getBitrate(size, sampleRate);
|
if (isAc3Passthrough()) {
|
||||||
|
passthroughBitrate = Ac3Util.getBitrate(size, sampleRate);
|
||||||
|
} else if (isDtsPassthrough()) {
|
||||||
|
int unscaledBitrate = size * 8 * sampleRate;
|
||||||
|
int divisor = 1000 * 512;
|
||||||
|
passthroughBitrate = (unscaledBitrate + divisor / 2) / divisor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the first time we've seen this {@code buffer}.
|
// This is the first time we've seen this {@code buffer}.
|
||||||
@ -583,7 +588,7 @@ public final class AudioTrack {
|
|||||||
public boolean hasPendingData() {
|
public boolean hasPendingData() {
|
||||||
return isInitialized()
|
return isInitialized()
|
||||||
&& (bytesToFrames(submittedBytes) > audioTrackUtil.getPlaybackHeadPosition()
|
&& (bytesToFrames(submittedBytes) > audioTrackUtil.getPlaybackHeadPosition()
|
||||||
|| audioTrackUtil.overrideHasPendingData());
|
|| overrideHasPendingData());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the playback volume. */
|
/** Sets the playback volume. */
|
||||||
@ -709,10 +714,13 @@ public final class AudioTrack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't sample the timestamp and latency if this is a passthrough AudioTrack, as the returned
|
if (needsPassthroughWorkarounds()) {
|
||||||
// values cause audio/video synchronization to be incorrect.
|
// Don't sample the timestamp and latency if this is an AC-3 passthrough AudioTrack on
|
||||||
if (!isPassthrough()
|
// platform API versions 21/22, as incorrect values are returned. See [Internal: b/21145353].
|
||||||
&& systemClockUs - lastTimestampSampleTimeUs >= MIN_TIMESTAMP_SAMPLE_INTERVAL_US) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (systemClockUs - lastTimestampSampleTimeUs >= MIN_TIMESTAMP_SAMPLE_INTERVAL_US) {
|
||||||
audioTimestampSet = audioTrackUtil.updateTimestamp();
|
audioTimestampSet = audioTrackUtil.updateTimestamp();
|
||||||
if (audioTimestampSet) {
|
if (audioTimestampSet) {
|
||||||
// Perform sanity checks on the timestamp.
|
// Perform sanity checks on the timestamp.
|
||||||
@ -818,17 +826,50 @@ public final class AudioTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPassthrough() {
|
private boolean isPassthrough() {
|
||||||
|
return isAc3Passthrough() || isDtsPassthrough();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAc3Passthrough() {
|
||||||
return encoding == C.ENCODING_AC3 || encoding == C.ENCODING_E_AC3;
|
return encoding == C.ENCODING_AC3 || encoding == C.ENCODING_E_AC3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isDtsPassthrough() {
|
||||||
|
return encoding == C.ENCODING_DTS || encoding == C.ENCODING_DTS_HD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether to work around problems with passthrough audio tracks.
|
||||||
|
* See [Internal: b/18899620, b/19187573, b/21145353].
|
||||||
|
*/
|
||||||
|
private boolean needsPassthroughWorkarounds() {
|
||||||
|
return Util.SDK_INT < 23 && isAc3Passthrough();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the audio track should behave as though it has pending data. This is to work
|
||||||
|
* around an issue on platform API versions 21/22 where AC-3 audio tracks can't be paused, so we
|
||||||
|
* empty their buffers when paused. In this case, they should still behave as if they have
|
||||||
|
* pending data, otherwise writing will never resume.
|
||||||
|
*/
|
||||||
|
private boolean overrideHasPendingData() {
|
||||||
|
return needsPassthroughWorkarounds()
|
||||||
|
&& audioTrack.getPlayState() == android.media.AudioTrack.PLAYSTATE_PAUSED
|
||||||
|
&& audioTrack.getPlaybackHeadPosition() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
private static int getEncodingForMimeType(String mimeType) {
|
private static int getEncodingForMimeType(String mimeType) {
|
||||||
if (MimeTypes.AUDIO_AC3.equals(mimeType)) {
|
switch (mimeType) {
|
||||||
return C.ENCODING_AC3;
|
case MimeTypes.AUDIO_AC3:
|
||||||
|
return C.ENCODING_AC3;
|
||||||
|
case MimeTypes.AUDIO_EC3:
|
||||||
|
return C.ENCODING_E_AC3;
|
||||||
|
case MimeTypes.AUDIO_DTS:
|
||||||
|
return C.ENCODING_DTS;
|
||||||
|
case MimeTypes.AUDIO_DTS_HD:
|
||||||
|
return C.ENCODING_DTS_HD;
|
||||||
|
default:
|
||||||
|
return AudioFormat.ENCODING_INVALID;
|
||||||
}
|
}
|
||||||
if (MimeTypes.AUDIO_EC3.equals(mimeType)) {
|
|
||||||
return C.ENCODING_E_AC3;
|
|
||||||
}
|
|
||||||
return AudioFormat.ENCODING_INVALID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -837,7 +878,7 @@ public final class AudioTrack {
|
|||||||
private static class AudioTrackUtil {
|
private static class AudioTrackUtil {
|
||||||
|
|
||||||
protected android.media.AudioTrack audioTrack;
|
protected android.media.AudioTrack audioTrack;
|
||||||
private boolean isPassthrough;
|
private boolean needsPassthroughWorkaround;
|
||||||
private int sampleRate;
|
private int sampleRate;
|
||||||
private long lastRawPlaybackHeadPosition;
|
private long lastRawPlaybackHeadPosition;
|
||||||
private long rawPlaybackHeadWrapCount;
|
private long rawPlaybackHeadWrapCount;
|
||||||
@ -851,11 +892,13 @@ public final class AudioTrack {
|
|||||||
* Reconfigures the audio track utility helper to use the specified {@code audioTrack}.
|
* Reconfigures the audio track utility helper to use the specified {@code audioTrack}.
|
||||||
*
|
*
|
||||||
* @param audioTrack The audio track to wrap.
|
* @param audioTrack The audio track to wrap.
|
||||||
* @param isPassthrough Whether the audio track is used for passthrough (e.g. AC-3) playback.
|
* @param needsPassthroughWorkaround Whether to workaround issues with pausing AC-3 passthrough
|
||||||
|
* audio tracks on platform API version 21/22.
|
||||||
*/
|
*/
|
||||||
public void reconfigure(android.media.AudioTrack audioTrack, boolean isPassthrough) {
|
public void reconfigure(android.media.AudioTrack audioTrack,
|
||||||
|
boolean needsPassthroughWorkaround) {
|
||||||
this.audioTrack = audioTrack;
|
this.audioTrack = audioTrack;
|
||||||
this.isPassthrough = isPassthrough;
|
this.needsPassthroughWorkaround = needsPassthroughWorkaround;
|
||||||
stopTimestampUs = -1;
|
stopTimestampUs = -1;
|
||||||
lastRawPlaybackHeadPosition = 0;
|
lastRawPlaybackHeadPosition = 0;
|
||||||
rawPlaybackHeadWrapCount = 0;
|
rawPlaybackHeadWrapCount = 0;
|
||||||
@ -865,20 +908,6 @@ public final class AudioTrack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the audio track should behave as though it has pending data. This is to work
|
|
||||||
* around an issue on platform API versions 21/22 where AC-3 audio tracks can't be paused, so we
|
|
||||||
* empty their buffers when paused. In this case, they should still behave as if they have
|
|
||||||
* pending data, otherwise writing will never resume.
|
|
||||||
*
|
|
||||||
* @see #handleBuffer
|
|
||||||
*/
|
|
||||||
public boolean overrideHasPendingData() {
|
|
||||||
return Util.SDK_INT <= 22 && isPassthrough
|
|
||||||
&& audioTrack.getPlayState() == android.media.AudioTrack.PLAYSTATE_PAUSED
|
|
||||||
&& audioTrack.getPlaybackHeadPosition() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the audio track in a way that ensures media written to it is played out in full, and
|
* Stops the audio track in a way that ensures media written to it is played out in full, and
|
||||||
* that {@link #getPlaybackHeadPosition()} and {@link #getPlaybackHeadPositionUs()} continue to
|
* that {@link #getPlaybackHeadPosition()} and {@link #getPlaybackHeadPositionUs()} continue to
|
||||||
@ -929,7 +958,7 @@ public final class AudioTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
long rawPlaybackHeadPosition = 0xFFFFFFFFL & audioTrack.getPlaybackHeadPosition();
|
long rawPlaybackHeadPosition = 0xFFFFFFFFL & audioTrack.getPlaybackHeadPosition();
|
||||||
if (Util.SDK_INT <= 22 && isPassthrough) {
|
if (needsPassthroughWorkaround) {
|
||||||
// Work around an issue with passthrough/direct AudioTracks on platform API versions 21/22
|
// Work around an issue with passthrough/direct AudioTracks on platform API versions 21/22
|
||||||
// where the playback head position jumps back to zero on paused passthrough/direct audio
|
// where the playback head position jumps back to zero on paused passthrough/direct audio
|
||||||
// tracks. See [Internal: b/19187573].
|
// tracks. See [Internal: b/19187573].
|
||||||
@ -1009,8 +1038,9 @@ public final class AudioTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reconfigure(android.media.AudioTrack audioTrack, boolean isPassthrough) {
|
public void reconfigure(android.media.AudioTrack audioTrack,
|
||||||
super.reconfigure(audioTrack, isPassthrough);
|
boolean needsPassthroughWorkaround) {
|
||||||
|
super.reconfigure(audioTrack, needsPassthroughWorkaround);
|
||||||
rawTimestampFramePositionWrapCount = 0;
|
rawTimestampFramePositionWrapCount = 0;
|
||||||
lastRawTimestampFramePosition = 0;
|
lastRawTimestampFramePosition = 0;
|
||||||
lastTimestampFramePosition = 0;
|
lastTimestampFramePosition = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user