1818 lines
63 KiB
Markdown
1818 lines
63 KiB
Markdown
# 前端接口API参考
|
||
|
||
## 目录
|
||
|
||
- [概述](#概述)
|
||
- [前端管理器](#前端管理器)
|
||
- [FrontendManager](#frontendmanager)
|
||
- [前端配置](#前端配置)
|
||
- [生命周期管理](#生命周期管理)
|
||
- [音频设备管理](#音频设备管理)
|
||
- [设备信息](#设备信息)
|
||
- [设备选择](#设备选择)
|
||
- [音频流控制](#音频流控制)
|
||
- [网络通信](#网络通信)
|
||
- [服务发现](#服务发现)
|
||
- [会话管理](#会话管理)
|
||
- [音频流传输](#音频流传输)
|
||
- [音频编解码](#音频编解码)
|
||
- [编解码器接口](#编解码器接口)
|
||
- [缓冲区管理](#缓冲区管理)
|
||
- [事件处理](#事件处理)
|
||
- [事件类型](#事件类型)
|
||
- [事件监听器](#事件监听器)
|
||
- [工厂函数](#工厂函数)
|
||
- [基础工厂](#基础工厂)
|
||
- [预配置组合](#预配置组合)
|
||
- [实现示例](#实现示例)
|
||
- [本地音频处理](#本地音频处理)
|
||
- [远程音频传输](#远程音频传输)
|
||
- [网络服务发现](#网络服务发现)
|
||
- [性能优化](#性能优化)
|
||
- [延迟优化](#延迟优化)
|
||
- [带宽优化](#带宽优化)
|
||
- [跨平台考虑](#跨平台考虑)
|
||
- [网络兼容性](#网络兼容性)
|
||
- [设备兼容性](#设备兼容性)
|
||
|
||
## 概述
|
||
|
||
前端接口模块提供了应用程序与音频后端引擎通信的统一接口,支持本地和网络通信。前端接口封装了设备管理、网络发现、音频传输和编解码等复杂功能,使应用程序能够简单地集成高级音频处理能力。
|
||
|
||
核心特性:
|
||
- **本地通信**: 与同一设备上运行的音频引擎通信
|
||
- **网络通信**: 与远程音频引擎进行低延迟通信
|
||
- **自动发现**: 通过mDNS/Bonjour自动发现网络上的音频服务
|
||
- **设备管理**: 枚举、配置和监控音频设备变化
|
||
- **编解码处理**: 支持多种音频编解码器,包括无损和有损压缩
|
||
- **低延迟传输**: 针对实时音频优化的网络传输
|
||
- **安全连接**: 支持加密的音频流和控制通道
|
||
|
||
前端接口的架构示意图:
|
||
|
||
```
|
||
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
|
||
│ 应用程序 │ │ 音频引擎进程 │
|
||
│ │ │ │
|
||
│ ┌─────────────────────────┐ │ │ ┌─────────────────────────┐ │
|
||
│ │ 前端管理器 │ │ │ │ 音频引擎 │ │
|
||
│ │ FrontendManager │ │ │ │ │ │
|
||
│ └───────────┬─────────────┘ │ │ └───────────┬─────────────┘ │
|
||
│ │ │ │ │ │
|
||
│ ▼ │ │ ▼ │
|
||
│ ┌───────────────────────────┐ │ │ ┌───────────────────────────┐ │
|
||
│ │ 引擎代理 │ │ │ │ 通信管理器 │ │
|
||
│ │ EngineProxy │ │ │ │ CommunicationManager │ │
|
||
│ └───────────┬───────────────┘ │ │ └───────────┬───────────────┘ │
|
||
│ │ │ │ │ │
|
||
│ │ │ │ │ │
|
||
│ ┌───────────▼───────────────┐ │ │ ┌───────────▼───────────────┐ │
|
||
│ │ 通信管理器 │ │ │ │ ZeroMQ通信 │ │
|
||
│ │ (ZeroMQ/共享内存) ├──┼──────┼─►│ (控制通道) │ │
|
||
│ └───────────────────────────┘ │ │ └───────────────────────────┘ │
|
||
│ │ │ │
|
||
│ ┌───────────────────────────┐ │ │ ┌───────────────────────────┐ │
|
||
│ │ 设备管理器 │ │ │ │ 共享内存 │ │
|
||
│ │ DeviceManager ├──┼──────┼─►│ (数据通道) │ │
|
||
│ └───────────────────────────┘ │ │ └───────────────────────────┘ │
|
||
│ │ │ │
|
||
│ ┌───────────────────────────┐ │ │ │
|
||
│ │ 网络传输 │ │ │ │
|
||
│ │ NetworkTransport │ │ │ │
|
||
│ └───────────────────────────┘ │ │ │
|
||
└─────────────────────────────────┘ └─────────────────────────────────┘
|
||
│
|
||
│ ┌─────────────────────────────────┐
|
||
│ │ 远程音频引擎 │
|
||
│ │ │
|
||
│ │ ┌─────────────────────────┐ │
|
||
│ │ │ 网络服务器 │ │
|
||
└──────────────────────┼─►│ NetworkServer │ │
|
||
│ └─────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────┘
|
||
```
|
||
|
||
## 前端管理器
|
||
|
||
### FrontendManager
|
||
|
||
`FrontendManager`是前端接口的核心类,负责协调所有前端组件,管理与音频引擎的通信,以及处理音频设备和网络服务。
|
||
|
||
```cpp
|
||
class FrontendManager {
|
||
public:
|
||
explicit FrontendManager(const FrontendConfig& config);
|
||
~FrontendManager();
|
||
|
||
// 生命周期管理
|
||
common::ErrorCode initialize();
|
||
common::ErrorCode shutdown();
|
||
bool is_initialized() const;
|
||
bool is_engine_connected() const;
|
||
|
||
// 事件监听
|
||
void add_event_listener(std::shared_ptr<IFrontendEventListener> listener);
|
||
void remove_event_listener(std::shared_ptr<IFrontendEventListener> listener);
|
||
|
||
// 音频引擎连接
|
||
common::ErrorCode connect_to_engine(const std::string& endpoint = "");
|
||
common::ErrorCode disconnect_from_engine();
|
||
|
||
// 音频设备管理
|
||
std::vector<AudioDeviceInfo> get_audio_devices() const;
|
||
common::ErrorCode set_input_device(const std::string& device_id);
|
||
common::ErrorCode set_output_device(const std::string& device_id);
|
||
common::ErrorCode start_audio_stream();
|
||
common::ErrorCode stop_audio_stream();
|
||
|
||
// 网络服务管理
|
||
common::ErrorCode start_network_discovery();
|
||
common::ErrorCode stop_network_discovery();
|
||
std::vector<NetworkServiceInfo> get_discovered_services() const;
|
||
common::ErrorCode connect_to_network_service(const std::string& service_id);
|
||
common::ErrorCode disconnect_from_network_service(const std::string& service_id);
|
||
|
||
// 音频流控制
|
||
common::ErrorCode start_network_audio_stream(const std::string& target_address, uint16_t port);
|
||
common::ErrorCode stop_network_audio_stream();
|
||
|
||
// 配置管理
|
||
const FrontendConfig& config() const;
|
||
common::ErrorCode update_config(const FrontendConfig& new_config);
|
||
|
||
// 统计信息
|
||
const FrontendStatistics& get_statistics() const;
|
||
void reset_statistics();
|
||
void print_statistics() const;
|
||
|
||
// 音频数据处理
|
||
common::ErrorCode send_audio_data(const engine::AudioBuffer& buffer);
|
||
common::ErrorCode send_audio_data_to_network(const engine::AudioBuffer& buffer,
|
||
const std::string& target);
|
||
|
||
// 控制命令
|
||
common::ErrorCode send_control_command(const std::string& command,
|
||
const std::string& parameters = "");
|
||
};
|
||
```
|
||
|
||
### 前端配置
|
||
|
||
`FrontendConfig`定义了前端接口的配置选项,包括基础设置、网络设置和性能选项:
|
||
|
||
```cpp
|
||
struct FrontendConfig {
|
||
// 基础配置
|
||
std::string process_name = "audio_frontend";
|
||
std::string engine_endpoint = "tcp://localhost:5555";
|
||
|
||
// 音频设备配置
|
||
bool auto_detect_devices = true;
|
||
bool enable_device_hotplug = true;
|
||
uint32_t device_poll_interval_ms = 1000;
|
||
|
||
// 网络配置
|
||
bool enable_network_discovery = true;
|
||
bool enable_network_streaming = true;
|
||
uint16_t network_discovery_port = 7777;
|
||
uint16_t audio_stream_port = 8888;
|
||
uint16_t control_port = 9999;
|
||
std::string network_interface = "0.0.0.0";
|
||
|
||
// 缓冲配置
|
||
uint32_t audio_buffer_size = 512;
|
||
uint32_t network_buffer_size = 2048;
|
||
uint32_t max_latency_ms = 50;
|
||
|
||
// 安全配置
|
||
bool require_authentication = false;
|
||
std::string certificate_path = "";
|
||
std::string private_key_path = "";
|
||
|
||
// 性能配置
|
||
uint32_t worker_thread_count = 2;
|
||
bool enable_performance_monitoring = true;
|
||
uint32_t statistics_interval_ms = 5000;
|
||
};
|
||
```
|
||
|
||
**配置选项说明:**
|
||
|
||
| 配置项 | 描述 | 默认值 | 推荐设置 |
|
||
|--------|------|--------|----------|
|
||
| **process_name** | 前端进程名称,用于标识 | "audio_frontend" | 应用名称 |
|
||
| **engine_endpoint** | 音频引擎连接地址 | "tcp://localhost:5555" | 本地部署保持默认 |
|
||
| **auto_detect_devices** | 是否自动检测音频设备 | true | 通常保持启用 |
|
||
| **enable_device_hotplug** | 是否监控设备热插拔 | true | 通常保持启用 |
|
||
| **enable_network_discovery** | 是否启用网络服务发现 | true | 需要网络功能时启用 |
|
||
| **enable_network_streaming** | 是否启用网络音频流 | true | 需要网络功能时启用 |
|
||
| **audio_buffer_size** | 音频缓冲区大小(帧) | 512 | 低延迟场景: 128-256<br>高质量场景: 512-2048 |
|
||
| **network_buffer_size** | 网络缓冲区大小(字节) | 2048 | 低延迟场景: 512-1024<br>高带宽场景: 4096+ |
|
||
| **max_latency_ms** | 最大可接受延迟(毫秒) | 50 | 实时通信: 20-30<br>音乐制作: 30-50<br>流媒体: 100-300 |
|
||
|
||
### 生命周期管理
|
||
|
||
前端管理器的典型生命周期包括:
|
||
|
||
1. **创建管理器**:使用工厂函数创建前端管理器
|
||
2. **初始化**:调用`initialize()`进行初始化
|
||
3. **连接引擎**:调用`connect_to_engine()`连接到音频引擎
|
||
4. **配置设备**:枚举并设置音频设备
|
||
5. **启动音频流**:调用`start_audio_stream()`开始音频处理
|
||
6. **处理音频数据**:发送/接收音频数据
|
||
7. **停止音频流**:调用`stop_audio_stream()`停止音频处理
|
||
8. **断开连接**:调用`disconnect_from_engine()`断开引擎连接
|
||
9. **关闭**:调用`shutdown()`清理资源
|
||
|
||
```cpp
|
||
// 示例:前端管理器基本生命周期
|
||
#include "audio_backend/frontend.h"
|
||
|
||
int main() {
|
||
// 初始化前端系统
|
||
audio_backend::frontend::initialize_frontend();
|
||
|
||
// 创建前端管理器
|
||
auto frontend = audio_backend::frontend::create_frontend_manager();
|
||
|
||
// 初始化
|
||
auto result = frontend->initialize();
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "前端初始化失败: " << audio_backend::common::error_to_string(result) << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// 连接到音频引擎
|
||
result = frontend->connect_to_engine("tcp://localhost:5555");
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "连接音频引擎失败" << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// ... 应用逻辑 ...
|
||
|
||
// 关闭前端管理器
|
||
frontend->shutdown();
|
||
|
||
// 清理前端系统
|
||
audio_backend::frontend::shutdown_frontend();
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
## 音频设备管理
|
||
|
||
### 设备信息
|
||
|
||
`AudioDeviceInfo`结构提供了音频设备的详细信息:
|
||
|
||
```cpp
|
||
struct AudioDeviceInfo {
|
||
std::string id; // 设备唯一标识符
|
||
std::string name; // 设备名称
|
||
std::string driver_name; // 驱动名称
|
||
bool is_input; // 是否为输入设备
|
||
bool is_output; // 是否为输出设备
|
||
bool is_default; // 是否为默认设备
|
||
std::vector<uint32_t> supported_sample_rates; // 支持的采样率
|
||
std::vector<uint16_t> supported_channel_counts; // 支持的声道数
|
||
std::vector<engine::AudioFormat> supported_formats; // 支持的音频格式
|
||
uint32_t default_buffer_size; // 默认缓冲区大小
|
||
double input_latency; // 输入延迟(毫秒)
|
||
double output_latency; // 输出延迟(毫秒)
|
||
};
|
||
```
|
||
|
||
枚举音频设备的示例:
|
||
|
||
```cpp
|
||
void list_audio_devices(audio_backend::frontend::FrontendManager& frontend) {
|
||
auto devices = frontend.get_audio_devices();
|
||
|
||
std::cout << "发现 " << devices.size() << " 个音频设备:" << std::endl;
|
||
std::cout << "-----------------------------------------" << std::endl;
|
||
|
||
for (const auto& device : devices) {
|
||
std::cout << (device.is_default ? "* " : " ")
|
||
<< device.name << " (" << device.id << ")" << std::endl;
|
||
|
||
std::cout << " 类型: "
|
||
<< (device.is_input && device.is_output ? "输入/输出" :
|
||
(device.is_input ? "输入" : "输出")) << std::endl;
|
||
|
||
std::cout << " 驱动: " << device.driver_name << std::endl;
|
||
|
||
std::cout << " 支持的采样率: ";
|
||
for (auto rate : device.supported_sample_rates) {
|
||
std::cout << rate << " ";
|
||
}
|
||
std::cout << std::endl;
|
||
|
||
std::cout << " 支持的声道数: ";
|
||
for (auto channels : device.supported_channel_counts) {
|
||
std::cout << channels << " ";
|
||
}
|
||
std::cout << std::endl;
|
||
|
||
std::cout << " 延迟: ";
|
||
if (device.is_input) std::cout << "输入=" << device.input_latency << "ms ";
|
||
if (device.is_output) std::cout << "输出=" << device.output_latency << "ms";
|
||
std::cout << std::endl;
|
||
|
||
std::cout << "-----------------------------------------" << std::endl;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 设备选择
|
||
|
||
前端管理器提供了设置输入和输出设备的方法:
|
||
|
||
```cpp
|
||
common::ErrorCode set_input_device(const std::string& device_id);
|
||
common::ErrorCode set_output_device(const std::string& device_id);
|
||
```
|
||
|
||
设置音频设备的示例:
|
||
|
||
```cpp
|
||
void configure_audio_devices(audio_backend::frontend::FrontendManager& frontend) {
|
||
// 获取所有音频设备
|
||
auto devices = frontend.get_audio_devices();
|
||
|
||
// 查找默认输入和输出设备
|
||
std::string input_device_id;
|
||
std::string output_device_id;
|
||
|
||
for (const auto& device : devices) {
|
||
if (device.is_default) {
|
||
if (device.is_input) {
|
||
input_device_id = device.id;
|
||
}
|
||
if (device.is_output) {
|
||
output_device_id = device.id;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 设置输入设备
|
||
if (!input_device_id.empty()) {
|
||
auto result = frontend.set_input_device(input_device_id);
|
||
if (result == audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cout << "设置默认输入设备成功" << std::endl;
|
||
} else {
|
||
std::cerr << "设置默认输入设备失败" << std::endl;
|
||
}
|
||
}
|
||
|
||
// 设置输出设备
|
||
if (!output_device_id.empty()) {
|
||
auto result = frontend.set_output_device(output_device_id);
|
||
if (result == audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cout << "设置默认输出设备成功" << std::endl;
|
||
} else {
|
||
std::cerr << "设置默认输出设备失败" << std::endl;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 音频流控制
|
||
|
||
前端管理器提供了启动和停止音频流的方法:
|
||
|
||
```cpp
|
||
common::ErrorCode start_audio_stream();
|
||
common::ErrorCode stop_audio_stream();
|
||
```
|
||
|
||
音频流控制示例:
|
||
|
||
```cpp
|
||
void control_audio_stream(audio_backend::frontend::FrontendManager& frontend) {
|
||
// 启动音频流
|
||
auto result = frontend.start_audio_stream();
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "启动音频流失败: "
|
||
<< audio_backend::common::error_to_string(result) << std::endl;
|
||
return;
|
||
}
|
||
|
||
std::cout << "音频流已启动,按回车键停止..." << std::endl;
|
||
std::cin.get();
|
||
|
||
// 停止音频流
|
||
result = frontend.stop_audio_stream();
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "停止音频流失败: "
|
||
<< audio_backend::common::error_to_string(result) << std::endl;
|
||
return;
|
||
}
|
||
|
||
std::cout << "音频流已停止" << std::endl;
|
||
}
|
||
```
|
||
|
||
## 网络通信
|
||
|
||
### 服务发现
|
||
|
||
前端接口支持通过mDNS/Bonjour协议自动发现网络上的音频服务:
|
||
|
||
```cpp
|
||
common::ErrorCode start_network_discovery();
|
||
common::ErrorCode stop_network_discovery();
|
||
std::vector<NetworkServiceInfo> get_discovered_services() const;
|
||
```
|
||
|
||
`NetworkServiceInfo`结构提供了网络服务的详细信息:
|
||
|
||
```cpp
|
||
struct NetworkServiceInfo {
|
||
std::string service_id; // 服务唯一标识符
|
||
std::string service_name; // 服务名称
|
||
std::string address; // IP地址
|
||
uint16_t port; // 端口
|
||
std::string service_type; // 服务类型
|
||
std::unordered_map<std::string, std::string> properties; // 服务属性
|
||
std::chrono::steady_clock::time_point last_seen; // 最后发现时间
|
||
};
|
||
```
|
||
|
||
服务发现示例:
|
||
|
||
```cpp
|
||
void discover_audio_services(audio_backend::frontend::FrontendManager& frontend) {
|
||
// 启动网络服务发现
|
||
auto result = frontend.start_network_discovery();
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "启动网络服务发现失败" << std::endl;
|
||
return;
|
||
}
|
||
|
||
std::cout << "搜索网络音频服务中..." << std::endl;
|
||
|
||
// 等待服务发现(实际应用中可能使用事件回调)
|
||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||
|
||
// 获取已发现的服务
|
||
auto services = frontend.get_discovered_services();
|
||
|
||
if (services.empty()) {
|
||
std::cout << "未发现音频服务" << std::endl;
|
||
} else {
|
||
std::cout << "发现 " << services.size() << " 个音频服务:" << std::endl;
|
||
|
||
for (const auto& service : services) {
|
||
std::cout << "- " << service.service_name
|
||
<< " (" << service.address << ":" << service.port << ")" << std::endl;
|
||
|
||
std::cout << " 服务类型: " << service.service_type << std::endl;
|
||
std::cout << " 服务ID: " << service.service_id << std::endl;
|
||
|
||
if (!service.properties.empty()) {
|
||
std::cout << " 服务属性:" << std::endl;
|
||
for (const auto& [key, value] : service.properties) {
|
||
std::cout << " " << key << ": " << value << std::endl;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 停止网络服务发现
|
||
frontend.stop_network_discovery();
|
||
}
|
||
```
|
||
|
||
### 会话管理
|
||
|
||
前端接口提供会话管理功能,用于建立和维护与远程服务的连接:
|
||
|
||
```cpp
|
||
common::ErrorCode connect_to_network_service(const std::string& service_id);
|
||
common::ErrorCode disconnect_from_network_service(const std::string& service_id);
|
||
```
|
||
|
||
此外,还可以使用专用的会话管理器来管理更复杂的会话场景:
|
||
|
||
```cpp
|
||
// 创建会话管理器
|
||
auto session_manager = audio_backend::frontend::create_session_manager();
|
||
|
||
// 创建会话服务器
|
||
auto session_server = audio_backend::frontend::create_session_server(9999);
|
||
```
|
||
|
||
会话管理示例:
|
||
|
||
```cpp
|
||
void manage_network_session(audio_backend::frontend::FrontendManager& frontend) {
|
||
// 启动网络服务发现
|
||
frontend.start_network_discovery();
|
||
|
||
// 等待服务发现
|
||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||
|
||
// 获取已发现的服务
|
||
auto services = frontend.get_discovered_services();
|
||
|
||
if (services.empty()) {
|
||
std::cout << "未发现音频服务,无法建立会话" << std::endl;
|
||
return;
|
||
}
|
||
|
||
// 连接到第一个发现的服务
|
||
auto service = services[0];
|
||
std::cout << "连接到服务: " << service.service_name << std::endl;
|
||
|
||
auto result = frontend.connect_to_network_service(service.service_id);
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "连接网络服务失败" << std::endl;
|
||
return;
|
||
}
|
||
|
||
std::cout << "已连接到网络服务,按回车键断开..." << std::endl;
|
||
std::cin.get();
|
||
|
||
// 断开与服务的连接
|
||
result = frontend.disconnect_from_network_service(service.service_id);
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "断开网络服务失败" << std::endl;
|
||
return;
|
||
}
|
||
|
||
std::cout << "已断开网络服务连接" << std::endl;
|
||
|
||
// 停止网络服务发现
|
||
frontend.stop_network_discovery();
|
||
}
|
||
```
|
||
|
||
### 音频流传输
|
||
|
||
前端接口提供了网络音频流传输功能:
|
||
|
||
```cpp
|
||
common::ErrorCode start_network_audio_stream(const std::string& target_address, uint16_t port);
|
||
common::ErrorCode stop_network_audio_stream();
|
||
common::ErrorCode send_audio_data_to_network(const engine::AudioBuffer& buffer, const std::string& target);
|
||
```
|
||
|
||
此外,还可以使用专用的音频流发送器和接收器来进行更灵活的控制:
|
||
|
||
```cpp
|
||
// 创建音频流发送器
|
||
auto sender = audio_backend::frontend::create_audio_stream_sender(48000, 2);
|
||
|
||
// 创建音频流接收器
|
||
auto receiver = audio_backend::frontend::create_audio_stream_receiver(48000, 2);
|
||
```
|
||
|
||
音频流传输示例:
|
||
|
||
```cpp
|
||
void stream_audio_over_network(audio_backend::frontend::FrontendManager& frontend,
|
||
const std::string& target_address,
|
||
uint16_t port) {
|
||
// 启动网络音频流
|
||
auto result = frontend.start_network_audio_stream(target_address, port);
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "启动网络音频流失败" << std::endl;
|
||
return;
|
||
}
|
||
|
||
std::cout << "网络音频流已启动,流向: " << target_address << ":" << port << std::endl;
|
||
std::cout << "按回车键停止..." << std::endl;
|
||
std::cin.get();
|
||
|
||
// 停止网络音频流
|
||
result = frontend.stop_network_audio_stream();
|
||
if (result != audio_backend::common::ErrorCode::SUCCESS) {
|
||
std::cerr << "停止网络音频流失败" << std::endl;
|
||
return;
|
||
}
|
||
|
||
std::cout << "网络音频流已停止" << std::endl;
|
||
}
|
||
```
|
||
|
||
## 音频编解码
|
||
|
||
### 编解码器接口
|
||
|
||
前端接口支持多种音频编解码器,用于网络传输时的压缩和解压缩:
|
||
|
||
```cpp
|
||
namespace audio_backend::frontend::codec {
|
||
|
||
enum class CodecType {
|
||
PCM, // 无压缩PCM
|
||
OPUS, // Opus编解码器
|
||
AAC, // AAC编解码器
|
||
FLAC // FLAC编解码器
|
||
};
|
||
|
||
struct CodecConfig {
|
||
CodecType codec_type; // 编解码器类型
|
||
uint32_t sample_rate; // 采样率
|
||
uint16_t channels; // 声道数
|
||
uint32_t bitrate; // 比特率
|
||
uint32_t complexity = 10; // 复杂度 (0-10)
|
||
bool vbr = true; // 是否使用可变比特率
|
||
bool fec = true; // 是否使用前向纠错
|
||
uint32_t packet_size_ms = 20; // 数据包大小(毫秒)
|
||
};
|
||
|
||
class IAudioCodec {
|
||
public:
|
||
virtual ~IAudioCodec() = default;
|
||
|
||
// 编码音频数据
|
||
virtual std::vector<uint8_t> encode(const float* audio_data,
|
||
size_t samples_per_channel) = 0;
|
||
|
||
// 解码音频数据
|
||
virtual size_t decode(const uint8_t* encoded_data,
|
||
size_t encoded_size,
|
||
float* output_buffer,
|
||
size_t output_buffer_size) = 0;
|
||
|
||
// 获取配置
|
||
virtual CodecConfig get_config() const = 0;
|
||
|
||
// 获取延迟样本数
|
||
virtual uint32_t get_codec_delay_samples() const = 0;
|
||
};
|
||
|
||
} // namespace audio_backend::frontend::codec
|
||
```
|
||
|
||
### 缓冲区管理
|
||
|
||
前端接口提供了缓冲区管理,用于处理网络传输中的抖动和延迟:
|
||
|
||
```cpp
|
||
namespace audio_backend::frontend::network {
|
||
|
||
struct BufferManagerConfig {
|
||
uint32_t initial_buffer_size_ms; // 初始缓冲区大小(毫秒)
|
||
uint32_t min_buffer_size_ms; // 最小缓冲区大小(毫秒)
|
||
uint32_t max_buffer_size_ms; // 最大缓冲区大小(毫秒)
|
||
uint32_t target_buffer_size_ms; // 目标缓冲区大小(毫秒)
|
||
uint32_t sample_rate; // 采样率
|
||
uint16_t channels; // 声道数
|
||
bool adaptive_buffering; // 是否启用自适应缓冲
|
||
uint32_t adaptation_speed; // 自适应速度 (1-10)
|
||
};
|
||
|
||
class BufferManager {
|
||
public:
|
||
virtual ~BufferManager() = default;
|
||
|
||
// 添加音频数据到缓冲区
|
||
virtual void add_audio_data(const std::vector<uint8_t>& data,
|
||
uint32_t timestamp) = 0;
|
||
|
||
// 获取可用的音频数据
|
||
virtual engine::AudioBuffer get_audio_data(uint32_t num_frames) = 0;
|
||
|
||
// 获取当前缓冲区状态
|
||
virtual uint32_t get_buffered_ms() const = 0;
|
||
virtual uint32_t get_underruns() const = 0;
|
||
virtual uint32_t get_overruns() const = 0;
|
||
|
||
// 缓冲区控制
|
||
virtual void clear() = 0;
|
||
virtual void pause() = 0;
|
||
virtual void resume() = 0;
|
||
|
||
// 配置
|
||
virtual void set_config(const BufferManagerConfig& config) = 0;
|
||
virtual BufferManagerConfig get_config() const = 0;
|
||
};
|
||
|
||
} // namespace audio_backend::frontend::network
|
||
```
|
||
|
||
使用编解码器和缓冲区管理的示例:
|
||
|
||
```cpp
|
||
void audio_codec_example() {
|
||
// 创建Opus编解码器
|
||
auto codec_config = audio_backend::frontend::codec::CodecConfig{
|
||
audio_backend::frontend::codec::CodecType::OPUS,
|
||
48000, // 采样率
|
||
2, // 声道数
|
||
128000 // 比特率
|
||
};
|
||
|
||
auto codec = audio_backend::frontend::codec::create_codec(codec_config);
|
||
|
||
// 创建缓冲区管理器
|
||
auto buffer_config = audio_backend::frontend::network::BufferManagerConfig{
|
||
50, // 初始缓冲50ms
|
||
20, // 最小缓冲20ms
|
||
200, // 最大缓冲200ms
|
||
50, // 目标缓冲50ms
|
||
48000, // 采样率
|
||
2, // 声道数
|
||
true, // 启用自适应缓冲
|
||
5 // 中等自适应速度
|
||
};
|
||
|
||
auto buffer_manager = audio_backend::frontend::create_buffer_manager(
|
||
buffer_config.initial_buffer_size_ms,
|
||
buffer_config.sample_rate,
|
||
buffer_config.channels);
|
||
|
||
// 模拟音频处理流程
|
||
const size_t frame_size = 480; // 10ms @ 48kHz
|
||
std::vector<float> audio_samples(frame_size * 2); // 立体声
|
||
|
||
// 生成一些示例音频数据(正弦波)
|
||
for (size_t i = 0; i < frame_size; i++) {
|
||
float sample = std::sin(2.0f * 3.14159f * 440.0f * i / 48000.0f);
|
||
audio_samples[i * 2] = sample; // 左声道
|
||
audio_samples[i * 2 + 1] = sample; // 右声道
|
||
}
|
||
|
||
// 编码音频数据
|
||
auto encoded_data = codec->encode(audio_samples.data(), frame_size);
|
||
|
||
std::cout << "原始音频大小: " << (frame_size * 2 * sizeof(float)) << " 字节" << std::endl;
|
||
std::cout << "编码后大小: " << encoded_data.size() << " 字节" << std::endl;
|
||
std::cout << "压缩率: " << ((float)encoded_data.size() / (frame_size * 2 * sizeof(float)) * 100) << "%" << std::endl;
|
||
|
||
// 将编码数据添加到缓冲区
|
||
buffer_manager->add_audio_data(encoded_data, 0);
|
||
|
||
// 获取音频数据
|
||
auto output_buffer = buffer_manager->get_audio_data(frame_size);
|
||
|
||
std::cout << "缓冲区状态: " << buffer_manager->get_buffered_ms() << "ms 已缓冲" << std::endl;
|
||
}
|
||
```
|
||
|
||
## 事件处理
|
||
|
||
### 事件类型
|
||
|
||
前端接口定义了多种事件类型,用于通知应用程序状态变化:
|
||
|
||
```cpp
|
||
enum class FrontendEvent {
|
||
EngineConnected, // 音频引擎连接成功
|
||
EngineDisconnected, // 音频引擎断开连接
|
||
DeviceAdded, // 音频设备添加
|
||
DeviceRemoved, // 音频设备移除
|
||
NetworkServiceFound, // 发现网络服务
|
||
NetworkServiceLost, // 网络服务丢失
|
||
AudioStreamStarted, // 音频流开始
|
||
AudioStreamStopped, // 音频流停止
|
||
ConfigurationChanged // 配置变更
|
||
};
|
||
```
|
||
|
||
### 事件监听器
|
||
|
||
应用程序可以通过实现`IFrontendEventListener`接口来接收前端事件:
|
||
|
||
```cpp
|
||
class IFrontendEventListener {
|
||
public:
|
||
virtual ~IFrontendEventListener() = default;
|
||
|
||
// 事件回调
|
||
virtual void on_frontend_event(FrontendEvent event, const std::string& data) = 0;
|
||
|
||
// 音频事件
|
||
virtual void on_audio_device_changed(const std::string& device_id, bool added) = 0;
|
||
virtual void on_audio_stream_data(const engine::AudioBuffer& buffer) = 0;
|
||
|
||
// 网络事件
|
||
virtual void on_network_service_discovered(const std::string& service_name,
|
||
const std::string& address,
|
||
uint16_t port) = 0;
|
||
|
||
// 错误事件
|
||
virtual void on_frontend_error(common::ErrorCode error, const std::string& message) = 0;
|
||
};
|
||
```
|
||
|
||
事件监听器示例:
|
||
|
||
```cpp
|
||
class MyFrontendListener : public audio_backend::frontend::IFrontendEventListener {
|
||
public:
|
||
void on_frontend_event(audio_backend::frontend::FrontendEvent event,
|
||
const std::string& data) override {
|
||
std::cout << "前端事件: ";
|
||
|
||
switch (event) {
|
||
case audio_backend::frontend::FrontendEvent::EngineConnected:
|
||
std::cout << "引擎已连接";
|
||
break;
|
||
case audio_backend::frontend::FrontendEvent::EngineDisconnected:
|
||
std::cout << "引擎已断开";
|
||
break;
|
||
case audio_backend::frontend::FrontendEvent::DeviceAdded:
|
||
std::cout << "设备已添加";
|
||
break;
|
||
case audio_backend::frontend::FrontendEvent::DeviceRemoved:
|
||
std::cout << "设备已移除";
|
||
break;
|
||
case audio_backend::frontend::FrontendEvent::NetworkServiceFound:
|
||
std::cout << "发现网络服务";
|
||
break;
|
||
case audio_backend::frontend::FrontendEvent::NetworkServiceLost:
|
||
std::cout << "网络服务丢失";
|
||
break;
|
||
case audio_backend::frontend::FrontendEvent::AudioStreamStarted:
|
||
std::cout << "音频流已启动";
|
||
break;
|
||
case audio_backend::frontend::FrontendEvent::AudioStreamStopped:
|
||
std::cout << "音频流已停止";
|
||
break;
|
||
case audio_backend::frontend::FrontendEvent::ConfigurationChanged:
|
||
std::cout << "配置已更改";
|
||
break;
|
||
default:
|
||
std::cout << "未知事件";
|
||
break;
|
||
}
|
||
|
||
if (!data.empty()) {
|
||
std::cout << " - 数据: " << data;
|
||
}
|
||
|
||
std::cout << std::endl;
|
||
}
|
||
|
||
void on_audio_device_changed(const std::string& device_id, bool added) override {
|
||
std::cout << "音频设备" << (added ? "添加" : "移除") << ": " << device_id << std::endl;
|
||
}
|
||
|
||
void on_audio_stream_data(const audio_backend::engine::AudioBuffer& buffer) override {
|
||
// 实际应用中可能对音频数据进行处理
|
||
std::cout << "接收到音频数据: " << buffer.frames() << " 帧, "
|
||
<< buffer.channels() << " 声道" << std::endl;
|
||
}
|
||
|
||
void on_network_service_discovered(const std::string& service_name,
|
||
const std::string& address,
|
||
uint16_t port) override {
|
||
std::cout << "发现网络服务: " << service_name << " @ "
|
||
<< address << ":" << port << std::endl;
|
||
}
|
||
|
||
void on_frontend_error(audio_backend::common::ErrorCode error,
|
||
const std::string& message) override {
|
||
std::cerr << "前端错误: " << audio_backend::common::error_to_string(error)
|
||
<< " - " << message << std::endl;
|
||
}
|
||
};
|
||
|
||
// 使用事件监听器
|
||
void use_event_listener(audio_backend::frontend::FrontendManager& frontend) {
|
||
auto listener = std::make_shared<MyFrontendListener>();
|
||
|
||
frontend.add_event_listener(listener);
|
||
|
||
// 执行会触发事件的操作
|
||
// ...
|
||
|
||
// 移除事件监听器
|
||
frontend.remove_event_listener(listener);
|
||
}
|
||
```
|
||
|
||
## 工厂函数
|
||
|
||
### 基础工厂
|
||
|
||
前端接口提供了多种工厂函数,用于创建各种前端组件:
|
||
|
||
```cpp
|
||
namespace audio_backend::frontend {
|
||
|
||
// 创建前端管理器
|
||
std::unique_ptr<Manager> create_frontend_manager(const std::string& process_name = "audio_frontend");
|
||
|
||
// 创建引擎代理
|
||
std::unique_ptr<EngineProxy> create_engine_proxy(const std::string& endpoint = "tcp://localhost:5555");
|
||
|
||
// 创建设备管理器
|
||
std::unique_ptr<DeviceManager> create_device_manager();
|
||
|
||
// 创建音频流发送器
|
||
std::unique_ptr<AudioStreamSender> create_audio_stream_sender(
|
||
uint32_t sample_rate = 48000,
|
||
uint16_t channels = 2,
|
||
frontend::network::AudioCodec codec = frontend::network::AudioCodec::OPUS);
|
||
|
||
// 创建音频流接收器
|
||
std::unique_ptr<AudioStreamReceiver> create_audio_stream_receiver(
|
||
uint32_t sample_rate = 48000,
|
||
uint16_t channels = 2,
|
||
frontend::network::AudioCodec codec = frontend::network::AudioCodec::OPUS);
|
||
|
||
// 创建服务发现
|
||
std::unique_ptr<ServiceDiscovery> create_service_discovery();
|
||
|
||
// 创建会话管理器
|
||
std::unique_ptr<SessionManager> create_session_manager();
|
||
|
||
// 创建会话服务器
|
||
std::unique_ptr<SessionServer> create_session_server(uint16_t port = 9999);
|
||
|
||
// 创建编解码器
|
||
std::unique_ptr<AudioCodec> create_audio_codec(
|
||
frontend::codec::CodecType type,
|
||
uint32_t sample_rate = 48000,
|
||
uint16_t channels = 2,
|
||
uint32_t bitrate = 128000);
|
||
|
||
// 创建缓冲管理器
|
||
std::unique_ptr<BufferManager> create_buffer_manager(
|
||
uint32_t initial_buffer_size_ms = 50,
|
||
uint32_t sample_rate = 48000,
|
||
uint16_t channels = 2);
|
||
|
||
} // namespace audio_backend::frontend
|
||
```
|
||
|
||
### 预配置组合
|
||
|
||
前端接口还提供了预配置的工厂函数,用于创建具有特定功能的前端管理器:
|
||
|
||
```cpp
|
||
namespace audio_backend::frontend {
|
||
|
||
// 创建本地音频前端(无网络功能)
|
||
std::unique_ptr<Manager> create_local_frontend();
|
||
|
||
// 创建网络客户端前端(连接到远程音频引擎)
|
||
std::unique_ptr<Manager> create_network_client_frontend(
|
||
const std::string& server_address,
|
||
uint16_t port);
|
||
|
||
// 创建低延迟音频流配置
|
||
std::unique_ptr<Manager> create_low_latency_frontend();
|
||
|
||
// 创建高质量音频流配置
|
||
std::unique_ptr<Manager> create_high_quality_frontend();
|
||
|
||
// 创建平衡配置(延迟和质量的平衡)
|
||
std::unique_ptr<Manager> create_balanced_frontend();
|
||
|
||
} // namespace audio_backend::frontend
|
||
```
|
||
|
||
使用工厂函数示例:
|
||
|
||
```cpp
|
||
void factory_functions_example() {
|
||
// 创建本地前端
|
||
auto local_frontend = audio_backend::frontend::create_local_frontend();
|
||
if (local_frontend) {
|
||
std::cout << "创建本地前端成功" << std::endl;
|
||
}
|
||
|
||
// 创建低延迟前端
|
||
auto low_latency = audio_backend::frontend::create_low_latency_frontend();
|
||
if (low_latency) {
|
||
std::cout << "创建低延迟前端成功" << std::endl;
|
||
}
|
||
|
||
// 创建高质量前端
|
||
auto high_quality = audio_backend::frontend::create_high_quality_frontend();
|
||
if (high_quality) {
|
||
std::cout << "创建高质量前端成功" << std::endl;
|
||
}
|
||
|
||
// 创建平衡前端
|
||
auto balanced = audio_backend::frontend::create_balanced_frontend();
|
||
if (balanced) {
|
||
std::cout << "创建平衡前端成功" << std::endl;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 实现示例
|
||
|
||
### 本地音频处理
|
||
|
||
以下是使用前端接口进行本地音频处理的完整示例:
|
||
|
||
```cpp
|
||
#include "audio_backend/frontend.h"
|
||
#include <iostream>
|
||
#include <thread>
|
||
#include <chrono>
|
||
|
||
using namespace audio_backend;
|
||
|
||
// 自定义音频处理回调
|
||
class MyAudioProcessor : public frontend::IFrontendEventListener {
|
||
public:
|
||
void on_frontend_event(frontend::FrontendEvent event, const std::string& data) override {
|
||
std::cout << "事件: ";
|
||
switch (event) {
|
||
case frontend::FrontendEvent::AudioStreamStarted:
|
||
std::cout << "音频流已启动" << std::endl;
|
||
break;
|
||
case frontend::FrontendEvent::AudioStreamStopped:
|
||
std::cout << "音频流已停止" << std::endl;
|
||
break;
|
||
default:
|
||
std::cout << "其他事件" << std::endl;
|
||
break;
|
||
}
|
||
}
|
||
|
||
void on_audio_device_changed(const std::string& device_id, bool added) override {
|
||
// 不处理设备变化
|
||
}
|
||
|
||
void on_network_service_discovered(const std::string& service_name,
|
||
const std::string& address,
|
||
uint16_t port) override {
|
||
// 不处理网络服务
|
||
}
|
||
|
||
void on_audio_stream_data(const engine::AudioBuffer& buffer) override {
|
||
// 处理音频数据(在本例中,简单地计算音量)
|
||
double sum = 0.0;
|
||
size_t samples = buffer.frames() * buffer.channels();
|
||
|
||
if (buffer.is_interleaved()) {
|
||
const float* data = buffer.interleaved_data<float>();
|
||
for (size_t i = 0; i < samples; i++) {
|
||
sum += std::abs(data[i]);
|
||
}
|
||
} else {
|
||
for (uint16_t ch = 0; ch < buffer.channels(); ch++) {
|
||
const float* data = buffer.channel_data<float>(ch);
|
||
for (uint32_t i = 0; i < buffer.frames(); i++) {
|
||
sum += std::abs(data[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
double average = sum / samples;
|
||
frames_processed += buffer.frames();
|
||
|
||
// 每秒更新一次统计
|
||
auto now = std::chrono::steady_clock::now();
|
||
if (now - last_update > std::chrono::seconds(1)) {
|
||
std::cout << "已处理: " << frames_processed << " 帧, 平均音量: "
|
||
<< (average * 100) << "%" << std::endl;
|
||
last_update = now;
|
||
}
|
||
}
|
||
|
||
void on_frontend_error(common::ErrorCode error, const std::string& message) override {
|
||
std::cerr << "错误: " << common::error_to_string(error) << " - " << message << std::endl;
|
||
}
|
||
|
||
private:
|
||
uint64_t frames_processed = 0;
|
||
std::chrono::steady_clock::time_point last_update = std::chrono::steady_clock::now();
|
||
};
|
||
|
||
int main() {
|
||
// 初始化前端系统
|
||
frontend::initialize_frontend();
|
||
|
||
// 创建前端管理器(本地模式)
|
||
auto frontend_manager = frontend::create_local_frontend();
|
||
if (!frontend_manager) {
|
||
std::cerr << "创建前端管理器失败" << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// 初始化前端管理器
|
||
auto result = frontend_manager->initialize();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "初始化前端管理器失败: " << common::error_to_string(result) << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// 创建并添加事件监听器
|
||
auto processor = std::make_shared<MyAudioProcessor>();
|
||
frontend_manager->add_event_listener(processor);
|
||
|
||
// 连接到本地音频引擎
|
||
result = frontend_manager->connect_to_engine("tcp://localhost:5555");
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "连接音频引擎失败: " << common::error_to_string(result) << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
// 列出可用音频设备
|
||
auto devices = frontend_manager->get_audio_devices();
|
||
std::cout << "可用音频设备:" << std::endl;
|
||
for (const auto& device : devices) {
|
||
std::cout << (device.is_default ? "* " : " ")
|
||
<< device.name << (device.is_input ? " (输入)" : " (输出)") << std::endl;
|
||
}
|
||
|
||
// 设置默认输入设备
|
||
for (const auto& device : devices) {
|
||
if (device.is_default && device.is_input) {
|
||
frontend_manager->set_input_device(device.id);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 设置默认输出设备
|
||
for (const auto& device : devices) {
|
||
if (device.is_default && device.is_output) {
|
||
frontend_manager->set_output_device(device.id);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 启动音频流
|
||
std::cout << "启动音频流..." << std::endl;
|
||
result = frontend_manager->start_audio_stream();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "启动音频流失败: " << common::error_to_string(result) << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
// 运行10秒钟
|
||
std::cout << "音频处理中,将运行10秒..." << std::endl;
|
||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||
|
||
// 停止音频流
|
||
std::cout << "停止音频流..." << std::endl;
|
||
result = frontend_manager->stop_audio_stream();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "停止音频流失败: " << common::error_to_string(result) << std::endl;
|
||
}
|
||
|
||
// 断开音频引擎连接
|
||
frontend_manager->disconnect_from_engine();
|
||
|
||
// 移除事件监听器
|
||
frontend_manager->remove_event_listener(processor);
|
||
|
||
// 关闭前端管理器
|
||
frontend_manager->shutdown();
|
||
|
||
// 清理前端系统
|
||
frontend::shutdown_frontend();
|
||
|
||
std::cout << "程序已完成" << std::endl;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
### 远程音频传输
|
||
|
||
以下是使用前端接口进行远程音频传输的完整示例:
|
||
|
||
```cpp
|
||
#include "audio_backend/frontend.h"
|
||
#include <iostream>
|
||
#include <thread>
|
||
#include <chrono>
|
||
#include <atomic>
|
||
|
||
using namespace audio_backend;
|
||
|
||
// 定义传输角色
|
||
enum class TransportRole {
|
||
Sender,
|
||
Receiver
|
||
};
|
||
|
||
// 网络音频监听器
|
||
class NetworkAudioListener : public frontend::IFrontendEventListener {
|
||
public:
|
||
explicit NetworkAudioListener(TransportRole role)
|
||
: role_(role), audio_frames_(0) {}
|
||
|
||
void on_frontend_event(frontend::FrontendEvent event, const std::string& data) override {
|
||
std::cout << (role_ == TransportRole::Sender ? "发送端" : "接收端")
|
||
<< " 事件: ";
|
||
|
||
switch (event) {
|
||
case frontend::FrontendEvent::EngineConnected:
|
||
std::cout << "引擎已连接" << std::endl;
|
||
break;
|
||
case frontend::FrontendEvent::NetworkServiceFound:
|
||
std::cout << "发现服务: " << data << std::endl;
|
||
services_discovered_ = true;
|
||
break;
|
||
case frontend::FrontendEvent::AudioStreamStarted:
|
||
std::cout << "音频流已启动" << std::endl;
|
||
stream_active_ = true;
|
||
break;
|
||
case frontend::FrontendEvent::AudioStreamStopped:
|
||
std::cout << "音频流已停止" << std::endl;
|
||
stream_active_ = false;
|
||
break;
|
||
default:
|
||
std::cout << "其他事件" << std::endl;
|
||
break;
|
||
}
|
||
}
|
||
|
||
void on_audio_device_changed(const std::string& device_id, bool added) override {
|
||
// 不处理设备变化
|
||
}
|
||
|
||
void on_network_service_discovered(const std::string& service_name,
|
||
const std::string& address,
|
||
uint16_t port) override {
|
||
std::cout << "发现网络服务: " << service_name
|
||
<< " @ " << address << ":" << port << std::endl;
|
||
|
||
discovered_address_ = address;
|
||
discovered_port_ = port;
|
||
services_discovered_ = true;
|
||
}
|
||
|
||
void on_audio_stream_data(const engine::AudioBuffer& buffer) override {
|
||
audio_frames_ += buffer.frames();
|
||
|
||
// 每秒报告一次
|
||
auto now = std::chrono::steady_clock::now();
|
||
if (now - last_report_ > std::chrono::seconds(1)) {
|
||
double seconds = std::chrono::duration<double>(now - last_report_).count();
|
||
double frames_per_second = audio_frames_ / seconds;
|
||
|
||
std::cout << (role_ == TransportRole::Sender ? "发送" : "接收")
|
||
<< " 速率: " << frames_per_second << " 帧/秒 ("
|
||
<< (frames_per_second / buffer.sample_rate()) << "x 实时速率)"
|
||
<< std::endl;
|
||
|
||
audio_frames_ = 0;
|
||
last_report_ = now;
|
||
}
|
||
}
|
||
|
||
void on_frontend_error(common::ErrorCode error, const std::string& message) override {
|
||
std::cerr << (role_ == TransportRole::Sender ? "发送端" : "接收端")
|
||
<< " 错误: " << common::error_to_string(error)
|
||
<< " - " << message << std::endl;
|
||
}
|
||
|
||
bool has_discovered_service() const { return services_discovered_; }
|
||
std::string get_discovered_address() const { return discovered_address_; }
|
||
uint16_t get_discovered_port() const { return discovered_port_; }
|
||
bool is_stream_active() const { return stream_active_; }
|
||
|
||
private:
|
||
TransportRole role_;
|
||
uint64_t audio_frames_;
|
||
std::chrono::steady_clock::time_point last_report_ = std::chrono::steady_clock::now();
|
||
std::atomic<bool> services_discovered_{false};
|
||
std::atomic<bool> stream_active_{false};
|
||
std::string discovered_address_;
|
||
uint16_t discovered_port_ = 0;
|
||
};
|
||
|
||
// 远程音频传输示例
|
||
int main(int argc, char* argv[]) {
|
||
if (argc < 2) {
|
||
std::cerr << "用法: " << argv[0] << " [sender|receiver]" << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
std::string mode = argv[1];
|
||
TransportRole role = (mode == "sender") ? TransportRole::Sender : TransportRole::Receiver;
|
||
|
||
// 初始化前端系统
|
||
frontend::initialize_frontend();
|
||
|
||
// 创建前端管理器
|
||
auto frontend_manager = frontend::create_frontend_manager();
|
||
if (!frontend_manager) {
|
||
std::cerr << "创建前端管理器失败" << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// 配置前端管理器
|
||
auto config = frontend_manager->config();
|
||
config.enable_network_discovery = true;
|
||
config.enable_network_streaming = true;
|
||
config.audio_stream_port = 8888;
|
||
frontend_manager->update_config(config);
|
||
|
||
// 初始化前端管理器
|
||
auto result = frontend_manager->initialize();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "初始化前端管理器失败" << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// 创建并添加事件监听器
|
||
auto listener = std::make_shared<NetworkAudioListener>(role);
|
||
frontend_manager->add_event_listener(listener);
|
||
|
||
// 连接到音频引擎
|
||
result = frontend_manager->connect_to_engine();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "连接音频引擎失败" << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
if (role == TransportRole::Sender) {
|
||
// === 发送端 ===
|
||
|
||
// 配置音频设备
|
||
auto devices = frontend_manager->get_audio_devices();
|
||
|
||
// 设置默认输入设备
|
||
for (const auto& device : devices) {
|
||
if (device.is_default && device.is_input) {
|
||
frontend_manager->set_input_device(device.id);
|
||
std::cout << "使用输入设备: " << device.name << std::endl;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 启动音频流
|
||
result = frontend_manager->start_audio_stream();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "启动音频流失败" << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
// 启动网络发现(可选)
|
||
frontend_manager->start_network_discovery();
|
||
|
||
// 启动网络音频流
|
||
std::cout << "启动网络音频流,监听端口: " << config.audio_stream_port << std::endl;
|
||
|
||
// 这里使用本地地址,实际应用中可能是远程地址
|
||
result = frontend_manager->start_network_audio_stream("0.0.0.0", config.audio_stream_port);
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "启动网络音频流失败" << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
std::cout << "发送音频中,按回车停止..." << std::endl;
|
||
std::cin.get();
|
||
|
||
// 停止网络音频流
|
||
frontend_manager->stop_network_audio_stream();
|
||
|
||
// 停止音频流
|
||
frontend_manager->stop_audio_stream();
|
||
|
||
} else {
|
||
// === 接收端 ===
|
||
|
||
// 启动网络发现
|
||
std::cout << "启动网络服务发现..." << std::endl;
|
||
frontend_manager->start_network_discovery();
|
||
|
||
// 设置默认输出设备
|
||
auto devices = frontend_manager->get_audio_devices();
|
||
for (const auto& device : devices) {
|
||
if (device.is_default && device.is_output) {
|
||
frontend_manager->set_output_device(device.id);
|
||
std::cout << "使用输出设备: " << device.name << std::endl;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 等待发现服务
|
||
std::cout << "等待发现音频发送服务..." << std::endl;
|
||
|
||
// 在实际应用中,应该使用事件驱动方式等待服务发现
|
||
// 这里简单起见使用轮询
|
||
int timeout_seconds = 30;
|
||
for (int i = 0; i < timeout_seconds; i++) {
|
||
if (listener->has_discovered_service()) {
|
||
break;
|
||
}
|
||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||
std::cout << "等待中... " << (timeout_seconds - i - 1) << " 秒" << std::endl;
|
||
}
|
||
|
||
if (!listener->has_discovered_service()) {
|
||
std::cerr << "未发现音频服务,超时" << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
std::string address = listener->get_discovered_address();
|
||
uint16_t port = listener->get_discovered_port();
|
||
|
||
if (port == 0) {
|
||
port = 8888; // 使用默认端口
|
||
}
|
||
|
||
std::cout << "连接到音频服务: " << address << ":" << port << std::endl;
|
||
|
||
// 启动音频流
|
||
result = frontend_manager->start_audio_stream();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "启动音频流失败" << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
// 连接到网络音频流
|
||
result = frontend_manager->start_network_audio_stream(address, port);
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "连接网络音频流失败" << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
std::cout << "接收音频中,按回车停止..." << std::endl;
|
||
std::cin.get();
|
||
|
||
// 停止网络音频流
|
||
frontend_manager->stop_network_audio_stream();
|
||
|
||
// 停止音频流
|
||
frontend_manager->stop_audio_stream();
|
||
}
|
||
|
||
// 断开音频引擎连接
|
||
frontend_manager->disconnect_from_engine();
|
||
|
||
// 移除事件监听器
|
||
frontend_manager->remove_event_listener(listener);
|
||
|
||
// 关闭前端管理器
|
||
frontend_manager->shutdown();
|
||
|
||
// 清理前端系统
|
||
frontend::shutdown_frontend();
|
||
|
||
std::cout << "程序已完成" << std::endl;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
### 网络服务发现
|
||
|
||
以下是使用前端接口进行网络服务发现的示例:
|
||
|
||
```cpp
|
||
#include "audio_backend/frontend.h"
|
||
#include <iostream>
|
||
#include <thread>
|
||
#include <chrono>
|
||
#include <map>
|
||
#include <mutex>
|
||
|
||
using namespace audio_backend;
|
||
|
||
// 服务浏览器
|
||
class ServiceBrowser : public frontend::IFrontendEventListener {
|
||
public:
|
||
void on_frontend_event(frontend::FrontendEvent event, const std::string& data) override {
|
||
if (event == frontend::FrontendEvent::NetworkServiceFound) {
|
||
std::cout << "发现服务: " << data << std::endl;
|
||
} else if (event == frontend::FrontendEvent::NetworkServiceLost) {
|
||
std::cout << "丢失服务: " << data << std::endl;
|
||
}
|
||
}
|
||
|
||
void on_audio_device_changed(const std::string& device_id, bool added) override {
|
||
// 不处理设备变化
|
||
}
|
||
|
||
void on_audio_stream_data(const engine::AudioBuffer& buffer) override {
|
||
// 不处理音频数据
|
||
}
|
||
|
||
void on_network_service_discovered(const std::string& service_name,
|
||
const std::string& address,
|
||
uint16_t port) override {
|
||
std::lock_guard<std::mutex> lock(mutex_);
|
||
|
||
std::string key = service_name + "@" + address + ":" + std::to_string(port);
|
||
|
||
if (services_.find(key) == services_.end()) {
|
||
std::cout << "新服务: " << service_name << " @ " << address << ":" << port << std::endl;
|
||
|
||
services_[key] = {
|
||
service_name,
|
||
address,
|
||
port,
|
||
std::chrono::steady_clock::now()
|
||
};
|
||
} else {
|
||
// 更新最后发现时间
|
||
services_[key].last_seen = std::chrono::steady_clock::now();
|
||
}
|
||
}
|
||
|
||
void on_frontend_error(common::ErrorCode error, const std::string& message) override {
|
||
std::cerr << "错误: " << common::error_to_string(error) << " - " << message << std::endl;
|
||
}
|
||
|
||
void print_active_services() {
|
||
std::lock_guard<std::mutex> lock(mutex_);
|
||
|
||
auto now = std::chrono::steady_clock::now();
|
||
|
||
std::cout << "\n当前活动服务:" << std::endl;
|
||
std::cout << "-----------------------------------------" << std::endl;
|
||
|
||
int count = 0;
|
||
for (const auto& [key, service] : services_) {
|
||
// 检查服务是否在过去30秒内活动
|
||
auto age = std::chrono::duration_cast<std::chrono::seconds>(now - service.last_seen).count();
|
||
|
||
if (age <= 30) {
|
||
std::cout << ++count << ". " << service.name
|
||
<< " @ " << service.address << ":" << service.port
|
||
<< " (上次发现: " << age << " 秒前)" << std::endl;
|
||
}
|
||
}
|
||
|
||
if (count == 0) {
|
||
std::cout << "未发现活动服务" << std::endl;
|
||
}
|
||
|
||
std::cout << "-----------------------------------------" << std::endl;
|
||
}
|
||
|
||
private:
|
||
struct ServiceInfo {
|
||
std::string name;
|
||
std::string address;
|
||
uint16_t port;
|
||
std::chrono::steady_clock::time_point last_seen;
|
||
};
|
||
|
||
std::map<std::string, ServiceInfo> services_;
|
||
std::mutex mutex_;
|
||
};
|
||
|
||
// 网络服务发现示例
|
||
int main() {
|
||
// 初始化前端系统
|
||
frontend::initialize_frontend();
|
||
|
||
std::cout << "音频服务浏览器" << std::endl;
|
||
std::cout << "===========================================" << std::endl;
|
||
|
||
// 创建前端管理器
|
||
auto frontend_manager = frontend::create_frontend_manager();
|
||
if (!frontend_manager) {
|
||
std::cerr << "创建前端管理器失败" << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// 配置前端管理器
|
||
auto config = frontend_manager->config();
|
||
config.enable_network_discovery = true;
|
||
frontend_manager->update_config(config);
|
||
|
||
// 初始化前端管理器
|
||
auto result = frontend_manager->initialize();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "初始化前端管理器失败" << std::endl;
|
||
return 1;
|
||
}
|
||
|
||
// 创建服务浏览器
|
||
auto browser = std::make_shared<ServiceBrowser>();
|
||
frontend_manager->add_event_listener(browser);
|
||
|
||
// 启动网络服务发现
|
||
result = frontend_manager->start_network_discovery();
|
||
if (result != common::ErrorCode::SUCCESS) {
|
||
std::cerr << "启动网络服务发现失败" << std::endl;
|
||
frontend_manager->shutdown();
|
||
frontend::shutdown_frontend();
|
||
return 1;
|
||
}
|
||
|
||
std::cout << "开始浏览网络音频服务..." << std::endl;
|
||
std::cout << "每10秒打印一次活动服务列表" << std::endl;
|
||
std::cout << "按回车键退出" << std::endl;
|
||
|
||
// 创建一个独立线程来打印服务列表
|
||
std::atomic<bool> running(true);
|
||
std::thread printer_thread([&]() {
|
||
while (running) {
|
||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||
if (running) {
|
||
browser->print_active_services();
|
||
}
|
||
}
|
||
});
|
||
|
||
// 等待用户输入退出
|
||
std::cin.get();
|
||
running = false;
|
||
|
||
// 等待打印线程结束
|
||
if (printer_thread.joinable()) {
|
||
printer_thread.join();
|
||
}
|
||
|
||
// 停止网络服务发现
|
||
frontend_manager->stop_network_discovery();
|
||
|
||
// 移除事件监听器
|
||
frontend_manager->remove_event_listener(browser);
|
||
|
||
// 关闭前端管理器
|
||
frontend_manager->shutdown();
|
||
|
||
// 清理前端系统
|
||
frontend::shutdown_frontend();
|
||
|
||
std::cout << "程序已完成" << std::endl;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
## 性能优化
|
||
|
||
### 延迟优化
|
||
|
||
针对低延迟场景的优化策略:
|
||
|
||
1. **缓冲区大小**:使用小缓冲区大小(128-256帧)
|
||
```cpp
|
||
// 配置低延迟前端
|
||
auto config = frontend_manager->config();
|
||
config.audio_buffer_size = 128;
|
||
config.network_buffer_size = 512;
|
||
config.max_latency_ms = 20;
|
||
frontend_manager->update_config(config);
|
||
```
|
||
|
||
2. **编解码器设置**:使用低延迟编解码器配置
|
||
```cpp
|
||
// 低延迟Opus编解码器配置
|
||
auto codec_config = frontend::codec::CodecConfig{
|
||
frontend::codec::CodecType::OPUS,
|
||
48000, // 采样率
|
||
2, // 声道数
|
||
64000, // 比特率
|
||
5, // 中等复杂度
|
||
false, // 禁用VBR
|
||
true, // 启用FEC
|
||
10 // 10ms包大小
|
||
};
|
||
```
|
||
|
||
3. **网络传输优化**:使用UDP传输和抖动缓冲
|
||
```cpp
|
||
// 创建UDP传输
|
||
auto transport = frontend::create_udp_transport(config.audio_stream_port);
|
||
|
||
// 配置抖动缓冲区
|
||
auto buffer_config = frontend::network::BufferManagerConfig{
|
||
20, // 初始缓冲20ms
|
||
5, // 最小缓冲5ms
|
||
50, // 最大缓冲50ms
|
||
20, // 目标缓冲20ms
|
||
48000, // 采样率
|
||
2, // 声道数
|
||
true, // 启用自适应缓冲
|
||
8 // 快速自适应
|
||
};
|
||
```
|
||
|
||
### 带宽优化
|
||
|
||
针对带宽受限场景的优化策略:
|
||
|
||
1. **编解码器设置**:使用高压缩率配置
|
||
```cpp
|
||
// 带宽优化Opus编解码器配置
|
||
auto codec_config = frontend::codec::CodecConfig{
|
||
frontend::codec::CodecType::OPUS,
|
||
48000, // 采样率
|
||
2, // 声道数
|
||
32000, // 低比特率
|
||
10, // 高复杂度
|
||
true, // 启用VBR
|
||
true, // 启用FEC
|
||
40 // 40ms包大小(更大包减少带宽开销)
|
||
};
|
||
```
|
||
|
||
2. **数据压缩**:对元数据进行压缩
|
||
```cpp
|
||
// 启用元数据压缩
|
||
transport->enable_metadata_compression(true);
|
||
```
|
||
|
||
3. **优化采样率**:根据带宽调整采样率
|
||
```cpp
|
||
// 根据网络带宽动态调整音频质量
|
||
frontend_manager->set_adaptive_quality(true);
|
||
```
|
||
|
||
## 跨平台考虑
|
||
|
||
### 网络兼容性
|
||
|
||
不同平台的网络差异:
|
||
|
||
1. **Windows**:
|
||
- 使用Winsock 2 API
|
||
- mDNS实现使用Bonjour或DNS-SD
|
||
- 网络安全策略:Windows防火墙
|
||
|
||
2. **Linux**:
|
||
- 使用标准BSD套接字
|
||
- mDNS实现使用Avahi
|
||
- 网络安全策略:iptables或ufw
|
||
|
||
3. **macOS**:
|
||
- 使用标准BSD套接字
|
||
- mDNS实现使用Bonjour
|
||
- 网络安全策略:pf或应用沙盒
|
||
|
||
跨平台网络兼容性考虑:
|
||
```cpp
|
||
// 检测平台特定网络功能
|
||
bool is_mdns_supported() {
|
||
#ifdef _WIN32
|
||
// Windows: 检查Bonjour或DNS-SD
|
||
return check_windows_mdns_support();
|
||
#elif defined(__APPLE__)
|
||
// macOS: 内置Bonjour支持
|
||
return true;
|
||
#elif defined(__linux__)
|
||
// Linux: 检查Avahi
|
||
return check_linux_avahi_support();
|
||
#else
|
||
// 其他平台
|
||
return false;
|
||
#endif
|
||
}
|
||
```
|
||
|
||
### 设备兼容性
|
||
|
||
不同平台的音频设备差异:
|
||
|
||
1. **Windows**:
|
||
- 支持WASAPI、DirectSound、ASIO
|
||
- 设备枚举通过Windows Multimedia API
|
||
- 设备格式限制与平台特定
|
||
|
||
2. **Linux**:
|
||
- 支持ALSA、PulseAudio、JACK
|
||
- 设备枚举因音频系统而异
|
||
- 权限问题(如对/dev/snd的访问权限)
|
||
|
||
3. **macOS**:
|
||
- 支持CoreAudio
|
||
- 设备枚举通过Audio Device API
|
||
- 应用沙盒限制对设备的访问
|
||
|
||
跨平台设备兼容性考虑:
|
||
```cpp
|
||
// 获取平台最佳设备配置
|
||
frontend::DeviceConfiguration get_platform_optimal_config() {
|
||
frontend::DeviceConfiguration config;
|
||
|
||
#ifdef _WIN32
|
||
// Windows优化配置
|
||
config.api = "wasapi";
|
||
config.exclusive_mode = true;
|
||
config.buffer_size = 480; // 10ms @ 48kHz
|
||
#elif defined(__APPLE__)
|
||
// macOS优化配置
|
||
config.api = "coreaudio";
|
||
config.buffer_size = 256;
|
||
#elif defined(__linux__)
|
||
// Linux优化配置
|
||
// 检测是否有JACK
|
||
if (check_jack_available()) {
|
||
config.api = "jack";
|
||
config.buffer_size = 256;
|
||
} else {
|
||
config.api = "pulseaudio";
|
||
config.buffer_size = 480;
|
||
}
|
||
#endif
|
||
|
||
return config;
|
||
} |