按键调用并控制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
- 按键调用并控制mplayer播放的程序--未整理
- Slackware安装MPlayer并基本支持firefox的流媒体播放(整理)
- Qt调用mplayer做一个小小的视频播放器
- mplayer的控制
- MPlayer的控制
- MPlayer的控制
- Android中通过耳机按键控制音乐播放的实现
- <Qt>windows平台调用mplayer播放视频
- mplayer 在播放的时候发现画面是倒立的---附上解决方法
- mplayer的控制(QT)
- Linux下电影播放器之解决方案-Mplayer
- PWM --用按键控制 LED 的亮度
- 关于mplayer播放rm,rmvb资料整理(mac)
- ARM开发板mini2440的按键控制LED小程序
- 第二个程序——播放AVI视频,并通过移动slider 控制视频播放
- 使用Mplayer播放avi文件的方法
- MPlayer - Linux下的电影播放器
- Fodora下mplayer播放rmvb的问题
- struts2 api
- Android 中的 Service 全面总结
- 六一悄悄的过了
- Android JNI编程指南及模拟器配置问题(LINUX)
- STL概述
- 按键调用并控制mplayer播放的程序--未整理
- MyEclipse安装jBMP插件
- Android JNI 调用DLL
- MP3文件的ID3V1信息与ID3V2信息结构的分析
- Android GPS架构分析(gps启动过程图)
- MP3文件的ID3V1信息与ID3V2信息结构的分析
- 软件架构设计-五视图方法论
- Copy From
- 在Android(OPhone)模拟器中加载和使用SDCard卡(好!)