mirror of
https://github.com/androidx/media.git
synced 2025-05-11 17:49:52 +08:00
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:
parent
c385ece69d
commit
71ffc7668c
@ -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}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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}.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user