Commit 4d2e0bf1 by ojw28 Committed by GitHub

Merge pull request #3635 from drhill/dev-v2_24bitpcm

add support in mediacodecaudiorenderer for 24bit pcm to float
parents 13b46dab aaf469ce
...@@ -164,10 +164,12 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -164,10 +164,12 @@ public final class DefaultAudioSink implements AudioSink {
public static boolean failOnSpuriousAudioTimestamp = false; public static boolean failOnSpuriousAudioTimestamp = false;
@Nullable private final AudioCapabilities audioCapabilities; @Nullable private final AudioCapabilities audioCapabilities;
private final boolean canConvertHiResPcmToFloat;
private final ChannelMappingAudioProcessor channelMappingAudioProcessor; private final ChannelMappingAudioProcessor channelMappingAudioProcessor;
private final TrimmingAudioProcessor trimmingAudioProcessor; private final TrimmingAudioProcessor trimmingAudioProcessor;
private final SonicAudioProcessor sonicAudioProcessor; private final SonicAudioProcessor sonicAudioProcessor;
private final AudioProcessor[] availableAudioProcessors; private final AudioProcessor[] toIntPcmAvailableAudioProcessors;
private final AudioProcessor[] toFloatPcmAvailableAudioProcessors;
private final ConditionVariable releasingConditionVariable; private final ConditionVariable releasingConditionVariable;
private final long[] playheadOffsets; private final long[] playheadOffsets;
private final AudioTrackUtil audioTrackUtil; private final AudioTrackUtil audioTrackUtil;
...@@ -180,12 +182,14 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -180,12 +182,14 @@ public final class DefaultAudioSink implements AudioSink {
private AudioTrack keepSessionIdAudioTrack; private AudioTrack keepSessionIdAudioTrack;
private AudioTrack audioTrack; private AudioTrack audioTrack;
private boolean isInputPcm; private boolean isInputPcm;
private boolean shouldUpResPCMAudio;
private int inputSampleRate; private int inputSampleRate;
private int sampleRate; private int sampleRate;
private int channelConfig; private int channelConfig;
private @C.Encoding int outputEncoding; private @C.Encoding int outputEncoding;
private AudioAttributes audioAttributes; private AudioAttributes audioAttributes;
private boolean processingEnabled; private boolean processingEnabled;
private boolean canApplyPlaybackParams;
private int bufferSize; private int bufferSize;
private long bufferSizeUs; private long bufferSizeUs;
...@@ -233,6 +237,8 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -233,6 +237,8 @@ public final class DefaultAudioSink implements AudioSink {
private boolean hasData; private boolean hasData;
private long lastFeedElapsedRealtimeMs; private long lastFeedElapsedRealtimeMs;
/** /**
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the * @param audioCapabilities The audio capabilities for playback on this device. May be null if the
* default capabilities (no encoded audio passthrough support) should be assumed. * default capabilities (no encoded audio passthrough support) should be assumed.
...@@ -241,7 +247,23 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -241,7 +247,23 @@ public final class DefaultAudioSink implements AudioSink {
*/ */
public DefaultAudioSink(@Nullable AudioCapabilities audioCapabilities, public DefaultAudioSink(@Nullable AudioCapabilities audioCapabilities,
AudioProcessor[] audioProcessors) { AudioProcessor[] audioProcessors) {
this(audioCapabilities, audioProcessors, false);
}
/**
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
* default capabilities (no encoded audio passthrough support) should be assumed.
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio before
* output. May be empty.
* @param canConvertHiResPcmToFloat Flag to convert > 16bit PCM Audio to 32bit Float PCM Audio to
* avoid dithering the input audio. If enabled other audio processors that expect 16bit PCM
* are disabled
*/
public DefaultAudioSink(@Nullable AudioCapabilities audioCapabilities,
AudioProcessor[] audioProcessors, boolean canConvertHiResPcmToFloat) {
this.audioCapabilities = audioCapabilities; this.audioCapabilities = audioCapabilities;
this.canConvertHiResPcmToFloat = canConvertHiResPcmToFloat;
releasingConditionVariable = new ConditionVariable(true); releasingConditionVariable = new ConditionVariable(true);
if (Util.SDK_INT >= 18) { if (Util.SDK_INT >= 18) {
try { try {
...@@ -259,12 +281,14 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -259,12 +281,14 @@ public final class DefaultAudioSink implements AudioSink {
channelMappingAudioProcessor = new ChannelMappingAudioProcessor(); channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
trimmingAudioProcessor = new TrimmingAudioProcessor(); trimmingAudioProcessor = new TrimmingAudioProcessor();
sonicAudioProcessor = new SonicAudioProcessor(); sonicAudioProcessor = new SonicAudioProcessor();
availableAudioProcessors = new AudioProcessor[4 + audioProcessors.length]; toIntPcmAvailableAudioProcessors = new AudioProcessor[4 + audioProcessors.length];
availableAudioProcessors[0] = new ResamplingAudioProcessor(); toIntPcmAvailableAudioProcessors[0] = new ResamplingAudioProcessor();
availableAudioProcessors[1] = channelMappingAudioProcessor; toIntPcmAvailableAudioProcessors[1] = channelMappingAudioProcessor;
availableAudioProcessors[2] = trimmingAudioProcessor; toIntPcmAvailableAudioProcessors[2] = trimmingAudioProcessor;
System.arraycopy(audioProcessors, 0, availableAudioProcessors, 3, audioProcessors.length); System.arraycopy(audioProcessors, 0, toIntPcmAvailableAudioProcessors, 3, audioProcessors.length);
availableAudioProcessors[3 + audioProcessors.length] = sonicAudioProcessor; toIntPcmAvailableAudioProcessors[3 + audioProcessors.length] = sonicAudioProcessor;
toFloatPcmAvailableAudioProcessors = new AudioProcessor[1];
toFloatPcmAvailableAudioProcessors[0] = new FloatResamplingAudioProcessor();
playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT]; playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT];
volume = 1.0f; volume = 1.0f;
startMediaTimeState = START_NOT_SET; startMediaTimeState = START_NOT_SET;
...@@ -342,12 +366,17 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -342,12 +366,17 @@ public final class DefaultAudioSink implements AudioSink {
int channelCount = inputChannelCount; int channelCount = inputChannelCount;
int sampleRate = inputSampleRate; int sampleRate = inputSampleRate;
isInputPcm = isEncodingPcm(inputEncoding); isInputPcm = isEncodingPcm(inputEncoding);
shouldUpResPCMAudio = canConvertHiResPcmToFloat &&
(inputEncoding == C.ENCODING_PCM_24BIT || inputEncoding == C.ENCODING_PCM_32BIT);
if (isInputPcm) { if (isInputPcm) {
pcmFrameSize = Util.getPcmFrameSize(inputEncoding, channelCount); pcmFrameSize = Util.getPcmFrameSize(inputEncoding, channelCount);
} }
@C.Encoding int encoding = inputEncoding; @C.Encoding int encoding = inputEncoding;
boolean processingEnabled = isInputPcm && inputEncoding != C.ENCODING_PCM_FLOAT; boolean processingEnabled = isInputPcm && inputEncoding != C.ENCODING_PCM_FLOAT;
canApplyPlaybackParams = processingEnabled && !shouldUpResPCMAudio;
if (processingEnabled) { if (processingEnabled) {
AudioProcessor[] availableAudioProcessors = shouldUpResPCMAudio ?
toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
trimmingAudioProcessor.setTrimSampleCount(trimStartSamples, trimEndSamples); trimmingAudioProcessor.setTrimSampleCount(trimStartSamples, trimEndSamples);
channelMappingAudioProcessor.setChannelMap(outputChannels); channelMappingAudioProcessor.setChannelMap(outputChannels);
for (AudioProcessor audioProcessor : availableAudioProcessors) { for (AudioProcessor audioProcessor : availableAudioProcessors) {
...@@ -460,6 +489,8 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -460,6 +489,8 @@ public final class DefaultAudioSink implements AudioSink {
private void resetAudioProcessors() { private void resetAudioProcessors() {
ArrayList<AudioProcessor> newAudioProcessors = new ArrayList<>(); ArrayList<AudioProcessor> newAudioProcessors = new ArrayList<>();
AudioProcessor[] availableAudioProcessors = shouldUpResPCMAudio ?
toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
for (AudioProcessor audioProcessor : availableAudioProcessors) { for (AudioProcessor audioProcessor : availableAudioProcessors) {
if (audioProcessor.isActive()) { if (audioProcessor.isActive()) {
newAudioProcessors.add(audioProcessor); newAudioProcessors.add(audioProcessor);
...@@ -808,7 +839,7 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -808,7 +839,7 @@ public final class DefaultAudioSink implements AudioSink {
@Override @Override
public PlaybackParameters setPlaybackParameters(PlaybackParameters playbackParameters) { public PlaybackParameters setPlaybackParameters(PlaybackParameters playbackParameters) {
if (isInitialized() && !processingEnabled) { if (isInitialized() && !canApplyPlaybackParams) {
// The playback parameters are always the default if processing is disabled. // The playback parameters are always the default if processing is disabled.
this.playbackParameters = PlaybackParameters.DEFAULT; this.playbackParameters = PlaybackParameters.DEFAULT;
return this.playbackParameters; return this.playbackParameters;
...@@ -964,7 +995,10 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -964,7 +995,10 @@ public final class DefaultAudioSink implements AudioSink {
public void release() { public void release() {
reset(); reset();
releaseKeepSessionIdAudioTrack(); releaseKeepSessionIdAudioTrack();
for (AudioProcessor audioProcessor : availableAudioProcessors) { for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
audioProcessor.reset();
}
for (AudioProcessor audioProcessor : toFloatPcmAvailableAudioProcessors) {
audioProcessor.reset(); audioProcessor.reset();
} }
audioSessionId = C.AUDIO_SESSION_ID_UNSET; audioSessionId = C.AUDIO_SESSION_ID_UNSET;
......
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.audio;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* An {@link AudioProcessor} that converts audio data to {@link C#ENCODING_PCM_16BIT}.
*/
/* package */ final class FloatResamplingAudioProcessor implements AudioProcessor {
private int sampleRateHz;
private static final double PCM_INT32_FLOAT = 1.0 / 0x7fffffff;
private int channelCount;
@C.PcmEncoding
private int sourceEncoding;
private ByteBuffer buffer;
private ByteBuffer outputBuffer;
private boolean inputEnded;
/**
* Creates a new audio processor that converts audio data to {@link C#ENCODING_PCM_16BIT}.
*/
public FloatResamplingAudioProcessor() {
sampleRateHz = Format.NO_VALUE;
channelCount = Format.NO_VALUE;
sourceEncoding = C.ENCODING_INVALID;
buffer = EMPTY_BUFFER;
outputBuffer = EMPTY_BUFFER;
}
@Override
public boolean configure(int sampleRateHz, int channelCount, @C.Encoding int encoding)
throws AudioProcessor.UnhandledFormatException {
if (encoding != C.ENCODING_PCM_24BIT && encoding != C.ENCODING_PCM_32BIT) {
throw new AudioProcessor.UnhandledFormatException(sampleRateHz, channelCount, encoding);
}
if (this.sampleRateHz == sampleRateHz && this.channelCount == channelCount
&& this.sourceEncoding == encoding) {
return false;
}
this.sampleRateHz = sampleRateHz;
this.channelCount = channelCount;
this.sourceEncoding = encoding;
return true;
}
@Override
public boolean isActive() {
return sourceEncoding == C.ENCODING_PCM_24BIT || sourceEncoding == C.ENCODING_PCM_32BIT;
}
@Override
public int getOutputChannelCount() { return channelCount; }
@Override
public int getOutputEncoding() { return C.ENCODING_PCM_FLOAT; }
@Override
public int getOutputSampleRateHz() {
return sampleRateHz;
}
@Override
public void queueInput(ByteBuffer inputBuffer) {
int offset = inputBuffer.position();
int limit = inputBuffer.limit();
int size = limit - offset;
int resampledSize;
switch (sourceEncoding) {
case C.ENCODING_PCM_24BIT:
resampledSize = (size / 3) * 4;
break;
case C.ENCODING_PCM_32BIT:
resampledSize = size;
break;
case C.ENCODING_PCM_8BIT:
case C.ENCODING_PCM_16BIT:
case C.ENCODING_PCM_FLOAT:
case C.ENCODING_INVALID:
case Format.NO_VALUE:
default:
// Never happens.
throw new IllegalStateException();
}
if (buffer.capacity() < resampledSize) {
buffer = ByteBuffer.allocateDirect(resampledSize).order(ByteOrder.nativeOrder());
} else {
buffer.clear();
}
// Samples are little endian.
switch (sourceEncoding) {
case C.ENCODING_PCM_24BIT:
// 24->32 bit resampling.
for (int i = offset; i < limit; i += 3) {
int val = (inputBuffer.get(i) << 8) & 0x0000ff00 | (inputBuffer.get(i + 1) << 16) & 0x00ff0000 |
(inputBuffer.get(i + 2) << 24) & 0xff000000;
writePcm32bitFloat(val, buffer);
}
break;
case C.ENCODING_PCM_32BIT:
// 32->32 bit conversion.
for (int i = offset; i < limit; i += 4) {
int val = inputBuffer.get(i) & 0x000000ff | (inputBuffer.get(i) << 8) & 0x0000ff00 |
(inputBuffer.get(i + 1) << 16) & 0x00ff0000 | (inputBuffer.get(i + 2) << 24) & 0xff000000;
writePcm32bitFloat(val, buffer);
}
break;
case C.ENCODING_PCM_8BIT:
case C.ENCODING_PCM_16BIT:
case C.ENCODING_PCM_FLOAT:
case C.ENCODING_INVALID:
case Format.NO_VALUE:
default:
// Never happens.
throw new IllegalStateException();
}
inputBuffer.position(inputBuffer.limit());
buffer.flip();
outputBuffer = buffer;
}
@Override
public void queueEndOfStream() {
inputEnded = true;
}
@Override
public ByteBuffer getOutput() {
ByteBuffer outputBuffer = this.outputBuffer;
this.outputBuffer = EMPTY_BUFFER;
return outputBuffer;
}
@SuppressWarnings("ReferenceEquality")
@Override
public boolean isEnded() {
return inputEnded && outputBuffer == EMPTY_BUFFER;
}
@Override
public void flush() {
outputBuffer = EMPTY_BUFFER;
inputEnded = false;
}
@Override
public void reset() {
flush();
buffer = EMPTY_BUFFER;
sampleRateHz = Format.NO_VALUE;
channelCount = Format.NO_VALUE;
sourceEncoding = C.ENCODING_INVALID;
}
/**
* Converts the provided value into 32-bit float PCM and writes to buffer.
*
* @param val 32-bit int value to convert to 32-bit float [-1.0, 1.0]
* @param buffer The output buffer.
*/
private static void writePcm32bitFloat(int val, ByteBuffer buffer) {
float convVal = (float) (PCM_INT32_FLOAT * val);
int bits = Float.floatToIntBits(convVal);
if (bits == 0x7fc00000)
bits = Float.floatToIntBits((float) 0.0);
buffer.putInt(bits);
}
}
\ No newline at end of file
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