Apple Media Service(AMS)を使ってAndroid WearからiPhoneのメディアを操作する話

1. 概要

iPhoneAndroid wearを使う話をしてきた.
で,その仕組み編として,前回の[通知に関するANCS話]に続いて, BLE経由でのメディアの操作や情報取得に関する Apple Media Service(AMS) についての話をする.
アプリを作った当初はこれに気づかずに,iPhone側にBLEで音楽を操作するアプリを仕込んだりしていた.

ぼくみたいなにわかiOSデベロッパが気づきにくい理由としては,AMSに関する解説記事とかブログみたいなものが全くなくて,公式のDeveloper Libraryでひっそりと公開されているだけだからだと思う.

iOS x BLEの参考書として有名な,堤さん(@shu223), 松村さん(@reo_matsumura)の,iOS×BLE Core Bluetoothプログラミング 本にもAMSのことは載ってなかったと思う.

www.amazon.co.jp

なので,メディア操作,楽曲情報取得に絞ってではあるが,簡単な解説記事を書こうと思う.

2. AMS: Apple Media Service の概要

これを利用して,BLE経由でiPhoneの音楽やビデオといったメディアが制御できるようになる. iPhoneの画面を下からスワイプした時にでてくるドロワーに音楽操作したり音量変えたりできるGUIがあるけど,あれをBLE経由で操作できると考えるといいと思う.なので,音楽だけでなくて,例えばビデオなんかも制御できる.

だから例えば,スマホ経由での操作だけじゃなくて,
BLEと慣性センサが載ったオリジナルデバイスからiPhoneのメディアを操作したりといったことがお手軽にできるようになる.

制御としては,次のようなことができる.

  • 再生/停止
  • 前の曲に戻る,次の曲に進む
  • 音量Up/Down
  • 曲情報や,プレイヤーアプリ状態の取得

補足: AMS用語

  • MS: Media Source
    • iOSデバイス等のメディアを保持,再生するデバイス
  • MR: Media Remote
    • MSのメディアを遠隔から操作するアクセサリデバイス

3. AMSのもつCharacteristicsについて

AMSは以下のUUIDのService,Characteristicsを持つ.これらのCharacteristicsの概要と利用方法は以降で述べる.

名前 UUID プロパティ 機能
Service Apple Media Service 89D3502B-0F36-433A-8EF4-C502AD55F8DC
Characteristics Remote Command 9B3C81D8-57B1-4A8A-B8DF-0E56F7CA51C2 write メディア,音量の操作
Entity Update 2F7CABCE-808D-411F-9A0C-BB92BA96C102 read, write, notify メディアや,プレイヤーアプリ情報のやりとり
Entity Attribute C6B2F38C-23AB-46D8-A6AB-A3A870BBD5D7 read, write EntityUpdateでは大きすぎるデータのやりとり

3.1. Remote Command Characteristic

このCharacteristicにコマンドを書き込むことで,メディアの操作(再生停止,前,次)や音量の操作を行う.

  • コマンド

以下のような,命令したい1Byteのコマンド(RemoteCommand)を書き込む.

Byte 1
Value RemoteCommendID
  • Remote Command IDの種類

現在は以下の11種類が提供されている.それぞれの機能はコマンド名そのまんま.

コマンド名 Value
RemoteCommandIDPlay 0
RemoteCommandIDPause 1
RemoteCommandIDTogglePlayPause 2
RemoteCommandIDNextTrack 3
RemoteCommandIDPreviousTrack 4
RemoteCommandIDVolumeUp 5
RemoteCommandIDVolumeDown 6
RemoteCommandIDAdvanceRepeatMode 7
RemoteCommandIDAdvanceShuffleMode 8
RemoteCommandIDSkipForward 9
RemoteCommandIDSkipBackward 10

3.2. Entity Update Characteristic

再生するメディアの情報やプレイヤーアプリの状態をやりとりする.Notifyを申し込んだ後に,欲しい情報を以下のコマンドを利用してCharacteristicに送信することで,その情報がNotifyされるようになる.

  • コマンド

