Commit 0d86f447 by hoangtc Committed by Oliver Woodman

Remove the resampling to 16bit step from FlacDecoder.

Currently FlacDecoder/FlacExtractor always perform resampling to 16bit. In some
case (with 24bit audio), this might lower the audio quality if the system
supports 24bit audio.
Since AudioTrack implementation supports resampling, we will remove the
resampling step, and return an output with the same bits-per-sample as the original stream.
User can choose to re-sample to 16bit in AudioTrack if necessary.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=167494350
parent bab2ce81
...@@ -38,7 +38,7 @@ NDK_PATH="<path to Android NDK>" ...@@ -38,7 +38,7 @@ NDK_PATH="<path to Android NDK>"
``` ```
cd "${FLAC_EXT_PATH}/jni" && \ cd "${FLAC_EXT_PATH}/jni" && \
curl http://downloads.xiph.org/releases/flac/flac-1.3.1.tar.xz | tar xJ && \ curl https://ftp.osuosl.org/pub/xiph/releases/flac/flac-1.3.1.tar.xz | tar xJ && \
mv flac-1.3.1 flac mv flac-1.3.1 flac
``` ```
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.ext.flac; package com.google.android.exoplayer2.ext.flac;
import static com.google.android.exoplayer2.util.Util.getPcmEncoding;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
...@@ -122,10 +124,20 @@ public final class FlacExtractor implements Extractor { ...@@ -122,10 +124,20 @@ public final class FlacExtractor implements Extractor {
} }
}); });
Format mediaFormat =
Format mediaFormat = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW, null, Format.createAudioSampleFormat(
streamInfo.bitRate(), Format.NO_VALUE, streamInfo.channels, streamInfo.sampleRate, null,
C.ENCODING_PCM_16BIT, null, null, 0, null); MimeTypes.AUDIO_RAW,
null,
streamInfo.bitRate(),
Format.NO_VALUE,
streamInfo.channels,
streamInfo.sampleRate,
getPcmEncoding(streamInfo.bitsPerSample),
null,
null,
0,
null);
trackOutput.format(mediaFormat); trackOutput.format(mediaFormat);
outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize()); outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize());
......
...@@ -42,6 +42,9 @@ ...@@ -42,6 +42,9 @@
#define CHECK(x) \ #define CHECK(x) \
if (!(x)) ALOGE("Check failed: %s ", #x) if (!(x)) ALOGE("Check failed: %s ", #x)
const int endian = 1;
#define isBigEndian() (*(reinterpret_cast<const char *>(&endian)) == 0)
// The FLAC parser calls our C++ static callbacks using C calling conventions, // The FLAC parser calls our C++ static callbacks using C calling conventions,
// inside FLAC__stream_decoder_process_until_end_of_metadata // inside FLAC__stream_decoder_process_until_end_of_metadata
// and FLAC__stream_decoder_process_single. // and FLAC__stream_decoder_process_single.
...@@ -180,85 +183,42 @@ void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) { ...@@ -180,85 +183,42 @@ void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) {
mErrorStatus = status; mErrorStatus = status;
} }
// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved. // Copy samples from FLAC native 32-bit non-interleaved to
// correct bit-depth (non-zero padded), interleaved.
// These are candidates for optimization if needed. // These are candidates for optimization if needed.
static void copyToByteArrayBigEndian(int8_t *dst, const int *const *src,
static void copyMono8(int16_t *dst, const int *const *src, unsigned nSamples, unsigned bytesPerSample, unsigned nSamples,
unsigned /* nChannels */) { unsigned nChannels) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i] << 8;
}
}
static void copyStereo8(int16_t *dst, const int *const *src, unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i] << 8;
*dst++ = src[1][i] << 8;
}
}
static void copyMultiCh8(int16_t *dst, const int *const *src, unsigned nSamples,
unsigned nChannels) {
for (unsigned i = 0; i < nSamples; ++i) { for (unsigned i = 0; i < nSamples; ++i) {
for (unsigned c = 0; c < nChannels; ++c) { for (unsigned c = 0; c < nChannels; ++c) {
*dst++ = src[c][i] << 8; // point to the first byte of the source address
// and then skip the first few bytes (most significant bytes)
// depending on the bit depth
const int8_t *byteSrc =
reinterpret_cast<const int8_t *>(&src[c][i]) + 4 - bytesPerSample;
memcpy(dst, byteSrc, bytesPerSample);
dst = dst + bytesPerSample;
} }
} }
} }
static void copyMono16(int16_t *dst, const int *const *src, unsigned nSamples, static void copyToByteArrayLittleEndian(int8_t *dst, const int *const *src,
unsigned /* nChannels */) { unsigned bytesPerSample,
for (unsigned i = 0; i < nSamples; ++i) { unsigned nSamples, unsigned nChannels) {
*dst++ = src[0][i];
}
}
static void copyStereo16(int16_t *dst, const int *const *src, unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i];
*dst++ = src[1][i];
}
}
static void copyMultiCh16(int16_t *dst, const int *const *src,
unsigned nSamples, unsigned nChannels) {
for (unsigned i = 0; i < nSamples; ++i) { for (unsigned i = 0; i < nSamples; ++i) {
for (unsigned c = 0; c < nChannels; ++c) { for (unsigned c = 0; c < nChannels; ++c) {
*dst++ = src[c][i]; // with little endian, the most significant bytes will be at the end
// copy the bytes in little endian will remove the most significant byte
// so we are good here.
memcpy(dst, &(src[c][i]), bytesPerSample);
dst = dst + bytesPerSample;
} }
} }
} }
// 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger static void copyTrespass(int8_t * /* dst */, const int *const * /* src */,
unsigned /* bytesPerSample */, unsigned /* nSamples */,
static void copyMono24(int16_t *dst, const int *const *src, unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i] >> 8;
}
}
static void copyStereo24(int16_t *dst, const int *const *src, unsigned nSamples,
unsigned /* nChannels */) { unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
*dst++ = src[0][i] >> 8;
*dst++ = src[1][i] >> 8;
}
}
static void copyMultiCh24(int16_t *dst, const int *const *src,
unsigned nSamples, unsigned nChannels) {
for (unsigned i = 0; i < nSamples; ++i) {
for (unsigned c = 0; c < nChannels; ++c) {
*dst++ = src[c][i] >> 8;
}
}
}
static void copyTrespass(int16_t * /* dst */, const int *const * /* src */,
unsigned /* nSamples */, unsigned /* nChannels */) {
TRESPASS(); TRESPASS();
} }
...@@ -340,6 +300,7 @@ bool FLACParser::decodeMetadata() { ...@@ -340,6 +300,7 @@ bool FLACParser::decodeMetadata() {
case 8: case 8:
case 16: case 16:
case 24: case 24:
case 32:
break; break;
default: default:
ALOGE("unsupported bits per sample %u", getBitsPerSample()); ALOGE("unsupported bits per sample %u", getBitsPerSample());
...@@ -363,23 +324,11 @@ bool FLACParser::decodeMetadata() { ...@@ -363,23 +324,11 @@ bool FLACParser::decodeMetadata() {
ALOGE("unsupported sample rate %u", getSampleRate()); ALOGE("unsupported sample rate %u", getSampleRate());
return false; return false;
} }
// configure the appropriate copy function, defaulting to trespass // configure the appropriate copy function based on device endianness.
static const struct { if (isBigEndian()) {
unsigned mChannels; mCopy = copyToByteArrayBigEndian;
unsigned mBitsPerSample; } else {
void (*mCopy)(int16_t *dst, const int *const *src, unsigned nSamples, mCopy = copyToByteArrayLittleEndian;
unsigned nChannels);
} table[] = {
{1, 8, copyMono8}, {2, 8, copyStereo8}, {8, 8, copyMultiCh8},
{1, 16, copyMono16}, {2, 16, copyStereo16}, {8, 16, copyMultiCh16},
{1, 24, copyMono24}, {2, 24, copyStereo24}, {8, 24, copyMultiCh24},
};
for (unsigned i = 0; i < sizeof(table) / sizeof(table[0]); ++i) {
if (table[i].mChannels >= getChannels() &&
table[i].mBitsPerSample == getBitsPerSample()) {
mCopy = table[i].mCopy;
break;
}
} }
} else { } else {
ALOGE("missing STREAMINFO"); ALOGE("missing STREAMINFO");
...@@ -424,7 +373,8 @@ size_t FLACParser::readBuffer(void *output, size_t output_size) { ...@@ -424,7 +373,8 @@ size_t FLACParser::readBuffer(void *output, size_t output_size) {
return -1; return -1;
} }
size_t bufferSize = blocksize * getChannels() * sizeof(int16_t); unsigned bytesPerSample = getBitsPerSample() >> 3;
size_t bufferSize = blocksize * getChannels() * bytesPerSample;
if (bufferSize > output_size) { if (bufferSize > output_size) {
ALOGE( ALOGE(
"FLACParser::readBuffer not enough space in output buffer " "FLACParser::readBuffer not enough space in output buffer "
...@@ -434,8 +384,8 @@ size_t FLACParser::readBuffer(void *output, size_t output_size) { ...@@ -434,8 +384,8 @@ size_t FLACParser::readBuffer(void *output, size_t output_size) {
} }
// copy PCM from FLAC write buffer to our media buffer, with interleaving. // copy PCM from FLAC write buffer to our media buffer, with interleaving.
(*mCopy)(reinterpret_cast<int16_t *>(output), mWriteBuffer, blocksize, (*mCopy)(reinterpret_cast<int8_t *>(output), mWriteBuffer, bytesPerSample,
getChannels()); blocksize, getChannels());
// fill in buffer metadata // fill in buffer metadata
CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
......
...@@ -86,8 +86,8 @@ class FLACParser { ...@@ -86,8 +86,8 @@ class FLACParser {
private: private:
DataSource *mDataSource; DataSource *mDataSource;
void (*mCopy)(int16_t *dst, const int *const *src, unsigned nSamples, void (*mCopy)(int8_t *dst, const int *const *src, unsigned bytesPerSample,
unsigned nChannels); unsigned nSamples, unsigned nChannels);
// handle to underlying libFLAC parser // handle to underlying libFLAC parser
FLAC__StreamDecoder *mDecoder; FLAC__StreamDecoder *mDecoder;
......
...@@ -65,7 +65,7 @@ public final class FlacStreamInfo { ...@@ -65,7 +65,7 @@ public final class FlacStreamInfo {
} }
public int maxDecodedFrameSize() { public int maxDecodedFrameSize() {
return maxBlockSize * channels * 2; return maxBlockSize * channels * (bitsPerSample / 8);
} }
public int bitRate() { public int bitRate() {
......
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