|
10 | 10 | import android.content.Context;
|
11 | 11 | import android.view.Surface;
|
12 | 12 | import androidx.annotation.NonNull;
|
13 |
| -import androidx.annotation.Nullable; |
14 |
| -import androidx.annotation.OptIn; |
15 | 13 | import androidx.annotation.VisibleForTesting;
|
16 | 14 | import androidx.media3.common.AudioAttributes;
|
17 | 15 | import androidx.media3.common.C;
|
18 | 16 | import androidx.media3.common.MediaItem;
|
19 |
| -import androidx.media3.common.MimeTypes; |
20 | 17 | import androidx.media3.common.PlaybackParameters;
|
21 |
| -import androidx.media3.common.util.UnstableApi; |
22 |
| -import androidx.media3.datasource.DataSource; |
23 |
| -import androidx.media3.datasource.DefaultDataSource; |
24 |
| -import androidx.media3.datasource.DefaultHttpDataSource; |
25 | 18 | import androidx.media3.exoplayer.ExoPlayer;
|
26 |
| -import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; |
27 | 19 | import io.flutter.view.TextureRegistry;
|
28 |
| -import java.util.Map; |
29 | 20 |
|
30 | 21 | final class VideoPlayer {
|
31 |
| - private static final String FORMAT_SS = "ss"; |
32 |
| - private static final String FORMAT_DASH = "dash"; |
33 |
| - private static final String FORMAT_HLS = "hls"; |
34 |
| - private static final String FORMAT_OTHER = "other"; |
35 |
| - |
36 | 22 | private ExoPlayer exoPlayer;
|
37 |
| - |
38 | 23 | private Surface surface;
|
39 |
| - |
40 | 24 | private final TextureRegistry.SurfaceTextureEntry textureEntry;
|
41 |
| - |
42 | 25 | private final VideoPlayerCallbacks videoPlayerEvents;
|
43 |
| - |
44 |
| - private static final String USER_AGENT = "User-Agent"; |
45 |
| - |
46 | 26 | private final VideoPlayerOptions options;
|
47 | 27 |
|
48 |
| - private final DefaultHttpDataSource.Factory httpDataSourceFactory; |
49 |
| - |
50 |
| - VideoPlayer( |
| 28 | + /** |
| 29 | + * Creates a video player. |
| 30 | + * |
| 31 | + * @param context application context. |
| 32 | + * @param events event callbacks. |
| 33 | + * @param textureEntry texture to render to. |
| 34 | + * @param asset asset to play. |
| 35 | + * @param options options for playback. |
| 36 | + * @return a video player instance. |
| 37 | + */ |
| 38 | + @NonNull |
| 39 | + static VideoPlayer create( |
51 | 40 | Context context,
|
52 | 41 | VideoPlayerCallbacks events,
|
53 | 42 | TextureRegistry.SurfaceTextureEntry textureEntry,
|
54 |
| - String dataSource, |
55 |
| - String formatHint, |
56 |
| - @NonNull Map<String, String> httpHeaders, |
| 43 | + VideoAsset asset, |
57 | 44 | VideoPlayerOptions options) {
|
58 |
| - this.videoPlayerEvents = events; |
59 |
| - this.textureEntry = textureEntry; |
60 |
| - this.options = options; |
61 |
| - |
62 |
| - MediaItem mediaItem = |
63 |
| - new MediaItem.Builder() |
64 |
| - .setUri(dataSource) |
65 |
| - .setMimeType(mimeFromFormatHint(formatHint)) |
66 |
| - .build(); |
67 |
| - |
68 |
| - httpDataSourceFactory = new DefaultHttpDataSource.Factory(); |
69 |
| - configureHttpDataSourceFactory(httpHeaders); |
70 |
| - |
71 |
| - ExoPlayer exoPlayer = buildExoPlayer(context, httpDataSourceFactory); |
72 |
| - |
73 |
| - exoPlayer.setMediaItem(mediaItem); |
74 |
| - exoPlayer.prepare(); |
75 |
| - |
76 |
| - setUpVideoPlayer(exoPlayer); |
| 45 | + ExoPlayer.Builder builder = |
| 46 | + new ExoPlayer.Builder(context).setMediaSourceFactory(asset.getMediaSourceFactory(context)); |
| 47 | + return new VideoPlayer(builder, events, textureEntry, asset.getMediaItem(), options); |
77 | 48 | }
|
78 | 49 |
|
79 |
| - // Constructor used to directly test members of this class. |
80 | 50 | @VisibleForTesting
|
81 | 51 | VideoPlayer(
|
82 |
| - ExoPlayer exoPlayer, |
| 52 | + ExoPlayer.Builder builder, |
83 | 53 | VideoPlayerCallbacks events,
|
84 | 54 | TextureRegistry.SurfaceTextureEntry textureEntry,
|
85 |
| - VideoPlayerOptions options, |
86 |
| - DefaultHttpDataSource.Factory httpDataSourceFactory) { |
| 55 | + MediaItem mediaItem, |
| 56 | + VideoPlayerOptions options) { |
87 | 57 | this.videoPlayerEvents = events;
|
88 | 58 | this.textureEntry = textureEntry;
|
89 | 59 | this.options = options;
|
90 |
| - this.httpDataSourceFactory = httpDataSourceFactory; |
91 | 60 |
|
92 |
| - setUpVideoPlayer(exoPlayer); |
93 |
| - } |
| 61 | + ExoPlayer exoPlayer = builder.build(); |
| 62 | + exoPlayer.setMediaItem(mediaItem); |
| 63 | + exoPlayer.prepare(); |
94 | 64 |
|
95 |
| - @VisibleForTesting |
96 |
| - public void configureHttpDataSourceFactory(@NonNull Map<String, String> httpHeaders) { |
97 |
| - final boolean httpHeadersNotEmpty = !httpHeaders.isEmpty(); |
98 |
| - final String userAgent = |
99 |
| - httpHeadersNotEmpty && httpHeaders.containsKey(USER_AGENT) |
100 |
| - ? httpHeaders.get(USER_AGENT) |
101 |
| - : "ExoPlayer"; |
102 |
| - |
103 |
| - unstableUpdateDataSourceFactory( |
104 |
| - httpDataSourceFactory, httpHeaders, userAgent, httpHeadersNotEmpty); |
| 65 | + setUpVideoPlayer(exoPlayer); |
105 | 66 | }
|
106 | 67 |
|
107 | 68 | private void setUpVideoPlayer(ExoPlayer exoPlayer) {
|
@@ -165,46 +126,4 @@ void dispose() {
|
165 | 126 | exoPlayer.release();
|
166 | 127 | }
|
167 | 128 | }
|
168 |
| - |
169 |
| - @NonNull |
170 |
| - private static ExoPlayer buildExoPlayer( |
171 |
| - Context context, DataSource.Factory baseDataSourceFactory) { |
172 |
| - DataSource.Factory dataSourceFactory = |
173 |
| - new DefaultDataSource.Factory(context, baseDataSourceFactory); |
174 |
| - DefaultMediaSourceFactory mediaSourceFactory = |
175 |
| - new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory); |
176 |
| - return new ExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build(); |
177 |
| - } |
178 |
| - |
179 |
| - @Nullable |
180 |
| - private static String mimeFromFormatHint(@Nullable String formatHint) { |
181 |
| - if (formatHint == null) { |
182 |
| - return null; |
183 |
| - } |
184 |
| - switch (formatHint) { |
185 |
| - case FORMAT_SS: |
186 |
| - return MimeTypes.APPLICATION_SS; |
187 |
| - case FORMAT_DASH: |
188 |
| - return MimeTypes.APPLICATION_MPD; |
189 |
| - case FORMAT_HLS: |
190 |
| - return MimeTypes.APPLICATION_M3U8; |
191 |
| - case FORMAT_OTHER: |
192 |
| - default: |
193 |
| - return null; |
194 |
| - } |
195 |
| - } |
196 |
| - |
197 |
| - // TODO: migrate to stable API, see https://github.com/flutter/flutter/issues/147039 |
198 |
| - @OptIn(markerClass = UnstableApi.class) |
199 |
| - private static void unstableUpdateDataSourceFactory( |
200 |
| - DefaultHttpDataSource.Factory factory, |
201 |
| - @NonNull Map<String, String> httpHeaders, |
202 |
| - String userAgent, |
203 |
| - boolean httpHeadersNotEmpty) { |
204 |
| - factory.setUserAgent(userAgent).setAllowCrossProtocolRedirects(true); |
205 |
| - |
206 |
| - if (httpHeadersNotEmpty) { |
207 |
| - factory.setDefaultRequestProperties(httpHeaders); |
208 |
| - } |
209 |
| - } |
210 | 129 | }
|
0 commit comments