1. 项目背景
播放器作为应用内使用最频繁的SDK之一,尝尝需要应对多种应用场景,因此如何通过合理设计框架,从而根据各个团队的需求开发出针对性的业务插件,是播放器SDK应用使用多种实用场景的重要思路。
2. 解耦架构
可以看到,在解耦架构下,所有的第三方功能均从内核中拆出,如画中画、换链、控制面板、上报等,使得内核保持纯粹,并且在需要的时候可以组合多个插件。
这里以UI控制面板
为例,阐述一下Flutter场景下的播放器UI插件化开发的应用。
├── base
│ └── ftp_panel_base.dart
├── components
│ ├── ftp_hide_timer.dart
│ ├── ftp_play_pause.dart
│ └── ftp_progress_bar.dart
└── utils
│ └── ftp_ui_utils.dart
├── ftp_panel.dart
其中ftp_panel_base.dart
为基础抽象类,提供FTPController
给插件访问,最终由组合出来的StatefulWidget
实现方法。
abstract class FTPPanelBase {
FTPPlayerController get playerController;
FTPPlayerValue get playerValue => playerController.value;
}
而播放按钮PlayPauseMixin
采用Dart中mixin
的写法,mix的对象为State<T>
,这样最后封装Panel的时候可以任意组合相关插件,而通过声明实现FTPPanelBase
抽象类,即可访问playerController
实例,直接获取播放状态,并控制播放和暂停。
mixin PlayPauseMixin<T extends StatefulWidget> on State<T>
implements HideTimer, FTPPanelBase {
GestureDetector buildPlayPause(Color iconColor) {
return GestureDetector(
onTap: playPause,
child: Container(
height: 48.0,
color: Colors.transparent,
margin: const EdgeInsets.only(left: 8.0, right: 4.0),
padding: const EdgeInsets.only(
left: 12.0,
right: 12.0,
),
child: Icon(
playerController.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: iconColor),
),
);
}
}
而在使用这些UI组件时,通过with
的方式对mixin和抽象接口类进行组合,即可访问UI组件的实现方法,快速组合出可用的控制面板UI。
class FTPDefaultPanelState extends State<FTPDefaultPanel>
with
FTPPanelBase,
CenterPlayPauseMixin,
HideTimerMixin,
HideTimer,
ExpandButtonMixin,
MuteButtonMixin,
SpeedButtonMixin,
TotalPositionMixin,
CurrentPositionMixin,
ProgressBarMixin,
PlayPauseMixin,
SingleTickerProviderStateMixin {
@override
FTPPlayerController get playerController => widget.playerController;
AnimatedOpacity _buildBottomBar(
BuildContext context,
) {
final iconColor = Theme.of(context).textTheme.button!.color;
return AnimatedOpacity(
opacity: hideStuff ? 0.0 : 1.0,
duration: const Duration(milliseconds: 300),
child: Container(
color: Colors.white,
height: barHeight,
child: Row(
children: <Widget>[
buildPlayPause(iconColor!),
const SizedBox(width: 12),
buildCurPosition(iconColor),
buildProgressBar(null),
buildTotalPosition(iconColor),
buildSpeedButton(iconColor),
buildMuteButton(iconColor),
buildExpandButton(iconColor),
],
),
),
);
}
@override
late bool isFullScreen = false;
@override
void setPlayPauseListener(FTPPlayPauseListener ftpPlayPauseListener) {
super.setPlayPauseListener(widget.ftpPlayPauseListener);
}
接下来以换链为例阐述非UI控制插件的实现方式。
这里通过extension的方式为FTPPlayerController
增加了使用腾讯云vid换链功能的方法,只要import
对应的扩展方法,即可无缝实用插件方法。
extension VideoInfoExtension on FTPPlayerController {
static const String BASEURL = 'https://playvideo.zijiebao.com/getplayinfo/v4/';
/// 使用设置播放地址
Future<int?> setDataSourceWithVideoId(
SuperPlayerVideoModel superPlayerVideoId) async {
if (superPlayerVideoId.fileId!.isEmpty ||
superPlayerVideoId.appId!.isEmpty) {
return -1;
}
var videoInfo = await VideoInfo.getVideoInfo(superPlayerVideoId);
return setDataSource(videoInfo.videoUrl ?? '');
}
}