mirror of
https://github.com/androidx/media.git
synced 2025-05-09 08:30:43 +08:00
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
This commit is contained in:
parent
d2c524d726
commit
b61e7cdbc4
@ -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());
|
||||
|
@ -59,6 +59,26 @@ public final class C {
|
||||
@SuppressWarnings("InlinedApi")
|
||||
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
|
||||
*/
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.google.android.exoplayer;
|
||||
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@ -131,6 +132,12 @@ public final class Format {
|
||||
* The audio sampling rate in Hz, or {@link #NO_VALUE} if unknown or not applicable.
|
||||
*/
|
||||
public final int sampleRate;
|
||||
/**
|
||||
* The encoding for PCM audio streams. If {@link #sampleMimeType} is {@link MimeTypes#AUDIO_RAW}
|
||||
* then one of {@link C#ENCODING_PCM_8BIT}, {@link C#ENCODING_PCM_16BIT} and
|
||||
* {@link C#ENCODING_PCM_24BIT}. Set to {@link #NO_VALUE} for other media types.
|
||||
*/
|
||||
public final int pcmEncoding;
|
||||
/**
|
||||
* The number of samples to trim from the start of the decoded audio stream.
|
||||
*/
|
||||
@ -167,7 +174,7 @@ public final class Format {
|
||||
String sampleMimeType, int bitrate, int width, int height, float frameRate,
|
||||
List<byte[]> initializationData) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, NO_VALUE, width, height,
|
||||
frameRate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null,
|
||||
frameRate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null,
|
||||
OFFSET_SAMPLE_RELATIVE, initializationData, null, false);
|
||||
}
|
||||
|
||||
@ -182,8 +189,8 @@ public final class Format {
|
||||
int maxInputSize, int width, int height, float frameRate, List<byte[]> initializationData,
|
||||
int rotationDegrees, float pixelWidthHeightRatio, DrmInitData drmInitData) {
|
||||
return new Format(id, null, sampleMimeType, bitrate, maxInputSize, width, height, frameRate,
|
||||
rotationDegrees, pixelWidthHeightRatio, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null,
|
||||
OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false);
|
||||
rotationDegrees, pixelWidthHeightRatio, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
null, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false);
|
||||
}
|
||||
|
||||
// Audio.
|
||||
@ -192,23 +199,31 @@ public final class Format {
|
||||
String sampleMimeType, int bitrate, int channelCount, int sampleRate,
|
||||
List<byte[]> initializationData, String language) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, channelCount, sampleRate, NO_VALUE, NO_VALUE, language,
|
||||
OFFSET_SAMPLE_RELATIVE, initializationData, null, false);
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, channelCount, sampleRate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
language, OFFSET_SAMPLE_RELATIVE, initializationData, null, false);
|
||||
}
|
||||
|
||||
public static Format createAudioSampleFormat(String id, String sampleMimeType, int bitrate,
|
||||
int maxInputSize, int channelCount, int sampleRate, List<byte[]> initializationData,
|
||||
String language, DrmInitData drmInitData) {
|
||||
DrmInitData drmInitData, String language) {
|
||||
return createAudioSampleFormat(id, sampleMimeType, bitrate, maxInputSize, channelCount,
|
||||
sampleRate, NO_VALUE, NO_VALUE, initializationData, language, drmInitData);
|
||||
sampleRate, NO_VALUE, initializationData, drmInitData, language);
|
||||
}
|
||||
|
||||
public static Format createAudioSampleFormat(String id, String sampleMimeType, int bitrate,
|
||||
int maxInputSize, int channelCount, int sampleRate, int encoderDelay, int encoderPadding,
|
||||
List<byte[]> initializationData, String language, DrmInitData drmInitData) {
|
||||
int maxInputSize, int channelCount, int sampleRate, int pcmEncoding,
|
||||
List<byte[]> initializationData, DrmInitData drmInitData, String language) {
|
||||
return createAudioSampleFormat(id, sampleMimeType, bitrate, maxInputSize, channelCount,
|
||||
sampleRate, pcmEncoding, NO_VALUE, NO_VALUE, initializationData, drmInitData, language);
|
||||
}
|
||||
|
||||
public static Format createAudioSampleFormat(String id, String sampleMimeType, int bitrate,
|
||||
int maxInputSize, int channelCount, int sampleRate, int pcmEncoding, int encoderDelay,
|
||||
int encoderPadding, List<byte[]> initializationData, DrmInitData drmInitData,
|
||||
String language) {
|
||||
return new Format(id, null, sampleMimeType, bitrate, maxInputSize, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, channelCount, sampleRate, encoderDelay, encoderPadding, language,
|
||||
OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false);
|
||||
NO_VALUE, NO_VALUE, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
|
||||
language, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false);
|
||||
}
|
||||
|
||||
// Text.
|
||||
@ -216,7 +231,7 @@ public final class Format {
|
||||
public static Format createTextContainerFormat(String id, String containerMimeType,
|
||||
String sampleMimeType, int bitrate, String language) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language,
|
||||
OFFSET_SAMPLE_RELATIVE, null, null, false);
|
||||
}
|
||||
|
||||
@ -229,7 +244,7 @@ public final class Format {
|
||||
public static Format createTextSampleFormat(String id, String sampleMimeType, int bitrate,
|
||||
String language, DrmInitData drmInitData, long subsampleOffsetUs) {
|
||||
return new Format(id, null, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language,
|
||||
subsampleOffsetUs, null, drmInitData, false);
|
||||
}
|
||||
|
||||
@ -238,7 +253,7 @@ public final class Format {
|
||||
public static Format createImageSampleFormat(String id, String sampleMimeType, int bitrate,
|
||||
List<byte[]> initializationData, String language, DrmInitData drmInitData) {
|
||||
return new Format(id, null, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language,
|
||||
OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, false);
|
||||
}
|
||||
|
||||
@ -247,22 +262,22 @@ public final class Format {
|
||||
public static Format createContainerFormat(String id, String containerMimeType,
|
||||
String sampleMimeType, int bitrate) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null,
|
||||
OFFSET_SAMPLE_RELATIVE, null, null, false);
|
||||
}
|
||||
|
||||
public static Format createSampleFormat(String id, String sampleMimeType, int bitrate,
|
||||
DrmInitData drmInitData) {
|
||||
return new Format(id, null, sampleMimeType, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, OFFSET_SAMPLE_RELATIVE,
|
||||
null, drmInitData, false);
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null,
|
||||
OFFSET_SAMPLE_RELATIVE, null, drmInitData, false);
|
||||
}
|
||||
|
||||
/* package */ Format(String id, String containerMimeType, String sampleMimeType,
|
||||
int bitrate, int maxInputSize, int width, int height, float frameRate, int rotationDegrees,
|
||||
float pixelWidthHeightRatio, int channelCount, int sampleRate, int encoderDelay,
|
||||
int encoderPadding, String language, long subsampleOffsetUs, List<byte[]> initializationData,
|
||||
DrmInitData drmInitData, boolean requiresSecureDecryption) {
|
||||
float pixelWidthHeightRatio, int channelCount, int sampleRate, int pcmEncoding,
|
||||
int encoderDelay, int encoderPadding, String language, long subsampleOffsetUs,
|
||||
List<byte[]> initializationData, DrmInitData drmInitData, boolean requiresSecureDecryption) {
|
||||
this.id = id;
|
||||
this.containerMimeType = containerMimeType;
|
||||
this.sampleMimeType = sampleMimeType;
|
||||
@ -275,6 +290,7 @@ public final class Format {
|
||||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||
this.channelCount = channelCount;
|
||||
this.sampleRate = sampleRate;
|
||||
this.pcmEncoding = pcmEncoding;
|
||||
this.encoderDelay = encoderDelay;
|
||||
this.encoderPadding = encoderPadding;
|
||||
this.language = language;
|
||||
@ -288,14 +304,14 @@ public final class Format {
|
||||
public Format copyWithMaxInputSize(int maxInputSize) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width,
|
||||
height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate,
|
||||
encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
pcmEncoding, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
drmInitData, requiresSecureDecryption);
|
||||
}
|
||||
|
||||
public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width,
|
||||
height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate,
|
||||
encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
pcmEncoding, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
drmInitData, requiresSecureDecryption);
|
||||
}
|
||||
|
||||
@ -303,21 +319,21 @@ public final class Format {
|
||||
String language) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width,
|
||||
height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate,
|
||||
encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
pcmEncoding, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
drmInitData, requiresSecureDecryption);
|
||||
}
|
||||
|
||||
public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width,
|
||||
height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate,
|
||||
encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
pcmEncoding, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
drmInitData, requiresSecureDecryption);
|
||||
}
|
||||
|
||||
public Format copyWithDrmInitData(DrmInitData drmInitData) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, bitrate, maxInputSize, width,
|
||||
height, frameRate, rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate,
|
||||
encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
pcmEncoding, encoderDelay, encoderPadding, language, subsampleOffsetUs, initializationData,
|
||||
drmInitData, requiresSecureDecryption);
|
||||
}
|
||||
|
||||
@ -379,8 +395,6 @@ public final class Format {
|
||||
result = 31 * result + height;
|
||||
result = 31 * result + channelCount;
|
||||
result = 31 * result + sampleRate;
|
||||
result = 31 * result + encoderDelay;
|
||||
result = 31 * result + encoderPadding;
|
||||
result = 31 * result + (language == null ? 0 : language.hashCode());
|
||||
result = 31 * result + (drmInitData == null ? 0 : drmInitData.hashCode());
|
||||
hashCode = result;
|
||||
@ -403,8 +417,8 @@ public final class Format {
|
||||
|| rotationDegrees != other.rotationDegrees
|
||||
|| pixelWidthHeightRatio != other.pixelWidthHeightRatio
|
||||
|| channelCount != other.channelCount || sampleRate != other.sampleRate
|
||||
|| encoderDelay != other.encoderDelay || encoderPadding != other.encoderPadding
|
||||
|| subsampleOffsetUs != other.subsampleOffsetUs
|
||||
|| pcmEncoding != other.pcmEncoding || encoderDelay != other.encoderDelay
|
||||
|| encoderPadding != other.encoderPadding || subsampleOffsetUs != other.subsampleOffsetUs
|
||||
|| !Util.areEqual(id, other.id) || !Util.areEqual(language, other.language)
|
||||
|| !Util.areEqual(containerMimeType, other.containerMimeType)
|
||||
|| !Util.areEqual(sampleMimeType, other.sampleMimeType)
|
||||
|
@ -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;
|
||||
@ -234,6 +235,15 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
return this;
|
||||
}
|
||||
|
||||
@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;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,7 +199,8 @@ public final class AudioTrack {
|
||||
private android.media.AudioTrack audioTrack;
|
||||
private int sampleRate;
|
||||
private int channelConfig;
|
||||
private int encoding;
|
||||
private int sourceEncoding;
|
||||
private int targetEncoding;
|
||||
private boolean passthrough;
|
||||
private int pcmFrameSize;
|
||||
private int bufferSize;
|
||||
@ -224,7 +225,10 @@ public final class AudioTrack {
|
||||
|
||||
private byte[] temporaryBuffer;
|
||||
private int temporaryBufferOffset;
|
||||
private ByteBuffer currentBuffer;
|
||||
private ByteBuffer currentSourceBuffer;
|
||||
|
||||
private ByteBuffer resampledBuffer;
|
||||
private boolean useResampledBuffer;
|
||||
|
||||
/**
|
||||
* Creates an audio track with default audio capabilities (no encoded audio passthrough support).
|
||||
@ -336,9 +340,11 @@ public final class AudioTrack {
|
||||
* @param mimeType The mime type.
|
||||
* @param channelCount The number of channels.
|
||||
* @param sampleRate The sample rate in Hz.
|
||||
* @param pcmEncoding For PCM formats, the encoding used. One of {@link C#ENCODING_PCM_16BIT},
|
||||
* {@link C#ENCODING_PCM_16BIT} and {@link C#ENCODING_PCM_24BIT}.
|
||||
*/
|
||||
public void configure(String mimeType, int channelCount, int sampleRate) {
|
||||
configure(mimeType, channelCount, sampleRate, 0);
|
||||
public void configure(String mimeType, int channelCount, int sampleRate, int pcmEncoding) {
|
||||
configure(mimeType, channelCount, sampleRate, pcmEncoding, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -347,10 +353,12 @@ public final class AudioTrack {
|
||||
* @param mimeType The mime type.
|
||||
* @param channelCount The number of channels.
|
||||
* @param sampleRate The sample rate in Hz.
|
||||
* @param pcmEncoding For PCM formats, the encoding used. One of {@link C#ENCODING_PCM_16BIT},
|
||||
* {@link C#ENCODING_PCM_16BIT} and {@link C#ENCODING_PCM_24BIT}.
|
||||
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to infer a
|
||||
* suitable buffer size automatically.
|
||||
*/
|
||||
public void configure(String mimeType, int channelCount, int sampleRate,
|
||||
public void configure(String mimeType, int channelCount, int sampleRate, int pcmEncoding,
|
||||
int specifiedBufferSize) {
|
||||
int channelConfig;
|
||||
switch (channelCount) {
|
||||
@ -381,37 +389,48 @@ public final class AudioTrack {
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported channel count: " + channelCount);
|
||||
}
|
||||
|
||||
boolean passthrough = !MimeTypes.AUDIO_RAW.equals(mimeType);
|
||||
int encoding = passthrough ? getEncodingForMimeType(mimeType) : AudioFormat.ENCODING_PCM_16BIT;
|
||||
if (isInitialized() && this.sampleRate == sampleRate && this.channelConfig == channelConfig
|
||||
&& this.encoding == encoding) {
|
||||
// We already have an audio track with the correct sample rate, encoding and channel config.
|
||||
int sourceEncoding;
|
||||
if (passthrough) {
|
||||
sourceEncoding = getEncodingForMimeType(mimeType);
|
||||
} else if (pcmEncoding == C.ENCODING_PCM_8BIT || pcmEncoding == C.ENCODING_PCM_16BIT
|
||||
|| pcmEncoding == C.ENCODING_PCM_24BIT) {
|
||||
sourceEncoding = pcmEncoding;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported PCM encoding: " + pcmEncoding);
|
||||
}
|
||||
|
||||
if (isInitialized() && this.sourceEncoding == sourceEncoding && this.sampleRate == sampleRate
|
||||
&& this.channelConfig == channelConfig) {
|
||||
// We already have an audio track with the correct sample rate, channel config and encoding.
|
||||
return;
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
this.encoding = encoding;
|
||||
this.sourceEncoding = sourceEncoding;
|
||||
this.passthrough = passthrough;
|
||||
this.sampleRate = sampleRate;
|
||||
this.channelConfig = channelConfig;
|
||||
pcmFrameSize = 2 * channelCount; // 2 bytes per 16 bit sample * number of channels.
|
||||
targetEncoding = passthrough ? sourceEncoding : C.ENCODING_PCM_16BIT;
|
||||
pcmFrameSize = 2 * channelCount; // 2 bytes per 16-bit sample * number of channels.
|
||||
|
||||
if (specifiedBufferSize != 0) {
|
||||
bufferSize = specifiedBufferSize;
|
||||
} else if (passthrough) {
|
||||
// TODO: Set the minimum buffer size using getMinBufferSize when it takes the encoding into
|
||||
// account. [Internal: b/25181305]
|
||||
if (encoding == C.ENCODING_AC3 || encoding == C.ENCODING_E_AC3) {
|
||||
if (targetEncoding == C.ENCODING_AC3 || targetEncoding == C.ENCODING_E_AC3) {
|
||||
// AC-3 allows bitrates up to 640 kbit/s.
|
||||
bufferSize = (int) (PASSTHROUGH_BUFFER_DURATION_US * 80 * 1024 / C.MICROS_PER_SECOND);
|
||||
} else { // encoding == C.ENCODING_DTS || encoding == C.ENCODING_DTS_HD
|
||||
} else /* (targetEncoding == C.ENCODING_DTS || targetEncoding == C.ENCODING_DTS_HD */ {
|
||||
// DTS allows an 'open' bitrate, but we assume the maximum listed value: 1536 kbit/s.
|
||||
bufferSize = (int) (PASSTHROUGH_BUFFER_DURATION_US * 192 * 1024 / C.MICROS_PER_SECOND);
|
||||
}
|
||||
} else {
|
||||
int minBufferSize =
|
||||
android.media.AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding);
|
||||
android.media.AudioTrack.getMinBufferSize(sampleRate, channelConfig, targetEncoding);
|
||||
Assertions.checkState(minBufferSize != android.media.AudioTrack.ERROR_BAD_VALUE);
|
||||
int multipliedBufferSize = minBufferSize * BUFFER_MULTIPLICATION_FACTOR;
|
||||
int minAppBufferSize = (int) durationUsToFrames(MIN_BUFFER_DURATION_US) * pcmFrameSize;
|
||||
@ -449,12 +468,12 @@ public final class AudioTrack {
|
||||
releasingConditionVariable.block();
|
||||
|
||||
if (sessionId == SESSION_ID_NOT_SET) {
|
||||
audioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig, encoding,
|
||||
bufferSize, android.media.AudioTrack.MODE_STREAM);
|
||||
audioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig,
|
||||
targetEncoding, bufferSize, android.media.AudioTrack.MODE_STREAM);
|
||||
} else {
|
||||
// Re-attach to the same audio session.
|
||||
audioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig, encoding,
|
||||
bufferSize, android.media.AudioTrack.MODE_STREAM, sessionId);
|
||||
audioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig,
|
||||
targetEncoding, bufferSize, android.media.AudioTrack.MODE_STREAM, sessionId);
|
||||
}
|
||||
checkAudioTrackInitialized();
|
||||
|
||||
@ -470,7 +489,7 @@ public final class AudioTrack {
|
||||
if (keepSessionIdAudioTrack == null) {
|
||||
int sampleRate = 4000; // Equal to private android.media.AudioTrack.MIN_SAMPLE_RATE.
|
||||
int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
|
||||
int encoding = AudioFormat.ENCODING_PCM_16BIT;
|
||||
int encoding = C.ENCODING_PCM_16BIT;
|
||||
int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback.
|
||||
keepSessionIdAudioTrack = new android.media.AudioTrack(streamType, sampleRate,
|
||||
channelConfig, encoding, bufferSize, android.media.AudioTrack.MODE_STATIC, sessionId);
|
||||
@ -551,15 +570,9 @@ public final class AudioTrack {
|
||||
* @throws WriteException If an error occurs writing the audio data.
|
||||
*/
|
||||
public int handleBuffer(ByteBuffer buffer, long presentationTimeUs) throws WriteException {
|
||||
boolean isNewBuffer = currentBuffer == null;
|
||||
Assertions.checkState(isNewBuffer || currentBuffer == buffer);
|
||||
currentBuffer = buffer;
|
||||
|
||||
int bytesRemaining = buffer.remaining();
|
||||
if (bytesRemaining == 0) {
|
||||
currentBuffer = null;
|
||||
return RESULT_BUFFER_CONSUMED;
|
||||
}
|
||||
boolean isNewSourceBuffer = currentSourceBuffer == null;
|
||||
Assertions.checkState(isNewSourceBuffer || currentSourceBuffer == buffer);
|
||||
currentSourceBuffer = buffer;
|
||||
|
||||
if (needsPassthroughWorkarounds()) {
|
||||
// An AC-3 audio track continues to play data written while it is paused. Stop writing so its
|
||||
@ -578,11 +591,26 @@ public final class AudioTrack {
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
if (isNewBuffer) {
|
||||
if (isNewSourceBuffer) {
|
||||
// We're seeing this buffer for the first time.
|
||||
|
||||
if (!currentSourceBuffer.hasRemaining()) {
|
||||
// The buffer is empty.
|
||||
currentSourceBuffer = null;
|
||||
return RESULT_BUFFER_CONSUMED;
|
||||
}
|
||||
|
||||
useResampledBuffer = targetEncoding != sourceEncoding;
|
||||
if (useResampledBuffer) {
|
||||
Assertions.checkState(targetEncoding == C.ENCODING_PCM_16BIT);
|
||||
// Resample the buffer to get the data in the target encoding.
|
||||
resampledBuffer = resampleTo16BitPcm(currentSourceBuffer, sourceEncoding, resampledBuffer);
|
||||
buffer = resampledBuffer;
|
||||
}
|
||||
|
||||
if (passthrough && framesPerEncodedSample == 0) {
|
||||
// If this is the first encoded sample, calculate the sample size in frames.
|
||||
framesPerEncodedSample = getFramesPerEncodedSample(encoding, buffer);
|
||||
framesPerEncodedSample = getFramesPerEncodedSample(targetEncoding, buffer);
|
||||
}
|
||||
if (startMediaTimeState == START_NOT_SET) {
|
||||
startMediaTimeUs = Math.max(0, presentationTimeUs);
|
||||
@ -607,6 +635,7 @@ public final class AudioTrack {
|
||||
}
|
||||
if (Util.SDK_INT < 21) {
|
||||
// Copy {@code buffer} into {@code temporaryBuffer}.
|
||||
int bytesRemaining = buffer.remaining();
|
||||
if (temporaryBuffer == null || temporaryBuffer.length < bytesRemaining) {
|
||||
temporaryBuffer = new byte[bytesRemaining];
|
||||
}
|
||||
@ -617,6 +646,8 @@ public final class AudioTrack {
|
||||
}
|
||||
}
|
||||
|
||||
buffer = useResampledBuffer ? resampledBuffer : buffer;
|
||||
int bytesRemaining = buffer.remaining();
|
||||
int bytesWritten = 0;
|
||||
if (Util.SDK_INT < 21) { // passthrough == false
|
||||
// Work out how many bytes we can write without the risk of blocking.
|
||||
@ -646,7 +677,7 @@ public final class AudioTrack {
|
||||
if (passthrough) {
|
||||
submittedEncodedFrames += framesPerEncodedSample;
|
||||
}
|
||||
currentBuffer = null;
|
||||
currentSourceBuffer = null;
|
||||
result |= RESULT_BUFFER_CONSUMED;
|
||||
}
|
||||
return result;
|
||||
@ -661,12 +692,6 @@ public final class AudioTrack {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static int writeNonBlockingV21(
|
||||
android.media.AudioTrack audioTrack, ByteBuffer buffer, int size) {
|
||||
return audioTrack.write(buffer, size, android.media.AudioTrack.WRITE_NON_BLOCKING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the audio track has more data pending that will be played back.
|
||||
*/
|
||||
@ -707,16 +732,6 @@ public final class AudioTrack {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static void setAudioTrackVolumeV21(android.media.AudioTrack audioTrack, float volume) {
|
||||
audioTrack.setVolume(volume);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static void setAudioTrackVolumeV3(android.media.AudioTrack audioTrack, float volume) {
|
||||
audioTrack.setStereoVolume(volume, volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses playback.
|
||||
*/
|
||||
@ -737,7 +752,7 @@ public final class AudioTrack {
|
||||
submittedPcmBytes = 0;
|
||||
submittedEncodedFrames = 0;
|
||||
framesPerEncodedSample = 0;
|
||||
currentBuffer = null;
|
||||
currentSourceBuffer = null;
|
||||
startMediaTimeState = START_NOT_SET;
|
||||
latencyUs = 0;
|
||||
resetSyncParams();
|
||||
@ -937,7 +952,8 @@ public final class AudioTrack {
|
||||
* See [Internal: b/18899620, b/19187573, b/21145353].
|
||||
*/
|
||||
private boolean needsPassthroughWorkarounds() {
|
||||
return Util.SDK_INT < 23 && (encoding == C.ENCODING_AC3 || encoding == C.ENCODING_E_AC3);
|
||||
return Util.SDK_INT < 23
|
||||
&& (targetEncoding == C.ENCODING_AC3 || targetEncoding == C.ENCODING_E_AC3);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -952,6 +968,66 @@ public final class AudioTrack {
|
||||
&& audioTrack.getPlaybackHeadPosition() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the provided buffer into 16-bit PCM.
|
||||
*
|
||||
* @param buffer The buffer containing the data to convert.
|
||||
* @param sourceEncoding The data encoding.
|
||||
* @param out A buffer into which the output should be written, if its capacity is sufficient.
|
||||
* @return The 16-bit PCM output. Different to the out parameter if null was passed, or if the
|
||||
* capacity was insufficient for the output.
|
||||
*/
|
||||
private static ByteBuffer resampleTo16BitPcm(ByteBuffer buffer, int sourceEncoding,
|
||||
ByteBuffer out) {
|
||||
int offset = buffer.position();
|
||||
int limit = buffer.limit();
|
||||
int size = limit - offset;
|
||||
|
||||
int resampledSize;
|
||||
switch (sourceEncoding) {
|
||||
case C.ENCODING_PCM_8BIT:
|
||||
resampledSize = size * 2;
|
||||
break;
|
||||
case C.ENCODING_PCM_24BIT:
|
||||
resampledSize = (size / 3) * 2;
|
||||
break;
|
||||
default:
|
||||
// Never happens.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
ByteBuffer resampledBuffer = out;
|
||||
if (resampledBuffer == null || resampledBuffer.capacity() < resampledSize) {
|
||||
resampledBuffer = ByteBuffer.allocateDirect(resampledSize);
|
||||
}
|
||||
resampledBuffer.position(0);
|
||||
resampledBuffer.limit(resampledSize);
|
||||
|
||||
// Samples are little endian.
|
||||
switch (sourceEncoding) {
|
||||
case C.ENCODING_PCM_8BIT:
|
||||
// 8->16 bit resampling. Shift each byte from [0, 256) to [-128, 128) and scale up.
|
||||
for (int i = offset; i < limit; i++) {
|
||||
resampledBuffer.put((byte) 0);
|
||||
resampledBuffer.put((byte) ((buffer.get(i) & 0xFF) - 128));
|
||||
}
|
||||
break;
|
||||
case C.ENCODING_PCM_24BIT:
|
||||
// 24->16 bit resampling. Drop the least significant byte.
|
||||
for (int i = offset; i < limit; i += 3) {
|
||||
resampledBuffer.put(buffer.get(i + 1));
|
||||
resampledBuffer.put(buffer.get(i + 2));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Never happens.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
resampledBuffer.position(0);
|
||||
return resampledBuffer;
|
||||
}
|
||||
|
||||
private static int getEncodingForMimeType(String mimeType) {
|
||||
switch (mimeType) {
|
||||
case MimeTypes.AUDIO_AC3:
|
||||
@ -963,7 +1039,7 @@ public final class AudioTrack {
|
||||
case MimeTypes.AUDIO_DTS_HD:
|
||||
return C.ENCODING_DTS_HD;
|
||||
default:
|
||||
return AudioFormat.ENCODING_INVALID;
|
||||
return C.ENCODING_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
@ -979,6 +1055,22 @@ public final class AudioTrack {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static int writeNonBlockingV21(
|
||||
android.media.AudioTrack audioTrack, ByteBuffer buffer, int size) {
|
||||
return audioTrack.write(buffer, size, android.media.AudioTrack.WRITE_NON_BLOCKING);
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static void setAudioTrackVolumeV21(android.media.AudioTrack audioTrack, float volume) {
|
||||
audioTrack.setVolume(volume);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static void setAudioTrackVolumeV3(android.media.AudioTrack audioTrack, float volume) {
|
||||
audioTrack.setStereoVolume(volume, volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an {@link android.media.AudioTrack} to expose useful utility methods.
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -777,6 +777,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.
|
||||
*
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user