Commit b61e7cdb by olly Committed by Oliver Woodman

Support 8-bit and 24-bit PCM with re-sampling.

This is a cleaned up version of the pull request below,
which will be merged with this change then applied on top.

https://github.com/google/ExoPlayer/pull/1490

Issue: #1246
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=121926682
parent d2c524d7
......@@ -105,8 +105,8 @@ public final class FlacExtractor implements Extractor {
});
Format mediaFormat = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW,
streamInfo.bitRate(), Format.NO_VALUE, streamInfo.channels, streamInfo.sampleRate, null,
null, null);
streamInfo.bitRate(), Format.NO_VALUE, streamInfo.channels, streamInfo.sampleRate,
C.ENCODING_PCM_16BIT, null, null, null);
trackOutput.format(mediaFormat);
outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize());
......
......@@ -60,6 +60,26 @@ public final class C {
public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR;
/**
* @see AudioFormat#ENCODING_INVALID
*/
public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID;
/**
* @see AudioFormat#ENCODING_PCM_8BIT
*/
public static final int ENCODING_PCM_8BIT = AudioFormat.ENCODING_PCM_8BIT;
/**
* @see AudioFormat#ENCODING_PCM_16BIT
*/
public static final int ENCODING_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT;
/**
* PCM encoding with 24 bits per sample.
*/
public static final int ENCODING_PCM_24BIT = 0x80000000;
/**
* @see AudioFormat#ENCODING_AC3
*/
@SuppressWarnings("InlinedApi")
......
......@@ -333,10 +333,11 @@ public final class FrameworkSampleSource implements SampleSource {
initializationData.add(data);
buffer.flip();
}
int pcmEncoding = MimeTypes.AUDIO_RAW.equals(mimeType) ? C.ENCODING_PCM_16BIT : Format.NO_VALUE;
Format format = new Format(Integer.toString(index), null, mimeType, Format.NO_VALUE,
maxInputSize, width, height, frameRate, rotationDegrees, Format.NO_VALUE, channelCount,
sampleRate, encoderDelay, encoderPadding, language, Format.OFFSET_SAMPLE_RELATIVE,
initializationData, drmInitData, false);
sampleRate, pcmEncoding, encoderDelay, encoderPadding, language,
Format.OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false);
format.setFrameworkMediaFormatV16(mediaFormat);
return format;
}
......
......@@ -70,6 +70,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
private boolean passthroughEnabled;
private android.media.MediaFormat passthroughMediaFormat;
private int pcmEncoding;
private int audioSessionId;
private long currentPositionUs;
private boolean allowPositionDiscontinuity;
......@@ -235,6 +236,15 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
}
@Override
protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException {
super.onInputFormatChanged(holder);
// If the input format is anything other than PCM then we assume that the audio decoder will
// output 16-bit PCM.
pcmEncoding = MimeTypes.AUDIO_RAW.equals(holder.format.sampleMimeType)
? holder.format.pcmEncoding : C.ENCODING_PCM_16BIT;
}
@Override
protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat) {
boolean passthrough = passthroughMediaFormat != null;
String mimeType = passthrough
......@@ -243,7 +253,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
android.media.MediaFormat format = passthrough ? passthroughMediaFormat : outputFormat;
int channelCount = format.getInteger(android.media.MediaFormat.KEY_CHANNEL_COUNT);
int sampleRate = format.getInteger(android.media.MediaFormat.KEY_SAMPLE_RATE);
audioTrack.configure(mimeType, channelCount, sampleRate);
audioTrack.configure(mimeType, channelCount, sampleRate, pcmEncoding);
}
/**
......
......@@ -1207,6 +1207,7 @@ public final class MatroskaExtractor implements Extractor {
public void initializeOutput(ExtractorOutput output, int trackId) throws ParserException {
String mimeType;
int maxInputSize = Format.NO_VALUE;
int pcmEncoding = Format.NO_VALUE;
List<byte[]> initializationData = null;
switch (codecId) {
case CODEC_ID_VP8:
......@@ -1291,13 +1292,15 @@ public final class MatroskaExtractor implements Extractor {
if (!parseMsAcmCodecPrivate(new ParsableByteArray(codecPrivate))) {
throw new ParserException("Non-PCM MS/ACM is unsupported");
}
if (audioBitDepth != 16) {
pcmEncoding = Util.getPcmEncoding(audioBitDepth);
if (pcmEncoding == C.ENCODING_INVALID) {
throw new ParserException("Unsupported PCM bit depth: " + audioBitDepth);
}
break;
case CODEC_ID_PCM_INT_LIT:
mimeType = MimeTypes.AUDIO_RAW;
if (audioBitDepth != 16) {
pcmEncoding = Util.getPcmEncoding(audioBitDepth);
if (pcmEncoding == C.ENCODING_INVALID) {
throw new ParserException("Unsupported PCM bit depth: " + audioBitDepth);
}
break;
......@@ -1320,8 +1323,8 @@ public final class MatroskaExtractor implements Extractor {
// into the trackId passed when creating the formats.
if (MimeTypes.isAudio(mimeType)) {
format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType,
Format.NO_VALUE, maxInputSize, channelCount, sampleRate, initializationData, language,
drmInitData);
Format.NO_VALUE, maxInputSize, channelCount, sampleRate, pcmEncoding,
initializationData, drmInitData, language);
} else if (MimeTypes.isVideo(mimeType)) {
if (displayUnit == Track.DISPLAY_UNIT_PIXELS) {
displayWidth = displayWidth == Format.NO_VALUE ? width : displayWidth;
......
......@@ -127,7 +127,7 @@ public final class Mp3Extractor implements Extractor {
extractorOutput.seekMap(seeker);
trackOutput.format(Format.createAudioSampleFormat(null, synchronizedHeader.mimeType,
Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, synchronizedHeader.channels,
synchronizedHeader.sampleRate, gaplessInfoHolder.encoderDelay,
synchronizedHeader.sampleRate, Format.NO_VALUE, gaplessInfoHolder.encoderDelay,
gaplessInfoHolder.encoderPadding, null, null, null));
}
return readSample(input);
......
......@@ -939,8 +939,8 @@ import java.util.List;
|| atomType == Atom.TYPE_dtsh || atomType == Atom.TYPE_dtsl)
&& childAtomType == Atom.TYPE_ddts) {
out.format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType,
Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, null, language,
drmInitData);
Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, null, drmInitData,
language);
return;
}
childAtomPosition += childAtomSize;
......@@ -951,10 +951,13 @@ import java.util.List;
return;
}
// TODO: Determine the correct PCM encoding.
int pcmEncoding = MimeTypes.AUDIO_RAW.equals(mimeType) ? C.ENCODING_PCM_16BIT : Format.NO_VALUE;
out.format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType,
Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate,
Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, pcmEncoding,
initializationData == null ? null : Collections.singletonList(initializationData),
language, drmInitData);
drmInitData, language);
}
/** Returns the position of the esds box within a parent, or -1 if no esds box is found */
......
......@@ -74,7 +74,7 @@ public final class WavExtractor implements Extractor, SeekMap {
}
Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW,
wavHeader.getBitrate(), MAX_INPUT_SIZE, wavHeader.getNumChannels(),
wavHeader.getSampleRateHz(), null, null, null);
wavHeader.getSampleRateHz(), wavHeader.getEncoding(), null, null, null);
trackOutput.format(format);
bytesPerFrame = wavHeader.getBytesPerFrame();
}
......
......@@ -30,22 +30,22 @@ import com.google.android.exoplayer.C;
private final int blockAlignment;
/** Bits per sample for the audio data. */
private final int bitsPerSample;
/** The pcm encoding */
private final int encoding;
/** Offset to the start of sample data. */
private long dataStartPosition;
/** Total size in bytes of the sample data. */
private long dataSize;
public WavHeader(
int numChannels,
int sampleRateHz,
int averageBytesPerSecond,
int blockAlignment,
int bitsPerSample) {
public WavHeader(int numChannels, int sampleRateHz, int averageBytesPerSecond, int blockAlignment,
int bitsPerSample, int encoding) {
this.numChannels = numChannels;
this.sampleRateHz = sampleRateHz;
this.averageBytesPerSecond = averageBytesPerSecond;
this.blockAlignment = blockAlignment;
this.bitsPerSample = bitsPerSample;
this.encoding = encoding;
}
/** Returns the duration in microseconds of this WAV. */
......@@ -110,4 +110,10 @@ import com.google.android.exoplayer.C;
this.dataStartPosition = dataStartPosition;
this.dataSize = dataSize;
}
/** Returns the PCM encoding. **/
public int getEncoding() {
return encoding;
}
}
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer.extractor.wav;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.util.Assertions;
......@@ -87,8 +88,10 @@ import java.io.IOException;
throw new ParserException("Expected block alignment: " + expectedBlockAlignment + "; got: "
+ blockAlignment);
}
if (bitsPerSample != 16) {
Log.e(TAG, "Only 16-bit WAVs are supported; got: " + bitsPerSample);
int encoding = Util.getPcmEncoding(bitsPerSample);
if (encoding == C.ENCODING_INVALID) {
Log.e(TAG, "Unsupported WAV bit depth: " + bitsPerSample);
return null;
}
......@@ -100,8 +103,8 @@ import java.io.IOException;
// If present, skip extensionSize, validBitsPerSample, channelMask, subFormatGuid, ...
input.advancePeekPosition((int) chunkHeader.size - 16);
return new WavHeader(
numChannels, sampleRateHz, averageBytesPerSecond, blockAlignment, bitsPerSample);
return new WavHeader(numChannels, sampleRateHz, averageBytesPerSecond, blockAlignment,
bitsPerSample, encoding);
}
/**
......
......@@ -80,7 +80,7 @@ public final class Ac3Util {
channelCount++;
}
return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_AC3, Format.NO_VALUE,
Format.NO_VALUE, channelCount, sampleRate, null, language, drmInitData);
Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, language);
}
/**
......@@ -107,7 +107,7 @@ public final class Ac3Util {
channelCount++;
}
return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_E_AC3, Format.NO_VALUE,
Format.NO_VALUE, channelCount, sampleRate, null, language, drmInitData);
Format.NO_VALUE, channelCount, sampleRate, null, drmInitData, language);
}
/**
......@@ -138,7 +138,7 @@ public final class Ac3Util {
boolean lfeon = data.readBit();
return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_AC3, Format.NO_VALUE,
Format.NO_VALUE, CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0),
SAMPLE_RATE_BY_FSCOD[fscod], null, language, drmInitData);
SAMPLE_RATE_BY_FSCOD[fscod], null, drmInitData, language);
}
/**
......@@ -166,7 +166,7 @@ public final class Ac3Util {
boolean lfeon = data.readBit();
return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_E_AC3, Format.NO_VALUE,
Format.NO_VALUE, CHANNEL_COUNT_BY_ACMOD[acmod] + (lfeon ? 1 : 0), sampleRate, null,
language, drmInitData);
drmInitData, language);
}
/**
......
......@@ -73,7 +73,7 @@ public final class DtsUtil {
frameBits.skipBits(10); // MIX, DYNF, TIMEF, AUXF, HDCD, EXT_AUDIO_ID, EXT_AUDIO, ASPF
channelCount += frameBits.readBits(2) > 0 ? 1 : 0; // LFF
return Format.createAudioSampleFormat(trackId, MimeTypes.AUDIO_DTS, bitrate, Format.NO_VALUE,
channelCount, sampleRate, null, language, drmInitData);
channelCount, sampleRate, null, drmInitData, language);
}
/**
......
......@@ -778,6 +778,27 @@ public final class Util {
}
/**
* Converts a sample bit depth to a corresponding PCM encoding constant.
*
* @param bitDepth The bit depth. Supported values are 8, 16 and 24.
* @return The corresponding encoding. One of {@link C#ENCODING_PCM_8BIT},
* {@link C#ENCODING_PCM_16BIT} and {@link C#ENCODING_PCM_24BIT}. If the bit depth is
* unsupported then {@link C#ENCODING_INVALID} is returned.
*/
public static int getPcmEncoding(int bitDepth) {
switch (bitDepth) {
case 8:
return C.ENCODING_PCM_8BIT;
case 16:
return C.ENCODING_PCM_16BIT;
case 24:
return C.ENCODING_PCM_24BIT;
default:
return C.ENCODING_INVALID;
}
}
/**
* Makes a best guess to infer the type from a file name.
*
* @param fileName Name of the file. It can include the path of the file.
......
......@@ -292,7 +292,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
int result = readSource(formatHolder, null);
if (result == TrackStream.FORMAT_READ) {
format = formatHolder.format;
audioTrack.configure(MimeTypes.AUDIO_RAW, format.channelCount, format.sampleRate);
audioTrack.configure(MimeTypes.AUDIO_RAW, format.channelCount, format.sampleRate,
C.ENCODING_PCM_16BIT);
return true;
}
return false;
......
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