I'm creating a C-program under Linux, and I want to catch FFMpeg's output-data and forward it. To do so, I'm calling FFMpeg via (an implementation of) popen2. It all is working fine: I'm able to get FFMpeg's data out of the pipe via read()
. Things start to get awkward if FFMpeg stops working. read()
seems to be blocking, which is the expected behaviour, but it never exits when the pipe is closed.
I learned that, for read()
to break and detect an EOF, the pipe should be closed on both ends, so when I detect that FFMpeg is crashed or killed, I close the reading-end of my pipe. I'm assuming that, since FFMpeg is closed, it disconnects its end of the pipe.
My assumption seems to be wrong: when I close the reading-end of the pipe by using close()
, the program still seems to be hanging in read()
.
Would somebody be able to explain me what's wrong with my assumption and point me in the right direction so that I can properly detect when the program that I'm piping to or from (in this case FFMpeg) has stopped sending data?
The relevant code:
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "helper.h"
#define READ 0
#define WRITE 1
FILE *sink_stream;
int ffmpeg_sink;
pid_t popen2_2(const char **command, int *infp, int *outfp);
void* pthread_sink_ffmpeg(void *arg);
pid_t popen2_2(const char **command, int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
int devNull = open("/dev/null", O_WRONLY);
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0) //first fork
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
//dup2(devNull,2); //pipes stderr to /dev/null....
execvp(*command, command);
_exit(1);
}
if (infp == NULL)
{
close(p_stdin[WRITE]);
close(p_stdin[READ]);
}
else
{
*infp = p_stdin[WRITE];
}
if (outfp == NULL)
{
close(p_stdout[WRITE]);
close(p_stdout[READ]);
}
else
{
*outfp = p_stdout[READ];
}
return pid;
}
void* pthread_sink_ffmpeg(void *arg)
{
char *pbuffer;
int length, res;
pbuffer = malloc(4096);
while(1)
{
/* Wait for input */
dbgfprintf("Waiting for input...");
while( (length = read(ffmpeg_sink, pbuffer, 1024)) )
{
fprintf(stderr, "Read [ %d ]...\r", length);
}
/* RIP */
dbgfprintf("Done for now..");
}
free(pbuffer);
}
int main( void )
{
int wstatus;
pid_t child_pid, w;
pthread_t t_ffmpeg_source;
const char *command_ffmpeg[] = {"ffmpeg",
"-hide_banner",
"-re",
"-i", "http://relay.ah.fm/192k",
"-c:a", "libfdk_aac",
"-profile:a", "aac_he_v2",
"-b:a", "128k",
"-f", "adts",
"pipe:1",
NULL};
child_pid = popen2_2(command_ffmpeg, NULL, &ffmpeg_sink);
dbgfprintf("Started ffmpeg with pid [ %d ]...", child_pid);
pthread_create(&t_ffmpeg_source, NULL, pthread_sink_ffmpeg, NULL);
do {
w = waitpid(child_pid, &wstatus, WUNTRACED | WCONTINUED);
} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
dbgfprintf("ffmpeg terminated...");
close(ffmpeg_sink);
pthread_join(t_ffmpeg_source, NULL);
}
This code is able to start the program, receive it's data, detect the execvp'd program crash/kill, but it never gets out of read()
when the execvp'd program is killed.
I've also tried to read the pipe as a filestream;
sink_stream = fdopen(ffmpeg_sink, "r");
while( !feof(sink_stream) )
{
length = fread(pbuffer, 1, 4096, sink_stream);
fprintf(stderr, "Read [ %d ]...\r", length);
}
..which gives the same result: it reads the data but it doesn't get out of fread()
.
Any help would be highly appreciated!
Thanks in advance!