Commit 7a995230 by tanghuan

压缩视频时,根据视频文件大小设置压缩参数

1 parent 8fc77aab
......@@ -95,9 +95,9 @@ class CompressVideoHandler extends MessageHandler {
throw Exception('参数错误');
}
final quality = params['quality'] as String?;
if (quality == null || quality.isEmpty) {
throw Exception('参数错误');
}
// if (quality == null || quality.isEmpty) {
// throw Exception('参数错误');
// }
final resolution = params['resolution'] as double?;
if (resolution == null || resolution > 1 || resolution <= 0) {
throw Exception('参数错误');
......
......@@ -85,7 +85,7 @@ class UploadFileHandler extends MessageHandler {
if (mimeType != 'video/mp4') {
success = await VideoUtil.convertToMp4(inputPath, outputPath);
} else {
success = await VideoUtil.compressVideo(inputPath, outputPath, 'low');
success = await VideoUtil.compressVideo(inputPath, outputPath, null);
}
var endTime = DateTime.now();
debugPrint('====================>压缩耗时:${endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch} 毫秒');
......
......@@ -181,7 +181,7 @@ class UploadStartHandler extends MessageHandler {
success = await VideoUtil.compressVideo(
inputPath,
outputPath,
'low',
null,
onProgress: (progress) {
// progress 范围 0 ~ 100
debugPrint('压缩进度: $_cmdUnique $progress%');
......
......@@ -8,7 +8,9 @@ import 'package:ffmpeg_kit_flutter_new/statistics.dart';
class VideoUtil {
///
/// 将视频格式转换为mp4
/// 转码的同时,进行压缩
/// 转码的同时,根据文件大小自动选择压缩参数
/// iOS: h264_videotoolbox 硬件加速 + 按文件大小自动码率
/// Android: libx264 + 按文件大小自动CRF
/// [onProgress] 进度回调,值范围 0.0 ~ 1.0
///
static Future<bool> convertToMp4(
......@@ -18,26 +20,29 @@ class VideoUtil {
}) async {
// 先获取视频总时长(微秒)
final duration = await _getVideoDuration(inputPath);
final fileSize = await File(inputPath).length();
String cmd;
if (Platform.isIOS) {
final bitrate = _getBitrateByFileSize(fileSize);
cmd = '-i "$inputPath" '
'-c:v h264_videotoolbox ' // 启用 iOS 硬件加速
'-b:v 1500k ' // 限制视频码率为 1.5Mbps (体积小,手机看足够)
'-b:v ${bitrate}k ' // 根据文件大小自动计算码率
'-vf scale=1280:-2 ' // 缩放到 720p (保持比例)
'-c:a aac ' // 音频转为 AAC (兼容性最好)
'-b:a 128k ' // 音频码率
'"$outputPath"';
} else {
cmd = '-i "$inputPath" ' // 指定输入文件路径
final crf = _getCrfByFileSize(fileSize);
cmd = '-i "$inputPath" '
'-c:v libx264 ' // 设置视频编码器为libx264(H.264)
'-crf 28 ' // 设置恒定速率因子CRF为28(中等压缩质量)
'-crf $crf ' // 根据文件大小自动计算CRF
'-c:a aac ' // 设置音频编码器为AAC
'-b:a 128k ' // 设置音频比特率为128kbps
'-preset fast '
'-threads 0 '
'-strict experimental ' // 允许使用实验性编解码器功能
'-movflags faststart ' // 优化MP4文件结构,使视频可以快速启动播放
'-movflags faststart ' // 优化MP4文件结构
'-f mp4 ' // 指定输出格式为MP4
'"$outputPath"'; // 指定输出文件路径
}
......@@ -65,12 +70,14 @@ class VideoUtil {
///
/// 通过 ffmpeg 压缩视频
/// [quality] 压缩质量,可选值: 'low' | 'middle' | 'high'
/// 未指定时根据文件大小自动计算CRF:文件越大压缩率越高,文件越小清晰度越高
/// [onProgress] 进度回调,值范围 0.0 ~ 1.0
///
static Future<bool> compressVideo(
String inputPath,
String outputPath,
String quality, {
String? quality, {
void Function(int progress)? onProgress,
}) async {
final duration = await _getVideoDuration(inputPath);
......@@ -80,6 +87,7 @@ class VideoUtil {
// 中等质量: CRF 23-26
// 低质量: CRF 28-32
int crf;
if (quality != null && quality.isNotEmpty) {
switch (quality) {
case 'low':
crf = 32;
......@@ -93,6 +101,11 @@ class VideoUtil {
default:
throw Exception('参数错误');
}
} else {
// 根据文件大小自动计算CRF
final fileSize = await File(inputPath).length();
crf = _getCrfByFileSize(fileSize);
}
String cmd = '-i "$inputPath" ' // 输入文件
'-c:v libx264 ' // 视频编码器
'-crf $crf ' // 恒定速率因子(质量控制)
......@@ -125,6 +138,47 @@ class VideoUtil {
return completer.future;
}
/// 根据文件大小自动计算视频码率(kbps)
/// 用于iOS h264_videotoolbox硬件加速模式(不支持CRF,需用码率控制质量)
/// 文件越大码率越低(压缩率越高,清晰度越低)
/// 文件越小码率越高(压缩率越低,清晰度越高)
static int _getBitrateByFileSize(int fileSizeBytes) {
final fileSizeMB = fileSizeBytes / (1024 * 1024);
if (fileSizeMB < 5) {
return 4000; // 小文件:高质量
} else if (fileSizeMB < 20) {
return 2500; // 中小文件:较高质量
} else if (fileSizeMB < 50) {
return 1500; // 中等文件:中等质量
} else if (fileSizeMB < 100) {
return 1000; // 较大文件:较低质量
} else if (fileSizeMB < 200) {
return 800; // 大文件:低质量
} else {
return 600; // 超大文件:最低质量
}
}
/// 根据文件大小自动计算CRF值
/// 文件越大CRF越高(压缩率越高,清晰度越低)
/// 文件越小CRF越低(压缩率越低,清晰度越高)
static int _getCrfByFileSize(int fileSizeBytes) {
final fileSizeMB = fileSizeBytes / (1024 * 1024);
if (fileSizeMB < 5) {
return 20; // 小文件:高质量
} else if (fileSizeMB < 20) {
return 23; // 中小文件:较高质量
} else if (fileSizeMB < 50) {
return 26; // 中等文件:中等质量
} else if (fileSizeMB < 100) {
return 28; // 较大文件:较低质量
} else if (fileSizeMB < 200) {
return 30; // 大文件:低质量
} else {
return 32; // 超大文件:最低质量
}
}
/// 获取视频总时长,返回毫秒
static Future<int> _getVideoDuration(String videoPath) async {
final session = await FFmpegKit.execute(
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!