制作一個基于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手冊都可以
所用到的視訊播放中使用到的“暫停/播放”按鈕的切換,用刷圖檔來解決,圖檔為
#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;
}