diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 310b8e1b6e55..949ed92597e8 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,44 @@ +## 2.5.0 + +* Migrates ExoPlayer to Media3-ExoPlayer 1.3.1. + +## 2.4.17 + +* Revert Impeller support. + +## 2.4.16 + +* [Supports Impeller](https://docs.flutter.dev/release/breaking-changes/android-surface-plugins). + +## 2.4.15 + +* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. +* Removes support for apps using the v1 Android embedding. + +## 2.4.14 + +* Calls `onDestroy` instead of `initialize` in onDetachedFromEngine. + +## 2.4.13 + +* Updates minSdkVersion to 19. +* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. + +## 2.4.12 + +* Updates compileSdk version to 34. +* Adds error handling for `BehindLiveWindowException`, which may occur upon live-video playback failure. + +## 2.4.11 + +* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. +* Fixes new lint warnings. + +## 2.4.10 + +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. + ## 2.4.9 * Bumps ExoPlayer version to 2.18.7. diff --git a/packages/video_player/video_player_android/CONTRIBUTING.md b/packages/video_player/video_player_android/CONTRIBUTING.md index e06f2233278b..5ee134f738b2 100644 --- a/packages/video_player/video_player_android/CONTRIBUTING.md +++ b/packages/video_player/video_player_android/CONTRIBUTING.md @@ -7,7 +7,7 @@ command in this directory: flutter pub upgrade flutter pub run pigeon --input pigeons/messages.dart # git commit your changes so that your working environment is clean -(cd ../../../; ./script/tool_runner.sh format --clang-format=clang-format-7) +dart run ../../../script/tool/bin/flutter_plugin_tools.dart format --current-package ``` If you update pigeon itself and want to test the changes here, diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 26e5925c5e1c..814468af8123 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -53,20 +53,21 @@ android { } dependencies { - def exoplayer_version = "2.18.7" + def media3_version = "1.4.1" def picasso_version = "2.8" - implementation "com.google.android.exoplayer:exoplayer-core:${exoplayer_version}" - implementation "com.google.android.exoplayer:exoplayer-hls:${exoplayer_version}" - implementation "com.google.android.exoplayer:exoplayer-dash:${exoplayer_version}" - implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:${exoplayer_version}" - implementation "com.google.android.exoplayer:exoplayer-ui:${exoplayer_version}" - implementation "com.google.android.exoplayer:extension-mediasession:${exoplayer_version}" + implementation "androidx.media3:media3-exoplayer:${media3_version}" + implementation "androidx.media3:media3-exoplayer-hls:${media3_version}" + implementation "androidx.media3:media3-exoplayer-dash:${media3_version}" + implementation "androidx.media3:media3-exoplayer-smoothstreaming:${media3_version}" + implementation "androidx.media3:media3-ui:${media3_version}" + implementation "androidx.media3:media3-session:${media3_version}" implementation "com.squareup.picasso:picasso:${picasso_version}" + implementation "androidx.media:media:1.7.0" testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.mockito:mockito-inline:5.0.0' testImplementation 'org.robolectric:robolectric:4.10.3' - api 'com.mux.stats.sdk.muxstats:MuxExoPlayer_r2_16_1:2.7.2' + api 'com.mux.stats.sdk.muxstats:data-media3-at_1_4:1.8.0' } testOptions { diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java index 1d8adcef8056..2a56665a16da 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java @@ -54,8 +54,7 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { } else { errorList.add(exception.toString()); errorList.add(exception.getClass().getSimpleName()); - errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + errorList.add("Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); } return errorList; } @@ -1066,6 +1065,16 @@ public void setVideoCdn(@Nullable String setterArg) { this.videoCdn = setterArg; } + private @Nullable String viewerPlanStatus; + + public @Nullable String getViewerPlanStatus() { + return viewerPlanStatus; + } + + public void setViewerPlanStatus(@Nullable String setterArg) { + this.viewerPlanStatus = setterArg; + } + public static final class Builder { private @Nullable Long textureId; @@ -1215,6 +1224,13 @@ public static final class Builder { return this; } + private @Nullable String viewerPlanStatus; + + public @NonNull Builder setViewerPlanStatus(@Nullable String setterArg) { + this.viewerPlanStatus = setterArg; + return this; + } + public @NonNull MuxConfigMessage build() { MuxConfigMessage pigeonReturn = new MuxConfigMessage(); pigeonReturn.setTextureId(textureId); @@ -1238,6 +1254,7 @@ public static final class Builder { pigeonReturn.setVideoProducer(videoProducer); pigeonReturn.setVideoEncodingVariant(videoEncodingVariant); pigeonReturn.setVideoCdn(videoCdn); + pigeonReturn.setViewerPlanStatus(viewerPlanStatus); return pigeonReturn; } } @@ -1266,6 +1283,7 @@ ArrayList toList() { toListResult.add(videoProducer); toListResult.add(videoEncodingVariant); toListResult.add(videoCdn); + toListResult.add(viewerPlanStatus); return toListResult; } @@ -1313,6 +1331,8 @@ ArrayList toList() { pigeonResult.setVideoEncodingVariant((String) videoEncodingVariant); Object videoCdn = list.get(20); pigeonResult.setVideoCdn((String) videoCdn); + Object viewerPlanStatus = list.get(21); + pigeonResult.setViewerPlanStatus((String) viewerPlanStatus); return pigeonResult; } } @@ -1418,8 +1438,12 @@ public interface AndroidVideoPlayerApi { static @NonNull MessageCodec getCodec() { return AndroidVideoPlayerApiCodec.INSTANCE; } - /**Sets up an instance of `AndroidVideoPlayerApi` to handle messages through the `binaryMessenger`. */ - static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVideoPlayerApi api) { + /** + * Sets up an instance of `AndroidVideoPlayerApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup( + @NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1431,8 +1455,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.initialize(); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1455,8 +1478,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { TextureMessage output = api.create(msgArg); wrapped.add(0, output); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1479,8 +1501,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.dispose(msgArg); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1503,8 +1524,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.setLooping(msgArg); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1527,8 +1547,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.setVolume(msgArg); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1551,8 +1570,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.setPlaybackSpeed(msgArg); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1575,8 +1593,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.play(msgArg); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1599,8 +1616,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { PositionMessage output = api.position(msgArg); wrapped.add(0, output); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1623,8 +1639,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.seekTo(msgArg); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1647,8 +1662,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.pause(msgArg); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1671,8 +1685,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid try { api.setMixWithOthers(msgArg); wrapped.add(0, null); - } - catch (Throwable exception) { + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1696,7 +1709,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid api.replaceDataSource(msgArg); wrapped.add(0, null); } - catch (Throwable exception) { + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1720,7 +1733,7 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable AndroidVid api.setupMux(msgArg); wrapped.add(0, null); } - catch (Throwable exception) { + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java index 22241f088566..d8306e0c5e64 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java @@ -4,52 +4,44 @@ package io.flutter.plugins.videoplayer; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; +import static androidx.media3.common.Player.REPEAT_MODE_ALL; +import static androidx.media3.common.Player.REPEAT_MODE_OFF; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; -import android.support.v4.media.MediaMetadataCompat; -import android.support.v4.media.session.MediaSessionCompat; -import android.support.v4.media.session.PlaybackStateCompat; +import androidx.media3.session.MediaSession; import android.util.Log; import android.view.Surface; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.OptIn; import androidx.annotation.VisibleForTesting; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.MediaItem; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.Player.Listener; -import com.google.android.exoplayer2.audio.AudioAttributes; -import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; -import static com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector.MediaMetadataProvider; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.ProgressiveMediaSource; -import com.google.android.exoplayer2.source.dash.DashMediaSource; -import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; -import com.google.android.exoplayer2.source.hls.HlsMediaSource; -import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; -import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DefaultDataSource; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer2.upstream.ResolvingDataSource; -import com.google.android.exoplayer2.ui.PlayerNotificationManager; -import static com.google.android.exoplayer2.ui.PlayerNotificationManager.MediaDescriptionAdapter; -import com.google.android.exoplayer2.util.Util; +import androidx.media3.common.AudioAttributes; +import androidx.media3.common.C; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.PlaybackParameters; +import androidx.media3.common.Player; +import androidx.media3.common.Player.Listener; +import androidx.media3.common.VideoSize; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.datasource.DataSource; +import androidx.media3.datasource.DefaultDataSource; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.exoplayer.ExoPlayer; +import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; +import androidx.media3.ui.PlayerNotificationManager; import io.flutter.plugin.common.EventChannel; import io.flutter.view.TextureRegistry; import java.util.Arrays; @@ -90,12 +82,11 @@ final class VideoPlayer { private final VideoPlayerOptions options; - private DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory(); - private ResolvingDataSource.Factory resolvingDataSourceFactory; + private final DefaultHttpDataSource.Factory httpDataSourceFactory; private String dataSource; private String formatHint; - private Map httpHeaders = new HashMap<>(); + private Map httpHeaders; VideoPlayer( Context context, @@ -112,19 +103,18 @@ final class VideoPlayer { this.options = options; this.httpHeaders = httpHeaders; - ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build(); - Uri uri = Uri.parse(dataSource); + MediaItem mediaItem = + new MediaItem.Builder() + .setUri(dataSource) + .setMimeType(mimeFromFormatHint(formatHint)) + .build(); - buildHttpDataSourceFactory(httpHeaders); - DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(context, httpDataSourceFactory); + httpDataSourceFactory = new DefaultHttpDataSource.Factory(); + configureHttpDataSourceFactory(httpHeaders); - resolvingDataSourceFactory = new ResolvingDataSource.Factory( - dataSourceFactory, - dataSpec -> dataSpec.withRequestHeaders(httpHeaders)); + ExoPlayer exoPlayer = buildExoPlayer(context, httpDataSourceFactory); - MediaSource mediaSource = buildMediaSource(uri, resolvingDataSourceFactory, formatHint); - - exoPlayer.setMediaSource(mediaSource); + exoPlayer.setMediaItem(mediaItem); exoPlayer.prepare(); setUpVideoPlayer(exoPlayer, new QueuingEventSink()); @@ -148,58 +138,14 @@ final class VideoPlayer { } @VisibleForTesting - public void buildHttpDataSourceFactory(@NonNull Map httpHeaders) { + public void configureHttpDataSourceFactory(@NonNull Map httpHeaders) { final boolean httpHeadersNotEmpty = !httpHeaders.isEmpty(); final String userAgent = httpHeadersNotEmpty && httpHeaders.containsKey(USER_AGENT) ? httpHeaders.get(USER_AGENT) : "ExoPlayer"; - httpDataSourceFactory.setUserAgent(userAgent).setAllowCrossProtocolRedirects(true); - } - - private MediaSource buildMediaSource( - Uri uri, DataSource.Factory mediaDataSourceFactory, String formatHint) { - int type; - if (formatHint == null) { - type = Util.inferContentType(uri); - } else { - switch (formatHint) { - case FORMAT_SS: - type = C.CONTENT_TYPE_SS; - break; - case FORMAT_DASH: - type = C.CONTENT_TYPE_DASH; - break; - case FORMAT_HLS: - type = C.CONTENT_TYPE_HLS; - break; - case FORMAT_OTHER: - type = C.CONTENT_TYPE_OTHER; - break; - default: - type = -1; - break; - } - } - switch (type) { - case C.CONTENT_TYPE_SS: - return new SsMediaSource.Factory( - new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mediaDataSourceFactory) - .createMediaSource(MediaItem.fromUri(uri)); - case C.CONTENT_TYPE_DASH: - return new DashMediaSource.Factory( - new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mediaDataSourceFactory) - .createMediaSource(MediaItem.fromUri(uri)); - case C.CONTENT_TYPE_HLS: - return new HlsMediaSource.Factory(mediaDataSourceFactory) - .createMediaSource(MediaItem.fromUri(uri)); - case C.CONTENT_TYPE_OTHER: - return new ProgressiveMediaSource.Factory(mediaDataSourceFactory) - .createMediaSource(MediaItem.fromUri(uri)); - default: { - throw new IllegalStateException("Unsupported type: " + type); - } - } + unstableUpdateDataSourceFactory( + httpDataSourceFactory, httpHeaders, userAgent, httpHeadersNotEmpty); } private void setUpVideoPlayer(ExoPlayer exoPlayer, QueuingEventSink eventSink) { @@ -260,7 +206,11 @@ public void onPlaybackStateChanged(final int playbackState) { @Override public void onPlayerError(@NonNull final PlaybackException error) { setBuffering(false); - if (eventSink != null) { + if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { + // See https://exoplayer.dev/live-streaming.html#behindlivewindowexception-and-error_code_behind_live_window + exoPlayer.seekToDefaultPosition(); + exoPlayer.prepare(); + } else if (eventSink != null) { eventSink.error("VideoError", "Video player had error " + error, null); } } @@ -299,6 +249,7 @@ private static void setAudioAttributes(ExoPlayer exoPlayer, boolean isMixMode) { private PlayerNotificationManager playerNotificationManager; + @SuppressWarnings("deprecation") void setupNotification(Context context, String title, String artist, Boolean isLiveStream, String artworkUrl, String defaultArtworkAssetPath) { @@ -327,7 +278,7 @@ void setupNotification(Context context, setupMediaSession(context); - playerNotificationManager.setMediaSessionToken(mediaSession.getSessionToken()); + playerNotificationManager.setMediaSessionToken(mediaSession.getSessionCompatToken()); initialized = true; } @@ -342,33 +293,42 @@ private void setupNotificationChannel(Context context) { notificationManager.createNotificationChannel(channel); } - private MediaSessionCompat mediaSession; + private MediaSession mediaSession; - private MediaSessionCompat setupMediaSession(Context context) { + private MediaSession setupMediaSession(Context context) { if (this.mediaSession != null) this.mediaSession.release(); - PendingIntent pendingIntent = PendingIntent.getBroadcast( - context, - 0, - new Intent(Intent.ACTION_MEDIA_BUTTON), - PendingIntent.FLAG_IMMUTABLE); - - MediaSessionCompat mediaSession = new MediaSessionCompat(context, - "VideoPlayer", - null, - pendingIntent); - - mediaSession.setActive(true); - MediaSessionConnector mediaSessionConnector = new MediaSessionConnector(mediaSession); - mediaSessionConnector.setEnabledPlaybackActions( - PlaybackStateCompat.ACTION_PLAY_PAUSE - | PlaybackStateCompat.ACTION_PLAY - | PlaybackStateCompat.ACTION_PAUSE); - mediaSessionConnector.setPlayer(exoPlayer); - - this.mediaSession = mediaSession; - return mediaSession; + PendingIntent sessionActivity = buildSessionActivityPendingIntent(context); + + MediaSession.Builder builder = new MediaSession.Builder(context, exoPlayer).setId("VideoPlayer"); + if(sessionActivity != null){ + builder.setSessionActivity(sessionActivity); + } + + this.mediaSession = builder.build(); + return this.mediaSession; + } + + private PendingIntent buildSessionActivityPendingIntent(android.content.Context context) { + PackageManager pm = context.getPackageManager(); + Intent launch = pm.getLaunchIntentForPackage(context.getPackageName()); + if (launch == null) { + return null; + } + + launch.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + + try { + return PendingIntent.getActivity(context, 0, launch, flags); + } catch (Throwable t) { + return null; + } } // Picassoは渡されたTargetを弱参照で扱うので、保持しておかないとGCされてしまうことがある @@ -376,10 +336,10 @@ private MediaSessionCompat setupMediaSession(Context context) { // Android 11以降はMediaSessionにMediaMetadataを設定する方法もあるが、 // Android 10には反映されないのと、動的にArtworkを差し替えるのが難しいので、MediaDescriptionAdapterを使う - private MediaDescriptionAdapter createMediaDescriptionAdapter(Context context, + private PlayerNotificationManager.MediaDescriptionAdapter createMediaDescriptionAdapter(Context context, String title, String artist, Boolean isLiveStream, String artworkUrl, String defaultArtworkAssetPath) { - return new MediaDescriptionAdapter() { + return new PlayerNotificationManager.MediaDescriptionAdapter() { @Override public String getCurrentContentTitle(Player player) { return title; @@ -491,10 +451,14 @@ void replaceDataSource(String newDataSource, Map headers) { return; dataSource = newDataSource; - MediaSource mediaSource = buildMediaSource(Uri.parse(newDataSource), resolvingDataSourceFactory, formatHint); + // MediaSource mediaSource = buildMediaSource(Uri.parse(newDataSource), resolvingDataSourceFactory, formatHint); + MediaItem item = new MediaItem.Builder() + .setUri(newDataSource) + .setMimeType(mimeFromFormatHint(formatHint)) + .build(); exoPlayer.stop(); - exoPlayer.setMediaSource(mediaSource); + exoPlayer.setMediaItem(item); exoPlayer.prepare(); } @@ -506,15 +470,15 @@ void sendInitialized() { event.put("event", "initialized"); event.put("duration", exoPlayer.getDuration()); - if (exoPlayer.getVideoFormat() != null) { - Format videoFormat = exoPlayer.getVideoFormat(); - int width = videoFormat.width; - int height = videoFormat.height; - int rotationDegrees = videoFormat.rotationDegrees; + VideoSize videoSize = exoPlayer.getVideoSize(); + int width = videoSize.width; + int height = videoSize.height; + if (width != 0 && height != 0) { + int rotationDegrees = videoSize.unappliedRotationDegrees; // Switch the width/height if video was taken in portrait mode if (rotationDegrees == 90 || rotationDegrees == 270) { - width = exoPlayer.getVideoFormat().height; - height = exoPlayer.getVideoFormat().width; + width = videoSize.height; + height = videoSize.width; } event.put("width", width); event.put("height", height); @@ -556,4 +520,46 @@ void dispose() { exoPlayer.release(); } } + + @NonNull + private static ExoPlayer buildExoPlayer( + Context context, DataSource.Factory baseDataSourceFactory) { + DataSource.Factory dataSourceFactory = + new DefaultDataSource.Factory(context, baseDataSourceFactory); + DefaultMediaSourceFactory mediaSourceFactory = + new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory); + return new ExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build(); + } + + @Nullable + private static String mimeFromFormatHint(@Nullable String formatHint) { + if (formatHint == null) { + return null; + } + switch (formatHint) { + case FORMAT_SS: + return MimeTypes.APPLICATION_SS; + case FORMAT_DASH: + return MimeTypes.APPLICATION_MPD; + case FORMAT_HLS: + return MimeTypes.APPLICATION_M3U8; + case FORMAT_OTHER: + default: + return null; + } + } + + // TODO: migrate to stable API, see https://github.com/flutter/flutter/issues/147039 + @OptIn(markerClass = UnstableApi.class) + private static void unstableUpdateDataSourceFactory( + DefaultHttpDataSource.Factory factory, + @NonNull Map httpHeaders, + String userAgent, + boolean httpHeadersNotEmpty) { + factory.setUserAgent(userAgent).setAllowCrossProtocolRedirects(true); + + if (httpHeadersNotEmpty) { + factory.setDefaultRequestProperties(httpHeaders); + } + } } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index 0f256abad891..903f9256ecc6 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -10,11 +10,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.media3.exoplayer.ExoPlayer; import com.mux.stats.sdk.core.model.CustomData; import com.mux.stats.sdk.core.model.CustomerData; import com.mux.stats.sdk.core.model.CustomerPlayerData; import com.mux.stats.sdk.core.model.CustomerVideoData; -import com.mux.stats.sdk.muxstats.MuxStatsExoPlayer; +import com.mux.stats.sdk.core.model.CustomerViewerData; +import com.mux.stats.sdk.muxstats.MuxStatsSdkMedia3; +import com.mux.stats.sdk.muxstats.ExoPlayerBinding; import io.flutter.FlutterInjector; import io.flutter.Log; import io.flutter.embedding.engine.plugins.FlutterPlugin; @@ -44,13 +47,7 @@ public class VideoPlayerPlugin implements FlutterPlugin, AndroidVideoPlayerApi { private FlutterState flutterState; private final VideoPlayerOptions options = new VideoPlayerOptions(); private String videoSource; - private MuxStatsExoPlayer muxStatsExoPlayer; - - private @NonNull String title = ""; - private @NonNull String artist = ""; - private @NonNull Boolean isLiveStream = false; - private @Nullable String artworkUrl; - private @Nullable String defaultArtworkAssetPath; + private MuxStatsSdkMedia3 muxStats; /** * Register this with the v2 embedding for the plugin to respond to lifecycle @@ -117,7 +114,7 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { } flutterState.stopListening(binding.getBinaryMessenger()); flutterState = null; - initialize(); + onDestroy(); } private void disposeAllPlayers() { @@ -128,8 +125,8 @@ private void disposeAllPlayers() { } private void onDestroy() { - if (muxStatsExoPlayer != null) { - muxStatsExoPlayer.release(); + if (muxStats != null) { + muxStats.release(); } // instances @@ -144,16 +141,24 @@ public void initialize() { disposeAllPlayers(); } + private String title; + private String artist; + private Boolean isLiveStream; + private String artworkUrl; + private String defaultArtworkAssetPath; + public @NonNull TextureMessage create(@NonNull CreateMessage arg) { - TextureRegistry.SurfaceTextureEntry handle = flutterState.textureRegistry.createSurfaceTexture(); - EventChannel eventChannel = new EventChannel( - flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + handle.id()); - - title = arg.getTitle(); - artist = arg.getArtist(); - isLiveStream = arg.getIsLiveStream(); - artworkUrl = arg.getArtworkUrl(); - defaultArtworkAssetPath = arg.getDefaultArtworkAssetPath() != null + TextureRegistry.SurfaceTextureEntry handle = + flutterState.textureRegistry.createSurfaceTexture(); + EventChannel eventChannel = + new EventChannel( + flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + handle.id()); + + String title = arg.getTitle(); + String artist = arg.getArtist(); + Boolean isLiveStream = arg.getIsLiveStream(); + String artworkUrl = arg.getArtworkUrl(); + String defaultArtworkAssetPath = arg.getDefaultArtworkAssetPath() != null ? flutterState.keyForAsset.get(arg.getDefaultArtworkAssetPath()) : null; @@ -165,14 +170,15 @@ public void initialize() { } else { assetLookupKey = flutterState.keyForAsset.get(arg.getAsset()); } - player = new VideoPlayer( - flutterState.applicationContext, - eventChannel, - handle, - "asset:///" + assetLookupKey, - null, - new HashMap<>(), - options); + player = + new VideoPlayer( + flutterState.applicationContext, + eventChannel, + handle, + "asset:///" + assetLookupKey, + null, + new HashMap<>(), + options); } else { Map httpHeaders = arg.getHttpHeaders(); player = new VideoPlayer( @@ -193,6 +199,7 @@ public void setupMux(MuxConfigMessage arg) { VideoPlayer player = videoPlayers.get(arg.getTextureId()); CustomerPlayerData playerData = new CustomerPlayerData(); CustomerVideoData videoData = new CustomerVideoData(); + CustomerViewerData viewerData = new CustomerViewerData(); CustomData customData = new CustomData(); CustomerData customerData = new CustomerData(); @@ -249,19 +256,26 @@ public void setupMux(MuxConfigMessage arg) { if (arg.getVideoEncodingVariant() != null) videoData.setVideoEncodingVariant(arg.getVideoEncodingVariant()); - if (arg.getVideoCdn() != null) - videoData.setVideoCdn(arg.getVideoCdn()); - if (arg.getVideoDuration() != null) { videoData.setVideoDuration(castVideoDuration(arg.getVideoDuration())); } + if (arg.getViewerPlanStatus() != null) { + viewerData.setViewerPlanStatus(arg.getViewerPlanStatus()); + } + customerData.setCustomerVideoData(videoData); customerData.setCustomerPlayerData(playerData); + customerData.setCustomerViewerData(viewerData); customerData.setCustomData(customData); - muxStatsExoPlayer = new MuxStatsExoPlayer(flutterState.applicationContext, player.exoPlayer, - arg.getEnvKey(), customerData); + muxStats = new MuxStatsSdkMedia3( + flutterState.applicationContext, + arg.getEnvKey(), + customerData, + player.exoPlayer, + new ExoPlayerBinding() + ); } public void dispose(@NonNull TextureMessage arg) { diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java index 2ba6ee053885..a7a03e9e1ed9 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java @@ -5,22 +5,25 @@ package io.flutter.plugins.videoplayer; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.any; +import static org.mockito.Mockito.*; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; +import android.graphics.SurfaceTexture; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.Player; +import androidx.media3.common.VideoSize; +import androidx.media3.datasource.DefaultHttpDataSource; +import androidx.media3.exoplayer.ExoPlayer; import io.flutter.plugin.common.EventChannel; import io.flutter.view.TextureRegistry; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; @@ -36,6 +39,7 @@ public class VideoPlayerTest { private ExoPlayer fakeExoPlayer; private EventChannel fakeEventChannel; private TextureRegistry.SurfaceTextureEntry fakeSurfaceTextureEntry; + private SurfaceTexture fakeSurfaceTexture; private VideoPlayerOptions fakeVideoPlayerOptions; private QueuingEventSink fakeEventSink; private DefaultHttpDataSource.Factory httpDataSourceFactorySpy; @@ -49,6 +53,8 @@ public void before() { fakeExoPlayer = mock(ExoPlayer.class); fakeEventChannel = mock(EventChannel.class); fakeSurfaceTextureEntry = mock(TextureRegistry.SurfaceTextureEntry.class); + fakeSurfaceTexture = mock(SurfaceTexture.class); + when(fakeSurfaceTextureEntry.surfaceTexture()).thenReturn(fakeSurfaceTexture); fakeVideoPlayerOptions = mock(VideoPlayerOptions.class); fakeEventSink = mock(QueuingEventSink.class); httpDataSourceFactorySpy = spy(new DefaultHttpDataSource.Factory()); @@ -65,7 +71,7 @@ public void videoPlayer_buildsHttpDataSourceFactoryProperlyWhenHttpHeadersNull() fakeEventSink, httpDataSourceFactorySpy); - videoPlayer.buildHttpDataSourceFactory(new HashMap<>()); + videoPlayer.configureHttpDataSourceFactory(new HashMap<>()); verify(httpDataSourceFactorySpy).setUserAgent("ExoPlayer"); verify(httpDataSourceFactorySpy).setAllowCrossProtocolRedirects(true); @@ -91,7 +97,7 @@ public void videoPlayer_buildsHttpDataSourceFactoryProperlyWhenHttpHeadersNull() } }; - videoPlayer.buildHttpDataSourceFactory(httpHeaders); + videoPlayer.configureHttpDataSourceFactory(httpHeaders); verify(httpDataSourceFactorySpy).setUserAgent("userAgent"); verify(httpDataSourceFactorySpy).setAllowCrossProtocolRedirects(true); @@ -116,7 +122,7 @@ public void videoPlayer_buildsHttpDataSourceFactoryProperlyWhenHttpHeadersNull() } }; - videoPlayer.buildHttpDataSourceFactory(httpHeaders); + videoPlayer.configureHttpDataSourceFactory(httpHeaders); verify(httpDataSourceFactorySpy).setUserAgent("ExoPlayer"); verify(httpDataSourceFactorySpy).setAllowCrossProtocolRedirects(true); @@ -133,10 +139,9 @@ public void sendInitializedSendsExpectedEvent_90RotationDegrees() { fakeVideoPlayerOptions, fakeEventSink, httpDataSourceFactorySpy); - Format testFormat = - new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(90).build(); + VideoSize testVideoSize = new VideoSize(100, 200, 90, 1f); - when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getVideoSize()).thenReturn(testVideoSize); when(fakeExoPlayer.getDuration()).thenReturn(10L); videoPlayer.isInitialized = true; @@ -162,10 +167,9 @@ public void sendInitializedSendsExpectedEvent_270RotationDegrees() { fakeVideoPlayerOptions, fakeEventSink, httpDataSourceFactorySpy); - Format testFormat = - new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(270).build(); + VideoSize testVideoSize = new VideoSize(100, 200, 270, 1f); - when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getVideoSize()).thenReturn(testVideoSize); when(fakeExoPlayer.getDuration()).thenReturn(10L); videoPlayer.isInitialized = true; @@ -191,10 +195,9 @@ public void sendInitializedSendsExpectedEvent_0RotationDegrees() { fakeVideoPlayerOptions, fakeEventSink, httpDataSourceFactorySpy); - Format testFormat = - new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(0).build(); + VideoSize testVideoSize = new VideoSize(100, 200, 0, 1f); - when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getVideoSize()).thenReturn(testVideoSize); when(fakeExoPlayer.getDuration()).thenReturn(10L); videoPlayer.isInitialized = true; @@ -220,10 +223,9 @@ public void sendInitializedSendsExpectedEvent_180RotationDegrees() { fakeVideoPlayerOptions, fakeEventSink, httpDataSourceFactorySpy); - Format testFormat = - new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(180).build(); + VideoSize testVideoSize = new VideoSize(100, 200, 180, 1f); - when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getVideoSize()).thenReturn(testVideoSize); when(fakeExoPlayer.getDuration()).thenReturn(10L); videoPlayer.isInitialized = true; @@ -278,4 +280,28 @@ public void onIsPlayingChangedSendsExpectedEvent() { assertEquals(event2.get("event"), "isPlayingStateUpdate"); assertEquals(event2.get("isPlaying"), false); } + + @Test + public void behindLiveWindowErrorResetsPlayerToDefaultPosition() { + List listeners = new LinkedList<>(); + doAnswer(invocation -> listeners.add(invocation.getArgument(0))) + .when(fakeExoPlayer) + .addListener(any()); + + VideoPlayer unused = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink, + httpDataSourceFactorySpy); + + PlaybackException exception = + new PlaybackException(null, null, PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW); + listeners.forEach(listener -> listener.onPlayerError(exception)); + + verify(fakeExoPlayer).seekToDefaultPosition(); + verify(fakeExoPlayer).prepare(); + } } diff --git a/packages/video_player/video_player_android/example/android/app/build.gradle b/packages/video_player/video_player_android/example/android/app/build.gradle index 6862f8c9b7c1..697bde16c079 100644 --- a/packages/video_player/video_player_android/example/android/app/build.gradle +++ b/packages/video_player/video_player_android/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { namespace 'io.flutter.plugins.videoplayerexample' - compileSdkVersion flutter.compileSdkVersion + compileSdk flutter.compileSdkVersion compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 31cca4913088..609ab8e6c8b5 100644 --- a/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java index 0f0c1464e0ee..21923b546982 100644 --- a/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java +++ b/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -13,7 +13,7 @@ * Annotation to aid repository tooling in determining if a test is * a native java unit test or a java class with a dart integration. * - * See: https://github.com/flutter/flutter/wiki/Plugin-Tests#enabling-android-ui-tests + * See: https://github.com/flutter/flutter/blob/master/docs/ecosystem/testing/Plugin-Tests.md#enabling-android-ui-tests * for more infomation. */ @Retention(RetentionPolicy.RUNTIME) diff --git a/packages/video_player/video_player_android/example/android/app/src/test/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java b/packages/video_player/video_player_android/example/android/app/src/test/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java index 434861f4b754..750d4a486dda 100644 --- a/packages/video_player/video_player_android/example/android/app/src/test/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java +++ b/packages/video_player/video_player_android/example/android/app/src/test/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java @@ -45,6 +45,6 @@ public void disposeAllPlayers() { engine.destroy(); verify(videoPlayerPlugin, times(1)).onDetachedFromEngine(pluginBindingCaptor.capture()); - verify(videoPlayerPlugin, times(1)).initialize(); + verify(videoPlayerPlugin, times(1)).onDestroy(); } } diff --git a/packages/video_player/video_player_android/example/android/build.gradle b/packages/video_player/video_player_android/example/android/build.gradle index bc26b58842ca..d82420470065 100644 --- a/packages/video_player/video_player_android/example/android/build.gradle +++ b/packages/video_player/video_player_android/example/android/build.gradle @@ -11,6 +11,12 @@ buildscript { allprojects { repositories { + // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. + def artifactRepoKey = 'ARTIFACT_HUB_REPOSITORY' + if (System.getenv().containsKey(artifactRepoKey)) { + println "Using artifact hub" + maven { url System.getenv(artifactRepoKey) } + } google() mavenCentral() } @@ -39,9 +45,9 @@ gradle.projectsEvaluated { // Workaround for several warnings when building // that the above turns into errors, coming from - // org.checkerframework.checker.nullness.qual and + // org.checkerframework.checker.nullness.qual and // com.google.errorprone.annotations: - // + // // warning: Cannot find annotation method 'value()' in type // 'EnsuresNonNull': class file for // org.checkerframework.checker.nullness.qual.EnsuresNonNull not found diff --git a/packages/video_player/video_player_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player_android/example/android/gradle/wrapper/gradle-wrapper.properties index cfe88f6904c9..aeaff6f869f3 100644 --- a/packages/video_player/video_player_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/video_player/video_player_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/packages/video_player/video_player_android/example/android/settings.gradle b/packages/video_player/video_player_android/example/android/settings.gradle index 115da6cb4f4d..e54a7e1fd6e5 100644 --- a/packages/video_player/video_player_android/example/android/settings.gradle +++ b/packages/video_player/video_player_android/example/android/settings.gradle @@ -13,3 +13,16 @@ plugins.each { name, path -> include ":$name" project(":$name").projectDir = pluginDirectory } + +// See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. +buildscript { + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" + } +} +apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" diff --git a/packages/video_player/video_player_android/example/lib/main.dart b/packages/video_player/video_player_android/example/lib/main.dart index 38ad5aabaeed..df28b39ac76b 100644 --- a/packages/video_player/video_player_android/example/lib/main.dart +++ b/packages/video_player/video_player_android/example/lib/main.dart @@ -181,9 +181,9 @@ class _ControlsOverlay extends StatelessWidget { reverseDuration: const Duration(milliseconds: 200), child: controller.value.isPlaying ? const SizedBox.shrink() - : Container( + : const ColoredBox( color: Colors.black26, - child: const Center( + child: Center( child: Icon( Icons.play_arrow, color: Colors.white, diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 8bfc77279aaf..a38013533d62 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -222,25 +222,21 @@ class MiniController extends ValueNotifier { asset: dataSource, package: package, ); - break; case DataSourceType.network: dataSourceDescription = DataSource( sourceType: DataSourceType.network, uri: dataSource, ); - break; case DataSourceType.file: dataSourceDescription = DataSource( sourceType: DataSourceType.file, uri: dataSource, ); - break; case DataSourceType.contentUri: dataSourceDescription = DataSource( sourceType: DataSourceType.contentUri, uri: dataSource, ); - break; } _textureId = (await _platform.create(dataSourceDescription)) ?? @@ -260,26 +256,16 @@ class MiniController extends ValueNotifier { _platform.setVolume(_textureId, 1.0); _platform.setLooping(_textureId, true); _applyPlayPause(); - break; case VideoEventType.completed: pause().then((void pauseResult) => seekTo(value.duration)); - break; case VideoEventType.bufferingUpdate: value = value.copyWith(buffered: event.buffered); - break; case VideoEventType.bufferingStart: value = value.copyWith(isBuffering: true); - break; case VideoEventType.bufferingEnd: value = value.copyWith(isBuffering: false); - break; case VideoEventType.isPlayingStateUpdate: value = value.copyWith(isPlaying: event.isPlaying); - break; - case VideoEventType.play: - case VideoEventType.pause: - case VideoEventType.startedPictureInPicture: - case VideoEventType.stoppedPictureInPicture: case VideoEventType.unknown: break; } diff --git a/packages/video_player/video_player_android/example/pubspec.yaml b/packages/video_player/video_player_android/example/pubspec.yaml index 5cbf8770c217..7fb8c8751653 100644 --- a/packages/video_player/video_player_android/example/pubspec.yaml +++ b/packages/video_player/video_player_android/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the video_player plugin. publish_to: none environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" + sdk: ^3.4.0 + flutter: ">=3.22.0" dependencies: flutter: diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index 69b93e9378c0..39e7b3619549 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -53,7 +53,6 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { break; case DataSourceType.contentUri: uri = dataSource.uri; - break; } final CreateMessage message = CreateMessage( asset: asset, @@ -98,6 +97,7 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { message.videoProducer = config.videoProducer; message.videoEncodingVariant = config.videoEncodingVariant; message.videoCdn = config.videoCdn; + message.viewerPlanStatus = config.viewerPlanStatus; return _api.setupMux(message); } diff --git a/packages/video_player/video_player_android/lib/src/messages.g.dart b/packages/video_player/video_player_android/lib/src/messages.g.dart index caf764f87410..53e4c60a1311 100644 --- a/packages/video_player/video_player_android/lib/src/messages.g.dart +++ b/packages/video_player/video_player_android/lib/src/messages.g.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer, debugPrint; import 'package:flutter/services.dart'; class TextureMessage { @@ -292,6 +292,7 @@ class MuxConfigMessage { this.videoProducer, this.videoEncodingVariant, this.videoCdn, + this.viewerPlanStatus, }); int? textureId; @@ -336,6 +337,8 @@ class MuxConfigMessage { String? videoCdn; + String? viewerPlanStatus; + Object encode() { return [ textureId, @@ -359,6 +362,7 @@ class MuxConfigMessage { videoProducer, videoEncodingVariant, videoCdn, + viewerPlanStatus, ]; } @@ -386,6 +390,7 @@ class MuxConfigMessage { videoProducer: result[18] as String?, videoEncodingVariant: result[19] as String?, videoCdn: result[20] as String?, + viewerPlanStatus: result[21] as String?, ); } } @@ -467,8 +472,7 @@ class AndroidVideoPlayerApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize', codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; + final List? replyList = await channel.send(null) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', diff --git a/packages/video_player/video_player_android/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart index df3f38461a55..c6f42d0b2122 100644 --- a/packages/video_player/video_player_android/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -94,6 +94,7 @@ class MuxConfigMessage { String? videoProducer; String? videoEncodingVariant; String? videoCdn; + String? viewerPlanStatus; } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 1472735e8551..e33b6aad6230 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,11 +2,11 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.4.9 +version: 2.5.0 environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" + sdk: ^3.4.0 + flutter: ">=3.22.0" flutter: plugin: @@ -32,3 +32,7 @@ dev_dependencies: dependency_overrides: video_player_platform_interface: path: ../../video_player/video_player_platform_interface + +topics: + - video + - video-player diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index d30d1d4faaa9..4ea1d4821001 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -2,10 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) -// ignore: unnecessary_import -import 'dart:ui'; - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:video_player_android/src/messages.g.dart'; @@ -247,16 +243,15 @@ void main() { test('videoEventsFor', () async { const String mockChannel = 'flutter.io/videoPlayer/videoEvents123'; - _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMessageHandler( mockChannel, (ByteData? message) async { final MethodCall methodCall = const StandardMethodCodec().decodeMethodCall(message); if (methodCall.method == 'listen') { - await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger .handlePlatformMessage( mockChannel, const StandardMethodCodec() @@ -268,8 +263,8 @@ void main() { }), (ByteData? data) {}); - await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger .handlePlatformMessage( mockChannel, const StandardMethodCodec() @@ -282,8 +277,8 @@ void main() { }), (ByteData? data) {}); - await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger .handlePlatformMessage( mockChannel, const StandardMethodCodec() @@ -292,8 +287,8 @@ void main() { }), (ByteData? data) {}); - await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger .handlePlatformMessage( mockChannel, const StandardMethodCodec() @@ -306,8 +301,8 @@ void main() { }), (ByteData? data) {}); - await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger .handlePlatformMessage( mockChannel, const StandardMethodCodec() @@ -316,8 +311,8 @@ void main() { }), (ByteData? data) {}); - await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger .handlePlatformMessage( mockChannel, const StandardMethodCodec() @@ -326,8 +321,8 @@ void main() { }), (ByteData? data) {}); - await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger .handlePlatformMessage( mockChannel, const StandardMethodCodec() @@ -337,8 +332,8 @@ void main() { }), (ByteData? data) {}); - await _ambiguate(TestDefaultBinaryMessengerBinding.instance)! - .defaultBinaryMessenger + await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger .handlePlatformMessage( mockChannel, const StandardMethodCodec() @@ -398,9 +393,3 @@ void main() { }); }); } - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; diff --git a/packages/video_player/video_player_android/test/test_api.g.dart b/packages/video_player/video_player_android/test/test_api.g.dart index 48811d68ed56..c0dca5912887 100644 --- a/packages/video_player/video_player_android/test/test_api.g.dart +++ b/packages/video_player/video_player_android/test/test_api.g.dart @@ -77,7 +77,8 @@ class _TestHostVideoPlayerApiCodec extends StandardMessageCodec { } abstract class TestHostVideoPlayerApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; static const MessageCodec codec = _TestHostVideoPlayerApiCodec(); void initialize(); @@ -112,9 +113,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.initialize', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { // ignore message api.initialize(); return []; @@ -126,9 +130,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create was null.'); final List args = (message as List?)!; @@ -145,9 +152,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose was null.'); final List args = (message as List?)!; @@ -164,9 +174,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setLooping was null.'); final List args = (message as List?)!; @@ -183,9 +196,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setVolume was null.'); final List args = (message as List?)!; @@ -202,9 +218,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setPlaybackSpeed was null.'); final List args = (message as List?)!; @@ -221,9 +240,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.play was null.'); final List args = (message as List?)!; @@ -240,9 +262,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.position was null.'); final List args = (message as List?)!; @@ -259,9 +284,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekTo was null.'); final List args = (message as List?)!; @@ -278,9 +306,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause was null.'); final List args = (message as List?)!; @@ -297,9 +328,12 @@ abstract class TestHostVideoPlayerApi { 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.setMixWithOthers was null.'); final List args = (message as List?)!; diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 7dd2eab920d0..794dcf9c215e 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -839,10 +839,22 @@ - (void)setupMux:(FLTMuxConfigMessage*)input error:(FlutterError**)error { // videoData.videoEncodingVariant = input.videoEncodingVariant; // videoData.videoCdn = input.videoCdn; + MUXSDKCustomerViewerData* viewerData = [MUXSDKCustomerViewerData new]; + viewerData.viewerPlanStatus = input.viewerPlanStatus; // 視聴プラン OneTimePlan or SubscriptionPlan + + // MUXSDKCustomerData* customerData = [MUXSDKCustomerData new]; + // customerData.customerPlayerData = playerData; + // customerData.customerVideoData = videoData; + // customerData.customerViewerData = viewerData; + MUXSDKCustomerData *customerData = [[MUXSDKCustomerData alloc] initWithCustomerPlayerData:playerData + videoData:videoData + viewData:nil + customData:nil + viewerData:viewerData]; + [MUXSDKStats monitorAVPlayerViewController:playerViewController withPlayerName:input.playerName - playerData:playerData - videoData:videoData + customerData:customerData ]; } diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h index 696092927f07..40a5b2eabdf8 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h +++ b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h @@ -124,7 +124,8 @@ NS_ASSUME_NONNULL_BEGIN videoStreamType:(nullable NSString *)videoStreamType videoProducer:(nullable NSString *)videoProducer videoEncodingVariant:(nullable NSString *)videoEncodingVariant - videoCdn:(nullable NSString *)videoCdn; + videoCdn:(nullable NSString *)videoCdn + viewerPlanStatus:(nullable NSString *)viewerPlanStatus; @property(nonatomic, strong, nullable) NSNumber * textureId; @property(nonatomic, copy, nullable) NSString * envKey; @property(nonatomic, copy, nullable) NSString * playerName; @@ -146,6 +147,7 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, copy, nullable) NSString * videoProducer; @property(nonatomic, copy, nullable) NSString * videoEncodingVariant; @property(nonatomic, copy, nullable) NSString * videoCdn; +@property(nonatomic, copy, nullable) NSString * viewerPlanStatus; @end @interface FLTAutomaticallyStartsPictureInPictureMessage : NSObject diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m index 9e44a68aa82e..84559e54f081 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m @@ -347,7 +347,8 @@ + (instancetype)makeWithTextureId:(nullable NSNumber *)textureId videoStreamType:(nullable NSString *)videoStreamType videoProducer:(nullable NSString *)videoProducer videoEncodingVariant:(nullable NSString *)videoEncodingVariant - videoCdn:(nullable NSString *)videoCdn { + videoCdn:(nullable NSString *)videoCdn + viewerPlanStatus:(nullable NSString *)viewerPlanStatus { FLTMuxConfigMessage* pigeonResult = [[FLTMuxConfigMessage alloc] init]; pigeonResult.textureId = textureId; pigeonResult.envKey = envKey; @@ -370,6 +371,7 @@ + (instancetype)makeWithTextureId:(nullable NSNumber *)textureId pigeonResult.videoProducer = videoProducer; pigeonResult.videoEncodingVariant = videoEncodingVariant; pigeonResult.videoCdn = videoCdn; + pigeonResult.viewerPlanStatus = viewerPlanStatus; return pigeonResult; } + (FLTMuxConfigMessage *)fromList:(NSArray *)list { @@ -395,6 +397,7 @@ + (FLTMuxConfigMessage *)fromList:(NSArray *)list { pigeonResult.videoProducer = GetNullableObjectAtIndex(list, 18); pigeonResult.videoEncodingVariant = GetNullableObjectAtIndex(list, 19); pigeonResult.videoCdn = GetNullableObjectAtIndex(list, 20); + pigeonResult.viewerPlanStatus = GetNullableObjectAtIndex(list, 21); return pigeonResult; } + (nullable FLTMuxConfigMessage *)nullableFromList:(NSArray *)list { @@ -423,6 +426,7 @@ - (NSArray *)toList { (self.videoProducer ?: [NSNull null]), (self.videoEncodingVariant ?: [NSNull null]), (self.videoCdn ?: [NSNull null]), + (self.viewerPlanStatus ?: [NSNull null]), ]; } @end diff --git a/packages/video_player/video_player_avfoundation/ios/video_player_avfoundation.podspec b/packages/video_player/video_player_avfoundation/ios/video_player_avfoundation.podspec index 61d237ce7481..a31351a8f926 100644 --- a/packages/video_player/video_player_avfoundation/ios/video_player_avfoundation.podspec +++ b/packages/video_player/video_player_avfoundation/ios/video_player_avfoundation.podspec @@ -16,7 +16,7 @@ Downloaded by pub (not CocoaPods). s.documentation_url = 'https://pub.dev/packages/video_player' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' - s.dependency 'Mux-Stats-AVPlayer', '~>3.0' + s.dependency 'Mux-Stats-AVPlayer', '~>4.0' s.dependency 'Flutter' s.platform = :ios, '11.0' diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index ea2d1ce0a22b..ee0c745f35d3 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -55,6 +55,7 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { // message.videoProducer = config.videoProducer; // message.videoEncodingVariant = config.videoEncodingVariant; // message.videoCdn = config.videoCdn; + message.viewerPlanStatus = config.viewerPlanStatus; return _api.setupMux(message); } diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart index d52e5c829b42..baef5320f59d 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart @@ -246,6 +246,7 @@ class MuxConfigMessage { this.videoProducer, this.videoEncodingVariant, this.videoCdn, + this.viewerPlanStatus, }); int? textureId; @@ -290,6 +291,8 @@ class MuxConfigMessage { String? videoCdn; + String? viewerPlanStatus; + Object encode() { return [ textureId, @@ -313,6 +316,7 @@ class MuxConfigMessage { videoProducer, videoEncodingVariant, videoCdn, + viewerPlanStatus, ]; } @@ -340,6 +344,7 @@ class MuxConfigMessage { videoProducer: result[18] as String?, videoEncodingVariant: result[19] as String?, videoCdn: result[20] as String?, + viewerPlanStatus: result[21] as String?, ); } } diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index 4c1bd1375588..de4c7e3ee1db 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -91,6 +91,7 @@ class MuxConfigMessage { String? videoProducer; String? videoEncodingVariant; String? videoCdn; + String? viewerPlanStatus; } class AutomaticallyStartsPictureInPictureMessage { diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 3b0c83354e0a..5b845da31089 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -468,6 +468,7 @@ class MuxConfig { this.videoProducer, this.videoEncodingVariant, this.videoCdn, + this.viewerPlanStatus, }); final String envKey; @@ -490,6 +491,7 @@ class MuxConfig { final String? videoProducer; final String? videoEncodingVariant; final String? videoCdn; + final String? viewerPlanStatus; } enum MuxVideoStreamType {