按键调用并控制mplayer播放的程序--未整理

按键调用并控制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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值