Mplayer TUI增强设计:Linux命令行播放器的效率革命
1. 项目背景
Mplayer作为经典开源媒体播放器,存在以下交互缺陷:
-
默认命令行界面需记忆复杂指令(如:
mplayer -fs -playlist file.list
) -
缺乏可视化播放列表管理
-
状态信息展示不直观(需依赖终端输出)
调研数据显示,78%的新用户在首次使用时需要超过15分钟学习基础操作。本设计旨在通过重构交互界面降低学习成本,提升操作效率(数据来自DeepSeek统计)。
2. 项目功能
2.1 核心功能矩阵
功能类别 | 具体功能 | 实现方式 |
---|---|---|
播放控制 | 播放/暂停/停止 | 进程信号控制 |
导航控制 | 上一首/下一首/定位 | 播放列表索引管理 |
播放模式 | 顺序/随机/单曲循环 | 状态机控制 |
系统控制 | 倍速播放/退出 | 进程信号控制 |
2.2 创新交互特性
-
分层式菜单导航
-
语音控制选择
3. 项目设计框架及分析
3.1 系统架构
3.2 关键技术实现
3.2.1音视频文件获取
//文件格式判断
static int is_music_file(char *pfilename)
{
char *ptmp = NULL;
ptmp = pfilename + strlen(pfilename);//指针移动至末尾
while (ptmp > pfilename && *ptmp != '.')//向指针前移至‘.’字符
{
ptmp--;
}
if(0 == strcmp(ptmp, ".mp3") || 0 == strcmp(ptmp, ".mp4") || 0 == strcmp(ptmp, ".flv")
|| 0 == strcmp(ptmp, ".rmvb") || 0 == strcmp(ptmp, ".avi"))//文件后缀判断
{
return 1;
}
return 0;
}
//打开文件并将路径传入链表
int load_music_file(char *pdirname)
{
DIR *dp = NULL;
struct dirent *pp = NULL;
music_node_t *pnode = NULL;
int cnt = 0;
INIT_LIST_HEAD(&musiclist);
dp = opendir(pdirname);//打开音视频文件夹
if(NULL == dp)
{
return -1;
}
while(1)
{
pp = readdir(dp);//读取文件
if(NULL == pp)//结束判断
{
break;
}
if(*pp->d_name == '.')//隐藏文件判断
{
continue;
}
if(is_music_file(pp->d_name))//音视频文件判断
{
pnode = malloc(sizeof(music_node_t));
if(NULL == pnode)
{
return -1;
}
sprintf(pnode->musicname, "%s/%s", pdirname, pp->d_name);//获取文件路径
list_add_tail(&pnode->node, &musiclist);//文件路径写入链表
cnt++;
}
}
closedir(dp);
return cnt;
}
3.2.2持续播放操作
signal(SIGCHLD, mode_control);//捕捉子进程结束信号,执行“mode_control”函数
//循环进程
void mode_control(int signo)
{
int random_num = 0;
int i = 0;
int status = 0;
wait(&status);//捕获信号
if(WIFEXITED(status))//判断子进程是否正常结束
{
if(cnt == 0)//当前用户选择的播放模式(0为单曲循环)
{
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
else if(cnt == 1)//当前用户选择的播放模式(1为循环播放)
{
pcurmusic_node = pcurmusic_node->next;
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->next;
}
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
else if(cnt == 2)//当前用户选择的播放模式(2为随机播放)
{
srand(time(NULL));
random_num = rand() % 10;
for(i = 0; i < random_num; i++)
{
pcurmusic_node = pcurmusic_node->next;
}
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->next;
}
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
}
//若不是正常结束则退出,不执行命令
return;
}
4. 项目各模块功能及实现
4.1 语音按键同步操作
需求:可以通过键盘和语音模块同步控制Mplayer的控制
解决方式:使用双线程对键盘控制和语音控制进行分离同步执行。
#include "public.h"
#include "mplayer.h"
#include "serial.h"
void *thread1(void *arg)
{
int n = 0;
load_music_file("/home/linux/Music");
signal(SIGCHLD, mode_control);
while(1)
{
menu_show();
printf("请选择\n");
scanf("%d", &n);
getchar();
switch(n)
{
case 1:show_music_list();break;
case 2:pause();break;
case 3:stop();break;
case 4:last_music();break;
case 5:next_music();break;
case 6:speed_up();break;
case 7:location();break;
case 8:pattern_cut();break;
case 9:exit(0);return NULL;
}
}
return NULL;
}
void *thread2(void *arg)
{
int fd = 0;
char tmpbuff[64] = {0};
fd = opendev("/dev/ttyUSB0");
set_port(fd, 9600, 8, 1, 'N');
while(1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("%s\n", tmpbuff);
if(strncmp(tmpbuff, "user_interrupt", 14) == 0)
{
}
else if(strncmp(tmpbuff, "play_start", 10) == 0)//开始播放
{
pause();
}
else if(strncmp(tmpbuff, "play_pause", 10) == 0)//暂停播放
{
pause();
}
else if(strncmp(tmpbuff, "play_stop", 9) == 0)//停止播放
{
stop();
}
else if(strncmp(tmpbuff, "next_media", 10) == 0)//播放下一首
{
next_music();
}
else if(strncmp(tmpbuff, "previous_media", 14) == 0)//播放上一首
{
last_music();
}
else if(strncmp(tmpbuff, "add_speed", 9) == 0)//倍速播放
{
add_speed();
}
else if(strncmp(tmpbuff, "sub_speed", 9) == 0)//减速播放
{
sub_speed();
}
}
return NULL;
}
int main(int argc, const char **argv)
{
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
4.2音视频链表创建
需求:能够完成对音视频文件的加载筛选
解决方式:打开音视频存放的文件夹将符合要求的音视频文件的文件路径存入双向链表中。
//文件格式判断
static int is_music_file(char *pfilename)
{
char *ptmp = NULL;
ptmp = pfilename + strlen(pfilename);//指针移动至末尾
while (ptmp > pfilename && *ptmp != '.')//向指针前移至‘.’字符
{
ptmp--;
}
if(0 == strcmp(ptmp, ".mp3") || 0 == strcmp(ptmp, ".mp4") || 0 == strcmp(ptmp, ".flv")
|| 0 == strcmp(ptmp, ".rmvb") || 0 == strcmp(ptmp, ".avi"))//文件后缀判断
{
return 1;
}
return 0;
}
//打开文件并将路径传入链表
int load_music_file(char *pdirname)
{
DIR *dp = NULL;
struct dirent *pp = NULL;
music_node_t *pnode = NULL;
int cnt = 0;
INIT_LIST_HEAD(&musiclist);
dp = opendir(pdirname);//打开音视频文件夹
if(NULL == dp)
{
return -1;
}
while(1)
{
pp = readdir(dp);//读取文件
if(NULL == pp)//结束判断
{
break;
}
if(*pp->d_name == '.')//隐藏文件判断
{
continue;
}
if(is_music_file(pp->d_name))//音视频文件判断
{
pnode = malloc(sizeof(music_node_t));
if(NULL == pnode)
{
return -1;
}
sprintf(pnode->musicname, "%s/%s", pdirname, pp->d_name);//获取文件路径
list_add_tail(&pnode->node, &musiclist);//文件路径写入链表
cnt++;
}
}
closedir(dp);
return cnt;
}
4.3播放列表选择
需求:选择打开播放列表,显示可播放的音视频文件并可选择播放哪一个。
解决方式:循环打印链表中的音视频路径并标号,等待终端输入标号,创建新的进程将对应的音视频路径在子进程中通过Mplayer打开。
//创建子进程,执行mplayer
int player_media(char *pmusicpath)
{
pid = fork();
if(pid == -1)
{
return -1;
}
if(pid == 0)
{
mkfifo("/tmp/myfifo", 0777);
close(1);
close(2);
execlp("mplayer", "mplayer", "-slave", "-input", "file=/tmp/myfifo", pmusicpath, NULL);
}
curstatus = MPLAYER_STATUS_PLAY;
curspeed = MPLAYER_SPEED_ONE;
return 0;
}
//目录选择
int show_music_list(void)
{
music_node_t *ptmpnode = NULL;
int cnt = 1;
int n = 0;
int i = 0;
printf("================================\n");
printf("0.返回上一级\n");
list_for_each_entry(ptmpnode, &musiclist, node)
{
printf("%d.%s\n", cnt, ptmpnode->musicname);
cnt++;
}
while(1)
{
printf("请选择:\n");
scanf("%d", &n);
getchar();
if(n == 0)
{
break;
}
else if(n < 0 || n > cnt)
{
continue;
}
else
{
pcurmusic_node = musiclist.next;
for(i = 0; i < n-1; i++)
{
pcurmusic_node = pcurmusic_node->next;
}
if(curstatus != MPLAYER_STATUS_FREE)
{
kill(pid, SIGKILL);
}
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
}
return 0;
}
4.4开始/暂停
需求:对已经打开的Mplayer进行暂停和播放操作。
解决方式:通过判断MPLAYER_STATUS_EN结构体的状态(MPLAYER_STATUS_PLAY或MPLAYER_STATUS_PAUSE)对已经创建的管道文件中输入“pause\n”字符串进行暂停开始操作。
//暂停
int pause(void)
{
int fd = 0;
if(curstatus == MPLAYER_STATUS_PLAY)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "pause\n", 6);
close(fd);
curstatus = MPLAYER_STATUS_PAUSE;
}
else if(curstatus == MPLAYER_STATUS_PAUSE)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "pause\n", 6);
close(fd);
curstatus = MPLAYER_STATUS_PLAY;
}
return 0;
}
4.5停止播放
需求:对已经打开的Mplayer进行关闭操作。
解决方式:对已经创建的子进程进行KILL操作以结束Mplayer的播放。
//停止
int stop(void)
{
int fd = 0;
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
kill(pid, SIGKILL);
close(fd);
curstatus = MPLAYER_STATUS_FREE;
curspeed = MPLAYER_SPEED_ONE;
return 0;
}
4.6上一首/下一首
需求:对已经打开的Mplayer进行上一首/下一首操作。
解决方式:对文件链表指针所在的位置进行向后偏移(向前偏移),寻找到对应音视频的下一首(上一首),后进行再次打开执行Mplayer。
//相关文件链表指针
struct list_head *pcurmusic_node;
//创建子进程,执行mplayer
int player_media(char *pmusicpath)
{
pid = fork();
if(pid == -1)
{
return -1;
}
if(pid == 0)
{
mkfifo("/tmp/myfifo", 0777);
close(1);
close(2);
execlp("mplayer", "mplayer", "-slave", "-input", "file=/tmp/myfifo", pmusicpath, NULL);
}
curstatus = MPLAYER_STATUS_PLAY;
curspeed = MPLAYER_SPEED_ONE;
return 0;
}
//停止
int stop(void)
{
int fd = 0;
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
kill(pid, SIGKILL);
close(fd);
curstatus = MPLAYER_STATUS_FREE;
curspeed = MPLAYER_SPEED_ONE;
return 0;
}
//上一首
int last_music(void)
{
pcurmusic_node = pcurmusic_node->prev;
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->prev;
}
stop();
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
//下一首
int next_music(void)
{
pcurmusic_node = pcurmusic_node->next;
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->next;
}
stop();
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
4.7倍速播放
需求:对已经打开的Mplayer进行1/2/4倍速度播放。
解决方式:通过判断MPLAYER_SPEED_EN结构体的状态(MPLAYER_SPEED_ONE或
MPLAYER_SPEED_TWO或MPLAYER_SPEED_FOUR)对已经创建的管道文件中输入“speed_set <倍速>\n”(<要加倍的程度(1.0/2.0/4.0)>)字符串进行倍速操作。
//变速播放
int speed_up(void)
{
int fd = 0;
if(curspeed == MPLAYER_SPEED_ONE)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 2.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_TWO;
}
else if(curspeed == MPLAYER_SPEED_TWO)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 4.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_FOUR;
}
else if(curspeed == MPLAYER_SPEED_FOUR)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 1.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_ONE;
}
return 0;
}
4.8定位
需求:对已经打开的Mplayer进行定位操作。
解决方式:获取终端需要定位的位置后进行字符串拼接,将拼接好的字符串写入管道文件中进行定位操作。
//定位
int location(void)
{
int fd = 0;
int n = 0;
char command[16] = {0};
printf("请输入要定位的位置(0~100)\n");
scanf("%d", &n);
getchar();
if(n < 0)
{
n = 0;
}
else if(n > 100)
{
n = 100;
}
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
snprintf(command, sizeof(command), "seek %d 1\n", n);
write(fd, command, strlen(command));
close(fd);
return 0;
}
4.9播放模式切换(单曲/循环/随机)
需求:对Mplayer的播放模式进行切换。
解决方式:通过判断全局变量MPLAYER_MODE_EN结构体的状态(MPLAYER_MODE_SINGLE或MPLAYER_MODE_ORDER或 MPLAYER_MODE_RANDOM)对已经当前播放状态进行切换
//播放模式
int pattern_cut(void)
{
if(curmode == MPLAYER_MODE_SINGLE)
{
curmode = MPLAYER_MODE_ORDER;
printf("顺序播放\n");
cnt = 1;
}
else if(curmode == MPLAYER_MODE_ORDER)
{
curmode = MPLAYER_MODE_RANDOM;
printf("随机播放\n");
cnt = 2;
}
else if(curmode == MPLAYER_MODE_RANDOM)
{
curmode = MPLAYER_MODE_SINGLE;
printf("单曲播放\n");
cnt = 0;
}
return 0;
}
5. 关键数据类型
5.1 Mplayer状态
typedef struct music_node
{
struct list_head node;
char musicname[64];
}music_node_t;
/* mplayer 播放状态 */
typedef enum mplayer_status
{
MPLAYER_STATUS_FREE,
MPLAYER_STATUS_PLAY,
MPLAYER_STATUS_PAUSE,
}MPLAYER_STATUS_EN;
/* mplayer 播放速度 */
typedef enum mplayer_speed
{
MPLAYER_SPEED_ONE,
MPLAYER_SPEED_TWO,
MPLAYER_SPEED_FOUR,
}MPLAYER_SPEED_EN;
/* mplayer 播放模式 */
typedef enum mplayer_mode
{
MPLAYER_MODE_SINGLE,
MPLAYER_MODE_ORDER,
MPLAYER_MODE_RANDOM,
}MPLAYER_MODE_EN;
6. 项目中的困难及解决方案
困难1:终端可同时打开多个Mplayer进行视频播放
分析:可能是在执行播放前未对播放状态进行判断,导致可以同时创建多个子进程。
解决方法:对执行子进程创建前加入状态判断,若不为空闲状态,则KILL上一个子进程后执行新的子进程
结果:
//目录选择
int show_music_list(void)
{
music_node_t *ptmpnode = NULL;
int cnt = 1;
int n = 0;
int i = 0;
printf("================================\n");
printf("0.返回上一级\n");
list_for_each_entry(ptmpnode, &musiclist, node)
{
printf("%d.%s\n", cnt, ptmpnode->musicname);
cnt++;
}
while(1)
{
printf("请选择:\n");
scanf("%d", &n);
getchar();
if(n == 0)
{
break;
}
else if(n < 0 || n > cnt)
{
continue;
}
else
{
pcurmusic_node = musiclist.next;
for(i = 0; i < n-1; i++)
{
pcurmusic_node = pcurmusic_node->next;
}
//修改后
*----------------------------------------------------------------*
if(curstatus != MPLAYER_STATUS_FREE)
{
kill(pid, SIGKILL);
}
*----------------------------------------------------------------*
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
}
return 0;
}
困难2:当前视频播放完成后无法自动播放下一个视频,
分析:经过网络搜索和AI询问得知,若要实现自动播放,可以使用轮询 waitpid
函数(父进程可以定期调用 waitpid
函数来检查子进程的状态),还可以使用signal
函数(当进程接收到指定信号时,会执行相应的信号处理函数)
解决方法:在父进程中加入“signal(SIGCHLD, mode_control)”函数,监测子进程是否结束,若结束执行“mode_control”函数,在函数体内判断是正常结束还是指令杀死,若正常结束则根据播放状态执行下一个视频的播放。
结果:
//循环进程
void mode_control(int signo)
{
int random_num = 0;
int i = 0;
int status = 0;
wait(&status);
if(WIFEXITED(status))
{
if(cnt == 0)//单曲循环
{
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
else if(cnt == 1)//循环播放
{
pcurmusic_node = pcurmusic_node->next;
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->next;
}
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
else if(cnt == 2)//随机播放
{
srand(time(NULL));
random_num = rand() % 10;
for(i = 0; i < random_num; i++)
{
pcurmusic_node = pcurmusic_node->next;
}
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->next;
}
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
}
return;
}
void *thread1(void *arg)
{
int n = 0;
load_music_file("/home/linux/Music");
signal(SIGCHLD, mode_control);
while(1)
{
menu_show();
printf("请选择\n");
scanf("%d", &n);
getchar();
switch(n)
{
case 1:show_music_list();break;
case 2:pause();break;
case 3:stop();break;
case 4:last_music();break;
case 5:next_music();break;
case 6:speed_up();break;
case 7:location();break;
case 8:pattern_cut();break;
case 9:exit(0);return NULL;
}
}
return NULL;
}
困难3:如何同时可以执行语音控制和终端控制
分析:语音控制和终端控制需要两个部分同时执行,则需要使用多进程或者多线程,根据执行效率决定应当使用多线程(内存占用少,通信效率高)
解决方法:在主函数中加入“pthread_create”函数,同时创建2个线程用于语音控制和终端控制。
结果:
#include "public.h"
#include "mplayer.h"
#include "serial.h"
void *thread1(void *arg)
{
int n = 0;
load_music_file("/home/linux/Music");
signal(SIGCHLD, mode_control);
while(1)
{
menu_show();
printf("请选择\n");
scanf("%d", &n);
getchar();
switch(n)
{
case 1:show_music_list();break;
case 2:pause();break;
case 3:stop();break;
case 4:last_music();break;
case 5:next_music();break;
case 6:speed_up();break;
case 7:location();break;
case 8:pattern_cut();break;
case 9:exit(0);return NULL;
}
}
return NULL;
}
void *thread2(void *arg)
{
int fd = 0;
char tmpbuff[64] = {0};
fd = opendev("/dev/ttyUSB0");
set_port(fd, 9600, 8, 1, 'N');
while(1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("%s\n", tmpbuff);
if(strncmp(tmpbuff, "user_interrupt", 14) == 0)
{
}
else if(strncmp(tmpbuff, "play_start", 10) == 0)//开始播放
{
pause();
}
else if(strncmp(tmpbuff, "play_pause", 10) == 0)//暂停播放
{
pause();
}
else if(strncmp(tmpbuff, "play_stop", 9) == 0)//停止播放
{
stop();
}
else if(strncmp(tmpbuff, "next_media", 10) == 0)//播放下一首
{
next_music();
}
else if(strncmp(tmpbuff, "previous_media", 14) == 0)//播放上一首
{
last_music();
}
else if(strncmp(tmpbuff, "add_speed", 9) == 0)//倍速播放
{
add_speed();
}
else if(strncmp(tmpbuff, "sub_speed", 9) == 0)//减速播放
{
sub_speed();
}
}
return NULL;
}
int main(int argc, const char **argv)
{
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
7. 整体预览
主函数部分------------------↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
#include "public.h"
#include "mplayer.h"
#include "serial.h"
void *thread1(void *arg)
{
int n = 0;
load_music_file("/home/linux/Music");
signal(SIGCHLD, mode_control);
while(1)
{
menu_show();
printf("请选择\n");
scanf("%d", &n);
getchar();
switch(n)
{
case 1:show_music_list();break;
case 2:pause();break;
case 3:stop();break;
case 4:last_music();break;
case 5:next_music();break;
case 6:speed_up();break;
case 7:location();break;
case 8:pattern_cut();break;
case 9:exit(0);return NULL;
}
}
return NULL;
}
void *thread2(void *arg)
{
int fd = 0;
char tmpbuff[64] = {0};
fd = opendev("/dev/ttyUSB0");
set_port(fd, 9600, 8, 1, 'N');
while(1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("%s\n", tmpbuff);
if(strncmp(tmpbuff, "user_interrupt", 14) == 0)
{
}
else if(strncmp(tmpbuff, "play_start", 10) == 0)//开始播放
{
pause();
}
else if(strncmp(tmpbuff, "play_pause", 10) == 0)//暂停播放
{
pause();
}
else if(strncmp(tmpbuff, "play_stop", 9) == 0)//停止播放
{
stop();
}
else if(strncmp(tmpbuff, "next_media", 10) == 0)//播放下一首
{
next_music();
}
else if(strncmp(tmpbuff, "previous_media", 14) == 0)//播放上一首
{
last_music();
}
else if(strncmp(tmpbuff, "add_speed", 9) == 0)//倍速播放
{
add_speed();
}
else if(strncmp(tmpbuff, "sub_speed", 9) == 0)//减速播放
{
sub_speed();
}
}
return NULL;
}
int main(int argc, const char **argv)
{
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
副函数1部分------------------↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
#include "public.h"
#include "mplayer.h"
struct list_head musiclist;
struct list_head *pcurmusic_node;
int cnt = 0;
pid_t pid;
MPLAYER_STATUS_EN curstatus = MPLAYER_STATUS_FREE;//状态
MPLAYER_SPEED_EN curspeed = MPLAYER_SPEED_ONE;//速度
MPLAYER_MODE_EN curmode = MPLAYER_MODE_SINGLE;//模式
int menu_show(void)
{
printf("================================\n");
printf("1.查看播放列表\n");
printf("2.开始/暂停\n");
printf("3.停止\n");
printf("4.播放上一首\n");
printf("5.播放下一首\n");
printf("6.倍速播放(1/2/4)\n");
printf("7.定位\n");
printf("8.播放模式(循环播放、单曲播放、随机播放)\n");
printf("9.退出\n");
printf("================================\n");
return 0;
}
//文件格式判断
static int is_music_file(char *pfilename)
{
char *ptmp = NULL;
ptmp = pfilename + strlen(pfilename);
while (ptmp > pfilename && *ptmp != '.')
{
ptmp--;
}
if(0 == strcmp(ptmp, ".mp3") || 0 == strcmp(ptmp, ".mp4") || 0 == strcmp(ptmp, ".flv")
|| 0 == strcmp(ptmp, ".rmvb") || 0 == strcmp(ptmp, ".avi"))
{
return 1;
}
return 0;
}
//打开文件并将路径传入链表
int load_music_file(char *pdirname)
{
DIR *dp = NULL;
struct dirent *pp = NULL;
music_node_t *pnode = NULL;
int cnt = 0;
INIT_LIST_HEAD(&musiclist);
dp = opendir(pdirname);
if(NULL == dp)
{
return -1;
}
while(1)
{
pp = readdir(dp);
if(NULL == pp)
{
break;
}
if(*pp->d_name == '.')
{
continue;
}
if(is_music_file(pp->d_name))
{
pnode = malloc(sizeof(music_node_t));
if(NULL == pnode)
{
return -1;
}
sprintf(pnode->musicname, "%s/%s", pdirname, pp->d_name);
list_add_tail(&pnode->node, &musiclist);
cnt++;
}
}
closedir(dp);
return cnt;
}
//创建子进程,执行mplayer
int player_media(char *pmusicpath)
{
pid = fork();
if(pid == -1)
{
return -1;
}
if(pid == 0)
{
mkfifo("/tmp/myfifo", 0777);
close(1);
close(2);
execlp("mplayer", "mplayer", "-slave", "-input", "file=/tmp/myfifo", pmusicpath, NULL);
}
curstatus = MPLAYER_STATUS_PLAY;
curspeed = MPLAYER_SPEED_ONE;
return 0;
}
//目录选择
int show_music_list(void)
{
music_node_t *ptmpnode = NULL;
int cnt = 1;
int n = 0;
int i = 0;
printf("================================\n");
printf("0.返回上一级\n");
list_for_each_entry(ptmpnode, &musiclist, node)
{
printf("%d.%s\n", cnt, ptmpnode->musicname);
cnt++;
}
while(1)
{
printf("请选择:\n");
scanf("%d", &n);
getchar();
if(n == 0)
{
break;
}
else if(n < 0 || n > cnt)
{
continue;
}
else
{
pcurmusic_node = musiclist.next;
for(i = 0; i < n-1; i++)
{
pcurmusic_node = pcurmusic_node->next;
}
if(curstatus != MPLAYER_STATUS_FREE)
{
kill(pid, SIGKILL);
}
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
}
return 0;
}
//暂停
int pause(void)
{
int fd = 0;
if(curstatus == MPLAYER_STATUS_PLAY)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "pause\n", 6);
close(fd);
curstatus = MPLAYER_STATUS_PAUSE;
}
else if(curstatus == MPLAYER_STATUS_PAUSE)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "pause\n", 6);
close(fd);
curstatus = MPLAYER_STATUS_PLAY;
}
return 0;
}
//停止
int stop(void)
{
int fd = 0;
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
kill(pid, SIGKILL);
close(fd);
curstatus = MPLAYER_STATUS_FREE;
curspeed = MPLAYER_SPEED_ONE;
return 0;
}
//上一首
int last_music(void)
{
pcurmusic_node = pcurmusic_node->prev;
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->prev;
}
stop();
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
//下一首
int next_music(void)
{
pcurmusic_node = pcurmusic_node->next;
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->next;
}
stop();
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
//变速播放
int speed_up(void)
{
int fd = 0;
if(curspeed == MPLAYER_SPEED_ONE)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 2.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_TWO;
}
else if(curspeed == MPLAYER_SPEED_TWO)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 4.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_FOUR;
}
else if(curspeed == MPLAYER_SPEED_FOUR)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 1.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_ONE;
}
return 0;
}
//倍速播放
int add_speed(void)
{
int fd = 0;
if(curspeed == MPLAYER_SPEED_ONE)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 2.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_TWO;
}
else if(curspeed == MPLAYER_SPEED_TWO)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 4.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_FOUR;
}
else if(curspeed == MPLAYER_SPEED_FOUR)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 4.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_FOUR;
}
return 0;
}
//减速播放
int sub_speed(void)
{
int fd = 0;
if(curspeed == MPLAYER_SPEED_ONE)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 1.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_ONE;
}
else if(curspeed == MPLAYER_SPEED_TWO)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 1.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_ONE;
}
else if(curspeed == MPLAYER_SPEED_FOUR)
{
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
write(fd, "speed_set 2.0\n", 14);
close(fd);
curspeed = MPLAYER_SPEED_TWO;
}
return 0;
}
//定位
int location(void)
{
int fd = 0;
int n = 0;
char command[16] = {0};
printf("请输入要定位的位置(0~100)\n");
scanf("%d", &n);
getchar();
if(n < 0)
{
n = 0;
}
else if(n > 100)
{
n = 100;
}
fd = open("/tmp/myfifo", O_RDWR);
if(fd == -1)
{
return -1;
}
snprintf(command, sizeof(command), "seek %d 1\n", n);
write(fd, command, strlen(command));
close(fd);
return 0;
}
//播放模式
int pattern_cut(void)
{
if(curmode == MPLAYER_MODE_SINGLE)
{
curmode = MPLAYER_MODE_ORDER;
printf("顺序播放\n");
cnt = 1;
}
else if(curmode == MPLAYER_MODE_ORDER)
{
curmode = MPLAYER_MODE_RANDOM;
printf("随机播放\n");
cnt = 2;
}
else if(curmode == MPLAYER_MODE_RANDOM)
{
curmode = MPLAYER_MODE_SINGLE;
printf("单曲播放\n");
cnt = 0;
}
return 0;
}
//循环进程
void mode_control(int signo)
{
int random_num = 0;
int i = 0;
int status = 0;
wait(&status);
if(WIFEXITED(status))
{
if(cnt == 0)
{
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
else if(cnt == 1)
{
pcurmusic_node = pcurmusic_node->next;
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->next;
}
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
else if(cnt == 2)
{
srand(time(NULL));
random_num = rand() % 10;
for(i = 0; i < random_num; i++)
{
pcurmusic_node = pcurmusic_node->next;
}
if(&musiclist == pcurmusic_node)
{
pcurmusic_node = pcurmusic_node->next;
}
player_media(list_entry(pcurmusic_node, music_node_t, node)->musicname);
}
}
return;
}
副函数2部分------------------↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
#include "serial.h"
/*============================================================
*
*===========================================================*/
int opendev(char *dev)
{
int fd = open(dev, O_RDWR); // O_NOCTTY O_NDELAY
if (-1 == fd)
{
fprintf (stderr, "opendev err: %s\n", strerror(errno));
exit (EXIT_FAILURE);
}
return fd;
}
/*============================================================
*
*===========================================================*/
void set_port(int fd, int speed, int databits, int stopbits, int parity)
{
set_speed (fd, speed);
if (!set_parity(fd, databits, stopbits, parity))
{
fprintf (stderr, "set_parity err: %s\n", strerror(errno));
exit (EXIT_FAILURE);
}
}
/*============================================================
*
*===========================================================*/
int speed_arr[]= {B38400, B19200, B9600, B4800, B2400, B1200, B300,B38400, B19200, B9600, B4800, B2400, B1200, B300,};
int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300,38400, 19200, 9600, 4800, 2400, 1200, 300, };
void set_speed(int fd, int speed)
{
int i;
int status;
struct termios opt; //定义了这样一个结构
tcgetattr(fd, &opt); //用来得到机器原端口的默认设置
for (i = 0; i < sizeof(speed_arr)/sizeof(speed_arr[0]); i++)
{
if (speed == name_arr[i])
{
tcflush(fd, TCIOFLUSH); //刷新输入输出缓冲
cfsetispeed(&opt, speed_arr[i]); //这里分别设置
cfsetospeed(&opt, speed_arr[i]);//这是立刻把rates设置真正写到串口中去
status = tcsetattr(fd, TCSANOW, &opt);
if (status != 0)
perror("tcsetattr fd1"); //设置错误
return ;
}
tcflush(fd,TCIOFLUSH); //同上
}
}
/*============================================================
*设置数据位、校验位和停止位
*===========================================================*/
int set_parity(int fd, int databits, int stopbits, int parity)
{
struct termios options; //定义一个结构
if (tcgetattr(fd, &options) != 0)
{
perror("SetupSerial 1");
return 0;
}
options.c_cflag &= ~CSIZE; //这是设置c_cflag选项不按位数据位掩码
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7; //设置c_cflag选项数据位为7位
break;
case 8:
options.c_cflag |= CS8; //设置c_cflag选项数据位为8位
break;
default:
fprintf(stderr,"unsupported data size\n"); //其他的都不支持
return 0;
}
switch (parity) //设置奇偶校验,c_cflag和c_iflag有效
{
case 'n':
case 'N': //无校验 当然都不选
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking*/
break;
case 'o': //奇校验 其中PARENB校验位有效;PARODD奇校验
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E': //偶校验,奇校验不选就是偶校验了
options.c_cflag |= PARENB;/* Enable parity */
options.c_cflag &= ~PARODD;/* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
default:
fprintf(stderr,"Unsupported parity\n");
return (0);
}
/* 设置停止位*/
switch (stopbits) //这是设置停止位数,影响的标志是c_cflag
{
case 1:
options.c_cflag &= ~CSTOPB; //不指明表示一位停止位
break;
case 2:
options.c_cflag |= CSTOPB; //指明CSTOPB表示两位,只有两种可能
break;
default:
fprintf(stderr, "unsupported stop bits\n");
return (0);
}
/* Set input parity option */
if (parity != 'n') //这是设置输入是否进行校验
options.c_iflag |= INPCK;
// 这个地方是用来设置控制字符和超时参数的,一般默认即可。稍微要注意的是c_cc数组的VSTART 和 VSTOP 元素被设定成DC1 和 DC3,代表ASCII 标准的XON和XOFF字符。所以如果在传输这两个字符的时候就传不过去,这时需要把软件流控制屏蔽
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_cc[VTIME] = 150; // 15 seconds
options.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH); //刷新和立刻写进去
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return 0;
}
//在makefile编译中新加入依赖
return 1;
}
//END FILE
自定义头文件部分----------↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
#ifndef __MPLAYER_H__
#define __MPLAYER_H__
#include "list.h"//双头链表
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <error.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
typedef struct music_node
{
struct list_head node;
char musicname[64];
}music_node_t;
/* mplayer 播放状态 */
typedef enum mplayer_status
{
MPLAYER_STATUS_FREE,
MPLAYER_STATUS_PLAY,
MPLAYER_STATUS_PAUSE,
}MPLAYER_STATUS_EN;
/* mplayer 播放速度 */
typedef enum mplayer_speed
{
MPLAYER_SPEED_ONE,
MPLAYER_SPEED_TWO,
MPLAYER_SPEED_FOUR,
}MPLAYER_SPEED_EN;
/* mplayer 播放模式 */
typedef enum mplayer_mode
{
MPLAYER_MODE_SINGLE,
MPLAYER_MODE_ORDER,
MPLAYER_MODE_RANDOM,
}MPLAYER_MODE_EN;
extern int menu_show(void);
extern int load_music_file(char *pdirname);
extern int show_music_list(void);
extern int pause(void);
extern int stop(void);
extern int last_music(void);
extern int next_music(void);
extern int speed_up(void);
extern int add_speed(void);
extern int sub_speed(void);
extern int location(void);
extern int pattern_cut(void);
extern void mode_control(int signo);
extern struct list_head musiclist;
#endif