使用 Qt 的 QProcess 作为 popen(使用 ffmpeg rawvideo)

Posted

技术标签:

【中文标题】使用 Qt 的 QProcess 作为 popen(使用 ffmpeg rawvideo)【英文标题】:using Qt's QProcess as popen (with ffmpeg rawvideo) 【发布时间】:2019-10-06 22:18:43 【问题描述】:

我在视频应用程序中插入了一些代码以使用 ffmpeg 导出 使用标准输入(rawideo rgba 格式),快速测试它是否有效 使用popen(),测试进行得很顺利,因为应用程序是 使用 Qt 编写我想使用QProcess 修改补丁和 ->write().

应用程序没有显示错误并且可以正常工作,但是生成的 视频文件既不能用 vlc 也不能用 mplayer 播放,而 使用popen() 生成的那些都可以完美地工作。我有 感觉->close()->terminate() 没有正确关闭 ffmpeg 和因此文件,但我不知道如何验证它 我也没有找到结束执行命令的替代方法,除了 ->waitForBytesWritten() 应该等待数据写入, 建议?我做错了吗?

(显然我无法准备一个可测试的示例,这将花费我更多时间 比补丁花费的时间)

下面是我输入的代码,如#else为Qt代码

初始化

#if defined(EXPORT_POPEN) && EXPORT_POPEN == 1
      pipe_frame.file = popen("/tmp/ffmpeg-rawpipe.sh", "w");
      if (pipe_frame.file == NULL) 
        return false;
      
#else
      pipe_frame.qproc = new QProcess;
      pipe_frame.qproc->start("/tmp/ffmpeg-rawpipe.sh", QIODevice::WriteOnly);
      if(!pipe_frame.qproc->waitForStarted()) 
        return false;
      
#endif

编写框架

#if defined(EXPORT_POPEN) && EXPORT_POPEN == 1
          fwrite(pipe_frame.data, pipe_frame.width*4*pipe_frame.height , 1, pipe_frame.file);
#else
          qint64 towrite = pipe_frame.width*4*pipe_frame.height,
            written = 0, partial;
          while(written < towrite) 
            partial = pipe_frame.qproc->write(&pipe_frame.data[written], towrite-written);
            pipe_frame.qproc->waitForBytesWritten(-1);
            written += partial;
          
#endif

终止

#if defined(EXPORT_POPEN) && EXPORT_POPEN == 1
      pclose(pipe_frame.file);
#else
      pipe_frame.qproc->terminate();
      //pipe_frame.qproc->close();
#endif

编辑

ffmpeg-rawpipe.sh

#!/bin/sh
exec ffmpeg-cuda -y -f rawvideo -s 1920x1080 -pix_fmt rgba -r 25 -i - -an -c:v h264_nvenc \
        -cq:v 19 \
        -profile:v high /tmp/test.mp4

我做了一些更改,我在 open 中添加了无缓冲标志

pipe_frame.qproc->start("/tmp/ffmpeg-rawpipe.sh", QIODevice::WriteOnly|QIODevice::Unbuffered);

因此简化了写作

qint64 towrite = pipe_frame.width*4*pipe_frame.height;
pipe_frame.qproc->write(pipe_frame.data, towrite);
pipe_frame.qproc->waitForBytesWritten(-1);

我在关闭应用程序之前添加了一个 closeWriteChannel(希望停止 stdin ffmpeg 管道正确结束,以防万一,我不确定它不会)

pipe_frame.qproc->waitForBytesWritten(-1);
pipe_frame.qproc->closeWriteChannel();
//pipe_frame.qproc->terminate();
pipe_frame.qproc->close();

但没有任何变化,mp4 文件已创建并包含数据,但从 mplayer 日志中我看到它被误解了,视频格式无法识别,它会查找不存在的音频。

【问题讨论】:

分享.sh ... @eyllanesc 当然,我将它添加到答案中,但它不相关,相同的 .sh 用于 popen 和 QProcess。 【参考方案1】:

修复,在closeWriteChannel()之后添加waitForFinished(),关闭stdin并等待ffmpeg自行终止。

pipe_frame.qproc->waitForBytesWritten(-1); // perhaps not necessary
pipe_frame.qproc->closeWriteChannel();
pipe_frame.qproc->waitForFinished();
pipe_frame.qproc->close();

编辑

注意,即使使用 unbuffered 标志初始化,QProcess 和 QIODevice 似乎缓冲了很多,似乎waitForBytesWritten () 不起作用,如果你正在输入高清视频,你会很快耗尽内存。

【讨论】:

以上是关于使用 Qt 的 QProcess 作为 popen(使用 ffmpeg rawvideo)的主要内容,如果未能解决你的问题,请参考以下文章

在 Qt QProcess 中运行 sudo 命令

QProcess 无法传递参数

运行 jar 文件使用 Qt (QProcess)

Qt QProcess

Qt:无法使用 QProcess 启动 Windows 控制台

QWidget closeEvent(...) 没有被 QProcess::terminate() 调用