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

之前代码 不能停止 只能kill进程pid停止

#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; }

停止播放后终端卡顿

卡顿代码

#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的输出(包括控制字符)仍然会发送到终端,从而造成卡顿。

最终不卡顿代码 按上下键可控制音量

#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; }

正常关闭不卡顿

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1146161.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

跨模块数据传递方案:SystemVerilog接口实践

跨模块数据传递的优雅解法&#xff1a;深入掌握SystemVerilog接口实战你有没有遇到过这样的场景&#xff1f;一个简单的请求-应答协议&#xff0c;DUT端口连了req,gnt,data[7:0],valid,ready……十几个信号。写测试平台时&#xff0c;每个driver、monitor都要把这些信号一一声明…

移动电源智能监测技术全面升级

随着智能手机、平板电脑等电子设备的普及&#xff0c;移动电源已成为现代人生活中不可或缺的“能量伴侣”。然而&#xff0c;近年来因移动电源质量问题引发的起火、爆炸等安全事故频发&#xff0c;尤其在民航等密闭空间中的隐患&#xff0c;让安全技术升级成为行业发展的核心命…

Redis 助力大数据平台实现高性能读写操作

Redis 助力大数据平台实现高性能读写操作 关键词&#xff1a;Redis, 大数据平台, 高性能读写, 内存数据库, 数据缓存, 分布式系统, 实时数据处理 摘要&#xff1a;在当今数据驱动的时代&#xff0c;大数据平台面临着前所未有的性能挑战。本文深入探讨Redis作为高性能内存数据库…

Pspice在OrCAD Capture中的集成配置:手把手教程

手把手教你打通 Pspice 与 OrCAD Capture 的“任督二脉”你有没有遇到过这种情况&#xff1a;满怀信心地打开 OrCAD Capture&#xff0c;画好了一个运放电路&#xff0c;准备跑个瞬态仿真看看响应——结果点击“Run Pspice”按钮时&#xff0c;发现它灰了&#xff1f;或者仿真一…

ARM Compiler 5.06目标文件格式解析:ELF结构全面讲解

深入ARM编译器的“黑盒”&#xff1a;从目标文件看ELF如何塑造嵌入式系统 你有没有遇到过这样的场景&#xff1f; 代码明明编译通过&#xff0c;链接时却报出 multiple definition of init_system &#xff1b;或者固件烧录后跑飞&#xff0c;调试器显示PC指针跳到了一片空…

L298N外围元件选型(电阻/电容/电感)系统学习

L298N驱动直流电机&#xff1a;从“能转”到“稳转”的无源元件设计之道你有没有遇到过这样的场景&#xff1f;MCU代码写得一丝不苟&#xff0c;PWM调速逻辑清晰&#xff0c;方向控制准确无误——可一接上电机&#xff0c;系统就复位、单片机重启、电机嗡嗡作响像在唱歌……最后…

数字电路与射频前端协同设计:现代通信设备深度剖析

数字电路与射频前端协同设计&#xff1a;现代通信设备的“神经”与“肌肉”如何共舞&#xff1f;你有没有遇到过这样的情况&#xff1a;明明算法跑得飞快&#xff0c;FPGA逻辑也写得滴水不漏&#xff0c;可实测时却发现Wi-Fi信号突然掉速、5G吞吐量上不去&#xff0c;甚至接收灵…

全面讲解PL2303芯片USB Serial驱动下载注意事项

一次搞懂PL2303 USB转串口&#xff1a;驱动下载避坑全指南你有没有遇到过这种情况——手里的USB转TTL模块插上电脑&#xff0c;设备管理器里却只显示“未知设备”&#xff1f;或者刚烧录完程序&#xff0c;再插回去COM口就消失了&#xff1f;又或者明明能识别&#xff0c;但高波…

vivado安装操作指南:适合初学者的完整流程

手把手教你安装 Vivado&#xff1a;从零开始搭建 FPGA 开发环境 你是不是也遇到过这种情况——刚想入门 FPGA&#xff0c;兴冲冲地打开 Xilinx 官网准备下载 Vivado&#xff0c;结果发现安装包几十个 G&#xff0c;流程复杂得像在解密&#xff0c;还没开始写代码就被“卡死”在…

大电流电感的热管理与散热设计实践案例

大电流电感的热管理&#xff1a;从设计误区到实战优化你有没有遇到过这样的情况&#xff1f;一款电源模块在实验室测试时表现良好&#xff0c;效率达标、波形干净。可一旦进入满载老化测试&#xff0c;电感就开始发热发烫&#xff0c;甚至出现啸叫、温升失控——最终系统不得不…

