北京理工大学 20981 陈罡
呵呵,今天可以放心的睡觉了,mplayer前端已经开发成功了(尽管还有一些细节需要调整)。
费了不少力气,不过总算可以对自己有一个交代了。
整体思路是这样的:
首先,编写一个filter,过滤以下从文件浏览器中穿过来的文件类型,凡是mplayer可以支持的
类型,就可以进行下一步的播放;然后,使用下面列出代码,开始播放文件(mplayer支持slave
模式就是好啊,开发mplayer的open source大哥们都是天才!)。
需要详细说明的若干函数:
(1)socketpair(...)
#include
int socketpair(int domain, int type, int protocol,
int socket_vector[2]);
这个函数用于创建两个互相互通的本地socket描述符,由于偶的front程序要与mplayer交换数据,
这已经属于进程间数据交换的范畴,可以考虑使用IPC的若干种方案(管道,本地socket,共享内存,
共享文件,全局信号等等)。在这里,我们采用了本地socket绑定标准输入输出的方案,把进程间
的数据交换,抽象为socket数据的收发,实现对slave模式下的mplayer控制。别的方式肯定也可以的
但是或许需要修改mplayer源代码,会丧失mplayer的独立性,所以暂时不做考虑。
(2)dup2(...)
#include
int dup2(int fildes, int fildes2);
该函数用于把fildes2的文件描述符复制给fildes,同时关闭fildes2的文件描述符。这里偶把mplayer的
STDOUT_FILENO(标准输出管道),STDIN_FILENO(标准输入管道),STDERR_FILENO(标准错误管道)
都复制给了socket pair的一个socket端口。也就是说,fork出来的子进程的输入输出和出错信息,都将
依赖于该socket,如此一来,偶在主进程里面,只要往另一个socket里面写入数据,就等效于往子进程
的命令行里面输入数据了,mplayer支持的slave模式,就是在播放的同时等待用户输入命令,用这个方
法刚刚好可以满足mplayer的要求。:oD
(3)execvp(...)
#include
int execvp( const char *file, char *const argv[]);
第一个参数是,将要被该函数载入的可执行文件的名称。
第二个参数是argv,相信大家对int main(int argc, char ** argv)都很了解吧?对了,这里的argv,就
是要传送给第一个参数程序用的。
官方对于该函数的解释原文如下:
When execvp() is executed, the program file given by the first argument will be loaded into
the caller's address space and over-write the program there. Then, the second argument will
be provided to the program and starts the execution. As a result, once the specified program
file starts its execution, the original program in the caller's address space is gone and is
replaced by the new program.
该函数是用于在当前进程中开启新的进程的,官方的解释是调用该函数后,会把该函数指定的程序载入当前
进程的地址空间,然后取代当前进程,开始执行指定的程序。注意,这里是取代当前程序,偶肯定不会允许
载入的mplayer取代front前端了,这样的话,前端不就没法继续使用了吗?!因此,又使用了fork()函数为
当前的front前端创建了一子进程,然后在子进程里面调用这个execvp(...),呵呵,你希望取代当前进程的
话,你就取代好了,反正是子进程,就是拿来给你取代的。不会影响到主进程执行,主要是不会影响到主进程
的触摸屏消息处理,这样就没有问题了。(呵呵,彻底搞明白这个东西折腾了我不少时间),希望更加深入
了解该函数的朋友,可以参考如下地址,写得很详细,连示例代码都有了:
至于fork()什么的创建子进程的方法,网上的参考实在太多了,在此不再聒噪。
下面的是测试代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int g_sock_pair[2] ;
pid_t g_pid ;
//这里定义了mplayer的命令行参数,等同与调用:
// ./mplayer -quiet -vf rotate=1,scale=144:176 -slave xxxx.avi
char * g_cmd[8] = {
"./mplayer", "-quiet", "-vf", "rotate=1,scale=144:176", "-slave", NULL, NULL, NULL
} ;
void send_cmd(char * cmd) {
write(g_sock_pair[0], cmd, strlen(cmd)) ;
}
int main(int argc, char * argv[])
{
int i ;
// 这里创建了本地socket对
if(socketpair(AF_UNIX, SOCK_STREAM, 0, g_sock_pair) == 0) {
if((g_pid = fork()) == 0) {
// 这里g_pid=0代表是子进程
printf("child\n") ;
close(g_sock_pair[0]) ; // 关闭通向父进程的管道
dup2(g_sock_pair[1], STDOUT_FILENO); // 绑定标准输入
dup2(g_sock_pair[1], STDIN_FILENO); // 绑定标准输出
dup2(g_sock_pair[1], STDERR_FILENO); // 绑定标准错误输出
close(g_sock_pair[1]) ; // 关闭已经复制过的读取管道
g_cmd[5] = "./a.avi" ; // 这里是设置要播放的视频文件名
for(i = 0 ; i < 6 ; i++) {
printf("-> %s\n", g_cmd[i]) ;
}
execvp(g_cmd[0],(char* const*)g_cmd) ;
exit(0) ;
} else {
// 这里是g_pid非零,是主进程了
printf("parent\n") ;
//parent process
close(g_sock_pair[1]) ;
waitpid(-1,NULL,WNOHANG) ;
}
} else {
printf("socketpair failed !\n") ;
}
sleep(10) ; // 在这里只播放10秒钟,然后往mplayer发送quit命令让其退出。
send_cmd("quit\n") ;
return 0 ;
}
呵呵,用上述代码,就可以把mplayer执行起来并且在播放的过程中,通过send_cmd向mplayer
实时的发送命令了,slave模式支持的命令,请参见“爱你不容易_4”文章中贴出来的slave命令集。
经过上面的实验,mplayer控制已经完全实现了,剩下的就是一些界面细节处理了。目前名字叫chok的
mplayer前端已经可以流畅运行了! :D
把a1200的真机抓图贴出来给感兴趣的朋友show一下:
由于本次的包过大(已经加入了mplayer二进制文件,未压缩之前竟然有5M多!怕怕),压缩后大约有2.9M
左右,偶的博客空间有限,就不上传了。我想林天涯等DX会在a1200人气较旺的论坛上发布一些的。
谢谢各位朋友的关注,下一步就是加入快进,快退,播放世锦等周边的功能了。
呵呵,到此为止,chok前端的主要技术问题已经解决了。
目前做的filter定义为:
char * filter_str[] = {
".mp4", ".MP4", ".avi", ".AVI", ".mp3", ".MP3", ".wav", ".WAV",
".wmv", ".WMV", ".mov", ".MOV", ".ogg", ".OGG", ".mpg", ".MPG"
} ;
这些是偶想当然胡乱定义的,正式版本的时候,是会仔细找找mplayer支持的文件列表的。
阅读(4100) | 评论(9) | 转发(0) |