1. 程式人生 > >遠端WEB控制MP3播放器設計(基於mini2440)

遠端WEB控制MP3播放器設計(基於mini2440)

網上有很多 基於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;
}
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了,大家知道是什麼原因嗎?博主查找了相關資料,卻沒找到答案,只在某地看到這個,大家看看是什麼意思,為什麼要把讀和寫都開啟


下面這段程式碼是不斷的監聽管道里面的數值,並作出相應操作

 /*共享記憶體:用於存放子程序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伺服器的呢?博主也會單開文章來講解!