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

对于一些MP4文件的压缩

事情是这样的

有一些MP4文件文件,体积比较大,需要压缩。

处理

这些视频文件主要是一些网课。20-30分钟一个视频,文件的大小在 300到500M。经过压缩后基本变为原来的十分之一的大小(例如452M的视频压缩到38M)。画面质量有所下降,但是不妨碍观看。

根据这批视频的特点。开始选择压缩的方式。

对于每个视频:

第一步先使用 FfmpegMediaInfo 获得视频的信息。

然后根据视频的信息,生成压缩参数

默认参数为 -crf 25

string arguments = @" -y -i ""[fn1]""   -vcodec libx264  -crf 25  ".Replace("[fn1]", fn);

先判断视频宽度:

if (mediaInfo.Width >= 1280)
{
arguments = arguments + "  -vf scale=1080:-1";
}

然后设置-b:v
if (mediaInfo.Bitrate > 0)
{
if (mediaInfo.Bitrate < 300)
{
}
else if (mediaInfo.Bitrate < 400)
{
arguments = arguments + " -b:v 300k";
}
else if (mediaInfo.Bitrate < 512)
{
arguments = arguments + " -b:v 400k";
}
else
{
arguments = arguments + " -b:v 512k";
}
}

然后设置帧率,因为这些视频基本都是网课,所以帧率可以设置到很低

                                if (!string.IsNullOrEmpty(mediaInfo.AvgFrameRate))
{
double r;

                                    if (double.TryParse(mediaInfo.AvgFrameRate.Trim(), out r))
{
if (r > 16)
arguments = arguments + " -r 16";
}
else
{
int ri = (int)r;
ri = ri - 2;
if (ri < 5)
ri = 5;
arguments = arguments + " -r " + ri.ToString();
}
}
else
{
arguments = arguments + " -r 16";
}

然后设置音频,32k 差不多是电话质量,对于普通对话来说还可以。

arguments = arguments + " -ac 1 -b:a 32k ";

然后压缩 

var compressor = new VideoCompressor(ffmpegPath);
compressor.ProgressUpdated += ProgressUpdated;
compressor.CompressionCompleted += CompressionCompleted;

 CompressionResult cr = await compressor.CompressVideoAsync(arguments, mediaInfo);

相关代码

