本期分享的是使用进程、mplayer应用使用mplayer命令实现一个媒体播放器的功能,本次使用到的主要有进程间通信(pipe),字符串的操作,多函数功能整合等。
功能主流程图如下:
各个分支模块的流程图如下:
这个就是主流程图以及各个分支函数的细节流程图;
头文件如下:
#ifndef __HEAD_H
#define __HEAD_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <dirent.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
/* mplayer 媒体列表 */
typedef struct __media_list__
{
char medialist[100][128]; //播放歌曲曲目
int curmedialist; //目录下歌曲个数
}MediaList_t;
/* mplayer 播放状态 */
typedef enum __mplayer_stat__
{
MPLAYER_STAT_FREE,
MPLAYER_STAT_PLAY,
MPLAYER_STAT_PAUSE,
}MplayerStat_en;
/* mplayer 播放速度 */
typedef enum __mplayer_speed__
{
MPLAYER_SPEED_ONE,
MPLAYER_SPEED_TWO,
MPLAYER_SPEED_FOUR,
}MplayerSpeed_en;
/* mplayer 播放模式 */
typedef enum __mplayer_mode__
{
MPLAYER_MODE_SINGLE,
MPLAYER_MODE_ORDER,
MPLAYER_MODE_RANDOM,
}MplayerMode_en;
/* mplayer 当前工作状态 */
typedef struct __media_stat__
{
MplayerStat_en curstat;
MplayerSpeed_en curspeed;
MplayerMode_en curmode;
int curmedianum;
}MediaStat_t;
extern void handler(int signo);
extern int showTerminal(void);
extern int choiceMode(void);
extern int loadMediaFile(const char *direntname);
extern int listOfMusicFile(void);
extern int Pause(void);
extern int playMedia(char *pMediaPath);
extern int playPause(void);
extern int pthreadOfWork();
extern int Stop(void);
extern int preMusic(void);
extern int nextMusic(void);
extern int addSpeed(void);
extern int seekPosition(void);
extern int changeModeOfWork(void);
extern int openLogFile(void);
extern int writeLogFile(const char *errbuf);
extern int closeLogFile(void);
#endif
日志文件的主要实现过程:
#include "head.h"
FILE *fp = NULL;
int openLogFile(void)
{
fp = fopen("./log.txt", "a");
if (NULL == fp)
{
perror("fail to fopen");
return -1;
}
return 0;
}
int writeLogFile(const char *errbuf)
{
char tmpbuf[1024] = {0};
time_t t;
struct tm *pp = NULL;
time(&t);
pp = localtime(&t);
sprintf(tmpbuf, "[%d-%d-%d %02d:%02d:%02d]:%s\n", pp->tm_year+1900, pp->tm_mon+1, pp->tm_mday, pp->tm_hour, pp->tm_min, pp->tm_sec, errbuf);
fwrite(tmpbuf, 1, strlen(tmpbuf), fp);
return 0;
}
int closeLogFile(void)
{
fclose(fp);
return -1;
}
所有的功能代码如下:
#include "head.h"
pid_t pid;
MediaList_t MediaList; //媒体列表结构体
MediaStat_t MplayerStat; //媒体状态结构体
//打印媒体播放器菜单
int showTerminal(void)
{
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");
return 0;
}
//循环打印媒体播放器菜单并进行模式选择
int choiceMode(void)
{
int n = -1;
while (1)
{
showTerminal(); //显示终端
printf("请选择:");
scanf("%d", &n); //输入需要选择的功能序号
switch (n) //条件分支进行选择执行相应功能
{
case 1: listOfMusicFile();break;
case 2: playPause();break;
case 3: Stop();break;
case 4: preMusic();break;
case 5: nextMusic();break;
case 6: addSpeed();break;
case 7: seekPosition();break;
case 8: changeModeOfWork();break;
case 9: exit(0);break;
}
}
return 0;
}
//判断一个文件是否为媒体文件
int isMediaFile(const char *filename)
{
while (filename) //找到文件后缀的点
{
if (*filename == '.')
{
break;
}
filename++;
}
if (!strcmp(filename, ".mp3") || !strcmp(filename, ".mp4") || !strcmp(filename, ".flv") || !strcmp(filename, ".avi"))
{
return 1;
}
return 0;
}
//加载媒体文件,使得媒体文件存入二维数组中
int loadMediaFile(const char *direntname)
{
DIR *dp = NULL;
struct dirent *pp = NULL;
MplayerStat.curmode = MPLAYER_MODE_ORDER; //起初初始化为顺序播放
MplayerStat.curstat = MPLAYER_STAT_FREE; //起初状态设置为空闲状态
MplayerStat.curmedianum = 0; //当前曲目为0
dp = opendir(direntname); //打开文件夹
if (NULL == dp)
{
writeLogFile("fail to opendir");
return -1;
}
while (1)
{
pp = readdir(dp); //读取文件夹下的文件名
if (NULL == pp) //读到没有文件时结束
{
break;
}
if (pp->d_name[0] == '.') //忽略隐藏文件
{
continue;
}
if (isMediaFile(pp->d_name)) //判断是否为媒体文件
{
if (MediaList.curmedialist < 100)
{
strcpy(MediaList.medialist[MediaList.curmedialist], pp->d_name); //储存该媒体文件
printf("%s\n", MediaList.medialist[MediaList.curmedialist]);
MediaList.curmedialist++; //媒体文件个数 +1
}
else
{
printf("播放列表加载歌曲超过100首\n"); //最多存储100首
break;
}
}
}
closedir(dp);
return 0;
}
//列出目录下的所有媒体文件
int listOfMusicFile(void)
{
int n = -1;
printf("============= 音乐列表 =============\n");
for (n = 0;n < MediaList.curmedialist;++n)
{
puts(MediaList.medialist[n]);
}
printf("====================================\n");
while (1)
{
printf("请选择(按0返回上一级):");
scanf("%d", &n);
if (n == 0)
{
break;
}
if (MplayerStat.curstat != MPLAYER_STAT_FREE)
{
kill(pid, SIGKILL);
}
playMedia(MediaList.medialist[n - 1]);
MplayerStat.curmedianum = n -1;
}
return 0;
}
//实现mplayer停止功能
int Pause(void)
{
int fd = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
write(fd, "pause\n", 6);
close(fd);
MplayerStat.curstat = MPLAYER_STAT_PAUSE;
return 0;
}
//实现继续播放功能
int Continue(void)
{
int fd = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
write(fd, "pause\n", 6);
close(fd);
MplayerStat.curstat = MPLAYER_STAT_PLAY;
return 0;
}
//播放指定的媒体文件
int playMedia(char *pMediaPath)
{
printf("pMediaPath = %s\n", pMediaPath);
pid = fork();
if (pid < 0)
{
writeLogFile("fail to fork");
return -1;
}
if (pid == 0)
{
close(1); //关闭stdout
close(2); //关闭stderr
execlp("mplayer", "mplayer", "-slave", "-input", "file=/tmp/myfifo", pMediaPath, NULL);
}
MplayerStat.curstat = MPLAYER_STAT_PLAY;
MplayerStat.curspeed = MPLAYER_SPEED_ONE;
// wait(NULL);
return 0;
}
//开始或者暂停播放
int playPause(void)
{
printf("进入playPause了\n");
if (MplayerStat.curstat == MPLAYER_STAT_FREE)
{
// printf("1");
if (MplayerStat.curmode == MPLAYER_MODE_SINGLE)
{
printf("当前单曲循环\n");
playMedia(MediaList.medialist[MplayerStat.curmedianum]);
}
else if (MplayerStat.curmode == MPLAYER_MODE_ORDER)
{
printf("当前顺序播放\n");
playMedia(MediaList.medialist[MplayerStat.curmedianum + 1]);
MplayerStat.curmedianum++;
}
else if (MplayerStat.curmode == MPLAYER_MODE_RANDOM)
{
printf("当前随机播放\n");
int ret = -1;
srand(time(NULL));
ret = rand() % 2;
playMedia(MediaList.medialist[ret]);
MplayerStat.curmedianum = ret;
}
}
else if (MplayerStat.curstat == MPLAYER_STAT_PLAY)
{
printf("1");
Pause();
}
else if (MplayerStat.curstat == MPLAYER_STAT_PAUSE)
{
printf("1");
Continue();
}
return 0;
}
//停止播放
int Stop(void)
{
int fd = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
write(fd, "quit\n", 5);
close(fd);
kill(pid, SIGKILL);
MplayerStat.curstat = MPLAYER_STAT_FREE;
return 0;
}
//切换下一曲
int nextMusic(void)
{
kill(pid, SIGKILL);
playMedia(MediaList.medialist[MplayerStat.curmedianum + 1]);
MplayerStat.curmedianum++;
return 0;
}
//切换上一曲
int preMusic(void)
{
kill(pid, SIGKILL);
playMedia(MediaList.medialist[MplayerStat.curmedianum - 1]);
MplayerStat.curmedianum--;
return 0;
}
//加速视频播放
int addSpeed(void)
{
int fd = -1;
char cmdbuf[128] = {0};
int n = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
printf("请输入你需要设置的视频速度:");
scanf("%d", &n);
getchar();
sprintf(cmdbuf, "speed_set %d\n", n);
write(fd, cmdbuf, strlen(cmdbuf));
printf("当前播放%d倍速!\n", n);
if (1 == n)
{
MplayerStat.curspeed = MPLAYER_SPEED_ONE;
}
else if (2 == n)
{
MplayerStat.curspeed = MPLAYER_SPEED_TWO;
}
else if (4 == n)
{
MplayerStat.curspeed = MPLAYER_SPEED_FOUR;
}
close(fd);
return 0;
}
//定位视频播放的位置(秒计时)
int seekPosition(void)
{
int fd = -1;
char cmdbuf[128] = {0};
int n = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
printf("请输入你需要跳转的位置:");
scanf("%d", &n);
getchar();
sprintf(cmdbuf, "seek %d 2\n", n);
write(fd, cmdbuf, strlen(cmdbuf));
close(fd);
return 0;
}
//切换视频的播放状态
int changeModeOfWork(void)
{
if (MplayerStat.curmode == MPLAYER_MODE_RANDOM)
{
MplayerStat.curmode = MPLAYER_MODE_SINGLE;
printf("当前单曲循环\n");
}
else if (MplayerStat.curmode == MPLAYER_MODE_SINGLE)
{
MplayerStat.curmode = MPLAYER_MODE_ORDER;
printf("当前顺序播放\n");
}
else if (MplayerStat.curmode == MPLAYER_MODE_ORDER)
{
MplayerStat.curmode = MPLAYER_MODE_RANDOM;
printf("当前随机播放\n");
}
return 0;
}
//信号处理函数
void handler(int signo)
{
int status = 0;
wait(&status); //获取进程状态
if (WIFSIGNALED(status)) //判断进程是否被信号杀死
{
return;
}
else if (WIFEXITED(status)) //判断进程是否正常结束(如果是则按照当前的播放模式继续播放下一首)
{
MplayerStat.curstat = MPLAYER_STAT_FREE;
MplayerStat.curspeed = MPLAYER_SPEED_ONE;
playPause();
}
return;
}
主函数如下:
#include "head.h"
pid_t pid;
MediaList_t MediaList; //媒体列表结构体
MediaStat_t MplayerStat; //媒体状态结构体
//打印媒体播放器菜单
int showTerminal(void)
{
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");
return 0;
}
//循环打印媒体播放器菜单并进行模式选择
int choiceMode(void)
{
int n = -1;
while (1)
{
showTerminal(); //显示终端
printf("请选择:");
scanf("%d", &n); //输入需要选择的功能序号
switch (n) //条件分支进行选择执行相应功能
{
case 1: listOfMusicFile();break;
case 2: playPause();break;
case 3: Stop();break;
case 4: preMusic();break;
case 5: nextMusic();break;
case 6: addSpeed();break;
case 7: seekPosition();break;
case 8: changeModeOfWork();break;
case 9: exit(0);break;
}
}
return 0;
}
//判断一个文件是否为媒体文件
int isMediaFile(const char *filename)
{
while (filename) //找到文件后缀的点
{
if (*filename == '.')
{
break;
}
filename++;
}
if (!strcmp(filename, ".mp3") || !strcmp(filename, ".mp4") || !strcmp(filename, ".flv") || !strcmp(filename, ".avi"))
{
return 1;
}
return 0;
}
//加载媒体文件,使得媒体文件存入二维数组中
int loadMediaFile(const char *direntname)
{
DIR *dp = NULL;
struct dirent *pp = NULL;
MplayerStat.curmode = MPLAYER_MODE_ORDER; //起初初始化为顺序播放
MplayerStat.curstat = MPLAYER_STAT_FREE; //起初状态设置为空闲状态
MplayerStat.curmedianum = 0; //当前曲目为0
dp = opendir(direntname); //打开文件夹
if (NULL == dp)
{
writeLogFile("fail to opendir");
return -1;
}
while (1)
{
pp = readdir(dp); //读取文件夹下的文件名
if (NULL == pp) //读到没有文件时结束
{
break;
}
if (pp->d_name[0] == '.') //忽略隐藏文件
{
continue;
}
if (isMediaFile(pp->d_name)) //判断是否为媒体文件
{
if (MediaList.curmedialist < 100)
{
strcpy(MediaList.medialist[MediaList.curmedialist], pp->d_name); //储存该媒体文件
printf("%s\n", MediaList.medialist[MediaList.curmedialist]);
MediaList.curmedialist++; //媒体文件个数 +1
}
else
{
printf("播放列表加载歌曲超过100首\n"); //最多存储100首
break;
}
}
}
closedir(dp);
return 0;
}
//列出目录下的所有媒体文件
int listOfMusicFile(void)
{
int n = -1;
printf("============= 音乐列表 =============\n");
for (n = 0;n < MediaList.curmedialist;++n)
{
puts(MediaList.medialist[n]);
}
printf("====================================\n");
while (1)
{
printf("请选择(按0返回上一级):");
scanf("%d", &n);
if (n == 0)
{
break;
}
if (MplayerStat.curstat != MPLAYER_STAT_FREE)
{
kill(pid, SIGKILL);
}
playMedia(MediaList.medialist[n - 1]);
MplayerStat.curmedianum = n -1;
}
return 0;
}
//实现mplayer停止功能
int Pause(void)
{
int fd = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
write(fd, "pause\n", 6);
close(fd);
MplayerStat.curstat = MPLAYER_STAT_PAUSE;
return 0;
}
//实现继续播放功能
int Continue(void)
{
int fd = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
write(fd, "pause\n", 6);
close(fd);
MplayerStat.curstat = MPLAYER_STAT_PLAY;
return 0;
}
//播放指定的媒体文件
int playMedia(char *pMediaPath)
{
printf("pMediaPath = %s\n", pMediaPath);
pid = fork();
if (pid < 0)
{
writeLogFile("fail to fork");
return -1;
}
if (pid == 0)
{
close(1); //关闭stdout
close(2); //关闭stderr
execlp("mplayer", "mplayer", "-slave", "-input", "file=/tmp/myfifo", pMediaPath, NULL);
}
MplayerStat.curstat = MPLAYER_STAT_PLAY;
MplayerStat.curspeed = MPLAYER_SPEED_ONE;
// wait(NULL);
return 0;
}
//开始或者暂停播放
int playPause(void)
{
printf("进入playPause了\n");
if (MplayerStat.curstat == MPLAYER_STAT_FREE)
{
// printf("1");
if (MplayerStat.curmode == MPLAYER_MODE_SINGLE)
{
printf("当前单曲循环\n");
playMedia(MediaList.medialist[MplayerStat.curmedianum]);
}
else if (MplayerStat.curmode == MPLAYER_MODE_ORDER)
{
printf("当前顺序播放\n");
playMedia(MediaList.medialist[MplayerStat.curmedianum + 1]);
MplayerStat.curmedianum++;
}
else if (MplayerStat.curmode == MPLAYER_MODE_RANDOM)
{
printf("当前随机播放\n");
int ret = -1;
srand(time(NULL));
ret = rand() % 2;
playMedia(MediaList.medialist[ret]);
MplayerStat.curmedianum = ret;
}
}
else if (MplayerStat.curstat == MPLAYER_STAT_PLAY)
{
printf("1");
Pause();
}
else if (MplayerStat.curstat == MPLAYER_STAT_PAUSE)
{
printf("1");
Continue();
}
return 0;
}
//停止播放
int Stop(void)
{
int fd = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
write(fd, "quit\n", 5);
close(fd);
kill(pid, SIGKILL);
MplayerStat.curstat = MPLAYER_STAT_FREE;
return 0;
}
//切换下一曲
int nextMusic(void)
{
kill(pid, SIGKILL);
playMedia(MediaList.medialist[MplayerStat.curmedianum + 1]);
MplayerStat.curmedianum++;
return 0;
}
//切换上一曲
int preMusic(void)
{
kill(pid, SIGKILL);
playMedia(MediaList.medialist[MplayerStat.curmedianum - 1]);
MplayerStat.curmedianum--;
return 0;
}
//加速视频播放
int addSpeed(void)
{
int fd = -1;
char cmdbuf[128] = {0};
int n = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
printf("请输入你需要设置的视频速度:");
scanf("%d", &n);
getchar();
sprintf(cmdbuf, "speed_set %d\n", n);
write(fd, cmdbuf, strlen(cmdbuf));
printf("当前播放%d倍速!\n", n);
if (1 == n)
{
MplayerStat.curspeed = MPLAYER_SPEED_ONE;
}
else if (2 == n)
{
MplayerStat.curspeed = MPLAYER_SPEED_TWO;
}
else if (4 == n)
{
MplayerStat.curspeed = MPLAYER_SPEED_FOUR;
}
close(fd);
return 0;
}
//定位视频播放的位置(秒计时)
int seekPosition(void)
{
int fd = -1;
char cmdbuf[128] = {0};
int n = -1;
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
writeLogFile("open myfifo failed!fail to pause!");
return -1;
}
printf("请输入你需要跳转的位置:");
scanf("%d", &n);
getchar();
sprintf(cmdbuf, "seek %d 2\n", n);
write(fd, cmdbuf, strlen(cmdbuf));
close(fd);
return 0;
}
//切换视频的播放状态
int changeModeOfWork(void)
{
if (MplayerStat.curmode == MPLAYER_MODE_RANDOM)
{
MplayerStat.curmode = MPLAYER_MODE_SINGLE;
printf("当前单曲循环\n");
}
else if (MplayerStat.curmode == MPLAYER_MODE_SINGLE)
{
MplayerStat.curmode = MPLAYER_MODE_ORDER;
printf("当前顺序播放\n");
}
else if (MplayerStat.curmode == MPLAYER_MODE_ORDER)
{
MplayerStat.curmode = MPLAYER_MODE_RANDOM;
printf("当前随机播放\n");
}
return 0;
}
//信号处理函数
void handler(int signo)
{
int status = 0;
wait(&status); //获取进程状态
if (WIFSIGNALED(status)) //判断进程是否被信号杀死
{
return;
}
else if (WIFEXITED(status)) //判断进程是否正常结束(如果是则按照当前的播放模式继续播放下一首)
{
MplayerStat.curstat = MPLAYER_STAT_FREE;
MplayerStat.curspeed = MPLAYER_SPEED_ONE;
playPause();
}
return;
}
以上就是所有的媒体播放器的相关实现过程,主要的收获有以下几个:
(1)自定义结构体与共用体的区别:结构体每个变量可以自主进行定义开辟空间,但是共用体内部变量共用一个空间,这个变量的值只能是共用体中的一个;
(2)信号处理函数的使用,当收到一个这个样的信号时该如何进行后续业务流程的处理;
以下是本项目的所有相关文件;链接:https://pan.baidu.com/s/1jAWbshZpsStZmbuwJ8fYIg?pwd=gsic 提取码:gsic