AMS 使用说明
AMS 简介
AMS 是一种在 iOS 系统中通过 BLE 连接,允许外部设备(如智能手表)远程控制 iOS 设备上的媒体播放器的服务。 通过 AMS,外部设备不仅可以控制 iOS 端的音乐播放(如播放、暂停、切歌等),还能获取当前 iOS 媒体播放器播放音乐的详细信息。
备注
有关详细规范,可参考: Apple 媒体服务 (AMS) 规范
AMS 服务包含三个主要特征:
Remote Command(远程命令) 用于向媒体播放器发送控制指令,如播放、暂停、上一曲、下一曲等。
Entity Update(实体更新) 用于获取媒体播放器的当前状态信息,比如播放状态、音量等。
Entity Attribute(实体属性) 用于读取或写入各类实体的详细属性信息。
Entity Update 主要支持三类实体(Entity):
播放器(Player) 表示当前活跃的媒体应用。其属性主要包括名称、播放状态、音量等。
队列(Queue) 表示当前已加载的播放队列。
音轨(Track) 表示当前正在播放的音轨。其属性主要包括艺术家、标题和播放时间等。
基于 SDK 的 AMS 调用流程
本节将介绍如何在当前 SDK 环境下,集成并调用 AMS 相关功能,实现蓝牙远程控制 iOS 音乐播放器。
-
服务初始化
-
在
app_ble_service_init()
中调用ams_init()
及client_init()
,完成 AMS 服务和客户端的初始化。client_init(1); ams_init(1);
-
-
消息处理注册
-
在
watch_handle_io_message()
函数中,添加对IO_MSG_TYPE_AMS
类型消息的处理,并调用ams_handle_msg()
进行分发和处理。#if (F_APP_BLE_AMS_CLIENT_SUPPORT == 1) case IO_MSG_TYPE_AMS: { ams_handle_msg(p_watch_msg); } break; #endif
-
-
服务发现流程
-
在
app_handle_authen_state_evt()
中,添加ams_start_discovery()
调用,发起 AMS 服务的发现操作。#if (F_APP_BLE_AMS_CLIENT_SUPPORT == 1) ams_start_discovery(conn_id); #endif
-
若同时需要使用 AMS 与 ANCS 服务,请在
ancs_client_cb()
回调函数内再次调用ams_start_discovery()
。case ANCS_WRITE_NOTIFICATION_SOURCE_NOTIFY_ENABLE: APP_PRINT_INFO0("ANCS_WRITE_NOTIFICATION_SOURCE_NOTIFY_ENABLE"); ams_start_discovery(conn_id); break;
-
-
特征订阅
-
成功发现服务后,在
ams_client_cb()
回调函数中,订阅 Remote Command 和 Entity Update 两个特征。-
订阅 Remote Command:
case AMS_DISC_DONE: APP_PRINT_INFO0("ams_client_cb: discover procedure done."); ams_subscribe_remote_cmd(conn_id, true); break;
-
订阅 Entity Update:
case AMS_WRITE_REMOTE_CMD_NOTIFY_ENABLE: APP_PRINT_INFO0("AMS_WRITE_REMOTE_CMD_NOTIFY_ENABLE"); ams_subscribe_entity_upd(conn_id, true); break;
-
-
-
远程命令发送
订阅完成后即可开始使用 AMS 服务。例如可于
play_pause_touch_cb()
回调中,调用ams_write_remote_cmd()
发送 Remote Command(如播放/暂停控制)。-
Remote Command 的参数为 remote command id,用于控制音乐播放器的暂停、播放等操作。
//在此处调用 player 界面 uint8_t conn_id = 0; T_AMS_MSG ams_msg_rem_cmd; ams_msg_rem_cmd.type = AMS_MSG_TYPE_REMOTE_CMD; ams_msg_rem_cmd.remote_cmd_id = (T_AMS_REMOTE_CMD_ID)REMOTE_CMD_ID_TOGGLE_PLAY_PAUSE; ams_send_msg(conn_id, &ams_msg_rem_cmd); ams_send_msg_to_app(conn_id);
-
请求及获取 Entity Update
-
向 AMS 服务器请求 Entity Update 信息。Entity 包含 Player、Queue 和 Track 三大类。收到的数据内容可在日志中查看。
case AMS_WRITE_ENTITY_UPD_NOTIFY_ENABLE: { APP_PRINT_INFO0("AMS_WRITE_ENTITY_UPD_NOTIFY_ENABLE"); T_AMS_MSG ams_msg; static uint8_t cmd[5]; cmd[0] = ENTITY_ID_TRACK; cmd[1] = TRACK_ATTR_ID_ARTIST; cmd[2] = TRACK_ATTR_ID_ALBUM; cmd[3] = TRACK_ATTR_ID_TITLE; cmd[4] = TRACK_ATTR_ID_DURATION; ams_msg.type = AMS_MSG_ENTITY_UPDATE_CMD; ams_msg.param = cmd; ams_msg.param_len = 5; ams_send_msg(conn_id, &ams_msg); T_AMS_MSG ams_msg1; static uint8_t cmd1[5]; cmd1[0] = ENTITY_ID_QUEUE; cmd1[1] = QUEUE_ATTR_ID_INDEX; cmd1[2] = QUEUE_ATTR_ID_COUNT; cmd1[3] = QUEUE_ATTR_ID_SHUFFLE_MODE; cmd1[4] = QUEUE_ATTR_ID_REPEAT_MODE; ams_msg1.type = AMS_MSG_ENTITY_UPDATE_CMD; ams_msg1.param = cmd1; ams_msg1.param_len = 5; ams_send_msg(conn_id, &ams_msg1); T_AMS_MSG ams_msg2; static uint8_t cmd2[4]; cmd2[0] = ENTITY_ID_PLAYER; cmd2[1] = PLAYER_ATTR_ID_VOLUME_NAME; cmd2[2] = PLAYER_ATTR_ID_PLAYBACK_INFO; cmd2[3] = PLAYER_ATTR_ID_VOLUME; ams_msg2.type = AMS_MSG_ENTITY_UPDATE_CMD; ams_msg2.param = cmd2; ams_msg2.param_len = 4; ams_send_msg(conn_id, &ams_msg2); } break;
-
-
解析 Entity Update 内容
-
获取到 Entity Update 数据后,其前三字节为
T_AMS_ENTITY_ID
及相应ATTR_ID
。从第四字节起为具体内容。case AMS_NOTIFY_FROM_ENTITY_UPD: { APP_PRINT_INFO2("AMS_NOTIFY_FROM_ENTITY_UPD: data[%d]: %b", p_cb_data->cb_content.notify_data.value_size, TRACE_BINARY(p_cb_data->cb_content.notify_data.value_size, p_cb_data->cb_content.notify_data.p_value)); uint32_t msg_num; os_msg_queue_peek(ams_queue_handle, &msg_num); if (msg_num) { ams_send_msg_to_app(conn_id); } ams_parse_entity_update(p_cb_data->cb_content.notify_data.p_value, p_cb_data->cb_content.notify_data.value_size); } break;
输出 log 如下:
AMS_NOTIFY_FROM_ENTITY_UPD: data[10]: 02 03 00 32 35 32 2E 30 30 30
-
具体解析过程可在
ams_parse_entity_update()
函数内部查看,或打印日志结果。PLAYER 相关: //获取播放器的名称 case PLAYER_ATTR_IDPLAYER_ATTR_ID_VOLUME_NAME: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS PLAYER_ATTR_IDPLAYER_ATTR_ID_VOLUME_NAME: %b", TRACE_STRING((uint8_t *)(p_data + 3))); } break; //获取歌曲的播放状态/播放速率/已经播放当前歌曲的时间 case PLAYER_ATTR_ID_PLAYBACK_INFO: { APP_PRINT_INFO0("AMS PLAYER_ATTR_ID_PLAYBACK_INFO"); *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS player_status, player_rate, elapsed_time = %s", TRACE_STRING((uint8_t *)(p_data + 3))); } break; //获取音量 case PLAYER_ATTR_ID_VOLUME: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS PLAYER_ATTR_ID_VOLUME = %s", TRACE_STRING((uint8_t *)(p_data + 3))); } break;
QUEUE 相关: //当前播放歌曲是列表中的第几首 case QUEUE_ATTR_ID_INDEX: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS PLAYER_ATTR_ID_VQUEUE_ATTR_ID_INDEXOLUME = %s", TRACE_STRING((uint8_t *)(p_data + 3))); } break; //列表歌曲总数 case QUEUE_ATTR_ID_COUNT: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS QUEUE_ATTR_ID_COUNT = %s", TRACE_STRING((uint8_t *)(p_data + 3))); } break; //随机播放模式(非随机 0;单曲随机 1;列表随机 2) case QUEUE_ATTR_ID_SHUFFLE_MODE: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS QUEUE_ATTR_ID_SHUFFLE_MODE = %s", TRACE_STRING((uint8_t *)(p_data + 3))); } break; //重复播放模式(非重复 0;单曲重复 1;列表循环 2) case QUEUE_ATTR_ID_REPEAT_MODE: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS QUEUE_ATTR_ID_REPEAT_MODE = %s", TRACE_STRING((uint8_t *)(p_data + 3))); } break;
TRACK 相关: //获取歌手名字 case TRACK_ATTR_ID_ARTIST: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS TRACK_ATTR_ID_ARTIST: %b", TRACE_STRING((uint8_t *)(p_data + 3))); } break; //获取专辑名字 case TRACK_ATTR_ID_ALBUM: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS TRACK_ATTR_ID_ALBUM: %b", TRACE_STRING((uint8_t *)(p_data + 3))); } break; //获取歌曲名称 case TRACK_ATTR_ID_TITLE: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS TRACK_ATTR_ID_TITLE: %b", TRACE_STRING((uint8_t *)(p_data + 3))); } break; //获取当前播放歌曲总时长 case TRACK_ATTR_ID_DURATION: { *(uint8_t *)(p_data + len) = '\0'; APP_PRINT_INFO1("AMS TRACK_ATTR_ID_DURATION = %s", TRACE_STRING((uint8_t *)(p_data + 3))); } break;
输出 log 如下:
AMS PLAYER_ATTR_IDPLAYER_ATTR_ID_VOLUME_NAME: 网易云音乐 AMS player_status, player_rate, elapsed_time = 1,1.0,0.081 //音量的区间是 0-1,每按动一次增/减 0.0625 AMS PLAYER_ATTR_ID_VOLUME = 0.0625
-
-
实际功能触发
通过上述流程配置后,将手表通过 BLE 与手机建立连接。连接成功后,打开手机音乐播放器,在手表触摸暂停/播放按钮,即可远程控制手机音乐播放/暂停。
其他控制功能可按需配置、集成。
备注
请根据实际业务流程补充接口调用的具体细节及数据收发例程。