我想从文件中提取视频的帧大小。为此,我通过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++调用它并读出结果。我使用带有GCC 4.8编译器的IDE Qt 4.8.6。

对于我的代码,我使用以下模板:

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控制台向我返回此警告,并且正在返回0:
/home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi: Invalid data found when processing input

并且“管道”为空。

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

最佳答案

旧帖子,但据我所知,这里有两点:

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

这是ffprobe正常的文件处理错误。通常,它发生在媒体文件中有错误时,它与c++程序无关。

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

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

FILE *pipe = popen("ffprobe ... 2>&1");
2>重定向句柄#2输出到当前&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++版本/系统中会执行此操作,因此请检查它。无论如何,只有一切正常,它才会为零。

    10-07 18:53
    查看更多