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:
tonihei 2023-10-04 01:47:28 -07:00 committed by oceanjules
parent 55633658c6
commit 6d2bf513fb
11 changed files with 262 additions and 19 deletions

View File

@ -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:

View File

@ -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) {

View File

@ -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.
/** /**

View File

@ -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.
* *

View File

@ -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();

View File

@ -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));
}
}
} }
} }

View File

@ -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. */

View File

@ -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)

View File

@ -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),

View File

@ -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)

View File

@ -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;
}
} }