天天看點

linux系統程式設計視訊播放器

制作一個基于linux->gec6818開發闆的視訊播放器

一、項目要求

1、基本操作:暫停,快進快退,視訊切換,音量調節,退出

2、進度條

在這裡頭檔案就不展示了,自己編譯,檢視man手冊

觸摸屏坐标

static int ts_fd;
//觸摸屏初始化 
int open_ts(void)
{
	// 1.打開觸摸屏檔案	
	int x,y;
	ts_fd = open("/dev/input/event0", O_RDONLY);
	if(ts_fd == -1)
	{
		perror("open ts failed");
		return -1;
	}
	return ts_fd;
}
//關閉觸摸屏檔案
void close_ts(void)
{
	close(ts_fd);
}

//擷取坐标
//push 按下狀态 傳出參數
int get_xy(int *x,int *y)
{
	// 2.讀取觸摸屏資料	
	struct input_event ts_buf;
	char x_flag=0,y_flag=0;
	int x_last,y_last;
	int press;
	int x_now,y_now;
	int i=1;
	while(1)
	{
		read(ts_fd, &ts_buf, sizeof(ts_buf));
		//判斷事件類型
		if(ts_buf.type == EV_ABS)
		{
			//判斷事件代号
			if(ts_buf.code == ABS_X)
			{
					x_now=ts_buf.value*800/1024;
					x_flag=1;y_flag=0;
			}
			if(ts_buf.code == ABS_Y)
			{
					y_now=ts_buf.value*480/600;
					y_flag=1;
			}
		}
		else if(ts_buf.type==EV_KEY && ts_buf.code==BTN_TOUCH)
		{
			press = ts_buf.value;
			//*push = press;
		}
		if(x_flag==1 && y_flag==1 && press==1){

					*x=x_now;
					*y=y_now;
					x_flag=0;y_flag=0;
					//printf("[%d,%d]\n",*x,*y);
					break;
		}
	}

}
           

調用的時候直接調用這個get_xy(&x,&y);函數,不過在此之前要定義一個int型的x,y的全局變量;在下面的代碼中會有示範。

lcd.c顯示圖檔檔案

typedef struct {			//bmp圖檔檔案頭資訊封裝 
	// 位圖檔案頭 
	u8  bit_file_type[2];	//位圖檔案類型:'BM'->0x4d42 
	u32 file_size;	  		//整個檔案大小 
	u16 reserved1;	  		//保留 
	u16 reserved2;	  		//保留 
	u32 offset;		  		//檔案頭到位圖資料之間的偏移量 

	// 位圖資訊頭 
	u32 head_size;			//位圖資訊頭長度 
	u32 width;		  		//位圖寬度 
	u32 height;		  		//位圖高度 
	u16 bit_planes;	  		//位圖位面數 
	u16 bits_per_pixel; 	//每個像素的位數 
	u32 compression;		//壓縮說明 
	u32 image_size;			//位圖資料大小 
	u32 h_res;				//水準分辨率 
	u32 v_res;				//垂直分辨率 
	u32 color_palette;		//位圖使用的顔色索引數 
	u32 vip_color;			//重要的顔色索引數目 

}bmp_head;


static unsigned char *FB;

//顯示bmp
void show_bmp(char *bmp_patpname,int x_offset,int y_offset)
{
	/*向lcd檔案中寫入像素點資料*/
	//打開bmp圖檔
	int  bmp_fd = open(bmp_patpname, O_RDONLY);//完整路徑 /mnt/hgfs/shear/day004/1.bmp
	if(bmp_fd == -1)
	{
		perror("打開bmp圖檔失敗");
		return ;
	}
	int n;//多出來的位元組數

	//讀取圖檔頭部資訊
	bmp_head myhead;
	read(bmp_fd, &myhead, sizeof(bmp_head));


	//如果每一行位元組數能被4整除,n為0,否則是多出來的位元組數
	n = ((myhead.width*3)%4==0) ? 0 : 4 -(myhead.width*3)%4;	


	//申請緩沖區
	char tmp_buf[(myhead.width*3+n)*myhead.height];
	//清空緩沖區
	bzero(tmp_buf,sizeof(tmp_buf));
	
	//讀取圖檔資料
	read(bmp_fd, tmp_buf, sizeof(tmp_buf));
	//關閉圖檔檔案
	close(bmp_fd);
	//将資料存入映射記憶體
	//将資料存入映射記憶體
	int i,j;
	for(i=0; i<myhead.height; i++)
		for(j=0; j<myhead.width; j++)
		{
			*(FB+((i+y_offset)*800+j+x_offset)*4) = tmp_buf[((myhead.height-1-i)*myhead.width+j)*3-i*n];
			*(FB+((i+y_offset)*800+j+x_offset)*4+1) = tmp_buf[((myhead.height-1-i)*myhead.width+j)*3+1-i*n];
			*(FB+((i+y_offset)*800+j+x_offset)*4+2) = tmp_buf[((myhead.height-1-i)*myhead.width+j)*3+2-i*n];
			*(FB+((i+y_offset)*800+j+x_offset)*4+3) = 0x00;
		}
	
}
           

