mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Pass an array of BufferProcessors to the AudioTrack.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=146215966
This commit is contained in:
parent
feeec77407
commit
74acbe04e3
10 changed files with 238 additions and 147 deletions
|
|
@ -19,8 +19,8 @@ import android.os.Handler;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
|
||||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||||
|
import com.google.android.exoplayer2.audio.BufferProcessor;
|
||||||
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
|
@ -43,21 +43,12 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
* null if delivery of events is not required.
|
* null if delivery of events is not required.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
*/
|
* @param bufferProcessors Optional {@link BufferProcessor}s which will process PCM audio buffers
|
||||||
public FfmpegAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener) {
|
* before they are output.
|
||||||
super(eventHandler, eventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
|
||||||
* null if delivery of events is not required.
|
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
|
||||||
* @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 FfmpegAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
|
public FfmpegAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
|
||||||
AudioCapabilities audioCapabilities) {
|
BufferProcessor... bufferProcessors) {
|
||||||
super(eventHandler, eventListener, audioCapabilities);
|
super(eventHandler, eventListener, bufferProcessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ package com.google.android.exoplayer2.ext.flac;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
|
||||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||||
|
import com.google.android.exoplayer2.audio.BufferProcessor;
|
||||||
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
|
@ -38,21 +38,12 @@ public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
* null if delivery of events is not required.
|
* null if delivery of events is not required.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
*/
|
* @param bufferProcessors Optional {@link BufferProcessor}s which will process PCM audio buffers
|
||||||
public LibflacAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener) {
|
* before they are output.
|
||||||
super(eventHandler, eventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
|
||||||
* null if delivery of events is not required.
|
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
|
||||||
* @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 LibflacAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
|
public LibflacAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
|
||||||
AudioCapabilities audioCapabilities) {
|
BufferProcessor... bufferProcessors) {
|
||||||
super(eventHandler, eventListener, audioCapabilities);
|
super(eventHandler, eventListener, bufferProcessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ package com.google.android.exoplayer2.ext.opus;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
|
||||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||||
|
import com.google.android.exoplayer2.audio.BufferProcessor;
|
||||||
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
||||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||||
|
|
@ -40,35 +40,26 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
* null if delivery of events is not required.
|
* null if delivery of events is not required.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
|
* @param bufferProcessors Optional {@link BufferProcessor}s which will process PCM audio buffers
|
||||||
|
* before they are output.
|
||||||
*/
|
*/
|
||||||
public LibopusAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener) {
|
public LibopusAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
|
||||||
super(eventHandler, eventListener);
|
BufferProcessor... bufferProcessors) {
|
||||||
|
super(eventHandler, eventListener, bufferProcessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
* null if delivery of events is not required.
|
* null if delivery of events is not required.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
|
* @param bufferProcessors Optional {@link BufferProcessor}s which will process PCM audio
|
||||||
* default capabilities (no encoded audio passthrough support) should be assumed.
|
* buffers before they are output.
|
||||||
*/
|
*/
|
||||||
public LibopusAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
|
public LibopusAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
|
||||||
AudioCapabilities audioCapabilities) {
|
DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||||
super(eventHandler, eventListener, audioCapabilities);
|
BufferProcessor... bufferProcessors) {
|
||||||
}
|
super(eventHandler, eventListener, null, drmSessionManager, playClearSamplesWithoutKeys,
|
||||||
|
bufferProcessors);
|
||||||
/**
|
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
|
||||||
* null if delivery of events is not required.
|
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
|
||||||
* @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 LibopusAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
|
|
||||||
AudioCapabilities audioCapabilities, DrmSessionManager<ExoMediaCrypto> drmSessionManager,
|
|
||||||
boolean playClearSamplesWithoutKeys) {
|
|
||||||
super(eventHandler, eventListener, audioCapabilities, drmSessionManager,
|
|
||||||
playClearSamplesWithoutKeys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import android.view.SurfaceView;
|
||||||
import android.view.TextureView;
|
import android.view.TextureView;
|
||||||
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
import com.google.android.exoplayer2.audio.AudioCapabilities;
|
||||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||||
|
import com.google.android.exoplayer2.audio.BufferProcessor;
|
||||||
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
|
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
|
||||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||||
|
|
@ -624,7 +625,7 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
buildVideoRenderers(context, mainHandler, drmSessionManager, extensionRendererMode,
|
buildVideoRenderers(context, mainHandler, drmSessionManager, extensionRendererMode,
|
||||||
componentListener, allowedVideoJoiningTimeMs, out);
|
componentListener, allowedVideoJoiningTimeMs, out);
|
||||||
buildAudioRenderers(context, mainHandler, drmSessionManager, extensionRendererMode,
|
buildAudioRenderers(context, mainHandler, drmSessionManager, extensionRendererMode,
|
||||||
componentListener, out);
|
componentListener, buildBufferProcessors(), out);
|
||||||
buildTextRenderers(context, mainHandler, extensionRendererMode, componentListener, out);
|
buildTextRenderers(context, mainHandler, extensionRendererMode, componentListener, out);
|
||||||
buildMetadataRenderers(context, mainHandler, extensionRendererMode, componentListener, out);
|
buildMetadataRenderers(context, mainHandler, extensionRendererMode, componentListener, out);
|
||||||
buildMiscellaneousRenderers(context, mainHandler, extensionRendererMode, out);
|
buildMiscellaneousRenderers(context, mainHandler, extensionRendererMode, out);
|
||||||
|
|
@ -636,7 +637,7 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
* @param context The {@link Context} associated with the player.
|
* @param context The {@link Context} associated with the player.
|
||||||
* @param mainHandler A handler associated with the main thread's looper.
|
* @param mainHandler A handler associated with the main thread's looper.
|
||||||
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player will
|
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player will
|
||||||
* not be used for DRM protected playbacks.
|
* not be used for DRM protected playbacks.
|
||||||
* @param extensionRendererMode The extension renderer mode.
|
* @param extensionRendererMode The extension renderer mode.
|
||||||
* @param eventListener An event listener.
|
* @param eventListener An event listener.
|
||||||
* @param allowedVideoJoiningTimeMs The maximum duration in milliseconds for which video renderers
|
* @param allowedVideoJoiningTimeMs The maximum duration in milliseconds for which video renderers
|
||||||
|
|
@ -681,17 +682,19 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
* @param context The {@link Context} associated with the player.
|
* @param context The {@link Context} associated with the player.
|
||||||
* @param mainHandler A handler associated with the main thread's looper.
|
* @param mainHandler A handler associated with the main thread's looper.
|
||||||
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player will
|
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player will
|
||||||
* not be used for DRM protected playbacks.
|
* not be used for DRM protected playbacks.
|
||||||
* @param extensionRendererMode The extension renderer mode.
|
* @param extensionRendererMode The extension renderer mode.
|
||||||
* @param eventListener An event listener.
|
* @param eventListener An event listener.
|
||||||
|
* @param bufferProcessors An array of {@link BufferProcessor}s which will process PCM audio
|
||||||
|
* buffers before they are output. May be empty.
|
||||||
* @param out An array to which the built renderers should be appended.
|
* @param out An array to which the built renderers should be appended.
|
||||||
*/
|
*/
|
||||||
protected void buildAudioRenderers(Context context, Handler mainHandler,
|
protected void buildAudioRenderers(Context context, Handler mainHandler,
|
||||||
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
||||||
@ExtensionRendererMode int extensionRendererMode, AudioRendererEventListener eventListener,
|
@ExtensionRendererMode int extensionRendererMode, AudioRendererEventListener eventListener,
|
||||||
ArrayList<Renderer> out) {
|
BufferProcessor[] bufferProcessors, ArrayList<Renderer> out) {
|
||||||
out.add(new MediaCodecAudioRenderer(MediaCodecSelector.DEFAULT, drmSessionManager, true,
|
out.add(new MediaCodecAudioRenderer(MediaCodecSelector.DEFAULT, drmSessionManager, true,
|
||||||
mainHandler, eventListener, AudioCapabilities.getCapabilities(context)));
|
mainHandler, eventListener, AudioCapabilities.getCapabilities(context), bufferProcessors));
|
||||||
|
|
||||||
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
|
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -705,8 +708,9 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
Class<?> clazz =
|
Class<?> clazz =
|
||||||
Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer");
|
Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer");
|
||||||
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
||||||
AudioRendererEventListener.class);
|
AudioRendererEventListener.class, BufferProcessor[].class);
|
||||||
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, componentListener);
|
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, componentListener,
|
||||||
|
bufferProcessors);
|
||||||
out.add(extensionRendererIndex++, renderer);
|
out.add(extensionRendererIndex++, renderer);
|
||||||
Log.i(TAG, "Loaded LibopusAudioRenderer.");
|
Log.i(TAG, "Loaded LibopusAudioRenderer.");
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
|
|
@ -719,8 +723,9 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
Class<?> clazz =
|
Class<?> clazz =
|
||||||
Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer");
|
Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer");
|
||||||
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
||||||
AudioRendererEventListener.class);
|
AudioRendererEventListener.class, BufferProcessor[].class);
|
||||||
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, componentListener);
|
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, componentListener,
|
||||||
|
bufferProcessors);
|
||||||
out.add(extensionRendererIndex++, renderer);
|
out.add(extensionRendererIndex++, renderer);
|
||||||
Log.i(TAG, "Loaded LibflacAudioRenderer.");
|
Log.i(TAG, "Loaded LibflacAudioRenderer.");
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
|
|
@ -733,8 +738,9 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
Class<?> clazz =
|
Class<?> clazz =
|
||||||
Class.forName("com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer");
|
Class.forName("com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer");
|
||||||
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
Constructor<?> constructor = clazz.getConstructor(Handler.class,
|
||||||
AudioRendererEventListener.class);
|
AudioRendererEventListener.class, BufferProcessor[].class);
|
||||||
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, componentListener);
|
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, componentListener,
|
||||||
|
bufferProcessors);
|
||||||
out.add(extensionRendererIndex++, renderer);
|
out.add(extensionRendererIndex++, renderer);
|
||||||
Log.i(TAG, "Loaded FfmpegAudioRenderer.");
|
Log.i(TAG, "Loaded FfmpegAudioRenderer.");
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
|
|
@ -787,6 +793,14 @@ public class SimpleExoPlayer implements ExoPlayer {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an array of {@link BufferProcessor}s which will process PCM audio buffers before they
|
||||||
|
* are output.
|
||||||
|
*/
|
||||||
|
protected BufferProcessor[] buildBufferProcessors() {
|
||||||
|
return new BufferProcessor[0];
|
||||||
|
}
|
||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
private void removeSurfaceCallbacks() {
|
private void removeSurfaceCallbacks() {
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,21 @@ public final class AudioTrack {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a failure occurs configuring the track.
|
||||||
|
*/
|
||||||
|
public static final class ConfigurationException extends Exception {
|
||||||
|
|
||||||
|
public ConfigurationException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when a failure occurs initializing an {@link android.media.AudioTrack}.
|
* Thrown when a failure occurs initializing an {@link android.media.AudioTrack}.
|
||||||
*/
|
*/
|
||||||
|
|
@ -254,6 +269,7 @@ public final class AudioTrack {
|
||||||
public static boolean failOnSpuriousAudioTimestamp = false;
|
public static boolean failOnSpuriousAudioTimestamp = false;
|
||||||
|
|
||||||
private final AudioCapabilities audioCapabilities;
|
private final AudioCapabilities audioCapabilities;
|
||||||
|
private final BufferProcessor[] bufferProcessors;
|
||||||
private final Listener listener;
|
private final Listener listener;
|
||||||
private final ConditionVariable releasingConditionVariable;
|
private final ConditionVariable releasingConditionVariable;
|
||||||
private final long[] playheadOffsets;
|
private final long[] playheadOffsets;
|
||||||
|
|
@ -267,12 +283,12 @@ public final class AudioTrack {
|
||||||
private android.media.AudioTrack audioTrack;
|
private android.media.AudioTrack audioTrack;
|
||||||
private int sampleRate;
|
private int sampleRate;
|
||||||
private int channelConfig;
|
private int channelConfig;
|
||||||
@C.StreamType
|
|
||||||
private int streamType;
|
|
||||||
@C.Encoding
|
@C.Encoding
|
||||||
private int inputEncoding;
|
private int encoding;
|
||||||
@C.Encoding
|
@C.Encoding
|
||||||
private int outputEncoding;
|
private int outputEncoding;
|
||||||
|
@C.StreamType
|
||||||
|
private int streamType;
|
||||||
private boolean passthrough;
|
private boolean passthrough;
|
||||||
private int pcmFrameSize;
|
private int pcmFrameSize;
|
||||||
private int bufferSize;
|
private int bufferSize;
|
||||||
|
|
@ -303,8 +319,6 @@ public final class AudioTrack {
|
||||||
private byte[] preV21OutputBuffer;
|
private byte[] preV21OutputBuffer;
|
||||||
private int preV21OutputBufferOffset;
|
private int preV21OutputBufferOffset;
|
||||||
|
|
||||||
private BufferProcessor resampler;
|
|
||||||
|
|
||||||
private boolean playing;
|
private boolean playing;
|
||||||
private int audioSessionId;
|
private int audioSessionId;
|
||||||
private boolean tunneling;
|
private boolean tunneling;
|
||||||
|
|
@ -312,11 +326,18 @@ public final class AudioTrack {
|
||||||
private long lastFeedElapsedRealtimeMs;
|
private long lastFeedElapsedRealtimeMs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param audioCapabilities The current audio capabilities.
|
* @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.
|
||||||
|
* @param bufferProcessors An array of {@link BufferProcessor}s which will process PCM audio
|
||||||
|
* buffers before they are output. May be empty.
|
||||||
* @param listener Listener for audio track events.
|
* @param listener Listener for audio track events.
|
||||||
*/
|
*/
|
||||||
public AudioTrack(AudioCapabilities audioCapabilities, Listener listener) {
|
public AudioTrack(AudioCapabilities audioCapabilities, BufferProcessor[] bufferProcessors,
|
||||||
|
Listener listener) {
|
||||||
this.audioCapabilities = audioCapabilities;
|
this.audioCapabilities = audioCapabilities;
|
||||||
|
this.bufferProcessors = new BufferProcessor[bufferProcessors.length + 1];
|
||||||
|
this.bufferProcessors[0] = new ResamplingBufferProcessor();
|
||||||
|
System.arraycopy(bufferProcessors, 0, this.bufferProcessors, 1, bufferProcessors.length);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
releasingConditionVariable = new ConditionVariable(true);
|
releasingConditionVariable = new ConditionVariable(true);
|
||||||
if (Util.SDK_INT >= 18) {
|
if (Util.SDK_INT >= 18) {
|
||||||
|
|
@ -413,9 +434,23 @@ public final class AudioTrack {
|
||||||
* {@link C#ENCODING_PCM_32BIT}.
|
* {@link C#ENCODING_PCM_32BIT}.
|
||||||
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to infer a
|
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to infer a
|
||||||
* suitable buffer size automatically.
|
* suitable buffer size automatically.
|
||||||
|
* @throws ConfigurationException If an error occurs configuring the track.
|
||||||
*/
|
*/
|
||||||
public void configure(String mimeType, int channelCount, int sampleRate,
|
public void configure(String mimeType, int channelCount, int sampleRate,
|
||||||
@C.PcmEncoding int pcmEncoding, int specifiedBufferSize) {
|
@C.PcmEncoding int pcmEncoding, int specifiedBufferSize) throws ConfigurationException {
|
||||||
|
boolean passthrough = !MimeTypes.AUDIO_RAW.equals(mimeType);
|
||||||
|
@C.Encoding int encoding = passthrough ? getEncodingForMimeType(mimeType) : pcmEncoding;
|
||||||
|
if (!passthrough) {
|
||||||
|
for (BufferProcessor bufferProcessor : bufferProcessors) {
|
||||||
|
try {
|
||||||
|
bufferProcessor.configure(sampleRate, channelCount, encoding);
|
||||||
|
} catch (BufferProcessor.UnhandledFormatException e) {
|
||||||
|
throw new ConfigurationException(e);
|
||||||
|
}
|
||||||
|
encoding = bufferProcessor.getOutputEncoding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int channelConfig;
|
int channelConfig;
|
||||||
switch (channelCount) {
|
switch (channelCount) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
@ -443,7 +478,7 @@ public final class AudioTrack {
|
||||||
channelConfig = C.CHANNEL_OUT_7POINT1_SURROUND;
|
channelConfig = C.CHANNEL_OUT_7POINT1_SURROUND;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unsupported channel count: " + channelCount);
|
throw new ConfigurationException("Unsupported channel count: " + channelCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for overly strict channel configuration checks on nVidia Shield.
|
// Workaround for overly strict channel configuration checks on nVidia Shield.
|
||||||
|
|
@ -461,25 +496,13 @@ public final class AudioTrack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean passthrough = !MimeTypes.AUDIO_RAW.equals(mimeType);
|
|
||||||
|
|
||||||
// Workaround for Nexus Player not reporting support for mono passthrough.
|
// Workaround for Nexus Player not reporting support for mono passthrough.
|
||||||
// (See [Internal: b/34268671].)
|
// (See [Internal: b/34268671].)
|
||||||
if (Util.SDK_INT <= 25 && "fugu".equals(Util.DEVICE) && passthrough && channelCount == 1) {
|
if (Util.SDK_INT <= 25 && "fugu".equals(Util.DEVICE) && passthrough && channelCount == 1) {
|
||||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@C.Encoding int inputEncoding;
|
if (isInitialized() && this.encoding == encoding && this.sampleRate == sampleRate
|
||||||
if (passthrough) {
|
|
||||||
inputEncoding = getEncodingForMimeType(mimeType);
|
|
||||||
} else if (pcmEncoding == C.ENCODING_PCM_8BIT || pcmEncoding == C.ENCODING_PCM_16BIT
|
|
||||||
|| pcmEncoding == C.ENCODING_PCM_24BIT || pcmEncoding == C.ENCODING_PCM_32BIT) {
|
|
||||||
inputEncoding = pcmEncoding;
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Unsupported PCM encoding: " + pcmEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInitialized() && this.inputEncoding == inputEncoding && this.sampleRate == sampleRate
|
|
||||||
&& this.channelConfig == channelConfig) {
|
&& this.channelConfig == channelConfig) {
|
||||||
// We already have an audio track with the correct sample rate, channel config and encoding.
|
// We already have an audio track with the correct sample rate, channel config and encoding.
|
||||||
return;
|
return;
|
||||||
|
|
@ -487,15 +510,12 @@ public final class AudioTrack {
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
this.inputEncoding = inputEncoding;
|
this.encoding = encoding;
|
||||||
this.passthrough = passthrough;
|
this.passthrough = passthrough;
|
||||||
this.sampleRate = sampleRate;
|
this.sampleRate = sampleRate;
|
||||||
this.channelConfig = channelConfig;
|
this.channelConfig = channelConfig;
|
||||||
pcmFrameSize = 2 * channelCount; // 2 bytes per 16-bit sample * number of channels.
|
pcmFrameSize = 2 * channelCount; // 2 bytes per 16-bit sample * number of channels.
|
||||||
outputEncoding = passthrough ? inputEncoding : C.ENCODING_PCM_16BIT;
|
outputEncoding = passthrough ? encoding : C.ENCODING_PCM_16BIT;
|
||||||
|
|
||||||
resampler = outputEncoding != inputEncoding ? new ResamplingBufferProcessor(inputEncoding)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (specifiedBufferSize != 0) {
|
if (specifiedBufferSize != 0) {
|
||||||
bufferSize = specifiedBufferSize;
|
bufferSize = specifiedBufferSize;
|
||||||
|
|
@ -684,8 +704,12 @@ public final class AudioTrack {
|
||||||
}
|
}
|
||||||
|
|
||||||
inputBuffer = buffer;
|
inputBuffer = buffer;
|
||||||
outputBuffer = resampler != null ? resampler.handleBuffer(inputBuffer, outputBuffer)
|
if (!passthrough) {
|
||||||
: inputBuffer;
|
for (BufferProcessor bufferProcessor : bufferProcessors) {
|
||||||
|
buffer = bufferProcessor.handleBuffer(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputBuffer = buffer;
|
||||||
if (Util.SDK_INT < 21) {
|
if (Util.SDK_INT < 21) {
|
||||||
int bytesRemaining = outputBuffer.remaining();
|
int bytesRemaining = outputBuffer.remaining();
|
||||||
if (preV21OutputBuffer == null || preV21OutputBuffer.length < bytesRemaining) {
|
if (preV21OutputBuffer == null || preV21OutputBuffer.length < bytesRemaining) {
|
||||||
|
|
@ -886,6 +910,9 @@ public final class AudioTrack {
|
||||||
framesPerEncodedSample = 0;
|
framesPerEncodedSample = 0;
|
||||||
inputBuffer = null;
|
inputBuffer = null;
|
||||||
avSyncHeader = null;
|
avSyncHeader = null;
|
||||||
|
for (BufferProcessor bufferProcessor : bufferProcessors) {
|
||||||
|
bufferProcessor.flush();
|
||||||
|
}
|
||||||
bytesUntilNextAvSync = 0;
|
bytesUntilNextAvSync = 0;
|
||||||
startMediaTimeState = START_NOT_SET;
|
startMediaTimeState = START_NOT_SET;
|
||||||
latencyUs = 0;
|
latencyUs = 0;
|
||||||
|
|
@ -919,6 +946,9 @@ public final class AudioTrack {
|
||||||
public void release() {
|
public void release() {
|
||||||
reset();
|
reset();
|
||||||
releaseKeepSessionIdAudioTrack();
|
releaseKeepSessionIdAudioTrack();
|
||||||
|
for (BufferProcessor bufferProcessor : bufferProcessors) {
|
||||||
|
bufferProcessor.release();
|
||||||
|
}
|
||||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||||
playing = false;
|
playing = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,23 +15,60 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.audio;
|
package com.google.android.exoplayer2.audio;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for processors of buffers, for use with {@link AudioTrack}.
|
* Interface for processors of audio buffers.
|
||||||
*/
|
*/
|
||||||
public interface BufferProcessor {
|
public interface BufferProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the data in the specified input buffer in its entirety. Populates {@code output} with
|
* Exception thrown when a processor can't be configured for a given input format.
|
||||||
* processed data if is not {@code null} and has sufficient capacity. Otherwise a different buffer
|
*/
|
||||||
* will be populated and returned.
|
final class UnhandledFormatException extends Exception {
|
||||||
|
|
||||||
|
public UnhandledFormatException(int sampleRateHz, int channelCount, @C.Encoding int encoding) {
|
||||||
|
super("Unhandled format: " + sampleRateHz + " Hz, " + channelCount + " channels in encoding "
|
||||||
|
+ encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures this processor to take input buffers with the specified format.
|
||||||
|
*
|
||||||
|
* @param sampleRateHz The sample rate of input audio in Hz.
|
||||||
|
* @param channelCount The number of interleaved channels in input audio.
|
||||||
|
* @param encoding The encoding of input audio.
|
||||||
|
* @throws UnhandledFormatException Thrown if the specified format can't be handled as input.
|
||||||
|
*/
|
||||||
|
void configure(int sampleRateHz, int channelCount, @C.Encoding int encoding)
|
||||||
|
throws UnhandledFormatException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the encoding used in buffers output by this processor.
|
||||||
|
*/
|
||||||
|
@C.Encoding
|
||||||
|
int getOutputEncoding();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the data in the specified input buffer in its entirety.
|
||||||
*
|
*
|
||||||
* @param input A buffer containing the input data to process.
|
* @param input A buffer containing the input data to process.
|
||||||
* @param output A buffer into which the output should be written, if its capacity is sufficient.
|
* @return A buffer containing the processed output. This may be the same as the input buffer if
|
||||||
* @return The processed output. Different to {@code output} if null was passed, or if its
|
* no processing was required.
|
||||||
* capacity was insufficient.
|
|
||||||
*/
|
*/
|
||||||
ByteBuffer handleBuffer(ByteBuffer input, ByteBuffer output);
|
ByteBuffer handleBuffer(ByteBuffer input);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears any state in preparation for receiving a new stream of buffers.
|
||||||
|
*/
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases any resources associated with this instance.
|
||||||
|
*/
|
||||||
|
void release();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,13 +121,16 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
|
* @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.
|
* default capabilities (no encoded audio passthrough support) should be assumed.
|
||||||
|
* @param bufferProcessors Optional {@link BufferProcessor}s which will process PCM audio buffers
|
||||||
|
* before they are output.
|
||||||
*/
|
*/
|
||||||
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector,
|
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector,
|
||||||
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
|
||||||
boolean playClearSamplesWithoutKeys, Handler eventHandler,
|
boolean playClearSamplesWithoutKeys, Handler eventHandler,
|
||||||
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities) {
|
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities,
|
||||||
|
BufferProcessor... bufferProcessors) {
|
||||||
super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
|
super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
|
||||||
audioTrack = new AudioTrack(audioCapabilities, new AudioTrackListener());
|
audioTrack = new AudioTrack(audioCapabilities, bufferProcessors, new AudioTrackListener());
|
||||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,14 +222,19 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat) {
|
protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat)
|
||||||
|
throws ExoPlaybackException {
|
||||||
boolean passthrough = passthroughMediaFormat != null;
|
boolean passthrough = passthroughMediaFormat != null;
|
||||||
String mimeType = passthrough ? passthroughMediaFormat.getString(MediaFormat.KEY_MIME)
|
String mimeType = passthrough ? passthroughMediaFormat.getString(MediaFormat.KEY_MIME)
|
||||||
: MimeTypes.AUDIO_RAW;
|
: MimeTypes.AUDIO_RAW;
|
||||||
MediaFormat format = passthrough ? passthroughMediaFormat : outputFormat;
|
MediaFormat format = passthrough ? passthroughMediaFormat : outputFormat;
|
||||||
int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
|
int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
|
||||||
int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
|
int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
|
||||||
audioTrack.configure(mimeType, channelCount, sampleRate, pcmEncoding, 0);
|
try {
|
||||||
|
audioTrack.configure(mimeType, channelCount, sampleRate, pcmEncoding, 0);
|
||||||
|
} catch (AudioTrack.ConfigurationException e) {
|
||||||
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -21,35 +21,47 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link BufferProcessor} that converts PCM input buffers from a specified input bit depth to
|
* A {@link BufferProcessor} that outputs buffers in {@link C#ENCODING_PCM_16BIT}.
|
||||||
* {@link C#ENCODING_PCM_16BIT} in preparation for writing to an {@link android.media.AudioTrack}.
|
|
||||||
*/
|
*/
|
||||||
/* package */ final class ResamplingBufferProcessor implements BufferProcessor {
|
/* package */ final class ResamplingBufferProcessor implements BufferProcessor {
|
||||||
|
|
||||||
@C.PcmEncoding
|
@C.PcmEncoding
|
||||||
private final int inputEncoding;
|
private int encoding;
|
||||||
|
private ByteBuffer outputBuffer;
|
||||||
|
|
||||||
/**
|
public ResamplingBufferProcessor() {
|
||||||
* Creates a new buffer processor for resampling input in the specified encoding.
|
encoding = C.ENCODING_INVALID;
|
||||||
*
|
|
||||||
* @param inputEncoding The PCM encoding of input buffers.
|
|
||||||
* @throws IllegalArgumentException Thrown if the input encoding is not PCM or its bit depth is
|
|
||||||
* not 8, 24 or 32-bits.
|
|
||||||
*/
|
|
||||||
public ResamplingBufferProcessor(@C.PcmEncoding int inputEncoding) {
|
|
||||||
Assertions.checkArgument(inputEncoding == C.ENCODING_PCM_8BIT
|
|
||||||
|| inputEncoding == C.ENCODING_PCM_24BIT || inputEncoding == C.ENCODING_PCM_32BIT);
|
|
||||||
this.inputEncoding = inputEncoding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer handleBuffer(ByteBuffer input, ByteBuffer output) {
|
public void configure(int sampleRateHz, int channelCount, @C.Encoding int encoding)
|
||||||
int offset = input.position();
|
throws UnhandledFormatException {
|
||||||
int limit = input.limit();
|
if (encoding != C.ENCODING_PCM_8BIT && encoding != C.ENCODING_PCM_16BIT
|
||||||
int size = limit - offset;
|
&& encoding != C.ENCODING_PCM_24BIT && encoding != C.ENCODING_PCM_32BIT) {
|
||||||
|
throw new UnhandledFormatException(sampleRateHz, channelCount, encoding);
|
||||||
|
}
|
||||||
|
if (encoding == C.ENCODING_PCM_16BIT) {
|
||||||
|
outputBuffer = null;
|
||||||
|
}
|
||||||
|
this.encoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOutputEncoding() {
|
||||||
|
return C.ENCODING_PCM_16BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer handleBuffer(ByteBuffer buffer) {
|
||||||
|
int position = buffer.position();
|
||||||
|
int limit = buffer.limit();
|
||||||
|
int size = limit - position;
|
||||||
|
|
||||||
int resampledSize;
|
int resampledSize;
|
||||||
switch (inputEncoding) {
|
switch (encoding) {
|
||||||
|
case C.ENCODING_PCM_16BIT:
|
||||||
|
// No processing required.
|
||||||
|
return buffer;
|
||||||
case C.ENCODING_PCM_8BIT:
|
case C.ENCODING_PCM_8BIT:
|
||||||
resampledSize = size * 2;
|
resampledSize = size * 2;
|
||||||
break;
|
break;
|
||||||
|
|
@ -59,7 +71,6 @@ import java.nio.ByteBuffer;
|
||||||
case C.ENCODING_PCM_32BIT:
|
case C.ENCODING_PCM_32BIT:
|
||||||
resampledSize = size / 2;
|
resampledSize = size / 2;
|
||||||
break;
|
break;
|
||||||
case C.ENCODING_PCM_16BIT:
|
|
||||||
case C.ENCODING_INVALID:
|
case C.ENCODING_INVALID:
|
||||||
case Format.NO_VALUE:
|
case Format.NO_VALUE:
|
||||||
default:
|
default:
|
||||||
|
|
@ -67,34 +78,34 @@ import java.nio.ByteBuffer;
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer resampledBuffer = output;
|
if (outputBuffer == null || outputBuffer.capacity() < resampledSize) {
|
||||||
if (resampledBuffer == null || resampledBuffer.capacity() < resampledSize) {
|
outputBuffer = ByteBuffer.allocateDirect(resampledSize).order(buffer.order());
|
||||||
resampledBuffer = ByteBuffer.allocateDirect(resampledSize);
|
} else {
|
||||||
|
Assertions.checkState(!outputBuffer.hasRemaining());
|
||||||
|
outputBuffer.clear();
|
||||||
}
|
}
|
||||||
resampledBuffer.position(0);
|
|
||||||
resampledBuffer.limit(resampledSize);
|
|
||||||
|
|
||||||
// Samples are little endian.
|
// Samples are little endian.
|
||||||
switch (inputEncoding) {
|
switch (encoding) {
|
||||||
case C.ENCODING_PCM_8BIT:
|
case C.ENCODING_PCM_8BIT:
|
||||||
// 8->16 bit resampling. Shift each byte from [0, 256) to [-128, 128) and scale up.
|
// 8->16 bit resampling. Shift each byte from [0, 256) to [-128, 128) and scale up.
|
||||||
for (int i = offset; i < limit; i++) {
|
for (int i = position; i < limit; i++) {
|
||||||
resampledBuffer.put((byte) 0);
|
outputBuffer.put((byte) 0);
|
||||||
resampledBuffer.put((byte) ((input.get(i) & 0xFF) - 128));
|
outputBuffer.put((byte) ((buffer.get(i) & 0xFF) - 128));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case C.ENCODING_PCM_24BIT:
|
case C.ENCODING_PCM_24BIT:
|
||||||
// 24->16 bit resampling. Drop the least significant byte.
|
// 24->16 bit resampling. Drop the least significant byte.
|
||||||
for (int i = offset; i < limit; i += 3) {
|
for (int i = position; i < limit; i += 3) {
|
||||||
resampledBuffer.put(input.get(i + 1));
|
outputBuffer.put(buffer.get(i + 1));
|
||||||
resampledBuffer.put(input.get(i + 2));
|
outputBuffer.put(buffer.get(i + 2));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case C.ENCODING_PCM_32BIT:
|
case C.ENCODING_PCM_32BIT:
|
||||||
// 32->16 bit resampling. Drop the two least significant bytes.
|
// 32->16 bit resampling. Drop the two least significant bytes.
|
||||||
for (int i = offset; i < limit; i += 4) {
|
for (int i = position; i < limit; i += 4) {
|
||||||
resampledBuffer.put(input.get(i + 2));
|
outputBuffer.put(buffer.get(i + 2));
|
||||||
resampledBuffer.put(input.get(i + 3));
|
outputBuffer.put(buffer.get(i + 3));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case C.ENCODING_PCM_16BIT:
|
case C.ENCODING_PCM_16BIT:
|
||||||
|
|
@ -105,8 +116,18 @@ import java.nio.ByteBuffer;
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
resampledBuffer.position(0);
|
outputBuffer.flip();
|
||||||
return resampledBuffer;
|
return outputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release() {
|
||||||
|
outputBuffer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,12 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
* null if delivery of events is not required.
|
* null if delivery of events is not required.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
|
* @param bufferProcessors Optional {@link BufferProcessor}s which will process PCM audio buffers
|
||||||
|
* before they are output.
|
||||||
*/
|
*/
|
||||||
public SimpleDecoderAudioRenderer(Handler eventHandler,
|
public SimpleDecoderAudioRenderer(Handler eventHandler,
|
||||||
AudioRendererEventListener eventListener) {
|
AudioRendererEventListener eventListener, BufferProcessor... bufferProcessors) {
|
||||||
this(eventHandler, eventListener, null);
|
this(eventHandler, eventListener, null, null, false, bufferProcessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -133,13 +135,16 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
||||||
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
|
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
|
||||||
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
||||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
||||||
|
* @param bufferProcessors Optional {@link BufferProcessor}s which will process PCM audio
|
||||||
|
* buffers before they are output.
|
||||||
*/
|
*/
|
||||||
public SimpleDecoderAudioRenderer(Handler eventHandler,
|
public SimpleDecoderAudioRenderer(Handler eventHandler,
|
||||||
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities,
|
AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities,
|
||||||
DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys) {
|
DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||||
|
BufferProcessor... bufferProcessors) {
|
||||||
super(C.TRACK_TYPE_AUDIO);
|
super(C.TRACK_TYPE_AUDIO);
|
||||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
audioTrack = new AudioTrack(audioCapabilities, new AudioTrackListener());
|
audioTrack = new AudioTrack(audioCapabilities, bufferProcessors, new AudioTrackListener());
|
||||||
this.drmSessionManager = drmSessionManager;
|
this.drmSessionManager = drmSessionManager;
|
||||||
formatHolder = new FormatHolder();
|
formatHolder = new FormatHolder();
|
||||||
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
|
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
|
||||||
|
|
@ -193,8 +198,8 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
||||||
while (drainOutputBuffer()) {}
|
while (drainOutputBuffer()) {}
|
||||||
while (feedInputBuffer()) {}
|
while (feedInputBuffer()) {}
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
} catch (AudioTrack.InitializationException | AudioTrack.WriteException
|
} catch (AudioDecoderException | AudioTrack.ConfigurationException
|
||||||
| AudioDecoderException e) {
|
| AudioTrack.InitializationException | AudioTrack.WriteException e) {
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
decoderCounters.ensureUpdated();
|
decoderCounters.ensureUpdated();
|
||||||
|
|
@ -255,7 +260,8 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean drainOutputBuffer() throws ExoPlaybackException, AudioDecoderException,
|
private boolean drainOutputBuffer() throws ExoPlaybackException, AudioDecoderException,
|
||||||
AudioTrack.InitializationException, AudioTrack.WriteException {
|
AudioTrack.ConfigurationException, AudioTrack.InitializationException,
|
||||||
|
AudioTrack.WriteException {
|
||||||
if (outputBuffer == null) {
|
if (outputBuffer == null) {
|
||||||
outputBuffer = decoder.dequeueOutputBuffer();
|
outputBuffer = decoder.dequeueOutputBuffer();
|
||||||
if (outputBuffer == null) {
|
if (outputBuffer == null) {
|
||||||
|
|
|
||||||
|
|
@ -779,8 +779,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
*
|
*
|
||||||
* @param codec The {@link MediaCodec} instance.
|
* @param codec The {@link MediaCodec} instance.
|
||||||
* @param outputFormat The new output format.
|
* @param outputFormat The new output format.
|
||||||
|
* @throws ExoPlaybackException Thrown if an error occurs handling the new output format.
|
||||||
*/
|
*/
|
||||||
protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat) {
|
protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat)
|
||||||
|
throws ExoPlaybackException {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -918,7 +920,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
/**
|
/**
|
||||||
* Processes a new output format.
|
* Processes a new output format.
|
||||||
*/
|
*/
|
||||||
private void processOutputFormat() {
|
private void processOutputFormat() throws ExoPlaybackException {
|
||||||
MediaFormat format = codec.getOutputFormat();
|
MediaFormat format = codec.getOutputFormat();
|
||||||
if (codecNeedsAdaptationWorkaround
|
if (codecNeedsAdaptationWorkaround
|
||||||
&& format.getInteger(MediaFormat.KEY_WIDTH) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT
|
&& format.getInteger(MediaFormat.KEY_WIDTH) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue