2828import androidx .media3 .datasource .DefaultHttpDataSource ;
2929import androidx .media3 .exoplayer .ExoPlayer ;
3030import androidx .media3 .exoplayer .source .DefaultMediaSourceFactory ;
31- import io .flutter .plugin .common .EventChannel ;
3231import io .flutter .view .TextureRegistry ;
33- import java .util .Arrays ;
34- import java .util .Collections ;
35- import java .util .HashMap ;
36- import java .util .List ;
3732import java .util .Map ;
3833
3934final class VideoPlayer {
@@ -48,9 +43,7 @@ final class VideoPlayer {
4843
4944 private final TextureRegistry .SurfaceTextureEntry textureEntry ;
5045
51- private QueuingEventSink eventSink ;
52-
53- private final EventChannel eventChannel ;
46+ private final VideoPlayerCallbacks videoPlayerEvents ;
5447
5548 private static final String USER_AGENT = "User-Agent" ;
5649
@@ -62,13 +55,13 @@ final class VideoPlayer {
6255
6356 VideoPlayer (
6457 Context context ,
65- EventChannel eventChannel ,
58+ VideoPlayerCallbacks events ,
6659 TextureRegistry .SurfaceTextureEntry textureEntry ,
6760 String dataSource ,
6861 String formatHint ,
6962 @ NonNull Map <String , String > httpHeaders ,
7063 VideoPlayerOptions options ) {
71- this .eventChannel = eventChannel ;
64+ this .videoPlayerEvents = events ;
7265 this .textureEntry = textureEntry ;
7366 this .options = options ;
7467
@@ -86,24 +79,23 @@ final class VideoPlayer {
8679 exoPlayer .setMediaItem (mediaItem );
8780 exoPlayer .prepare ();
8881
89- setUpVideoPlayer (exoPlayer , new QueuingEventSink () );
82+ setUpVideoPlayer (exoPlayer );
9083 }
9184
9285 // Constructor used to directly test members of this class.
9386 @ VisibleForTesting
9487 VideoPlayer (
9588 ExoPlayer exoPlayer ,
96- EventChannel eventChannel ,
89+ VideoPlayerCallbacks events ,
9790 TextureRegistry .SurfaceTextureEntry textureEntry ,
9891 VideoPlayerOptions options ,
99- QueuingEventSink eventSink ,
10092 DefaultHttpDataSource .Factory httpDataSourceFactory ) {
101- this .eventChannel = eventChannel ;
93+ this .videoPlayerEvents = events ;
10294 this .textureEntry = textureEntry ;
10395 this .options = options ;
10496 this .httpDataSourceFactory = httpDataSourceFactory ;
10597
106- setUpVideoPlayer (exoPlayer , eventSink );
98+ setUpVideoPlayer (exoPlayer );
10799 }
108100
109101 @ VisibleForTesting
@@ -118,37 +110,29 @@ public void configureHttpDataSourceFactory(@NonNull Map<String, String> httpHead
118110 httpDataSourceFactory , httpHeaders , userAgent , httpHeadersNotEmpty );
119111 }
120112
121- private void setUpVideoPlayer (ExoPlayer exoPlayer , QueuingEventSink eventSink ) {
113+ private void setUpVideoPlayer (ExoPlayer exoPlayer ) {
122114 this .exoPlayer = exoPlayer ;
123- this .eventSink = eventSink ;
124-
125- eventChannel .setStreamHandler (
126- new EventChannel .StreamHandler () {
127- @ Override
128- public void onListen (Object o , EventChannel .EventSink sink ) {
129- eventSink .setDelegate (sink );
130- }
131-
132- @ Override
133- public void onCancel (Object o ) {
134- eventSink .setDelegate (null );
135- }
136- });
137115
138116 surface = new Surface (textureEntry .surfaceTexture ());
139117 exoPlayer .setVideoSurface (surface );
140118 setAudioAttributes (exoPlayer , options .mixWithOthers );
141119
120+ // Avoids synthetic accessor.
121+ VideoPlayerCallbacks events = this .videoPlayerEvents ;
122+
142123 exoPlayer .addListener (
143124 new Listener () {
144125 private boolean isBuffering = false ;
145126
146127 public void setBuffering (boolean buffering ) {
147- if (isBuffering != buffering ) {
148- isBuffering = buffering ;
149- Map <String , Object > event = new HashMap <>();
150- event .put ("event" , isBuffering ? "bufferingStart" : "bufferingEnd" );
151- eventSink .success (event );
128+ if (isBuffering == buffering ) {
129+ return ;
130+ }
131+ isBuffering = buffering ;
132+ if (buffering ) {
133+ events .onBufferingStart ();
134+ } else {
135+ events .onBufferingEnd ();
152136 }
153137 }
154138
@@ -163,11 +147,8 @@ public void onPlaybackStateChanged(final int playbackState) {
163147 sendInitialized ();
164148 }
165149 } else if (playbackState == Player .STATE_ENDED ) {
166- Map <String , Object > event = new HashMap <>();
167- event .put ("event" , "completed" );
168- eventSink .success (event );
150+ events .onCompleted ();
169151 }
170-
171152 if (playbackState != Player .STATE_BUFFERING ) {
172153 setBuffering (false );
173154 }
@@ -180,30 +161,20 @@ public void onPlayerError(@NonNull final PlaybackException error) {
180161 // See https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window
181162 exoPlayer .seekToDefaultPosition ();
182163 exoPlayer .prepare ();
183- } else if ( eventSink != null ) {
184- eventSink . error ("VideoError" , "Video player had error " + error , null );
164+ } else {
165+ events . onError ("VideoError" , "Video player had error " + error , null );
185166 }
186167 }
187168
188169 @ Override
189170 public void onIsPlayingChanged (boolean isPlaying ) {
190- if (eventSink != null ) {
191- Map <String , Object > event = new HashMap <>();
192- event .put ("event" , "isPlayingStateUpdate" );
193- event .put ("isPlaying" , isPlaying );
194- eventSink .success (event );
195- }
171+ events .onIsPlayingStateUpdate (isPlaying );
196172 }
197173 });
198174 }
199175
200176 void sendBufferingUpdate () {
201- Map <String , Object > event = new HashMap <>();
202- event .put ("event" , "bufferingUpdate" );
203- List <? extends Number > range = Arrays .asList (0 , exoPlayer .getBufferedPosition ());
204- // iOS supports a list of buffered ranges, so here is a list with a single range.
205- event .put ("values" , Collections .singletonList (range ));
206- eventSink .success (event );
177+ videoPlayerEvents .onBufferingUpdate (exoPlayer .getBufferedPosition ());
207178 }
208179
209180 private static void setAudioAttributes (ExoPlayer exoPlayer , boolean isMixMode ) {
@@ -248,43 +219,36 @@ long getPosition() {
248219 @ SuppressWarnings ("SuspiciousNameCombination" )
249220 @ VisibleForTesting
250221 void sendInitialized () {
251- if (isInitialized ) {
252- Map <String , Object > event = new HashMap <>();
253- event .put ("event" , "initialized" );
254- event .put ("duration" , exoPlayer .getDuration ());
255-
256- VideoSize videoSize = exoPlayer .getVideoSize ();
257- int width = videoSize .width ;
258- int height = videoSize .height ;
259- if (width != 0 && height != 0 ) {
260- int rotationDegrees = videoSize .unappliedRotationDegrees ;
261- // Switch the width/height if video was taken in portrait mode
262- if (rotationDegrees == 90 || rotationDegrees == 270 ) {
263- width = videoSize .height ;
264- height = videoSize .width ;
265- }
266- event .put ("width" , width );
267- event .put ("height" , height );
268-
269- // Rotating the video with ExoPlayer does not seem to be possible with a Surface,
270- // so inform the Flutter code that the widget needs to be rotated to prevent
271- // upside-down playback for videos with rotationDegrees of 180 (other orientations work
272- // correctly without correction).
273- if (rotationDegrees == 180 ) {
274- event .put ("rotationCorrection" , rotationDegrees );
275- }
222+ if (!isInitialized ) {
223+ return ;
224+ }
225+ VideoSize videoSize = exoPlayer .getVideoSize ();
226+ int rotationCorrection = 0 ;
227+ int width = videoSize .width ;
228+ int height = videoSize .height ;
229+ if (width != 0 && height != 0 ) {
230+ int rotationDegrees = videoSize .unappliedRotationDegrees ;
231+ // Switch the width/height if video was taken in portrait mode
232+ if (rotationDegrees == 90 || rotationDegrees == 270 ) {
233+ width = videoSize .height ;
234+ height = videoSize .width ;
235+ }
236+ // Rotating the video with ExoPlayer does not seem to be possible with a Surface,
237+ // so inform the Flutter code that the widget needs to be rotated to prevent
238+ // upside-down playback for videos with rotationDegrees of 180 (other orientations work
239+ // correctly without correction).
240+ if (rotationDegrees == 180 ) {
241+ rotationCorrection = rotationDegrees ;
276242 }
277-
278- eventSink .success (event );
279243 }
244+ videoPlayerEvents .onInitialized (width , height , exoPlayer .getDuration (), rotationCorrection );
280245 }
281246
282247 void dispose () {
283248 if (isInitialized ) {
284249 exoPlayer .stop ();
285250 }
286251 textureEntry .release ();
287- eventChannel .setStreamHandler (null );
288252 if (surface != null ) {
289253 surface .release ();
290254 }
0 commit comments