feat: 无音轨视频容错;缓冲状态提示;错误自动重试与汇报

This commit is contained in:
orz12
2024-07-11 17:50:37 +08:00
parent 17e9f00eca
commit 39b5e369f8
3 changed files with 89 additions and 32 deletions

View File

@@ -155,6 +155,9 @@ class VideoDetailController extends GetxController
showReplyReplyPanel() { showReplyReplyPanel() {
replyReplyBottomSheetCtr = replyReplyBottomSheetCtr =
scaffoldKey.currentState?.showBottomSheet((BuildContext context) { scaffoldKey.currentState?.showBottomSheet((BuildContext context) {
// SmartDialog.show(
// alignment: Alignment.bottomRight,
// builder: (context) {
return VideoReplyReplyPanel( return VideoReplyReplyPanel(
oid: oid.value, oid: oid.value,
rpid: fRpid, rpid: fRpid,
@@ -391,8 +394,7 @@ class VideoDetailController extends GetxController
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量 /// 优先顺序 设置中指定质量 -> 当前可选的最高质量
late AudioItem? firstAudio; late AudioItem? firstAudio;
final List<AudioItem> audiosList = data.dash!.audio!; final List<AudioItem> audiosList = data.dash!.audio ?? <AudioItem>[];
if (data.dash!.dolby?.audio != null && if (data.dash!.dolby?.audio != null &&
data.dash!.dolby!.audio!.isNotEmpty) { data.dash!.dolby!.audio!.isNotEmpty) {
// 杜比 // 杜比
@@ -413,17 +415,17 @@ class VideoDetailController extends GetxController
} }
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber, firstAudio = audiosList.firstWhere((e) => e.id == closestNumber,
orElse: () => audiosList.first); orElse: () => audiosList.first);
} else {
firstAudio = AudioItem();
}
audioUrl = enableCDN audioUrl = enableCDN
? VideoUtils.getCdnUrl(firstAudio) ? VideoUtils.getCdnUrl(firstAudio)
: (firstAudio.backupUrl ?? firstAudio.baseUrl!); : (firstAudio.backupUrl ?? firstAudio.baseUrl!);
//
if (firstAudio.id != null) { if (firstAudio.id != null) {
currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!; currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!;
} }
} else {
firstAudio = AudioItem();
audioUrl = '';
}
//
defaultST = Duration(milliseconds: data.lastPlayTime!); defaultST = Duration(milliseconds: data.lastPlayTime!);
if (autoPlay.value) { if (autoPlay.value) {
isShowCover.value = false; isShowCover.value = false;

View File

@@ -24,6 +24,7 @@ import 'package:screen_brightness/screen_brightness.dart';
import 'package:universal_platform/universal_platform.dart'; import 'package:universal_platform/universal_platform.dart';
import '../../models/video/play/subtitle.dart'; import '../../models/video/play/subtitle.dart';
import '../../pages/video/detail/controller.dart';
// import 'package:wakelock_plus/wakelock_plus.dart'; // import 'package:wakelock_plus/wakelock_plus.dart';
Box videoStorage = GStorage.video; Box videoStorage = GStorage.video;
@@ -509,7 +510,7 @@ class PlPlayerController {
Player player = _videoPlayerController ?? Player player = _videoPlayerController ??
Player( Player(
configuration: PlayerConfiguration( configuration: PlayerConfiguration(
// 默认缓冲 5M 大小 // 默认缓冲 3M 大小
bufferSize: bufferSize, bufferSize: bufferSize,
), ),
); );
@@ -534,9 +535,8 @@ class PlPlayerController {
await player.setAudioTrack( await player.setAudioTrack(
AudioTrack.auto(), AudioTrack.auto(),
); );
// 音轨 // 音轨
if (dataSource.audioSource != '' && dataSource.audioSource != null) { if (dataSource.audioSource?.isNotEmpty ?? false) {
await pp.setProperty( await pp.setProperty(
'audio-files', 'audio-files',
UniversalPlatform.isWindows UniversalPlatform.isWindows
@@ -574,7 +574,6 @@ class PlPlayerController {
); );
player.setPlaylistMode(looping); player.setPlaylistMode(looping);
if (dataSource.type == DataSourceType.asset) { if (dataSource.type == DataSourceType.asset) {
final assetUrl = dataSource.videoSource!.startsWith("asset://") final assetUrl = dataSource.videoSource!.startsWith("asset://")
? dataSource.videoSource! ? dataSource.videoSource!
@@ -599,7 +598,25 @@ class PlPlayerController {
Future refreshPlayer() async { Future refreshPlayer() async {
Duration currentPos = _position.value; Duration currentPos = _position.value;
await _videoPlayerController?.open( if (_videoPlayerController == null) {
SmartDialog.showToast('视频播放器为空,请重新进入本页面');
return;
}
if (dataSource.videoSource?.isEmpty ?? true) {
SmartDialog.showToast('视频源为空,请重新进入本页面');
return;
}
if (dataSource.audioSource?.isEmpty ?? true) {
SmartDialog.showToast('音频源为空');
} else {
await (_videoPlayerController!.platform as NativePlayer).setProperty(
'audio-files',
UniversalPlatform.isWindows
? dataSource.audioSource!.replaceAll(';', '\\;')
: dataSource.audioSource!.replaceAll(':', '\\:'),
);
}
await _videoPlayerController!.open(
Media( Media(
dataSource.videoSource!, dataSource.videoSource!,
httpHeaders: dataSource.httpHeaders, httpHeaders: dataSource.httpHeaders,
@@ -724,6 +741,29 @@ class PlPlayerController {
videoPlayerServiceHandler.onStatusChange( videoPlayerServiceHandler.onStatusChange(
playerStatus.status.value, event); playerStatus.status.value, event);
}), }),
// videoPlayerController!.stream.log.listen((event) {
// print('videoPlayerController!.stream.log.listen');
// print(event);
// SmartDialog.showToast('视频加载日志: $event');
// }),
videoPlayerController!.stream.error.listen((event) {
if (event.startsWith("Failed to open https://")) {
EasyThrottle.throttle('videoPlayerController!.stream.error.listen',
const Duration(milliseconds: 1000), () {
SmartDialog.showToast('视频链接打开失败,重试中',
displayTime: const Duration(milliseconds: 500));
refreshPlayer();
});
return;
}
print('videoPlayerController!.stream.error.listen');
print(event);
//tcp: ffurl_read returned 0xdfb9b0bb
//tcp: ffurl_read returned 0xffffff99
if (!event.startsWith('tcp: ffurl_read returned ')) {
SmartDialog.showToast('视频加载错误, $event');
}
}),
// videoPlayerController!.stream.volume.listen((event) { // videoPlayerController!.stream.volume.listen((event) {
// if (!mute.value && _volumeBeforeMute != event) { // if (!mute.value && _volumeBeforeMute != event) {
// _volumeBeforeMute = event / 100; // _volumeBeforeMute = event / 100;

View File

@@ -1090,13 +1090,28 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
colors: [Colors.black26, Colors.transparent], colors: [Colors.black26, Colors.transparent],
), ),
), ),
child: Image.asset( child: Column(mainAxisSize: MainAxisSize.min, children: [
Image.asset(
'assets/images/loading.gif', 'assets/images/loading.gif',
height: 25, height: 25,
semanticLabel: "加载中", semanticLabel: "加载中",
), ),
), if (_.isBuffering.value)
)); Obx(() {
if (_.buffered.value == Duration.zero) {
return const Text('Buffering...',
style: TextStyle(
color: Colors.white, fontSize: 12));
}
String bufferStr = _.buffered.toString();
return Text(
bufferStr.substring(0, bufferStr.length - 3),
style: const TextStyle(
color: Colors.white, fontSize: 12),
);
}),
]),
)));
} else { } else {
return const SizedBox(); return const SizedBox();
} }