使用C#封裝FFmpeg實(shí)現(xiàn)視頻格式轉(zhuǎn)換,你學(xué)會(huì)了嗎?
1. 簡(jiǎn)介
FFmpeg是一個(gè)功能強(qiáng)大的開(kāi)源多媒體框架,可以用于視頻和音頻的編碼、解碼、轉(zhuǎn)碼等操作。本文將介紹如何使用C#封裝FFmpeg,實(shí)現(xiàn)一個(gè)簡(jiǎn)單但功能完整的視頻格式轉(zhuǎn)換工具。
2. 環(huán)境準(zhǔn)備
- Visual Studio 2022
 - .NET 6.0或更高版本
 - FFmpeg可執(zhí)行文件 (ffmpeg.exe) 可以從官網(wǎng)下載:https://ffmpeg.org/download.html
 
3. 項(xiàng)目實(shí)現(xiàn)
3.1 創(chuàng)建FFmpeg包裝類(lèi)
首先,我們創(chuàng)建一個(gè)FFmpegWrapper類(lèi)來(lái)封裝FFmpeg的核心功能:
using System.Diagnostics;
public class FFmpegWrapper
{
    private readonly string _ffmpegPath;
    // FFmpeg進(jìn)程執(zhí)行的事件委托
    public delegate void OnProgressHandler(double percentage);
    public event OnProgressHandler OnProgress;
    // 構(gòu)造函數(shù)
    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>
    /// 轉(zhuǎn)換視頻格式
    /// </summary>
    public async Task ConvertVideoAsync(string inputPath, string outputPath, VideoConversionOptions options)
    {
        if (!File.Exists(inputPath))
        {
            throw new FileNotFoundException("Input video file not found!", inputPath);
        }
        // 構(gòu)建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 };
        // 添加輸出數(shù)據(jù)接收處理
        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>
    /// 構(gòu)建FFmpeg命令參數(shù)
    /// </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);
        }
        // 設(shè)置視頻比特率
        if (options.VideoBitrate > 0)
        {
            args.Add("-b:v");
            args.Add($"{options.VideoBitrate}k");
        }
        // 設(shè)置音頻比特率
        if (options.AudioBitrate > 0)
        {
            args.Add("-b:a");
            args.Add($"{options.AudioBitrate}k");
        }
        // 設(shè)置分辨率
        if (!string.IsNullOrEmpty(options.Resolution))
        {
            args.Add("-s");
            args.Add(options.Resolution);
        }
        // 添加輸出文件路徑
        args.Add($"\"{outputPath}\"");
        return string.Join(" ", args);
    }
    /// <summary>
    /// 更新轉(zhuǎn)換進(jìn)度
    /// </summary>
    private void UpdateProgress(string data)
    {
        // 解析FFmpeg輸出,計(jì)算進(jìn)度
        if (data.Contains("time="))
        {
            try
            {
                var timeIndex = data.IndexOf("time=");
                var timeStr = data.Substring(timeIndex + 5, 11);
                var time = TimeSpan.Parse(timeStr);
                // 假設(shè)我們已經(jīng)知道總時(shí)長(zhǎng),這里簡(jiǎn)化處理
                double percentage = (time.TotalSeconds / 100.0) * 100;
                OnProgress?.Invoke(Math.Min(percentage, 100));
            }
            catch
            {
                // 解析失敗時(shí)忽略
            }
        }
    }
    /// <summary>
    /// 解析視頻信息
    /// </summary>
    private VideoInfo ParseVideoInfo(string ffmpegOutput)
    {
        var videoInfo = new VideoInfo();
        // 解析時(shí)長(zhǎng)
        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>
/// 視頻轉(zhuǎn)換選項(xiàng)類(lèi)
/// </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>
/// 視頻信息類(lèi)
/// </summary>
public class VideoInfo
{
    public TimeSpan Duration { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
}3.2 使用示例
下面是如何使用FFmpegWrapper類(lèi)的示例代碼:
static async Task Main(string[] args)
{
    try
    {
        // 初始化FFmpeg包裝器
        var ffmpeg = new FFmpegWrapper(@"D:\Software\ffmpeg-master-latest-win64-gpl-shared\bin\ffmpeg.exe");
        // 設(shè)置進(jìn)度回調(diào)
        ffmpeg.OnProgress += (percentage) =>
        {
            Console.WriteLine($"轉(zhuǎn)換進(jìn)度: {percentage:F2}%");
        };
        // 獲取視頻信息
        string inputPath = @"D:\Video\1.mp4";
        var videoInfo = await ffmpeg.GetVideoInfoAsync(inputPath);
        Console.WriteLine($"視頻時(shí)長(zhǎng): {videoInfo.Duration}");
        Console.WriteLine($"分辨率: {videoInfo.Width}x{videoInfo.Height}");
        // 設(shè)置轉(zhuǎn)換選項(xiàng)
        var options = new VideoConversionOptions
        {
            VideoCodec = "libx264", // H.264編碼
            AudioCodec = "aac",     // AAC音頻編碼
            VideoBitrate = 2000,    // 2000kbps視頻比特率
            AudioBitrate = 128,     // 128kbps音頻比特率
            Resolution = "1280x720" // 720p分辨率
        };
        // 執(zhí)行轉(zhuǎn)換
        string outputPath = @"D:\output.mkv";
        await ffmpeg.ConvertVideoAsync(inputPath, outputPath, options);
        Console.WriteLine("視頻轉(zhuǎn)換完成!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"錯(cuò)誤: {ex.Message}");
    }
}
圖片
4. 常見(jiàn)視頻轉(zhuǎn)換場(chǎng)景
4.1 轉(zhuǎn)換為MP4格式
var options = new VideoConversionOptions
{
    VideoCodec = "libx264",
    AudioCodec = "aac",
    VideoBitrate = 2000,
    AudioBitrate = 128
};
await ffmpeg.ConvertVideoAsync("input.avi", "output.mp4", options);4.2 轉(zhuǎn)換為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. 總結(jié)
本文介紹了如何使用C#封裝FFmpeg實(shí)現(xiàn)視頻格式轉(zhuǎn)換功能。通過(guò)封裝,我們可以:
- 更方便地調(diào)用FFmpeg功能
 - 監(jiān)控轉(zhuǎn)換進(jìn)度
 - 獲取視頻信息
 - 靈活設(shè)置轉(zhuǎn)換參數(shù)
 
這個(gè)實(shí)現(xiàn)可以作為基礎(chǔ),根據(jù)實(shí)際需求進(jìn)行擴(kuò)展,比如添加更多的轉(zhuǎn)換選項(xiàng)、支持更多的格式等。















 
 
 


















 
 
 
 