lib/aliplayer_widget_controller.dart (438 lines of code) (raw):

// Copyright © 2025 Alibaba Cloud. All rights reserved. // // Author: keria // Date: 2025/2/10 // Brief: Player widget controller, used to manage the initialization, playback, and destruction logic of Alibaba Cloud Player part of 'aliplayer_widget_lib.dart'; /// 播放器组件控制器,用于管理阿里云播放器的初始化、播放和销毁逻辑 /// /// Player widget controller, used to manage the initialization, playback, /// and destruction logic of Alibaba Cloud Player class AliPlayerWidgetController { /// 日志标签 static const _logTag = "AliPlayerWidgetController"; final BuildContext _context; /// 播放器实例 late FlutterAliplayer _aliPlayer; /// 播放器唯一标识符 late String _playerUniqueId; /// 是否已准备播放 bool _isPrepared = false; /// 播放数据源准备开始时间 int _prepareStartTime = 0; /// 渲染状态变化通知器 /// /// Value notifier for rendering status final SafeValueNotifier<bool> isRenderedNotifier = SafeValueNotifier(false); /// 视频尺寸变化通知器 /// /// Value notifier for video size final SafeValueNotifier<Size> videoSizeNotifier = SafeValueNotifier(Size.zero); /// 播放状态变化通知器 /// /// Value notifier for playback status final SafeValueNotifier<int> playStateNotifier = SafeValueNotifier(FlutterAvpdef.unknow); /// 播放错误信息变化通知器 /// /// Value notifier for playback error final SafeValueNotifier<Map<int?, String?>?> playErrorNotifier = SafeValueNotifier(null); /// 播放进度变化通知器 /// /// Value notifier for current position final SafeValueNotifier<Duration> currentPositionNotifier = SafeValueNotifier(Duration.zero); /// 播放缓冲进度变化通知器 /// /// Value notifier for buffered position final SafeValueNotifier<Duration> bufferedPositionNotifier = SafeValueNotifier(Duration.zero); /// 播放总时长变化通知器 /// /// Value notifier for total duration final SafeValueNotifier<Duration> totalDurationNotifier = SafeValueNotifier(Duration.zero); /// 播放亮度变化通知器 /// /// Value notifier for brightness final SafeValueNotifier<double> brightnessNotifier = SafeValueNotifier(SettingConstants.defaultBrightness); /// 播放音量变化通知器 /// /// Value notifier for volume final SafeValueNotifier<double> volumeNotifier = SafeValueNotifier(SettingConstants.defaultVolume); /// 播放速度变化通知器 /// /// Value notifier for speed final SafeValueNotifier<double> speedNotifier = SafeValueNotifier(SettingConstants.defaultSpeed); /// 循环播放变化通知器 /// /// Value notifier for loop playback final SafeValueNotifier<bool> isLoopNotifier = SafeValueNotifier(SettingConstants.defaultIsLoop); /// 静音播放变化通知器 /// /// Value notifier for mute playback final SafeValueNotifier<bool> isMuteNotifier = SafeValueNotifier(SettingConstants.defaultIsMute); /// 镜像模式变化通知器 /// /// Value notifier for mirror mode final SafeValueNotifier<int> mirrorModeNotifier = SafeValueNotifier(SettingConstants.defaultMirrorMode); /// 旋转角度变化通知器 /// /// Value notifier for rotate mode final SafeValueNotifier<int> rotateModeNotifier = SafeValueNotifier(SettingConstants.defaultRotateMode); /// 渲染模式变化通知器 /// /// Value notifier for render mode final SafeValueNotifier<int> scaleModeNotifier = SafeValueNotifier(SettingConstants.defaultScaleMode); /// 播放清晰度变化通知器 /// /// Value notifier for track info final SafeValueNotifier<AVPTrackInfo?> currentTrackInfoNotifier = SafeValueNotifier(null); /// 播放清晰度信息列表变化通知器 /// /// Value notifier for track info list final SafeValueNotifier<List<AVPTrackInfo>?> trackInfoListNotifier = SafeValueNotifier(null); /// 缩略图变化通知器 /// /// Value notifier for thumbnail final SafeValueNotifier<MemoryImage?> thumbnailNotifier = SafeValueNotifier(null); /// 缩略图是否获取成功 bool _thumbnailSuccess = false; /// 播放数据源 AliPlayerWidgetData? _widgetData; /// 播放器组件控制器构造函数,用于创建 [AliPlayerWidgetController] 实例。 /// /// Constructor to create an instance of [AliPlayerWidgetController]. /// /// 参数: /// - [_context]:当前 Widget 的上下文,必须提供,用于初始化控制器并与 UI 层交互。 /// /// Parameters: /// - _context: The context of the current widget, required for initializing the controller and interacting with the UI layer. AliPlayerWidgetController(this._context) { logi("[lifecycle] construct", tag: _logTag); _init(); } /// 初始化操作 void _init() { // 初始化全局配置 AliPlayerWidgetGlobalSetting.setupConfig(); // 初始化播放器实例 _initializePlayer(); // 设置默认值 _batchLoadDefaultValues(); } /// 销毁播放控制器 /// /// Destroy the player controller void destroy() { // 销毁播放器实例 Future.microtask(() => _destroyPlayer()); // 销毁值监听器 Future.microtask(() => _disposeValueNotifiers()); } /// 初始化播放器实例 /// /// initialize the player void _initializePlayer() { logi("Initializing player"); /// 1、创建播放器 _aliPlayer = FlutterAliPlayerFactory.createAliPlayer(); _playerUniqueId = _aliPlayer.playerId; _playerLog("[api][lifecycle][create]"); /// 2、设置播放器事件回调 // 设置播放器事件回调,准备完成事件 _aliPlayer.setOnPrepared((String playerId) { _isPrepared = true; int cost = DateTime.now().millisecondsSinceEpoch - _prepareStartTime; _playerLog("[cbk][onPrepared]: costTime: $cost"); // 创建清晰度信息 Future.microtask(() => _createTrackInfoWhenPrepared()); // 创建缩略图(方式1) Future.microtask(() => _createThumbnailWhenPrepared()); }); // 设置播放器事件回调,首帧显示事件 _aliPlayer.setOnRenderingStart((String playerId) async { int cost = DateTime.now().millisecondsSinceEpoch - _prepareStartTime; _playerLog("[cbk][renderingStart]: costTime: $cost"); // 更新渲染状态 isRenderedNotifier.value = true; final totalDuration = await _aliPlayer.getDuration(); if (totalDuration == 0) { return; } totalDurationNotifier.value = Duration(milliseconds: totalDuration); }); // 设置播放器事件回调,播放完成事件 _aliPlayer.setOnCompletion((String playerId) { _isPrepared = false; _prepareStartTime = 0; }); // 设置视频大小变化回调 _aliPlayer.setOnVideoSizeChanged(( int width, int height, int? rotation, String playerId, ) async { _playerLog("[cbk][videoSizeChanged]: $width, $height, $rotation"); // 更新视频尺寸 _updateVideoSize(); }); // 设置视频当前播放位置回调 _aliPlayer.setOnInfo( (int? infoCode, int? extraValue, String? extraMsg, String playerId) { if (infoCode == 1) { // _playerLog("[cbk][bufferedPosition]: $extraValue, $extraMsg"); final bufferedPosition = extraValue ?? 0; bufferedPositionNotifier.value = Duration(milliseconds: bufferedPosition); } else if (infoCode == 2) { // _playerLog("[cbk][currentPosition]: $extraValue, $extraMsg"); final currentPosition = extraValue ?? 0; currentPositionNotifier.value = Duration(milliseconds: currentPosition); } }); // 设置获取 track 信息回调 _aliPlayer.setOnTrackReady((String playerId) async { _playerLog("[cbk][setOnTrackReady]"); // 更新视频尺寸 _updateVideoSize(); // 创建清晰度信息 Future.microtask(() => _createTrackInfoWhenPrepared()); // 创建缩略图(方式2) Future.microtask(() => _createThumbnailWhenTrackReady()); }); // 设置track切换完成回调 _aliPlayer.setOnTrackChanged((dynamic value, String playerId) { final trackInfoList = trackInfoListNotifier.value; final selectedTrackInfo = TrackInfoUtil.getTrackInfoByIndex( trackInfoList, value["trackIndex"], ); // 如果没有找到对应的清晰度信息,则不处理 if (selectedTrackInfo == null) { return; } // 更新当前清晰度信息 currentTrackInfoNotifier.value = selectedTrackInfo; // 显示提示信息 String quality = TrackInfoUtil.getQuality(selectedTrackInfo); SnackBarUtil.success(_context, "selectTrack: $quality"); }); // 设置错误代理回调 _aliPlayer.setOnError(( int errorCode, String? errorExtra, String? errorMsg, String playerId, ) { _playerLog("[cbk][error]: errorCode: $errorCode, errorMsg: $errorMsg"); playErrorNotifier.value = {errorCode: errorMsg}; // If occurs error, update video size as default. if (videoSizeNotifier.value == Size.zero) { final videoSize = ScreenUtil.calculateDefaultDimensions(_context); videoSizeNotifier.value = videoSize; } }); // 设置播放器状态改变回调 _aliPlayer.setOnStateChanged((int newState, String playerId) { final oldState = playStateNotifier.value; _playerLog("[cbk][stateChanged]: state: $oldState->$newState"); // Update play state playStateNotifier.value = newState; }); // 设置缩略图代理回调 _aliPlayer.setOnThumbnailPreparedListener( preparedSuccess: (playerId) { _playerLog("[cbk][thumbnailPrepared]: preparedSuccess, $playerId"); _thumbnailSuccess = true; }, preparedFail: (playerId) { _playerLog("[cbk][thumbnailPrepared]: preparedFail, $playerId"); _thumbnailSuccess = false; }, ); // 设置缩略图获取代理回调 _aliPlayer.setOnThumbnailGetListener( onThumbnailGetSuccess: (bitmap, range, playerId) { var provider = MemoryImage(bitmap); thumbnailNotifier.value = provider; }, onThumbnailGetFail: (playerId) {}, ); /// 3. 其它配置 // IPlayer.ScaleMode.SCALE_ASPECT_FILL _aliPlayer.setScalingMode(1); logi("Player initialization completed."); } /// 销毁播放器实例 /// /// Destroy the player void _destroyPlayer() { logi("destroy player"); // 暂停播放 pause(); // 停止播放 stop(); // 异步释放播放器 _playerLog("[api][lifecycle][releaseAsync]"); _aliPlayer.releaseAsync(); // 重置播放状态 _isPrepared = false; _prepareStartTime = 0; // 重置渲染状态 isRenderedNotifier.value = false; logi("Player destroyed."); } /// 销毁 ValueNotifier void _disposeValueNotifiers() { isRenderedNotifier.dispose(); videoSizeNotifier.dispose(); playStateNotifier.dispose(); playErrorNotifier.dispose(); currentPositionNotifier.dispose(); bufferedPositionNotifier.dispose(); totalDurationNotifier.dispose(); brightnessNotifier.dispose(); volumeNotifier.dispose(); speedNotifier.dispose(); isLoopNotifier.dispose(); isMuteNotifier.dispose(); mirrorModeNotifier.dispose(); rotateModeNotifier.dispose(); scaleModeNotifier.dispose(); currentTrackInfoNotifier.dispose(); trackInfoListNotifier.dispose(); thumbnailNotifier.dispose(); } /// 设置默认值 Future<void> _batchLoadDefaultValues() async { // 核心任务 // final coreResults = await Future.wait([]); // 非核心任务 Future(() async { // TODO keria: brightness feature to be implemented double brightness = SettingConstants.defaultBrightness; brightnessNotifier.value = brightness; final nonCoreResults = await Future.wait([ _aliPlayer.getVolume(), _aliPlayer.getRate(), _aliPlayer.isLoop(), _aliPlayer.isMuted(), _aliPlayer.getMirrorMode(), _aliPlayer.getRotateMode(), _aliPlayer.getScalingMode(), ]); volumeNotifier.value = nonCoreResults[0]; speedNotifier.value = nonCoreResults[1]; isLoopNotifier.value = nonCoreResults[2]; isMuteNotifier.value = nonCoreResults[3]; mirrorModeNotifier.value = nonCoreResults[4]; rotateModeNotifier.value = nonCoreResults[5]; scaleModeNotifier.value = nonCoreResults[6]; }); } /// 设置播放器视图 /// /// Set the rendering view for the player. /// /// [playerViewId] The ID of the player view to be set. void _setPlayerView(int playerViewId) { _playerLog("[api][setPlayerView]: $playerViewId"); _aliPlayer.setPlayerView(playerViewId); // 列表播放模式下,允许预渲染 if (_widgetData?.sceneType == SceneType.listPlayer) { _aliPlayer.setOption(FlutterAvpdef.ALLOW_PRE_RENDER, 1); } } /// 配置播放控制器 /// /// Configure the player controller with the given data. /// /// [data] The configuration data for the player, including video URL and other settings. void configure(AliPlayerWidgetData data) { logi("[api][configure]: $data"); _widgetData = data; // 列表播放模式下,允许预渲染 if (_widgetData?.sceneType == SceneType.listPlayer) { _aliPlayer.setOption(FlutterAvpdef.ALLOW_PRE_RENDER, 1); } // 配置播放源 _configurePlayerSource(data); _aliPlayer.setStartTime(data.startTime, data.seekMode); _aliPlayer.setAutoPlay(data.autoPlay); // 准备播放 prepare(); } /// 根据视频源配置播放器 /// /// Configure the player based on video source void _configurePlayerSource(AliPlayerWidgetData data) { if (data.videoSource == null || !data.videoSource!.validate()) { throw ArgumentError("Invalid video source"); } final videoSource = data.videoSource; // 确保视频源不为空 if (videoSource == null) { return; } // 根据视频源类型设置播放器 switch (videoSource.sourceType) { case SourceType.url: // 对于URL类型,直接使用videoUrl设置 final urlSource = videoSource as UrlVideoSource; _aliPlayer.setUrl(urlSource.url); break; case SourceType.vidSts: // 对于VidSts类型,提取所需参数 final stsSource = videoSource as VidStsVideoSource; _aliPlayer.setVidSts( vid: stsSource.vid, region: stsSource.region, accessKeyId: stsSource.accessKeyId, accessKeySecret: stsSource.accessKeySecret, securityToken: stsSource.securityToken, ); break; case SourceType.vidAuth: // 对于VidAuth类型,提取所需参数 final authSource = videoSource as VidAuthVideoSource; _aliPlayer.setVidAuth( vid: authSource.vid, playAuth: authSource.playAuth, ); break; } } /// 准备播放 /// /// Prepare the player for playback. void prepare() { _playerLog("[api][prepare]"); _aliPlayer.prepare(); // 记录准备开始时间 _prepareStartTime = DateTime.now().millisecondsSinceEpoch; } /// 继续播放 /// /// Start or resume playback of the player. void play() { _playerLog("[api][play]"); _aliPlayer.play(); } /// 暂停播放 /// /// Pause the player's playback. void pause() { _playerLog("[api][pause]"); _aliPlayer.pause(); } /// 停止播放 /// /// Stop the player's playback. void stop() { _playerLog("[api][stop]"); _aliPlayer.stop(); } /// 切换播放状态 /// /// Toggle between play and pause states. void togglePlayState() { final playState = playStateNotifier.value; logi("togglePlayState: $playState"); if (playState == FlutterAvpdef.started) { pause(); } else if (playState == FlutterAvpdef.completion) { replay(); } else if (_isPrepared) { play(); } } /// 重新播放 /// /// Restart playback from the beginning. void replay() async { prepare(); play(); } /// 跳转播放位置 /// /// Seek to a specific position in the video. /// /// [position] The target playback position. void seek(Duration position) { currentPositionNotifier.value = position; _aliPlayer.seekTo( position.inMilliseconds, _widgetData?.seekMode ?? FlutterAvpdef.ACCURATE, ); } /// 设置播放速度 /// /// Set the playback speed of the player. /// /// [speed] The target playback speed. Defaults to [SettingConstants.defaultSpeed]. void setSpeed({double speed = SettingConstants.defaultSpeed}) async { // Validate the speed value. if (speed <= 0) { return; } await _aliPlayer.setSpeed(speed); speedNotifier.value = speed; } /// 设置亮度 /// /// Set the brightness level of the player. /// /// [brightness] The target brightness value, clamped between 0 and 1. void setBrightness(double brightness) { final value = clampDouble(brightness, 0, 1); // TODO keria: brightness feature to be implemented logi("setBrightness: ${brightnessNotifier.value} -> $value"); brightnessNotifier.value = value; } /// 设置亮度(增量) /// /// Adjust the brightness level by a delta value. /// /// [delta] The amount by which to adjust the brightness. void setBrightnessWithDelta(double delta) { final brightness = brightnessNotifier.value; final sum = brightness + delta; logi("setBrightnessWithDelta: $brightness, $delta, sum: $sum"); setBrightness(sum); } /// 设置音量 /// /// Set the volume level of the player. /// /// [volume] The target volume value, clamped between 0 and 1. void setVolume(double volume) async { final value = clampDouble(volume, 0, 1); await _aliPlayer.setVolume(value); double newValue = await _aliPlayer.getVolume(); logi("setVolume: $volume, real: $newValue"); volumeNotifier.value = newValue; } /// 设置音量(增量) /// /// Adjust the volume level by a delta value. /// /// [delta] The amount by which to adjust the volume. void setVolumeWithDelta(double delta) { final oldVolume = volumeNotifier.value; final sum = oldVolume + delta; logi("setVolumeWithDelta: $oldVolume, $delta, sum: $sum"); setVolume(sum); } /// 设置循环播放 /// /// Enable or disable loop playback. /// /// [loop] Whether to enable loop playback. Future<void> setLoop(bool loop) async { await _aliPlayer.setLoop(loop); bool newValue = await _aliPlayer.isLoop(); logi("setLoop: $loop, real: $newValue"); isLoopNotifier.value = newValue; } /// 设置静音 /// /// Enable or disable mute mode. /// /// [mute] Whether to enable mute mode. Future<void> setMute(bool mute) async { await _aliPlayer.setMuted(mute); bool newValue = await _aliPlayer.isMuted(); logi("setMute: $mute, real: $newValue"); isMuteNotifier.value = newValue; } /// 设置镜像模式 /// /// Set the mirror mode of the player. /// /// [mirrorMode] The target mirror mode. Future<void> setMirrorMode(int mirrorMode) async { await _aliPlayer.setMirrorMode(mirrorMode); int newValue = await _aliPlayer.getMirrorMode(); logi("setMirrorMode: $mirrorMode, real: $newValue"); mirrorModeNotifier.value = newValue; } /// 设置旋转角度 /// /// Set the rotation angle of the player. /// /// [rotateMode] The target rotation angle. Future<void> setRotateMode(int rotateMode) async { await _aliPlayer.setRotateMode(rotateMode); int newValue = await _aliPlayer.getRotateMode(); logi("setRotateMode: $rotateMode, real: $newValue"); rotateModeNotifier.value = newValue; } /// 设置渲染填充模式 /// /// Set the scaling mode of the player. /// /// [scaleMode] The target scaling mode. Future<void> setScaleMode(int scaleMode) async { await _aliPlayer.setScalingMode(scaleMode); int newValue = await _aliPlayer.getScalingMode(); logi("setScaleMode: $scaleMode, real: $newValue"); scaleModeNotifier.value = newValue; } /// 更新视频尺寸 /// /// Update the video size based on the current media information. void _updateVideoSize() async { // 获取视频尺寸 final videoWidth = await _aliPlayer.getVideoWidth() as int; final videoHeight = await _aliPlayer.getVideoHeight() as int; // 如果视频高度没有变化,则不更新视频尺寸 final oldSize = videoSizeNotifier.value; if (videoHeight == oldSize.height && videoWidth == oldSize.width) { return; } // 如果视频尺寸发生变化,则更新视频尺寸 final newSize = Size( videoWidth.toDouble(), videoHeight.toDouble(), ); logi("_updateVideoSize: $oldSize -> $newSize"); videoSizeNotifier.value = newSize; } /// 获取播放清晰度信息 /// /// Retrieve and update the track information when the player is prepared. Future<void> _createTrackInfoWhenPrepared() async { // update track info list when player is prepared var mediaInfo = await _aliPlayer.getMediaInfo(); var tracks = mediaInfo["tracks"]; // 过滤出视频清晰度信息 final trackInfoList = TrackInfoUtil.filterVideoTrackInfoList(tracks); final trackInfo = (trackInfoList.isNotEmpty) ? trackInfoList.first : null; // 更新当前清晰度信息 trackInfoListNotifier.value = trackInfoList; currentTrackInfoNotifier.value = trackInfo; } /// 切换清晰度 /// /// Switch to a specific track for playback. /// /// [trackInfo] The target track information. void selectTrack(AVPTrackInfo? trackInfo) { if (trackInfo == null || trackInfo.trackIndex == null) { return; } // 切换清晰度 _aliPlayer.selectTrack( trackInfo.trackIndex!, accurate: 1, ); // 显示清晰度信息 String quality = TrackInfoUtil.getQuality(trackInfo); SnackBarUtil.show(_context, "selectTrack: $quality"); } /// 创建缩略图(方式1) /// /// Create a thumbnail using method 1 (external URL). void _createThumbnailWhenPrepared() { // 如果外部配置了缩略图地址,则使用方式1创建缩略图 if (_widgetData == null || _widgetData!.thumbnailUrl.isEmpty) { return; } // 创建缩略图 _aliPlayer.createThumbnailHelper(_widgetData!.thumbnailUrl); } /// 创建缩略图(方式2) /// /// Create a thumbnail using method 2 (media info). void _createThumbnailWhenTrackReady() { if (_widgetData != null && _widgetData!.thumbnailUrl.isNotEmpty) { // 如果外部配置了缩略图地址,则使用方式1创建缩略图 return; } _aliPlayer.getMediaInfo().then((value) { final thumbnails = value['thumbnails']; if (thumbnails?.isNotEmpty ?? false) { _aliPlayer.createThumbnailHelper(thumbnails[0]['url']); } else { _thumbnailSuccess = false; } }); } /// 请求缩略图 /// /// Request a thumbnail bitmap at a specific position. /// /// [position] The target playback position for the thumbnail. void requestThumbnailBitmap(Duration position) { if (_thumbnailSuccess) { _aliPlayer.requestBitmapAtPosition(position.inMilliseconds); } } /// Player log information with a consistent format. /// /// 使用统一格式记录播放器信息 void _playerLog(String message, {bool isError = false}) { final logMessage = "[$_playerUniqueId][player]$message"; if (isError) { loge(logMessage, tag: _logTag); } else { logi(logMessage, tag: _logTag); } } /// 获取 Flutter Widget 版本号 /// /// Get Flutter Widget version static String getWidgetVersion() { return AliPlayerWidgetGlobalSetting.kWidgetVersion; } /// 清除 Widget 缓存 /// /// Clear widget cache static Future<void> clearCaches() async { // 清除视频缓存 await FlutterAliplayer.clearCaches(); // 清除图片缓存 PaintingBinding.instance.imageCache.clear(); PaintingBinding.instance.imageCache.clearLiveImages(); } }