2.智梯云枢・全维管控广告系统——解决串口卡顿 + 优化稳定性

之前代码 不能停止 只能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;
}

正常关闭不卡顿

相关推荐
南行*4 小时前
C语言Linux环境编程
linux·c语言·开发语言·网络安全
海清河晏1114 小时前
Linux进阶篇:HTTP协议
linux·运维·http
June`4 小时前
IO模型全解析:从阻塞到异步(高并发的reactor模型)
linux·服务器·网络·c++
ASS-ASH4 小时前
快速处理虚拟机磁盘扩容问题
linux·数据库·vmware·虚拟机·磁盘扩容
AI_56784 小时前
零基础学Linux:21天从“命令小白”到独立部署服务器
linux·服务器·人工智能·github
江湖有缘4 小时前
Fenrus + Docker 实战:构建简洁高效的浏览器新标签页
运维·docker·容器
乾元4 小时前
如何把 CCIE / HCIE 的实验案例改造成 AI 驱动的工程项目——从“实验室能力”到“可交付系统”的完整迁移路径
大数据·运维·网络·人工智能·深度学习·安全·机器学习
不染尘.4 小时前
Linux基本概述
linux·windows·centos·ssh
lpfasd1234 小时前
宝塔面板(BT Panel)集成雷池 SafeLine WAF(社区版)
运维