核心功能
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;
}
项目流程图
项目挑战
在开发过程中,我遇到了不少挑战,比如如何优雅地处理用户输入、如何确保进程间通信的可靠性等。通过查阅资料和不断调试,我逐步克服了这些难题。
项目收获
通过这个项目,我不仅提升了自己的编程能力,也对操作系统的原理有了更深入的理解。此外,我还学会了如何设计用户友好的命令行界面,以及如何处理多进程编程中的同步问题。
结语
这个视频播放器项目是我学习旅程中的一个里程碑。我相信,随着技术的不断进步,我将能够开发出更加复杂和功能丰富的应用程序。感谢大家的阅读,希望我的分享能给大家带来一些启发。