STM32网络收音机: http://radio.rt-thread.org
网络播放器需求说明网络播放器需求说明网络播放器需求说明网络播放器需求说明
说明说明说明说明:对于一个软件系统的设计,最开始的步骤就是需求说明,所以网络播放器的开篇还是从最正规的设
计方法开始。另外,所有的设计文档都是从实用角度出发,所以在文档格式上不做过多
,或许最后会
有一个总结格式。
嵌入式系统是一个完整的系统,包含了软件、硬件两个方面。嵌入式系统之软件系统需求说
明是从完整的系统需求中截取和软件相关的部分。当然有的时候也会存在,需要的部分功能
应该采用软件还是硬件方式来实现的抉择。例如,STM32 网络播放器中,mp3 部分的解码
部分,到底是采用硬解码还是软解码。
在通常的实施过程中,系统的需求说明还会先包括一部分系统可行性研究工作,以避免设计
虽然做出来了,但到最后却不可能实现(也就意味着产品失败),例如这款网络播放器项目
在前期就做了 STM32的 mp3软解压可行性
。
网络播放器的上下文环境网络播放器的上下文环境网络播放器的上下文环境网络播放器的上下文环境
说明说明说明说明::::一个产品的设计离不开产品所处的环境,只有目的明确了,才能保证后续产品的实现能够满足一些
先决条件。
如上图所示的网络播放器操作环境,
1、 网络播放器通过网线连接到路由器,通过路由器连接到互联网中。
2、 网络播放器采用 5V电源供电(能够兼容从电脑 USB取电)。
3、 用户可通过网络播放器收听网络上的音频流(当前互联网音频流格式主要包括三种:主
要传递 wma音频的mms协议;主要传递mp3、ogg音频的 shoutcast协议;主要
传递 real audio的 RSTP协议)。
4、 用户可通过网络播放器播放用户提供的 SD卡上的数据。
5、 用户可通过网络播放器上的按键和触摸屏进行网络播放器的操作。
6、 用户可通过网络播放器自带的喇叭或耳机收听网络电台;
系统硬件框图
Reset
波浪线
Reset
波浪线
STM32网络收音机: http://radio.rt-thread.org
软件的设计离不开硬件的实现,所以在设计时很有必要知道硬件框图是什么样的。
软件相关的需求说明软件相关的需求说明软件相关的需求说明软件相关的需求说明
1、 网络播放器通过网线连接到路由器,通过路由器连接到互联网中。
网络播放器支持 DHCP方式从路由器获取 IP v4地址。
2、网络播放器采用 5V电源供电(能够兼容从电脑 USB取电)。
3、用户可通过网络播放器收听网络上的音频流:
用户可播放网络上的mp3、wma音频,能够支持 http音频流和mms音频流协议。
为了更好地支持网络音频流的播放,网络播放器支持音频流的缓冲播放。
4、用户可通过网络播放器播放用户提供的 SD卡上的数据。
用户可播放 SD卡上的mp3、wav、wma歌曲。当用户插上 USB线后,用户通过电
脑操作能够操作 SD卡上的文件(做为 U盘操作文件)。
5、用户可通过网络播放器上的按钮和触摸屏进行网络播放器的操作。
网络播放器能够接收按键进行播放器的操作,进行下一电台,上一电台,播放开始,播
放停止,声音增大,声音减小的操作。
网络播放器能够通过自带的液晶显示屏给出播放的状态。用户能够使用自带触摸屏对网
络播放进行操作:选择相应的功能、播放 SD卡上的音频文件,播放互联网上的音频流。
用户可从网络中更新网络电台的列表并存放到播放器中。
6、 用户可通过网络播放器自带的喇叭或耳机收听网络电台;
【注:当前版本未包括 wma软解码实现】
STM32F103ZE
WM8978
SD Card
IIS
SDIO
DM9000A
Internet
FMSC
LCD
触摸屏
FMSC
STM32网络收音机: http://radio.rt-thread.org
可行性分析可行性分析可行性分析可行性分析
在实施项目前,必要的可行性分析是不可缺少的,它将决定着一个项目的成败,也是项目(产
品)开展的前期步骤。
网络收音机项目因为采用的mp3软解码,并且后续还需要加入网络音频流播放的功能,图
形用户界面的功能等,其中最为关键的就是 mp3 软解码。Mp3 软解码有几个影响非常大
的地方:
- 软解码的性能,当进行软解压时,还剩余多少空闲时间给其他线程。
- 软解码的内存占用,还得计算进读取文件时的缓冲,解码出来的 PCM 数据存放用的缓
冲等。
- 因为使用的主控芯片是 STM32,还很有必要了解采用板载外扩 SRAM访问时速度情况
如何,如果速度和片内 SRAM相差不大,那么内存方面的制约将相差不大。
前期的可行性分析是基于一块已有 STM32开发板进行的,仅自行搭建了一个WM8753的
codec开发板,即仅包括如下的框图:
经过实际测试发现,当进行 mp3软解码时,mp3码率是 64kpbs时,系统的占用大约是
50%,mp3 码率是 320kbps 时,系统的占用大约是 70%。在网络中,大多数网络音频
流的码率是 64kbps – 128kbps,基本上 CPU的运算性能满足条件。
内存占用上,如果采用 libmad 整数软解码器,它的内存占用量会需求很多,远远超过
片内的 64k SRAM,必然将使用板载外扩 SRAM。如果采用 helix的 mp3整数软解码器,
内存占用是 23kB,再加上解码后的 PCM数据缓冲,大约在 30kB左右。
板载 SRAM和片内 SRAM的测试数据如下:
当把数据段放在片内 SRAM时的测试结果如下:(分别对内存块做 8位、16位、32位访问
及采用库函数的 memset访问结果)
finsh>>benchmark()
internal sram[8bit]: 326
external sram[8bit]: 466
internal sram[16bit]: 210
external sram[16bit]: 233
internal sram[32bit]: 105
STM32F103ZE
WM8753
SD Card
IIS
SDIO
STM32网络收音机: http://radio.rt-thread.org
external sram[32bit]: 163
internal sram[memset]: 27
external sram[memset]: 163
从上面的数据可以看得出来,如果是单独的 8位、16位、32位数据访问,性能有些差别,
基本上板载的外扩 SRAM要比片内的 SRAM慢:1.42、1.10、1.55倍,但是当使用系统的
memset时,访问速度要慢:6倍。
实际测试下来,如果把 Helix的MP3软解码程序使用板载外扩 SRAM,速度会明显变慢,
声音不连续。
STM32网络收音机: http://radio.rt-thread.org
网络播放器体系结构设计网络播放器体系结构设计网络播放器体系结构设计网络播放器体系结构设计
说明说明说明说明:体系结构设计是需求说明后,软件系统划分的第一步。它会把一个潘多拉黑盒打开,当然一般是大
黑盒套小黑盒,也即把系统这个最大的潘多拉黑盒打开,按照它的功能特点进行细一些的模块划分,每个
模块在这个阶段依然是一个小的潘多拉黑盒。
如何确定模块?一般的原则是:
� 由一些用例直接派生出子系统,例如本例中的音频播放,派生音频解码库(包括 mp3 软解码、
wma软解码等)。
� 在功能上或问题域上把一些相对独立的、大粒度的功能模块划分为模块,例如本例中的网络协
议栈。
� 体系结构设计是为了在团队之间合作开发,所以在定义模块时也需要考虑到模块的规模,避免
定义得过大,导致一个团队不能够完成,或完成的时间比较慢,最后导致产品的延时。
在确定了模块的基础上再明确各模块的职责,定义相关职责接口,需要保证模块与模块间功能不相重叠。
另外,模块与模块间的依赖关系也非常重要,需要对每一个用例实例化,做到不会丢失前期的需求。
系统分解系统分解系统分解系统分解
网络播放器组件分解如下图所示(一些小型组件,例如键盘未给出)
RT-Thread 内核
STM32 Radio硬件: STM32F103ZE
DFS
虚拟文件系统
RT-Thread/GUI:
rtgui服务器线程
LwIP:
tcp线程,erx/etx线程
player 播放器
解码库:mp3,wma,
wav库
设备驱动:
wm8978, DM9000,
lcd, sd card etc
原始数据获取:
shoutcast,mms
协议,文件访问
系
统
级
子
系
统
应
用
子
系
统
STM32网络收音机: http://radio.rt-thread.org
系统级模块:
系统模块部分尽量能够采用一些成熟的平台,以避免当问题出现时,不知道到底是上层的问
题还是应用的问题。RT-Thread,一套国内主导开发的开源实时系统,它已被数家国内公
司所采用,并且还提供相配套的附属组件(而这些也是这个网络播放器需要用到的),选择
RT-Thread组件有一定风险,也有一些非常有利的地方。
RTOS Kernel:使用国内开源实时操作系统 RT-Thread。RT-Thread做为一套完善、稳定
的实时操作系统,对于这个应用完全能够适用。通过使用 RT-Thread 来作为网络收音机的
操作系统,也能够了解、学习 RT-Thread实时操作系统。
文件系统:使用 RT-Thread内置的 DFS虚拟文件系统,目前这个 DFS虚拟文件系统包装
的是 ELM FatFS。采用虚拟文件系统的好就是,底层具体的文件系统实现替换掉,但上层
的应用可以保持不变(在 STM32 网络收音机的开发过程中就出现过用 ELM FatFS 替换
EFSL 文件系统的例子,而上层应用代码没动过任何一行),而且 DFS 向上提供的接口是
POSIX兼容的,这对代码的可移植性更有好处。
TCP/IP协议栈:LwIP轻型 TCP/IP协议栈,与 RT-Thread的整合也几乎是无缝的,当前
的 RT-Thread/STM32 也已经支持 ENC28J60 接口的驱动,只需要把它改成 DM9000A
即可。LwIP 协议栈对应用层提供了
的 socket 接口,并且 LwIP 自身支持多种协议,
例如 ICMP,IGMP,DHCP,DNS,PPP 等,有 DNS、DHCP 协议也能够直接应用到这
个网络收音机中,这样可以不用手动的设置 IP地址。
图形用户界面:RT-Thread/GUI,这个是 RT-Thread开发的多窗口多线程图形用户界面,
应用于 STM32网络收音机也是非常好的。网络收音机项目包含一个 320x240的 TFT屏,
5向导航键,触摸屏,采用键盘与触摸相结合的操作方式。
USB Mass Storage:U盘,采用 STM32 USB固件库来实现一个完整的 U盘,使得能够
直接从电脑上操作播放器上的文件。在通过 U 盘操作文件时,系统自动停止播放,关闭打
开的文件,不再操作文件,当 USB连接断开时,系统自动复位以恢复到初始状态。
应用部分模块
其中应用部分的模块可进一部分分解成如下表示的组成图:
STM32网络收音机: http://radio.rt-thread.org
音频解码库音频解码库音频解码库音频解码库 (codec)
从输入的数据中进行软件解码,给出相应的 PCM 数据;另外音频数据一般会携带 TAG 信
息,所以音频解码库也需要提供相应的 TAG信息,播放进度等信息。
音频解码库主要包括三个解码:MP3解码,WAV解码(除掉文件头以后也就是 PCM数据),
WMA解码。
网络缓冲区管理网络缓冲区管理网络缓冲区管理网络缓冲区管理 (net buffer)
这个子系统用于取得原始的音频数据,即未解码前的原始音频数据。这部分包括网络方式获
取数据(包括 ShoutCase协议方式和MMS协议方式)。
播放器播放器播放器播放器 (player)
播放器部分用于控制播放的过程,例如开始播放,停止播放,以及响应用户的操作请求。这
部分按照它的功能,又细分为两个部分:
播放器 UI,即播放器的界面,用于获取用户的输入请求。为了满足一些操作上的便利,它
还将提供一些额外的功能。
播放器控制,这个是音频播放的主控部分。
STM32 Radio
网络播放器系统
音频解码库
WMA解码
WAV解码
MP3解码
net buffer模块
mms协议
Shoutcase协议
播放器
播放器 UI
播放器控制
STM32网络收音机: http://radio.rt-thread.org
各各各各模块模块模块模块间间间间的的的的依赖关系依赖关系依赖关系依赖关系
此处主要给出了播放器核心模块间的依赖关系,而对于 RTOS底层服务间的关系没给出(因
为基本都会有关联)。
播放器模块需通过 net buffer模块或文件系统模块获得音频的原始数据;
播放器模块需要传递数据报文给 Codec模块,Codec模块转换成 PCM数据后再发回数据
给播放器模块。
播放器模块在系统中需要使用 snd驱动进行音频的播放,lcd驱动显示
net buffer模块在获取网络音频流时需要使用 LwIP协议栈获得网络报文;
用例需求在设计中的反映用例需求在设计中的反映用例需求在设计中的反映用例需求在设计中的反映
1、网络播放器通过网线连接到路由器,通过路由器连接到互联网中。
网络播放器支持 DHCP方式从路由器获取 IP v4地址。
2、网络播放器采用 5V电源供电(能够兼容从电脑 USB取电)。
3、用户可通过网络播放器收听网络上的音频流:
LwIP:
tcp线程,erx/etx线程
播放器: player
Codec:
mp3, wma, wav
Net buffer:
shotcast, mms 协
议
snd 驱动
key, touch驱
动
系统驱动
lcd 驱动
LwIP网络协议栈
初始化
路由器
获取 IP地址
STM32网络收音机: http://radio.rt-thread.org
用户可播放网络上的 mp3、wma音频,能够支持 http音频流和 mms音频流协议。为了
更好地支持网络音频流的播放,网络播放器支持音频流的缓冲播放。
4、用户可通过网络播放器播放用户提供的 SD卡上的数据。
用户可播放 SD卡上的mp3、wav、wma歌曲。当用户插上 USB线后,用户通过电
脑操作能够操作 SD卡上的文件(做为 U盘操作文件)。
5、用户可通过网络播放器上的按钮和触摸屏进行网络播放器的操作。
网络播放器能够接收按键进行播放器的操作,进行下一电台,上一电台,播放开始,播
放停止,声音增大,声音减小的操作。
网络播放器能够通过自带的液晶显示屏给出播放的状态。用户能够使用自带触摸屏对网
络播放进行操作:选择相应的功能、播放 SD卡上的音频文件,播放互联网上的音频流。
player模块 文件系统
获取音频数据
Codec
音频解码
PCM数据
播放 PCM音频
player模块 net buffer
获取音频数据
路由器 LwIP协议栈 Codec
音频解码
PCM数据
播放 PCM音频
缓冲音频流
音频原始数据
STM32网络收音机: http://radio.rt-thread.org
7、 用户可通过网络播放器自带的喇叭或耳机收听网络电台;
按键、触摸 播放模块 RT-Thread/GUI
界面操作
发送事件
响应事件
STM32网络收音机: http://radio.rt-thread.org
网络播放器网络播放器网络播放器网络播放器模块设计模块设计模块设计模块设计
说明说明说明说明:
模块设计在整个设计过程中相当于把一个个小的潘多拉黑盒打开,清晰化。这个过程注重于模块相互之间
的接口,模块内部的清晰
性,能够真正的达到相互之间的并行开发。
但是嵌入式软件系统的设计和通常意义的软件系统设计是不相同的。在通用系统中,通常考虑的是如何顺
序的完成一个任务,所以大多数关注点在于如何完成这个任务,如何把一个复杂的任务进行层层分解,由
繁到简的划分模块,实现模块。
在嵌入式系统上,当引入了实时操作系统后,编程模式需要转换成并行任务的方式解决问题,即包括了线
程、任务的模式。所以在进行嵌入式软件系统设计时,不仅需要考虑通常意义上的对象划分,对象与对象
之间的关系,更需要考虑系统的动态行为:线程的划分,线程的状态图跃迁(如果复杂的话),线程间的消
息序列等。
在下面的设计中,按照上面的原则,将从静态和动态的角度来分析系统模块的实现。
Player模块设计模块设计模块设计模块设计
播放器模块是网络收音机中的核心模块,相对来说,这个模块功能涉及比较多,所以在目前
的基础上把它进行拆分形成:播放控制模块和播放 UI模块两个模块。
静态视图静态视图静态视图静态视图
播放器模块
播放控制 播放 UI
播放列表
触摸屏校正
电台更新
电台列表
播放器界面
STM32网络收音机: http://radio.rt-thread.org
播放播放播放播放 UI模块模块模块模块主要涉及到界面相关的操作,主要又分为:
- 播放器界面
这个界面中将实现歌曲的播放、停止;下一首,上一首;音量调节
- 播放列表
用于管理当前播放歌曲的列表
- 触摸屏校正
用于对触碰屏进行校正
- 电台列表
从文件中读取存储的电台列表,提供给用户进行选择。
- 电台更新
从互联网更新电台列表。(网址是:http://radio.rt-thread.org)
播放控制模块播放控制模块播放控制模块播放控制模块进行实际的播放操作,在播放的过程中由它调用 Codec库进行相应的音频软
解码。
这个模块提供的接口如下
播放控制模块
名称 描述
player_play_req 请求播放指定的文件名。
player_radio_req 请求播放指定服务地址的电台。
player_stop_req 请求停止播放。
player_is_playing 返回当前处于停止或播放状态。
播放 UI模块接口
名称 描述
player_ui_freeze 冻结播放界面<用于 USB联机时>
player_notify_play 指示 UI已经开始播放
player_notify_stop 指示 UI播放已经停止
player_notify_info 指示 UI当前正在播放歌曲的信息
player_notify_functionview 请求 UI进入功能菜单
player_set_position 设置当前播放歌曲的正在播放的位置
player_set_title 设置当前播放歌曲的标题
player_set_buffer_status 设置播放器是否处于缓冲状态
线程视图线程视图线程视图线程视图
播放器控制线程:线程名 ply_bg (player background)
播放器控制线程接收来自用户的命令,进行播放文件或播放音频流操作。播放器在接收到播
放命令时,它将先识别出是文件或流,如果是文件,将直接调用文件系统的接口进行文件访
问(不再做专门的音频缓冲)。如果是网络音频流将从 net buffer模块获得内容。
STM32网络收音机: http://radio.rt-thread.org
播放器 UI线程:线程名 ply_ui (player UI)
播放器 UI线程是根据 RT-Thread/GUI特点来设计的,因为 RT-Thread/GUI的一个应用
需要一个独立的上下文环境。从通常的 UI、数据分离的角度出发,也需要把播放器 UI 和
播放器控制分离开。播放器 UI 线程将实现所有绘图显示操作,并且能够接收 UI 上的人机
交互事件,例如按键和触摸屏事件。
线程交互的主要是ply_ui和ply_bg相互间的请求和
。而值得注意的是ply_bg与nbuf
线程间的行为。
用例实现视图用例实现视图用例实现视图用例实现视图
3、用户可通过网络播放器收听网络上的音频流:
Player UI Player Control codec net buffer
player_radio_req
ice_mp3
net_buf_read
net_buf_start_job
snd driver
device write
decode
done
player_notify_start
player_notify_stop
write done and release memory block
播放电台时的模块间消息序列图
Thread: ply_ui Thread: ply_bg
notify_event: 通知 stream事件
输入设备事件
(键盘、触摸)
tx_done: 通知 PCM数据发生完成
Thread: nbuf
音频数据缓冲
读取数据
request: 对控制线程发起请求
snd设备
(DMA模式)
GUI event
STM32网络收音机: http://radio.rt-thread.org
Player UI通过 Player Control提供的接口:player_radio_req来发起一条播放请求,
其中包含了电台网址和电台名称;Player Control模块在验证请求无误后,先调用 Player
UI模块提供的接口 player_notify_start通知 Player UI播放已经开始。接下来,Player
Control模块根据请求的情况调用 Codec中的 ice_mp3接口启动播放。Codec模块先采
用 shoutcast协议打开相应的 TCP连接,然后向 net buffer模块请求开始一项缓冲任务,
同时发起一条读取数据请求。当 net buffer 缓冲数据达到一定程度时,它将返回 Codec
预读取的数据。Codec 拿到相应的数据后开始解码获得 PCM 数据写入到 snd 驱动中进行
播放。
4、用户可通过网络播放器播放用户提供的 SD卡上的数据。
Player UI Player Control codec Filesystem
player_play_req
mp3/wav
read
open
snd driver
device write
decode
done
player_notify_start
player_notify_stop
write done and release memory block
播放MP3或WAV文件时模块与模块间的消息序列图
Player UI 通过 Player Control 提供的 player_play_req 接口来发起一条播放请求;
Player Control 模块在验证请求无误后,先调用 Player UI 模块提供的接口
player_notify_start通知 Player UI播放已经开始。接下来,Player Control模块根据
请求的情况调用 Codec中的 MP3播放或WAV播放。Codec模块则根据请求的文件,打
开文件并读取文件,然后解码,最终获得 PCM数据写入到 snd驱动中。
补充的停止播放用例(3、4用例都包含这个过程)
STM32网络收音机: http://radio.rt-thread.org
Player UI Player Control codec
player_stop_req
decoding
doneplayer_notify_stop
set is_playing = FALSE is decoding and
playing
停止播放的模块间消息序列图
Player UI通过调用 Player Control提供的 player_stop_req接口通知一次播放过程结
束。Player Control 此时应该是一直解码播放的过程,当它得到播放停止的状态时,将停
止余下的软解码播放过程,并调用 Player UI提供的 player_notify_stop接口通知 Player
UI 模块,这次播放过程已经结束。当播放结束时,Player UI 可以抉择是否进行下一步行
动,例如播放下一首歌曲。
播放时的用例实例化补充
ply_bg线程当要解码 PCM数据时,将向memory pool请求内存块,如果内存块已经用
完,将被挂起。否则,它将获得内存块以放置 PCM数据。
而后 ply_bg线程把包含 PCM数据的内存块写入到 snd抽象设备中。(线程会立刻返回,
ply_bg snd driver
请求内存块
无内存,线程挂起
m
em
o
ry
p
o
ol
DMA写入数据
请求内存块
写入完成,释放内存块
唤醒线程
继续解码
STM32网络收音机: http://radio.rt-thread.org
继续制造解码出更多的 PCM数据)
当 snd 抽象设备写入完成后,将调用 ply_bg 设置的回调函数,释放写入的内存块。如果
此时,播放线程因为把 memory pool 里的内存块都用光而挂起时,将唤醒播放线程,让
播放线程继续软解码更多的 PCM。
Net buffer模块设计模块设计模块设计模块设计
网络缓冲管理模块主要用于获取网络上的音频流数据并把它放置到自己的缓冲区中进行管
理,网络电台多数采用 64kbps- 128kbps的码率进行音频流传输。按照 128kbps,缓存 20秒
需要用到 320kB的内存量。
静态视图静态视图静态视图静态视图
网络缓冲管理模块的分解图
这个模块主要包括两个部分,一个是缓冲区管理,用于把数据进行缓冲。
缓冲区管理的接口如下:
名称 描述
net_buf_read 从 net buffer中读取指定长度的数据。
net_buf_start_job 请求 net buffer开始一项工作。
net_buf_stop_job 请求 net buffer停止一项工作。
net_buf_get_usage 获得 net buffer中已获得的数据缓冲长度;
一个是一些网络协议的实现,包括 http协议,shoutcast协议和 mms协议。shoutcast协议是
基于 http协议添加自己固定的信息。http协议并不是网络收音机需要支持的格式。为了便于
管理几种协议实例,定义如下接口:
http协议
名称 描述
http_session_open 根据 URL打开相应的协议会话,成功返回相应句柄,否则 NULL
http_session_read 从协议句柄中读取指定的数据
http_session_seek 对本次会话向前跳过(忽略)指定的数据字节长度
net buffer模块
缓冲管理 http协议
shoutcast协议
mms协议
STM32网络收音机: http://radio.rt-thread.org
http_session_close 关闭本次协议会话。
shoutcast协议
名称 描述
shoutcast_session_open 根据 URL打开相应的协议会话,成功返回相应句柄,否则 NULL
shoutcast_session_read 从协议句柄中读取指定的数据
shoutcast_session_seek 对本次会话向前跳过(忽略)指定的数据字节长度
shoutcast_session_close 关闭本次协议会话。
mms协议
名称 描述
mms_session_open 根据 URL打开相应的协议会话,成功返回相应句柄,否则 NULL
mms_session_read 从协议句柄中读取指定的数据
mms_session_seek 对本次会话向前跳过(忽略)指定的数据字节长度
mms_session_close 关闭本次协议会话。
线程线程线程线程视图视图视图视图
这个模块提供一个线程用于获取网络数据:线程名 nbuf。线程的状态转换图如下图
Buffering
Suspend
Stopping
Stopped
Start
Buffer Full
Has Buffer
Finished
Stop
Stop Done
Stop
状态描述
名称 描述
stopped 停止状态,nbuf线程等待在自己的消息队列中以接收新的命令。
buffering 缓冲状态,正在从网络中读取数据进行缓冲。
suspend 挂起状态,保存数据达到缓存的最大空间,线程处于挂起状态等待空间释放。
stopping 预停止状态,停止请求已经发送,但线程还未达到停止状态。
这个线程与其他模块间的消息序列图
STM32网络收音机: http://radio.rt-thread.org
nbufply_bg tcp
net_buf_start_job
net_buf_read
job.fetch
ply_bg线程调用 net buffer提供的 net_buf_start_job接口以开始一个缓冲工作,此时 nbuf线
程将唤醒(假设它先处于 stopped 状态)。nbuf 线程唤醒后开始调用 job 的 fetch 接口向 tcp
线程获取数据报文。ply_bg线程在发送完开始工作后,它将紧接着调用 net_buf_read接口读
取网络数据,但此时 net buffer还是 buffering状态,ply_bg线程将被挂起。当 nbuf线程获得
足够的网络数据时,它将唤醒 ply_bg线程并给它足够的数据用于播放。当 nbuf线程读取到
的网络数据把缓冲区都填满时,它将挂起以等待缓冲区的数据读取掉释放出空间出来。当
ply_bg 线程读出数据,缓冲去遗留的剩余数据小于一个数值时,它将唤醒 nbuf 线程继续获
取接下来的网络数据。
Codec模块设计模块设计模块设计模块设计
codec模块提供对 wav数据,mp3数据和 wma数据的解码操作。除了解码外,它也需要
包括提供一些文件格式信息的分析。
静态视图静态视图静态视图静态视图
- wav格式,未压缩的 PCM数据,但有文件头信息;
- mp3格式,压缩格式,包含 ID3Tag信息;
- wma格式,压缩格式
Codec模块
wav mp3 wma
STM32网络收音机: http://radio.rt-thread.org
相应的接口包括:
名称 描述
wav 根据提供的文件名播放 wav文件。
mp3 根据提供的文件名播放 mp3文件。
ice_mp3 根据提供的电台地址和电台名播放 shoutcast电台。
http_mp3 根据提供的地址播放 http方式的 mp3流。
wma 根据提供的文件名播放 wma文件。
mms_wma 更加提供的电台地址和电台名播放 mms电台。
线程视图线程视图线程视图线程视图
这个模块不提供独立的线程。
STM32网络收音机: http://radio.rt-thread.org
网络播放器网络播放器网络播放器网络播放器详细设计详细设计详细设计详细设计
说明说明说明说明:
详细设计是一个非常底层的环节,通常来说,这也是一个“各显神通”的环节:和实现人员素质密切相关
的环节:
在实现中,或许由于实现人员水平参差不齐,里面隐藏的 BUG 数目也不尽相同,当完成后还能够依据一
些其他手段进行修补,例如开发人员间的代码评审,单元测试,集成测试等。
模块详细说明模块详细说明模块详细说明模块详细说明
播放控制模块:player_bg.c
接口详细说明
名称 描述
player_play_req void player_play_req(const char* fn);
请求播放指定的文件名,参数 fn 为播放的文件名,给出的应该是
绝对路径。
在这个函数中,将发送类型为:
PLAYER_REQUEST_PLAY_SINGLE_FILE的消息给 ply_bg线程。
player_radio_req void player_radio_req(const char* fn, const char* station);
请求播放指定服务地址的电台,参数 fn为播放电台的服务器地址,
参数 station为电台名称,用于 UI后续的 UI显示。
在这个函数中,将发送类型为:
PLAYER_REQUEST_PLAY_SINGLE_FILE的消息给 ply_bg线程。
这个消息和 player_play_req不同的是,它也会填充 station字段。
player_stop_req void player_stop_req();
请求停止播放。
在这个函数中,会设置变量 is_playing为 RT_FALSE;这个变量将
在 ply_bg线程解码一帧后检查一次。
player_is_playing rt_bool_t player_is_playing();
返回当前处于停止或播放状态,如果是播放状态返回 RT_TRUE,
如果是停止状态返回 RT_FALSE;
其他说明:
void player_init(void);
播放器模块的初始化函数,会创建 ply_bg线程及初始化 ply_ui。
STM32网络收音机: http://radio.rt-thread.org
void player_thread(void* parameter);
ply_bg线程入口函数。ply_bg线程启动后主要等待在消息队列(player_thread_mq)上,
当收到消息时(当前仅对 PLAYER_REQUEST_PLAY_SINGLE_FILE消息进行处理),将
根据消息中的 fn成员判断是 wav、mp3还是电台(http开头)。
播 放 UI 模 块 : player_ui.c, calibration.c, play_list.c, station_list.c,
radio_list_update.c
player_ui包含了播放器的主界面
通常 player_ui,即播放器 workbench,包括了两个视图:
home视图,用于显示播放器主界面
function 视图,用于显示功能菜单(可以通过在播放器主界面点击触摸屏的 RT-Thread
LOGO部位或不播放时按右方向键盘进入功能视图)
功能列表定义在 function_list数组变量中,采用的是一个 rtgui_list_view的形式,当前
没添加上图标的支持,能做的扩展是加入图标的支持,并变换显示格式为图标显示,将变成
手机上常见的九宫格方式显示。
接口的详细描述:
名称 描述
player_ui_freeze void player_ui_freeze(void);
冻结播放界面<用于 USB 联机时>,这个函数会给发送
id=PLAYER_REQUEST_FREEZE的命令。
player_notify_play void player_notify_play(void);
指示 UI 已经开始播放,这个函数会向 home 视图发送
PLAYER_REQUEST_PLAY_SINGLE_FILE 的命令以标识一首
歌曲播放开始。
player_notify_stop void player_notify_stop(void);
指示 UI 播放已经停止,这个函数会向 home 视图发送
PLAYER_REQUEST_STOP的命令以标识一首歌曲播放停止。
player_notify_info void player_notify_info(const char* information);
指示 UI当前正在播放歌曲的信息,这个函数会更新当前播放信
息,并向 home 视图发送 PLAYER_REQUEST_UPDATE_INFO
的命令以请求重新绘制播放信息。
player_notify_functionview void player_notfiy_functionview(void);
请求 UI 进入功能菜单,这个函数会向 home 视图发送
PLAYER_REQUEST_FUNCTION_VIEW 的命令以请求进入功
能视图<这个函数被 info workbench调用>。
STM32网络收音机: http://radio.rt-thread.org
player_set_position void player_set_position(rt_uint32_t position);
设置当前播放歌曲的正在播放的位置,这个函数会更新当前播
放歌曲正在播放的位置。
player_set_title void player_set_title(const char* title);
设置当前播放歌曲的标题,这个函数会更新当前播放信息,并
向 home视图发送 PLAYER_REQUEST_UPDATE_INFO 的命令
以请求重新绘制播放信息。
player_set_buffer_status void player_set_buffer_status(rt_bool_t buffering);
设置播放器是否处于缓冲状态,这个函数会更新当前播放缓冲
状态,并向 home视图发送 PLAYER_REQUEST_UPDATE_INFO
的命令以请求重新绘制播放信息。
play_list提供的是播放列表的管理
每个播放项由如下结构描述
struct play_item
{
char title[40]; /* 播放项标题或名称 */
char *fn; /* 播放项指向的文件名或 URL */
rt_uint32_t duration; /* 歌曲的总长度,单位是秒 */
};
名称 描述
play_list_start struct play_item *play_list_start(void);
开始一个播放列表,播放列表将从 0的位置开始给出播放项,并
返回第 0个播放项。
play_list_items rt_uint32_t play_list_items(void);
获得播放列表中播放项的个数。
play_list_item struct play_item* play_list_item(rt_uint32_t n);
获得第 n个播放项,N是从 0开始的正整数。
play_list_current struct play_item* play_list_current(void);
获得当前的播放项。
play_list_set_current void play_list_set_current(rt_uint16_t n);
设置当前的播放项为第 n个播放项。
play_list_get_current rt_uint16_t play_list_get_current(void);
获得当前的播放项。
play_list_next struct play_item* play_list_next(int mode);
获得下一个播放项。
play_list_prev struct play_item* play_list_prev(int mode);
获得上一个播放项。
STM32网络收音机: http://radio.rt-thread.org
play_list_get_mode int play_list_get_mode(void);
获得播放列表的模式,分为
PLAY_LIST_SINGLE – 仅播放单一歌曲
PLAY_LIST_REPEAT – 重复顺序播放
PLAY_LIST_RANDOM – 随机播放
play_list_append void play_list_append(char* fn);
添加文件到播放列表中。
play_list_append_radio void play_list_append_radio(const char* url, const char* station);
添加电台到播放列表中。
play_list_append_directory void play_list_append_directory(const char* path);
添加目录中所有支持的媒体文件到播放列表中。
play_list_append_m3u void play_list_append_m3u(const char* file);
添加 m3u列表中的歌曲到播放列表中。
station_list提供的是电台列表的选择
电台列表及其中的项定义为
struct station_item
{
char title[40]; /* 电台标题 */
char url[128]; /* 电台网址 */
};
struct station_list
{
rt_uint32_t count; /* 列表中的电台项数目 */
struct station_item* items; /* 电台项列表指针 */
};
接口详细说明
名称 描述
station_list_create struct station_list* station_list_create(const char* fn);
这个函数根据指定的电台列表文件创建相应的电台列表。
station_list_destroy void station_list_destroy(struct station_list* list);
这个函数删除 station_list_create创建出来的电台列表。
station_list_select struct station_item* station_list_select(struct station_list* list, struct
rtgui_workbench* workbench);
这个函数在指定的 workbench上提供了电台选择的视图,并且是以
模态方式显示的视图。参数 list为 station_list_create创建的电台列
表,参数 workbench为父 workbench。
当用户选择了电台时,将返回电台项,否则返回 NULL。
net buffer模块模块模块模块: netbuffer.c, wav.c, mp3.c
接口详细说明如下:
STM32网络收音机: http://radio.rt-thread.org
名称 描述
net_buf_init void net_buf_init(rt_size_t size);
初始化 net buffer,参数 size为缓冲区的大小。
net_buf_read rt_size_t net_buf_read(rt_uint8_t* buffer, rt_size_t length);
从 net buffer中读取指定长度的数据。
参数 buffer用于保存读取到的数据,参数 length指示出最大能够保
存的长度。
返回实际读取到的长度。
net_buf_start_job int net_buf_start_job(rt_size_t (*fetch)(rt_uint8_t* ptr, rt_size_t len,
void* parameter),
void (*close)(void* parameter),
void* parameter);
请求 net buffer 开始一项工作,它会更改 net buffer 的状态为
buffering。
参数 fetch给出获取数据的函数指针,参数 close给出关闭连接的函
数指针,参数 parameter用于提供给 fetch、close函数参数。
成功返回 0,失败返回-1(例如当前状态并不是 stopped状态)。
net_buf_stop_job void net_buf_stop_job(void);
请求 net buffer停止一项工作。如果 net buffer当前状态是 suspend
状态,说明 nbuf线程处于挂起状态,会先唤醒 nbuf线程,然后更
改 net buffer的状态为 stopping。
net_buf_get_usage int net_buf_get_usage(void);
获得 net buffer中已获得的数据缓冲长度;
其他说明:
static void net_buf_thread_entry(void* parameter);
这个函数是 nbuf线程的入口,它运行后会等待_netbuf_mq上的消息。当有请求达到时,
将开始设定的工作(net_buf_do_job函数)。
STM32上有板载的外扩 SRAM,但存在两个限制:
- 速度比较慢,特别当有芯片的读取预测时,速度会慢很多。
- 外扩的 SRAM做 DMA有限制,当进行 DMA传送时,FSMC外设将不能够被主机再行
访问,否则出 fault错误。
基于这个原因,在 net buffer模块中添加了片内的一个memoy pool支持:
void sbuf_init(void);
初始化片内 SRAM内存块区;总计大小定义为
#define MP3_DECODE_MP_SZ 2