首页 > 编程语言 >【C#】使用ffmpeg image2pipe将图片保存为mp4视频

【C#】使用ffmpeg image2pipe将图片保存为mp4视频

时间:2022-12-02 22:36:01浏览次数:52  
标签:image2pipe ffmpeg C# passthrough mp4 timestamp StartInfo proc

文章目录
需求
实现
需求
在正式开始之前,先介绍下我的需求是怎么样的,基于此需求如何使用ffmpeg实现。仅供参考。

需求点:

将图片保存为视频
图片数量不是固定的,是由上游的webrtc传下的帧数据,转成的bitmap。所以只要webrtc开着,图片流就一直会有。
每帧图像的间隔时间依赖于不同的网络环境,所以不是固定的时间间隔。
实现
在使用原生ffmpeg之前,笔者使用了几个第三方的nuget库,如:FFmpeg.AutoGen、Xabe.FFmpeg、Accord.Video.FFMPEG。前两个库要么只支持将文件夹里现有的图片保存为mp4,要么不支持设置每帧的PTS,导致生成的mp4播放速度太快。最后选用了Accord.Video.FFMPEG,这个库能满足上述的三个需求点。无奈此库已长期不维护,当上游的FPS>15时,WriteVideoFrame方法抛出异常的频率会大大提升,导致内存泄漏,而且当前帧也会被丢掉。

然后项目使用的是.net452,一时半会版本也升级不上去,这就过滤大多数的nuget库。最后,只能使用的原生的ffmpeg了。
ffmpeg只是提供了一个exe,并没有官方的API可供我们调用,只提供了一大堆的参数说明,真是令人头大。经过不断的看文档和搜索调试之后,发现配置以下参数可以达到我们的需求。

-f image2pipe -use_wallclock_as_timestamps 1 -i - -c:v libx264 -pix_fmt yuv420p -vsync passthrough -maxrate 5000k -an -y 123.mp4
1
以下对各参数做个简单介绍:

image2pipe:使用图片管道,我们可以将图片数据一直往管道里塞,ffmpeg会不断将其添加到mp4文件中。用来满足需求1和2.
use_wallclock_as_timestamps 1:开启此选项,ffmpeg就会将接收此图片的时间作为该帧的timestamp。这样生成的MP4播放速度才正常,满足需求3.
pix_fmt yuv420p:设置像素格式,解决生成的视频无法使用windows media player播放的问题。
-vsync passthrough:可以理解为动态帧率,根据上游的帧率来决定输出mp4的帧率。默认有以下几个选项:
passthrough :使用帧原始的timestamp.
cfr (1):根据输出帧率的配置,进行自动插帧(上游帧率小于输入帧率)或者丢帧(上游帧率大于输入帧率)。
vfr (2):类似于passthrough, 不过当两帧具有timestamp时会丢弃其中一个。
drop:类似于passthrough,只不过会丢弃帧原始的timstamp,然后重新生成符合帧率要求的timestamp。
auto (-1):默认行为。在cfr和vfr之前自动选择。
maxrate:设置最大比特率
123.mp4:保存的文件名或者路径,注意里面不要有空格。
最后的C#代码如下,我们需要使用Process类来启动ffmpeg.exe。

public class FfmpegToVideoService 
{
        private bool _isRunning = false;
        private int _fps;
        private readonly Process _proc;

        /// <summary>
        /// Bitmap保存为MP4
        /// </summary>
        /// <param name="filePath">mp4要保存的路径,如:D:\\a\b\123.mp4</param>
        /// <param name="maxBitRate">最大比特率,单位K</param>
        public FfmpegToVideoService(string filePath,int maxBitRate = 5000)
        {
            var formattedPath = Path.GetFullPath(filePath);
            _proc = new Process();
            //-pix_fmt yuv420p -movflags +faststart  -r {30}  -i pipe:.bmp -r {_fps} -timecode 00:00:00.000
            //-vsync cfr自动差值 vfr根据timestamp,重复的丢弃    passthrough根据timestamp重复的不丢  -vsync passthrough
            //-r 30 入帧出帧都是30
            _proc.StartInfo.FileName = @"ffmpeg.exe";
            _proc.StartInfo.Arguments = $"-f image2pipe -use_wallclock_as_timestamps 1 -i - -c:v libx264 -pix_fmt yuv420p -vsync passthrough -maxrate {maxBitRate}k  -an -y {formattedPath}";
            _proc.StartInfo.WorkingDirectory = CommonFunctions.BasePath;
            _proc.StartInfo.UseShellExecute = false;
            _proc.StartInfo.RedirectStandardInput = true;
            _proc.StartInfo.RedirectStandardOutput = true;
            _proc.Start();
        }