show_bmp(char *bmp_patpname,int x_offset,int y_offset);

char *bmp_patpname:檔案名

int x_offset:從設定的x軸坐标開始顯示

int y_offset:從設定的y軸坐标開始顯示

為了容易了解以下代碼不拆分,并且代碼中有詳細的注釋

(注:本代碼用到了漢字庫,自己搞),因為該檔案是.a檔案,無法檢視,隻有聲明,漢字庫等聲明有下:

int   Init_Font(void);  //顯示字型庫前先調用本函數進行初始化
void  UnInit_Font(void); //程式退出前,調用本函數
//清屏函數
int   Clean_Area(int X,  //x坐标起始點
                 int Y,  //y坐标起始點
				 int width, //繪制的寬度
				 int height,//繪制的高度
				 unsigned long color); //往螢幕指定區域填充顔色

//漢字顯示				 
int   Display_characterX(unsigned int x,          //x坐标起始點
                         unsigned int y,          //y坐标起始點
						 unsigned char *string,   //GB2312 中文字元串
						 unsigned int color ,     //字型顔色值
						 int size);               //字型放大倍數 1~8
           

接下來是本項目最主要的東西:(注:該項目沒有聲明,所有從下往上看)

有些函數看不懂的話,自己檢視資料,man手冊都可以

所用到的視訊播放中使用到的“暫停/播放”按鈕的切換,用刷圖檔來解決,圖檔為

linux系統程式設計視訊播放器
linux系統程式設計視訊播放器
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>

#include "ts.h"
#include "lcd.h"
#include "font.h"

int x,y;
FILE* mp;
int c;

struct list_node{
	char video[30];  //視訊名字
	
	struct list_node *prev;
	struct list_node *next;
};

struct list_node *init_list_node()
{
	struct list_node *head = NULL;
	head = (struct list_node *)malloc(sizeof(struct list_node));
	if(head == NULL)
		printf("malloc head error!\n");
		
	head->prev = head;
	head->next = head;
	
	return head;
	
};

int video_option(struct list_node *head);


//============================================
//子程序=======================================
void *progress1(void *arg)
{	
	c=0;
	int posy;
	char buf[1024]={0};
	//擷取目前播放的進度
	while(1)
	{
		if(c==1)
		{
			break;
		}
		bzero(buf,1024);
		fgets(buf,1024,mp);
		//printf("fgets = %s\n",buf);
		
		int pos = 0;
		sscanf(buf,"ANS_PERCENT_POSITION=%d",&pos);
		//printf("pos=%d\n",pos);
		
		//使用清屏函數實作刷進度條
		Clean_Area(0,  390, pos*8,10,0x0);
				//x軸、y軸、長、 寬、顔色
		
		
		/*特别解釋一下*/
		/*在此之前定義了一個整形的posy,一般下面的if是不會滿足的,會直接執行if以外下面的代碼,
		  每次會進行一次指派操作(将pos指派給posy),每過一次循環在“if”裡面會判斷本次“pos”是否小于上一次循環的“posy”,
		  如果小于,則會在執行滿足if條件裡面的代碼,進行一次定位“x軸”往後的清屏操作*/
		if(pos<posy)
		{
			Clean_Area(pos*8,390,800-(pos*8),10,0x0000f0f0);
		}			  //x軸、y軸、長、        寬、顔色
		posy=pos;
	}	
}

