Commit 7b10e637 by andrewlewis Committed by Oliver Woodman

Support mu-law and A-law PCM with ffmpeg

Issue: #4360

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=200186465
parent 877c6965
......@@ -27,6 +27,8 @@
([#4023](https://github.com/google/ExoPlayer/issues/4023)).
* Allow apps to register custom MIME types
([#4264](https://github.com/google/ExoPlayer/issues/4264)).
* Add support for mu-law and A-law PCM with the ffmpeg extension
([#4360](https://github.com/google/ExoPlayer/issues/4360)).
### 2.8.2 ###
......
......@@ -37,6 +37,8 @@ android {
dependencies {
implementation project(modulePrefix + 'library-core')
implementation 'com.android.support:support-annotations:' + supportLibraryVersion
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
}
ext {
......
......@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.ext.ffmpeg;
import android.os.Handler;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
......@@ -26,7 +27,10 @@ import com.google.android.exoplayer2.audio.DefaultAudioSink;
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Collections;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* Decodes and renders audio using FFmpeg.
......@@ -45,10 +49,10 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
private final boolean enableFloatOutput;
private FfmpegDecoder decoder;
private @MonotonicNonNull FfmpegDecoder decoder;
public FfmpegAudioRenderer() {
this(null, null);
this(/* eventHandler= */ null, /* eventListener= */ null);
}
/**
......@@ -57,9 +61,15 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output.
*/
public FfmpegAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
public FfmpegAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioProcessor... audioProcessors) {
this(eventHandler, eventListener, new DefaultAudioSink(null, audioProcessors), false);
this(
eventHandler,
eventListener,
new DefaultAudioSink(/* audioCapabilities= */ null, audioProcessors),
/* enableFloatOutput= */ false);
}
/**
......@@ -72,8 +82,11 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
* 32-bit float output, any audio processing will be disabled, including playback speed/pitch
* adjustment.
*/
public FfmpegAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
AudioSink audioSink, boolean enableFloatOutput) {
public FfmpegAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioSink audioSink,
boolean enableFloatOutput) {
super(
eventHandler,
eventListener,
......@@ -86,10 +99,11 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
@Override
protected int supportsFormatInternal(DrmSessionManager<ExoMediaCrypto> drmSessionManager,
Format format) {
String sampleMimeType = format.sampleMimeType;
if (!FfmpegLibrary.isAvailable() || !MimeTypes.isAudio(sampleMimeType)) {
Assertions.checkNotNull(format.sampleMimeType);
if (!FfmpegLibrary.isAvailable() || !MimeTypes.isAudio(format.sampleMimeType)) {
return FORMAT_UNSUPPORTED_TYPE;
} else if (!FfmpegLibrary.supportsFormat(sampleMimeType) || !isOutputSupported(format)) {
} else if (!FfmpegLibrary.supportsFormat(format.sampleMimeType, format.pcmEncoding)
|| !isOutputSupported(format)) {
return FORMAT_UNSUPPORTED_SUBTYPE;
} else if (!supportsFormatDrm(drmSessionManager, format.drmInitData)) {
return FORMAT_UNSUPPORTED_DRM;
......@@ -106,18 +120,35 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
@Override
protected FfmpegDecoder createDecoder(Format format, ExoMediaCrypto mediaCrypto)
throws FfmpegDecoderException {
decoder = new FfmpegDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE,
format.sampleMimeType, format.initializationData, shouldUseFloatOutput(format));
decoder =
new FfmpegDecoder(
NUM_BUFFERS,
NUM_BUFFERS,
INITIAL_INPUT_BUFFER_SIZE,
format,
shouldUseFloatOutput(format));
return decoder;
}
@Override
public Format getOutputFormat() {
Assertions.checkNotNull(decoder);
int channelCount = decoder.getChannelCount();
int sampleRate = decoder.getSampleRate();
@C.PcmEncoding int encoding = decoder.getEncoding();
return Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW, null, Format.NO_VALUE,
Format.NO_VALUE, channelCount, sampleRate, encoding, null, null, 0, null);
return Format.createAudioSampleFormat(
/* id= */ null,
MimeTypes.AUDIO_RAW,
/* codecs= */ null,
Format.NO_VALUE,
Format.NO_VALUE,
channelCount,
sampleRate,
encoding,
Collections.emptyList(),
/* drmInitData= */ null,
/* selectionFlags= */ 0,
/* language= */ null);
}
private boolean isOutputSupported(Format inputFormat) {
......@@ -125,6 +156,7 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
}
private boolean shouldUseFloatOutput(Format inputFormat) {
Assertions.checkNotNull(inputFormat.sampleMimeType);
if (!enableFloatOutput || !supportsOutputEncoding(C.ENCODING_PCM_FLOAT)) {
return false;
}
......
......@@ -15,10 +15,13 @@
*/
package com.google.android.exoplayer2.ext.ffmpeg;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.nio.ByteBuffer;
......@@ -30,13 +33,12 @@ import java.util.List;
/* package */ final class FfmpegDecoder extends
SimpleDecoder<DecoderInputBuffer, SimpleOutputBuffer, FfmpegDecoderException> {
// Space for 64 ms of 48 kHz 8 channel 16-bit PCM audio.
private static final int OUTPUT_BUFFER_SIZE_16BIT = 64 * 48 * 8 * 2;
// Space for 64 ms of 48 KhZ 8 channel 32-bit PCM audio.
// Output buffer sizes when decoding PCM mu-law streams, which is the maximum FFmpeg outputs.
private static final int OUTPUT_BUFFER_SIZE_16BIT = 65536;
private static final int OUTPUT_BUFFER_SIZE_32BIT = OUTPUT_BUFFER_SIZE_16BIT * 2;
private final String codecName;
private final byte[] extraData;
private final @Nullable byte[] extraData;
private final @C.Encoding int encoding;
private final int outputBufferSize;
......@@ -45,18 +47,26 @@ import java.util.List;
private volatile int channelCount;
private volatile int sampleRate;
public FfmpegDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize,
String mimeType, List<byte[]> initializationData, boolean outputFloat)
public FfmpegDecoder(
int numInputBuffers,
int numOutputBuffers,
int initialInputBufferSize,
Format format,
boolean outputFloat)
throws FfmpegDecoderException {
super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]);
if (!FfmpegLibrary.isAvailable()) {
throw new FfmpegDecoderException("Failed to load decoder native libraries.");
}
codecName = FfmpegLibrary.getCodecName(mimeType);
extraData = getExtraData(mimeType, initializationData);
Assertions.checkNotNull(format.sampleMimeType);
codecName =
Assertions.checkNotNull(
FfmpegLibrary.getCodecName(format.sampleMimeType, format.pcmEncoding));
extraData = getExtraData(format.sampleMimeType, format.initializationData);
encoding = outputFloat ? C.ENCODING_PCM_FLOAT : C.ENCODING_PCM_16BIT;
outputBufferSize = outputFloat ? OUTPUT_BUFFER_SIZE_32BIT : OUTPUT_BUFFER_SIZE_16BIT;
nativeContext = ffmpegInitialize(codecName, extraData, outputFloat);
nativeContext =
ffmpegInitialize(codecName, extraData, outputFloat, format.sampleRate, format.channelCount);
if (nativeContext == 0) {
throw new FfmpegDecoderException("Initialization failed.");
}
......@@ -84,7 +94,7 @@ import java.util.List;
}
@Override
protected FfmpegDecoderException decode(
protected @Nullable FfmpegDecoderException decode(
DecoderInputBuffer inputBuffer, SimpleOutputBuffer outputBuffer, boolean reset) {
if (reset) {
nativeContext = ffmpegReset(nativeContext, extraData);
......@@ -103,6 +113,7 @@ import java.util.List;
channelCount = ffmpegGetChannelCount(nativeContext);
sampleRate = ffmpegGetSampleRate(nativeContext);
if (sampleRate == 0 && "alac".equals(codecName)) {
Assertions.checkNotNull(extraData);
// ALAC decoder did not set the sample rate in earlier versions of FFMPEG.
// See https://trac.ffmpeg.org/ticket/6096
ParsableByteArray parsableExtraData = new ParsableByteArray(extraData);
......@@ -148,7 +159,7 @@ import java.util.List;
* Returns FFmpeg-compatible codec-specific initialization data ("extra data"), or {@code null} if
* not required.
*/
private static byte[] getExtraData(String mimeType, List<byte[]> initializationData) {
private static @Nullable byte[] getExtraData(String mimeType, List<byte[]> initializationData) {
switch (mimeType) {
case MimeTypes.AUDIO_AAC:
case MimeTypes.AUDIO_ALAC:
......@@ -173,12 +184,20 @@ import java.util.List;
}
}
private native long ffmpegInitialize(String codecName, byte[] extraData, boolean outputFloat);
private native long ffmpegInitialize(
String codecName,
@Nullable byte[] extraData,
boolean outputFloat,
int rawSampleRate,
int rawChannelCount);
private native int ffmpegDecode(long context, ByteBuffer inputData, int inputSize,
ByteBuffer outputData, int outputSize);
private native int ffmpegGetChannelCount(long context);
private native int ffmpegGetSampleRate(long context);
private native long ffmpegReset(long context, byte[] extraData);
private native long ffmpegReset(long context, @Nullable byte[] extraData);
private native void ffmpegRelease(long context);
}
......@@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer2.ext.ffmpeg;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.util.LibraryLoader;
import com.google.android.exoplayer2.util.MimeTypes;
......@@ -51,10 +53,8 @@ public final class FfmpegLibrary {
return LOADER.isAvailable();
}
/**
* Returns the version of the underlying library if available, or null otherwise.
*/
public static String getVersion() {
/** Returns the version of the underlying library if available, or null otherwise. */
public static @Nullable String getVersion() {
return isAvailable() ? ffmpegGetVersion() : null;
}
......@@ -62,19 +62,21 @@ public final class FfmpegLibrary {
* Returns whether the underlying library supports the specified MIME type.
*
* @param mimeType The MIME type to check.
* @param encoding The PCM encoding for raw audio.
*/
public static boolean supportsFormat(String mimeType) {
public static boolean supportsFormat(String mimeType, @C.PcmEncoding int encoding) {
if (!isAvailable()) {
return false;
}
String codecName = getCodecName(mimeType);
String codecName = getCodecName(mimeType, encoding);
return codecName != null && ffmpegHasDecoder(codecName);
}
/**
* Returns the name of the FFmpeg decoder that could be used to decode {@code mimeType}.
* Returns the name of the FFmpeg decoder that could be used to decode the format, or {@code null}
* if it's unsupported.
*/
/* package */ static String getCodecName(String mimeType) {
/* package */ static @Nullable String getCodecName(String mimeType, @C.PcmEncoding int encoding) {
switch (mimeType) {
case MimeTypes.AUDIO_AAC:
return "aac";
......@@ -103,6 +105,14 @@ public final class FfmpegLibrary {
return "flac";
case MimeTypes.AUDIO_ALAC:
return "alac";
case MimeTypes.AUDIO_RAW:
if (encoding == C.ENCODING_PCM_MU_LAW) {
return "pcm_mulaw";
} else if (encoding == C.ENCODING_PCM_A_LAW) {
return "pcm_alaw";
} else {
return null;
}
default:
return null;
}
......
......@@ -27,6 +27,7 @@ extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavresample/avresample.h>
#include <libavutil/channel_layout.h>
#include <libavutil/error.h>
#include <libavutil/opt.h>
}
......@@ -72,8 +73,9 @@ AVCodec *getCodecByName(JNIEnv* env, jstring codecName);
* provided extraData as initialization data for the decoder if it is non-NULL.
* Returns the created context.
*/
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec,
jbyteArray extraData, jboolean outputFloat);
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
jboolean outputFloat, jint rawSampleRate,
jint rawChannelCount);
/**
* Decodes the packet into the output buffer, returning the number of bytes
......@@ -110,13 +112,14 @@ LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) {
}
DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName, jbyteArray extraData,
jboolean outputFloat) {
jboolean outputFloat, jint rawSampleRate, jint rawChannelCount) {
AVCodec *codec = getCodecByName(env, codecName);
if (!codec) {
LOGE("Codec not found.");
return 0L;
}
return (jlong) createContext(env, codec, extraData, outputFloat);
return (jlong)createContext(env, codec, extraData, outputFloat, rawSampleRate,
rawChannelCount);
}
DECODER_FUNC(jint, ffmpegDecode, jlong context, jobject inputData,
......@@ -180,8 +183,11 @@ DECODER_FUNC(jlong, ffmpegReset, jlong jContext, jbyteArray extraData) {
LOGE("Unexpected error finding codec %d.", codecId);
return 0L;
}
return (jlong) createContext(env, codec, extraData,
context->request_sample_fmt == OUTPUT_FORMAT_PCM_FLOAT);
jboolean outputFloat =
(jboolean)(context->request_sample_fmt == OUTPUT_FORMAT_PCM_FLOAT);
return (jlong)createContext(env, codec, extraData, outputFloat,
/* rawSampleRate= */ -1,
/* rawChannelCount= */ -1);
}
avcodec_flush_buffers(context);
......@@ -204,8 +210,9 @@ AVCodec *getCodecByName(JNIEnv* env, jstring codecName) {
return codec;
}
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec,
jbyteArray extraData, jboolean outputFloat) {
AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
jboolean outputFloat, jint rawSampleRate,
jint rawChannelCount) {
AVCodecContext *context = avcodec_alloc_context3(codec);
if (!context) {
LOGE("Failed to allocate context.");
......@@ -225,6 +232,12 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec,
}
env->GetByteArrayRegion(extraData, 0, size, (jbyte *) context->extradata);
}
if (context->codec_id == AV_CODEC_ID_PCM_MULAW ||
context->codec_id == AV_CODEC_ID_PCM_ALAW) {
context->sample_rate = rawSampleRate;
context->channels = rawChannelCount;
context->channel_layout = av_get_default_channel_layout(rawChannelCount);
}
int result = avcodec_open2(context, codec, NULL);
if (result < 0) {
logError("avcodec_open2", result);
......
......@@ -136,6 +136,8 @@ public final class C {
ENCODING_PCM_24BIT,
ENCODING_PCM_32BIT,
ENCODING_PCM_FLOAT,
ENCODING_PCM_MU_LAW,
ENCODING_PCM_A_LAW,
ENCODING_AC3,
ENCODING_E_AC3,
ENCODING_DTS,
......@@ -144,12 +146,19 @@ public final class C {
})
public @interface Encoding {}
/**
* Represents a PCM audio encoding, or an invalid or unset value.
*/
/** Represents a PCM audio encoding, or an invalid or unset value. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({Format.NO_VALUE, ENCODING_INVALID, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT,
ENCODING_PCM_24BIT, ENCODING_PCM_32BIT, ENCODING_PCM_FLOAT})
@IntDef({
Format.NO_VALUE,
ENCODING_INVALID,
ENCODING_PCM_8BIT,
ENCODING_PCM_16BIT,
ENCODING_PCM_24BIT,
ENCODING_PCM_32BIT,
ENCODING_PCM_FLOAT,
ENCODING_PCM_MU_LAW,
ENCODING_PCM_A_LAW
})
public @interface PcmEncoding {}
/** @see AudioFormat#ENCODING_INVALID */
public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID;
......@@ -163,6 +172,10 @@ public final class C {
public static final int ENCODING_PCM_32BIT = 0x40000000;
/** @see AudioFormat#ENCODING_PCM_FLOAT */
public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT;
/** Audio encoding for mu-law. */
public static final int ENCODING_PCM_MU_LAW = 0x10000000;
/** Audio encoding for A-law. */
public static final int ENCODING_PCM_A_LAW = 0x20000000;
/** @see AudioFormat#ENCODING_AC3 */
public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3;
/** @see AudioFormat#ENCODING_E_AC3 */
......
......@@ -125,12 +125,12 @@ public final class Format implements Parcelable {
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},
* {@link C#ENCODING_PCM_24BIT} and {@link C#ENCODING_PCM_32BIT}. Set to {@link #NO_VALUE} for
* other media types.
* then one of {@link C#ENCODING_PCM_8BIT}, {@link C#ENCODING_PCM_16BIT}, {@link
* C#ENCODING_PCM_24BIT}, {@link C#ENCODING_PCM_32BIT}, {@link C#ENCODING_PCM_FLOAT}, {@link
* C#ENCODING_PCM_MU_LAW} or {@link C#ENCODING_PCM_A_LAW}. Set to {@link #NO_VALUE} for other
* media types.
*/
@C.PcmEncoding
public final int pcmEncoding;
public final @C.PcmEncoding int pcmEncoding;
/**
* The number of frames to trim from the start of the decoded audio stream, or 0 if not
* applicable.
......
......@@ -193,7 +193,7 @@ import java.lang.reflect.Method;
audioTimestampPoller = new AudioTimestampPoller(audioTrack);
outputSampleRate = audioTrack.getSampleRate();
needsPassthroughWorkarounds = needsPassthroughWorkarounds(outputEncoding);
isOutputPcm = Util.isEncodingPcm(outputEncoding);
isOutputPcm = Util.isEncodingLinearPcm(outputEncoding);
bufferSizeUs = isOutputPcm ? framesToDurationUs(bufferSize / outputPcmFrameSize) : C.TIME_UNSET;
lastRawPlaybackHeadPosition = 0;
rawPlaybackHeadWrapCount = 0;
......
......@@ -371,7 +371,7 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public boolean isEncodingSupported(@C.Encoding int encoding) {
if (Util.isEncodingPcm(encoding)) {
if (Util.isEncodingLinearPcm(encoding)) {
// AudioTrack supports 16-bit integer PCM output in all platform API versions, and float
// output from platform API version 21 only. Other integer PCM encodings are resampled by this
// sink to 16-bit PCM.
......@@ -405,7 +405,7 @@ public final class DefaultAudioSink implements AudioSink {
this.inputSampleRate = inputSampleRate;
int channelCount = inputChannelCount;
int sampleRate = inputSampleRate;
isInputPcm = Util.isEncodingPcm(inputEncoding);
isInputPcm = Util.isEncodingLinearPcm(inputEncoding);
shouldConvertHighResIntPcmToFloat =
enableConvertHighResIntPcmToFloat
&& isEncodingSupported(C.ENCODING_PCM_32BIT)
......
......@@ -20,6 +20,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.BaseRenderer;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
......@@ -121,7 +122,9 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output.
*/
public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
public SimpleDecoderAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioProcessor... audioProcessors) {
this(
eventHandler,
......@@ -139,8 +142,10 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
* @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.
*/
public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
AudioCapabilities audioCapabilities) {
public SimpleDecoderAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
@Nullable AudioCapabilities audioCapabilities) {
this(
eventHandler,
eventListener,
......@@ -164,9 +169,13 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output.
*/
public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
AudioCapabilities audioCapabilities, DrmSessionManager<ExoMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys, AudioProcessor... audioProcessors) {
public SimpleDecoderAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
@Nullable AudioCapabilities audioCapabilities,
@Nullable DrmSessionManager<ExoMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys,
AudioProcessor... audioProcessors) {
this(eventHandler, eventListener, drmSessionManager,
playClearSamplesWithoutKeys, new DefaultAudioSink(audioCapabilities, audioProcessors));
}
......@@ -184,8 +193,11 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param audioSink The sink to which audio will be output.
*/
public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys,
public SimpleDecoderAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
@Nullable DrmSessionManager<ExoMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys,
AudioSink audioSink) {
super(C.TRACK_TYPE_AUDIO);
this.drmSessionManager = drmSessionManager;
......
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.decoder;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import java.util.ArrayDeque;
......@@ -292,14 +293,13 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
* Decodes the {@code inputBuffer} and stores any decoded output in {@code outputBuffer}.
*
* @param inputBuffer The buffer to decode.
* @param outputBuffer The output buffer to store decoded data. The flag
* {@link C#BUFFER_FLAG_DECODE_ONLY} will be set if the same flag is set on
* {@code inputBuffer}, but may be set/unset as required. If the flag is set when the call
* returns then the output buffer will not be made available to dequeue. The output buffer
* may not have been populated in this case.
* @param outputBuffer The output buffer to store decoded data. The flag {@link
* C#BUFFER_FLAG_DECODE_ONLY} will be set if the same flag is set on {@code inputBuffer}, but
* may be set/unset as required. If the flag is set when the call returns then the output
* buffer will not be made available to dequeue. The output buffer may not have been populated
* in this case.
* @param reset Whether the decoder must be reset before decoding.
* @return A decoder exception if an error occurred, or null if decoding was successful.
*/
protected abstract E decode(I inputBuffer, O outputBuffer, boolean reset);
protected abstract @Nullable E decode(I inputBuffer, O outputBuffer, boolean reset);
}
......@@ -33,6 +33,10 @@ import java.io.IOException;
private static final int TYPE_PCM = 0x0001;
/** Float PCM audio data. */
private static final int TYPE_FLOAT = 0x0003;
/** 8-bit ITU-T G.711 A-law audio data. */
private static final int TYPE_A_LAW = 0x0006;
/** 8-bit ITU-T G.711 mu-law audio data. */
private static final int TYPE_MU_LAW = 0x0007;
/** Extended WAVE format. */
private static final int TYPE_WAVE_FORMAT_EXTENSIBLE = 0xFFFE;
......@@ -98,6 +102,12 @@ import java.io.IOException;
case TYPE_FLOAT:
encoding = bitsPerSample == 32 ? C.ENCODING_PCM_FLOAT : C.ENCODING_INVALID;
break;
case TYPE_A_LAW:
encoding = C.ENCODING_PCM_A_LAW;
break;
case TYPE_MU_LAW:
encoding = C.ENCODING_PCM_MU_LAW;
break;
default:
Log.e(TAG, "Unsupported WAV format type: " + type);
return null;
......
......@@ -1092,12 +1092,12 @@ public final class Util {
}
/**
* Returns whether {@code encoding} is one of the PCM encodings.
* Returns whether {@code encoding} is one of the linear PCM encodings.
*
* @param encoding The encoding of the audio data.
* @return Whether the encoding is one of the PCM encodings.
*/
public static boolean isEncodingPcm(@C.Encoding int encoding) {
public static boolean isEncodingLinearPcm(@C.Encoding int encoding) {
return encoding == C.ENCODING_PCM_8BIT
|| encoding == C.ENCODING_PCM_16BIT
|| encoding == C.ENCODING_PCM_24BIT
......
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