【问题标题】:Executing bash shell command and extract output --> invalid file error执行 bash shell 命令并提取输出 --> 无效文件错误
【发布时间】:2015-11-02 08:16:25
【问题描述】:

我想从文件中提取视频的帧大小。为此,我通过 bash shell 启动了一个 ffmpeg 命令,我想提取输出。此命令在 bash shell 中运行良好,并根据需要返回输出。

ffprobe -v error -count_frames -of flat=s=_ -select_streams v:0 -show_entries stream=nb_read_frames /home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi

我想通过 C++ 调用它并读出结果。我使用 IDE Qt 4.8.6 和 GCC 4.8 编译器。

对于我的代码,我使用这个模板:

executing shell command with popen

并根据我的要求将其更改为

#include <iostream>
#include <string>
#include <stdio.h>

using namespace std;


int main()
{
   FILE* pipe = popen("echo $(ffprobe -v error -count_frames -of flat=s=_ -select_streams v:0 -show_entries stream=nb_read_frames /home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi)", "r");
 if(!pipe)
 {
     cout << "error" << endl;
     return 1;
 }
 char* buffer = new char[512];

string result;
fgets(buffer, sizeof(buffer), pipe) ;
while(!feof(pipe))
{
    if(fgets(buffer, sizeof(buffer), pipe) != NULL)
    {
        cout << buffer << endl;
        result += buffer;
    }
}
pclose(pipe);
cout << result<< endl;
return 0;   
}

Qt 控制台向我返回了这个警告,它正在以 return 0 进行渲染:

/home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi: Invalid data found when processing input

而“管道”是空的。

当我在 shell 中使用 g++ 编译上面的 main.cpp 文件时,它也很好用。

【问题讨论】:

  • 你为什么不干脆做while (fgets(...) != nullptr)?什么是String(大写S)?
  • “处理输入时发现无效数据”是一个 ffmpeg 错误,这意味着它很难读取文件。该错误不应与您的代码相关。此外,“回声”似乎是多余的。
  • @criswell 我很确定你需要 echo 进入管道。
  • @erip:popen 从命令创建管道,写入管道的是 ffprobe - @criswell 说,echo 是多余的
  • 大写 S 已更改。谢谢

标签: c++ bash shell ffmpeg qt5


【解决方案1】:

旧帖,但我看到这里有两点:

错误“处理输入时发现无效数据”

这是 ffprobe 正常文件处理错误。一般是媒体文件有错误时发生,与c++程序无关。

ffprobe 将警告/错误消息写入stderr 流,但popen 仅捕获stdout 流,这就是您的程序无法通过管道获取该错误消息的原因。

如何在我的程序中获取stdout+stderr

popen 允许执行任何 shell 命令,因此我们可以使用它将stderr 重定向到stdout,这样您的程序也可以获得该输出,如下所示:

FILE *pipe = popen("ffprobe ... 2>&1");

2&gt; 重定向句柄#2 输出到当前&amp;1 句柄#1 输出(#1=stdout,#2=stderr)。

绝对没有必要执行FILE *pipe = popen("echo $(ffprobe ...)");,因为最终结果将是相同的:注意$(...) 返回一个带有stdout 命令输出的字符串,echo 打印它。完全多余。

为了改进您的代码的一些观察:

  • 当一个字符串太大而无法在一个屏幕宽度上显示时,最好将它分成多行(也许在某些逻辑中将每一行内的文本分组),因为这将改善其他人对您的代码的阅读(并且最终在几个月内由你自己)。

    您可以使用 C/C++ 编译器功能来完成此操作,该功能可以连接由空格(换行符、制表符等)分隔的字符串,例如。 "hi " "world" 对于编译器来说与"hi world" 相同。

  • 当您的程序必须写入错误消息时,请使用stderr 流。在 C++ 中是 std::cerr 而不是 std::cout

  • 在没有使用记录器时总是分配空闲内存(每个new 必须有一个delete

  • 避免使用using namespace std;,而是为您将使用的每个标准实例/类使用using std::name;。前任。 using std::string;,可以避免将来出现问题,特别是在大型程序中。一个常见错误的示例是here。一般避免使用using namespace xxxx;

重新组织你的代码,我们有:

#include <iostream>
#include <stdio.h>

using std::string;
using std::cout;
using std::cerr;
using std::endl;

int main() {
    static char ffprobeCmd[] =
        "ffprobe " // command
            "-v error " // args
            "-count_frames "
            "-of flat=s=_ "
            "-select_streams v:0 "
            "-show_entries stream=nb_read_frames "
            "/home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi" // file
            " 2>&1"; // Send stderr to stdout

    FILE *pipe = popen(ffprobeCmd, "r");
    if (!pipe) {
        perror("Cannot open pipe.");
        return 1;
    }
    char* buffer = new char[512];

    string result;
    while ((fgets(buffer, sizeof(buffer), pipe)) != NULL) {
        result += buffer;
    }

    // See Note below
    int retCode = pclose(pipe);
    if (retCode != 0) {
        // Program ends with error, and result has the error message
        cerr << "retCode: " << retCode << "\nMessage: " << result << endl;
        return retCode;
    } else {
        // Program ends normally, prints: streams_stream_0_nb_read_frames="xxx"
        cout << result << endl;
    }

    delete buffer; // free memory
    return 0;
}

注意

pclose 并非旨在返回已执行的程序状态码,但如果您需要此值,pclose 在某些 c++ 版本/系统中会执行此操作,因此请检查一下。无论如何,只有一切正常,它才会为零。

【讨论】:

    猜你喜欢
    • 2014-06-29
    • 2013-01-01
    • 2018-02-02
    • 2012-10-08
    • 2014-01-11
    • 2018-01-16
    • 1970-01-01
    • 2015-01-29
    • 1970-01-01
    相关资源
    最近更新 更多