Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support in mediacodecaudiorenderer for 24bit pcm to float #3635

Merged
merged 3 commits into from
Jan 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,12 @@ public InvalidAudioTrackTimestampException(String message) {
public static boolean failOnSpuriousAudioTimestamp = false;

@Nullable private final AudioCapabilities audioCapabilities;
private final boolean canConvertHiResPcmToFloat;
private final ChannelMappingAudioProcessor channelMappingAudioProcessor;
private final TrimmingAudioProcessor trimmingAudioProcessor;
private final SonicAudioProcessor sonicAudioProcessor;
private final AudioProcessor[] availableAudioProcessors;
private final AudioProcessor[] toIntPcmAvailableAudioProcessors;
private final AudioProcessor[] toFloatPcmAvailableAudioProcessors;
private final ConditionVariable releasingConditionVariable;
private final long[] playheadOffsets;
private final AudioTrackUtil audioTrackUtil;
Expand All @@ -180,12 +182,14 @@ public InvalidAudioTrackTimestampException(String message) {
private AudioTrack keepSessionIdAudioTrack;
private AudioTrack audioTrack;
private boolean isInputPcm;
private boolean shouldUpResPCMAudio;
private int inputSampleRate;
private int sampleRate;
private int channelConfig;
private @C.Encoding int outputEncoding;
private AudioAttributes audioAttributes;
private boolean processingEnabled;
private boolean canApplyPlaybackParams;
private int bufferSize;
private long bufferSizeUs;

Expand Down Expand Up @@ -233,6 +237,8 @@ public InvalidAudioTrackTimestampException(String message) {
private boolean hasData;
private long lastFeedElapsedRealtimeMs;



/**
* @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.
Expand All @@ -241,7 +247,23 @@ public InvalidAudioTrackTimestampException(String message) {
*/
public DefaultAudioSink(@Nullable AudioCapabilities audioCapabilities,
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.canConvertHiResPcmToFloat = canConvertHiResPcmToFloat;
releasingConditionVariable = new ConditionVariable(true);
if (Util.SDK_INT >= 18) {
try {
Expand All @@ -259,12 +281,14 @@ public DefaultAudioSink(@Nullable AudioCapabilities audioCapabilities,
channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
trimmingAudioProcessor = new TrimmingAudioProcessor();
sonicAudioProcessor = new SonicAudioProcessor();
availableAudioProcessors = new AudioProcessor[4 + audioProcessors.length];
availableAudioProcessors[0] = new ResamplingAudioProcessor();
availableAudioProcessors[1] = channelMappingAudioProcessor;
availableAudioProcessors[2] = trimmingAudioProcessor;
System.arraycopy(audioProcessors, 0, availableAudioProcessors, 3, audioProcessors.length);
availableAudioProcessors[3 + audioProcessors.length] = sonicAudioProcessor;
toIntPcmAvailableAudioProcessors = new AudioProcessor[4 + audioProcessors.length];
toIntPcmAvailableAudioProcessors[0] = new ResamplingAudioProcessor();
toIntPcmAvailableAudioProcessors[1] = channelMappingAudioProcessor;
toIntPcmAvailableAudioProcessors[2] = trimmingAudioProcessor;
System.arraycopy(audioProcessors, 0, toIntPcmAvailableAudioProcessors, 3, audioProcessors.length);
toIntPcmAvailableAudioProcessors[3 + audioProcessors.length] = sonicAudioProcessor;
toFloatPcmAvailableAudioProcessors = new AudioProcessor[1];
toFloatPcmAvailableAudioProcessors[0] = new FloatResamplingAudioProcessor();
playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT];
volume = 1.0f;
startMediaTimeState = START_NOT_SET;
Expand Down Expand Up @@ -342,12 +366,17 @@ public void configure(@C.Encoding int inputEncoding, int inputChannelCount, int
int channelCount = inputChannelCount;
int sampleRate = inputSampleRate;
isInputPcm = isEncodingPcm(inputEncoding);
shouldUpResPCMAudio = canConvertHiResPcmToFloat &&
(inputEncoding == C.ENCODING_PCM_24BIT || inputEncoding == C.ENCODING_PCM_32BIT);
if (isInputPcm) {
pcmFrameSize = Util.getPcmFrameSize(inputEncoding, channelCount);
}
@C.Encoding int encoding = inputEncoding;
boolean processingEnabled = isInputPcm && inputEncoding != C.ENCODING_PCM_FLOAT;
canApplyPlaybackParams = processingEnabled && !shouldUpResPCMAudio;
if (processingEnabled) {
AudioProcessor[] availableAudioProcessors = shouldUpResPCMAudio ?
toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
trimmingAudioProcessor.setTrimSampleCount(trimStartSamples, trimEndSamples);
channelMappingAudioProcessor.setChannelMap(outputChannels);
for (AudioProcessor audioProcessor : availableAudioProcessors) {
Expand Down Expand Up @@ -460,6 +489,8 @@ public void configure(@C.Encoding int inputEncoding, int inputChannelCount, int

private void resetAudioProcessors() {
ArrayList<AudioProcessor> newAudioProcessors = new ArrayList<>();
AudioProcessor[] availableAudioProcessors = shouldUpResPCMAudio ?
toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
for (AudioProcessor audioProcessor : availableAudioProcessors) {
if (audioProcessor.isActive()) {
newAudioProcessors.add(audioProcessor);
Expand Down Expand Up @@ -808,7 +839,7 @@ public boolean hasPendingData() {

@Override
public PlaybackParameters setPlaybackParameters(PlaybackParameters playbackParameters) {
if (isInitialized() && !processingEnabled) {
if (isInitialized() && !canApplyPlaybackParams) {
// The playback parameters are always the default if processing is disabled.
this.playbackParameters = PlaybackParameters.DEFAULT;
return this.playbackParameters;
Expand Down Expand Up @@ -964,7 +995,10 @@ public void run() {
public void release() {
reset();
releaseKeepSessionIdAudioTrack();
for (AudioProcessor audioProcessor : availableAudioProcessors) {
for (AudioProcessor audioProcessor : toIntPcmAvailableAudioProcessors) {
audioProcessor.reset();
}
for (AudioProcessor audioProcessor : toFloatPcmAvailableAudioProcessors) {
audioProcessor.reset();
}
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* 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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please could you add an AOSP header? (Just copy from another file and replace the year with 2018.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check



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);
}

}