mirror of
https://github.com/androidx/media.git
synced 2025-05-14 19:19:58 +08:00
Add onAudioTrackInitialized/Released events
This is useful for analytics and understanding player behavior during transitions. #minor-release PiperOrigin-RevId: 570623227 (cherry picked from commit 8e2bf21011c63e2ca2fc58c4353cd66930b621e3)
This commit is contained in:
parent
55633658c6
commit
6d2bf513fb
@ -8,6 +8,9 @@
|
|||||||
* Track Selection:
|
* Track Selection:
|
||||||
* Extractors:
|
* Extractors:
|
||||||
* Audio:
|
* Audio:
|
||||||
|
* Add `onAudioTrackInitialized` and `onAudioTrackReleased` callbacks to
|
||||||
|
`AnalyticsListener`, `AudioRendererEventListener` and
|
||||||
|
`AudioSink.Listener`.
|
||||||
* Video:
|
* Video:
|
||||||
* Text:
|
* Text:
|
||||||
* Metadata:
|
* Metadata:
|
||||||
|
@ -97,6 +97,7 @@ import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector;
|
|||||||
import androidx.media3.exoplayer.analytics.MediaMetricsListener;
|
import androidx.media3.exoplayer.analytics.MediaMetricsListener;
|
||||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||||
|
import androidx.media3.exoplayer.audio.AudioSink;
|
||||||
import androidx.media3.exoplayer.metadata.MetadataOutput;
|
import androidx.media3.exoplayer.metadata.MetadataOutput;
|
||||||
import androidx.media3.exoplayer.source.MaskingMediaSource;
|
import androidx.media3.exoplayer.source.MaskingMediaSource;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
@ -3120,6 +3121,16 @@ import java.util.concurrent.TimeoutException;
|
|||||||
analyticsCollector.onAudioCodecError(audioCodecError);
|
analyticsCollector.onAudioCodecError(audioCodecError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackInitialized(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
analyticsCollector.onAudioTrackInitialized(audioTrackConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackReleased(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
analyticsCollector.onAudioTrackReleased(audioTrackConfig);
|
||||||
|
}
|
||||||
|
|
||||||
// TextOutput implementation
|
// TextOutput implementation
|
||||||
@Override
|
@Override
|
||||||
public void onCues(List<Cue> cues) {
|
public void onCues(List<Cue> cues) {
|
||||||
|
@ -179,6 +179,22 @@ public interface AnalyticsCollector
|
|||||||
*/
|
*/
|
||||||
void onAudioCodecError(Exception audioCodecError);
|
void onAudioCodecError(Exception audioCodecError);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an {@link AudioTrack} has been initialized.
|
||||||
|
*
|
||||||
|
* @param audioTrackConfig The {@link AudioSink.AudioTrackConfig} of the initialized {@link
|
||||||
|
* AudioTrack}.
|
||||||
|
*/
|
||||||
|
void onAudioTrackInitialized(AudioSink.AudioTrackConfig audioTrackConfig);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an {@link AudioTrack} has been released.
|
||||||
|
*
|
||||||
|
* @param audioTrackConfig The {@link AudioSink.AudioTrackConfig} of the released {@link
|
||||||
|
* AudioTrack}.
|
||||||
|
*/
|
||||||
|
void onAudioTrackReleased(AudioSink.AudioTrackConfig audioTrackConfig);
|
||||||
|
|
||||||
// Video events.
|
// Video events.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,6 +22,7 @@ import static java.lang.annotation.ElementType.METHOD;
|
|||||||
import static java.lang.annotation.ElementType.PARAMETER;
|
import static java.lang.annotation.ElementType.PARAMETER;
|
||||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
|
import android.media.AudioTrack;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodec.CodecException;
|
import android.media.MediaCodec.CodecException;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@ -232,6 +233,8 @@ public interface AnalyticsListener {
|
|||||||
EVENT_PLAYER_RELEASED,
|
EVENT_PLAYER_RELEASED,
|
||||||
EVENT_AUDIO_CODEC_ERROR,
|
EVENT_AUDIO_CODEC_ERROR,
|
||||||
EVENT_VIDEO_CODEC_ERROR,
|
EVENT_VIDEO_CODEC_ERROR,
|
||||||
|
EVENT_AUDIO_TRACK_INITIALIZED,
|
||||||
|
EVENT_AUDIO_TRACK_RELEASED
|
||||||
})
|
})
|
||||||
@interface EventFlags {}
|
@interface EventFlags {}
|
||||||
|
|
||||||
@ -435,6 +438,12 @@ public interface AnalyticsListener {
|
|||||||
/** The video codec encountered an error. */
|
/** The video codec encountered an error. */
|
||||||
@UnstableApi int EVENT_VIDEO_CODEC_ERROR = 1030;
|
@UnstableApi int EVENT_VIDEO_CODEC_ERROR = 1030;
|
||||||
|
|
||||||
|
/** An audio track has been initialized. */
|
||||||
|
@UnstableApi int EVENT_AUDIO_TRACK_INITIALIZED = 1031;
|
||||||
|
|
||||||
|
/** An audio track has been released. */
|
||||||
|
@UnstableApi int EVENT_AUDIO_TRACK_RELEASED = 1032;
|
||||||
|
|
||||||
/** Time information of an event. */
|
/** Time information of an event. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
final class EventTime {
|
final class EventTime {
|
||||||
@ -1113,6 +1122,28 @@ public interface AnalyticsListener {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
default void onAudioCodecError(EventTime eventTime, Exception audioCodecError) {}
|
default void onAudioCodecError(EventTime eventTime, Exception audioCodecError) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an {@link AudioTrack} has been initialized.
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param audioTrackConfig The {@link AudioSink.AudioTrackConfig} of the initialized {@link
|
||||||
|
* AudioTrack}.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
default void onAudioTrackInitialized(
|
||||||
|
EventTime eventTime, AudioSink.AudioTrackConfig audioTrackConfig) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an {@link AudioTrack} has been released.
|
||||||
|
*
|
||||||
|
* @param eventTime The event time.
|
||||||
|
* @param audioTrackConfig The {@link AudioSink.AudioTrackConfig} of the released {@link
|
||||||
|
* AudioTrack}.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
default void onAudioTrackReleased(
|
||||||
|
EventTime eventTime, AudioSink.AudioTrackConfig audioTrackConfig) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the volume changes.
|
* Called when the volume changes.
|
||||||
*
|
*
|
||||||
|
@ -52,6 +52,7 @@ import androidx.media3.exoplayer.DecoderCounters;
|
|||||||
import androidx.media3.exoplayer.DecoderReuseEvaluation;
|
import androidx.media3.exoplayer.DecoderReuseEvaluation;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime;
|
import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime;
|
||||||
|
import androidx.media3.exoplayer.audio.AudioSink;
|
||||||
import androidx.media3.exoplayer.drm.DrmSession;
|
import androidx.media3.exoplayer.drm.DrmSession;
|
||||||
import androidx.media3.exoplayer.source.LoadEventInfo;
|
import androidx.media3.exoplayer.source.LoadEventInfo;
|
||||||
import androidx.media3.exoplayer.source.MediaLoadData;
|
import androidx.media3.exoplayer.source.MediaLoadData;
|
||||||
@ -263,6 +264,24 @@ public class DefaultAnalyticsCollector implements AnalyticsCollector {
|
|||||||
listener -> listener.onAudioCodecError(eventTime, audioCodecError));
|
listener -> listener.onAudioCodecError(eventTime, audioCodecError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackInitialized(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_AUDIO_TRACK_INITIALIZED,
|
||||||
|
listener -> listener.onAudioTrackInitialized(eventTime, audioTrackConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackReleased(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
sendEvent(
|
||||||
|
eventTime,
|
||||||
|
AnalyticsListener.EVENT_AUDIO_TRACK_RELEASED,
|
||||||
|
listener -> listener.onAudioTrackReleased(eventTime, audioTrackConfig));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void onVolumeChanged(float volume) {
|
public final void onVolumeChanged(float volume) {
|
||||||
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
EventTime eventTime = generateReadingMediaPeriodEventTime();
|
||||||
|
@ -150,6 +150,22 @@ public interface AudioRendererEventListener {
|
|||||||
*/
|
*/
|
||||||
default void onAudioSinkError(Exception audioSinkError) {}
|
default void onAudioSinkError(Exception audioSinkError) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an {@link AudioTrack} has been initialized.
|
||||||
|
*
|
||||||
|
* @param audioTrackConfig The {@link AudioSink.AudioTrackConfig} of the initialized {@link
|
||||||
|
* AudioTrack}.
|
||||||
|
*/
|
||||||
|
default void onAudioTrackInitialized(AudioSink.AudioTrackConfig audioTrackConfig) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an {@link AudioTrack} has been released.
|
||||||
|
*
|
||||||
|
* @param audioTrackConfig The {@link AudioSink.AudioTrackConfig} of the released {@link
|
||||||
|
* AudioTrack}.
|
||||||
|
*/
|
||||||
|
default void onAudioTrackReleased(AudioSink.AudioTrackConfig audioTrackConfig) {}
|
||||||
|
|
||||||
/** Dispatches events to an {@link AudioRendererEventListener}. */
|
/** Dispatches events to an {@link AudioRendererEventListener}. */
|
||||||
final class EventDispatcher {
|
final class EventDispatcher {
|
||||||
|
|
||||||
@ -256,5 +272,19 @@ public interface AudioRendererEventListener {
|
|||||||
handler.post(() -> castNonNull(listener).onAudioCodecError(audioCodecError));
|
handler.post(() -> castNonNull(listener).onAudioCodecError(audioCodecError));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Invokes {@link AudioRendererEventListener#onAudioTrackInitialized}. */
|
||||||
|
public void audioTrackInitialized(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
if (handler != null) {
|
||||||
|
handler.post(() -> castNonNull(listener).onAudioTrackInitialized(audioTrackConfig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invokes {@link AudioRendererEventListener#onAudioTrackReleased}. */
|
||||||
|
public void audioTrackReleased(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
if (handler != null) {
|
||||||
|
handler.post(() -> castNonNull(listener).onAudioTrackReleased(audioTrackConfig));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,68 @@ public interface AudioSink {
|
|||||||
|
|
||||||
/** Called when audio capabilities changed. */
|
/** Called when audio capabilities changed. */
|
||||||
default void onAudioCapabilitiesChanged() {}
|
default void onAudioCapabilitiesChanged() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an {@link AudioTrack} has been initialized.
|
||||||
|
*
|
||||||
|
* @param audioTrackConfig The {@link AudioTrackConfig} of the initialized {@link AudioTrack}.
|
||||||
|
*/
|
||||||
|
default void onAudioTrackInitialized(AudioTrackConfig audioTrackConfig) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an {@link AudioTrack} has been released.
|
||||||
|
*
|
||||||
|
* @param audioTrackConfig The {@link AudioTrackConfig} of the released {@link AudioTrack}.
|
||||||
|
*/
|
||||||
|
default void onAudioTrackReleased(AudioTrackConfig audioTrackConfig) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Configuration parameters used for an {@link AudioTrack}. */
|
||||||
|
final class AudioTrackConfig {
|
||||||
|
|
||||||
|
/* The {@link C.Encoding} of the audio data. */
|
||||||
|
public final @C.Encoding int encoding;
|
||||||
|
|
||||||
|
/** The sample rate of the audio data. */
|
||||||
|
public final int sampleRate;
|
||||||
|
|
||||||
|
/** The channel configuration of the track. See {@code AudioTrack.CHANNEL_OUT_XXX} constants. */
|
||||||
|
public final int channelConfig;
|
||||||
|
|
||||||
|
/** Whether tunneling is enabled for this track. */
|
||||||
|
public final boolean tunneling;
|
||||||
|
|
||||||
|
/** Whether offload is enabled for this track. */
|
||||||
|
public final boolean offload;
|
||||||
|
|
||||||
|
/** The buffer size of the track in bytes. */
|
||||||
|
public final int bufferSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the audio track configuration parameters.
|
||||||
|
*
|
||||||
|
* @param encoding The {@link C.Encoding} of the audio data
|
||||||
|
* @param sampleRate The sample rate of the audio data.
|
||||||
|
* @param channelConfig The channel configuration of the track. See {@code
|
||||||
|
* AudioTrack.CHANNEL_OUT_XXX} constants.
|
||||||
|
* @param tunneling Whether tunneling is enabled for this track.
|
||||||
|
* @param offload Whether offload is enabled for this track.
|
||||||
|
* @param bufferSize The buffer size of the track in bytes.
|
||||||
|
*/
|
||||||
|
public AudioTrackConfig(
|
||||||
|
@C.Encoding int encoding,
|
||||||
|
int sampleRate,
|
||||||
|
int channelConfig,
|
||||||
|
boolean tunneling,
|
||||||
|
boolean offload,
|
||||||
|
int bufferSize) {
|
||||||
|
this.encoding = encoding;
|
||||||
|
this.sampleRate = sampleRate;
|
||||||
|
this.channelConfig = channelConfig;
|
||||||
|
this.tunneling = tunneling;
|
||||||
|
this.offload = offload;
|
||||||
|
this.bufferSize = bufferSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Thrown when a failure occurs configuring the sink. */
|
/** Thrown when a failure occurs configuring the sink. */
|
||||||
|
@ -856,6 +856,16 @@ public abstract class DecoderAudioRenderer<
|
|||||||
Log.e(TAG, "Audio sink error", audioSinkError);
|
Log.e(TAG, "Audio sink error", audioSinkError);
|
||||||
eventDispatcher.audioSinkError(audioSinkError);
|
eventDispatcher.audioSinkError(audioSinkError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackInitialized(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
eventDispatcher.audioTrackInitialized(audioTrackConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackReleased(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
eventDispatcher.audioTrackReleased(audioTrackConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(23)
|
@RequiresApi(23)
|
||||||
|
@ -767,7 +767,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
bufferSize,
|
bufferSize,
|
||||||
audioProcessingPipeline,
|
audioProcessingPipeline,
|
||||||
enableAudioTrackPlaybackParams,
|
enableAudioTrackPlaybackParams,
|
||||||
enableOffloadGapless);
|
enableOffloadGapless,
|
||||||
|
tunneling);
|
||||||
if (isAudioTrackInitialized()) {
|
if (isAudioTrackInitialized()) {
|
||||||
this.pendingConfiguration = pendingConfiguration;
|
this.pendingConfiguration = pendingConfiguration;
|
||||||
} else {
|
} else {
|
||||||
@ -817,8 +818,12 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
if (preferredDevice != null && Util.SDK_INT >= 23) {
|
if (preferredDevice != null && Util.SDK_INT >= 23) {
|
||||||
Api23.setPreferredDeviceOnAudioTrack(audioTrack, preferredDevice);
|
Api23.setPreferredDeviceOnAudioTrack(audioTrack, preferredDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
startMediaTimeUsNeedsInit = true;
|
startMediaTimeUsNeedsInit = true;
|
||||||
|
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onAudioTrackInitialized(configuration.buildAudioTrackConfig());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1021,8 +1026,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
|
|
||||||
private AudioTrack buildAudioTrack(Configuration configuration) throws InitializationException {
|
private AudioTrack buildAudioTrack(Configuration configuration) throws InitializationException {
|
||||||
try {
|
try {
|
||||||
AudioTrack audioTrack =
|
AudioTrack audioTrack = configuration.buildAudioTrack(audioAttributes, audioSessionId);
|
||||||
configuration.buildAudioTrack(tunneling, audioAttributes, audioSessionId);
|
|
||||||
if (audioOffloadListener != null) {
|
if (audioOffloadListener != null) {
|
||||||
audioOffloadListener.onOffloadedPlayback(isOffloadedPlayback(audioTrack));
|
audioOffloadListener.onOffloadedPlayback(isOffloadedPlayback(audioTrack));
|
||||||
}
|
}
|
||||||
@ -1427,12 +1431,13 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
// we next create an audio track.
|
// we next create an audio track.
|
||||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
||||||
}
|
}
|
||||||
|
AudioTrackConfig oldAudioTrackConfig = configuration.buildAudioTrackConfig();
|
||||||
if (pendingConfiguration != null) {
|
if (pendingConfiguration != null) {
|
||||||
configuration = pendingConfiguration;
|
configuration = pendingConfiguration;
|
||||||
pendingConfiguration = null;
|
pendingConfiguration = null;
|
||||||
}
|
}
|
||||||
audioTrackPositionTracker.reset();
|
audioTrackPositionTracker.reset();
|
||||||
releaseAudioTrackAsync(audioTrack, releasingConditionVariable);
|
releaseAudioTrackAsync(audioTrack, releasingConditionVariable, listener, oldAudioTrackConfig);
|
||||||
audioTrack = null;
|
audioTrack = null;
|
||||||
}
|
}
|
||||||
writeExceptionPendingExceptionHolder.clear();
|
writeExceptionPendingExceptionHolder.clear();
|
||||||
@ -1823,11 +1828,15 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void releaseAudioTrackAsync(
|
private static void releaseAudioTrackAsync(
|
||||||
AudioTrack audioTrack, ConditionVariable releasedConditionVariable) {
|
AudioTrack audioTrack,
|
||||||
|
ConditionVariable releasedConditionVariable,
|
||||||
|
@Nullable Listener listener,
|
||||||
|
AudioTrackConfig audioTrackConfig) {
|
||||||
// AudioTrack.release can take some time, so we call it on a background thread. The background
|
// AudioTrack.release can take some time, so we call it on a background thread. The background
|
||||||
// thread is shared statically to avoid creating many threads when multiple players are released
|
// thread is shared statically to avoid creating many threads when multiple players are released
|
||||||
// at the same time.
|
// at the same time.
|
||||||
releasedConditionVariable.close();
|
releasedConditionVariable.close();
|
||||||
|
Handler audioTrackThreadHandler = new Handler(Looper.myLooper());
|
||||||
synchronized (releaseExecutorLock) {
|
synchronized (releaseExecutorLock) {
|
||||||
if (releaseExecutor == null) {
|
if (releaseExecutor == null) {
|
||||||
releaseExecutor = Util.newSingleThreadExecutor("ExoPlayer:AudioTrackReleaseThread");
|
releaseExecutor = Util.newSingleThreadExecutor("ExoPlayer:AudioTrackReleaseThread");
|
||||||
@ -1839,6 +1848,9 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
audioTrack.flush();
|
audioTrack.flush();
|
||||||
audioTrack.release();
|
audioTrack.release();
|
||||||
} finally {
|
} finally {
|
||||||
|
if (listener != null && audioTrackThreadHandler.getLooper().getThread().isAlive()) {
|
||||||
|
audioTrackThreadHandler.post(() -> listener.onAudioTrackReleased(audioTrackConfig));
|
||||||
|
}
|
||||||
releasedConditionVariable.open();
|
releasedConditionVariable.open();
|
||||||
synchronized (releaseExecutorLock) {
|
synchronized (releaseExecutorLock) {
|
||||||
pendingReleaseCount--;
|
pendingReleaseCount--;
|
||||||
@ -2018,6 +2030,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
public final AudioProcessingPipeline audioProcessingPipeline;
|
public final AudioProcessingPipeline audioProcessingPipeline;
|
||||||
public final boolean enableAudioTrackPlaybackParams;
|
public final boolean enableAudioTrackPlaybackParams;
|
||||||
public final boolean enableOffloadGapless;
|
public final boolean enableOffloadGapless;
|
||||||
|
public final boolean tunneling;
|
||||||
|
|
||||||
public Configuration(
|
public Configuration(
|
||||||
Format inputFormat,
|
Format inputFormat,
|
||||||
@ -2030,7 +2043,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
int bufferSize,
|
int bufferSize,
|
||||||
AudioProcessingPipeline audioProcessingPipeline,
|
AudioProcessingPipeline audioProcessingPipeline,
|
||||||
boolean enableAudioTrackPlaybackParams,
|
boolean enableAudioTrackPlaybackParams,
|
||||||
boolean enableOffloadGapless) {
|
boolean enableOffloadGapless,
|
||||||
|
boolean tunneling) {
|
||||||
this.inputFormat = inputFormat;
|
this.inputFormat = inputFormat;
|
||||||
this.inputPcmFrameSize = inputPcmFrameSize;
|
this.inputPcmFrameSize = inputPcmFrameSize;
|
||||||
this.outputMode = outputMode;
|
this.outputMode = outputMode;
|
||||||
@ -2042,6 +2056,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
this.audioProcessingPipeline = audioProcessingPipeline;
|
this.audioProcessingPipeline = audioProcessingPipeline;
|
||||||
this.enableAudioTrackPlaybackParams = enableAudioTrackPlaybackParams;
|
this.enableAudioTrackPlaybackParams = enableAudioTrackPlaybackParams;
|
||||||
this.enableOffloadGapless = enableOffloadGapless;
|
this.enableOffloadGapless = enableOffloadGapless;
|
||||||
|
this.tunneling = tunneling;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Configuration copyWithBufferSize(int bufferSize) {
|
public Configuration copyWithBufferSize(int bufferSize) {
|
||||||
@ -2056,7 +2071,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
bufferSize,
|
bufferSize,
|
||||||
audioProcessingPipeline,
|
audioProcessingPipeline,
|
||||||
enableAudioTrackPlaybackParams,
|
enableAudioTrackPlaybackParams,
|
||||||
enableOffloadGapless);
|
enableOffloadGapless,
|
||||||
|
tunneling);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns if the configurations are sufficiently compatible to reuse the audio track. */
|
/** Returns if the configurations are sufficiently compatible to reuse the audio track. */
|
||||||
@ -2078,12 +2094,21 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
return Util.sampleCountToDurationUs(frameCount, outputSampleRate);
|
return Util.sampleCountToDurationUs(frameCount, outputSampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioTrack buildAudioTrack(
|
public AudioTrackConfig buildAudioTrackConfig() {
|
||||||
boolean tunneling, AudioAttributes audioAttributes, int audioSessionId)
|
return new AudioTrackConfig(
|
||||||
|
outputEncoding,
|
||||||
|
outputSampleRate,
|
||||||
|
outputChannelConfig,
|
||||||
|
tunneling,
|
||||||
|
outputMode == OUTPUT_MODE_OFFLOAD,
|
||||||
|
bufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AudioTrack buildAudioTrack(AudioAttributes audioAttributes, int audioSessionId)
|
||||||
throws InitializationException {
|
throws InitializationException {
|
||||||
AudioTrack audioTrack;
|
AudioTrack audioTrack;
|
||||||
try {
|
try {
|
||||||
audioTrack = createAudioTrack(tunneling, audioAttributes, audioSessionId);
|
audioTrack = createAudioTrack(audioAttributes, audioSessionId);
|
||||||
} catch (UnsupportedOperationException | IllegalArgumentException e) {
|
} catch (UnsupportedOperationException | IllegalArgumentException e) {
|
||||||
throw new InitializationException(
|
throw new InitializationException(
|
||||||
AudioTrack.STATE_UNINITIALIZED,
|
AudioTrack.STATE_UNINITIALIZED,
|
||||||
@ -2115,20 +2140,18 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
return audioTrack;
|
return audioTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AudioTrack createAudioTrack(
|
private AudioTrack createAudioTrack(AudioAttributes audioAttributes, int audioSessionId) {
|
||||||
boolean tunneling, AudioAttributes audioAttributes, int audioSessionId) {
|
|
||||||
if (Util.SDK_INT >= 29) {
|
if (Util.SDK_INT >= 29) {
|
||||||
return createAudioTrackV29(tunneling, audioAttributes, audioSessionId);
|
return createAudioTrackV29(audioAttributes, audioSessionId);
|
||||||
} else if (Util.SDK_INT >= 21) {
|
} else if (Util.SDK_INT >= 21) {
|
||||||
return createAudioTrackV21(tunneling, audioAttributes, audioSessionId);
|
return createAudioTrackV21(audioAttributes, audioSessionId);
|
||||||
} else {
|
} else {
|
||||||
return createAudioTrackV9(audioAttributes, audioSessionId);
|
return createAudioTrackV9(audioAttributes, audioSessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(29)
|
@RequiresApi(29)
|
||||||
private AudioTrack createAudioTrackV29(
|
private AudioTrack createAudioTrackV29(AudioAttributes audioAttributes, int audioSessionId) {
|
||||||
boolean tunneling, AudioAttributes audioAttributes, int audioSessionId) {
|
|
||||||
AudioFormat audioFormat =
|
AudioFormat audioFormat =
|
||||||
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding);
|
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding);
|
||||||
android.media.AudioAttributes audioTrackAttributes =
|
android.media.AudioAttributes audioTrackAttributes =
|
||||||
@ -2144,8 +2167,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
@RequiresApi(21)
|
||||||
private AudioTrack createAudioTrackV21(
|
private AudioTrack createAudioTrackV21(AudioAttributes audioAttributes, int audioSessionId) {
|
||||||
boolean tunneling, AudioAttributes audioAttributes, int audioSessionId) {
|
|
||||||
return new AudioTrack(
|
return new AudioTrack(
|
||||||
getAudioTrackAttributesV21(audioAttributes, tunneling),
|
getAudioTrackAttributesV21(audioAttributes, tunneling),
|
||||||
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding),
|
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding),
|
||||||
|
@ -1022,6 +1022,16 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
public void onAudioCapabilitiesChanged() {
|
public void onAudioCapabilitiesChanged() {
|
||||||
MediaCodecAudioRenderer.this.onRendererCapabilitiesChanged();
|
MediaCodecAudioRenderer.this.onRendererCapabilitiesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackInitialized(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
eventDispatcher.audioTrackInitialized(audioTrackConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackReleased(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
eventDispatcher.audioTrackReleased(audioTrackConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(23)
|
@RequiresApi(23)
|
||||||
|
@ -38,6 +38,7 @@ import androidx.media3.common.util.UnstableApi;
|
|||||||
import androidx.media3.exoplayer.DecoderCounters;
|
import androidx.media3.exoplayer.DecoderCounters;
|
||||||
import androidx.media3.exoplayer.DecoderReuseEvaluation;
|
import androidx.media3.exoplayer.DecoderReuseEvaluation;
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
||||||
|
import androidx.media3.exoplayer.audio.AudioSink;
|
||||||
import androidx.media3.exoplayer.drm.DrmSession;
|
import androidx.media3.exoplayer.drm.DrmSession;
|
||||||
import androidx.media3.exoplayer.source.LoadEventInfo;
|
import androidx.media3.exoplayer.source.LoadEventInfo;
|
||||||
import androidx.media3.exoplayer.source.MediaLoadData;
|
import androidx.media3.exoplayer.source.MediaLoadData;
|
||||||
@ -401,6 +402,20 @@ public class EventLogger implements AnalyticsListener {
|
|||||||
logd(eventTime, "volume", Float.toString(volume));
|
logd(eventTime, "volume", Float.toString(volume));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackInitialized(
|
||||||
|
EventTime eventTime, AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
logd(eventTime, "audioTrackInit", getAudioTrackConfigString(audioTrackConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
@Override
|
||||||
|
public void onAudioTrackReleased(
|
||||||
|
EventTime eventTime, AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
logd(eventTime, "audioTrackReleased", getAudioTrackConfigString(audioTrackConfig));
|
||||||
|
}
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void onVideoEnabled(EventTime eventTime, DecoderCounters decoderCounters) {
|
public void onVideoEnabled(EventTime eventTime, DecoderCounters decoderCounters) {
|
||||||
@ -745,4 +760,18 @@ public class EventLogger implements AnalyticsListener {
|
|||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getAudioTrackConfigString(AudioSink.AudioTrackConfig audioTrackConfig) {
|
||||||
|
return audioTrackConfig.encoding
|
||||||
|
+ ","
|
||||||
|
+ audioTrackConfig.channelConfig
|
||||||
|
+ ","
|
||||||
|
+ audioTrackConfig.sampleRate
|
||||||
|
+ ","
|
||||||
|
+ audioTrackConfig.tunneling
|
||||||
|
+ ","
|
||||||
|
+ audioTrackConfig.offload
|
||||||
|
+ ","
|
||||||
|
+ audioTrackConfig.bufferSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user