Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
ethan
/
appframe
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Commit dba5d2f6
authored
2026-04-20 17:32:19 +0800
by
tanghuan
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
增加视频转码和压缩的进度
1 parent
7b82321d
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
109 additions
and
13 deletions
lib/bloc/web_cubit.dart
lib/data/repositories/message/upload_start_handler.dart
lib/utils/video_util.dart
lib/bloc/web_cubit.dart
View file @
dba5d2f
...
@@ -415,10 +415,11 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
...
@@ -415,10 +415,11 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
void
sendUploadStartResponse
(
String
unique
,
String
uploadId
)
{
void
sendUploadStartResponse
(
String
unique
,
String
uploadId
)
{
var
resp
=
{
var
resp
=
{
'unique'
:
unique
,
'unique'
:
unique
,
'cmd'
:
'upload
File
'
,
'cmd'
:
'upload
Start
'
,
'data'
:
{
'data'
:
{
'uploadId'
:
uploadId
,
'uploadId'
:
uploadId
,
'status'
:
1
,
'status'
:
1
,
'percent'
:
0
,
'totalPart'
:
0
,
'totalPart'
:
0
,
'sendedPart'
:
0
,
'sendedPart'
:
0
,
'totalByte'
:
0
,
'totalByte'
:
0
,
...
@@ -437,13 +438,14 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
...
@@ -437,13 +438,14 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
/// [totalByte] 总字节数
/// [totalByte] 总字节数
/// [sendedByte] 已上传字节数
/// [sendedByte] 已上传字节数
void
sendUploadProgress
(
void
sendUploadProgress
(
String
unique
,
String
uploadId
,
int
totalPart
,
int
sendedPart
,
int
totalByte
,
int
sendedByte
)
{
String
unique
,
String
uploadId
,
int
status
,
int
percent
,
int
totalPart
,
int
sendedPart
,
int
totalByte
,
int
sendedByte
)
{
var
resp
=
{
var
resp
=
{
'unique'
:
unique
,
'unique'
:
unique
,
'cmd'
:
'upload
File
Progress'
,
'cmd'
:
'uploadProgress'
,
'data'
:
{
'data'
:
{
'uploadId'
:
uploadId
,
'uploadId'
:
uploadId
,
'status'
:
2
,
'status'
:
status
,
'percent'
:
percent
,
'totalPart'
:
totalPart
,
'totalPart'
:
totalPart
,
'sendedPart'
:
sendedPart
,
'sendedPart'
:
sendedPart
,
'totalByte'
:
totalByte
,
'totalByte'
:
totalByte
,
...
@@ -457,7 +459,7 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
...
@@ -457,7 +459,7 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
void
sendUploadEnd
(
String
unique
,
String
uploadId
,
String
url
)
{
void
sendUploadEnd
(
String
unique
,
String
uploadId
,
String
url
)
{
var
resp
=
{
var
resp
=
{
'unique'
:
''
,
'unique'
:
''
,
'cmd'
:
'upload
File
End'
,
'cmd'
:
'uploadEnd'
,
'data'
:
{
'data'
:
{
'uploadId'
:
uploadId
,
'uploadId'
:
uploadId
,
'url'
:
url
,
'url'
:
url
,
...
...
lib/data/repositories/message/upload_start_handler.dart
View file @
dba5d2f
...
@@ -122,9 +122,30 @@ class UploadStartHandler extends MessageHandler {
...
@@ -122,9 +122,30 @@ class UploadStartHandler extends MessageHandler {
bool
success
=
false
;
bool
success
=
false
;
var
startTime
=
DateTime
.
now
();
var
startTime
=
DateTime
.
now
();
if
(
mimeType
!=
'video/mp4'
)
{
if
(
mimeType
!=
'video/mp4'
)
{
success
=
await
VideoUtil
.
convertToMp4
(
inputPath
,
outputPath
);
success
=
await
VideoUtil
.
convertToMp4
(
inputPath
,
outputPath
,
onProgress:
(
progress
)
{
// progress 范围 0 ~ 100
debugPrint
(
'转码进度:
$progress
%'
);
/// 发送转码进度
_webCubit
?.
sendUploadProgress
(
_cmdUnique
,
_cmdUploadId
,
1
,
progress
,
0
,
0
,
0
,
0
);
},
);
}
else
{
}
else
{
success
=
await
VideoUtil
.
compressVideo
(
inputPath
,
outputPath
,
'low'
);
success
=
await
VideoUtil
.
compressVideo
(
inputPath
,
outputPath
,
'low'
,
onProgress:
(
progress
)
{
// progress 范围 0 ~ 100
debugPrint
(
'压缩进度:
$progress
%'
);
/// 发送压缩进度
_webCubit
?.
sendUploadProgress
(
_cmdUnique
,
_cmdUploadId
,
1
,
progress
,
0
,
0
,
0
,
0
);
},
);
}
}
var
endTime
=
DateTime
.
now
();
var
endTime
=
DateTime
.
now
();
debugPrint
(
'====================>压缩耗时:
${endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch}
毫秒'
);
debugPrint
(
'====================>压缩耗时:
${endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch}
毫秒'
);
...
@@ -251,6 +272,8 @@ class UploadStartHandler extends MessageHandler {
...
@@ -251,6 +272,8 @@ class UploadStartHandler extends MessageHandler {
_webCubit
?.
sendUploadProgress
(
_webCubit
?.
sendUploadProgress
(
_cmdUnique
,
_cmdUnique
,
_cmdUploadId
,
_cmdUploadId
,
2
,
((
_cmdUploadedChunks
/
_cmdTotalChunks
)
*
100
).
floor
(),
_cmdTotalChunks
,
_cmdTotalChunks
,
_cmdUploadedChunks
,
_cmdUploadedChunks
,
_cmdTotalByte
,
_cmdTotalByte
,
...
...
lib/utils/video_util.dart
View file @
dba5d2f
import
'dart:io'
;
import
'dart:io'
;
import
'dart:async'
;
import
'package:ffmpeg_kit_flutter_new/ffmpeg_kit.dart'
;
import
'package:ffmpeg_kit_flutter_new/ffmpeg_kit.dart'
;
import
'package:ffmpeg_kit_flutter_new/return_code.dart'
;
import
'package:ffmpeg_kit_flutter_new/return_code.dart'
;
import
'package:ffmpeg_kit_flutter_new/statistics.dart'
;
class
VideoUtil
{
class
VideoUtil
{
///
///
/// 将视频格式转换为mp4
/// 将视频格式转换为mp4
/// 转码的同时,进行压缩
/// 转码的同时,进行压缩
/// [onProgress] 进度回调,值范围 0.0 ~ 1.0
///
///
static
Future
<
bool
>
convertToMp4
(
String
inputPath
,
String
outputPath
)
async
{
static
Future
<
bool
>
convertToMp4
(
String
inputPath
,
String
outputPath
,
{
void
Function
(
int
progress
)?
onProgress
,
})
async
{
// 先获取视频总时长(微秒)
final
duration
=
await
_getVideoDuration
(
inputPath
);
String
cmd
;
String
cmd
;
if
(
Platform
.
isIOS
)
{
if
(
Platform
.
isIOS
)
{
cmd
=
'-i "
$inputPath
" '
cmd
=
'-i "
$inputPath
" '
...
@@ -29,15 +39,40 @@ class VideoUtil {
...
@@ -29,15 +39,40 @@ class VideoUtil {
'-f mp4 '
// 指定输出格式为MP4
'-f mp4 '
// 指定输出格式为MP4
'"
$outputPath
"'
;
// 指定输出文件路径
'"
$outputPath
"'
;
// 指定输出文件路径
}
}
final
session
=
await
FFmpegKit
.
execute
(
cmd
);
final
completer
=
Completer
<
bool
>();
FFmpegKit
.
executeAsync
(
cmd
,
(
session
)
async
{
final
returnCode
=
await
session
.
getReturnCode
();
final
returnCode
=
await
session
.
getReturnCode
();
return
ReturnCode
.
isSuccess
(
returnCode
);
completer
.
complete
(
ReturnCode
.
isSuccess
(
returnCode
));
},
null
,
(
Statistics
statistics
)
{
if
(
onProgress
!=
null
&&
duration
>
0
)
{
final
currentTime
=
statistics
.
getTime
();
final
progress
=
(
currentTime
/
duration
).
clamp
(
0.0
,
1.0
);
onProgress
((
progress
*
100
).
floor
());
}
},
);
return
completer
.
future
;
}
}
///
///
/// 通过 ffmpeg 压缩视频
/// 通过 ffmpeg 压缩视频
/// [onProgress] 进度回调,值范围 0.0 ~ 1.0
///
///
static
Future
<
bool
>
compressVideo
(
String
inputPath
,
String
outputPath
,
String
quality
)
async
{
static
Future
<
bool
>
compressVideo
(
String
inputPath
,
String
outputPath
,
String
quality
,
{
void
Function
(
int
progress
)?
onProgress
,
})
async
{
final
duration
=
await
_getVideoDuration
(
inputPath
);
// 使用CRF模式进行压缩,值范围0-51,建议值18-28
// 使用CRF模式进行压缩,值范围0-51,建议值18-28
// 高质量: CRF 18-20
// 高质量: CRF 18-20
// 中等质量: CRF 23-26
// 中等质量: CRF 23-26
...
@@ -64,9 +99,45 @@ class VideoUtil {
...
@@ -64,9 +99,45 @@ class VideoUtil {
'-preset medium '
// 编码预设
'-preset medium '
// 编码预设
'-movflags faststart '
// 优化MP4文件结构
'-movflags faststart '
// 优化MP4文件结构
'"
$outputPath
"'
;
// 输出文件
'"
$outputPath
"'
;
// 输出文件
final
session
=
await
FFmpegKit
.
execute
(
cmd
);
final
completer
=
Completer
<
bool
>();
FFmpegKit
.
executeAsync
(
cmd
,
(
session
)
async
{
final
returnCode
=
await
session
.
getReturnCode
();
final
returnCode
=
await
session
.
getReturnCode
();
return
ReturnCode
.
isSuccess
(
returnCode
);
completer
.
complete
(
ReturnCode
.
isSuccess
(
returnCode
));
},
null
,
(
Statistics
statistics
)
{
if
(
onProgress
!=
null
&&
duration
>
0
)
{
final
currentTime
=
statistics
.
getTime
();
final
progress
=
(
currentTime
/
duration
).
clamp
(
0.0
,
1.0
);
onProgress
((
progress
*
100
).
floor
());
}
},
);
return
completer
.
future
;
}
/// 获取视频总时长,返回毫秒
static
Future
<
int
>
_getVideoDuration
(
String
videoPath
)
async
{
final
session
=
await
FFmpegKit
.
execute
(
'-i "
$videoPath
"'
,
);
final
output
=
await
session
.
getOutput
();
// 从 ffmpeg 输出中解析时长,格式如: Duration: 00:01:23.45
final
regex
=
RegExp
(
r'Duration:\s*(\d+):(\d+):(\d+)\.(\d+)'
);
final
match
=
regex
.
firstMatch
(
output
??
''
);
if
(
match
!=
null
)
{
final
hours
=
int
.
parse
(
match
.
group
(
1
)!);
final
minutes
=
int
.
parse
(
match
.
group
(
2
)!);
final
seconds
=
int
.
parse
(
match
.
group
(
3
)!);
final
ms
=
int
.
parse
(
match
.
group
(
4
)!);
return
(
hours
*
3600
+
minutes
*
60
+
seconds
)
*
1000
+
ms
*
10
;
}
return
0
;
}
}
/// 为视频文件生成缩略图
/// 为视频文件生成缩略图
...
...
Write
Preview
Styling with
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment