使用C#封装FFmpeg实现视频格式转换,你学会了吗?

开发 架构
FFmpeg是一个功能强大的开源多媒体框架,可以用于视频和音频的编码、解码、转码等操作。本文将介绍如何使用C#封装FFmpeg,实现一个简单但功能完整的视频格式转换工具。

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功能
  • 监控转换进度
  • 获取视频信息
  • 灵活设置转换参数

这个实现可以作为基础,根据实际需求进行扩展,比如添加更多的转换选项、支持更多的格式等。

责任编辑:武晓燕 来源: 技术老小子
相关推荐

2024-10-16 11:28:42

2024-09-10 10:34:48

2024-11-06 11:38:59

C#单例模式

2024-05-07 07:58:47

C#程序类型

2024-10-21 07:05:14

C#特性语言

2024-05-17 08:42:52

AttributeMyClass方法

2022-06-16 07:50:35

数据结构链表

2024-02-02 11:03:11

React数据Ref

2024-07-03 08:15:39

C#字符串表达式

2022-03-05 23:29:18

LibuvwatchdogNode.js

2024-01-29 08:21:59

AndroidOpenCV车牌

2024-07-29 10:35:44

KubernetesCSI存储

2023-10-30 07:05:31

2023-12-27 07:31:45

json产品场景

2021-12-01 07:19:44

C# Npoi Excel

2022-11-11 08:29:24

C语言中文字符代码

2024-03-11 08:27:43

前端图片格式

2023-08-01 12:51:18

WebGPT机器学习模型

2024-01-02 12:05:26

Java并发编程

2024-01-19 08:25:38

死锁Java通信
点赞
收藏

51CTO技术栈公众号