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 音乐播放器。

  1. 服务初始化

    • app_ble_service_init() 中调用 ams_init()client_init(),完成 AMS 服务和客户端的初始化。

      client_init(1);
      ams_init(1);
      
  2. 消息处理注册

    • 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
      
  3. 服务发现流程

    • 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;
      
  4. 特征订阅

    • 成功发现服务后,在 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;
        
  5. 远程命令发送

    • 订阅完成后即可开始使用 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);
      
  6. 请求及获取 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;
      
  7. 解析 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
      
  8. 实际功能触发

    • 通过上述流程配置后,将手表通过 BLE 与手机建立连接。连接成功后,打开手机音乐播放器,在手表触摸暂停/播放按钮,即可远程控制手机音乐播放/暂停。

    • 其他控制功能可按需配置、集成。

备注

请根据实际业务流程补充接口调用的具体细节及数据收发例程。