按键调用并控制mplayer播放的程序--未整理
//mplayer进入slave模式下播放的命令行为:
//./mplayer -slave -quiet -vf rotate=2,scale=240:320 a.av
//v3版本实现的功能:正确接收单击输入,对传递给子进程的字符串cmd赋值。
//v3版本没实现的功能:子进程不能得到并对父进程发送的命令进行处理。导致不能控制播放。
//v4要实现通用的进程间通信功能,不只是针对调用和控制slave模式下的mplayer。
//v4_3:1增加用sigaction处理信号。去掉原来用signal()函数来连结信号。
// 增加测试命令f: full_screen模式
// 存在的问题 : 在一次父子进程循环后,不能正常进入第二次循环。原因可能是:1新IO信号杀死了当前进程,
//意外退出。2管道暗中传递了其他无效字符串,导致mplayer主动终止子进程,则父进程随之退出。
/*
v4_3 输入 f 和回车后的信息:
VO: [xv] 320x176 => 320x176 Planar YV12
f(开始转入全屏幕播放模式)
[root@lyl cplayer]# No bind found for key _
No bind found for key u
No bind found for key l
No bind found for key l
No bind found for key c
No bind found for key n
*/
# include < unistd. h>
# include < stdio. h>
# include < stdlib. h>
# include < malloc . h>
# include < string . h>
# include < aio. h>
# define TRUE 1
# define OPTION_LEN 300
# define CHAR_LEN 30
# define MSG_input_th( i) { printf ( "input the %d choice/n" , i+ 1) ; }
# define MSG_input { printf ( "input your choice/n" ) ; }
# define MSG_str_len( str, i) { printf ( " the %s 's length is %d/n" , str, i) ; }
# define MSG_str_is( str) { printf ( "the str is %s/n" , str ) ; }
//#define "/arm2410s/tm.mpg"
# define ADDON_OPTION " -slave -quiet -vf "
# define PNTERR { fprintf ( stderr , "/n%s,%s,%d/n" , __FILE__ , __FUNCTION__ , __LINE__ ) ; }
static struct aiocb kbcbuf;
//static volatile struct aiocb kbcbuf;
static int to_chil[ 2] ; // 到父、子进程的管道
static struct sigaction sig_act;
//static void sig_pipe( int signo );
int main( ) {
void parentCode( ) ;
void childCode2( char * pname) ;
void on_input( int signo, siginfo_t * info, void * context ) ;
void setup_aio_buf( ) ;
char buf[ CHAR_LEN] ;
char filename[ CHAR_LEN] ;
pid_t pid;
int nwhile = 0;
//指定pipe两头的值
if ( pipe( to_chil) < 0) {
perror ( "pipe " ) ;
}
PNTERR
fprintf ( stderr , "main(): nothing /n" ) ;
//signal(SIGIO,on_input);//指定处理函数
sigemptyset ( & sig_act. sa_mask ) ;
//如果sigaction.sa_flags & SA_SIGINFO为真,则安装sigaction.sa_sigaction作为信号处理函数
sig_act. sa_flags = SA_SIGINFO;
sig_act. sa_sigaction = on_input;
//set up the aio request
setup_aio_buf( ) ;
//aio_read( &kbcbuf );
//signal();
while ( TRUE ) { //here should clear cmd's value
PNTERR
nwhile+ + ;
if ( nwhile > = 2 ) {
//pipe( to_chil );
}
fprintf ( stderr , "main(): now start while.nwhile is %d /n" , nwhile) ;
//printf("input the filename to play/n");
// scanf("%s",filename);
// PNTERR
strcpy ( filename, "/arm2410s/tm.mpg" ) ;
fprintf ( stderr , "main(): now file name is %s /n" , filename) ;
pid = fork( ) ;
if ( pid= = 0 ) {
// childCode1(playOption2); //way1
//signal(SIGCHLD,SIG_IGN);
childCode2( filename ) ; //way2
//wait();
_exit( EXIT_SUCCESS ) ;
PNTERR
} else if ( pid > 0 ) {
//下面的设置在父进程中进行。问题是,子进程会复制父进程的设置,怎么办?
//signal(SIGIO,on_input);
PNTERR
//setup_aio_buf();
//aio_read( &kbcbuf );
parentCode( ) ;
wait( NULL ) ;
} else //printf("failed/n");
perror ( "fork" ) ;
return - 1;
}
return 0;
}
/*
如果sigaction.sa_flags & SA_SIGINFO为真,则安装sigaction.sa_sigaction作为信号处理函数,该函数原型如下:
void func(int signo, siginfo_t *info, void *context);
signo为信号编号,siginfo_t至少包含如下信息:
int si_signo; // 信号码
int si_code; // 引发信号的原因
union sigval si_value; // 信号值
其中si_signo和参数signo同,si_code为:SI_USER, SI_QUEUE, SI_TIMER, SI_ASYNCIO, SI_MESGQ。
只有当实现支持POSIX:RTS且si_code为SI_QUEUE, SI_TIMER, SI_ASYNCIO, SI_MESGQ时,si_value才有值,如下:
union sigval
{
int sival_int;
void *sival_ptr;
};
*/
//on_input version 3
void on_input( int signo, siginfo_t * info, void * context ) {
//char c[2];
char * p_c;
//char cmd[20];
int writen = 0;
struct aiocb * req;
ssize_t ret_status;
//ensure it's our signal
if ( info- > si_signo = = SIGIO )
{
req = ( struct aiocb * ) info- > si_value. sival_ptr;
//static char cmd[CHAR_LEN];
//char * ptr_c = (char *) kbcbuf.aio_buf;
//fprintf(stderr,"on_input: chars cmd(a) is %s/n",cmd);
if ( aio_error( req ) = = 0 ) {
//perror("reading failed");
ret_status = aio_return( req ) ;
} else { return ; }
if ( ret_status = = 2 ) { //正确得到一个输入的字符,但这里考虑到了回车符号
p_c = ( char * ) req- > aio_buf; //忽略ptr_c指向的第二个,即回车符
//strcpy( c,ptr_c );
//fprintf(stderr," you put %c/n", *c);
if ( * p_c ! = ( char ) '/0' & & p_c[ 1] = = '/n' ) {
//*c = *p_c;
//close( to_chil[0] );
switch ( * p_c ) {
case 'q' : //PNTERR
case EOF :
//strcpy( cmd,"quit");
if ( ( writen = write ( to_chil[ 1] , "quit/n" , 5) ) ! = 5 )
{
write ( STDERR_FILENO, "write error/n" , strlen ( "write error/n" ) ) ;
//_exit(5);
return ;
} else {
write ( STDERR_FILENO, "youput q, quit/n" , strlen ( "youput q, quit/n" ) ) ;
//write();
}
//write(to_chil[1],'/n',1);
break ;
case 'p' :
//PNTERR
//strcpy( cmd,"pause");
if ( ( writen = write ( to_chil[ 1] , "pause/n" , 6) ) ! = 6 ) {
write ( STDERR_FILENO, "write error/n" , strlen ( "write error/n" ) ) ;
return ;
} else {
write ( STDERR_FILENO, "you put p, pause/n" , strlen ( "you put p, pause/n" ) ) ;
//write();
}
//write(to_chil[1],'/n',1);
break ;
case 'f' : {
writen = strlen ( "vo_fullscreen" ) ;
if ( write ( to_chil[ 1] , "vo_fullscreen" , writen) ! = writen ) {
return ;
}
else {
STDERR_FILENO, "you put f, full/n" , strlen ( "you put f, full/n" ) ;
}
break ;
}
case 's' : {
writen = strlen ( "seek 120.2" ) ;
if ( write ( to_chil[ 1] , "seek 120.2" , writen) ! = writen ) {
return ;
}
else {
STDERR_FILENO, "you put s, seek 30/n" , strlen ( "you put s, seek 30/n" ) ;
}
break ;
}
default :
//PNTERR
//fprintf(stderr,"no corresponding cmd/n");
//strcpy(cmd,"");
break ;
}
}
}
}
//fprintf(stderr,"on_input: afte asign, cmd(b) is %s/n", cmd);
aio_read( & kbcbuf ) ; //place a new request
}
//version 2
void setup_aio_buf( ) {
PNTERR
fprintf ( stderr , "entering setup_aio_buf/n" ) ;
static char input[ 2] ;
fprintf ( stderr , "chars input is %s/n" , input) ;
kbcbuf. aio_fildes = 0; //父子进程虽然都用本函数,但0的意义对父进程是键盘输入,对子进程是来自父进程的输出。
kbcbuf. aio_buf = input;
kbcbuf. aio_nbytes = 2;
kbcbuf. aio_offset = 0;
//link the aio request with teh siganl handler
kbcbuf. aio_sigevent. sigev_notify = SIGEV_SIGNAL;
kbcbuf. aio_sigevent. sigev_signo = SIGIO;
kbcbuf. aio_sigevent. sigev_value. sival_ptr = & kbcbuf;
//map the signal to signal handler
sigaction ( SIGIO, & sig_act, NULL ) ;
aio_read( & kbcbuf ) ;
fprintf ( stderr , "leave out setup_aio_buf/n" ) ;
}
// not used now
void setup_aio_buf_child( ) {
PNTERR
fprintf ( stderr , "child entering setup_aio_buf/n" ) ;
static char input[ 2] ;
fprintf ( stderr , "chars input is %s/n" , input) ;
kbcbuf. aio_fildes = 0; //父子进程虽然都用本函数,但0的意义对父进程是键盘输入,对子进程是来自父进程的输出。
kbcbuf. aio_buf = input;
kbcbuf. aio_nbytes = 2;
kbcbuf. aio_offset = 0;
//link the aio request with teh siganl handler
kbcbuf. aio_sigevent. sigev_notify = SIGEV_SIGNAL;
kbcbuf. aio_sigevent. sigev_signo = SIGIO;
kbcbuf. aio_sigevent. sigev_value. sival_ptr = & kbcbuf;
//map the signal to signal handler
sigaction ( SIGIO, & sig_act, NULL ) ;
aio_read( & kbcbuf ) ;
fprintf ( stderr , "leave out setup_aio_buf/n" ) ;
}
void parentCode( ) {
PNTERR
fprintf ( stderr , "parent start/n" ) ;
signal ( SIGCHLD, SIG_IGN ) ; //需要吗?
//父进程的标准输入不变,还是键盘
fprintf ( stderr , "parent: close 1/n" ) ;
//close(1); // 重新设置输出
close ( to_chil[ 0] ) ; //关闭读端,这样才好让on_input写数据到管道
if ( dup2( to_chil[ 1] , 1) ! = 1) { // 设置父进程的标准输出写到子进程
perror ( "parent dup2" ) ;
_exit( 1) ;
}
//close( to_chil[1] );//经过复制后,因为已经指定管道的写端是到1。所以这里可以关掉。
fprintf ( stderr , "parent dup2(to_chil) finished/n" ) ;
// close(0);
// dup(to_par[0]); // 设置子进程的输入为
// close(to_par[0]);
//close(to_chil[0]);//要想通信,应该不能关闭to_chil[0]
// close(to_par[1]);
fprintf ( stderr , "parent begin to wait/n" ) ;
//wait(NULL);
// PNTERR
// fprintf(stderr,"parent wait finished/n");
// printf("parent wait finished/n");
// fprintf(stderr,"about to exit parent_code /n");
// _exit(EXIT_SUCCESS);//如果这里注释掉,那么播放完后进入死循环
//fprintf(stderr,"parent start/n");
}
/*
http://blog.chinaunix.net/u2/62281/showart_502251.html
fork后子进程的特点
1.有自己的进程id,父进程
2.有父进程的文件描述字副本(系统打开文件表项相同,包括了文件状态标签(open的flags参数)、文件当前位置和指向该文件v-node的ptr)
3.进程所耗费的时间全部置0
4.进程的悬挂信号和悬挂的时钟定时器都被清除。但是继承父进程的信号屏蔽位和信号动作
5.不继承父进程的文件锁
所以,根据4,下面的子进程函数,继承了信号屏蔽,信号动作。
另外
继承的是:
信号(signal)控制设定(就是指信号屏蔽,信号动作)
子进程所独有:
不继承异步输入和输出。
这样,下面的子进程函数,没有继承setup_aio_buf()。
*/
/*
播放选项里面加了-ao oss 选项,是因为参考了网页:
http://forum.ubuntu.org.cn/viewtopic.php?t=114969&sid=2d9393fe0b007faa7bd114351f8a7366
不加的话,终端中播放会间歇性出现(mplayer相关)
alsa-space: xrun of at least 2.682 msecs. resetting stream
的提示
*/
void childCode2( char * pfilename) {
PNTERR
int n;
char readbuf[ 1024] ;
fprintf ( stderr , "child start/n" ) ;
fprintf ( stderr , "child :close 0/n" ) ;
close ( to_chil[ 1] ) ; // 这样才能读
if ( dup2( to_chil[ 0] , 0) = = - 1 ) // 将管道的读复制到标准输入,也就是读入to_chil[1]传递的数据
{
perror ( "child dup2" ) ;
_exit( 1) ;
} /*else{ //for test
n = read(0,readbuf,1024);//blocking
n = write( STDERR_FILENO,readbuf,n );
}*/
//close( to_chil[0]);
//标准输出还是屏幕
//close(1); // 关闭老的标准输出
//dup(to_par[1]); // 将管道的写复制到标准输出//也就是让mplayer的文字输出仍热显示在屏幕
//close(to_par[1]); // 关闭不必要的管道描述符
//close(to_chil[0]);
//close(to_par[0]);
//close(to_chil[1]);
//filename = fgets();
PNTERR
fprintf ( stderr , "child :about to execlp mplayer/n" ) ;
execlp( "/usr/bin/mplayer" , "-slave" , "-quiet" , "-ao" , "oss" , /*"-vf",*/ pfilename, NULL ) ;
//fprintf(stderr,"child :about to exit playing /n");
//fprintf(stderr,"child :after exit/n");
//_exit(EXIT_SUCCESS);
}
/*
http://www.ibm.com/developerworks/cn/linux/l-pipebid/index.html
http://www.chinalinuxpub.com/doc/pro/fork.html
再来看看另外一个例子:
#include <string.h>
char string[] = "Hello, world";
main()
{
int count, i;
int to_par[2], to_chil[2]; // 到父、子进程的管道
char buf[256];
pipe(to_par);
pipe(to_chil);
if (fork() == 0) {
// 子进程在此执行
close(0); // 关闭老的标准输入
dup(to_child[0]); // 将管道的读复制到标准输入
close(1); // 关闭老的标准输出
dup(to_par[1]); // 将管道的写复制到标准输出
close(to_par[1]); // 关闭不必要的管道描述符
close(to_chil[0]);
close(to_par[0]);
close(to_chil[1]);
for (;;) {
if ((count = read(0, buf, sizeof(buf)) == 0)
exit();
write(1, buf, count);//child write to parent
}
}
// 父进程在此执行
close(1); // 重新设置标准输入、输出
dup(to_chil[1]);
close(0);
dup(to_par[0]);
close(to_chil[1]);
close(to_par[0]);
close(to_chil[0]);
close(to_par[1]);
for (i = 0; i < 15; i++) {
write(1, string, strlen(string));
read(0, buf, sizeof(buf));
}
}
子进程从父进程继承了文件描述符0和1(标准输入和标准输出)。两次执行系统调用 pipe 分别在数组 to_par 和 to_chil 中分配了两个文件描述符。然后该进程 执行系统调用 fork,并复制进程上下文:象前一个例子一样,每个进程存取 自己的私有数据。父进程关闭他的标准输出文件(文件描述符1),并复制(dup)从管道 线 to_chil 返回的写文件描述符。因为在父进程文件描述符表中的第一个空槽是刚刚 由关闭腾出来的,所以核心将管道线写文件描述符复制到了文件描述符表中的第一 项中,这样,标准输出文件描述符变成了管道线 to_chil 的写文件描述符。 父进程以类似的操作将标准输入文件描述符替换为管道线 to_par 的读文件 描述符。与此类似,子进程关闭他的标准输入文件(文件描述符0),然后复制 (dup) 管道 线 to_chil 的读文件描述符。由于文件描述符表的第一个空项是原先的标准 输入项,所以子进程的标准输入变成了管道线 to_chil 的读文件描述符。 子进程做一组类似的操作使他的标准输出变成管道线 to_par 的写文件描述 符。然后两个进程关闭从 pipe 返回的文件描述符。上述操作的结果是:当 父进程向标准输出写东西的时候,他实际上是写向 to_chil--向子进程发送 数据,而子进程则从他的标准输入读管道线。当子进程向他的标准输出写的时候, 他实际上是写入 to_par--向父进程发送数据,而父进程则从他的标准输入 接收来自管道线的数据。两个进程通过两条管道线交换消息。
无论两个进程执行的顺序如何,这个程序执行的结果是不变的。他们可能去执行睡眠 和唤醒来等待对方。父进程在15次循环后退出。然后子进程因管道线没有写进程而读 到“文件尾”标志,并退出。
*/
/*/ / on_input version 2
void on_input( ) {
int c[ 2] ;
int writen= 0;
//static char cmd[CHAR_LEN];
char * ptr_c = ( char * ) kbcbuf. aio_buf;
//fprintf(stderr,"on_input: chars cmd(a) is %s/n",cmd);
if ( aio_error( & kbcbuf ) ! = 0) {
//perror("reading failed");
return ;
}
else if ( aio_return( & kbcbuf ) = = 2 ) { //正确得到一个输入的字符,但这里考虑到了回车符号
* c = * ptr_c; //忽略ptr_c指向的第二个,即回车符
//strcpy( c,ptr_c );
//fprintf(stderr," you put %c/n", *c);
switch ( * c ) {
case 'q' : //PNTERR
case EOF :
//strcpy( cmd,"quit");
if ( ( writen = write ( to_chil[ 1] , "quit" , 4) ) ! = 4 )
exit ( 5) ;
break ;
case 'p' :
//PNTERR
//strcpy( cmd,"pause");
if ( ( writen = write ( to_chil[ 1] , "pause" , 5) ) ! = 5 ) {
exit ( 6) ;
}
break ;
default :
//PNTERR
//fprintf(stderr,"no corresponding cmd/n");
//strcpy(cmd,"");
break ;
}
}
//fprintf(stderr,"on_input: afte asign, cmd(b) is %s/n", cmd);
aio_read( & kbcbuf ) ; //place a new request*/
//}
/*
为何在一个fork的子进程分支中使用_exit函数而不使用exit函数?
‘exit()’与‘_exit()’有不少区别在使用‘fork()’,特别是‘vfork()’时变得很突出。
‘exit()’与‘_exit()’的基本区别在于前一个调用实施与调用库里用户状态结构 (user-mode constructs)有关的清除工作(clean-up),而且调用用户自定义的清除程序 (译者注:自定义清除程序由atexit函数定义,可定义多次,并以倒序执行),相对应,后一个函数只为进程实施内核清除工作。
在由‘fork()’创建的子进程分支里,正常情况下使用‘exit()’是不正确的,这是因为使用它会导致标准输入输出(译者注:stdio: Standard Input Output)的缓冲区被清空两次,而且临时文件被出乎意料的删除(译者注:临时文件由tmpfile函数创建在系统临时目录下,文件名由系统随机生 成)。在C++程序中情况会更糟,因为静态目标(static objects)的析构函数(destructors)可以被错误地执行。(还有一些特殊情况,比如守护程序,它们的*父进程*需要调用 ‘_exit()’而不是子进程;适用于绝大多数情况的基本规则是,‘exit()’在每一次进入‘main’函数后只调用一次。)
在由‘vfork()’创建的子进程分支里,‘exit()’的使用将更加危险,因为它将影响 *父*进程的状态。
来至:http://www.cublog.cn/u2/61322/showart_1271285.html