前言在嵌入式 Linux 开发中音视频播放是非常常见的需求比如广告机、工业触摸屏、智能家居中控等场景。MPlayer 作为一款轻量级、开源、跨平台的多媒体播放器对硬件资源要求低支持几乎所有主流音视频格式是嵌入式平台的首选方案。本文基于 RK3399 开发板NanoPC-T4/SOM-RK3399 通用从零环境搭建开始一步步讲解 MPlayer 的基础使用、核心 Slave 模式编程控制最终实现一个支持自动扫描视频、播放列表、上一首 / 下一首、音量调节、进度跳转的完整视频播放器所有代码均可直接复制运行。一、MPlayer 环境快速搭建免源码编译MPlayer 的源码编译需要依赖大量第三方库FFmpeg、ALSA、x264 等对新手极不友好。本文直接使用预编译好的完整依赖包5 分钟即可完成环境搭建永久生效。1.1 准备工作准备好预编译包mplayer-cout.tar.bz2包含 MPlayer 可执行文件和所有依赖库确保开发板与虚拟机在同一局域网可通过 SSH 或串口连接开发板已烧录好 Linux 系统支持 Framebuffer 显示和 ALSA 音频1.2 安装步骤步骤 1传输预编译包到开发板# 方法1通过SFTP传输推荐 sftp root开发板IP put mplayer-cout.tar.bz2 /root/work/ # 方法2通过NFS共享目录 cp /mnt/nfs/mplayer-cout.tar.bz2 /root/work/步骤 2解压并配置环境# 进入工作目录 cd /root/work/ # 解压压缩包 tar -jxvf mplayer-cout.tar.bz2 cd mplayer-cout/ # 1. 复制可执行文件到系统路径 cp bin/mplayer /bin/ # 2. 复制所有依赖库到系统库路径必须加-r递归复制 cp -r lib/* /lib/ # 3. 验证安装是否成功 mplayer -version # 输出版本信息即表示安装成功步骤 3配置默认音频输出解决没声音问题# 编辑ALSA配置文件 vi /etc/asound.conf写入以下内容RK3399 通用pcm.!default { type hw card 0 device 0 } ctl.!default { type hw card 0 }保存退出后重启开发板使配置生效。二、MPlayer 基础命令与播放测试环境搭建完成后先通过终端命令测试 MPlayer 的基本功能熟悉常用参数和控制命令。2.1 核心播放参数参数功能说明常用示例-vo fbdev2指定视频输出为 FramebufferLCD 屏幕-vo fbdev2-ao alsa指定音频输出为 ALSA-ao alsa-vf rotateN视频旋转角度-vf rotate1顺时针 90 度-vf rotate0正常-vf rotate-1逆时针 90 度-zoom -x W -y H强制缩放视频到指定尺寸-zoom -x 800 -y 1280-vf scaleW:-3等比例缩放视频-vf scale800:-3宽度 800高度自动计算-geometry X:Y指定视频左上角坐标-geometry 0:0全屏左上角-slave -quiet开启 Slave 模式关闭冗余打印-slave -quiet-loop N循环播放 N 次-loop 0无限循环2.2 常用播放命令示例# 1. 基础播放最常用 mplayer -vo fbdev2 -ao alsa test.mp4 # 2. 顺时针旋转90度播放竖屏LCD必备 mplayer -vo fbdev2 -ao alsa -vf rotate1 test.mp4 # 3. 等比例缩放至宽度800播放 mplayer -vo fbdev2 -ao alsa -vf scale800:-3 test.mp4 # 4. 强制全屏播放800x1280分辨率 mplayer -vo fbdev2 -ao alsa -zoom -x 800 -y 1280 test.mp4 # 5. 无限循环播放 mplayer -vo fbdev2 -ao alsa -loop 0 test.mp42.3 终端交互式控制命令播放过程中可在终端输入以下命令控制播放命令功能pause暂停 / 继续播放volume 50 1设置音量为 500-100mute 1静音mute 0取消静音seek 30跳转到第 30 秒播放get_time_length获取视频总时长秒get_time_pos获取当前播放位置秒quit退出播放三、核心Slave 模式编程控制实际项目中我们不可能通过终端手动控制播放器必须通过 C 语言程序实现自动化控制。MPlayer 的Slave 模式就是为此设计的它允许程序通过管道向 MPlayer 发送命令实现完全的编程控制。3.1 Slave 模式原理普通模式MPlayer 截获键盘事件从终端读取控制命令Slave 模式MPlayer 后台运行从命名管道FIFO读取控制命令不再响应键盘程序通过向命名管道写入命令字符串即可实现对 MPlayer 的所有控制3.2 完整 C 语言控制代码以下代码实现了 MPlayer 的启动、播放、暂停、音量调节、进度跳转、停止等核心功能#include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include sys/stat.h #include string.h #include signal.h #define FIFO_PATH /tmp/mplayer_fifo static pid_t mplayer_pid -1; static int fifo_fd -1; /** * brief 向MPlayer发送命令 * param cmd 命令字符串 */ void mplayer_send_cmd(const char *cmd) { if (fifo_fd 0 || cmd NULL) { return; } write(fifo_fd, cmd, strlen(cmd)); write(fifo_fd, \n, 1); // 命令必须以换行符结尾 } /** * brief 启动MPlayer并进入Slave模式 * param video_path 视频文件路径 * return 成功返回0失败返回-1 */ int mplayer_start(const char *video_path) { // 1. 创建命名管道 unlink(FIFO_PATH); if (mkfifo(FIFO_PATH, 0666) 0) { perror(mkfifo failed); return -1; } // 2. 创建子进程运行MPlayer mplayer_pid fork(); if (mplayer_pid 0) { perror(fork failed); unlink(FIFO_PATH); return -1; } // 子进程执行MPlayer if (mplayer_pid 0) { execlp(mplayer, mplayer, -slave, -quiet, -input, fileFIFO_PATH, -vo, fbdev2, -ao, alsa, -zoom, -x, 800, -y, 1280, // 根据自己的LCD分辨率修改 video_path, NULL); perror(execlp mplayer failed); exit(EXIT_FAILURE); } // 父进程打开命名管道写端 fifo_fd open(FIFO_PATH, O_WRONLY); if (fifo_fd 0) { perror(open fifo failed); kill(mplayer_pid, SIGKILL); unlink(FIFO_PATH); return -1; } printf(MPlayer启动成功正在播放%s\n, video_path); return 0; } /** * brief 停止MPlayer并释放资源 */ void mplayer_stop(void) { if (mplayer_pid 0) { mplayer_send_cmd(quit); usleep(500000); // 等待MPlayer正常退出 kill(mplayer_pid, SIGKILL); mplayer_pid -1; } if (fifo_fd 0) { close(fifo_fd); fifo_fd -1; } unlink(FIFO_PATH); printf(MPlayer已停止\n); } // 测试主函数 int main() { char cmd[128]; // 启动播放 if (mplayer_start(/root/video/test.mp4) 0) { return -1; } // 简单控制台交互 while (1) { printf(\n MPlayer控制菜单 \n); printf(1. 暂停/继续\n); printf(2. 设置音量(0-100)\n); printf(3. 跳转到指定秒数\n); printf(4. 停止并退出\n); printf(请输入选项); fflush(stdout); fgets(cmd, sizeof(cmd), stdin); int choice atoi(cmd); switch (choice) { case 1: mplayer_send_cmd(pause); break; case 2: printf(请输入音量(0-100)); fgets(cmd, sizeof(cmd), stdin); int vol atoi(cmd); sprintf(cmd, volume %d 1, vol); mplayer_send_cmd(cmd); break; case 3: printf(请输入跳转秒数); fgets(cmd, sizeof(cmd), stdin); int sec atoi(cmd); sprintf(cmd, seek %d 0, sec); mplayer_send_cmd(cmd); break; case 4: mplayer_stop(); return 0; default: printf(无效选项\n); break; } } return 0; }3.3 编译与运行# 编译代码 aarch64-linux-gnu-gcc mplayer_ctl.c -o mplayer_ctl # 传输到开发板 sftp root开发板IP put mplayer_ctl /root/ put test.mp4 /root/video/ # 运行程序 chmod x mplayer_ctl ./mplayer_ctl四、进阶实现自动扫描视频播放列表上面的代码只能播放单个视频实际项目中通常需要自动扫描指定目录下的所有视频文件生成播放列表支持上一首 / 下一首切换。这里结合 Linux 文件夹遍历函数实现该功能。4.1 文件夹遍历核心函数基于opendir/readdir/closedir实现自动过滤非视频文件#include dirent.h #include string.h // 视频文件后缀列表 static const char *video_suffix[] {.mp4, .avi, .mpg, .mov, .mkv, NULL}; /** * brief 判断文件是否为视频文件 * param filename 文件名 * return 是视频文件返回1否则返回0 */ static int is_video_file(const char *filename) { const char **suffix video_suffix; while (*suffix ! NULL) { if (strstr(filename, *suffix) ! NULL) { return 1; } suffix; } return 0; } /** * brief 扫描指定目录下的所有视频文件 * param dir_path 目录路径 * param video_list 输出视频文件路径数组 * param max_count 最大支持的视频数量 * return 扫描到的视频文件个数 */ int scan_video_files(const char *dir_path, char **video_list, int max_count) { DIR *dir opendir(dir_path); if (dir NULL) { perror(opendir failed); return 0; } struct dirent *entry; int count 0; while ((entry readdir(dir)) ! NULL count max_count) { // 跳过.和.. if (strcmp(entry-d_name, .) 0 || strcmp(entry-d_name, ..) 0) { continue; } // 只处理普通文件 if (entry-d_type DT_REG is_video_file(entry-d_name)) { // 拼接完整路径 video_list[count] malloc(256); snprintf(video_list[count], 256, %s/%s, dir_path, entry-d_name); count; } } closedir(dir); return count; } /** * brief 释放视频列表内存 * param video_list 视频文件路径数组 * param count 视频个数 */ void free_video_list(char **video_list, int count) { for (int i 0; i count; i) { free(video_list[i]); } }4.2 播放列表功能整合在原有代码基础上添加播放列表索引管理实现上一首 / 下一首切换#define MAX_VIDEO_COUNT 100 static char *video_list[MAX_VIDEO_COUNT]; static int video_count 0; static int current_index 0; /** * brief 播放指定索引的视频 * param index 视频索引 */ void play_video(int index) { if (index 0 || index video_count) { return; } // 停止当前播放 mplayer_stop(); // 播放新视频 current_index index; mplayer_start(video_list[current_index]); } // 修改主函数添加播放列表功能 int main() { char cmd[128]; // 扫描视频目录 video_count scan_video_files(/root/video, video_list, MAX_VIDEO_COUNT); if (video_count 0) { printf(未找到任何视频文件\n); return -1; } printf(扫描到%d个视频文件\n, video_count); // 播放第一个视频 play_video(0); // 控制台交互 while (1) { printf(\n 视频播放器控制菜单 \n); printf(当前播放第%d个 / 共%d个\n, current_index 1, video_count); printf(1. 暂停/继续\n); printf(2. 设置音量(0-100)\n); printf(3. 上一首\n); printf(4. 下一首\n); printf(5. 停止并退出\n); printf(请输入选项); fflush(stdout); fgets(cmd, sizeof(cmd), stdin); int choice atoi(cmd); switch (choice) { case 1: mplayer_send_cmd(pause); break; case 2: printf(请输入音量(0-100)); fgets(cmd, sizeof(cmd), stdin); int vol atoi(cmd); sprintf(cmd, volume %d 1, vol); mplayer_send_cmd(cmd); break; case 3: play_video((current_index - 1 video_count) % video_count); break; case 4: play_video((current_index 1) % video_count); break; case 5: mplayer_stop(); free_video_list(video_list, video_count); return 0; default: printf(无效选项\n); break; } } return 0; }五、常见踩坑与排错指南5.1 播放视频没有声音检查耳机是否插好开发板是否有硬件音频输出确认/etc/asound.conf配置正确重启开发板测试 ALSA 音频是否正常aplay test.wav播放时必须添加-ao alsa参数5.2 播放视频黑屏确认 Framebuffer 设备存在ls /dev/fb0播放时必须添加-vo fbdev2参数检查视频分辨率是否超过 LCD 分辨率使用-zoom -x W -y H强制缩放关闭桌面系统systemctl stop lightdm桌面会占用 Framebuffer5.3 Slave 模式命令不响应检查命名管道是否创建成功ls /tmp/mplayer_fifo发送的命令必须以 ** 换行符\n** 结尾确保 MPlayer 启动时添加了-slave -input file/tmp/mplayer_fifo参数不要在 MPlayer 运行时手动按键盘会导致 Slave 模式失效5.4 视频播放卡顿使用 H.264 编码的视频分辨率不超过 LCD 分辨率关闭 MPlayer 的冗余打印添加-quiet参数将视频文件拷贝到开发板本地播放不要通过 NFS 网络播放降低视频帧率建议 25fps 以下5.5 中文文件名乱码设置开发板系统编码为 UTF-8export LANGen_US.UTF-8视频文件名尽量使用英文和数字避免中文本文所有代码均基于标准 C 语言实现无平台依赖如果本文对你有帮助欢迎点赞收藏有任何问题可在评论区交流讨论。