MOSFET驱动电路设计项目应用:LED调光控制实例

用MOSFET做LED调光&#xff0c;到底怎么才算“设计到位”&#xff1f;你有没有遇到过这样的情况&#xff1a;明明写好了PWM代码&#xff0c;占空比也能调&#xff0c;可一接上大功率LED&#xff0c;灯不是闪烁就是发热严重&#xff0c;甚至MOSFET直接烫手烧掉&#xff1f;别急—…

超详细版HBuilderX真机调试微信小程序教程

HBuilderX真机调试微信小程序&#xff1a;从零开始的实战指南 你有没有遇到过这样的情况&#xff1f;在HBuilderX里写好的页面&#xff0c;模拟器跑得顺风顺水&#xff0c;一到手机上就白屏、卡顿、接口报错。别急——这正是 只依赖模拟器开发 的典型痛点。 真实设备千差万…

快速理解risc-v五级流水线cpu:核心要点通俗解释

深入浅出&#xff1a;彻底搞懂RISC-V五级流水线CPU的工作原理你有没有想过&#xff0c;为什么现代处理器能“同时”执行多条指令&#xff1f;明明电路是按周期一步步运行的&#xff0c;却给人一种“并行处理”的错觉。其实&#xff0c;这背后的核心技术就是——流水线&#xff…

[特殊字符]_压力测试与性能调优的完整指南[20260111170735]

作为一名经历过无数次压力测试的工程师&#xff0c;我深知压力测试在性能调优中的重要性。压力测试不仅是验证系统性能的必要手段&#xff0c;更是发现性能瓶颈和优化方向的关键工具。今天我要分享的是基于真实项目经验的压力测试与性能调优完整指南。 &#x1f4a1; 压力测试…

hbuilderx下载全流程图解:快速理解安装步骤

从零开始搭建开发环境&#xff1a;HBuilderX 下载与安装全指南 你是不是也曾在搜索引擎里输入“hbuilderx下载”&#xff0c;结果跳出来一堆广告网站、捆绑软件&#xff0c;甚至还有“高速通道”诱导你装一堆莫名其妙的工具&#xff1f;别急——这正是无数新手开发者踩过的坑。…

图解说明无源蜂鸣器驱动电路连接方式与参数设置

无源蜂鸣器驱动电路设计全解析&#xff1a;从原理到实战&#xff0c;一文搞懂你有没有遇到过这种情况&#xff1f;明明代码写好了&#xff0c;PWM也输出了&#xff0c;可蜂鸣器就是“哑巴”&#xff1b;或者声音微弱、断断续续&#xff0c;甚至系统莫名其妙重启……如果你用的是…

IAR中使用C99标准的完整指南:版本兼容性说明

如何在 IAR 中真正用好 C99&#xff1f;一份来自实战的配置与避坑指南你有没有遇到过这种情况&#xff1a;写了一段结构清晰、初始化优雅的 C 代码&#xff0c;结果 IAR 编译器报错说.id 1是非法语法&#xff1f;或者你在for循环里声明一个临时变量&#xff0c;编译直接卡在“…

Multisim下载安装路径选择注意事项:通俗解释

安装Multisim前&#xff0c;你真的选对路径了吗&#xff1f;一个被忽视却致命的细节 你有没有遇到过这种情况&#xff1a;好不容易从官网完成 multisim下载 &#xff0c;兴冲冲地双击安装包&#xff0c;一路“下一步”走到底&#xff0c;结果软件刚打开就闪退、报错“无法加…

Intel HAXM安装指南:新手必看的AVD配置详解

Intel HAXM安装全解析&#xff1a;从报错到流畅运行AVD的实战指南你是否曾在启动Android模拟器时&#xff0c;突然弹出一条红色警告&#xff1a;Intel HAXM is required to run this AVD或者更直接地提示&#xff1a;HAXM is not installed然后眼睁睁看着模拟器卡住、崩溃、甚至…

vivado除法器ip核界面功能详解:入门级全面讲解

Vivado除法器IP核深度解析&#xff1a;从界面操作到实战避坑在FPGA设计中&#xff0c;我们每天都在和加法、乘法打交道。但一旦遇到除法运算&#xff0c;很多新手立刻头大——为什么&#xff1f;因为硬件实现除法远不像软件里写个a/b那么简单。如果你正在用Xilinx的Vivado做项目…