Mplayer in slave mode - multiple instances - qt

I am developing a Qt application that is showing various media. Currently there is an issue with video files. As there were some problems in using Phonon with ATI graphic card acceleration, we are currently using mplayer with vaapi in slave mode.
There is however an issue with loading the files. Every time the new file is going to be shown, mplayer takes some time (about 2 seconds) to load it, showing only black screen. As most of the files is rather short (10 - 25 seconds) it is quite noticeable.
The first question is - does anybody knows how to tell mplayer to start loading one file while playing the previous one? Is it possible?
The second one: I was thinking of creating two instances of mplayer, telling one to load the first file and the other to load the second one then telling the second one to pause. After the first file is finished I would unpause the second one. I am using QProcesses but right now the second mplayer won't start before the second one finishes, even if I am not pausing it. In the code below, player1 and player2 are QProcess objects, and player2 won't start doing anything before player1 is finished. All "readyRead ..." slots are my functions for parsing mplayer output. So far they don't do much, just print the output to qDebug().
Do you have any idea why aren't the two processes starting together? It works ok if I use for example mplayer in player1 and vlc in player2 and I can run two mplayer instances from the command line.
bool Player::run(){
QStringList args;
args << "-va" << "vaapi" << "-vo" << "vaapi:gl" << "-noborder" << "-framedrop" << "-v" << "-slave" << "-idle";
connect(&player1, SIGNAL(readyReadStandardError()), this, SLOT(readyReadErr1()));
connect(&player1, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadOut1()));
connect(&player2, SIGNAL(readyReadStandardError()), this, SLOT(readyReadErr2()));
connect(&player2, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadOut2()));
player1.start("mplayer", args << "-geometry" << "860x540+0+0");
player2.start("mplayer", args << "-geometry" << "860x540+800+500");
player1.write("loadfile w_1.avi 1\n");
player2.write("loadfile w_2.avi 1\n");
if (!player1.waitForStarted(5000))
{
return false;
}
player2.waitForStarted(5000);
player1.waitForFinished(50000);
player2.waitForFinished(10000);
return true;
}

