跳转到内容

音频流连接

音频流连接

本节介绍如何配置 ESP32 连接网络音频流(网络电台)。学习完成后,您将能够:

  • 理解音频流的连接原理和缓冲机制
  • 获取和配置网络电台的 URL
  • 处理音频流的连接、断开和重连
  • 获取流媒体的元数据信息(歌曲标题等)

在开始本节之前,请确保:

  • 已完成 ESP8266Audio 库的配置
  • ESP32 已连接 WiFi
  • I2S 音频硬件已接线并验证
网络电台服务器 ──→ HTTP/ICY 协议 ──→ ESP32 WiFi ──→ 音频缓冲区 ──→ 解码器 ──→ I2S 输出 ──→ 扬声器
│ │
├── MP3 流数据包 ──────────────────────────────────────────────────────────────────→
├── AAC 流数据包 ──────────────────────────────────────────────────────────────────→
├── 流元数据(歌曲名、电台信息)──────────────────────────────────────────────────────→
└── TCP 控制信号(ACK、重传)────────────────────────────────────────────────────────→
协议描述典型使用场景
HTTP Streaming标准的 HTTP 渐进式下载MP3 文件流
ICY ProtocolShoutCast/IceCast 协议网络电台直播流
HTTPS Streaming加密的 HTTP 流需要加密传输的电台
RTSP实时流传输协议IP 摄像头、VoIP

ESP8266Audio 库的 AudioFileSourceHTTPStream 类支持 HTTP 和 ICY 协议,覆盖了大多数网络电台的需求。

网络电台使用 ICY(I Can Yell)协议扩展 HTTP 头部来传输流媒体和元数据:

HTTP/1.0 200 OK
icy-notice1: This stream requires <br>Winamp
icy-notice2: SHOUTcast Distributed Network Audio Server/Linux v1.9.8
icy-name: 电台名称
icy-genre: Pop Rock
icy-url: http://radio-station-url
content-type: audio/mpeg
icy-pub: 1
icy-br: 128 ← 比特率 (kbps)
icy-metaint: 32768 ← 元数据间隔 (字节)

音频二进制数据紧随 HTTP 头部之后,以连续的数据块发送。

// AudioFileSourceHTTPStream 内部缓存机制
// - TCP 接收缓冲区:4KB(默认)
// - 应用层缓冲区:16KB(对流数据)
// - 当缓冲区数据小于阈值时触发 TCP 请求新数据
// - 足够的缓冲可以减少网络抖动导致的卡顿

缓冲区设置建议

使用场景建议缓冲区大小说明
高速 WiFi4KB(默认)延迟低,无需大缓冲区
低速 WiFi16KB防止 WiFi 抖动导致的音频中断
远程流媒体32KB高延迟网络下保证流畅播放
#include <Arduino.h>
#include <WiFi.h>
#include <AudioOutputI2S.h>
#include <AudioGeneratorMP3.h>
#include <AudioFileSourceHTTPStream.h>
#include <AudioFileSourceICYStream.h> // 支持 ICY 协议的增强版
// WiFi 和 I2S 配置
const char* ssid = "您的WiFi名称";
const char* password = "您的WiFi密码";
#define I2S_BCLK 26
#define I2S_LRCK 25
#define I2S_DIN 22
// 网络电台 URL(示例 - 替换为实际电台)
const char* streamURL = "http://stream.radio.co:80/listen.mp3";
AudioGeneratorMP3* mp3;
AudioFileSourceICYStream* file;
AudioOutputI2S* output;
// 辅助函数:显示流信息
void showStreamInfo(const char* info) {
Serial.printf("[流信息] %s\n", info);
}
void setup() {
Serial.begin(115200);
// 连接 WiFi
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(1000);
Serial.print(".");
attempts++;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("\nWiFi 连接失败!");
return;
}
Serial.println("\nWiFi 已连接");
Serial.printf("IP 地址: %s\n", WiFi.localIP().toString().c_str());
// 初始化 I2S 输出
output = new AudioOutputI2S();
output->SetPinout(I2S_BCLK, I2S_LRCK, I2S_DIN);
output->SetVolume(50);
output->begin();
// 创建 ICY 流源并连接
file = new AudioFileSourceICYStream();
file->open(streamURL);
// 检查连接状态
if (!file->isOpen()) {
Serial.println("无法连接到音频流!");
return;
}
// 创建 MP3 解码器并开始播放
mp3 = new AudioGeneratorMP3();
mp3->begin(file, output);
Serial.println("音频流播放中...");
Serial.printf("连接至: %s\n", streamURL);
}
void loop() {
if (mp3->isRunning()) {
if (!mp3->loop()) {
mp3->stop();
Serial.println("音频流已结束");
delay(3000);
// 自动重连
Serial.println("尝试重新连接...");
file->open(streamURL);
mp3->begin(file, output);
}
}
// 可选:显示流元数据(每 10 秒)
static unsigned long lastMetaCheck = 0;
if (millis() - lastMetaCheck > 10000) {
lastMetaCheck = millis();
if (file->getStreamTitle()) {
Serial.printf("当前播放: %s\n", file->getStreamTitle());
}
}
}

