1、项目背景、意义
(1)项目背景
在当今数字化时代,音乐和视频等多媒体资源丰富多样,人们对于多媒体播放的需求也日益增长。然而,现有的一些播放器可能功能不够完善,或者在特定场景下无法满足用户的个性化需求。例如,在一些嵌入式系统或者特定的开发环境中,需要一个轻量级、可定制的媒体播放器。
(2)项目意义
本项目旨在开发一个功能齐全、易于使用的媒体播放器,为用户提供一个方便的多媒体播放解决方案。通过这个项目,可以深入学习和实践 C 语言编程、文件操作、进程管理、链表等技术,同时也可以满足用户对音乐和视频播放的基本需求,如播放、暂停、停止、切换歌曲等。
2、项目功能和用户用例
(1)项目功能
- 播放列表管理:能够加载指定目录下的媒体文件,并将其存储在链表中,用户可以查看播放列表。
- 播放控制:支持开始 / 暂停、停止、播放上一首、播放下一首等基本操作。
- 播放速度调节:用户可以根据自己的需求设置播放速度,如一倍速、二倍速、四倍速。
- 播放模式选择:提供顺序播放、随机播放、单曲播放三种播放模式。
- 定位功能:用户可以跳转到指定的播放位置。
(2)用户用例
- 用户运行代码显示出音乐播放器菜单:
- 用户输入数字1-9选择想要实现的功能。
- 用户输入1,则会查看播放列表,如下图:
- 输入歌曲对应数字即可播放对应歌曲或视频,例如:用户输入1,就会播放我们的童年歌曲——《别看我只是一只羊》啦。在这里有个小细节,如果先输入1,在输入2,软件会停止播放《别看我只是一只羊》,开始播放《图图》。
- 输入0会返回上一级(即音乐播放器菜单)
- 用户输入2、3、4、5:实现对应功能。
- 用户输入6:支持1倍速、二倍速、四倍速。
- 用户输入7:输入秒数,例如输入123,即会跳转到当前媒体文件的第123秒开始播放。
- 用户输入8:默认为顺序播放,按下后会切换为随机播放,再次按下会切换为单曲循环,然后切换到顺序播放,以此循环。
3、项目的设计框架及分析
(1)设计框架
本项目采用模块化设计,主要分为以下几个模块:
- 菜单模块:负责显示菜单和接收用户的选择。
- 文件加载模块:负责加载指定目录下的媒体文件,并将其存储在链表中。
- 播放模块:负责播放、暂停、停止等操作,通过进程管理和命名管道与 mplayer 进行通信。
- 播放列表模块:负责显示播放列表和处理用户的选择。
- 速度调节模块:负责调节播放速度。
- 模式选择模块:负责选择播放模式。
- 定位模块:负责跳转到指定的播放位置。
(2)分析
- 菜单模块:通过循环显示菜单和接收用户的输入,实现用户与播放器的交互。
- 文件加载模块:使用
opendir
和readdir
函数遍历指定目录下的文件,使用is_music_file
函数判断文件是否为媒体文件,使用链表存储媒体文件的信息。 - 播放模块:使用
fork
函数创建子进程,使用execlp
函数启动 mplayer 进程,使用命名管道与 mplayer 进行通信,实现播放、暂停、停止等操作。 - 播放列表模块:使用链表遍历和
list_entry
函数显示播放列表,处理用户的选择。 - 速度调节模块:通过命名管道向 mplayer 发送速度调节命令。
- 模式选择模块:根据用户选择的播放模式,实现顺序播放、随机播放、单曲播放。
- 定位模块:通过命名管道向 mplayer 发送定位命令。
4、项目各模块功能及实现技术
(1)菜单模块
- 功能:显示菜单,接收用户的选择。
- 实现技术:使用
printf
函数显示菜单,使用scanf
函数接收用户的输入。
(2)文件加载模块
- 功能:加载指定目录下的媒体文件,并将其存储在链表中。
- 实现技术:使用
opendir
和readdir
函数遍历指定目录下的文件,使用is_music_file
函数判断文件是否为媒体文件,使用malloc
函数分配内存,使用sprintf
函数拼接文件路径,使用list_add_tail
函数将节点添加到链表尾部。
(3)播放模块
- 功能:播放、暂停、停止等操作。
- 实现技术:使用
fork
函数创建子进程,使用execlp
函数启动 mplayer 进程,使用mkfifo
函数创建命名管道,使用open
和write
函数向命名管道发送命令,使用kill
函数终止子进程。
(4)播放列表模块
- 功能:显示播放列表,处理用户的选择。
- 实现技术:使用
list_for_each_entry
函数遍历链表,使用printf
函数显示播放列表,使用scanf
函数接收用户的输入,使用list_entry
函数获取节点信息。
(5)速度调节模块
- 功能:调节播放速度。
- 实现技术:使用
open
和write
函数向命名管道发送速度调节命令。
(6)模式选择模块
- 功能:选择播放模式。
- 实现技术:根据用户选择的播放模式,使用
rand
函数实现随机播放,使用链表遍历实现顺序播放和单曲播放。
(7)定位模块
- 功能:跳转到指定的播放位置。
- 实现技术:使用
open
和write
函数向命名管道发送定位命令。
5、关键的数据类型及接口
(1)关键数据类型
struct list_head
:链表节点结构体,用于存储链表节点信息。music_node_t
:音乐节点结构体,用于存储音乐文件的信息,包括文件路径等。MPLAYER_STATUS_EN
:播放器状态枚举类型,用于表示播放器的状态,如未播放、正在播放、暂停播放。MPLAYER_SPEED_EN
:播放速度枚举类型,用于表示播放速度,如一倍速、二倍速、四倍速。MPLAYER_MODE_EN
:播放模式枚举类型,用于表示播放模式,如顺序播放、随机播放、单曲播放。
(2)关键接口
menu_show
:显示菜单。menu_choose
:接收用户选择。is_music_file
:判断文件是否为媒体文件。load_music_file
:加载指定目录下的媒体文件。player_media
:播放歌曲。show_music_list
:显示播放列表。pause_start
:开始 / 暂停播放。stop
:停止播放。play_pre
:播放上一首。play_next
:播放下一首。add_speed
:调节播放速度。seekPosition
:跳转到指定位置。play_mode
:选择播放模式。
6、项目中遇到的困难及解决方法(现象->分析->原因->解决方法)
以下只展示部分令我有收获的问题:
(1)问题一:
- 现象:播放上一首下一首时,无法切换。
- 原因:函数中传入的ptmpnode(当前播放音乐的大节点)每次都为NULL了。
- 解决方法:将ptmpnode(当前播放音乐的大节点)在main.c中定义,调用完show_music_list()函数返回ptmpnode,在调用play_pre和play_next时传入ptmpnode并更新ptmpnode。
(2)问题二:
- 现象:想在菜单中显示正在播放的歌曲名字,但是pmusicpath是“/home/linux/Music/歌曲名.mp3”
- 原因:需要解析字符串,将歌曲名字解析出来。
- 解决方法:使用strtok函数解析字符串。
(3)问题三:
- 想要在菜单中显示当前播放状态、播放速度、播放模式,操作如下图:
7、项目改进与优化(发展空间)
- 界面优化:目前的菜单界面比较简单,可以使用图形界面库(如 GTK、Qt 等)进行界面设计,提高用户体验。
- 功能扩展:可以增加一些高级功能,如歌词显示、音频均衡器、视频字幕等。
- 性能优化:可以对文件加载和链表遍历等操作进行性能优化,减少内存开销和提高运行效率。
- 跨平台支持:目前的项目主要在 Linux 环境下开发,可以进行跨平台开发,支持 Windows、Mac 等操作系统。
- 网络功能:可以增加网络功能,支持在线音乐和视频的播放。