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.video; 17 18 import static com.google.android.exoplayer2.util.Util.castNonNull; 19 20 import android.os.Handler; 21 import android.os.SystemClock; 22 import android.view.Surface; 23 import android.view.TextureView; 24 import androidx.annotation.Nullable; 25 import com.google.android.exoplayer2.Format; 26 import com.google.android.exoplayer2.Renderer; 27 import com.google.android.exoplayer2.decoder.DecoderCounters; 28 import com.google.android.exoplayer2.util.Assertions; 29 30 /** 31 * Listener of video {@link Renderer} events. All methods have no-op default implementations to 32 * allow selective overrides. 33 */ 34 public interface VideoRendererEventListener { 35 36 /** 37 * Called when the renderer is enabled. 38 * 39 * @param counters {@link DecoderCounters} that will be updated by the renderer for as long as it 40 * remains enabled. 41 */ onVideoEnabled(DecoderCounters counters)42 default void onVideoEnabled(DecoderCounters counters) {} 43 44 /** 45 * Called when a decoder is created. 46 * 47 * @param decoderName The decoder that was created. 48 * @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization 49 * finished. 50 * @param initializationDurationMs The time taken to initialize the decoder in milliseconds. 51 */ onVideoDecoderInitialized( String decoderName, long initializedTimestampMs, long initializationDurationMs)52 default void onVideoDecoderInitialized( 53 String decoderName, long initializedTimestampMs, long initializationDurationMs) {} 54 55 /** 56 * Called when the format of the media being consumed by the renderer changes. 57 * 58 * @param format The new format. 59 */ onVideoInputFormatChanged(Format format)60 default void onVideoInputFormatChanged(Format format) {} 61 62 /** 63 * Called to report the number of frames dropped by the renderer. Dropped frames are reported 64 * whenever the renderer is stopped having dropped frames, and optionally, whenever the count 65 * reaches a specified threshold whilst the renderer is started. 66 * 67 * @param count The number of dropped frames. 68 * @param elapsedMs The duration in milliseconds over which the frames were dropped. This duration 69 * is timed from when the renderer was started or from when dropped frames were last reported 70 * (whichever was more recent), and not from when the first of the reported drops occurred. 71 */ onDroppedFrames(int count, long elapsedMs)72 default void onDroppedFrames(int count, long elapsedMs) {} 73 74 /** 75 * Called to report the video processing offset of video frames processed by the video renderer. 76 * 77 * <p>Video processing offset represents how early a video frame is processed compared to the 78 * player's current position. For each video frame, the offset is calculated as <em>P<sub>vf</sub> 79 * - P<sub>pl</sub></em> where <em>P<sub>vf</sub></em> is the presentation timestamp of the video 80 * frame and <em>P<sub>pl</sub></em> is the current position of the player. Positive values 81 * indicate the frame was processed early enough whereas negative values indicate that the 82 * player's position had progressed beyond the frame's timestamp when the frame was processed (and 83 * the frame was probably dropped). 84 * 85 * <p>The renderer reports the sum of video processing offset samples (one sample per processed 86 * video frame: dropped, skipped or rendered) and the total number of samples. 87 * 88 * @param totalProcessingOffsetUs The sum of all video frame processing offset samples for the 89 * video frames processed by the renderer in microseconds. 90 * @param frameCount The number of samples included in the {@code totalProcessingOffsetUs}. 91 * @param format The {@link Format} that is currently output. 92 */ onVideoFrameProcessingOffset( long totalProcessingOffsetUs, int frameCount, Format format)93 default void onVideoFrameProcessingOffset( 94 long totalProcessingOffsetUs, int frameCount, Format format) {} 95 96 /** 97 * Called before a frame is rendered for the first time since setting the surface, and each time 98 * there's a change in the size, rotation or pixel aspect ratio of the video being rendered. 99 * 100 * @param width The video width in pixels. 101 * @param height The video height in pixels. 102 * @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise 103 * rotation in degrees that the application should apply for the video for it to be rendered 104 * in the correct orientation. This value will always be zero on API levels 21 and above, 105 * since the renderer will apply all necessary rotations internally. On earlier API levels 106 * this is not possible. Applications that use {@link TextureView} can apply the rotation by 107 * calling {@link TextureView#setTransform}. Applications that do not expect to encounter 108 * rotated videos can safely ignore this parameter. 109 * @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case of 110 * square pixels this will be equal to 1.0. Different values are indicative of anamorphic 111 * content. 112 */ onVideoSizeChanged( int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio)113 default void onVideoSizeChanged( 114 int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {} 115 116 /** 117 * Called when a frame is rendered for the first time since setting the surface, and when a frame 118 * is rendered for the first time since the renderer was reset. 119 * 120 * @param surface The {@link Surface} to which a first frame has been rendered, or {@code null} if 121 * the renderer renders to something that isn't a {@link Surface}. 122 */ onRenderedFirstFrame(@ullable Surface surface)123 default void onRenderedFirstFrame(@Nullable Surface surface) {} 124 125 /** 126 * Called when the renderer is disabled. 127 * 128 * @param counters {@link DecoderCounters} that were updated by the renderer. 129 */ onVideoDisabled(DecoderCounters counters)130 default void onVideoDisabled(DecoderCounters counters) {} 131 132 /** 133 * Dispatches events to a {@link VideoRendererEventListener}. 134 */ 135 final class EventDispatcher { 136 137 @Nullable private final Handler handler; 138 @Nullable private final VideoRendererEventListener listener; 139 140 /** 141 * @param handler A handler for dispatching events, or null if creating a dummy instance. 142 * @param listener The listener to which events should be dispatched, or null if creating a 143 * dummy instance. 144 */ EventDispatcher(@ullable Handler handler, @Nullable VideoRendererEventListener listener)145 public EventDispatcher(@Nullable Handler handler, 146 @Nullable VideoRendererEventListener listener) { 147 this.handler = listener != null ? Assertions.checkNotNull(handler) : null; 148 this.listener = listener; 149 } 150 151 /** Invokes {@link VideoRendererEventListener#onVideoEnabled(DecoderCounters)}. */ enabled(DecoderCounters decoderCounters)152 public void enabled(DecoderCounters decoderCounters) { 153 if (handler != null) { 154 handler.post(() -> castNonNull(listener).onVideoEnabled(decoderCounters)); 155 } 156 } 157 158 /** Invokes {@link VideoRendererEventListener#onVideoDecoderInitialized(String, long, long)}. */ decoderInitialized( String decoderName, long initializedTimestampMs, long initializationDurationMs)159 public void decoderInitialized( 160 String decoderName, long initializedTimestampMs, long initializationDurationMs) { 161 if (handler != null) { 162 handler.post( 163 () -> 164 castNonNull(listener) 165 .onVideoDecoderInitialized( 166 decoderName, initializedTimestampMs, initializationDurationMs)); 167 } 168 } 169 170 /** Invokes {@link VideoRendererEventListener#onVideoInputFormatChanged(Format)}. */ inputFormatChanged(Format format)171 public void inputFormatChanged(Format format) { 172 if (handler != null) { 173 handler.post(() -> castNonNull(listener).onVideoInputFormatChanged(format)); 174 } 175 } 176 177 /** Invokes {@link VideoRendererEventListener#onDroppedFrames(int, long)}. */ droppedFrames(int droppedFrameCount, long elapsedMs)178 public void droppedFrames(int droppedFrameCount, long elapsedMs) { 179 if (handler != null) { 180 handler.post(() -> castNonNull(listener).onDroppedFrames(droppedFrameCount, elapsedMs)); 181 } 182 } 183 184 /** Invokes {@link VideoRendererEventListener#onVideoFrameProcessingOffset}. */ reportVideoFrameProcessingOffset( long totalProcessingOffsetUs, int frameCount, Format format)185 public void reportVideoFrameProcessingOffset( 186 long totalProcessingOffsetUs, int frameCount, Format format) { 187 if (handler != null) { 188 handler.post( 189 () -> 190 castNonNull(listener) 191 .onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount, format)); 192 } 193 } 194 195 /** Invokes {@link VideoRendererEventListener#onVideoSizeChanged(int, int, int, float)}. */ videoSizeChanged( int width, int height, final int unappliedRotationDegrees, final float pixelWidthHeightRatio)196 public void videoSizeChanged( 197 int width, 198 int height, 199 final int unappliedRotationDegrees, 200 final float pixelWidthHeightRatio) { 201 if (handler != null) { 202 handler.post( 203 () -> 204 castNonNull(listener) 205 .onVideoSizeChanged( 206 width, height, unappliedRotationDegrees, pixelWidthHeightRatio)); 207 } 208 } 209 210 /** Invokes {@link VideoRendererEventListener#onRenderedFirstFrame(Surface)}. */ renderedFirstFrame(@ullable Surface surface)211 public void renderedFirstFrame(@Nullable Surface surface) { 212 if (handler != null) { 213 handler.post(() -> castNonNull(listener).onRenderedFirstFrame(surface)); 214 } 215 } 216 217 /** Invokes {@link VideoRendererEventListener#onVideoDisabled(DecoderCounters)}. */ disabled(DecoderCounters counters)218 public void disabled(DecoderCounters counters) { 219 counters.ensureUpdated(); 220 if (handler != null) { 221 handler.post( 222 () -> { 223 counters.ensureUpdated(); 224 castNonNull(listener).onVideoDisabled(counters); 225 }); 226 } 227 } 228 229 } 230 231 } 232