4
4
5
5
package io .flutter .plugins .videoplayer ;
6
6
7
- import static com . google . android . exoplayer2 .Player .REPEAT_MODE_ALL ;
8
- import static com . google . android . exoplayer2 .Player .REPEAT_MODE_OFF ;
7
+ import static androidx . media3 . common .Player .REPEAT_MODE_ALL ;
8
+ import static androidx . media3 . common .Player .REPEAT_MODE_OFF ;
9
9
10
10
import android .content .Context ;
11
- import android .net .Uri ;
12
11
import android .view .Surface ;
13
12
import androidx .annotation .NonNull ;
13
+ import androidx .annotation .Nullable ;
14
+ import androidx .annotation .OptIn ;
14
15
import androidx .annotation .VisibleForTesting ;
15
- import com .google .android .exoplayer2 .C ;
16
- import com .google .android .exoplayer2 .ExoPlayer ;
17
- import com .google .android .exoplayer2 .Format ;
18
- import com .google .android .exoplayer2 .MediaItem ;
19
- import com .google .android .exoplayer2 .PlaybackException ;
20
- import com .google .android .exoplayer2 .PlaybackParameters ;
21
- import com .google .android .exoplayer2 .Player ;
22
- import com .google .android .exoplayer2 .Player .Listener ;
23
- import com .google .android .exoplayer2 .audio .AudioAttributes ;
24
- import com .google .android .exoplayer2 .source .MediaSource ;
25
- import com .google .android .exoplayer2 .source .ProgressiveMediaSource ;
26
- import com .google .android .exoplayer2 .source .dash .DashMediaSource ;
27
- import com .google .android .exoplayer2 .source .dash .DefaultDashChunkSource ;
28
- import com .google .android .exoplayer2 .source .hls .HlsMediaSource ;
29
- import com .google .android .exoplayer2 .source .smoothstreaming .DefaultSsChunkSource ;
30
- import com .google .android .exoplayer2 .source .smoothstreaming .SsMediaSource ;
31
- import com .google .android .exoplayer2 .upstream .DataSource ;
32
- import com .google .android .exoplayer2 .upstream .DefaultDataSource ;
33
- import com .google .android .exoplayer2 .upstream .DefaultHttpDataSource ;
34
- import com .google .android .exoplayer2 .util .Util ;
16
+ import androidx .media3 .common .AudioAttributes ;
17
+ import androidx .media3 .common .C ;
18
+ import androidx .media3 .common .MediaItem ;
19
+ import androidx .media3 .common .MimeTypes ;
20
+ import androidx .media3 .common .PlaybackException ;
21
+ import androidx .media3 .common .PlaybackParameters ;
22
+ import androidx .media3 .common .Player ;
23
+ import androidx .media3 .common .Player .Listener ;
24
+ import androidx .media3 .common .VideoSize ;
25
+ import androidx .media3 .common .util .UnstableApi ;
26
+ import androidx .media3 .datasource .DataSource ;
27
+ import androidx .media3 .datasource .DefaultDataSource ;
28
+ import androidx .media3 .datasource .DefaultHttpDataSource ;
29
+ import androidx .media3 .exoplayer .ExoPlayer ;
30
+ import androidx .media3 .exoplayer .source .DefaultMediaSourceFactory ;
35
31
import io .flutter .plugin .common .EventChannel ;
36
32
import io .flutter .view .TextureRegistry ;
37
33
import java .util .Arrays ;
@@ -62,7 +58,7 @@ final class VideoPlayer {
62
58
63
59
private final VideoPlayerOptions options ;
64
60
65
- private DefaultHttpDataSource .Factory httpDataSourceFactory = new DefaultHttpDataSource . Factory () ;
61
+ private final DefaultHttpDataSource .Factory httpDataSourceFactory ;
66
62
67
63
VideoPlayer (
68
64
Context context ,
@@ -76,16 +72,18 @@ final class VideoPlayer {
76
72
this .textureEntry = textureEntry ;
77
73
this .options = options ;
78
74
79
- ExoPlayer exoPlayer = new ExoPlayer .Builder (context ).build ();
80
- Uri uri = Uri .parse (dataSource );
75
+ MediaItem mediaItem =
76
+ new MediaItem .Builder ()
77
+ .setUri (dataSource )
78
+ .setMimeType (mimeFromFormatHint (formatHint ))
79
+ .build ();
81
80
82
- buildHttpDataSourceFactory (httpHeaders );
83
- DataSource .Factory dataSourceFactory =
84
- new DefaultDataSource .Factory (context , httpDataSourceFactory );
81
+ httpDataSourceFactory = new DefaultHttpDataSource .Factory ();
82
+ configureHttpDataSourceFactory (httpHeaders );
85
83
86
- MediaSource mediaSource = buildMediaSource ( uri , dataSourceFactory , formatHint );
84
+ ExoPlayer exoPlayer = buildExoPlayer ( context , httpDataSourceFactory );
87
85
88
- exoPlayer .setMediaSource ( mediaSource );
86
+ exoPlayer .setMediaItem ( mediaItem );
89
87
exoPlayer .prepare ();
90
88
91
89
setUpVideoPlayer (exoPlayer , new QueuingEventSink ());
@@ -109,64 +107,15 @@ final class VideoPlayer {
109
107
}
110
108
111
109
@ VisibleForTesting
112
- public void buildHttpDataSourceFactory (@ NonNull Map <String , String > httpHeaders ) {
110
+ public void configureHttpDataSourceFactory (@ NonNull Map <String , String > httpHeaders ) {
113
111
final boolean httpHeadersNotEmpty = !httpHeaders .isEmpty ();
114
112
final String userAgent =
115
113
httpHeadersNotEmpty && httpHeaders .containsKey (USER_AGENT )
116
114
? httpHeaders .get (USER_AGENT )
117
115
: "ExoPlayer" ;
118
116
119
- httpDataSourceFactory .setUserAgent (userAgent ).setAllowCrossProtocolRedirects (true );
120
-
121
- if (httpHeadersNotEmpty ) {
122
- httpDataSourceFactory .setDefaultRequestProperties (httpHeaders );
123
- }
124
- }
125
-
126
- private MediaSource buildMediaSource (
127
- Uri uri , DataSource .Factory mediaDataSourceFactory , String formatHint ) {
128
- int type ;
129
- if (formatHint == null ) {
130
- type = Util .inferContentType (uri );
131
- } else {
132
- switch (formatHint ) {
133
- case FORMAT_SS :
134
- type = C .CONTENT_TYPE_SS ;
135
- break ;
136
- case FORMAT_DASH :
137
- type = C .CONTENT_TYPE_DASH ;
138
- break ;
139
- case FORMAT_HLS :
140
- type = C .CONTENT_TYPE_HLS ;
141
- break ;
142
- case FORMAT_OTHER :
143
- type = C .CONTENT_TYPE_OTHER ;
144
- break ;
145
- default :
146
- type = -1 ;
147
- break ;
148
- }
149
- }
150
- switch (type ) {
151
- case C .CONTENT_TYPE_SS :
152
- return new SsMediaSource .Factory (
153
- new DefaultSsChunkSource .Factory (mediaDataSourceFactory ), mediaDataSourceFactory )
154
- .createMediaSource (MediaItem .fromUri (uri ));
155
- case C .CONTENT_TYPE_DASH :
156
- return new DashMediaSource .Factory (
157
- new DefaultDashChunkSource .Factory (mediaDataSourceFactory ), mediaDataSourceFactory )
158
- .createMediaSource (MediaItem .fromUri (uri ));
159
- case C .CONTENT_TYPE_HLS :
160
- return new HlsMediaSource .Factory (mediaDataSourceFactory )
161
- .createMediaSource (MediaItem .fromUri (uri ));
162
- case C .CONTENT_TYPE_OTHER :
163
- return new ProgressiveMediaSource .Factory (mediaDataSourceFactory )
164
- .createMediaSource (MediaItem .fromUri (uri ));
165
- default :
166
- {
167
- throw new IllegalStateException ("Unsupported type: " + type );
168
- }
169
- }
117
+ unstableUpdateDataSourceFactory (
118
+ httpDataSourceFactory , httpHeaders , userAgent , httpHeadersNotEmpty );
170
119
}
171
120
172
121
private void setUpVideoPlayer (ExoPlayer exoPlayer , QueuingEventSink eventSink ) {
@@ -304,15 +253,15 @@ void sendInitialized() {
304
253
event .put ("event" , "initialized" );
305
254
event .put ("duration" , exoPlayer .getDuration ());
306
255
307
- if ( exoPlayer .getVideoFormat () != null ) {
308
- Format videoFormat = exoPlayer . getVideoFormat () ;
309
- int width = videoFormat . width ;
310
- int height = videoFormat . height ;
311
- int rotationDegrees = videoFormat . rotationDegrees ;
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 ;
312
261
// Switch the width/height if video was taken in portrait mode
313
262
if (rotationDegrees == 90 || rotationDegrees == 270 ) {
314
- width = exoPlayer . getVideoFormat () .height ;
315
- height = exoPlayer . getVideoFormat () .width ;
263
+ width = videoSize .height ;
264
+ height = videoSize .width ;
316
265
}
317
266
event .put ("width" , width );
318
267
event .put ("height" , height );
@@ -343,4 +292,46 @@ void dispose() {
343
292
exoPlayer .release ();
344
293
}
345
294
}
295
+
296
+ @ NonNull
297
+ private static ExoPlayer buildExoPlayer (
298
+ Context context , DataSource .Factory baseDataSourceFactory ) {
299
+ DataSource .Factory dataSourceFactory =
300
+ new DefaultDataSource .Factory (context , baseDataSourceFactory );
301
+ DefaultMediaSourceFactory mediaSourceFactory =
302
+ new DefaultMediaSourceFactory (context ).setDataSourceFactory (dataSourceFactory );
303
+ return new ExoPlayer .Builder (context ).setMediaSourceFactory (mediaSourceFactory ).build ();
304
+ }
305
+
306
+ @ Nullable
307
+ private static String mimeFromFormatHint (@ Nullable String formatHint ) {
308
+ if (formatHint == null ) {
309
+ return null ;
310
+ }
311
+ switch (formatHint ) {
312
+ case FORMAT_SS :
313
+ return MimeTypes .APPLICATION_SS ;
314
+ case FORMAT_DASH :
315
+ return MimeTypes .APPLICATION_MPD ;
316
+ case FORMAT_HLS :
317
+ return MimeTypes .APPLICATION_M3U8 ;
318
+ case FORMAT_OTHER :
319
+ default :
320
+ return null ;
321
+ }
322
+ }
323
+
324
+ // TODO: migrate to stable API, see https://github.com/flutter/flutter/issues/147039
325
+ @ OptIn (markerClass = UnstableApi .class )
326
+ private static void unstableUpdateDataSourceFactory (
327
+ DefaultHttpDataSource .Factory factory ,
328
+ @ NonNull Map <String , String > httpHeaders ,
329
+ String userAgent ,
330
+ boolean httpHeadersNotEmpty ) {
331
+ factory .setUserAgent (userAgent ).setAllowCrossProtocolRedirects (true );
332
+
333
+ if (httpHeadersNotEmpty ) {
334
+ factory .setDefaultRequestProperties (httpHeaders );
335
+ }
336
+ }
346
337
}
0 commit comments