I don't know if you have found a solution for your problem in the meantime but I'm doing something similar from a bash script and starting multiple instances works just fine with backgrounding. The double mplayer trick is also nice, I think I might have to use that. Anyway, my bash hack after a few hours, but note that the FIFO is currently only created for one of them, I'm thinking of a good naming scheme:
#!/bin/bash
set -e
set -u
# add working directory to $PATH
export PATH=$(dirname "$0"):$PATH
res_tuple=($(xres.sh))
max_width="${res_tuple[0]}"
max_height="${res_tuple[1]}"
echo "w = $max_width, h = $max_height"
mplayer() {
/home/player/Downloads/vaapi-mplayer/mplayer \
-vo vaapi \
-va vaapi \
-fixed-vo \
-nolirc \
-slave \
-input file="$5"\
-idle \
-quiet \
-noborder \
-geometry $1x$2+$3+$4 \
{ /home/player/Downloads/*.mov } \
-loop 0 \
> /dev/null 2>&1 &
}
mfifo() {
pipe='/tmp/mplayer.pipe'
if [[ ! -p $pipe ]]; then
mkfifo $pipe
fi
}
half_width=$(($max_width / 2))
half_height=$(($max_height / 2))
mfifo
mplayer $half_width $half_height 0 0 $pipe
mplayer $half_width $half_height $half_width 0 $pipe
mplayer $half_width $half_height 0 $half_height $pipe
mplayer $half_width $half_height $half_width $half_height $pipe

Related

arp command with grep argument in QProcess [duplicate]

I'm using Qt and bash over it, need to execute something like:
bash: cat file | grep string
in Qt:
QString cmd = "cat file | grep string";
QProcess *process = new QProcess;
process->start(cmd);
process->waitForBytesWritten();
process->waitForFinished();
qDebug() << process->readAll();
The problem is in pipe ("|"), and process returs nothing. If there is no ("|"), like
"cat file"
everything is ok.
I tried smth. like
"cat file \\| grep string",
"cat file \| grep string"
but result is the same. If I copy the command and run it in bash everything is ok.
QString::toAscii().data()
and other transforms also have bad result.
The problem is you cannot run a system command with QProcess, but only a single process. So the workaround will be to pass your command as an argument to bash:
process.start("bash", QStringList() << "-c" << "cat file | grep string");
The quick and dirty hack would be this:
QString cmd = "/bin/sh -c \"cat file | grep string\"";
You could also avoid the escaping in there with C++11's R"", but the point is that do not use bash in there because that will make it only work with bash. It will not work on embedded with busybox without bash, just ash, or any other common desktop shell.
/bin/sh is usually a symlink to the shell interpreter used, so that will eventually work.
BUT!
I think you are thinking a bit too low-level when using a high-level C++/OOP framework such as Qt. I would not recommend to invoke the commands in the low-level way when you run it from bash. There is some dedicated high-level convenience API for this use case.
Based on the official documentation, QProcess is supposed to work for pipe'd commands:
void QProcess::setStandardOutputProcess(QProcess * destination)
Pipes the standard output stream of this process to the destination process' standard input.
In other words, the command1 | command2 shell command command can be achieved in the following way:
QProcess process1;
QProcess process2;
process1.setStandardOutputProcess(&process2);
process1.start("cat file");
process2.start("grep string");
process2.setProcessChannelMode(QProcess::ForwardedChannels);
// Wait for it to start
if(!process1.waitForStarted())
return 0;
bool retval = false;
QByteArray buffer;
while ((retval = process2.waitForFinished()));
buffer.append(process2.readAll());
if (!retval) {
qDebug() << "Process 2 error:" << process2.errorString();
return 1;
}
qDebug() << "Buffer data" << buffer;
This is not the main point, but a useful suggestion: do not use QString::toAscii(). That API has been deprecated in Qt 5.
The problem is that when you call process->start(cmd), the commands following the the call to cat are all interpreted as arguments to cat, so the pipe is not doing what you're expecting. If you start with a call to bash with a parameter of a string, you should get what you want: -
QString cmd = "bash -c \"cat file | grep string\"";
Alternatively, you could just call "cat file" and do the search on the returned QString when you read the output from the QProcess
how about this :
QString program = "program";
QStringList arguments;
download = new QProcess(this);
download->start(program, arguments);
If Google brought you here and you are using PyQt5 or PySide2
process1 = QProcess()
process2 = QProcess()
process1.setStandardOutputProcess(process2)
process1.start(cat, [file])
process2.start(grep, [string])

using Qt's QProcess as popen (with ffmpeg rawvideo)

I inserted some code in a video application to export using ffmpeg
with stdin (rawideo rgba format), to quickly test that it worked I
used popen(), the tests went well and since the application is
written using Qt I thought of modify the patch using QProcess and
->write().
The application shows no errors and works properly but the generated
video files are not playable neither with vlc nor with mplayer while
those generated with popen() work perfectly with both. I have the
feeling that ->close() or ->terminate() does not properly close
ffmpeg and consequently the file, but I don't know how to verify it
nor I found alternative ways to end the executed command, beside
->waitForBytesWritten() should wait for the data to be written,
suggestions? Am I doing something wrong?
(Obviously I can't prepare a testable example it would take me more
time than the patch took)
Below is the code I entered, in the case #else the Qt code
Initialization
#if defined(EXPORT_POPEN) && EXPORT_POPEN == 1
pipe_frame.file = popen("/tmp/ffmpeg-rawpipe.sh", "w");
if (pipe_frame.file == NULL) {
return false;
}
#else
pipe_frame.qproc = new QProcess;
pipe_frame.qproc->start("/tmp/ffmpeg-rawpipe.sh", QIODevice::WriteOnly);
if(!pipe_frame.qproc->waitForStarted()) {
return false;
}
#endif
Writing a frame
#if defined(EXPORT_POPEN) && EXPORT_POPEN == 1
fwrite(pipe_frame.data, pipe_frame.width*4*pipe_frame.height , 1, pipe_frame.file);
#else
qint64 towrite = pipe_frame.width*4*pipe_frame.height,
written = 0, partial;
while(written < towrite) {
partial = pipe_frame.qproc->write(&pipe_frame.data[written], towrite-written);
pipe_frame.qproc->waitForBytesWritten(-1);
written += partial;
}
#endif
Termination
#if defined(EXPORT_POPEN) && EXPORT_POPEN == 1
pclose(pipe_frame.file);
#else
pipe_frame.qproc->terminate();
//pipe_frame.qproc->close();
#endif
edit
ffmpeg-rawpipe.sh
#!/bin/sh
exec ffmpeg-cuda -y -f rawvideo -s 1920x1080 -pix_fmt rgba -r 25 -i - -an -c:v h264_nvenc \
-cq:v 19 \
-profile:v high /tmp/test.mp4
I made some changes, I added the unbuffered flag to the open
pipe_frame.qproc->start("/tmp/ffmpeg-rawpipe.sh", QIODevice::WriteOnly|QIODevice::Unbuffered);
And therefore simplified the write
qint64 towrite = pipe_frame.width*4*pipe_frame.height;
pipe_frame.qproc->write(pipe_frame.data, towrite);
pipe_frame.qproc->waitForBytesWritten(-1);
I added a closeWriteChannel before closing the application (hoping that stopping the stdin ffmpeg pipe ends properly, just in case, I'm not sure it doesn't)
pipe_frame.qproc->waitForBytesWritten(-1);
pipe_frame.qproc->closeWriteChannel();
//pipe_frame.qproc->terminate();
pipe_frame.qproc->close();
But nothing changes, the mp4 file is created and contains data but from the mplayer log I see that it is misinterpreted, the video format is not recognized and it looks for an audio that is not there.
Fixed, adding waitForFinished() after closeWriteChannel(), closes stdin and wait for ffmpeg to terminate on its own.
pipe_frame.qproc->waitForBytesWritten(-1); // perhaps not necessary
pipe_frame.qproc->closeWriteChannel();
pipe_frame.qproc->waitForFinished();
pipe_frame.qproc->close();
edit
Note, even if initialized with the unbuffered flag, QProcess and QIODevice seem to buffer quite a lot, it seems as if waitForBytesWritten () is not working, and if you are feeding HD video you will go out of memory very quickly.

SCP always returns the same error code

I have a problem copying files with scp. I use Qt and copy my files with scp using QProcess. And when something bad happens I always get exitCode=1. It always returns 1. I tried copying files with a terminal. The first time I got the error "Permission denied" and the exit code was 1. Then I unplugged my Ethernet cable and got the error "Network is unreachable". And the return code was still 1. It confuses me very much cause in my application I have to distinct these types of errors.
Any help is appreciated. Thank you so much!
See this code as a working example:
bool Utility::untarScript(QString filename, QString& statusMessages)
{
// Untar tar-bzip2 file, only extract script to temp-folder
QProcess tar;
QStringList arguments;
arguments << "-xvjf";
arguments << filename;
arguments << "-C";
arguments << QDir::tempPath();
arguments << "--strip-components=1";
arguments << "--wildcards";
arguments << "*/folder.*";
// tar -xjf $file -C $tmpDir --strip-components=1 --wildcards
tar.start("tar", arguments);
// Wait for tar to finish
if (tar.waitForFinished(10000) == true)
{
if (tar.exitCode() == 0)
{
statusMessages.append(tar.readAllStandardError());
return true;
}
}
statusMessages.append(tar.readAllStandardError());
statusMessages.append(tar.readAllStandardOutput());
statusMessages.append(QString("Exitcode = %1\n").arg(tar.exitCode()));
return false;
}
It gathers all available process output for you to analyse. Especially look at readAllStandardError().

Cshell and Awk Infinite Running

When I run the below program, I get no return, however the program still runs forever until I end it. Can some one please exoplain to me why this would happen. I am trying to get this complex awk statement to work, however, have been very unsuccessful.
The code I am using for my Cshell is (its all on one line, but I split it here to make it easier to read):
awk '{split($2,b,""); counter = 1; while (counter < 13)
{if (b[counter] == 1 && "'$cmonth'" > counter)
{{printf("%s%s%s\n", $1, "'$letter'","'$year3'")}; counter++;
else if (b[counter] == 1 && "'$cmonth'" <= counter)
{{printf("%s%s%s\n", $1, "'$letter'","'$year2'")}; counter++;}
else echo "fail"}}' fileRead >> $year$month
The text file I am reading from looks like
fff 101010101010
yyy 100100100100
Here $year2 and $year3 represent counters that start from 1987 and go up 1 year for each line read.
$cmonth is just a month counter from 1–12.
$letter is just a ID.
The goal is for the program to read each line and print out the ID, month, and year if the position in the byte code is 1.
You have some mismatched curly braces, I have reformatted to one standard of indentation.
awk '{ \
split($2,b,""); counter = 1 \
while (counter < 13) { \
if (b[counter] == 1 && "'$cmonth'" > counter){ \
printf("%s%s%s\n", $1, "'$letter'","'$year3'") \
counter++ \
} \
else if (b[counter] == 1 && "'$cmonth'" <= counter) { \
printf("%s%s%s\n", $1, "'$letter'","'$year2'") \
counter++ \
} \
else print "fail" \
} # while \
}' fileRead >> $year$month
Also awk does'nt support echo.
Make sure that the \ is the LAST char on the line (no space or tab chars!!!), or you'll get a syntax error.
Else, you can 'fold' up all of the lines into one line. adding the occasional ';' as needed.
edit
OR you can take the previous version of this awk script (without the \ line continuation chars), put it in a file (without any of the elements outside of the ' ....' (single quotes) and call it from awk as a file. You'll also need to made so you can pass the variables cmonth, letter, year2 and any others that I've missed.
save as file
edit file, remove any `\' chars, change all vars like "'$letter'" to letter **
call program like
**
awk -v letter="$letter" -v year2="$year2" -v month="$month" -f myScript fileRead >> $year$month
**
for example
printf("%s%s%s\n", $1, "'$letter'","'$year2'")
becomes
printf("%s%s%s\n", $1, letter,year2)
IHTH.

How to set font color for STDOUT and STDERR

I want to differentiate the STDOUT and STDERR messages in my terminal.
If a script or command is printing a message in terminal I want to differentiate by colors; is it possible?
(E.g. stderr font color is red, and stdout font color is blue.)
Example (using bold):
$date
Wed Jul 27 12:36:50 IST 2011
$datee
bash: datee: command not found
$alias ls
alias ls='ls --color=auto -F'
$aliass ls
bash: aliass: command not found
Create a function in a bash shell or script:
color()(set -o pipefail;"$#" 2>&1>&3|sed $'s,.*,\e[31m&\e[m,'>&2)3>&1
Use it like this:
$ color command -program -args
It will show the command's stderr in red.
Keep reading for an explanation of how it works. There are some interesting features demonstrated by this command.
color()... — Creates a bash function called color.
set -o pipefail — This is a shell option that preserves the error return code of a command whose output is piped into another command. This is done in a subshell, which is created by the parentheses, so as not to change the pipefail option in the outer shell.
"$#" — Executes the arguments to the function as a new command. "$#" is equivalent to "$1" "$2" ...
2>&1 — Redirects the stderr of the command to stdout so that it becomes sed's stdin.
>&3 — Shorthand for 1>&3, this redirects stdout to a new temporary file descriptor 3. 3 gets routed back into stdout later.
sed ... — Because of the redirects above, sed's stdin is the stderr of the executed command. Its function is to surround each line with color codes.
$'...' A bash construct that causes it to understand backslash-escaped characters
.* — Matches the entire line.
\e[31m — The ANSI escape sequence that causes the following characters to be red
& — The sed replace character that expands to the entire matched string (the entire line in this case).
\e[m — The ANSI escape sequence that resets the color.
>&2 — Shorthand for 1>&2, this redirects sed's stdout to stderr.
3>&1 — Redirects the temporary file descriptor 3 back into stdout.
Here's a hack that I thought of and it seems to work:
Given the following aliases for readability:
alias blue='echo -en "\033[36m"'
alias red='echo -en "\033[31m"'
alias formatOutput='while read line; do blue; echo $line; red; done'
Now, you need to first set the font color in your terminal to red (as the default, which will be used for stderr).
Then, run your command and pipe the stdout through formatOutput defined above (which simply prints each line as blue and then resets the font color to red):
shell$ red
shell$ ls / somenonexistingfile | formatOutput
The above command will print in both stderr and stdout and you'll see that the lines are coloured differently.
Hope this helps
UPDATE:
To make this reusable, I've put it all in a small script:
$ cat bin/run
#!/bin/bash
echo -en "\033[31m" ## red
eval $* | while read line; do
echo -en "\033[36m" ## blue
echo $line
echo -en "\033[31m" ## red
done
echo -en "\033[0m" ## reset color
Now you can use this with any command:
$ run yourCommand
I color stderr red by linking the file descriptor to a custom function that adds color to everything that goes through it. Add to following to your .bashrc:
export COLOR_RED="$(tput setaf 1)"
export COLOR_RESET="$(tput sgr0)"
exec 9>&2
exec 8> >(
perl -e '$|=1; while(sysread STDIN,$a,9999) {print
"$ENV{COLOR_RED}$a$ENV{COLOR_RESET}"}'
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'
So what is happening? The debug trap is executed just before and immediately after executing a command. stderr is thus redirected before a command is executed to enable red output. PROMPT_COMMAND is evaluated before the prompt is shown and with this I restore stderr to its normal state. This is necessary because PS1 and PS2 (your prompt) are printed over stderr and I do not want a red prompt. voila, red output over stderr!
You should check out stderred: https://github.com/sickill/stderred
Yes it's not possible natively. You'll have to hack the tty management (in the kernel).
I somehow finished some little C wrapper before I saw the other answers :-)
Might be buggy, and values are hardcoded, don't use this except for testing.
#include "unistd.h"
#include "stdio.h"
#include <sys/select.h>
int main(int argc, char **argv)
{
char buf[1024];
int pout[2], perr[2];
pipe(pout); pipe(perr);
if (fork()!=0)
{
close(1); close(2);
dup2(pout[1],1); dup2(perr[1],2);
close(pout[1]); close(perr[1]);
execvp(argv[1], argv+1);
fprintf(stderr,"exec failed\n");
return 0;
}
close(pout[1]); close(perr[1]);
while (1)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(pout[0], &fds);
FD_SET(perr[0], &fds);
int max = pout[0] > perr[0] ? pout[0] : perr[0];
int v = select(max+1, &fds, NULL, NULL, NULL);
if (FD_ISSET(pout[0], &fds))
{
int r;
r = read(pout[0], buf, 1024);
if (!r) {close(pout[0]); continue;}
write(1, "\033[33m", 5);
write(1, buf, r);
write(1, "\033[0m", 4);
}
if (FD_ISSET(perr[0], &fds))
{
int r;
r = read(perr[0], buf, 1024);
if (!r) {close(perr[0]); continue;}
write(2, "\033[31m", 5);
write(2, buf, r);
write(2, "\033[0m", 4);
}
if (v <= 0) break;
}
return 0;
}
Edit: Compared to the shell solution, this one will preserve the order of lines/characters more often. (It's not possible to be as accurate as direct tty reading.) Hitting ^C won't show an ugly error message, and it behaves correctly on this example:
./c_color_script sh -c "while true; do (echo -n a; echo -n b 1>&2) done"
I'm surprised that nobody has actually figured out how to color stdio streams. This will color stderr red for the entire (sub)shell:
exec 3>&2
exec 2> >(sed -u 's/^\(.*\)$/'$'\e''[31m\1'$'\e''[m/' >&3)
In this case, &3 will hold the original stderr stream.
You should not be passing any commands to exec, only the redirects. This special case causes exec to replace the current (sub)shell's stdio streams with those that it receives.
There are a few caveats:
Since sed will be running persistently in a parallel subshell, any direct output immediately following a write to the colored stdio will probably beat sed to the tty.
This method uses a FIFO file descriptor; FIFO nodes only deal in lines. If you don't write a linefeed to the stream, your output will be buffered until a newline is encountered. This is not buffering on sed's part: it's how these file types function.
The most troublesome of the caveats is the first, but a race condition can be more or less avoided by applying similar processing to all outputs, even if you use the default color.
You can perform similar processing for single commands by piping to the same sed command with the normal pipe operator (|). Piped chains are executed synchronously, so no race condition will occur, though the last command in a pipe chain receives its own subshell by default.
Expanding on the answer #gospes gave, I added the functionality to print out partial lines without waiting for a newline, and some comments. Allows for better output from wget or typing in a interactive shell.
exec 9>&2
exec 8> >(
while [ "$r" != "1" ]; do
# read input, no field separators or backslash escaping, 1/20th second timeout
IFS='' read -rt 0.05 line
r=$?
# if we have input, print the color change control char and what input we have
if ! [ "${#line}" = "0" ]; then
echo -ne "\e[1;33m${line}"
fi
# end of line detected, print default color control char and newline
if [ "$r" = "0" ] ; then
echo -e "\e[0m"
fi
# slow infinite loops on unexpected returns - shouldn't happen
if ! [ "$r" = "0" ] && ! [ "$r" = "142" ]; then
sleep 0.05
fi
done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'
I used bold yellow (1;33) but you can replace it with whatever, red for example (31) or bold red (1;33), and I arbitrarily chose 0.05 seconds for re-checking for end-of-lines and pausing on unexpected return codes (never found any); it could probably be lowered, or possibly removed from the read command.
You can make use of grep for this. Note that this assumes that grep is configured to have coloured output (this is the default on many systems).
$ aliass ls 2> >(GREP_COLORS='ms=01;31' grep .) 1> >(GREP_COLORS='ms=01;32' grep .)
aliass: command not found
This is a little long winded, if you are simply wanting to distinguish stderr fromstdout you can simply do this:
$ (echo "this is stdout"; echo "this is stderr" >&2) | grep .
this is stderr
this is stdout
This will result in stdout being highlighted with the default grep colour and stderr being white.
This might be the opposite of what you want if your default grep colour is red. If so you can explicitly set the grep colour to green:
$ GREP_COLORS='ms=01;32'
$ (echo "this is stdout"; echo "this is stderr" >&2) | grep .
If you explicitly want to get red output for stderr:
$ GREP_COLORS='ms=01;31'
$ (echo "this is stdout"; echo "this is stderr" >&2) 2> >(grep .)
Note that this solution will not preserve the output order.

Resources