以下のように,1Byte目でエンティティを選択,2Byte目以降でそのエンティティの中で取得したいアトリビュートを指定する.アトリビュート複数指定することができる.

Byte 1 2 3
Value EntityID Attribute1 Attribute2
  • Entity IDの種類

Entityは以下のようなものがある.例えば,曲情報が欲しい場合には "2"というEntityIDを使う.

名前 Value 説明
EntityIDPlayer 0 プレイヤーアプリ名や再生状態,音量に関するエンティティ
EntityIDQueue 1 メディアQueueに関するEntity
EntityIDTrack 2 曲等のTrackに関するEntity
  • TrackAttributeの種類(EntityIDTrackの場合)

EntityIDごとのAttributeについては公式資料を確認して欲しい.ここでは,例としてEntityIDTrackを指定した場合に選択可能なAttributeについて説明する.

名前 Value 説明
TrackAttributeIDArtist 0 アーティスト名
TrackAttributeIDAlbum 1 アルバム名
TrackAttributeIDTitle 2 曲名
TrackAttributeIDDuration 3 曲の長さ(秒単位)
  • コマンドを送信するコードの例

Notifyを申請した後に,その成功/失敗がonDescriptorWriteを通して通知される.成功だった場合に,以下のようにコマンドを送信して欲しい情報を申請する.この例では,EntityIDTrackのうち,曲名とアーティスト名に関する情報を取得する.

 @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            Log.d(TAG_LOG, " onDescriptorWrite:: " + status);

            // Notification source
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG_LOG, "status: write success ");

                //find AMS
                BluetoothGattService service = bluetooth_gatt.getService(UUID.fromString(service_ams));
                if(service != null) {
                    BluetoothGattCharacteristic chara = service.getCharacteristic(UUID.fromString(characteristics_entity_update));
                    if(chara != null) {
                        chara.setValue(new byte[]{(byte) 0x02, (byte) 0x00, (byte) 0x02});
                        bluetooth_gatt.writeCharacteristic(chara);
                    }
                }
            }
        }

Notifyされてくるデータ

  • パケット構造

1Byte目にエンティティを示すIDが入っており,2Byte目にAttributeを示すIDが入っており,4Byte目以降は実際の情報が入っている.

  @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            Log.d(TAG_LOG, "onCharacteristicChanged:: " + characteristic.getUuid().toString());

            if (characteristics_entity_update.toString().equals(characteristic.getUuid().toString())) {
                Log.d(TAG_LOG, "entity update ");

                byte[] get_data = characteristic.getValue();
                String music_info = characteristic.getStringValue(3);
                Log.d(TAG_LOG, "new music info: " + music_info);

                if(get_data[1] == (byte)0x00) {
                    Log.d(TAG_LOG, "get artist info: " + music_info);
                }else if(get_data[1] == (byte)0x02) {
                    Log.d(TAG_LOG, "get title info: " + music_info);
                }
            }
        }

Entity Attribute

Entity Updateでは,データが大きすぎて伝えられない情報をやりとりする.
ちなみに作成したアプリでは,Android wearで表示する文字が多くなると文字が小さくなって見づらくなるのでこれを利用していない.

  • パケット

取得したい情報のEntity,AttributeのIDを指定する.

Byte 1 2
Value EntityID AttributeID

3. シーケンス

3-1. 準備

AMSのEntityUpdateCharacteristicを探してほしい情報を登録すると,曲が変わったタイミングなどでNotifyされてくる.

fig2-2

3-2. メディアの操作

RemoteCommandCharacteristicを探して,操作したいコマンドを送信すると,MS側でその操作が実行される.

Fig 2-3

3-3. メディア情報の取得

3-1. 準備 の手順で曲名の一部(曲名が長い場合は先頭のみ)が送られてくるが,正式なタイトル情報が知りたい場合はEntityAttributeへコマンドを書き込み情報を読み出す.

Fig2-4

4. まとめと実際のコード

音楽のアーティスト名と曲名を取得するという例にそって,AMSの使い方を簡単に説明した.
シンプルなので,BLE Centralなプログラムが書ける人には難しくないと思う.
実際にアプリの中では,MusicControlActivityの中で利用している.