//往管道中寫入資料
void *progress2(void *arg)
{	
	c=0;
	int fd = open("/tmp/cmd",O_RDWR);
		if(fd<0)
		{
			printf("管道打開失敗!\n");
			exit(0);
		}
	
	while(1)
	{
		if(c==1)
		{
			break;
		}
		char cmd[1024]={"get_percent_pos\n"};
		write(fd,cmd,strlen(cmd));
		sleep(1);
	}
	
	close(fd);
}
//=================================================
//=================================================


///=================================================
//-------------------------------------------------

//播放
int play(struct list_node *p,struct list_node *head)
{	
	
	mkfifo("/tmp/cmd",0777);
	char buf[100];

	sprintf(buf,"mplayer -quiet -slave -zoom -x 800 -y 390 -input file=/tmp/cmd '/fuxue/%s'",p->video);//拼接字元串到buf裡
	mp=popen(buf,"r");  	
	
	int i = 1;
	//顯示底部控制欄
	show_bmp("./z.bmp",0,400);

	///======================================
	//創兩條線程,一條線程用來刷進度條,一條線程來往播放中寫入擷取進度指令
	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,progress1,NULL);
	pthread_create(&tid2,NULL,progress2,NULL);
	///======================================
	
	while(1)
	{
		//擷取點選的坐标
		get_xy(&x,&y); 
		
		//減音量
		if(x>73&&x<153&&y>400&&y<480)
		{
			system("echo \"volume -20 0\" > /tmp/cmd");
			Display_characterX(2,400,"音量",0x0,2);//螢幕顯示字元“音量”(注:要搞漢字庫到開發闆中)
			Display_characterX(3,450,"-20",0x0,2); //螢幕顯示字元“+10”(注:要搞漢字庫到開發闆中)
			sleep(1);
			Clean_Area(0,  400, 73, 80, 0x00ffffff);  //對左下腳這塊區域清屏
		}           //x軸、y軸、長、寬、顔色
		
		//切換到上一個視訊
		if(x>156&&x<252&&y>400&&y<480)
		{	
			system("killall -9 mplayer");
			p=p->prev;
	
			if(p==head)                /*現在的p就是之前的p的前面一位,假如現在的p就是連結清單中的頭節點,那麼p應該再向前面移動一位*/
			{
				p=p->prev;
			}
			sprintf(buf,"mplayer -quiet -slave -zoom -x 800 -y 390 -input file=/tmp/cmd '/fuxue/%s'",p->video);
			mp = popen(buf,"r");
		}
		
		//快退5s
		if(x>258&&x<360&&y>400&&y<480)
		{	
			system("echo \"seek -5 0\" > /tmp/cmd");	
			printf("前進5S\n");
		}
		
		
		//暫停/播放
		if(x>365&&x<458&&y>400&&y<480)
		{
 			 i++;
			if(i%2==1)
			{
 				show_bmp("./b.bmp",0,400);
				printf("暫停!\n");  
				system("killall -STOP mplayer");//發送一個暫停信号
			}
			if(i%2==0)
			{
				show_bmp("./z.bmp",0,400);
				printf("繼續!\n");
				system("killall -CONT mplayer");//發送一個繼續信号
			} 
		}
		
		
		
		//快進5s		
		if(x>435&&x<537&&y>400&&y<480)
		{
			//快進5s
			system("echo \"seek +5 0\" > /tmp/cmd");			
			printf("快進5S\n");
		}
		
		//切換到下一個視訊		
		if(x>548&&x<632&&y>400&&y<480)
		{
			system("killall -9 mplayer");
			
			p=p->next; 
			if(p==head)    /*現在的p就是之前的p的前面一位,假如現在的p就是連結清單中的頭節點,那麼p應該再向前面移動一位*/
			{
				p=p->next; 
			}
			
			//将目前節點的視訊檔案名拼接到這段字元串中,并儲存到buf裡
			sprintf(buf,"mplayer -quiet -slave -zoom -x 800 -y 390 -input file=/tmp/cmd '/fuxue/%s'",p->video);
			mp = popen(buf,"r");	
		}
		
		//音量加
		if(x>640&&x<717&&y>400&&y<480)
		{
			system("echo \"volume +10 0\" > /tmp/cmd");
			Display_characterX(2,400,"音量",0x0,2);//螢幕顯示字元“音量”(注:要搞漢字庫到開發闆中)
			Display_characterX(3,450,"+10",0x0,2);  //螢幕顯示字元“+10”(注:要搞漢字庫到開發闆中)
			sleep(1);
			Clean_Area(0,  400,73,80,0x00ffffff);//對左下腳這塊區域清屏
		}           //x軸、y軸、長、寬、顔色
		
		if(x>724&&x<800&&y>400&&y<480)
		{
			//退出
			system("killall -9 mplayer");
			c = 1;
			pthread_join(tid1,NULL);
			pthread_join(tid2,NULL);
			video_option(head);

			//break;
		}	
	}
	return 0;
}

