[MPlayer-dev-eng] mplayer slave suggestion
Chandan Pitta
chandan.pitta at gmail.com
Thu Oct 12 22:54:22 CEST 2006
Not sure what exactly you need. Yes mplayer dumps everything on the
output and you have to parse the output to get what you need. I wrote
a small application a while ago for an embedded system that uses
mplayer to play audio and video files. Basically it loads up mplayer
in "-idle -slave" mode and reads commands such as "play", "pause",
"loadfile" from a java application using a file and sends proper
commands to the mplayer process. It also parses the output from
mplayer to obtain details like total time, current position, artist,
title, album, audio and video bitrates etc. take a look at the code.
All you need to understand is sendInfo() and sendCommand().
Also here is the link to a demo of the java application that uses
mplayer via C++ code:
http://video.google.com/videoplay?docid=2746307597104554801
On 10/11/06, Kevron Rees <kev000 at email.com> wrote:
> hello,
>
> If I may make a suggestion about the mplayer slave documentation: After searching google for hours trying to find a suitable solution I have come to the conclusion that example code on the use of mplayer as a slave application to a front-end is lacking. I am making a front-end using mplayer in a car computer setting. This, to my knowledge has never been done so I needed to start from scratch. I have no problem writing commands to mplayer. sending a write(write_fd[1]...) works without problem, but when I want to read read(read_fd[0]...) I get everything that mplayer outputs when it starts up (i.e. "...CPU runtime detection..." etc etc). I know you guys are working hard developing mplayer but if anyone has an idea of where I can find additional info or example code I would be most grateful. If there is no current documentation or example code if anyone is willing to help me solve my simple problem I would be more than happy to write and document a great howto on using mplayer as a slave. This would help any future users in my situation find the resources they need without spamming the dev mailing lists. Thanks in advance for any help.
>
> -Kev
>
> P.S. if some noble soul is willing to help me here is my example code:
> http://nextabyte.com/nanonymous/current/example-code.txt
>
> --
> ___________________________________________________
> Play 100s of games for FREE! http://games.mail.com
>
> _______________________________________________
> MPlayer-dev-eng mailing list
> MPlayer-dev-eng at mplayerhq.hu
> http://lists.mplayerhq.hu/mailman/listinfo/mplayer-dev-eng
>
-------------- next part --------------
// TODO: synchronize commpipe0 and commpipe1
// TODO: optimize to clear pause buffers in ao_pcm and here
// TODO: kill all mplayer threads in sig_handler
#include <sys/shm.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/sem.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <strings.h>
#include <sys/timeb.h>
#include "ChandanSoundEngine.h"
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux specific) */
};
SOUNDM_Audio_NormalDevice *pDev_;
char *buffer_ = 0;
int segmentID_ = -1, semaphoreID_ = -1;
int BUFFER_SIZE = 65536;
FILE *in_fp_, *out_fp_;
pthread_t soundThreadID_, mediaThreadID_;
int pipeIn_[2], pipeOut_[2];
int playerState_ = PLAYER_STATE_STOPPED;
int mediaTime_ = 0;
timeb lastCmdTime;
int writeToSoundDevice(char *buffer, int wc)
{
int ret = pDev_->write((short *)buffer, wc >> 1); // we do >> 1 since we are transfering
// shorts from char buffer (8 bits to 16 bits)
if (ret < 0) {
return wc;
}
return (ret << 1);
}
int writeRequiredBytes(char *buffer, int ac)
{
// write only WRITE_SIZE bytes, so as to avoid blocking read thread until we write all data in buffer.
// Doing that is as bad as having everything done in one thread. So we write a chunk and free that
// space in the buffer for the read thread to use.
if (ac >= WRITE_SIZE) { // if we have more bytes available than the number of bytes needed to write
//printf("Required: %d\n", WRITE_SIZE);
return writeToSoundDevice(buffer, WRITE_SIZE); // write the required bytes
}
else {
//printf("Required: %d\n", ac);
return writeToSoundDevice(buffer, ac); // write the rest of buffer. We do not loop back and write more data
// until wc becomes 0. The rest of the data will be written in the
// next loop. So its better to have BUFFER_SIZE as an integer
// multiple of WRITE_SIZE so we do not come to this section of code
// which writes partial data
}
}
void printQueue(int head, int tail)
{
for (int i=0; i<BUFFER_SIZE/1024; i++) {
if (i == head / 1024)
printf("H");
else if (i == tail / 1024)
printf("T");
else
printf(".");
}
printf("\n");
}
void *dataWriterThread(void *data)
{
nice(-5);
printf("dataWriterThread() ---> Data writer thread started\n");
int head, tail;
while (1) { // loop for ever
while (1) { // write until the buffer is empty
if (!semctl(semaphoreID_, M_PLAY, GETVAL)) {
printf("dataWriterThread() ---> Paused\n");
break;
}
head = semctl(semaphoreID_, HEAD, GETVAL) << SHIFT_BITS;
tail = semctl(semaphoreID_, TAIL, GETVAL) << SHIFT_BITS;
if (head == tail) break; // end loop if there is no more data
int wc;
if (tail > head) { // need to write the rest of buffer and then write upto head
wc = writeRequiredBytes(buffer_ + tail, BUFFER_SIZE - tail);
}
else { // meaning we don't have to wrap around for the data
wc = writeRequiredBytes(buffer_ + tail, head - tail);
}
tail += wc;
if (tail == BUFFER_SIZE)
tail = 0;
//printQueue(head, tail);
union semun argument;
if (!semctl(semaphoreID_, RESET, GETVAL)) {
argument.val = tail >> SHIFT_BITS;
semctl(semaphoreID_, TAIL, SETVAL, argument);
}
else {
printf("dataWriterThread() ---> Buffer got reset\n");
pDev_->flush();
argument.val = 0;
semctl(semaphoreID_, RESET, SETVAL, argument);
}
//printQueue(head, tail);
}
//printf("dataWriterThread() ---> waiting\n");
struct sembuf operations[1];
operations[0].sem_num = DATA_ARRIVED;
operations[0].sem_op = -1;
operations[0].sem_flg = SEM_UNDO;
semop(semaphoreID_, operations, 1);
}
}
void allDone(int retval)
{
printf("allDone() ---> All done exiting now\n");
if (buffer_ != NULL) {
// detach shared memory
shmdt(buffer_);
}
if (segmentID_ >= 0) {
// deallocate shared memory
shmctl(segmentID_, IPC_RMID, 0);
}
if (semaphoreID_ >= 0) {
// deallocate shared memory
semctl(semaphoreID_, NUM_SEMS, IPC_RMID, 0);
}
if (in_fp_ != NULL) {
fclose(in_fp_);
}
if (out_fp_ != NULL) {
fclose(out_fp_);
}
exit(retval);
}
static void exit_sighandler(int x) {
printf("exit_sighandler() ---> Caught signal: %d\n", x);
// TODO: exit cleanly based on the type of signal
switch(x){
case SIGHUP: // 1 Exit Hangup (see termio(7I))
case SIGINT: // 2 Exit Interrupt (see termio(7I))
case SIGQUIT: // 3 Core Quit (see termio(7I))
case SIGILL: // 4 Core Illegal Instruction
case SIGTRAP: // 5 Core Trace or Breakpoint Trap
case SIGABRT: // 6 Core Abort
// case SIGEMT: // 7 Core Emulation Trap
case SIGFPE: // 8 Core Arithmetic Exception
case SIGKILL: // 9 Exit Killed
case SIGBUS: // 10 Core Bus Error
case SIGSEGV: // 11 Core Segmentation Fault
case SIGSYS: // 12 Core Bad System Call
case SIGPIPE: // 13 Exit Broken Pipe
case SIGALRM: // 14 Exit Alarm Clock
case SIGTERM: // 15 Exit Terminated
case SIGPOLL: // 22 Exit Pollable Event (see streamio(7I))
case SIGSTOP: // 23 Stop Stopped (signal)
case SIGTSTP: // 24 Stop Stopped (user) (see termio(7I))
case SIGCONT: // 25 Ignore Continued
case SIGTTIN: // 26 Stop Stopped (tty input) (see termio(7I))
case SIGTTOU: // 27 Stop Stopped (tty output) (see termio(7I))
case SIGVTALRM: // 28 Exit Virtual Timer Expired
case SIGPROF: // 29 Exit Profiling Timer Expired
case SIGXCPU: // 30 Core CPU time limit exceeded (see getrlimit(2))
case SIGXFSZ: // 31 Core File size limit exceeded (see getrlimit(2))
allDone(100);
case SIGUSR1: // 16 Exit User Signal 1
case SIGUSR2: // 17 Exit User Signal 2
case SIGCHLD: // 18 Ignore Child Status Changed
case SIGPWR: // 19 Ignore Power Fail or Restart
case SIGWINCH: // 20 Ignore Window Size Change
case SIGURG: // 21 Ignore Urgent Socket Condition
// case SIGWAITING: // 32 Ignore Concurrency signal reserved by threads library
// case SIGLWP: // 33 Ignore Inter-LWP signal reserved by threads library
// case SIGFREEZE: // 34 Ignore Check point Freeze
// case SIGTHAW: // 35 Ignore Check point Thaw
// case SIGCANCEL: // 36 Ignore Cancellation signal reserved by threads library
default:
return;
}
}
void initAudio()
{
// Catch signals
signal(SIGCHLD, exit_sighandler);
//========= Catch terminate signals: ================
// terminate requests:
signal(SIGTERM,exit_sighandler); // kill
signal(SIGHUP,exit_sighandler); // kill -HUP / xterm closed
signal(SIGINT,exit_sighandler); // Interrupt from keyboard
signal(SIGQUIT,exit_sighandler); // Quit from keyboard
// fatal errors:
signal(SIGBUS,exit_sighandler); // bus error
signal(SIGSEGV,exit_sighandler); // segfault
signal(SIGILL,exit_sighandler); // illegal instruction
signal(SIGFPE,exit_sighandler); // floating point exc.
signal(SIGABRT,exit_sighandler); // abort()
printf("main() ---> Buffer size: %d\n", BUFFER_SIZE);
printf("main() ---> Write size: %d\n", WRITE_SIZE);
printf("main() ---> Creating key for semaphore\n");
key_t keyS = ftok("ChandanSoundEngine", 'S');
printf("main() ---> Allocating semaphore\n");
// create the following semaphores
// 1. for head
// 2. for tail
// 3. for signaling new data arrival
semaphoreID_ = semget(keyS, NUM_SEMS, IPC_CREAT | 0666);
if (semaphoreID_ == -1) {
perror("main() ---> Failed to create semaphores\n");
allDone(1);
}
printf("main() ---> Initializing semaphores\n");
union semun arguments;
unsigned short values[NUM_SEMS];
for (int i=0; i<NUM_SEMS; i++) {
values[i] = 0;
}
arguments.array = values;
if (semctl(semaphoreID_, NUM_SEMS, SETALL, arguments) < 0) {
perror("main() ---> Failed to initialize semaphores");
allDone(2);
}
printf("main() ---> Creating key for buffer segment\n");
key_t keyB = ftok("ChandanSoundEngine", 'B');
printf("main() ---> Allocating segment");
segmentID_ = shmget(keyB, BUFFER_SIZE, IPC_CREAT | 0666);
if (segmentID_ == -1) {
perror("main() ---> Failed to create segment");
allDone(3);
}
printf("main() ---> Obtaining segment address\n");
buffer_ = (char *)shmat(segmentID_, NULL, SHM_RND);
if (buffer_ == (char *)-1) {
printf("main() ---> Failed to obtain segment address\n");
allDone(4);
}
printf("main() ---> Determining segment size\n");
struct shmid_ds shmbuffer;
shmctl(segmentID_, IPC_STAT, &shmbuffer);
BUFFER_SIZE = shmbuffer.shm_segsz;
printf("main() ---> Buffer size: %d bytes\n", BUFFER_SIZE);
printf("main() ---> Obtaining audio device\n");
pDev_ = new SOUNDM_Audio_NormalDevice();
if (pDev_ < 0) {
printf("main() ---> Audio device failed\n");
allDone(5);
}
pDev_->setAudioDevice(44100, 2, 2);
pDev_->setVolume(1);
printf("main() ---> Starting thread\n");
pthread_create(&soundThreadID_, NULL, dataWriterThread, NULL);
}
int getChar(FILE *fp)
{
int retval;
while(1) {
retval = fgetc(fp);
if (retval != -1) {
break;
}
usleep(DELAY);
}
return retval;
}
long getLong(FILE *fp)
{
long retval = 0;
int i = 0;
for (i=0; i<8; i++) {
retval = (retval << 8) + getChar(fp);
}
return retval;
}
long getInt(FILE *fp)
{
long retval = 0;
int i = 0;
for (i=0; i<4; i++) {
retval = (retval << 8) + getChar(fp);
}
return retval;
}
void getString(char *str, int len, FILE *fp)
{
while(1) {
if (fgets(str, len, fp) != NULL) {
break;
}
usleep(DELAY);
}
}
void sendCommand(char *cmd)
{
write(pipeOut_[1], cmd, strlen(cmd));
}
void writeUTF(FILE *fp, char *buf)
{
int len = strlen(buf);
// for UTF the next 2 bytes should be he length of the string
putc((len >> 8) & 0xff, fp);
putc(len & 0xff, fp);
fprintf(fp, "%s", buf);
}
void setAudioParms(char *buf)
{
int hz, ch, bps;
char s;
sscanf(buf, "%d Hz, %d ch, %c%dle", &hz, &ch, &s, &bps);
bps >>= 3;
printf("setAudio() ---> setting audio to %d hz, %d ch, %d bps\n", hz, ch, bps);
pDev_->setAudioDevice(hz, ch, bps);
}
void sendInfo(char *buf)
{
char *msgs[20] = {
"Failed to open", // 0
"Starting playback...", // 1
" Title:", // 2
" Artist:", // 3
" Album:", // 4
" Year:", // 5
" Comment:", // 6
" Genre:", // 7
"ANS_TIME_POSITION", // 8
"ANS_LENGTH", // 9
"EOF", // 10
"AUDIO:", // 11
"VIDEO:" // 12
};
for (int i=0; i<13; i++) {
int msgLen = strlen(msgs[i]);
if (strncmp(buf, msgs[i], msgLen) == 0) {
putc(i, out_fp_);
if (i != 0 && i != 1 && i != 10) {
buf = buf + msgLen + 1;
// for media time we should store the values
if (i == 8 || i == 9) {
char *buf1 = index(buf, '.');
if (buf1 != NULL)
buf1[0] = 0;
}
// if we got some audio info set the values to the local device
if (i == 11) {
setAudioParms(buf);
}
writeUTF(out_fp_, buf);
}
fflush(out_fp_);
break;
}
}
}
void *mediaPlayerThread(void *data)
{
pid_t pid;
// keep creating mplayer process endlessly if the process exits
while (1) {
/* Setup communication pipeline first */
if (pipe(pipeIn_)) {
fprintf(stderr,"Pipe error!\n");
allDone(10);
}
/* Setup communication pipeline first */
if (pipe(pipeOut_)) {
fprintf(stderr,"Pipe error!\n");
allDone(11);
}
/* Attempt to fork and check for errors */
if ((pid = fork()) == -1){
fprintf(stderr,"Fork error. Exiting.\n"); /* something went wrong */
allDone(12);
}
if (pid) {
/* A positive (non-negative) PID indicates the parent process */
//dup2(commpipeParent[1],1); /* Replace stdout with out side of the pipe */
close(pipeOut_[0]); /* Close unused side of pipe (in side) */
//dup2(commpipeChild[0],0); /* Replace stdin with the in side of the pipe */
close(pipeIn_[1]); /* Close unused side of pipe (out side) */
//sleep(5);
//write(commpipe1[1], "quit\n", 5);
//waitpid(pid, NULL, 0);
FILE *infp = fdopen(pipeIn_[0], "r");
char buf[CMD_BUFFER_SIZE];
while (1) {
fgets(buf, CMD_BUFFER_SIZE, infp);
sendInfo(buf);
}
fclose(infp);
// if we are here that means the child process has exited
close(pipeOut_[1]); /* Close unused side of pipe (out side) */
close(pipeIn_[0]); /* Close unused side of pipe (out side) */
// better kill the child if it is still alive
kill(pid, SIGKILL);
}
else{
nice(-5);
/* A zero PID indicates that this is the child process */
dup2(pipeIn_[1], 1); /* Replace stdout with out side of the pipe */
//close(commpipe0[0]); /* Close unused side of pipe (in side) */
dup2(pipeOut_[0], 0); /* Replace stdin with the in side of the pipe */
dup2(1, 2); // divert stderr to stdout
//close(commpipe1[1]); /* Close unused side of pipe (out side) */
setvbuf(stdout,(char*)NULL,_IONBF,0); /* Set non-buffered output on stdout */
setvbuf(stdin,(char*)NULL,_IONBF,0); /* Set non-buffered output on stdin */
/* Replace the child fork with a new process */
if(execl("mplayer", "mplayer", "-autosync", "100", "-ac", "mad,", "-ao", "pcm", "-vo", "fbdev", "-bpp", "19",
"-fs", "-dr", "-delay", "0.8", "-fixed-vo", "-quiet", "-slave", "-idle", "-v", NULL) == -1) {
fprintf(stderr,"execl Error!");
allDone(13);
}
}
}
}
void doWait()
{
long oldTime = lastCmdTime.time*1000l + lastCmdTime.millitm;
while (1) {
struct timeb t_current;
ftime(&t_current);
long curTime = t_current.time*1000l + t_current.millitm;
if (curTime - oldTime >= CMD_WAIT_TIME)
break;
usleep(10000l);
}
}
int waitMore()
{
long oldTime = lastCmdTime.time*1000l + lastCmdTime.millitm;
struct timeb t_current;
ftime(&t_current);
long curTime = t_current.time*1000l + t_current.millitm;
if (curTime - oldTime < CMD_WAIT_TIME)
return 1;
return 0;
}
int main(void)
{
char cmdBuff[CMD_BUFFER_SIZE]; // select a buffer large enough to read long filenames
int volume = 3;
initAudio();
pthread_create(&mediaThreadID_, NULL, mediaPlayerThread, NULL);
printf("main() ---> waiting for thread to join (endless wait)\n");
//pthread_join(soundThreadID_, NULL);
//allDone(0);
printf("main() ---> Opening file %s\n", IN_FILE);
// delete old file
unlink(IN_FILE);
// create and close the file again
close(creat(IN_FILE, 0666));
in_fp_ = fopen(IN_FILE, "r");
chmod(IN_FILE, 0666);
printf("main() ---> Opening file %s\n", OUT_FILE);
// delete old file
unlink(OUT_FILE);
// create and close the file again
close(creat(OUT_FILE, 0666));
chmod(OUT_FILE, 0666);
out_fp_ = fopen(OUT_FILE, "w");
// keep receiving connections from clients for ever
while (1) {
// as long as the connection is open read the command in blocking mode
printf("main() ---> waiting for command\n");
int cmd = getChar(in_fp_);
printf("main() ---> received command: %d\n", cmd);
// take action based on the command
switch(cmd) {
case LOAD_FILE:
printf("main() ---> [ LOAD_FILE ] waiting for filename\n");
// read the file name
getString(cmdBuff, CMD_BUFFER_SIZE, in_fp_);
printf("main() ---> [ LOAD_FILE ] obtained filename: %s\n", cmdBuff);
playerState_ = PLAYER_STATE_PAUSED;
/* fclose(in_fp_);
truncate(IN_FILE, 0);
in_fp_ = fopen(IN_FILE, "r");
fclose(out_fp_);
truncate(OUT_FILE, 0);
out_fp_ = fopen(OUT_FILE, "w");*/
printf("main() ---> [ LOAD_FILE ] sending filename to mplayer\n");
// mute before loading the file
sendCommand(LOAD_CMD);
// fortunately the filename end with '\n' so no need to send it after the filename
sendCommand(cmdBuff);
//sendCommand(PAUSE_CMD);
printf("main() ---> [ LOAD_FILE ] done loading file\n");
ftime(&lastCmdTime);
break;
case PLAY:
// This message is received after LOAD_FILE or after PAUSE
// in either case the player is waiting for a signal from this thread
if (playerState_ != PLAYER_STATE_PAUSED) {
printf("main() ---> [ PLAY ] player is not in paused state, ignoring command\n");
}
else {
ftime(&lastCmdTime);
printf("main() ---> [ PLAY ] changing state to playing\n");
playerState_ = PLAYER_STATE_PLAYING;
printf("main() ---> [ PLAY ] sending pause command\n");
sendCommand(PAUSE_CMD);
}
break;
case PAUSE:
// This message is received only after a PLAY message
if (playerState_ != PLAYER_STATE_PLAYING) {
printf("main() ---> [ PAUSE ] player is not in playing state, ignoring command\n");
}
else {
ftime(&lastCmdTime);
printf("main() ---> [ PAUSE ] changing state to paused\n");
playerState_ = PLAYER_STATE_PAUSED;
printf("main() ---> [ PAUSE ] sending pause command\n");
sendCommand(PAUSE_CMD);
}
break;
case CLOSE_FILE:
if (playerState_ == PLAYER_STATE_PLAYING) {
ftime(&lastCmdTime);
printf("main() ---> [ CLOSE_FILE ] sending pause command\n");
sendCommand(PAUSE_CMD);
}
printf("main() ---> [ CLOSE_FILE ] changing state to stopped\n");
playerState_ = PLAYER_STATE_STOPPED;
// nothing else to do!
break;
case SET_VOLUME:
volume = getChar(in_fp_);
printf("main() ---> [ SET_VOLUME ] setting volume %d\n", volume);
pDev_->setVolume(volume);
break;
case SET_MEDIA_TIME:
printf("main() ---> [ SET_MEDIA_TIME ] waiting for time\n");
mediaTime_ = getInt(in_fp_);
printf("main() ---> [ SET_MEDIA_TIME ] setting media time to %ld\n", time);
sendCommand(SEEK_CMD_HEAD);
sprintf(cmdBuff, "%d", mediaTime_);
sendCommand(cmdBuff);
sendCommand(SEEK_CMD_TAIL);
break;
case GET_MEDIA_TIME:
//printf("main() ---> [ GET_MEDIA_TIME ] obtaining time\n");
//doWait();
if (!waitMore())
sendCommand(MEDIA_TIME_CMD);
/* if (!waitMore())
sendCommand(MEDIA_TIME_CMD);
sprintf(cmdBuff, "%d", mediaTime_);
putc(8, out_fp_);
writeUTF(out_fp_, cmdBuff);
fflush(out_fp_);*/
break;
case GET_MEDIA_DURATION:
//printf("main() ---> [ GET_MEDIA_DURATION ] obtaining time\n");
//doWait();
if (!waitMore())
sendCommand(MEDIA_DURATION_CMD);
/* if (!waitMore())
sendCommand(MEDIA_DURATION_CMD);
sprintf(cmdBuff, "%d", duration_);
putc(9, out_fp_);
writeUTF(out_fp_, cmdBuff);
fflush(out_fp_);*/
break;
case SEND_MPLAYER_CMD:
printf("main() ---> [ SEND_MPLAYER_CMD ] waiting for mplayer command\n");
cmd = getChar(in_fp_);
char *cmdStr;
printf("main() ---> [ SEND_MPLAYER_CMD ] got %d\n", cmd);
switch (cmd) {
case 1:
cmdStr = "pausing_keep contrast -5\n";
break;
case 2:
cmdStr = "pausing_keep contrast +5\n";
break;
case 3:
cmdStr = "pausing_keep brightness -5\n";
break;
case 4:
cmdStr = "pausing_keep brightness +5\n";
break;
case 7:
cmdStr = "pausing_keep saturation -5\n";
break;
case 8:
cmdStr = "pausing_keep saturation +5\n";
break;
}
printf("main() ---> [ SEND_MPLAYER_CMD ] command: %s\n", cmdStr);
sendCommand(cmdStr);
break;
case RESTART:
fclose(in_fp_);
truncate(IN_FILE, 0);
in_fp_ = fopen(IN_FILE, "r");
fclose(out_fp_);
truncate(OUT_FILE, 0);
out_fp_ = fopen(OUT_FILE, "w");
break;
}
}
}
More information about the MPlayer-dev-eng
mailing list