1

I want to extract the framesize of a video from a file. For this purpose, I have launched an ffmpeg command via bash shell, and I want to extract the output. This command is working well in the bash shell, and returns the output as wanted.

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

I want to call it via C++ and read out the result. I use the IDE Qt 4.8.6 with GCC 4.8 compiler.

For my code, I use this template:

executing shell command with popen

and changed it for my demands to

#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;   
}

The Qt console returned me this warning, and it is rending with return 0:

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

and "pipe" is empty.

When I compile the main.cpp file above with g++ in the shell it works nice too.

Community
  • 1
  • 1
Ingeborg
  • 87
  • 13
  • 1
    Why don't you just simply do `while (fgets(...) != nullptr)`? And what is `String` (with upper-case `S`)? – Some programmer dude Aug 10 '15 at 12:12
  • 3
    "Invalid data found when processing input" is an ffmpeg error, it means it's having a hard time reading the file. That error shouldn't be related to your code. Also, the "echo" seems superfluous. – criswell Aug 10 '15 at 12:24
  • @criswell I'm pretty sure you need to `echo` into pipes. – erip Aug 10 '15 at 12:49
  • 1
    @erip: the `popen` creates the pipe from the command, it is `ffprobe` that write to the pipe - the `echo` is superfluous as @criswell says – cdarke Aug 10 '15 at 15:10
  • Uppercase S is changed. Thx – Ingeborg Aug 10 '15 at 15:24

1 Answers1

1

Old post, but as I see, there are two points here:

Error "Invalid data found when processing input"

That's an ffprobe normal file processing error. Usually it happens when there are errors inside media file, it is not related to c++ program.

ffprobe writes warning/error messages into stderr stream, but popen only captures stdout stream, that's why your program couldn't get that error message trough the pipe.

How get the stdout+stderr in my program

popen allows execute any shell command, so we can use it to redirect stderr into stdout, so your program can get that output too, like this:

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

The 2> redirect handle#2 output into current &1 handle#1 output (#1=stdout, #2=stderr).

There's absolute no need to execute FILE *pipe = popen("echo $(ffprobe ...)");, because the final result will be the same: Note that $(...) returns a string with stdout command output, and echo prints it. Totally redundant.

A few observations in order to improve your code:

  • When a string is too big to be displayed in one screen width, it's better split it into multiple lines (maybe grouping text inside each line within some logic), because that will improve the reading of your code by other people (and eventually by yourself in a few months).

    You can do this with a C/C++ compiler feature that concatenates strings separated by spaces (newlines, tab, etc.), ex. "hi " "world" is the same as "hi world" to the compiler.

  • When your program have to write error messages, use the stderr stream. In c++ that's std::cerr instead std::cout.

  • Always free memory allocated when it's no loger used (each new has to have a delete)

  • Avoid use using namespace std;, instead use using std::name; for each standard instance/class that you'll use. Ex. using std::string;, that avoids future problems, specially in big programs. An example of a common error is here. In general avoid using using namespace xxxx;.

Reorganizing your code, we have:

#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;
}

Note

pclose is not intended to return the executed program status code, but if you need this value, pclose does it in some c++ versions/systems, so check it. Anyway it will be zero only if everything was OK.

Community
  • 1
  • 1
Wilfredo Pomier
  • 1,091
  • 9
  • 12