ESP-ADF esp_dispatcher组件之audio_service子模块回调管理函数详解
目录
- ESP-ADF esp_dispatcher组件之audio_service子模块回调管理函数详解
- 回调机制概述
- 回调管理函数分析
- audio_service_set_callback
- audio_service_callback
- 回调机制的实际应用
- 典型音频事件类型
- 回调注册流程
- 回调处理示例
- 服务内部触发回调示例
- 回调管理的优势
- 回调管理与其他功能的集成
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子模块为各种音频服务提供了统一的管理框架,简化了音频服务的开发和使用,使应用程序能够以一致的方式处理不同类型的音频服务和事件。