//周遊(顯示視訊圖示)(方法二:用連結清單的方法周遊連結清單)
int video_option(struct list_node *head)
{
	
	int n;
	char name[8][60];//實時顯示到螢幕的名字
	struct list_node *p = NULL;

	while(1)
	{
		int j=1;
		int i = 0;
		
		Clean_Area(0,0,800,480,0x00f0f0);//清屏
		printf("==============\n");
		for(p=head->next;p!=head;p=p->next)
		{
			//儲存名字到目前顯示數組
			strcpy(name[i],p->video);
			//在終端中顯示
			printf("目前目錄下的檔案為%s\n",name[i]);
			//在開發闆螢幕中顯示
			Display_characterX(100,50*(i+1),name[i],0x0,2);
			i++;
		}	
		printf("目前有%d個檔案\n",i);
		printf("===============\n");
		
		get_xy(&x,&y);//擷取點選的坐标
		for(n=0;n<i;n++)
		{
			//這個會根據檔案名子在螢幕下的坐标值,實時将坐标搞到二維數組當中(二級指針),也就是說,檔案名字檔案名字顯示的區域對應相應的坐标值
			if(x>0 && x<400&&y>50*(n+1)&&y<50+50*(n+1))
				break;
		}

			name[n];
			
		for(p=head->next;p!=head;p=p->next)
		{
			if(strstr(p->video,name[n]))
			{
				play(p,head);
			}
		}		
		
		
	}
	return 0;
}
//-----------------------------------------------
///===============================================



///==============================================================
//初始化連結清單(将根目錄下->“/fuxue”下的檔案進行周遊,尾插到連結清單中)-
int read_file(struct list_node *head,char *name)
{	
	//為新節點申請空間
	struct list_node *new = NULL;
	new = (struct list_node *)malloc(sizeof(struct list_node));
	if(new == NULL)
		printf("malloc new error!\n");
	
	strcpy(new->video,name);

	//尋找最後一個節點
 	struct list_node *p = NULL;
	for(p=head;p->next!=head;p=p->next);//如果跳出循環,則目前的"p"就是連結清單的最後一個節點
	p->next = new;
	new->prev = p;
	new->next = head;
	head->prev = new; 

	return 0;
}


int init_old_video(struct list_node *head)
{
	DIR *dp = opendir("/fuxue");
	if(dp == NULL)
		printf("opendir error!\n");
	
	//chdir("/fuxue");
	struct dirent *ep = NULL;
	
	//将讀取到的.avi檔案尾插到連結清單中
	while(1)
	{
		ep = readdir(dp);
		if(ep == NULL)
			break;
		if(ep->d_name[0] == '.')
			continue;
		if(strstr(ep->d_name,".avi"))
		{
			read_file(head,ep->d_name);
		}
	}
}
//---------------------------------------------------------------------
///====================================================================



int main(int argc,char *argv[])
{		
	//建立一個管道
	FILE* mp;
	mkfifo("/tmp/cmd",0777);

	//初始化頭節點
	struct list_node *head = init_list_node();
	
	//初始化連結清單視訊連結清單
	init_old_video(head);
	
	open_lcd();   //初始化lcd屏
	open_ts();    //初始化觸摸屏
	Init_Font(); //初始化漢字庫
	
	video_option(head);//視訊選擇
	
	return 0;
}
           

繼續閱讀