远程WEB控制MP3播放器设计(基于mini2440)

网上有很多 基于mini2440的MP3播放器设计的资料,多是按键控制,这里博主做了些轻微改动,利用远程WEB来控制MP3播放,具体怎么实现,下面会给出,大家先看看效果:

WEB界面:


后台运行:

远程WEB控制MP3播放器设计(基于mini2440)_第1张图片

因为不是什么课程设计报告,博主就不阐述的那么详细,这个设计主要有三部分组成:

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;
}


MP3播放的实现答题架构没有变,主要修改部分是控制部分,原来控制是由按键中断来实现的,现将其改为由进程间通信—— 命名管道来控制,现将主要部分代码列出:

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了,大家知道是什么原因吗?博主查找了相关资料,却没找到答案,只在某地看到这个,大家看看是什么意思,为什么要把读和写都打开

远程WEB控制MP3播放器设计(基于mini2440)_第2张图片


下面这段代码是不断的监听管道里面的数值,并作出相应操作

 /*共享内存:用于存放子进程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


这里是用shell脚本编写的,当然也可以用C来编写,具体BOA服务器和CGI博主打算单独写在一篇文章里。


三、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>


而WEB浏览器如何将信息提交给WEB服务器的呢?博主也会单开文章来讲解!








你可能感兴趣的:(Web,linux,嵌入式,web服务器,mp3)