目录
ESP-ADF esp_dispatcher组件之audio_service子模块回调管理函数详解
版本信息: v2.7-65-gcf908721
本章节分析的源码位于
/components/esp_dispatcher/audio_service.c
文件
回调机制概述
audio_service子模块的回调管理函数负责设置和触发事件回调,是音频服务与应用程序之间通信的桥梁。回调机制允许音频服务将检测到的事件(如播放状态变化、连接状态变化等)通知给应用程序,应用程序则可以根据这些事件执行相应的逻辑。
回调机制的核心是以下事件结构和回调函数类型定义:
/**
* @brief 音频服务事件信息
*/
typedef struct {
int type; // 事件类型
void *source; // 事件源
void *data; // 事件数据
int len; // 数据长度
} service_event_t;
/**
* @brief 服务回调函数类型
*/
typedef esp_err_t (*service_callback)(audio_service_handle_t handle, service_event_t *evt, void *ctx);
通过这个回调机制,audio_service子模块实现了以下功能:
- 事件隔离:应用程序不需要直接处理底层音频事件
- 统一接口:不同类型的音频服务使用相同的回调机制
- 上下文传递:回调函数可以接收用户提供的上下文数据
- 灵活处理:应用程序可以根据事件类型和数据执行不同的逻辑
回调管理函数分析
audio_service_set_callback
audio_service_set_callback
函数用于设置音频服务的事件回调函数。下面是其源码实现:
/**
* @brief 设置音频服务的事件回调函数
*
* 该函数为音频服务设置一个回调函数,用于接收服务产生的事件
* 回调函数会在服务检测到事件时被调用,如播放状态变化、连接状态变化等
*
* @param handle 音频服务实例句柄
* @param cb 回调函数指针
* @param ctx 传递给回调函数的上下文指针,可以是任意用户数据
* @return esp_err_t 始终返回ESP_OK
*/
esp_err_t audio_service_set_callback(audio_service_handle_t handle, service_callback cb, void *ctx)
{
// 获取服务实例
audio_service_impl_t *impl = (audio_service_impl_t *) handle;
// 设置回调函数和上下文
impl->callback_func = cb;
impl->user_cb_ctx = ctx;
// 始终返回成功
return ESP_OK;
}
下面的时序图展示了 audio_service_set_callback
函数的执行流程:
audio_service_set_callback
函数的实现非常简单直接,它将应用程序提供的回调函数和上下文保存在服务实例中,供后续触发回调时使用。
audio_service_callback
audio_service_callback
函数用于触发音频服务的事件回调。下面是其源码实现:
/**
* @brief 触发音频服务的事件回调
*
* 该函数由音频服务在检测到事件时调用,用于通知应用程序
* 如果设置了回调函数,会调用该函数并传递事件数据
*
* @param handle 音频服务实例句柄
* @param evt 事件数据结构指针
* @return esp_err_t 成功返回ESP_OK或回调函数的返回值,失败返回错误码
*/
esp_err_t audio_service_callback(audio_service_handle_t handle, service_event_t *evt)
{
// 获取服务实例并进行参数检查
audio_service_impl_t *impl = (audio_service_impl_t *) handle;
AUDIO_NULL_CHECK(TAG, (handle && impl->callback_func), return ESP_ERR_INVALID_ARG);
// 调用回调函数,传递服务句柄、事件数据和上下文
return impl->callback_func(handle, evt, impl->user_cb_ctx);
}
下面的时序图展示了 audio_service_callback
函数的执行流程:
audio_service_callback
函数在检测到事件后,会调用应用程序提供的回调函数,并传递服务句柄、事件数据和上下文。这个函数通常由服务实现内部调用,而不是由应用程序直接调用。
回调机制的实际应用
典型音频事件类型
音频服务可能产生各种类型的事件,包括但不限于:
-
连接事件:
- 连接中(CONNECTING)
- 已连接(CONNECTED)
- 连接失败(CONNECTION_FAILED)
- 断开连接(DISCONNECTED)
-
播放控制事件:
- 播放(PLAY)
- 暂停(PAUSE)
- 停止(STOP)
- 下一曲(NEXT)
- 上一曲(PREV)
-
状态变化事件:
- 缓冲中(BUFFERING)
- 播放中(PLAYING)
- 已暂停(PAUSED)
- 已停止(STOPPED)
-
信息事件:
- 元数据更新(METADATA_UPDATED)
- 音量变化(VOLUME_CHANGED)
- 错误发生(ERROR_OCCURRED)
回调注册流程
应用程序通常在服务创建后,连接前注册回调函数,以确保可以接收到服务连接过程中产生的所有事件。下面是回调注册的典型流程:
回调处理示例
下面是一个蓝牙音频服务回调处理的示例:
// 应用程序回调函数
static esp_err_t bt_audio_service_cb(audio_service_handle_t handle, service_event_t *evt, void *ctx)
{
// 获取用户上下文
app_context_t *app_ctx = (app_context_t *)ctx;
// 根据事件类型处理
switch (evt->type) {
case BT_A2D_CONNECTION_STATE_EVT:
if (evt->data) {
uint8_t state = *((uint8_t *)evt->data);
if (state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
ESP_LOGI(TAG, "Bluetooth audio connected");
// 执行连接后逻辑
app_ctx->bt_connected = true;
// 可能需要启动音频服务
audio_service_start(handle);
} else if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
ESP_LOGI(TAG, "Bluetooth audio disconnected");
// 执行断开后逻辑
app_ctx->bt_connected = false;
// 可能需要停止音频服务
audio_service_stop(handle);
}
}
break;
case BT_A2D_AUDIO_STATE_EVT:
if (evt->data) {
uint8_t state = *((uint8_t *)evt->data);
if (state == ESP_A2D_AUDIO_STATE_STARTED) {
ESP_LOGI(TAG, "Bluetooth audio playing");
// 执行播放开始逻辑
app_ctx->bt_playing = true;
} else if (state == ESP_A2D_AUDIO_STATE_STOPPED) {
ESP_LOGI(TAG, "Bluetooth audio stopped");
// 执行播放停止逻辑
app_ctx->bt_playing = false;
}
}
break;
case BT_A2D_AUDIO_CFG_EVT:
if (evt->data && evt->len >= sizeof(esp_a2d_audio_cfg_t)) {
esp_a2d_audio_cfg_t *cfg = (esp_a2d_audio_cfg_t *)evt->data;
ESP_LOGI(TAG, "Audio configuration: %d Hz, %d bit, %d channel",
cfg->sample_rate, cfg->bits_per_sample, cfg->channel_count);
// 更新音频配置
app_ctx->audio_cfg = *cfg;
// 可能需要重新配置音频输出
reconfigure_audio_output(app_ctx);
}
break;
default:
ESP_LOGI(TAG, "Unknown bluetooth audio event: %d", evt->type);
break;
}
return ESP_OK;
}
服务内部触发回调示例
下面是一个音频服务内部如何触发回调的示例:
// 蓝牙连接状态回调处理函数
static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
{
bt_audio_impl_t *impl = bt_audio_instance;
switch (event) {
case ESP_A2D_CONNECTION_STATE_EVT:
{
// 创建事件数据
service_event_t evt = {
.type = BT_A2D_CONNECTION_STATE_EVT,
.source = impl,
.data = ¶m->conn_stat.state,
.len = sizeof(uint8_t)
};
// 更新内部状态
if (param->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
impl->connected = true;
impl->state = SERVICE_STATE_CONNECTED;
} else if (param->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
impl->connected = false;
impl->state = SERVICE_STATE_IDLE;
}
// 触发回调
audio_service_callback((audio_service_handle_t)impl, &evt);
}
break;
case ESP_A2D_AUDIO_STATE_EVT:
{
// 创建事件数据
service_event_t evt = {
.type = BT_A2D_AUDIO_STATE_EVT,
.source = impl,
.data = ¶m->audio_stat.state,
.len = sizeof(uint8_t)
};
// 更新内部状态
if (param->audio_stat.state == ESP_A2D_AUDIO_STATE_STARTED) {
impl->playing = true;
} else if (param->audio_stat.state == ESP_A2D_AUDIO_STATE_STOPPED) {
impl->playing = false;
}
// 触发回调
audio_service_callback((audio_service_handle_t)impl, &evt);
}
break;
default:
break;
}
}
回调管理的优势
audio_service子模块的回调管理机制具有以下优势:
- 解耦性:服务实现和应用程序逻辑完全分离,提高了代码的模块化程度
- 统一接口:不同类型的音频服务使用相同的回调接口,便于应用程序统一处理
- 上下文传递:可以传递任意用户数据作为上下文,使回调函数能够访问应用程序状态
- 事件驱动:采用事件驱动模型,提高了系统响应效率
- 简单易用:回调设置和触发接口简单清晰,易于使用
通过回调管理函数,audio_service子模块实现了高效的事件通知机制,使音频服务能够将事件及时传递给应用程序,应用程序也能根据事件执行相应的逻辑,提高了系统的灵活性和响应性。
回调管理与其他功能的集成
回调管理机制是audio_service子模块功能体系中的重要组成部分,它与其他功能(资源管理、连接管理、状态控制等)紧密集成,共同构成了完整的音频服务管理框架。
下面的图表展示了回调管理在audio_service功能体系中的位置:
通过这种集成,audio_service子模块为各种音频服务提供了统一的管理框架,简化了音频服务的开发和使用,使应用程序能够以一致的方式处理不同类型的音频服务和事件。