        // 将Bitmap数据写入管道
        private void SendToPipe(Bitmap bitmap)
        {
            if (_proc.StartInfo.RedirectStandardInput)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    bitmap.Save(ms, ImageFormat.Png);
                    ms.WriteTo(_proc.StandardInput.BaseStream);
                }
            }
        }

        /// <summary>
        /// 异步线程启动服务
        /// </summary>
        public override void StartAsync()
        {
            _isRunning = true;
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        public override void Stop()
        {
            _isRunning = false;
            try
            {
                _proc.StartInfo.RedirectStandardInput = false;
                _proc.StartInfo.RedirectStandardOutput = false;
                _proc.StandardInput.Close();
                _proc.StandardOutput.Close();
                _proc.Close();
            }
            catch (Exception ex)
            {
                Log.Error(ex, "");
            }
        }

        /// <summary>
        /// 添加item
        /// </summary>
        /// <param name="item"></param>
        public override void Add(FrameInfo item)
        {
            if(_isRunning)
            {
                SendToPipe(item.Bitmap);
            }
        }
    }

https://trac.ffmpeg.org/wiki/Slideshow
https://ffmpeg.org/ffmpeg.html#filter_005foption
https://stackoverflow.com/questions/60977555/adding-current-time-as-timestamp-in-h-264-raw-stream-with-few-frames
————————————————
版权声明:本文为CSDN博主「JimCarter」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/catshitone/article/details/126930470

 

标签:image2pipe,ffmpeg,C#,passthrough,mp4,timestamp,StartInfo,proc
From: https://www.cnblogs.com/webenh/p/16945834.html

相关文章

  • ProcessStartInfo 类
    定义命名空间:System.Diagnostics程序集:System.Diagnostics.Process.dll指定启动进程时使用的一组值。C#复制 publicsealedclassProcessStartInfo继承......
  • USACO 2018 Jan 题解
    USACO2018JanSolution目录USACO2018JanSolution更好的阅读体验戳此进入题面Luogu链接LG-P4188[USACO18JAN]LifeguardsS题面SolutionCodeLG-P4182[USACO18JAN]L......
  • USACO 2018 Feb 题解
    USACO2018FebSolution目录USACO2018FebSolution更好的阅读体验戳此进入题面Luogu链接LG-P4266[USACO18FEB]RestStopsS题面ExamplesSolutionCodeLG-P4264[USAC......
  • ForexClub:如何在外汇交易中使用心理水平
    在下面的澳元/日元图表中,75.00的价格水平出现了六次强烈的拐点。每次价格接近75.00时,货币对都会反弹。这是因为交易员看到75.00的价格并认为这是便宜的,这促使澳......
  • CISAW风险管理学习笔记(4)-风险管理标准ISO31000
    个人学习总结,CISAW学习笔记之风险管理标准ISO31000......
  • static
    带static的方法,和不带static的方法,带静态修饰符的是普通方法,不带的是实例方法。实例方法不能直接引用,要先new对象,不然就会报错。不带static是对象级别的行为,带上stati......
  • [ABC248Ex] Beautiful Subsequences 题解
    [ABC248Ex]BeautifulSubsequencesSolution目录[ABC248Ex]BeautifulSubsequencesSolution更好的阅读体验戳此进入题面SolutionCodeCodeUPD更好的阅读体验戳此进入......
  • 使用 cookies
    访问的页面需要cookies时,在脚本中加入“配置元件”­>“HTTPCookies管理器”,要点:cookies管理器元件需要位于需要使用cookies的请求的上一级节点。 在示例中加......
  • USACO 2017 Dec 题解
    USACO2017DecSolution目录USACO2017DecSolution更好的阅读体验戳此进入题面Luogu链接LG-P4122[USACO17DEC]BlockedBillboardB题面ExamplesSolutionCodeLG-P408......
  • nginx反向代理websocket,ws转wss
    记录一下nginx规则location^~/echargenet-admin#本地代理目录{proxy_passhttp://127.0.0.1:8080/xxx;#websocket地址proxy_set_headerHost$host;#......