• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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