当前位置: 首页 > wzjs >正文

如何做律所网站十大高端网站定制设计师

如何做律所网站,十大高端网站定制设计师,微信公众平台和微网站的区别,vs2005做的网站转换为2012记录一下解决问题的过程,希望自己以后可以参考看看,解决更多的问题。 需求:flutter 缓存网络视频文件,可离线观看。 解决: 1,flutter APP视频播放组件调整; 2,找到视频播放组件&a…

记录一下解决问题的过程,希望自己以后可以参考看看,解决更多的问题。

需求:flutter 缓存网络视频文件,可离线观看。

解决:

1,flutter APP视频播放组件调整;

2,找到视频播放组件,传入url解析的地方;

 _meeduPlayerController.setDataSource(DataSource(//指定是网络类型的数据type: DataSourceType.network,//设置url参数source: widget.videoUrl != ""? widget.videoUrl: "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h720p.mov",httpHeaders: {"Range": "bytes=0-1023"},),autoplay: !background && widget.autoplay,);

3,那也就是无网路的时候播放本地已经缓存了的对应url的对应视频,先在没有缓存的时候,缓存该文件

        3.1,添加缓存(保存视频)功能依赖

                3.1.1,在视频播放依赖包中添加缓存依赖:flutter_cache_manager: ^3.4.1

                3.1.2,添加基于这个新依赖的功能代码:

import 'package:flutter_cache_manager/flutter_cache_manager.dart';class CustomVideoCacheManager {static const key = 'customCacheKey';static final CacheManager instance = CacheManager(Config(key,maxNrOfCacheObjects: 50, // 最多缓存 50 个视频// maxTotalSize: 1024 * 1024 * 1024 * 2, // 最大缓存 2GBstalePeriod: Duration(days: 7), // 缓存保留时间repo: JsonCacheInfoRepository(databaseName: key),fileSystem: IOFileSystem(key),fileService: HttpFileService(),),);
}

 暴露出来新加的dart类

                3.1.3,在video_widget组件中使用缓存工具缓存视频 

        3.2,在没有网的时候使用该缓存视频,改造步骤2中的播放方法:

_meeduPlayerController.setDataSource(//1网络时候的DataSource// DataSource(//   type: DataSourceType.network,//   source: widget.videoUrl != ""//       ? widget.videoUrl//       : "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h720p.mov",//   httpHeaders: {"Range": "bytes=0-1023"},// ),//2本地文件//错误写法:// DataSource(//   type: DataSourceType.file,//   // file: cacheFile,//   source://       "/data/user/0/com.example.client/cache/customCacheKey/9dade030-3153-11f0-b119-93d21292c9e9.mp4",// ),//正确写法// DataSource(//   type: DataSourceType.file,//   // file: cacheFile,//   file: File(//       "/data/user/0/com.example.client/cache/customCacheKey/9dade030-3153-11f0-b119-93d21292c9e9.mp4"),// ),//所以根据条件判断用上边的任一个dataSourcedataSource,autoplay: !background && widget.autoplay,);

        3.2.1,这个步骤中的插曲,就是使用本地文件一直报空,打印了_meeduPlayerController,和cacheFile都不为空,但是还是报空。

可能得问题有:

一,以为是异步写法awiat获得值,会产生后边的代码先于值计算出来,就运行了导致空

二,错误写法只是照搬了网络视频的写法,更换了一下type的参数,并没有多想,以为也是根据source来写;

解决问题一:

1看下await是怎么产生的。

        1.1,拿本地的缓存文件就有异步

        1.2,判断文件是否完成,是否可播也有await 

2如何避免;如果避免不了await,如何等值完全算完,不为空了再进行下一步的调用。

判断内容长度,是否下载完成,获取sp,判断是否可播都需要异步,就是不能直接拿到值。

Future<int?> getCachedContentLength(String url) async {final prefs = await SharedPreferences.getInstance();return prefs.getInt('video_content_length_$url');}Future<void> cacheContentLength(String url, int length) async {final prefs = await SharedPreferences.getInstance();prefs.setInt('video_content_length_$url', length);}Future<bool> isVideoFileComplete(String url) async {// 获取之前缓存的原始大小final expectedLength = await getCachedContentLength(url);if (expectedLength == null) return false;// 获取本地缓存文件FileInfo? fileInfo = await DefaultCacheManager().getFileFromCache(url);final file = fileInfo?.file;if (file == null || !file.existsSync()) return false;final localLength = file.lengthSync();bool isSame = (localLength == expectedLength);print("video_widget 是否下载完成:$isSame");return isSame;}Future<bool> isVideoPlayable(String filePath) async {final controller = VideoPlayerController.file(File(filePath));try {await controller.initialize();await controller.dispose();print("video_widget 可以 正常播放");return true;} catch (e) {print("video_widget 不能 正常播放");return false;}}

所以用到这几个方法的设置setDataSource()方法也必定是异步的

// 单独封装异步判断逻辑Future<DataSource> _getDataSource(File? cachedFile, String url) async {if (cachedFile != null) {final exists = cachedFile.existsSync();final playable = await isVideoPlayable(cachedFile.path);final complete = await isVideoFileComplete(cachedFile.path);print("video_widget: cachedFile != null: ${cachedFile != null}");print("video_widget: existsSync: $exists");print("video_widget: isVideoPlayable: $playable");print("video_widget: isVideoFileComplete: $complete");if (exists && playable && complete) {print("video_widget:即将使用缓存视频");return DataSource(type: DataSourceType.file,source: cachedFile.path,httpHeaders: {"Range": "bytes=0-1023"},);}}// 如果没有命中缓存或缓存不完整,则走网络加载File? cacheFile;try {cacheFile = await CustomVideoCacheManager.instance.getSingleFile(url);} catch (e) {print("video_widget:网络文件获取失败: $e");}final networkSource = DataSource(type: DataSourceType.network,source: widget.videoUrl.isNotEmpty? widget.videoUrl: "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h720p.mov",httpHeaders: {"Range": "bytes=0-1023"},);return cacheFile != null? DataSource(type: DataSourceType.file,file: cacheFile,): networkSource;}

使用这种方法,就不用在_meeduPlayerController设置参数的时候使用一个异步返回的dataSource了,代码如下,这样就可以使用一个看似同步的代码,完成了一个异步的操作。(并不会因为看起来像是同步的写法,就会发生dataSource的值还没回来的时候就执行了后边的代码,导致null产生。这个就是典型的支持异步操作的代码,不然就得像Java一样写回调了。)

// 封装异步获取 DataSource 的逻辑
dataSource = await _getDataSource(cachedFile, lastUrl);_meeduPlayerController.setDataSource(//所以根据条件判断用上边的任一个dataSourcedataSource,autoplay: !background && widget.autoplay,);

不用特意写then来完成这个异步操作,以下代码不推荐:

await _getDataSource(cachedFile, lastUrl).then((dataSource) {if (dataSource != null) {_meeduPlayerController.setDataSource(dataSource,autoplay: !background && widget.autoplay,);} else {print("video_widget:dataSource为空");}});

解决问题二:

1更换本地缓存文件的地址来写死dataSource参数,还是不行

2想到看下这个依赖包的说明文件是否支持本地文件播放,flutter_meedu_videoplayer example | Flutter package 看到是支持的,

3看依赖包的例子是怎么写的,没有具体写怎么播放本地视频

4看依赖包的源码是怎么使用

4.1,dataSource源码怎么使用的(只是设置参数,没有使用这个参数的逻辑)

4.2,那就找使用这个参数的源码:_meeduPlayerController,有怎么设置本地文件的DataSource方法,最终调整成正确的参数设置方式。

最后,以下是video_widget.dart的完整代码,仅供参考

import 'dart:async';
import 'dart:io';import 'package:audio_session/audio_session.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_meedu_videoplayer/meedu_player.dart';
import 'package:game_lib/common/common_page.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wakelock_plus/wakelock_plus.dart';class VideoWidget extends StatefulWidget {String videoUrl;Function? onVideoEnd;bool autoplay;Function(MeeduPlayerController)? onInit;Function(PlayerStatus)? onVideoStatusChanged;Function(Duration)? onVideoPositionChanged;bool closeFullscreenOnEnd;BoxFit fit;bool fullscreen;Function(bool)? onBackground;VideoWidget({super.key,required this.videoUrl,this.onVideoEnd,this.onInit,this.autoplay = true,this.fullscreen = true,this.fit = BoxFit.contain,this.onVideoStatusChanged,this.closeFullscreenOnEnd = true,this.onVideoPositionChanged,this.onBackground});@overrideState<VideoWidget> createState() => _VideoWidgetState();
}class _VideoWidgetState extends State<VideoWidget>with WidgetsBindingObserver, RouteAware {late final _meeduPlayerController = MeeduPlayerController(controlsStyle: ControlsStyle.primary,screenManager: const ScreenManager(orientations: [DeviceOrientation.landscapeLeft,]),enabledButtons: EnabledButtons(videoFit: false, muteAndSound: false, fullscreen: widget.fullscreen),fits: [BoxFit.contain],initialFit: widget.fit);StreamSubscription? _playerEventSubs;int lastPosition = 0;String lastUrl = "";bool background = false; // 是否处于后台@overridevoid initState() {WidgetsBinding.instance.addObserver(this);WidgetsBinding.instance.addPostFrameCallback((_) {Future.delayed(const Duration(milliseconds: 500), () {_init();});});widget.onInit?.call(_meeduPlayerController);_meeduPlayerController.onPositionChanged.listen((event) {if (event.inSeconds != lastPosition) {lastPosition = event.inMilliseconds;widget.onVideoPositionChanged?.call(event);//print("onPositionChanged: $event ${event.inSeconds}");}});_playerEventSubs = _meeduPlayerController.onPlayerStatusChanged.listen((PlayerStatus status) async {widget.onVideoStatusChanged?.call(status);print("onPlayerStatusChanged: $status");if (status == PlayerStatus.playing) {WakelockPlus.enable();Future.delayed(const Duration(milliseconds: 100), () {if (widget.fit == BoxFit.contain) {_meeduPlayerController.toggleVideoFit();}});} else {WakelockPlus.disable();final session = await AudioSession.instance;if (await session.setActive(false)) {print("AudioSession setActive abandon");}}if (status == PlayerStatus.completed) {if (widget.closeFullscreenOnEnd &&_meeduPlayerController.fullscreen.value &&Navigator.canPop(context)) {
//            Navigator.pop(context);
//            注释上面代码,播放完后不退出全屏}if (widget.onVideoEnd != null) {widget.onVideoEnd!();}}},);Timer? timer;_meeduPlayerController.onDataStatusChanged.listen((DataStatus status) {if (status == DataStatus.error) {setState(() {_meeduPlayerController.errorText = "";});print("============= video widget onDataStatusChanged: $status videoUrl: ${widget.videoUrl}");if (widget.videoUrl.isNotEmpty) {timer?.cancel();timer = Timer(const Duration(milliseconds: 1), () {setSource();});}}});super.initState();}@overridevoid dispose() {_playerEventSubs?.cancel();_meeduPlayerController.dispose();WidgetsBinding.instance.removeObserver(this);AppRouteObserver().routeObserver.unsubscribe(this);super.dispose();}@overrideFuture<void> didChangeAppLifecycleState(AppLifecycleState state) async {print("video widget didChangeAppLifecycleState: $state");final session = await AudioSession.instance;if (state == AppLifecycleState.resumed) {background = false;widget.onBackground?.call(background);_meeduPlayerController.play();} else if (state == AppLifecycleState.paused) {background = true;widget.onBackground?.call(background);_meeduPlayerController.pause();}}@overridevoid didChangeDependencies() {// TODO: implement didChangeDependenciessuper.didChangeDependencies();AppRouteObserver().routeObserver.subscribe(this, ModalRoute.of(context)!);}@overridevoid didPushNext() {Future.delayed(const Duration(milliseconds: 500), () {if (!_meeduPlayerController.fullscreen.value) {_meeduPlayerController.pause();}});}_init() {print("autoplay: ${widget.autoplay}");setSource();}Future<void> setSource() async {if (widget.videoUrl == lastUrl) {return;}lastUrl = widget.videoUrl;File? cachedFile;DataSource? dataSource;try {print("video_widget:设置视频资源,lastUrl:$lastUrl");FileInfo? fileInfo =await CustomVideoCacheManager.instance.getFileFromCache(lastUrl);cachedFile = fileInfo?.file;print("video_widget:缓存文件地址${cachedFile?.path}");} catch (e) {print("video_widget:未找到缓存视频");}// 封装异步获取 DataSource 的逻辑dataSource = await _getDataSource(cachedFile, lastUrl);// await _getDataSource(cachedFile, lastUrl).then((dataSource) {//   print(//       "=====video_widget:_meeduPlayerController是否为空:${_meeduPlayerController == null}");//   print("=====video_widget:dataSource是否为空:${dataSource == null}");//   if (dataSource != null) {//     _meeduPlayerController.setDataSource(//       // DataSource(//       //   type: DataSourceType.network,//       //   source: widget.videoUrl != ""//       //       ? widget.videoUrl//       //       : "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h720p.mov",//       //   httpHeaders: {"Range": "bytes=0-1023"},//       // ),////       dataSource,////       autoplay: !background && widget.autoplay,//     );//   } else {//     print("video_widget:dataSource为空");//   }// });//清除缓存//await CustomVideoCacheManager.instance.emptyCache();_meeduPlayerController.setDataSource(// DataSource(//   type: DataSourceType.network,//   source: widget.videoUrl != ""//       ? widget.videoUrl//       : "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h720p.mov",//   httpHeaders: {"Range": "bytes=0-1023"},// ),// DataSource(//   type: DataSourceType.file,//   // file: cacheFile,//   file: File(//       "/data/user/0/com.example.client/cache/customCacheKey/9dade030-3153-11f0-b119-93d21292c9e9.mp4"),// ),dataSource,autoplay: !background && widget.autoplay,);}// 单独封装异步判断逻辑Future<DataSource> _getDataSource(File? cachedFile, String url) async {if (cachedFile != null) {final exists = cachedFile.existsSync();final playable = await isVideoPlayable(cachedFile.path);final complete = await isVideoFileComplete(cachedFile.path);print("video_widget: cachedFile != null: ${cachedFile != null}");print("video_widget: existsSync: $exists");print("video_widget: isVideoPlayable: $playable");print("video_widget: isVideoFileComplete: $complete");if (exists && playable && complete) {print("video_widget:即将使用缓存视频");return DataSource(type: DataSourceType.file,source: cachedFile.path,httpHeaders: {"Range": "bytes=0-1023"},);}}// 如果没有命中缓存或缓存不完整,则走网络加载File? cacheFile;try {cacheFile = await CustomVideoCacheManager.instance.getSingleFile(url);} catch (e) {print("video_widget:网络文件获取失败: $e");}final networkSource = DataSource(type: DataSourceType.network,source: widget.videoUrl.isNotEmpty? widget.videoUrl: "https://movietrailers.apple.com/movies/paramount/the-spongebob-movie-sponge-on-the-run/the-spongebob-movie-sponge-on-the-run-big-game_h720p.mov",httpHeaders: {"Range": "bytes=0-1023"},);return cacheFile != null? DataSource(type: DataSourceType.file,file: cacheFile,): networkSource;}Future<int?> getCachedContentLength(String url) async {final prefs = await SharedPreferences.getInstance();return prefs.getInt('video_content_length_$url');}Future<void> cacheContentLength(String url, int length) async {final prefs = await SharedPreferences.getInstance();prefs.setInt('video_content_length_$url', length);}Future<bool> isVideoFileComplete(String url) async {// 获取之前缓存的原始大小final expectedLength = await getCachedContentLength(url);if (expectedLength == null) return false;// 获取本地缓存文件FileInfo? fileInfo = await DefaultCacheManager().getFileFromCache(url);final file = fileInfo?.file;if (file == null || !file.existsSync()) return false;final localLength = file.lengthSync();bool isSame = (localLength == expectedLength);print("video_widget 是否下载完成:$isSame");return isSame;}Future<bool> isVideoPlayable(String filePath) async {final controller = VideoPlayerController.file(File(filePath));try {await controller.initialize();await controller.dispose();print("video_widget 可以 正常播放");return true;} catch (e) {print("video_widget 不能 正常播放");return false;}}@overrideWidget build(BuildContext context) {setSource();return AspectRatio(aspectRatio: 16 / 9,child: MeeduVideoPlayer(key: UniqueKey(),controller: _meeduPlayerController,),);}
}

http://www.dtcms.com/wzjs/639364.html

相关文章:

  • 网页制作网站制作步骤wordpress建站行吗
  • 云南购物网站建设文字转链接网址
  • 南京网站设计建设2023网络营销成功案例
  • wordpress导航站的源码wordpress高并发
  • asp.net视频网站模板下载建立公司网站的目的
  • 泰州市建设局审图中心网站深圳创业补贴2023
  • 如何做网站产品经理抚州 提供网站建站 公司
  • 族谱网站建设方案制作相册
  • 做plc课程设计的网站怎么自建导购网站做淘客
  • 触屏版网站模板低代码前端开发平台
  • 高端网站登录入口海门网页定制
  • 长沙城通基础管网建设有限公司怎么让客户做网站优化
  • 专业外贸制作网站网站备案信息查询接口
  • 网泰网站建设网络推广海南论坛论坛网站建设
  • 制作网站多少钱一个外贸网站建设哪里实惠
  • 烟台高端网站建设公司沈阳网站建设模块维护
  • 方案模板网站与客户沟通网站建设的技巧
  • 有做义工的相亲网站吗鹰潭建设网站
  • 百度网站建设及推广易做文学网站的logo
  • seo网站怎么做网站建设制作优帮云
  • 怎么形容网站做的很好旅游网站建设ppt模板
  • 智慧团建app北京seo服务商
  • 做网站手机版百度的网址是多少
  • 不用开源程序怎么做网站微信营销工具有哪些
  • 淘宝这种网站怎么做的衡阳关键词优化首选
  • c 做网站用什么框架店面设计师是什么
  • 禁止粘贴的网站个人网站域名取名
  • 宁波免费做网站企业网站框架
  • 软件开发职业学校百度关键词seo推广
  • 手机访问wordpress网站卡网页图片大全