1. 简介
FFmpeg是一个功能强大的开源多媒体框架,可以用于视频和音频的编码、解码、转码等操作。本文将介绍如何使用C#封装FFmpeg,实现一个简单但功能完整的视频格式转换工具。
2. 环境准备
- Visual Studio 2022
- .NET 6.0或更高版本
- FFmpeg可执行文件 (ffmpeg.exe) 可以从官网下载:https://ffmpeg.org/download.html
3. 项目实现
3.1 创建FFmpeg包装类
首先,我们创建一个FFmpegWrapper类来封装FFmpeg的核心功能:
using System.Diagnostics;
public class FFmpegWrapper
{
private readonly string _ffmpegPath;
// FFmpeg进程执行的事件委托
public delegate void OnProgressHandler(double percentage);
public event OnProgressHandler OnProgress;
// 构造函数
public FFmpegWrapper(string ffmpegPath)
{
_ffmpegPath = ffmpegPath;
if (!File.Exists(_ffmpegPath))
{
throw new FileNotFoundException("FFmpeg executable not found!", _ffmpegPath);
}
}
/// <summary>
/// 获取视频信息
/// </summary>
public async Task<VideoInfo> GetVideoInfoAsync(string inputPath)
{
if (!File.Exists(inputPath))
{
throw new FileNotFoundException("Input video file not found!", inputPath);
}
var startInfo = new ProcessStartInfo
{
FileName = _ffmpegPath,
Arguments = $"-i \"{inputPath}\"",
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var process = new Process { StartInfo = startInfo };
process.Start();
string output = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
// 解析视频信息
return ParseVideoInfo(output);
}
/// <summary>
/// 转换视频格式
/// </summary>
public async Task ConvertVideoAsync(string inputPath, string outputPath, VideoConversionOptions options)
{
if (!File.Exists(inputPath))
{
throw new FileNotFoundException("Input video file not found!", inputPath);
}
// 构建FFmpeg命令
string arguments = BuildFFmpegArguments(inputPath, outputPath, options);
var startInfo = new ProcessStartInfo
{
FileName = _ffmpegPath,
Arguments = arguments,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var process = new Process { StartInfo = startInfo };
// 添加输出数据接收处理
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data != null)
{
UpdateProgress(e.Data);
}
};
process.Start();
process.BeginErrorReadLine();
await process.WaitForExitAsync();
if (process.ExitCode != 0)
{
throw new Exception($"FFmpeg process exited with code {process.ExitCode}");
}
}
/// <summary>
/// 构建FFmpeg命令参数
/// </summary>
private string BuildFFmpegArguments(string inputPath, string outputPath, VideoConversionOptions options)
{
var args = new List<string>
{
"-i", $"\"{inputPath}\"",
"-y" // 覆盖输出文件
};
// 添加视频编码器
if (!string.IsNullOrEmpty(options.VideoCodec))
{
args.Add("-c:v");
args.Add(options.VideoCodec);
}
// 添加音频编码器
if (!string.IsNullOrEmpty(options.AudioCodec))
{
args.Add("-c:a");
args.Add(options.AudioCodec);
}
// 设置视频比特率
if (options.VideoBitrate > 0)
{
args.Add("-b:v");
args.Add($"{options.VideoBitrate}k");
}
// 设置音频比特率
if (options.AudioBitrate > 0)
{
args.Add("-b:a");
args.Add($"{options.AudioBitrate}k");
}
// 设置分辨率
if (!string.IsNullOrEmpty(options.Resolution))
{
args.Add("-s");
args.Add(options.Resolution);
}
// 添加输出文件路径
args.Add($"\"{outputPath}\"");
return string.Join(" ", args);
}
/// <summary>
/// 更新转换进度
/// </summary>
private void UpdateProgress(string data)
{
// 解析FFmpeg输出,计算进度
if (data.Contains("time="))
{
try
{
var timeIndex = data.IndexOf("time=");
var timeStr = data.Substring(timeIndex + 5, 11);
var time = TimeSpan.Parse(timeStr);
// 假设我们已经知道总时长,这里简化处理
double percentage = (time.TotalSeconds / 100.0) * 100;
OnProgress?.Invoke(Math.Min(percentage, 100));
}
catch
{
// 解析失败时忽略
}
}
}
/// <summary>
/// 解析视频信息
/// </summary>
private VideoInfo ParseVideoInfo(string ffmpegOutput)
{
var videoInfo = new VideoInfo();
// 解析时长
var durationMatch = System.Text.RegularExpressions.Regex.Match(ffmpegOutput, @"Duration: (\d{2}):(\d{2}):(\d{2})\.(\d{2})");
if (durationMatch.Success)
{
videoInfo.Duration = TimeSpan.Parse($"{durationMatch.Groups[1]}:{durationMatch.Groups[2]}:{durationMatch.Groups[3]}.{durationMatch.Groups[4]}");
}
// 解析分辨率
var resolutionMatch = System.Text.RegularExpressions.Regex.Match(ffmpegOutput, @"(\d{2,4})x(\d{2,4})");
if (resolutionMatch.Success)
{
videoInfo.Width = int.Parse(resolutionMatch.Groups[1].Value);
videoInfo.Height = int.Parse(resolutionMatch.Groups[2].Value);
}
return videoInfo;
}
}
/// <summary>
/// 视频转换选项类
/// </summary>
public class VideoConversionOptions
{
public string VideoCodec { get; set; }
public string AudioCodec { get; set; }
public int VideoBitrate { get; set; }
public int AudioBitrate { get; set; }
public string Resolution { get; set; }
}
/// <summary>
/// 视频信息类
/// </summary>
public class VideoInfo
{
public TimeSpan Duration { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
3.2 使用示例
下面是如何使用FFmpegWrapper类的示例代码:
static async Task Main(string[] args)
{
try
{
// 初始化FFmpeg包装器
var ffmpeg = new FFmpegWrapper(@"D:\Software\ffmpeg-master-latest-win64-gpl-shared\bin\ffmpeg.exe");
// 设置进度回调
ffmpeg.OnProgress += (percentage) =>
{
Console.WriteLine($"转换进度: {percentage:F2}%");
};
// 获取视频信息
string inputPath = @"D:\Video\1.mp4";
var videoInfo = await ffmpeg.GetVideoInfoAsync(inputPath);
Console.WriteLine($"视频时长: {videoInfo.Duration}");
Console.WriteLine($"分辨率: {videoInfo.Width}x{videoInfo.Height}");
// 设置转换选项
var options = new VideoConversionOptions
{
VideoCodec = "libx264", // H.264编码
AudioCodec = "aac", // AAC音频编码
VideoBitrate = 2000, // 2000kbps视频比特率
AudioBitrate = 128, // 128kbps音频比特率
Resolution = "1280x720" // 720p分辨率
};
// 执行转换
string outputPath = @"D:\output.mkv";
await ffmpeg.ConvertVideoAsync(inputPath, outputPath, options);
Console.WriteLine("视频转换完成!");
}
catch (Exception ex)
{
Console.WriteLine($"错误: {ex.Message}");
}
}
图片
4. 常见视频转换场景
4.1 转换为MP4格式
var options = new VideoConversionOptions
{
VideoCodec = "libx264",
AudioCodec = "aac",
VideoBitrate = 2000,
AudioBitrate = 128
};
await ffmpeg.ConvertVideoAsync("input.avi", "output.mp4", options);
4.2 转换为WebM格式
var options = new VideoConversionOptions
{
VideoCodec = "libvpx-vp9",
AudioCodec = "libvorbis",
VideoBitrate = 1500,
AudioBitrate = 128
};
await ffmpeg.ConvertVideoAsync("input.mp4", "output.webm", options);
4.3 压缩视频
var options = new VideoConversionOptions
{
VideoCodec = "libx264",
AudioCodec = "aac",
VideoBitrate = 1000, // 降低比特率
Resolution = "1280x720" // 降低分辨率
};
await ffmpeg.ConvertVideoAsync("input.mp4", "compressed.mp4", options);
5. 总结
本文介绍了如何使用C#封装FFmpeg实现视频格式转换功能。通过封装,我们可以:
- 更方便地调用FFmpeg功能
- 监控转换进度
- 获取视频信息
- 灵活设置转换参数
这个实现可以作为基础,根据实际需求进行扩展,比如添加更多的转换选项、支持更多的格式等。