1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.audio; 17 18 import static com.google.android.exoplayer2.util.Util.castNonNull; 19 20 import android.os.Handler; 21 import android.os.SystemClock; 22 import androidx.annotation.Nullable; 23 import com.google.android.exoplayer2.C; 24 import com.google.android.exoplayer2.Format; 25 import com.google.android.exoplayer2.Renderer; 26 import com.google.android.exoplayer2.decoder.DecoderCounters; 27 import com.google.android.exoplayer2.util.Assertions; 28 29 /** 30 * Listener of audio {@link Renderer} events. All methods have no-op default implementations to 31 * allow selective overrides. 32 */ 33 public interface AudioRendererEventListener { 34 35 /** 36 * Called when the renderer is enabled. 37 * 38 * @param counters {@link DecoderCounters} that will be updated by the renderer for as long as it 39 * remains enabled. 40 */ onAudioEnabled(DecoderCounters counters)41 default void onAudioEnabled(DecoderCounters counters) {} 42 43 /** 44 * Called when the audio session is set. 45 * 46 * @param audioSessionId The audio session id. 47 */ onAudioSessionId(int audioSessionId)48 default void onAudioSessionId(int audioSessionId) {} 49 50 /** 51 * Called when a decoder is created. 52 * 53 * @param decoderName The decoder that was created. 54 * @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization 55 * finished. 56 * @param initializationDurationMs The time taken to initialize the decoder in milliseconds. 57 */ onAudioDecoderInitialized( String decoderName, long initializedTimestampMs, long initializationDurationMs)58 default void onAudioDecoderInitialized( 59 String decoderName, long initializedTimestampMs, long initializationDurationMs) {} 60 61 /** 62 * Called when the format of the media being consumed by the renderer changes. 63 * 64 * @param format The new format. 65 */ onAudioInputFormatChanged(Format format)66 default void onAudioInputFormatChanged(Format format) {} 67 68 /** 69 * Called when an {@link AudioSink} underrun occurs. 70 * 71 * @param bufferSize The size of the {@link AudioSink}'s buffer, in bytes. 72 * @param bufferSizeMs The size of the {@link AudioSink}'s buffer, in milliseconds, if it is 73 * configured for PCM output. {@link C#TIME_UNSET} if it is configured for passthrough output, 74 * as the buffered media can have a variable bitrate so the duration may be unknown. 75 * @param elapsedSinceLastFeedMs The time since the {@link AudioSink} was last fed data. 76 */ onAudioSinkUnderrun( int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs)77 default void onAudioSinkUnderrun( 78 int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {} 79 80 /** 81 * Called when the renderer is disabled. 82 * 83 * @param counters {@link DecoderCounters} that were updated by the renderer. 84 */ onAudioDisabled(DecoderCounters counters)85 default void onAudioDisabled(DecoderCounters counters) {} 86 87 /** 88 * Called when skipping silences is enabled or disabled in the audio stream. 89 * 90 * @param skipSilenceEnabled Whether skipping silences in the audio stream is enabled. 91 */ onSkipSilenceEnabledChanged(boolean skipSilenceEnabled)92 default void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {} 93 94 /** Dispatches events to a {@link AudioRendererEventListener}. */ 95 final class EventDispatcher { 96 97 @Nullable private final Handler handler; 98 @Nullable private final AudioRendererEventListener listener; 99 100 /** 101 * @param handler A handler for dispatching events, or null if creating a dummy instance. 102 * @param listener The listener to which events should be dispatched, or null if creating a 103 * dummy instance. 104 */ EventDispatcher(@ullable Handler handler, @Nullable AudioRendererEventListener listener)105 public EventDispatcher(@Nullable Handler handler, 106 @Nullable AudioRendererEventListener listener) { 107 this.handler = listener != null ? Assertions.checkNotNull(handler) : null; 108 this.listener = listener; 109 } 110 111 /** 112 * Invokes {@link AudioRendererEventListener#onAudioEnabled(DecoderCounters)}. 113 */ enabled(final DecoderCounters decoderCounters)114 public void enabled(final DecoderCounters decoderCounters) { 115 if (handler != null) { 116 handler.post(() -> castNonNull(listener).onAudioEnabled(decoderCounters)); 117 } 118 } 119 120 /** 121 * Invokes {@link AudioRendererEventListener#onAudioDecoderInitialized(String, long, long)}. 122 */ decoderInitialized(final String decoderName, final long initializedTimestampMs, final long initializationDurationMs)123 public void decoderInitialized(final String decoderName, 124 final long initializedTimestampMs, final long initializationDurationMs) { 125 if (handler != null) { 126 handler.post( 127 () -> 128 castNonNull(listener) 129 .onAudioDecoderInitialized( 130 decoderName, initializedTimestampMs, initializationDurationMs)); 131 } 132 } 133 134 /** 135 * Invokes {@link AudioRendererEventListener#onAudioInputFormatChanged(Format)}. 136 */ inputFormatChanged(final Format format)137 public void inputFormatChanged(final Format format) { 138 if (handler != null) { 139 handler.post(() -> castNonNull(listener).onAudioInputFormatChanged(format)); 140 } 141 } 142 143 /** 144 * Invokes {@link AudioRendererEventListener#onAudioSinkUnderrun(int, long, long)}. 145 */ audioTrackUnderrun(final int bufferSize, final long bufferSizeMs, final long elapsedSinceLastFeedMs)146 public void audioTrackUnderrun(final int bufferSize, final long bufferSizeMs, 147 final long elapsedSinceLastFeedMs) { 148 if (handler != null) { 149 handler.post( 150 () -> 151 castNonNull(listener) 152 .onAudioSinkUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs)); 153 } 154 } 155 156 /** 157 * Invokes {@link AudioRendererEventListener#onAudioDisabled(DecoderCounters)}. 158 */ disabled(final DecoderCounters counters)159 public void disabled(final DecoderCounters counters) { 160 counters.ensureUpdated(); 161 if (handler != null) { 162 handler.post( 163 () -> { 164 counters.ensureUpdated(); 165 castNonNull(listener).onAudioDisabled(counters); 166 }); 167 } 168 } 169 170 /** 171 * Invokes {@link AudioRendererEventListener#onAudioSessionId(int)}. 172 */ audioSessionId(final int audioSessionId)173 public void audioSessionId(final int audioSessionId) { 174 if (handler != null) { 175 handler.post(() -> castNonNull(listener).onAudioSessionId(audioSessionId)); 176 } 177 } 178 179 /** Invokes {@link AudioRendererEventListener#onSkipSilenceEnabledChanged(boolean)}. */ skipSilenceEnabledChanged(final boolean skipSilenceEnabled)180 public void skipSilenceEnabledChanged(final boolean skipSilenceEnabled) { 181 if (handler != null) { 182 handler.post(() -> castNonNull(listener).onSkipSilenceEnabledChanged(skipSilenceEnabled)); 183 } 184 } 185 } 186 } 187