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

来源:互联网 发布:第3套人民币10元爱淘宝 编辑:程序博客网 时间:2024/06/06 05:09

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

原创粉丝点击