说明

  • 使用 AudioFileSourceICYStream(而非 AudioFileSourceHTTPStream)以获得更好的元数据处理
  • 连接失败时输出错误信息并停止播放
  • 支持自动重连机制
  • 定期获取并显示流标题(当前播放的歌曲名)
// AudioFileSourceICYStream 的增强功能
class ICYStreamWithMetadata : public AudioFileSourceICYStream {
public:
// 重载元数据回调
virtual void MDCallback(const char* type, bool isUnicode,
const char* string) override {
if (strcmp(type, "StreamTitle") == 0) {
Serial.printf("🎵 当前播放: %s\n", string);
} else if (strcmp(type, "StreamUrl") == 0) {
Serial.printf("🔗 流URL: %s\n", string);
}
}
};
void monitorStreamStatus() {
static int lastByteCount = 0;
int currentByteCount = file->getByteCount();
int bytesReceived = currentByteCount - lastByteCount;
lastByteCount = currentByteCount;
// 计算接收速率
float kbps = (bytesReceived * 8) / 10.0; // 每 10 秒调用一次
Serial.printf("已接收: %d KB | 速率: %.1f kbps\n",
currentByteCount / 1024, kbps);
// 检查是否卡住(10 秒内没有数据)
if (bytesReceived == 0) {
Serial.println("⚠️ 警告: 音频流可能已断开");
}
}
音频格式带宽需求推荐 WiFi 信号强度
MP3 64kbps> 80 kbpsRSSI > -75 dBm
MP3 128kbps> 160 kbpsRSSI > -70 dBm
AAC 96kbps> 120 kbpsRSSI > -75 dBm
FLAC 16bit/44kHz> 800 kbpsRSSI > -65 dBm
指标良好一般
RSSI> -60 dBm-60 到 -75 dBm< -75 dBm
延迟< 50ms50-150ms> 150ms
抖动< 10ms10-30ms> 30ms

症状: 播放过程中出现间歇性的声音中断或杂音

原因:

  • WiFi 信号不稳定
  • 音频缓冲区过小
  • 网络带宽不足

解决方案:

  1. 检查 WiFi RSSI 值(应 > -70 dBm)
  2. 添加 ESP32 天线或改善 WiFi 覆盖
  3. 降低音频码率(使用 64kbps 而非 128kbps 的电台)
  4. 增加缓冲区大小(如果库支持自定义)

症状: 连接失败,file->isOpen() 返回 false

原因:

  • URL 格式错误
  • 电台服务器不支持直接连接
  • 防火墙或网络限制

解决方案:

  1. 使用 MQTT Explorer 或桌面播放器验证 URL 可用性
  2. 检查 URL 是否为直接流 URL(而非网页地址)
  3. 尝试使用 HTTP 而非 HTTPS
  4. 确认电台的音频格式与解码器匹配

本节介绍了 ESP32 如何连接网络音频流:

  1. 流协议:HTTP Streaming 和 ICY 协议是网络广播的标准
  2. 连接流程:创建文件源 → 打开 URL → 创建解码器 → 开始播放
  3. 元数据:通过 ICY 协议获取歌曲标题和电台信息
  4. 网络要求:稳定的 WiFi 连接(RSSI > -70 dBm)和足够带宽