制作一个基于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;
}