网上有很多 基于mini2440的MP3播放器设计的资料,多是按键控制,这里博主做了些轻微改动,利用远程WEB来控制MP3播放,具体怎么实现,下面会给出,大家先看看效果:
WEB界面:
后台运行:
因为不是什么课程设计报告,博主就不阐述的那么详细,这个设计主要有三部分组成:
1、WEB控制端(就是浏览器);
2、WEB服务器端(将mini2440开发板作为服务器,其中移植了BOA服务器,这里不做详细介绍,大家网上搜一搜,好多资料,BOA服务器是嵌入式设备中用的比较多的WEB服务器);
3、应用程序部分(就是MP3播放器);
下面将代码列出,就其中重要部分进行详解:
MP3播放代码如下:
/* * SD卡mp3播放器控制程序 * 功能: k1:播放、暂停 k2:停止播放 k3:上一首 k4:下一首 * 附加:歌曲自动循环播放SD卡/sdcard/song目录中mp3歌曲 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h> #include <sys/wait.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> /*共享内存申请标记*/ #define PERM S_IRUSR|S_IWUSR /*双向循环列表:存放歌曲名*/ struct song { char songname[20]; struct song *prev; struct song *next; }; /*孙子进程id号*/ pid_t gradchild; /*子进程id号*/ pid_t pid; /*共享内存描述标记*/ int shmid; char *p_addr; /*共享内存内容格式*/ /*|gradchild(孙子进程PID) |+ |空一个字节|+ currentsong(当前播放列表的节点指针)|*/ /*播放标记*/ int first_key=1; int play_flag=0; /************************************************* Function name: play Parameter : struct song * Description : 播放函数 Return : void Argument : void **************************************************/ void play(struct song *currentsong) { pid_t fd; char *c_addr; char *p; int len; char my_song[30]="/sdcard/song/"; while(currentsong) { /*创建子进程,即孙子进程*/ fd = fork(); if(fd == -1) { perror("fork"); exit(1); } else if(fd == 0) { /*把歌曲名加上根路径*/ strcat(my_song,currentsong->songname); p = my_song; len = strlen(p); /*去掉文件名最后的'\n'*/ my_song[len-1]='\0'; printf("THIS SONG IS %s\n",my_song); /*运行madplay播放器,播放MP3*/ system("printf(" THIS SONG IS %s\n", $my_song) >>/tmp/songname"); execl("/bin/madplay","madplay",my_song,NULL); printf("\n\n\n"); } else { /*内存映射*/ c_addr = shmat(shmid,0,0); memcpy(c_addr,&fd,sizeof(pid_t)); memcpy(c_addr + sizeof(pid_t)+1,¤tsong,4); /*使用wait阻塞子进程,直到孙子进程播放完才能被唤醒; 当被唤醒时,表示播放MP3期间没有按键按下,则继续顺序播放下一首MP3*/ if(fd == wait(NULL)) { currentsong = currentsong->next; printf("THE NEXT SONG IS %s\n",currentsong->songname); } } } } /************************************************* Function name: creat_song_list Parameter : void Description : 创建歌曲名的双向循环链表 Return : struct song * Argument : void **************************************************/ struct song *creat_song_list(void) { FILE *fd; size_t size; size_t len; char *line = NULL; struct song *head; struct song *p1; struct song *p2; system("ls /sdcard/song >song_list"); fd = fopen("song_list","r"); p1 = (struct song *)malloc(sizeof(struct song)); printf("==================================song list=====================================\n"); system("ls /sdcard/song"); printf("\n"); printf("================================================================================\n"); size = getline(&line,&len,fd); strncpy(p1->songname,line,strlen(line)); head = p1; while((size = getline(&line,&len,fd)) != -1) //从文件中读取一行,直到出错或者到文件尾EOF返回-1 { p2 = p1; p1 = (struct song *)malloc(sizeof(struct song)); strncpy(p1->songname,line,strlen(line)); p2->next = p1; p1->prev = p2; } //此时到了文件尾,若是循环列表,则下一首为head,即第一首 p1->next = head; head->prev = p1; p1 = NULL; p2 = NULL; system("rm -rf song_list"); return head; } /************************************************* Function name: startplay Parameter : pid_t *,struct song * Description : 开始播放函数 Return : void Argument : void **************************************************/ void startplay(pid_t *childpid,struct song *my_song) { pid_t pid; int ret; /*创建子进程*/ pid = fork(); if(pid > 0) //父进程 { *childpid = pid; //子进程PID初始化 play_flag = 1; sleep(1); /*读取共享内存保存的pid,初始化孙子进程的pid*/ memcpy(&gradchild,p_addr,sizeof(pid_t)); } else if(0 == pid) //子进程 { /*子进程播放MP3函数*/ play(my_song); } } /************************************************* Function name: my_pause Parameter : pid_t Description : 暂停函数 Return : void Argument : void **************************************************/ void my_pause(pid_t pid) { printf("=======================PAUSE!PRESS K1 TO CONTINUE===================\n"); kill(pid,SIGSTOP); //对孙子进程发送SIGSTOP信号 play_flag = 0; } /************************************************* Function name: my_pause Parameter : pid_t Description : 停止播放函数 Return : void Argument : void Autor & date : Hanson 11,04, 05 **************************************************/ void my_stop(pid_t g_pid) { printf("=======================STOP!PRESS K1 TO START PLAY===================\n"); kill(g_pid,SIGKILL); //对孙子进程发送SIGKILL信号 kill(pid,SIGKILL); //对子进程发送SIGKILL信号 first_key=1; } /************************************************* Function name: conti_play Parameter : pid_t Description : 继续函数 Return : void Argument : void **************************************************/ void conti_play(pid_t pid) { printf("===============================CONTINUE=============================\n"); kill(pid,SIGCONT); //对孙子进程发送SIGCONT信号 play_flag=1; } /************************************************* Function name: next Parameter : pid_t Description : 下一首函数 Return : void Argument : void **************************************************/ void next(pid_t next_pid) { struct song *nextsong; printf("===============================NEXT MP3=============================\n"); /*从共享内存获得孙子进程播放歌曲的节点指针*/ memcpy(&nextsong,p_addr + sizeof(pid_t)+1,4); /*指向下首歌曲的节点*/ nextsong = nextsong->next; /*杀死当前歌曲播放的子进程,孙子进程*/ kill(pid,SIGKILL); kill(next_pid,SIGKILL); wait(NULL); startplay(&pid,nextsong); } /************************************************* Function name: prev Parameter : pid_t Description : 上一首函数 Return : void Argument : void **************************************************/ void prev(pid_t prev_pid) { struct song *prevsong; /*从共享内存获得孙子进程播放歌曲的节点指针*/ printf("===============================PRIOR MP3=============================\n"); memcpy(&prevsong,p_addr + sizeof(pid_t)+1,4); /*指向上首歌曲的节点*/ prevsong = prevsong->prev; /*杀死当前歌曲播放的子进程,孙子进程*/ kill(pid,SIGKILL); kill(prev_pid,SIGKILL); wait(NULL); startplay(&pid,prevsong); } /************************************************* Function name: main Parameter : void Description : 主函数 Return : int Argument : void **************************************************/ int main(void) { // int buttons_fd; int mp3_control_pipe; int mp3_control_pipe_write; // int key_value; int playnum; // char playnum; struct song *head; /*打开设备文件*/ // buttons_fd = open("/dev/key", 0); // if (buttons_fd < 0) { // perror("open device buttons"); // exit(1); // } /*创建命名管道mp3-control*/ unlink("/tmp/mp3_control"); mkfifo("/tmp/mp3_control",0666); mp3_control_pipe=open("/tmp/mp3_control",O_RDONLY|O_NONBLOCK); mp3_control_pipe_write=open("/tmp/mp3_control",O_WRONLY|O_NONBLOCK); if(mp3_control_pipe<0) { perror("open control pipe for read"); } printf("**********基于mini2440的网页控制MP3播放器*********** \n"); printf("********************XXX制作 ************************ \n"); printf("**************嵌入式开发项目************************ \n"); /*创建播放列表*/ head = creat_song_list(); printf("===================================FUNTION======================================\n\n"); printf(" K1:播放、暂停 K2:停止播放 K3:下一歌曲 K4:上一歌曲 \n\n"); printf("================================================================================\n"); /*共享内存:用于存放子进程ID,播放列表位置*/ if((shmid = shmget(IPC_PRIVATE,5,PERM))== -1) exit(1); p_addr = shmat(shmid,0,0); memset(p_addr,'\0',1024); while(1) { fd_set rds;/*先声明一个 fd_set 集合来保存我们要检测的 buttons_fd句柄*/ int ret1; FD_ZERO(&rds);/*用select函数之前将rds清零*/ FD_SET(mp3_control_pipe, &rds);/*将检测到的句柄放到集合rds里*/ /*监听获取键值*/ //struct timeval timeout = {30,0}; ret1 = select(mp3_control_pipe + 1, &rds, NULL, NULL,NULL); //printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret1); if (ret1 < 0) { perror("select"); exit(1); } if (ret1 == 0) printf("Timeout.\n"); else if (FD_ISSET(mp3_control_pipe, &rds)) { static char buffer[10]; int ret = read(mp3_control_pipe, &buffer, 2); // printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret); if (ret != 2) { if (errno != EAGAIN) { perror("read buttons\n"); continue; } } else { int playnum1; if(sscanf(buffer,"%d",&playnum1)==1) { playnum=playnum1; } memset(buffer,0,sizeof(buffer)); printf("buttons_value: %d\n", playnum); /*首次播放,必须是按键1*/ if(first_key){ switch(playnum) { case 0: startplay(&pid,head); first_key=0; break; case 1: case 2: case 3: printf("=======================PRESS K1 TO START PLAY===================\n"); break; default: printf("=======================PRESS K1 TO START PLAY===================\n"); break; } //end switch }//end if(first_key) /*若不是首次播放,则根据不同键值处理*/ else if(!first_key){ switch(playnum) { case 0: //printf("play_flag:%d\n",play_flag); if(play_flag) my_pause(gradchild); else conti_play(gradchild); break; case 1: my_stop(gradchild); break; case 2: next(gradchild); break; case 3: prev(gradchild); break; } //end switch }//end if(!first_key) } } } close(mp3_control_pipe); return 0; }
int main(void) { int mp3_control_pipe; int mp3_control_pipe_write; int playnum; struct song *head; /*打开设备文件*/ *创建命名管道mp3-control*/ unlink("/tmp/mp3_control"); mkfifo("/tmp/mp3_control",0666); mp3_control_pipe=open("/tmp/mp3_control",O_RDONLY|O_NONBLOCK); mp3_control_pipe_write=open("/tmp/mp3_control",O_WRONLY|O_NONBLOCK); if(mp3_control_pipe<0) { perror("open control pipe for read"); }这部分主要是建立一个命名管道mp3_control,以实现进程间通信,原来博主只是为了在MP3模块中不断的读取管道里面的数据,所以第一次时只是单独的读:
mp3_control_pipe=open("/tmp/mp3_control",O_RDONLY|O_NONBLOCK);但是后面执行时,却一直读取管道里面的内容,就是对一个相同的数据不断地读取,成了一个死循环,最后查找资料把写管道也打开了,这样才OK了,大家知道是什么原因吗?博主查找了相关资料,却没找到答案,只在某地看到这个,大家看看是什么意思,为什么要把读和写都打开
下面这段代码是不断的监听管道里面的数值,并作出相应操作
/*共享内存:用于存放子进程ID,播放列表位置*/ if((shmid = shmget(IPC_PRIVATE,5,PERM))== -1) exit(1); p_addr = shmat(shmid,0,0); memset(p_addr,'\0',1024); while(1) { fd_set rds;/*先声明一个 fd_set 集合来保存我们要检测的 buttons_fd句柄*/ int ret1; FD_ZERO(&rds);/*用select函数之前将rds清零*/ FD_SET(mp3_control_pipe, &rds);/*将检测到的句柄放到集合rds里*/ /*监听获取键值*/ //struct timeval timeout = {30,0}; ret1 = select(mp3_control_pipe + 1, &rds, NULL, NULL,NULL); //printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret1); if (ret1 < 0) { perror("select"); exit(1); } if (ret1 == 0) printf("Timeout.\n"); else if (FD_ISSET(mp3_control_pipe, &rds)) { static char buffer[10]; int ret = read(mp3_control_pipe, &buffer, 2); // printf ("%s %d %d\n", __FUNCTION__, __LINE__, ret); if (ret != 2) { if (errno != EAGAIN) { perror("read buttons\n"); continue; } } else { int playnum1; if(sscanf(buffer,"%d",&playnum1)==1) { playnum=playnum1; } memset(buffer,0,sizeof(buffer)); printf("buttons_value: %d\n", playnum);
BOA服务器这里先不详细介绍,主要通过调用CGI程序实现与应用程序的信息交互,这里就是调用CGI程序向管道里写数据的,代码如下:
#!/bin/sh type=0 case $QUERY_STRING in *play*) type=0 ;; *stop*) type=1 ;; *priv*) type=2 ;; *next*) type=3 ;; esac /bin/echo $type > /tmp/mp3_control echo "Content-type: text/html; charset=gb2312" echo /bin/cat mp3-result.template exit 0
三、WEB控制部分
博主对WEB开发没有学习过,只是利用别人的脚本进行简单更改,代码如下:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>网页控制MP3</title> <style type="text/css"> <!-- a:link { color:#FFFFFF; text-decoration: none } a:visited { color:#FFFFFF; text-decoration: none} a:hover { color: #FF6600; text-decoration: underline} a.develop_item_link:link {color:#FF6600; font-size: 9pt; text-decoration:underline} a.left_item_style:hover {color:green; font-size: 9pt; text-decoration:none} a.develop_item_style:hover {color:"#00FF00"; font-size: 9pt; text-decoration:none} a.download_item_style:hover {color:bule; font-size: 9pt; text-decoration:none} a.middle_item_style:hover {color:red; font-size: 12pt; text-decoration:underline} a.middle_item_style:link { color: #000000; text-decoration: none} a.middle_item_style:visited { color:#000000; text-decoration: none} .STYLE13 {color:#FFFFFF; text-decoration: none} .STYLE14 { font-size: 16px; font-weight: bold; } --> </style> <script language="JavaScript"> <!-- var j,old_image; function change_over(){ var ob=change_over.arguments; for(i=0;i<document.all.length;i++) if(document.all[i].name==ob[0]) j=i; old_image=document.all[j].src; document.all[j].src=ob[1]; } function change_out(){ document.all[j].src=old_image; } //--> </script> </head> <body bgcolor="#666666"> <table width="800" align="center" cellpadding="0"> <tr> <td width="9"> </td> <td width="781" colspan="2"><div align="center"><img src="images1/bar.jpg" width="780" height="120" /></div></td> </tr> <tr> <td> </td> <td colspan="2"><table width="780" cellpadding="0" cellspacing="0" border="0"> <tr> <td> </td> <td> </td> <td> </td> </tr> <tr> <td width="250"><a href="mp3_control.html" onMouseOut="change_out()" onMouseOver="change_over('Image1','images1/bar_left_thick.jpg')"><img src="images1/bar_left_thick.jpg" width="780" height="37" border="0" name="Image1"></a></td> </tr> </table></td> </tr> <tr> <td> </td> <td colspan="2"> </td> </tr> <tr> <td> </td> <td colspan="2"><form method="get" action="mp3_control.cgi" name="WEBCAM-TEST"> <div align="center"><span class="STYLE9">点击下面的MP3播放选项,可以选择MP3播放 </span> </div> </form></td> </tr> <tr> <td> </td> <td colspan="2" align="center"><form method="get" action="mp3_control.cgi" name="LED-TEST"> <div align="left"> <table border="0" width="280" align="center"> <tr> <td width="133"> <p align="center">选择播放</td> </tr> <tr> </tr> <tr> <td width="140"> <p align="center"> <input type="radio" value="play" checked name="type">播放/暂停</td> </tr> <tr> <td width="140"> <p align="center"><input type="radio" name="type" value="stop" checked>停止播放</td> </tr> <tr> <td width="140"> <p align="center"> <input type="radio" name="type" value="priv">上一歌曲</td> </tr> <tr> <td width="140"> <p align="center"><input type="radio" name="type" value="next">下一歌曲</td> </tr> <tr> <td colspan="2" width="272"> <p align="center"><input type="submit" value="确定(OK)" name="submit"></td> </tr> </table> </div> <div align="center"></div><div align="center"></div><div align="left"></div><div align="left"></div></form> </td> </tr> <tr> <td height="42"> </td> <td colspan="2" background="images1/bottom.jpg" height="39" > <div align="center" class="STYLE13"> <strong>Web Server test page 肖志强制作</strong> <a href="http://embedclub.taobao.com/" class="STYLE14">17768137013 </a></div></td> </tr> <tr> <td> </td> <td colspan="2" align="center"> </td> </tr> </table> </body> </html>