Support specifying AudioAttributes for AudioTrack

Add a compatibility AudioAttributes class so that the app can specify
audio attributes in the same way before and after API 21.

Deprecate SimpleExoPlayer.setStreamType. Add
SimpleExoPlayer.setAudioAttributes and MSG_SET_AUDIO_ATTRIBUTES.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=160408574
This commit is contained in:
andrewlewis 2017-06-28 08:02:07 -07:00 committed by Oliver Woodman
parent c385ece69d
commit 71ffc7668c
7 changed files with 529 additions and 105 deletions

View File

@ -189,13 +189,17 @@ public final class C {
* Stream types for an {@link android.media.AudioTrack}.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({STREAM_TYPE_ALARM, STREAM_TYPE_MUSIC, STREAM_TYPE_NOTIFICATION, STREAM_TYPE_RING,
STREAM_TYPE_SYSTEM, STREAM_TYPE_VOICE_CALL})
@IntDef({STREAM_TYPE_ALARM, STREAM_TYPE_DTMF, STREAM_TYPE_MUSIC, STREAM_TYPE_NOTIFICATION,
STREAM_TYPE_RING, STREAM_TYPE_SYSTEM, STREAM_TYPE_VOICE_CALL, STREAM_TYPE_USE_DEFAULT})
public @interface StreamType {}
/**
* @see AudioManager#STREAM_ALARM
*/
public static final int STREAM_TYPE_ALARM = AudioManager.STREAM_ALARM;
/**
* @see AudioManager#STREAM_DTMF
*/
public static final int STREAM_TYPE_DTMF = AudioManager.STREAM_DTMF;
/**
* @see AudioManager#STREAM_MUSIC
*/
@ -216,11 +220,164 @@ public final class C {
* @see AudioManager#STREAM_VOICE_CALL
*/
public static final int STREAM_TYPE_VOICE_CALL = AudioManager.STREAM_VOICE_CALL;
/**
* @see AudioManager#USE_DEFAULT_STREAM_TYPE
*/
public static final int STREAM_TYPE_USE_DEFAULT = AudioManager.USE_DEFAULT_STREAM_TYPE;
/**
* The default stream type used by audio renderers.
*/
public static final int STREAM_TYPE_DEFAULT = STREAM_TYPE_MUSIC;
/**
* Content types for {@link com.google.android.exoplayer2.audio.AudioAttributes}.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({CONTENT_TYPE_MOVIE, CONTENT_TYPE_MUSIC, CONTENT_TYPE_SONIFICATION, CONTENT_TYPE_SPEECH,
CONTENT_TYPE_UNKNOWN})
public @interface AudioContentType {}
/**
* @see android.media.AudioAttributes#CONTENT_TYPE_MOVIE
*/
@SuppressWarnings("InlinedApi")
public static final int CONTENT_TYPE_MOVIE = android.media.AudioAttributes.CONTENT_TYPE_MOVIE;
/**
* @see android.media.AudioAttributes#CONTENT_TYPE_MUSIC
*/
@SuppressWarnings("InlinedApi")
public static final int CONTENT_TYPE_MUSIC = android.media.AudioAttributes.CONTENT_TYPE_MUSIC;
/**
* @see android.media.AudioAttributes#CONTENT_TYPE_SONIFICATION
*/
@SuppressWarnings("InlinedApi")
public static final int CONTENT_TYPE_SONIFICATION =
android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION;
/**
* @see android.media.AudioAttributes#CONTENT_TYPE_SPEECH
*/
@SuppressWarnings("InlinedApi")
public static final int CONTENT_TYPE_SPEECH =
android.media.AudioAttributes.CONTENT_TYPE_SPEECH;
/**
* @see android.media.AudioAttributes#CONTENT_TYPE_UNKNOWN
*/
@SuppressWarnings("InlinedApi")
public static final int CONTENT_TYPE_UNKNOWN =
android.media.AudioAttributes.CONTENT_TYPE_UNKNOWN;
/**
* Flags for {@link com.google.android.exoplayer2.audio.AudioAttributes}.
* <p>
* Note that {@code FLAG_HW_AV_SYNC} is not available because the player takes care of setting the
* flag when tunneling is enabled via a track selector.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {FLAG_AUDIBILITY_ENFORCED})
public @interface AudioFlags {}
/**
* @see android.media.AudioAttributes#FLAG_AUDIBILITY_ENFORCED
*/
@SuppressWarnings("InlinedApi")
public static final int FLAG_AUDIBILITY_ENFORCED =
android.media.AudioAttributes.FLAG_AUDIBILITY_ENFORCED;
/**
* Usage types for {@link com.google.android.exoplayer2.audio.AudioAttributes}.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({USAGE_ALARM, USAGE_ASSISTANCE_ACCESSIBILITY, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
USAGE_ASSISTANCE_SONIFICATION, USAGE_GAME, USAGE_MEDIA, USAGE_NOTIFICATION,
USAGE_NOTIFICATION_COMMUNICATION_DELAYED, USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
USAGE_NOTIFICATION_COMMUNICATION_REQUEST, USAGE_NOTIFICATION_EVENT,
USAGE_NOTIFICATION_RINGTONE, USAGE_UNKNOWN, USAGE_VOICE_COMMUNICATION,
USAGE_VOICE_COMMUNICATION_SIGNALLING})
public @interface AudioUsage {}
/**
* @see android.media.AudioAttributes#USAGE_ALARM
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_ALARM = android.media.AudioAttributes.USAGE_ALARM;
/**
* @see android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_ASSISTANCE_ACCESSIBILITY =
android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
/**
* @see android.media.AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE =
android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
/**
* @see android.media.AudioAttributes#USAGE_ASSISTANCE_SONIFICATION
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_ASSISTANCE_SONIFICATION =
android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
/**
* @see android.media.AudioAttributes#USAGE_GAME
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_GAME = android.media.AudioAttributes.USAGE_GAME;
/**
* @see android.media.AudioAttributes#USAGE_MEDIA
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_MEDIA = android.media.AudioAttributes.USAGE_MEDIA;
/**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_NOTIFICATION = android.media.AudioAttributes.USAGE_NOTIFICATION;
/**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED =
android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED;
/**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT =
android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT;
/**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST =
android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST;
/**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_EVENT
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_NOTIFICATION_EVENT =
android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT;
/**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_RINGTONE
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_NOTIFICATION_RINGTONE =
android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
/**
* @see android.media.AudioAttributes#USAGE_UNKNOWN
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_UNKNOWN = android.media.AudioAttributes.USAGE_UNKNOWN;
/**
* @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_VOICE_COMMUNICATION =
android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION;
/**
* @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING
*/
@SuppressWarnings("InlinedApi")
public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING =
android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING;
/**
* Flags which can apply to a buffer containing a media sample.
*/
@ -498,16 +655,25 @@ public final class C {
/**
* A type of a message that can be passed to an audio {@link Renderer} via
* {@link ExoPlayer#sendMessages} or {@link ExoPlayer#blockingSendMessages}. The message object
* should be one of the integer stream types in {@link C.StreamType}, and will specify the stream
* type of the underlying {@link android.media.AudioTrack}. See also
* {@link android.media.AudioTrack#AudioTrack(int, int, int, int, int, int)}. If the stream type
* is not set, audio renderers use {@link #STREAM_TYPE_DEFAULT}.
* should be an {@link com.google.android.exoplayer2.audio.AudioAttributes} instance that will
* configure the underlying audio track. If not set, the default audio attributes will be used.
* They are suitable for general media playback.
* <p>
* Note that when the stream type changes, the AudioTrack must be reinitialized, which can
* introduce a brief gap in audio output. Note also that tracks in the same audio session must
* share the same routing, so a new audio session id will be generated.
* Setting the audio attributes during playback may introduce a short gap in audio output as the
* audio track is recreated. A new audio session id will also be generated.
* <p>
* If tunneling is enabled by the track selector, the specified audio attributes will be ignored,
* but they will take effect if audio is later played without tunneling.
* <p>
* If the device is running a build before platform API version 21, audio attributes cannot be set
* directly on the underlying audio track. In this case, the usage will be mapped onto an
* equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}.
* <p>
* To get audio attributes that are equivalent to a legacy stream type, pass the stream type to
* {@link Util#getAudioUsageForStreamType(int)} and use the returned {@link C.AudioUsage} to build
* an audio attributes instance.
*/
public static final int MSG_SET_STREAM_TYPE = 3;
public static final int MSG_SET_AUDIO_ATTRIBUTES = 3;
/**
* The type of a message that can be passed to a {@link MediaCodec}-based video {@link Renderer}

View File

@ -27,6 +27,7 @@ import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.metadata.Metadata;
@ -37,6 +38,7 @@ import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.util.List;
@ -105,8 +107,7 @@ public class SimpleExoPlayer implements ExoPlayer {
private DecoderCounters videoDecoderCounters;
private DecoderCounters audioDecoderCounters;
private int audioSessionId;
@C.StreamType
private int audioStreamType;
private AudioAttributes audioAttributes;
private float audioVolume;
protected SimpleExoPlayer(RenderersFactory renderersFactory, TrackSelector trackSelector,
@ -136,7 +137,7 @@ public class SimpleExoPlayer implements ExoPlayer {
// Set initial values.
audioVolume = 1;
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
audioStreamType = C.STREAM_TYPE_DEFAULT;
audioAttributes = AudioAttributes.DEFAULT;
videoScalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
// Build the player and associated objects.
@ -293,33 +294,70 @@ public class SimpleExoPlayer implements ExoPlayer {
}
/**
* Sets the stream type for audio playback (see {@link C.StreamType} and
* {@link android.media.AudioTrack#AudioTrack(int, int, int, int, int, int)}). If the stream type
* is not set, audio renderers use {@link C#STREAM_TYPE_DEFAULT}.
* Sets the stream type for audio playback, used by the underlying audio track.
* <p>
* Note that when the stream type changes, the AudioTrack must be reinitialized, which can
* introduce a brief gap in audio output. Note also that tracks in the same audio session must
* share the same routing, so a new audio session id will be generated.
* Setting the stream type during playback may introduce a short gap in audio output as the audio
* track is recreated. A new audio session id will also be generated.
* <p>
* Calling this method overwrites any attributes set previously by calling
* {@link #setAudioAttributes(AudioAttributes)}.
*
* @param audioStreamType The stream type for audio playback.
* @deprecated Use {@link #setAudioAttributes(AudioAttributes)}.
* @param streamType The stream type for audio playback.
*/
public void setAudioStreamType(@C.StreamType int audioStreamType) {
this.audioStreamType = audioStreamType;
@Deprecated
public void setAudioStreamType(@C.StreamType int streamType) {
@C.AudioUsage int usage = Util.getAudioUsageForStreamType(streamType);
@C.AudioContentType int contentType = Util.getAudioContentTypeForStreamType(streamType);
AudioAttributes audioAttributes =
new AudioAttributes.Builder().setUsage(usage).setContentType(contentType).build();
setAudioAttributes(audioAttributes);
}
/**
* Returns the stream type for audio playback.
*
* @deprecated Use {@link #getAudioAttributes()}.
*/
@Deprecated
public @C.StreamType int getAudioStreamType() {
return Util.getStreamTypeForAudioUsage(audioAttributes.usage);
}
/**
* Sets the attributes for audio playback, used by the underlying audio track. If not set, the
* default audio attributes will be used. They are suitable for general media playback.
* <p>
* Setting the audio attributes during playback may introduce a short gap in audio output as the
* audio track is recreated. A new audio session id will also be generated.
* <p>
* If tunneling is enabled by the track selector, the specified audio attributes will be ignored,
* but they will take effect if audio is later played without tunneling.
* <p>
* If the device is running a build before platform API version 21, audio attributes cannot be set
* directly on the underlying audio track. In this case, the usage will be mapped onto an
* equivalent stream type using {@link Util#getStreamTypeForAudioUsage(int)}.
*
* @param audioAttributes The attributes to use for audio playback.
*/
public void setAudioAttributes(AudioAttributes audioAttributes) {
this.audioAttributes = audioAttributes;
ExoPlayerMessage[] messages = new ExoPlayerMessage[audioRendererCount];
int count = 0;
for (Renderer renderer : renderers) {
if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) {
messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_STREAM_TYPE, audioStreamType);
messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_AUDIO_ATTRIBUTES,
audioAttributes);
}
}
player.sendMessages(messages);
}
/**
* Returns the stream type for audio playback.
* Returns the attributes for audio playback.
*/
public @C.StreamType int getAudioStreamType() {
return audioStreamType;
public AudioAttributes getAudioAttributes() {
return audioAttributes;
}
/**

View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.audio;
import android.annotation.TargetApi;
import com.google.android.exoplayer2.C;
/**
* Attributes for audio playback, which configure the underlying platform
* {@link android.media.AudioTrack}.
* <p>
* To set the audio attributes, create an instance using the {@link Builder} and either pass it to
* {@link com.google.android.exoplayer2.SimpleExoPlayer#setAudioAttributes(AudioAttributes)} or
* send a message of type {@link C#MSG_SET_AUDIO_ATTRIBUTES} to the audio renderers.
* <p>
* This class is based on {@link android.media.AudioAttributes}, but can be used on all supported
* API versions.
*/
public final class AudioAttributes {
public static final AudioAttributes DEFAULT = new Builder().build();
/**
* Builder for {@link AudioAttributes}.
*/
public static final class Builder {
@C.AudioContentType
private int contentType;
@C.AudioFlags
private int flags;
@C.AudioUsage
private int usage;
/**
* Creates a new builder for {@link AudioAttributes}.
* <p>
* By default the content type is {@link C#CONTENT_TYPE_UNKNOWN}, usage is
* {@link C#USAGE_MEDIA}, and no flags are set.
*/
public Builder() {
contentType = C.CONTENT_TYPE_UNKNOWN;
flags = 0;
usage = C.USAGE_MEDIA;
}
/**
* @see android.media.AudioAttributes.Builder#setContentType(int)
*/
public Builder setContentType(@C.AudioContentType int contentType) {
this.contentType = contentType;
return this;
}
/**
* @see android.media.AudioAttributes.Builder#setFlags(int)
*/
public Builder setFlags(@C.AudioFlags int flags) {
this.flags = flags;
return this;
}
/**
* @see android.media.AudioAttributes.Builder#setUsage(int)
*/
public Builder setUsage(@C.AudioUsage int usage) {
this.usage = usage;
return this;
}
/**
* Creates an {@link AudioAttributes} instance from this builder.
*/
public AudioAttributes build() {
return new AudioAttributes(contentType, flags, usage);
}
}
@C.AudioContentType
public final int contentType;
@C.AudioFlags
public final int flags;
@C.AudioUsage
public final int usage;
private android.media.AudioAttributes audioAttributesV21;
private AudioAttributes(@C.AudioContentType int contentType, @C.AudioFlags int flags,
@C.AudioUsage int usage) {
this.contentType = contentType;
this.flags = flags;
this.usage = usage;
}
@TargetApi(21)
/* package */ android.media.AudioAttributes getAudioAttributesV21() {
if (audioAttributesV21 == null) {
audioAttributesV21 = new android.media.AudioAttributes.Builder()
.setContentType(contentType)
.setFlags(flags)
.setUsage(usage)
.build();
}
return audioAttributesV21;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
AudioAttributes other = (AudioAttributes) obj;
return this.contentType == other.contentType && this.flags == other.flags
&& this.usage == other.usage;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + contentType;
result = 31 * result + flags;
result = 31 * result + usage;
return result;
}
}

View File

@ -17,8 +17,8 @@ package com.google.android.exoplayer2.audio;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTimestamp;
import android.os.ConditionVariable;
import android.os.SystemClock;
@ -40,9 +40,9 @@ import java.util.LinkedList;
* <p>
* Before starting playback, specify the input format by calling
* {@link #configure(String, int, int, int, int)}. Optionally call {@link #setAudioSessionId(int)},
* {@link #setStreamType(int)}, {@link #enableTunnelingV21(int)} and {@link #disableTunneling()}
* to configure audio playback. These methods may be called after writing data to the track, in
* which case it will be reinitialized as required.
* {@link #setAudioAttributes(AudioAttributes)}, {@link #enableTunnelingV21(int)} and
* {@link #disableTunneling()} to configure audio playback. These methods may be called after
* writing data to the track, in which case it will be reinitialized as required.
* <p>
* Call {@link #handleBuffer(ByteBuffer, long)} to write data, and {@link #handleDiscontinuity()}
* when the data being fed is discontinuous. Call {@link #play()} to start playing the written data.
@ -299,8 +299,7 @@ public final class AudioTrack {
private int encoding;
@C.Encoding
private int outputEncoding;
@C.StreamType
private int streamType;
private AudioAttributes audioAttributes;
private boolean passthrough;
private int bufferSize;
private long bufferSizeUs;
@ -384,7 +383,7 @@ public final class AudioTrack {
playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT];
volume = 1.0f;
startMediaTimeState = START_NOT_SET;
streamType = C.STREAM_TYPE_DEFAULT;
audioAttributes = AudioAttributes.DEFAULT;
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
playbackParameters = PlaybackParameters.DEFAULT;
drainingAudioProcessorIndex = C.INDEX_UNSET;
@ -634,19 +633,7 @@ public final class AudioTrack {
// initialization of the audio track to fail.
releasingConditionVariable.block();
if (tunneling) {
audioTrack = createHwAvSyncAudioTrackV21(sampleRate, channelConfig, outputEncoding,
bufferSize, audioSessionId);
} else if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
audioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig,
outputEncoding, bufferSize, MODE_STREAM);
} else {
// Re-attach to the same audio session.
audioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig,
outputEncoding, bufferSize, MODE_STREAM, audioSessionId);
}
checkAudioTrackInitialized();
audioTrack = initializeAudioTrack();
int audioSessionId = audioTrack.getAudioSessionId();
if (enablePreV21AudioSessionWorkaround) {
if (Util.SDK_INT < 21) {
@ -657,12 +644,7 @@ public final class AudioTrack {
releaseKeepSessionIdAudioTrack();
}
if (keepSessionIdAudioTrack == null) {
int sampleRate = 4000; // Equal to private android.media.AudioTrack.MIN_SAMPLE_RATE.
int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
@C.PcmEncoding 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, MODE_STATIC, audioSessionId);
keepSessionIdAudioTrack = initializeKeepSessionIdAudioTrack(audioSessionId);
}
}
}
@ -1021,23 +1003,23 @@ public final class AudioTrack {
}
/**
* Sets the stream type for audio track. If the stream type has changed and if the audio track
* Sets the attributes for audio playback. If the attributes have changed and if the audio track
* is not configured for use with tunneling, then the audio track is reset and the audio session
* id is cleared.
* <p>
* If the audio track is configured for use with tunneling then the stream type is ignored, the
* audio track is not reset and the audio session id is not cleared. The passed stream type will
* be used if the audio track is later re-configured into non-tunneled mode.
* If the audio track is configured for use with tunneling then the audio attributes are ignored.
* The audio track is not reset and the audio session id is not cleared. The passed attributes
* will be used if the audio track is later re-configured into non-tunneled mode.
*
* @param streamType The {@link C.StreamType} to use for audio output.
* @param audioAttributes The attributes for audio playback.
*/
public void setStreamType(@C.StreamType int streamType) {
if (this.streamType == streamType) {
public void setAudioAttributes(AudioAttributes audioAttributes) {
if (this.audioAttributes.equals(audioAttributes)) {
return;
}
this.streamType = streamType;
this.audioAttributes = audioAttributes;
if (tunneling) {
// The stream type is ignored in tunneling mode, so no need to reset.
// The audio attributes are ignored in tunneling mode, so no need to reset.
return;
}
reset();
@ -1333,31 +1315,6 @@ public final class AudioTrack {
}
}
/**
* Checks that {@link #audioTrack} has been successfully initialized. If it has then calling this
* method is a no-op. If it hasn't then {@link #audioTrack} is released and set to null, and an
* exception is thrown.
*
* @throws InitializationException If {@link #audioTrack} has not been successfully initialized.
*/
private void checkAudioTrackInitialized() throws InitializationException {
int state = audioTrack.getState();
if (state == STATE_INITIALIZED) {
return;
}
// The track is not successfully initialized. Release and null the track.
try {
audioTrack.release();
} catch (Exception e) {
// The track has already failed to initialize, so it wouldn't be that surprising if release
// were to fail too. Swallow the exception.
} finally {
audioTrack = null;
}
throw new InitializationException(state, sampleRate, channelConfig, bufferSize);
}
private boolean isInitialized() {
return audioTrack != null;
}
@ -1408,24 +1365,65 @@ public final class AudioTrack {
&& audioTrack.getPlaybackHeadPosition() == 0;
}
/**
* Instantiates an {@link android.media.AudioTrack} to be used with tunneling video playback.
*/
private android.media.AudioTrack initializeAudioTrack() throws InitializationException {
android.media.AudioTrack audioTrack;
if (Util.SDK_INT >= 21) {
audioTrack = createAudioTrackV21();
} else {
int streamType = Util.getStreamTypeForAudioUsage(audioAttributes.usage);
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
audioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig,
outputEncoding, bufferSize, MODE_STREAM);
} else {
// Re-attach to the same audio session.
audioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig,
outputEncoding, bufferSize, MODE_STREAM, audioSessionId);
}
}
int state = audioTrack.getState();
if (state != STATE_INITIALIZED) {
try {
audioTrack.release();
} catch (Exception e) {
// The track has already failed to initialize, so it wouldn't be that surprising if release
// were to fail too. Swallow the exception.
}
throw new InitializationException(state, sampleRate, channelConfig, bufferSize);
}
return audioTrack;
}
@TargetApi(21)
private static android.media.AudioTrack createHwAvSyncAudioTrackV21(int sampleRate,
int channelConfig, int encoding, int bufferSize, int sessionId) {
AudioAttributes attributesBuilder = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
.setFlags(AudioAttributes.FLAG_HW_AV_SYNC)
.build();
private android.media.AudioTrack createAudioTrackV21() {
android.media.AudioAttributes attributes;
if (tunneling) {
attributes = new android.media.AudioAttributes.Builder()
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE)
.setFlags(android.media.AudioAttributes.FLAG_HW_AV_SYNC)
.setUsage(android.media.AudioAttributes.USAGE_MEDIA)
.build();
} else {
attributes = audioAttributes.getAudioAttributesV21();
}
AudioFormat format = new AudioFormat.Builder()
.setChannelMask(channelConfig)
.setEncoding(encoding)
.setEncoding(outputEncoding)
.setSampleRate(sampleRate)
.build();
return new android.media.AudioTrack(attributesBuilder, format, bufferSize, MODE_STREAM,
sessionId);
int audioSessionId = this.audioSessionId != C.AUDIO_SESSION_ID_UNSET ? this.audioSessionId
: AudioManager.AUDIO_SESSION_ID_GENERATE;
return new android.media.AudioTrack(attributes, format, bufferSize, MODE_STREAM,
audioSessionId);
}
private android.media.AudioTrack initializeKeepSessionIdAudioTrack(int audioSessionId) {
int sampleRate = 4000; // Equal to private android.media.AudioTrack.MIN_SAMPLE_RATE.
int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
@C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT;
int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback.
return new android.media.AudioTrack(C.STREAM_TYPE_DEFAULT, sampleRate, channelConfig, encoding,
bufferSize, MODE_STATIC, audioSessionId);
}
@C.Encoding

View File

@ -399,9 +399,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
case C.MSG_SET_VOLUME:
audioTrack.setVolume((Float) message);
break;
case C.MSG_SET_STREAM_TYPE:
@C.StreamType int streamType = (Integer) message;
audioTrack.setStreamType(streamType);
case C.MSG_SET_AUDIO_ATTRIBUTES:
AudioAttributes audioAttributes = (AudioAttributes) message;
audioTrack.setAudioAttributes(audioAttributes);
break;
default:
super.handleMessage(messageType, message);

View File

@ -595,9 +595,9 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
case C.MSG_SET_VOLUME:
audioTrack.setVolume((Float) message);
break;
case C.MSG_SET_STREAM_TYPE:
@C.StreamType int streamType = (Integer) message;
audioTrack.setStreamType(streamType);
case C.MSG_SET_AUDIO_ATTRIBUTES:
AudioAttributes audioAttributes = (AudioAttributes) message;
audioTrack.setAudioAttributes(audioAttributes);
break;
default:
super.handleMessage(messageType, message);

View File

@ -796,6 +796,85 @@ public final class Util {
}
}
/**
* Returns the {@link C.AudioUsage} corresponding to the specified {@link C.StreamType}.
*/
@C.AudioUsage
public static int getAudioUsageForStreamType(@C.StreamType int streamType) {
switch (streamType) {
case C.STREAM_TYPE_ALARM:
return C.USAGE_ALARM;
case C.STREAM_TYPE_DTMF:
return C.USAGE_VOICE_COMMUNICATION_SIGNALLING;
case C.STREAM_TYPE_NOTIFICATION:
return C.USAGE_NOTIFICATION;
case C.STREAM_TYPE_RING:
return C.USAGE_NOTIFICATION_RINGTONE;
case C.STREAM_TYPE_SYSTEM:
return C.USAGE_ASSISTANCE_SONIFICATION;
case C.STREAM_TYPE_VOICE_CALL:
return C.USAGE_VOICE_COMMUNICATION;
case C.STREAM_TYPE_USE_DEFAULT:
case C.STREAM_TYPE_MUSIC:
default:
return C.USAGE_MEDIA;
}
}
/**
* Returns the {@link C.AudioContentType} corresponding to the specified {@link C.StreamType}.
*/
@C.AudioContentType
public static int getAudioContentTypeForStreamType(@C.StreamType int streamType) {
switch (streamType) {
case C.STREAM_TYPE_ALARM:
case C.STREAM_TYPE_DTMF:
case C.STREAM_TYPE_NOTIFICATION:
case C.STREAM_TYPE_RING:
case C.STREAM_TYPE_SYSTEM:
return C.CONTENT_TYPE_SONIFICATION;
case C.STREAM_TYPE_VOICE_CALL:
return C.CONTENT_TYPE_SPEECH;
case C.STREAM_TYPE_USE_DEFAULT:
case C.STREAM_TYPE_MUSIC:
default:
return C.CONTENT_TYPE_MUSIC;
}
}
/**
* Returns the {@link C.StreamType} corresponding to the specified {@link C.AudioUsage}.
*/
@C.StreamType
public static int getStreamTypeForAudioUsage(@C.AudioUsage int usage) {
switch (usage) {
case C.USAGE_MEDIA:
case C.USAGE_GAME:
case C.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
return C.STREAM_TYPE_MUSIC;
case C.USAGE_ASSISTANCE_SONIFICATION:
return C.STREAM_TYPE_SYSTEM;
case C.USAGE_VOICE_COMMUNICATION:
return C.STREAM_TYPE_VOICE_CALL;
case C.USAGE_VOICE_COMMUNICATION_SIGNALLING:
return C.STREAM_TYPE_DTMF;
case C.USAGE_ALARM:
return C.STREAM_TYPE_ALARM;
case C.USAGE_NOTIFICATION_RINGTONE:
return C.STREAM_TYPE_RING;
case C.USAGE_NOTIFICATION:
case C.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
case C.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
case C.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
case C.USAGE_NOTIFICATION_EVENT:
return C.STREAM_TYPE_NOTIFICATION;
case C.USAGE_ASSISTANCE_ACCESSIBILITY:
case C.USAGE_UNKNOWN:
default:
return C.STREAM_TYPE_DEFAULT;
}
}
/**
* Makes a best guess to infer the type from a {@link Uri}.
*