之前代码 不能停止 只能kill进程pid停止
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdbool.h>
#include <sys/stat.h>
#define VIDEO_DIR "/userdata/videos"
#define LOG_FILE "/tmp/ad_player.log"
#define PID_FILE "/tmp/ad_player.pid"
volatile sig_atomic_t stop_flag = 0;
void signal_handler(int sig)
{
if (sig == SIGINT || sig == SIGTERM)
{
printf("接收到停止信号\n");
stop_flag = 1;
}
}
// 获取视频文件列表
int get_video_files(char ***file_list)
{
DIR *dir;
struct dirent *ent;
int count = 0;
dir = opendir(VIDEO_DIR);
if (dir == NULL)
{
printf("无法打开视频目录: %s\n", VIDEO_DIR);
return 0;
}
// 第一遍:计算文件数量
while ((ent = readdir(dir)) != NULL)
{
char *ext = strrchr(ent->d_name, '.');
if (ext && (strcmp(ext, ".mp4") == 0 ||
strcmp(ext, ".MP4") == 0 ||
strcmp(ext, ".avi") == 0 ||
strcmp(ext, ".mkv") == 0))
{
count++;
}
}
rewinddir(dir);
// 分配内存
*file_list = malloc(count * sizeof(char *));
// 第二遍:存储文件名
int i = 0;
while ((ent = readdir(dir)) != NULL && i < count)
{
char *ext = strrchr(ent->d_name, '.');
if (ext && (strcmp(ext, ".mp4") == 0 ||
strcmp(ext, ".MP4") == 0 ||
strcmp(ext, ".avi") == 0 ||
strcmp(ext, ".mkv") == 0))
{
(*file_list)[i] = malloc(strlen(VIDEO_DIR) + strlen(ent->d_name) + 2);
sprintf((*file_list)[i], "%s/%s", VIDEO_DIR, ent->d_name);
i++;
}
}
closedir(dir);
return count;
}
// 播放单个视频
void play_video(const char *video_path)
{
char command[1024];
// 使用gst-play-1.0播放,使用kmssink用于MIPI屏幕
snprintf(command, sizeof(command),
"gst-play-1.0 \"%s\" --videosink=kmssink --audiosink=alsasink --volume=0.8 2>> %s",
video_path, LOG_FILE);
printf("播放: %s\n", video_path);
system(command);
}
// 保存PID到文件
void save_pid()
{
FILE *fp = fopen(PID_FILE, "w");
if (fp)
{
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
}
// 删除PID文件
void remove_pid()
{
remove(PID_FILE);
}
int main(int argc, char *argv[])
{
printf("=== 电梯广告播放系统 ===\n");
printf("视频目录: %s\n", VIDEO_DIR);
// 设置信号处理
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// 保存PID
save_pid();
// 检查视频目录
DIR *dir = opendir(VIDEO_DIR);
if (dir == NULL)
{
printf("错误: 视频目录不存在,创建目录...\n");
mkdir(VIDEO_DIR, 0755);
printf("请将MP4视频文件放入: %s\n", VIDEO_DIR);
remove_pid();
return 1;
}
closedir(dir);
// 主循环
while (!stop_flag)
{
char **video_files = NULL;
int video_count = get_video_files(&video_files);
if (video_count == 0)
{
printf("没有找到视频文件,等待10秒...\n");
sleep(10);
continue;
}
printf("找到 %d 个视频文件\n", video_count);
// 循环播放所有视频
for (int i = 0; i < video_count && !stop_flag; i++)
{
if (video_files[i])
{
play_video(video_files[i]);
free(video_files[i]);
}
}
if (video_files)
{
free(video_files);
}
printf("一轮播放完成\n");
}
printf("停止播放系统\n");
remove_pid();
return 0;
}
停止播放后终端卡顿
卡顿代码
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h> // 新增:终端模式控制头文件
#define VIDEO_DIR "/userdata/videos"
#define LOG_FILE "/tmp/ad_player.log"
#define PID_FILE "/tmp/ad_player.pid"
volatile sig_atomic_t stop_flag = 0;
pid_t play_child_pid = -1;
struct termios orig_termios; // 新增:保存终端初始模式
// 新增:恢复终端初始模式(解决串口卡顿核心函数)
void restore_terminal() {
// 忽略恢复失败的情况(避免程序退出时崩溃)
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
}
// 信号处理函数(优化:恢复终端+强制退出)
void signal_handler(int sig)
{
if (sig == SIGINT || sig == SIGTERM)
{
printf("\n接收到停止信号,正在终止播放...\n");
stop_flag = 1;
// 终止播放子进程
if (play_child_pid > 0)
{
kill(play_child_pid, SIGKILL);
waitpid(play_child_pid, NULL, WNOHANG);
play_child_pid = -1;
}
// 关键:恢复终端模式,解决串口卡顿
restore_terminal();
remove(PID_FILE); // 清理PID文件
printf("播放系统已停止,串口已恢复正常\n");
exit(0); // 强制退出程序,避免阻塞
}
}
// 获取视频文件列表(原代码不变)
int get_video_files(char ***file_list)
{
DIR *dir;
struct dirent *ent;
int count = 0;
dir = opendir(VIDEO_DIR);
if (dir == NULL)
{
printf("无法打开视频目录: %s\n", VIDEO_DIR);
return 0;
}
while ((ent = readdir(dir)) != NULL)
{
char *ext = strrchr(ent->d_name, '.');
if (ext && (strcmp(ext, ".mp4") == 0 ||
strcmp(ext, ".MP4") == 0 ||
strcmp(ext, ".avi") == 0 ||
strcmp(ext, ".mkv") == 0))
{
count++;
}
}
rewinddir(dir);
*file_list = malloc(count * sizeof(char *));
int i = 0;
while ((ent = readdir(dir)) != NULL && i < count)
{
char *ext = strrchr(ent->d_name, '.');
if (ext && (strcmp(ext, ".mp4") == 0 ||
strcmp(ext, ".MP4") == 0 ||
strcmp(ext, ".avi") == 0 ||
strcmp(ext, ".mkv") == 0))
{
(*file_list)[i] = malloc(strlen(VIDEO_DIR) + strlen(ent->d_name) + 2);
sprintf((*file_list)[i], "%s/%s", VIDEO_DIR, ent->d_name);
i++;
}
}
closedir(dir);
return count;
}
// 播放单个视频(原逻辑不变,仅优化日志重定向)
void play_video(const char *video_path)
{
if (stop_flag)
return;
char *cmd_args[] = {
"gst-play-1.0",
(char *)video_path,
"--videosink=kmssink",
"--audiosink=alsasink",
"--volume=0.8",
NULL
};
printf("播放: %s\n", video_path);
play_child_pid = fork();
if (play_child_pid == 0)
{
// 重定向日志(优化:创建日志文件时设置权限)
int log_fd = open(LOG_FILE, O_WRONLY | O_APPEND | O_CREAT, 0644);
if (log_fd >= 0)
{
dup2(log_fd, STDERR_FILENO);
close(log_fd);
}
execvp("gst-play-1.0", cmd_args);
perror("执行播放命令失败");
exit(1);
}
else if (play_child_pid < 0)
{
perror("创建播放进程失败");
play_child_pid = -1;
return;
}
// 非阻塞等待子进程,及时响应停止信号
while (play_child_pid > 0 && !stop_flag)
{
int status;
pid_t ret = waitpid(play_child_pid, &status, WNOHANG);
if (ret != 0)
{
play_child_pid = -1;
break;
}
usleep(100000);
}
}
// 保存PID到文件(原代码不变)
void save_pid()
{
FILE *fp = fopen(PID_FILE, "w");
if (fp)
{
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
}
// 删除PID文件(原代码不变)
void remove_pid()
{
remove(PID_FILE);
}
int main(int argc, char *argv[])
{
// 第一步:保存终端初始模式(核心!)
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) {
perror("保存终端模式失败");
exit(1);
}
// 注册退出清理函数:即使程序异常退出,也会恢复终端
atexit(restore_terminal);
printf("=== 电梯广告播放系统 ===\n");
printf("视频目录: %s\n", VIDEO_DIR);
printf("提示:按 Ctrl+C 可立即停止播放并退出程序\n");
// 设置信号处理(支持Ctrl+C和kill命令)
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// 保存PID
save_pid();
// 检查视频目录(优化:退出时恢复终端)
DIR *dir = opendir(VIDEO_DIR);
if (dir == NULL)
{
printf("错误: 视频目录不存在,创建目录...\n");
mkdir(VIDEO_DIR, 0755);
printf("请将MP4视频文件放入: %s\n", VIDEO_DIR);
remove_pid();
restore_terminal(); // 恢复终端
return 1;
}
closedir(dir);
// 主循环(原逻辑不变)
while (!stop_flag)
{
char **video_files = NULL;
int video_count = get_video_files(&video_files);
if (video_count == 0)
{
printf("没有找到视频文件,等待10秒...\n");
sleep(10);
continue;
}
printf("找到 %d 个视频文件\n", video_count);
for (int i = 0; i < video_count && !stop_flag; i++)
{
if (video_files[i])
{
play_video(video_files[i]);
free(video_files[i]);
}
}
if (video_files)
{
free(video_files);
}
if (!stop_flag)
{
printf("一轮播放完成,继续循环...\n");
}
}
// 正常退出时的清理
restore_terminal();
remove_pid();
printf("停止播放系统\n");
return 0;
} 在这个代码的基础上改正卡顿 其他功能保留

核心原因:play_video 函数只重定向了标准错误(STDERR_FILENO),但没有重定向标准输出(STDOUT_FILENO)。这会导致 gst-play-1.0 的输出(包括控制字符)仍然会发送到终端,从而造成卡顿。
最终不卡顿代码 按上下键可控制音量
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h> // 新增:终端模式控制头文件
#define VIDEO_DIR "/userdata/videos"
#define LOG_FILE "/tmp/ad_player.log"
#define PID_FILE "/tmp/ad_player.pid"
volatile sig_atomic_t stop_flag = 0;
pid_t play_child_pid = -1;
struct termios orig_termios; // 新增:保存终端初始模式
// 新增:恢复终端初始模式(解决串口卡顿核心函数)
void restore_terminal()
{
// 忽略恢复失败的情况(避免程序退出时崩溃)
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
}
// 信号处理函数(优化:恢复终端+强制退出)
void signal_handler(int sig)
{
if (sig == SIGINT || sig == SIGTERM)
{
printf("\n接收到停止信号,正在终止播放...\n");
stop_flag = 1;
// 终止播放子进程
if (play_child_pid > 0)
{
kill(play_child_pid, SIGKILL);
waitpid(play_child_pid, NULL, WNOHANG);
play_child_pid = -1;
}
// 关键:恢复终端模式,解决串口卡顿
restore_terminal();
remove(PID_FILE); // 清理PID文件
printf("播放系统已停止,串口已恢复正常\n");
exit(0); // 强制退出程序,避免阻塞
}
}
// 获取视频文件列表(原代码不变)
int get_video_files(char ***file_list)
{
DIR *dir;
struct dirent *ent;
int count = 0;
dir = opendir(VIDEO_DIR);
if (dir == NULL)
{
printf("无法打开视频目录: %s\n", VIDEO_DIR);
return 0;
}
while ((ent = readdir(dir)) != NULL)
{
char *ext = strrchr(ent->d_name, '.');
if (ext && (strcmp(ext, ".mp4") == 0 ||
strcmp(ext, ".MP4") == 0 ||
strcmp(ext, ".avi") == 0 ||
strcmp(ext, ".mkv") == 0))
{
count++;
}
}
rewinddir(dir);
*file_list = malloc(count * sizeof(char *));
int i = 0;
while ((ent = readdir(dir)) != NULL && i < count)
{
char *ext = strrchr(ent->d_name, '.');
if (ext && (strcmp(ext, ".mp4") == 0 ||
strcmp(ext, ".MP4") == 0 ||
strcmp(ext, ".avi") == 0 ||
strcmp(ext, ".mkv") == 0))
{
(*file_list)[i] = malloc(strlen(VIDEO_DIR) + strlen(ent->d_name) + 2);
sprintf((*file_list)[i], "%s/%s", VIDEO_DIR, ent->d_name);
i++;
}
}
closedir(dir);
return count;
}
// 播放单个视频(关键修改:重定向标准输出和标准错误)
void play_video(const char *video_path)
{
if (stop_flag)
return;
char *cmd_args[] = {
"gst-play-1.0",
(char *)video_path,
"--videosink=kmssink",
"--audiosink=alsasink",
"--volume=0.8",
NULL};
printf("播放: %s\n", video_path);
play_child_pid = fork();
if (play_child_pid == 0)
{
// 重定向日志(关键修改:同时重定向标准输出和标准错误)
int log_fd = open(LOG_FILE, O_WRONLY | O_APPEND | O_CREAT, 0644);
if (log_fd >= 0)
{
// 修复点:重定向标准输出和标准错误
dup2(log_fd, STDOUT_FILENO); // 重定向标准输出
dup2(log_fd, STDERR_FILENO); // 重定向标准错误
close(log_fd);
}
execvp("gst-play-1.0", cmd_args);
perror("执行播放命令失败");
exit(1);
}
else if (play_child_pid < 0)
{
perror("创建播放进程失败");
play_child_pid = -1;
return;
}
// 非阻塞等待子进程,及时响应停止信号
while (play_child_pid > 0 && !stop_flag)
{
int status;
pid_t ret = waitpid(play_child_pid, &status, WNOHANG);
if (ret != 0)
{
play_child_pid = -1;
break;
}
usleep(100000);
}
}
// 保存PID到文件(原代码不变)
void save_pid()
{
FILE *fp = fopen(PID_FILE, "w");
if (fp)
{
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
}
// 删除PID文件(原代码不变)
void remove_pid()
{
remove(PID_FILE);
}
int main(int argc, char *argv[])
{
// 第一步:保存终端初始模式(核心!)
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
{
perror("保存终端模式失败");
exit(1);
}
// 注册退出清理函数:即使程序异常退出,也会恢复终端
atexit(restore_terminal);
printf("=== 电梯广告播放系统 ===\n");
printf("视频目录: %s\n", VIDEO_DIR);
printf("提示:按 Ctrl+C 可立即停止播放并退出程序\n");
// 设置信号处理(支持Ctrl+C和kill命令)
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// 保存PID
save_pid();
// 检查视频目录(优化:退出时恢复终端)
DIR *dir = opendir(VIDEO_DIR);
if (dir == NULL)
{
printf("错误: 视频目录不存在,创建目录...\n");
mkdir(VIDEO_DIR, 0755);
printf("请将MP4视频文件放入: %s\n", VIDEO_DIR);
remove_pid();
restore_terminal(); // 恢复终端
return 1;
}
closedir(dir);
// 主循环(原逻辑不变)
while (!stop_flag)
{
char **video_files = NULL;
int video_count = get_video_files(&video_files);
if (video_count == 0)
{
printf("没有找到视频文件,等待10秒...\n");
sleep(10);
continue;
}
printf("找到 %d 个视频文件\n", video_count);
for (int i = 0; i < video_count && !stop_flag; i++)
{
if (video_files[i])
{
play_video(video_files[i]);
free(video_files[i]);
}
}
if (video_files)
{
free(video_files);
}
if (!stop_flag)
{
printf("一轮播放完成,继续循环...\n");
}
}
// 正常退出时的清理
restore_terminal();
remove_pid();
printf("停止播放系统\n");
return 0;
}

正常关闭不卡顿