{public class FfmpegMediaInfo{private string _ffmpegPath;public FfmpegMediaInfo(string ffmpegPath){if (!File.Exists(ffmpegPath))throw new FileNotFoundException("FFmpeg executable not found", ffmpegPath);_ffmpegPath = ffmpegPath;}public MediaInfo GetMediaInfo(string filePath){if (!File.Exists(filePath))throw new FileNotFoundException("Media file not found", filePath);var mediaInfo = new MediaInfo();try{// 获取详细媒体信息string output = ExecuteFfmpegCommand($"-i \"{filePath}\"");// 按行解析输出信息ParseMediaInfoByLines(output, mediaInfo);// 获取文件大小mediaInfo.FileSize = new FileInfo(filePath).Length;// 获取文件路径和名称mediaInfo.FilePath = filePath;mediaInfo.FileName = Path.GetFileName(filePath);mediaInfo.FileExtension = Path.GetExtension(filePath);}catch (Exception ex){Console.WriteLine($"Error getting media info: {ex.Message}");throw;}return mediaInfo;}private void ParseMediaInfoByLines(string output, MediaInfo mediaInfo){try{// 将输出按行分割string[] lines = output.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);bool isVideoStream = false;bool isAudioStream = false;foreach (string line in lines){string trimmedLine = line.Trim();// 解析格式信息if (trimmedLine.StartsWith("Duration:")){ParseDurationLine(trimmedLine, mediaInfo);}// 解析视频流信息else if (trimmedLine.Contains(": Video:")){ParseVideoStreamLine(trimmedLine, mediaInfo);isVideoStream = true;isAudioStream = false;}// 解析音频流信息else if (trimmedLine.Contains(": Audio:")){ParseAudioStreamLine(trimmedLine, mediaInfo);isVideoStream = false;isAudioStream = true;}// 解析流的详细信息(如果有的话)else if (trimmedLine.StartsWith("Stream #") && !trimmedLine.Contains("Video:") && !trimmedLine.Contains("Audio:")){isVideoStream = false;isAudioStream = false;}}// 设置视频和音频时长(如果未单独获取)if (mediaInfo.VideoDuration == TimeSpan.Zero)mediaInfo.VideoDuration = mediaInfo.Duration;if (mediaInfo.AudioDuration == TimeSpan.Zero)mediaInfo.AudioDuration = mediaInfo.Duration;}catch (Exception ex){Console.WriteLine($"Error parsing media info: {ex.Message}");throw;}}private void ParseDurationLine(string line, MediaInfo mediaInfo){try{// 格式: Duration: 00:05:30.12, start: 0.000000, bitrate: 1280 kb/sstring[] parts = line.Split(',');foreach (string part in parts){string trimmedPart = part.Trim();if (trimmedPart.StartsWith("Duration:")){string durationStr = trimmedPart.Substring("Duration:".Length).Trim();mediaInfo.Duration = TimeSpan.Parse(durationStr);}else if (trimmedPart.StartsWith("start:")){string startStr = trimmedPart.Substring("start:".Length).Trim();mediaInfo.StartTime = double.Parse(startStr);}else if (trimmedPart.StartsWith("bitrate:")){string bitrateStr = trimmedPart.Substring("bitrate:".Length).Trim().Replace("kb/s", "").Trim();mediaInfo.Bitrate = int.Parse(bitrateStr);}}}catch (Exception ex){Console.WriteLine($"Error parsing duration line: {ex.Message}");}}private void ParseVideoStreamLine(string line, MediaInfo mediaInfo){try{// 格式示例: Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 1150 kb/s, 24 fps, 24 tbr, 12288 tbn, 48 tbc (default)string[] parts = line.Split(',');foreach (string part in parts){string trimmedPart = part.Trim();if (trimmedPart.Contains(": Video:")){// 提取视频编码string videoPart = trimmedPart.Substring(trimmedPart.IndexOf(": Video:") + ": Video:".Length).Trim();mediaInfo.VideoCodec = videoPart.Split()[0];}else if (trimmedPart.Contains("x") && IsResolution(trimmedPart)){// 提取分辨率mediaInfo.Resolution = trimmedPart;string[] resolutionParts = trimmedPart.Split('x');if (resolutionParts.Length == 2){mediaInfo.Width = int.Parse(resolutionParts[0]);mediaInfo.Height = int.Parse(resolutionParts[1].Split()[0]);}}else if (trimmedPart.EndsWith("kb/s")){// 提取视频码率string bitrateStr = trimmedPart.Replace("kb/s", "").Trim();if (int.TryParse(bitrateStr, out int bitrate)){mediaInfo.VideoBitrate = bitrate;}}else if (trimmedPart.EndsWith("fps") || trimmedPart.Contains("tbr")){// 提取帧率if (trimmedPart.EndsWith("fps")){mediaInfo.FrameRate = trimmedPart.Replace("fps", "").Trim();mediaInfo.AvgFrameRate = mediaInfo.FrameRate;}else if (trimmedPart.Contains("tbr")){string tbrValue = trimmedPart.Split()[0];if (tbrValue.Contains("/")){var fpsParts = tbrValue.Split('/');if (fpsParts.Length == 2){double numerator = double.Parse(fpsParts[0]);double denominator = double.Parse(fpsParts[1]);mediaInfo.AvgFrameRate = (numerator / denominator).ToString("F2");}}else{mediaInfo.AvgFrameRate = tbrValue;}}}else if (!trimmedPart.Contains("kb/s") && !trimmedPart.Contains("fps") &&!trimmedPart.Contains("tbr") && !trimmedPart.Contains("x") &&!string.IsNullOrEmpty(trimmedPart)){// 尝试提取像素格式if (string.IsNullOrEmpty(mediaInfo.PixelFormat)){mediaInfo.PixelFormat = trimmedPart;}}}}catch (Exception ex){Console.WriteLine($"Error parsing video stream line: {ex.Message}");}}private void ParseAudioStreamLine(string line, MediaInfo mediaInfo){try{// 格式示例: Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)string[] parts = line.Split(',');foreach (string part in parts){string trimmedPart = part.Trim();if (trimmedPart.Contains(": Audio:")){// 提取音频编码string audioPart = trimmedPart.Substring(trimmedPart.IndexOf(": Audio:") + ": Audio:".Length).Trim();mediaInfo.AudioCodec = audioPart.Split()[0];}else if (trimmedPart.EndsWith("Hz")){// 提取采样率mediaInfo.SampleRate = trimmedPart;}else if (trimmedPart.Contains("channel") || trimmedPart.Contains("stereo") ||trimmedPart.Contains("mono") || trimmedPart.Contains("surround")){// 提取声道信息mediaInfo.Channels = trimmedPart;}else if (trimmedPart.EndsWith("kb/s")){// 提取音频码率string bitrateStr = trimmedPart.Replace("kb/s", "").Trim();if (int.TryParse(bitrateStr, out int bitrate)){mediaInfo.AudioBitrate = bitrate;}}}}catch (Exception ex){Console.WriteLine($"Error parsing audio stream line: {ex.Message}");}}private bool IsResolution(string part){// 简单判断是否为分辨率格式 (数字x数字)if (string.IsNullOrEmpty(part))return false;string[] parts = part.Split('x');if (parts.Length != 2)return false;if (!int.TryParse(parts[0], out int width))return false;string heightPart = parts[1].Split()[0];if (!int.TryParse(heightPart, out int height))return false;return width > 0 && height > 0;}private string ExecuteFfmpegCommand(string arguments){try{using (var process = new Process()){process.StartInfo.FileName = _ffmpegPath;process.StartInfo.Arguments = arguments;process.StartInfo.RedirectStandardOutput = true;process.StartInfo.RedirectStandardError = true;process.StartInfo.UseShellExecute = false;process.StartInfo.CreateNoWindow = true;process.StartInfo.StandardOutputEncoding = Encoding.UTF8;process.StartInfo.StandardErrorEncoding = Encoding.UTF8;process.Start();string output = process.StandardOutput.ReadToEnd();string error = process.StandardError.ReadToEnd();process.WaitForExit();// FFmpeg通常将信息输出到stderrreturn error + output;}}catch (Exception ex){Console.WriteLine($"Error executing FFmpeg command: {ex.Message}");throw;}}}public class MediaInfo{// 文件信息public string FilePath { get; set; }public string FileName { get; set; }public string FileExtension { get; set; }public long FileSize { get; set; }// 总体信息public TimeSpan Duration { get; set; }public double StartTime { get; set; }public int Bitrate { get; set; }// 视频信息public string VideoCodec { get; set; }public string Resolution { get; set; }public int Width { get; set; }public int Height { get; set; }public string PixelFormat { get; set; }public string FrameRate { get; set; }public string AvgFrameRate { get; set; }public TimeSpan VideoDuration { get; set; }public int VideoBitrate { get; set; }// 音频信息public string AudioCodec { get; set; }public string SampleRate { get; set; }public string Channels { get; set; }public int AudioBitrate { get; set; }public TimeSpan AudioDuration { get; set; }public override string ToString(){return $"MediaInfo: {FileName}\n" +$"Duration: {Duration}\n" +$"Resolution: {Width}x{Height}\n" +$"Video Codec: {VideoCodec}\n" +$"Audio Codec: {AudioCodec}\n" +$"FrameRate: {FrameRate}\n" +$"AvgFrameRate: {AvgFrameRate}\n" +$"Bitrate: {Bitrate} kbps\n" +$"File Size: {FormatFileSize(FileSize)}";}public string FormatFileSize(long bytes){if (bytes < 1024) return $"{bytes} B";else if (bytes < 1024 * 1024) return $"{(bytes / 1024.0):F2} KB";else if (bytes < 1024 * 1024 * 1024) return $"{(bytes / (1024.0 * 1024.0)):F2} MB";else return $"{(bytes / (1024.0 * 1024.0 * 1024.0)):F2} GB";}}public class VideoCompressor{private string _ffmpegPath;private CancellationTokenSource _cts;// 压缩进度事件public event EventHandler<CompressionProgressEventArgs> ProgressUpdated;// 压缩完成事件public event EventHandler<CompressionCompletedEventArgs> CompressionCompleted;public VideoCompressor(string ffmpegPath){if (!File.Exists(ffmpegPath))throw new FileNotFoundException("FFmpeg executable not found", ffmpegPath);_ffmpegPath = ffmpegPath;}/// <summary>/// 压缩视频文件/// </summary>/// <param name="inputPath">输入文件路径</param>/// <param name="outputPath">输出文件路径</param>/// <param name="quality">质量参数 (0-51, 越低质量越好)</param>/// <param name="crf">CRF参数 (0-51, 越低质量越好,默认23)</param>/// <param name="preset">预设参数 (ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow)</param>
//        public async Task CompressVideoAsync(string inputPath, string outputPath, int crf = 23, string preset = "medium", CancellationToken cancellationToken = default)public async Task<CompressionResult> CompressVideoAsync(string arguments, MediaInfo mediaInfo, CancellationToken cancellationToken = default){var result = new CompressionResult{Success = false };_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);try{// 构建FFmpeg命令 using (var process = new Process()){process.StartInfo.FileName = _ffmpegPath;process.StartInfo.Arguments = arguments;process.StartInfo.RedirectStandardOutput = true;process.StartInfo.RedirectStandardError = true;process.StartInfo.UseShellExecute = false;process.StartInfo.CreateNoWindow = true;process.StartInfo.StandardOutputEncoding = Encoding.UTF8;process.StartInfo.StandardErrorEncoding = Encoding.UTF8;// 进度跟踪变量double lastProgress = 0;DateTime lastUpdateTime = DateTime.MinValue;// 错误输出处理process.ErrorDataReceived += (sender, e) =>{if (string.IsNullOrEmpty(e.Data))return;try{// 解析进度信息string data = e.Data.Trim();if (data.StartsWith("frame=") && data.Contains("time=")){// 格式示例: frame=  100 fps= 24 q=23.0 size=   10240kB time=00:00:04.17 bitrate=20000.0kbits/s speed=   1xstring[] parts = data.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);foreach (string part in parts){if (part.StartsWith("time=")){string timeStr = part.Substring("time=".Length);if (TimeSpan.TryParse(timeStr, out TimeSpan currentTime)){double progress = (currentTime.TotalSeconds / mediaInfo.Duration.TotalSeconds) * 100;progress = Math.Min(progress, 100); // 确保不超过100%// 限制更新频率,避免UI过于频繁刷新if (progress > lastProgress + 0.5 ||(progress > 0 && DateTime.Now - lastUpdateTime > TimeSpan.FromSeconds(1))){lastProgress = progress;lastUpdateTime = DateTime.Now;// 触发进度更新事件ProgressUpdated?.Invoke(this, new CompressionProgressEventArgs{ProgressPercentage = progress,CurrentTime = currentTime,TotalDuration = mediaInfo.Duration,Status = "Compressing..."});}}break;}}}else if (data.Contains("Error") || data.Contains("error") || data.Contains("failed")){// 触发错误事件CompressionCompleted?.Invoke(this, new CompressionCompletedEventArgs{Success = false,ErrorMessage = data });}}catch (Exception ex){Console.WriteLine($"Error parsing FFmpeg output: {ex.Message}");}};// 启动进程process.Start();process.BeginErrorReadLine();// 等待进程完成或取消var processTask = Task.Run(() => process.WaitForExit(), _cts.Token);try{await processTask;}catch (TaskCanceledException){// 取消操作process.Kill();throw new OperationCanceledException("Compression was canceled");}// 检查进程退出代码if (process.ExitCode == 0){// 压缩成功CompressionCompleted?.Invoke(this, new CompressionCompletedEventArgs{Success = true});result.Success = true;}else{// 压缩失败CompressionCompleted?.Invoke(this, new CompressionCompletedEventArgs{Success = false,ErrorMessage = $"FFmpeg exited with code {process.ExitCode}" });result.ErrorMessage = string.IsNullOrEmpty(result.ErrorMessage)? $"FFmpeg exited with code {process.ExitCode}": result.ErrorMessage;}}}catch (OperationCanceledException){throw;}catch (Exception ex){CompressionCompleted?.Invoke(this, new CompressionCompletedEventArgs{Success = false,ErrorMessage = ex.Message });result.ErrorMessage = ex.Message;}finally{_cts.Dispose();}return result;}/// <summary>/// 取消当前的压缩操作/// </summary>public void CancelCompression(){_cts?.Cancel();}}public class CompressionResult{/// <summary>是否成功</summary>public bool Success { get; set; }/// <summary>是否被取消</summary>public bool Canceled { get; set; }/// <summary>错误信息</summary>public string ErrorMessage { get; set; } public override string ToString(){if (Success){return $"Compression succeeded. ";}else if (Canceled){return "Compression was canceled.";}else{return $"Compression failed: {ErrorMessage}";}}}// 进度事件参数public class CompressionProgressEventArgs : EventArgs{public double ProgressPercentage { get; set; }public TimeSpan CurrentTime { get; set; }public TimeSpan TotalDuration { get; set; }public string Status { get; set; }}// 完成事件参数public class CompressionCompletedEventArgs : EventArgs{public bool Success { get; set; } public string ErrorMessage { get; set; } }
}

http://www.dtcms.com/a/549483.html

相关文章:

  • 基于Selenium和AI的图像处理
  • Selenium Wire 网络拦截实现方案
  • 无锡手机网站制作费用网页设计与网站建设在线考试
  • 【Qt】【1. 版本特性介绍】
  • pyside6的历史发展、Qt 介绍、PyQt 和 pyside6对比
  • 做没用的网站建立个网站
  • numpy的random函数总结
  • ⸢ 拾-Ⅱ⸥⤳ 威胁感知与响应建设方案:威胁运营威胁响应
  • Auto Dark Mode,一款Windows 自动深浅色切换工具
  • 惠民县建设网站信宜网站设计公司
  • 论文对应项目复现教程
  • 第165期 无需提示词的微调:Bonepoke 与系统姿态的隐藏调控旋钮
  • 口腔种植中叠腮技术的适应证与考量
  • 原码、反码、补码与正数、负数的运算关系介绍
  • ShimetaPi丨事件相机新版SDK发布:支持Python调用,可降低使用门槛
  • 计算机图形学:【Games101】学习笔记03——光栅化(三角形的离散化、深度测试与抗锯齿)
  • 如何掌握【Java】 IO/NIO设计模式?工厂/适配器/装饰器/观察者模式全解析
  • C# 中的空条件运算符(?.)与空合并运算符(??)详解
  • 福建人力资源建设网站房地产销售技巧
  • 佳木斯 网站建设网页版qq登录入口版qq账号登录界面
  • 基于django网站开发课设报告广州开公司的基本流程及费用
  • VecDeque 的环形缓冲区:从 `head/tail` 到 `wrapping_add`,一次把缓存、SIMD 与 `no_std` 全部打通
  • EasyGBS在智慧仓储物流场景下视频实时监控系统应用方案
  • 网站建设分哪几种医院网站做竞价需要注意广告法
  • 数据分析平台:驱动智能决策的利器
  • 初识Java-7
  • 潍坊比较大的网站制作公司网站建设和网页制作
  • Postman vs Swagger vs PostIn,接口管理工具一文纵评
  • 如何使用React和Redux构建现代化Web应用程序
  • 湖北省住房和城乡建设厅网站首页wordpress自动刷新2次