video_utils.dart
8.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:one_key/utils/utils.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_compress/video_compress.dart';
import 'package:video_thumbnail/video_thumbnail.dart';
class VideoUtils {
// 1280*720
static final int maxVideoPixel = 1280 * 720;
static final int maxVideoSize = 50;
static Future<String> downloadVideo(String videoUrl, String savePath) async {
var dio = Dio();
var response = await dio.get(
videoUrl,
options: Options(responseType: ResponseType.bytes),
);
final file = File(savePath);
File tempFile = await file.writeAsBytes(response.data); // 将字节写入文件
return tempFile.path;
}
/// 清除缓存
static Future<void> clearTemporaryDirectory() async {
Directory tempDir = await getTemporaryDirectory();
if (tempDir == null) return;
await _delete(tempDir);
}
static Future<void> _delete(FileSystemEntity file) async {
if (file is Directory) {
final List<FileSystemEntity> children = file.listSync();
for (final FileSystemEntity child in children) {
await _delete(child);
}
} else {
await file.delete();
}
}
static getDuration(double duration, {String dateFormat = 'mm:ss'}) {
int value = duration ~/ 1000; // 毫秒转换为秒;
int hour = (value) ~/ 3600;
String hourStr = '${hour}';
if (hour < 10) {
//时
hourStr = "0" + '${hour}';
}
int minute = (value % 3600) ~/ 60;
String minuteStr = '${minute}';
if (minute < 10) {
//分
minuteStr = "0" + '${minute}';
}
int second = value % 60;
String secondStr = '${second}';
if (second < 10) {
//秒
secondStr = "0" + '${second}';
}
switch (dateFormat) {
case 'hh:mm':
return hourStr + ":" + minuteStr;
break;
case 'mm:ss':
return minuteStr + ":" + secondStr;
break;
case 'hh':
return hourStr;
break;
default:
return hourStr + ":" + minuteStr + ":" + secondStr;
break;
}
}
// 获取视频的大小(以 MB 为单位)
static Future<double> getVideoFileSize(String videoPath) async {
final File file = File(videoPath);
return file.lengthSync() / 1024 / 1024;
}
// 获取视频的像素大小(宽度 * 高度)
static Future<int> getVideoPixelSize(String videoPath) async {
final info =
await VideoCompress.getMediaInfo(videoPath); //视频信息:时长、像素(宽高)、大小、path
final int width = info.width!;
final int height = info.height!;
return width * height;
}
// 获取视频的像素大小(宽度 * 高度)
// static Future<double> getVideoPixelSize(String videoPath) async {
// final VideoPlayerController _controller = VideoPlayerController.file(File(videoPath));
// await _controller.initialize();
// final double width = _controller.value.size.width;
// final double height = _controller.value.size.height;
// await _controller.dispose();
// return width * height;
// }
// 获取视频的封面图
// static Future<String> getVideoThumbnail(String videoPath) async {
// final thumbnailPath = await VideoThumbnail.thumbnailFile(
// video: videoPath,
// thumbnailPath: (await getTemporaryDirectory()).path,
// imageFormat: ImageFormat.JPEG,
// maxHeight: 128, // 设置最大高度
// quality: 75, // 设置质量
// );
// return thumbnailPath;
// }
// 获取视频的封面图
static Future<String> getThumbnailFile(String url,
{bool isNetUrl = true}) async {
// final fileName = await VideoThumbnail.thumbnailFile(
// video: url,
// thumbnailPath: (await getTemporaryDirectory()).path,
// // imageFormat: ImageFormat.WEBP,
// // maxHeight:
// // 64, // specify the height of the thumbnail, let the width auto-scaled to keep the source aspect ratio
// quality: 100,
// );
String thumbnailPath = '';
if (Platform.isAndroid || Platform.isIOS) {
final thumbnail = (await VideoThumbnail.thumbnailFile(
video: url,
imageFormat: ImageFormat.PNG,
quality: 100,
))!;
// 获取应用文件夹路径
String name = DateTime.now().millisecondsSinceEpoch.toString();
final directory = await getApplicationDocumentsDirectory();
thumbnailPath = '${directory.path}/${name}.png';
// 将封面图保存到应用文件夹
final File file = File(thumbnailPath);
await file.writeAsBytes(await File(thumbnail).readAsBytes());
// print('Thumbnail saved to $thumbnailPath');
} else if (Platform.isOhos) {
// final PathProviderPlatform provider = PathProviderPlatform.instance;
// thumbnailPath = await provider.getExternalStoragePath();
// Directory? downloadsDir = await getTemporaryDirectory();
String thumbnailPathVideo = url;
String fileName = '';
String thumbnailPathSave = (await getTemporaryDirectory()).path;
print(thumbnailPathSave);
if (isNetUrl) {
// 鸿蒙端网络地址需要自己提前下载
// getTemporaryDirectory().then((d) => thumbnailPath = d.path);
fileName = Utils.extractFileNameFromUrl(url);
print("File Name: $fileName"); // 输出:File Name: file.txt
//鸿蒙,flutter侧下载视频到缓存,然后使用video_thumbnail 插件从鸿蒙设备本地缓存读取文件并获取第一帧图片
thumbnailPathVideo = await VideoUtils.downloadVideo(
url, thumbnailPathSave + '/' + fileName);
}
try {
// 封面图片路径
String name = DateTime.now().millisecondsSinceEpoch.toString();
thumbnailPath = '${thumbnailPathSave}/${name}.png';
String? imagePath = await VideoThumbnail.thumbnailFile(
video: thumbnailPathVideo,
thumbnailPath: thumbnailPath,
// imageFormat: ImageFormat.WEBP,
// maxHeight:
// 64, // specify the height of the thumbnail, let the width auto-scaled to keep the source aspect ratio
quality: 100,
);
thumbnailPath = imagePath!;
///没有直接使用插件返回的图片地址 /cachepng 图片地址有问题,url没有后缀 .mp4 鸿蒙端是根据字符串中 . 位置分割来获取名称的, 找不到. 状态返回-1 ,所以就出现了路径错误
// if (isNetUrl && !fileName.contains('.')) {
// // 获取应用文件夹路径
// String name = DateTime.now().millisecondsSinceEpoch.toString();
// thumbnailPath = '${thumbnailPath}/${name}.png';
// // 将封面图保存到应用文件夹
// final File file = File(thumbnailPath);
// File imagePathNew =
// await file.writeAsBytes(await File(imagePath!).readAsBytes());
// print(imagePathNew.path);
// thumbnailPath = imagePathNew.path;
// }
} catch (e) {
print(e);
return '';
}
}
return thumbnailPath;
}
Future<void> copyFile(String oldPath, String newPath) async {
// 获取应用的临时目录
final tempDir = await getTemporaryDirectory();
final newTempPath = '${tempDir.path}/$newPath';
// 创建新文件的目录
final newDir =
Directory(newTempPath.substring(0, newTempPath.lastIndexOf('/')));
if (!newDir.existsSync()) {
newDir.createSync(recursive: true);
}
// 复制文件到新路径
final file = File(oldPath);
await file.copy(newTempPath);
print('文件已复制到: $newTempPath');
}
// Future<String> getTempDirectoryPathWithTimestamp() async {
// // 获取临时目录
// final tempDir = await getTemporaryDirectory();
// // 获取当前时间戳
// final timestamp = DateTime.now().millisecondsSinceEpoch;
// // 拼接路径和时间戳
// final tempPathWithTimestamp = join(tempDir.path, '$timestamp');
// return tempPathWithTimestamp;
// }
// 是否压缩 (压缩前的校验)
// 视频像素大小 小于 720p || 视频大小 小于 50mb --> 压缩
static Future<bool> shouldCompress(String videoPath) async {
int p = await getVideoPixelSize(videoPath);
double s = await getVideoFileSize(videoPath); //size
if (p > maxVideoPixel || s >= maxVideoSize) {
return true;
} else {
return false;
}
}
// 压缩视频的方法
static Future<File?> compress(String videoPath) async {
// 订阅压缩进度
// _subscription = VideoCompress.compressProgress$.subscribe((progress) {
// _progress = progress;
// // 回调进度给外部
// if (onProgress != null) {
// onProgress!(progress);
// }
// });
MediaInfo info = (await VideoCompress.compressVideo(
videoPath,
quality: VideoQuality.Res1280x720Quality,
deleteOrigin: false,
))!;
return info.file;
}
}