网上有很多 基于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_pipe=open("/tmp/mp3_control",O_RDONLY|O_NONBLOCK);
下面这段代码是不断的监听管道里面的数值,并作出相应操作
- /*共享内存:用于存放子进程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服务器
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>