核心功能
1. 界面设计
项目首先定义了一个clearscreen
函数,用于清空屏幕,为用户界面的更新提供了便利。yemian
函数负责显示主菜单界面,提供了包括查看播放列表、播放控制、播放模式选择等在内的9个选项。
2. 文件格式支持
is_supported_file
函数用于检测文件扩展名,确保只有支持的视频和音频文件被播放器处理。目前支持的格式包括.mp4
、.avi
等。
3. 文件列表展示
print_file_list
函数遍历指定目录,列出所有支持的视频文件,并由allnum
变量记录文件总数。
4. 播放控制
open_file
函数根据用户选择打开相应的视频文件,并使用mplayer
命令行工具进行播放。通过execvp
系统调用,我们可以启动mplayer
进程并传递必要的参数。
5. 命令发送
send_command_to_mplayer
函数通过命名管道与mplayer
进程通信,发送播放控制命令,如停止、暂停、快进等。
6. 播放模式
control_mplayer
函数实现了一个循环,根据用户输入的选项,执行不同的播放控制逻辑。它还处理了播放模式的切换,包括列表循环、单曲循环和随机播放。
7. 多进程管理
项目使用了fork
系统调用创建子进程来运行mplayer
,并通过管道与主进程通信,实现了进程间的同步和数据传递。
项目代码:
#include <stdio.h>
#include<time.h>
#include<string.h>
#include<stdlib.h>
#include <unistd.h>
#include <termios.h>
#include<dirent.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
void clearscreen() {
printf("\033[2J");
}
void yemian()
{
clearscreen();
printf(" +-----------------------------------+\n");
printf(" | 视频播放器 |\n");
printf(" +-----------------------------------+\n");
printf(" | 1. 查看播放列表 |\n");
printf(" | 2. 继续/暂停 |\n");
printf(" | 3. 停止 |\n");
printf(" | 4. 上一个 |\n");
printf(" | 5. 下一个 |\n");
printf(" | 6. 快进 |\n");
printf(" | 7. 定位 |\n");
printf(" | 8. 播放方式 |\n");
printf(" | 9. 退出 |\n");
printf(" +-----------------------------------+\n");
}
int is_supported_file(const char *filename) {
const char *extensions[] = {".mp4", ".avi", ".flv", ".wma", ".rmvb", ".mp3", ".rm",NULL}; // 支持的文件扩展名
const char **ext = extensions;
while (*ext != NULL) {
if (strstr(filename, *ext) != NULL) {
return 1;
}
ext++;
}
return 0;
}
int allnum;
int print_file_list(DIR *dir) {
struct dirent *entry;
int i=1;
while ((entry = readdir(dir)) != NULL) {
if (is_supported_file(entry->d_name)) {
printf(" |%d %s \n",i++, entry->d_name);
}
}
return i-1;
}
void print_file_list2() {
int i=1;
printf(" |%d list_cycle \n",i++);
printf(" |%d single_cycle \n",i++);
printf(" |%d random_cycle \n",i++);
}
int open_file(DIR *dir, const char *base_path, int n) {
struct dirent *entry;
int count = 0;
rewinddir(dir);
while ((entry = readdir(dir)) != NULL) {
if (is_supported_file(entry->d_name)) {
count++;
if (count == n) {
char full_path[1024];
snprintf(full_path, sizeof(full_path), "%s%s%s", base_path,"/", entry->d_name);
char *argv[] = {"mplayer", "-slave", "-quiet", "-input", "file=/tmp/fifofile", full_path, NULL};
if(execvp("mplayer", argv) == -1) {
perror("execvp failed");
}
break;
}
}
}
return count;
}
int page2(const char *dirname) {
clearscreen();
printf(" +-----------------------------------+\n");
printf(" | 视频文件播放列表 |\n");
printf(" +-----------------------------------+\n");
DIR *dir = opendir(dirname);
if (dir == NULL) {
perror("打开目录失败");
}
printf(" |以下是可播放的视频文件列表: |\n");
int t= print_file_list(dir);
closedir(dir);
printf(" +-----------------------------------+\n");
return t;
}
int page3() {
clearscreen();
printf(" +-----------------------------------+\n");
printf(" | 请选择你的播放模式 |\n");
printf(" +-----------------------------------+\n");
print_file_list2();
printf(" | |\n");
printf(" +-----------------------------------+\n");
}
int position;
char temp[1000];
void func1(const char*dirname,int n) {
DIR *dir = opendir(dirname);
if (dir == NULL) {
perror("打开目录失败");
}
open_file(dir,dirname,n);
closedir(dir);
}
#define FIFO_NAME "/tmp/fifofile"
void send_command_to_mplayer(const char *command) {
int fd_fifo = open(FIFO_NAME, O_WRONLY);
if (fd_fifo < 0) {
perror("open FIFO for writing");
return;
}
write(fd_fifo, command, strlen(command));
close(fd_fifo);
}
int mode=1;
void control_mplayer() {
int command;
while (1) {
yemian(); // 显示菜单
printf("请输入操作选项:");
scanf("%d", &command);
switch (command) {
case 1:
send_command_to_mplayer("stop\n");
system("./a.out");
break;
case 2:
send_command_to_mplayer("pause\n");
break;
case 3:
send_command_to_mplayer("stop\n");
break;
case 4:
send_command_to_mplayer("stop\n");
sleep(1); // 这里只是示例,具体时间需要根据您的程序调整
if (mkfifo(FIFO_NAME, 0666) == -1 && errno != EEXIST) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t mplayer_pid = fork();
if (mplayer_pid == -1) {;
perror("fork");
exit(EXIT_FAILURE);
}
if (mplayer_pid == 0) {
close(pipefd[0]);
write(pipefd[1], &(mode), sizeof(mode));
if(position>0&&position<(allnum+1))
{
if(mode==1&&position!=1)
{
position--;
}
if(mode==2)
{
}
if(mode==3)
{
srand((unsigned int)time(NULL));
position=1 + (rand() % 3);
}
}
write(pipefd[1], &(position), sizeof(position));
close(pipefd[1]);
sleep(1);
func1(temp,position);
}
close(pipefd[1]);
read(pipefd[0], &mode, sizeof(mode));
read(pipefd[0], &position, sizeof(position));
close(pipefd[0]);
sleep(2); // 等待时间可能需要调整
control_mplayer();
unlink(FIFO_NAME);
waitpid(mplayer_pid, NULL, 0);
break;
case 5:
send_command_to_mplayer("stop\n");
sleep(1); // 这里只是示例,具体时间需要根据您的程序调整
if (mkfifo(FIFO_NAME, 0666) == -1 && errno != EEXIST) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
int pipefd1[2];
if (pipe(pipefd1) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t mplayer_pid1 = fork();
if (mplayer_pid1 == -1) {;
perror("fork");
exit(EXIT_FAILURE);
}
if (mplayer_pid1 == 0) {
close(pipefd1[0]);
write(pipefd1[1], &(mode), sizeof(mode));
if(position>0&&position<allnum+1)
{
if(mode==1&&position!=allnum)
{
position++;
}
if(mode==2)
{
}
if(mode==3)
{
srand((unsigned int)time(NULL));
position=1 +(rand()%3);
}
}
write(pipefd1[1], &(position), sizeof(position));
close(pipefd1[1]);
sleep(1);
func1(temp,position);
}
close(pipefd1[1]);
read(pipefd1[0], &mode, sizeof(mode));
read(pipefd1[0], &position, sizeof(position));
close(pipefd1[0]);
sleep(2); // 等待时间可能需要调整
control_mplayer();
unlink(FIFO_NAME);
waitpid(mplayer_pid1, NULL, 0);
break;
case 6:
printf("请你输入你需要播放的倍速");
float speed_rate;
scanf("%f",&speed_rate);
char speed_command[50];
snprintf(speed_command, sizeof(speed_command), "speed_set %f\n", speed_rate);
send_command_to_mplayer(speed_command);
break;
case 7:
printf("请你输入你需要定到的位置");
int num2;
scanf("%d",&num2);
char seek_command[50];
snprintf(seek_command, sizeof(seek_command), "seek %d 1\n",num2);
send_command_to_mplayer(seek_command);
break;
case 8:
page3();
printf("请输入您想要的播放模式(1-列表循环,2-单曲循环,3-随机播放):");
scanf("%d",&mode);
sleep(2);
break;
case 9:
// 发送退出命令给 MPlayer,然后退出控制循环
send_command_to_mplayer("quit\n");
break;
default:
printf("未知的命令,请重新输入。\n");
break;
}
}
}
int main() {
if (mkfifo(FIFO_NAME, 0666) == -1 && errno != EEXIST) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t mplayer_pid = fork();
if (mplayer_pid == -1) {;
perror("fork");
exit(EXIT_FAILURE);
}
if (mplayer_pid == 0) {
close(pipefd[0]);
char dirname[1024];
printf("请输入目录路径: ");
fgets(dirname, sizeof(dirname), stdin);
size_t len = strlen(dirname);
if(len > 0 && dirname[len - 1] == '\n') {
dirname[len - 1] = '\0';
}
strcpy(temp,dirname);
allnum=page2(dirname);
printf("please input the Video_no");
int t;
scanf("%d",&t);
position=t;
write(pipefd[1], &position, sizeof(position));
write(pipefd[1], temp, sizeof(temp));
write(pipefd[1], &allnum, sizeof(allnum));
sleep(5);
func1(dirname,t);
close(pipefd[1]);
}
close(pipefd[1]);
read(pipefd[0], &position, sizeof(position));
read(pipefd[0], temp, sizeof(temp));
read(pipefd[0], &allnum, sizeof(allnum));
close(pipefd[0]);
sleep(8); // 等待时间可能需要调整
control_mplayer();
unlink(FIFO_NAME);
waitpid(mplayer_pid, NULL, 0);
return 0;
}
项目流程图
项目挑战
在开发过程中,我遇到了不少挑战,比如如何优雅地处理用户输入、如何确保进程间通信的可靠性等。通过查阅资料和不断调试,我逐步克服了这些难题。
项目收获
通过这个项目,我不仅提升了自己的编程能力,也对操作系统的原理有了更深入的理解。此外,我还学会了如何设计用户友好的命令行界面,以及如何处理多进程编程中的同步问题。
结语
这个视频播放器项目是我学习旅程中的一个里程碑。我相信,随着技术的不断进步,我将能够开发出更加复杂和功能丰富的应用程序。感谢大家的阅读,希望我的分享能给大家带来一些启发。