Cleanup merged pull requests

This commit is contained in:
Oliver Woodman 2018-01-24 10:51:05 +00:00
parent 4d2e0bf122
commit d098effa69
7 changed files with 120 additions and 122 deletions

View File

@ -66,8 +66,12 @@
* Fix ID3 context reuse across segment format changes
([#3622](https://github.com/google/ExoPlayer/issues/3622)).
* New Cast extension: Simplifies toggling between local and Cast playbacks.
* Audio: Support TrueHD passthrough for rechunked samples in Matroska files
([#2147](https://github.com/google/ExoPlayer/issues/2147)).
* Audio:
* Support TrueHD passthrough for rechunked samples in Matroska files
([#2147](https://github.com/google/ExoPlayer/issues/2147)).
* Support resampling 24-bit and 32-bit integer to 32-bit float for high
resolution output in `DefaultAudioSink`
([#3635](https://github.com/google/ExoPlayer/pull/3635)).
* Captions: Initial support for PGS subtitles
([#3008](https://github.com/google/ExoPlayer/issues/3008)).
* CacheDataSource: Check periodically if it's possible to read from/write to

View File

@ -164,7 +164,7 @@ public final class DefaultAudioSink implements AudioSink {
public static boolean failOnSpuriousAudioTimestamp = false;
@Nullable private final AudioCapabilities audioCapabilities;
private final boolean canConvertHiResPcmToFloat;
private final boolean enableConvertHighResIntPcmToFloat;
private final ChannelMappingAudioProcessor channelMappingAudioProcessor;
private final TrimmingAudioProcessor trimmingAudioProcessor;
private final SonicAudioProcessor sonicAudioProcessor;
@ -182,14 +182,14 @@ public final class DefaultAudioSink implements AudioSink {
private AudioTrack keepSessionIdAudioTrack;
private AudioTrack audioTrack;
private boolean isInputPcm;
private boolean shouldUpResPCMAudio;
private boolean shouldConvertHighResIntPcmToFloat;
private int inputSampleRate;
private int sampleRate;
private int channelConfig;
private @C.Encoding int outputEncoding;
private AudioAttributes audioAttributes;
private boolean processingEnabled;
private boolean canApplyPlaybackParams;
private boolean canApplyPlaybackParameters;
private int bufferSize;
private long bufferSizeUs;
@ -237,8 +237,6 @@ public final class DefaultAudioSink implements AudioSink {
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.
@ -247,7 +245,7 @@ public final class DefaultAudioSink implements AudioSink {
*/
public DefaultAudioSink(@Nullable AudioCapabilities audioCapabilities,
AudioProcessor[] audioProcessors) {
this(audioCapabilities, audioProcessors, false);
this(audioCapabilities, audioProcessors, /* enableConvertHighResIntPcmToFloat= */ false);
}
/**
@ -255,15 +253,17 @@ public final class DefaultAudioSink implements AudioSink {
* 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
* @param enableConvertHighResIntPcmToFloat Whether to enable conversion of high resolution
* integer PCM to 32-bit float for output, if possible. Functionality that uses 16-bit integer
* audio processing (for example, speed and pitch adjustment) will not be available when float
* output is in use.
*/
public DefaultAudioSink(@Nullable AudioCapabilities audioCapabilities,
AudioProcessor[] audioProcessors, boolean canConvertHiResPcmToFloat) {
public DefaultAudioSink(
@Nullable AudioCapabilities audioCapabilities,
AudioProcessor[] audioProcessors,
boolean enableConvertHighResIntPcmToFloat) {
this.audioCapabilities = audioCapabilities;
this.canConvertHiResPcmToFloat = canConvertHiResPcmToFloat;
this.enableConvertHighResIntPcmToFloat = enableConvertHighResIntPcmToFloat;
releasingConditionVariable = new ConditionVariable(true);
if (Util.SDK_INT >= 18) {
try {
@ -285,10 +285,10 @@ public final class DefaultAudioSink implements AudioSink {
toIntPcmAvailableAudioProcessors[0] = new ResamplingAudioProcessor();
toIntPcmAvailableAudioProcessors[1] = channelMappingAudioProcessor;
toIntPcmAvailableAudioProcessors[2] = trimmingAudioProcessor;
System.arraycopy(audioProcessors, 0, toIntPcmAvailableAudioProcessors, 3, audioProcessors.length);
System.arraycopy(
audioProcessors, 0, toIntPcmAvailableAudioProcessors, 3, audioProcessors.length);
toIntPcmAvailableAudioProcessors[3 + audioProcessors.length] = sonicAudioProcessor;
toFloatPcmAvailableAudioProcessors = new AudioProcessor[1];
toFloatPcmAvailableAudioProcessors[0] = new FloatResamplingAudioProcessor();
toFloatPcmAvailableAudioProcessors = new AudioProcessor[] {new FloatResamplingAudioProcessor()};
playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT];
volume = 1.0f;
startMediaTimeState = START_NOT_SET;
@ -366,20 +366,20 @@ public final class DefaultAudioSink implements AudioSink {
int channelCount = inputChannelCount;
int sampleRate = inputSampleRate;
isInputPcm = isEncodingPcm(inputEncoding);
shouldUpResPCMAudio = canConvertHiResPcmToFloat &&
(inputEncoding == C.ENCODING_PCM_24BIT || inputEncoding == C.ENCODING_PCM_32BIT);
shouldConvertHighResIntPcmToFloat =
enableConvertHighResIntPcmToFloat
&& isEncodingSupported(C.ENCODING_PCM_32BIT)
&& Util.isEncodingHighResolutionIntegerPcm(inputEncoding);
if (isInputPcm) {
pcmFrameSize = Util.getPcmFrameSize(inputEncoding, channelCount);
}
@C.Encoding int encoding = inputEncoding;
boolean processingEnabled = isInputPcm && inputEncoding != C.ENCODING_PCM_FLOAT;
canApplyPlaybackParams = processingEnabled && !shouldUpResPCMAudio;
canApplyPlaybackParameters = processingEnabled && !shouldConvertHighResIntPcmToFloat;
if (processingEnabled) {
AudioProcessor[] availableAudioProcessors = shouldUpResPCMAudio ?
toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
trimmingAudioProcessor.setTrimSampleCount(trimStartSamples, trimEndSamples);
channelMappingAudioProcessor.setChannelMap(outputChannels);
for (AudioProcessor audioProcessor : availableAudioProcessors) {
for (AudioProcessor audioProcessor : getAvailableAudioProcessors()) {
try {
flush |= audioProcessor.configure(sampleRate, channelCount, encoding);
} catch (AudioProcessor.UnhandledFormatException e) {
@ -489,9 +489,7 @@ public final class DefaultAudioSink implements AudioSink {
private void resetAudioProcessors() {
ArrayList<AudioProcessor> newAudioProcessors = new ArrayList<>();
AudioProcessor[] availableAudioProcessors = shouldUpResPCMAudio ?
toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
for (AudioProcessor audioProcessor : availableAudioProcessors) {
for (AudioProcessor audioProcessor : getAvailableAudioProcessors()) {
if (audioProcessor.isActive()) {
newAudioProcessors.add(audioProcessor);
} else {
@ -839,8 +837,7 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public PlaybackParameters setPlaybackParameters(PlaybackParameters playbackParameters) {
if (isInitialized() && !canApplyPlaybackParams) {
// The playback parameters are always the default if processing is disabled.
if (isInitialized() && !canApplyPlaybackParameters) {
this.playbackParameters = PlaybackParameters.DEFAULT;
return this.playbackParameters;
}
@ -1256,6 +1253,12 @@ public final class DefaultAudioSink implements AudioSink {
MODE_STATIC, audioSessionId);
}
private AudioProcessor[] getAvailableAudioProcessors() {
return shouldConvertHighResIntPcmToFloat
? toFloatPcmAvailableAudioProcessors
: toIntPcmAvailableAudioProcessors;
}
private static boolean isEncodingPcm(@C.Encoding int encoding) {
return encoding == C.ENCODING_PCM_8BIT || encoding == C.ENCODING_PCM_16BIT
|| encoding == C.ENCODING_PCM_24BIT || encoding == C.ENCODING_PCM_32BIT

View File

@ -15,31 +15,30 @@
*/
package com.google.android.exoplayer2.audio;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* An {@link AudioProcessor} that converts audio data to {@link C#ENCODING_PCM_16BIT}.
* An {@link AudioProcessor} that converts 24-bit and 32-bit integer PCM audio to 32-bit float PCM
* audio.
*/
/* package */ final class FloatResamplingAudioProcessor implements AudioProcessor {
private int sampleRateHz;
private static final double PCM_INT32_FLOAT = 1.0 / 0x7fffffff;
private static final int FLOAT_NAN_AS_INT = Float.floatToIntBits(Float.NaN);
private static final double PCM_32_BIT_INT_TO_PCM_32_BIT_FLOAT_FACTOR = 1.0 / 0x7FFFFFFF;
private int sampleRateHz;
private int channelCount;
@C.PcmEncoding
private int sourceEncoding;
private @C.PcmEncoding 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}.
*/
/** Creates a new audio processor that converts audio data to {@link C#ENCODING_PCM_FLOAT}. */
public FloatResamplingAudioProcessor() {
sampleRateHz = Format.NO_VALUE;
channelCount = Format.NO_VALUE;
@ -50,31 +49,35 @@ import java.nio.ByteOrder;
@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);
throws UnhandledFormatException {
if (!Util.isEncodingHighResolutionIntegerPcm(encoding)) {
throw new UnhandledFormatException(sampleRateHz, channelCount, encoding);
}
if (this.sampleRateHz == sampleRateHz && this.channelCount == channelCount
&& this.sourceEncoding == encoding) {
if (this.sampleRateHz == sampleRateHz
&& this.channelCount == channelCount
&& sourceEncoding == encoding) {
return false;
}
this.sampleRateHz = sampleRateHz;
this.channelCount = channelCount;
this.sourceEncoding = encoding;
sourceEncoding = encoding;
return true;
}
@Override
public boolean isActive() {
return sourceEncoding == C.ENCODING_PCM_24BIT || sourceEncoding == C.ENCODING_PCM_32BIT;
return Util.isEncodingHighResolutionIntegerPcm(sourceEncoding);
}
@Override
public int getOutputChannelCount() { return channelCount; }
public int getOutputChannelCount() {
return channelCount;
}
@Override
public int getOutputEncoding() { return C.ENCODING_PCM_FLOAT; }
public int getOutputEncoding() {
return C.ENCODING_PCM_FLOAT;
}
@Override
public int getOutputSampleRateHz() {
@ -83,60 +86,36 @@ import java.nio.ByteOrder;
@Override
public void queueInput(ByteBuffer inputBuffer) {
int offset = inputBuffer.position();
Assertions.checkState(isActive());
boolean isInput32Bit = sourceEncoding == C.ENCODING_PCM_32BIT;
int position = 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();
}
int size = limit - position;
int resampledSize = isInput32Bit ? size : (size / 3) * 4;
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();
if (isInput32Bit) {
for (int i = position; i < limit; i += 4) {
int pcm32BitInteger =
(inputBuffer.get(i) & 0xFF)
| ((inputBuffer.get(i + 1) & 0xFF) << 8)
| ((inputBuffer.get(i + 2) & 0xFF) << 16)
| ((inputBuffer.get(i + 3) & 0xFF) << 24);
writePcm32BitFloat(pcm32BitInteger, buffer);
}
} else {
for (int i = position; i < limit; i += 3) {
int pcm32BitInteger =
((inputBuffer.get(i) & 0xFF) << 8)
| ((inputBuffer.get(i + 1) & 0xFF) << 16)
| ((inputBuffer.get(i + 2) & 0xFF) << 24);
writePcm32BitFloat(pcm32BitInteger, buffer);
}
}
inputBuffer.position(inputBuffer.limit());
@ -178,17 +157,17 @@ import java.nio.ByteOrder;
}
/**
* Converts the provided value into 32-bit float PCM and writes to buffer.
* Converts the provided 32-bit integer to a 32-bit float value and writes it to {@code buffer}.
*
* @param val 32-bit int value to convert to 32-bit float [-1.0, 1.0]
* @param pcm32BitInt The 32-bit integer value to convert to 32-bit float in [-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);
private static void writePcm32BitFloat(int pcm32BitInt, ByteBuffer buffer) {
float pcm32BitFloat = (float) (PCM_32_BIT_INT_TO_PCM_32_BIT_FLOAT_FACTOR * pcm32BitInt);
int floatBits = Float.floatToIntBits(pcm32BitFloat);
if (floatBits == FLOAT_NAN_AS_INT) {
floatBits = Float.floatToIntBits((float) 0.0);
}
buffer.putInt(floatBits);
}
}
}

View File

@ -21,7 +21,8 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* An {@link AudioProcessor} that converts audio data to {@link C#ENCODING_PCM_16BIT}.
* An {@link AudioProcessor} that converts 8-bit, 24-bit and 32-bit integer PCM audio to 16-bit
* integer PCM audio.
*/
/* package */ final class ResamplingAudioProcessor implements AudioProcessor {

View File

@ -27,21 +27,21 @@ import java.lang.reflect.InvocationTargetException;
* A {@link DataSource} that supports multiple URI schemes. The supported schemes are:
*
* <ul>
* <li>file: For fetching data from a local file (e.g. file:///path/to/media/media.mp4, or just
* /path/to/media/media.mp4 because the implementation assumes that a URI without a scheme is a
* local file URI).
* <li>asset: For fetching data from an asset in the application's apk (e.g. asset:///media.mp4).
* <li>rawresource: For fetching data from a raw resource in the applications' apk
* (e.g. rawresource:///resourceId, where rawResourceId is the integer identifier of the raw
* resource).</li>
* <li>content: For fetching data from a content URI (e.g. content://authority/path/123).
* <li>rtmp: For fetching data over RTMP. Only supported if the project using ExoPlayer has an
* explicit dependency on ExoPlayer's RTMP extension.</li>
* <li>data: For parsing data inlined in the URI as defined in RFC 2397.</li>
* <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4), if
* constructed using {@link #DefaultDataSource(Context, TransferListener, String, boolean)}, or
* any other schemes supported by a base data source if constructed using
* {@link #DefaultDataSource(Context, TransferListener, DataSource)}.</li>
* <li>file: For fetching data from a local file (e.g. file:///path/to/media/media.mp4, or just
* /path/to/media/media.mp4 because the implementation assumes that a URI without a scheme is
* a local file URI).
* <li>asset: For fetching data from an asset in the application's apk (e.g. asset:///media.mp4).
* <li>rawresource: For fetching data from a raw resource in the application's apk (e.g.
* rawresource:///resourceId, where rawResourceId is the integer identifier of the raw
* resource).
* <li>content: For fetching data from a content URI (e.g. content://authority/path/123).
* <li>rtmp: For fetching data over RTMP. Only supported if the project using ExoPlayer has an
* explicit dependency on ExoPlayer's RTMP extension.
* <li>data: For parsing data inlined in the URI as defined in RFC 2397.
* <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4),
* if constructed using {@link #DefaultDataSource(Context, TransferListener, String,
* boolean)}, or any other schemes supported by a base data source if constructed using {@link
* #DefaultDataSource(Context, TransferListener, DataSource)}.
* </ul>
*/
public final class DefaultDataSource implements DataSource {

View File

@ -58,6 +58,7 @@ public final class RawResourceDataSource implements DataSource {
return Uri.parse(RAW_RESOURCE_SCHEME + ":///" + rawResourceId);
}
/** The scheme part of a raw resource URI. */
public static final String RAW_RESOURCE_SCHEME = "rawresource";
private final Resources resources;

View File

@ -941,6 +941,16 @@ public final class Util {
}
}
/**
* Returns whether {@code encoding} is high resolution (> 16-bit) integer PCM.
*
* @param encoding The encoding of the audio data.
* @return Whether the encoding is high resolution integer PCM.
*/
public static boolean isEncodingHighResolutionIntegerPcm(@C.PcmEncoding int encoding) {
return encoding == C.ENCODING_PCM_24BIT || encoding == C.ENCODING_PCM_32BIT;
}
/**
* Returns the frame size for audio with {@code channelCount} channels in the specified encoding.
*