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.