Communication

MediaRemoteTV is a TCP based protocol which uses length prefixed protobuf encoded messages to communicate between the client and server. The prefixed length is encoded as a Google Protobuf base 128 varint.

The Apple TV acts as the server and clients can connect to it in order to issue various commands (playback, keyboard, voice, game controller, etc).

All messages are encrypted after negotiating the pairing between the client and the Apple TV.

Protocol Message

Each message exchanged between the Apple TV and the remote client is wrapped in a common envelope which contains the type of the message among other fields.

syntax = "proto2";

message ProtocolMessage {
  extensions 6 to max;

  enum Type { 
    SEND_COMMAND_MESSAGE = 1;
    SEND_COMMAND_RESULT_MESSAGE = 2;
    GET_STATE_MESSAGE = 3;
    SET_STATE_MESSAGE = 4;
    SET_ARTWORK_MESSAGE = 5;
    REGISTER_HID_DEVICE_MESSAGE = 6;
    REGISTER_HID_DEVICE_RESULT_MESSAGE = 7;
    SEND_HID_EVENT_MESSAGE = 8;
    SEND_HID_REPORT_MESSAGE = 9;
    SEND_VIRTUAL_TOUCH_EVENT_MESSAGE = 10;
    NOTIFICATION_MESSAGE = 11;
    CONTENT_ITEMS_CHANGED_NOTIFICATION_MESSAGE = 12;
    // 13-14 not used
    DEVICE_INFO_MESSAGE = 15; 
    CLIENT_UPDATES_CONFIG_MESSAGE = 16;
    VOLUME_CONTROL_AVAILABILITY_MESSAGE = 17;
    GAME_CONTROLLER_MESSAGE = 18;
    REGISTER_GAME_CONTROLLER_MESSAGE = 19;
    REGISTER_GAME_CONTROLLER_RESPONSE_MESSAGE = 20;
    UNREGISTER_GAME_CONTROLLER_MESSAGE = 21;
    REGISTER_FOR_GAME_CONTROLLER_EVENTS_MESSAGE = 22;
    KEYBOARD_MESSAGE = 23;
    GET_KEYBOARD_SESSION_MESSAGE = 24;
    TEXT_INPUT_MESSAGE = 25;
    GET_VOICE_INPUT_DEVICES_MESSAGE = 26;
    GET_VOICE_INPUT_DEVICES_RESPONSE_MESSAGE = 27;
    REGISTER_VOICE_INPUT_DEVICE_MESSAGE = 28;
    REGISTER_VOICE_INPUT_DEVICE_RESPONSE_MESSAGE = 29;
    SET_RECORDING_STATE_MESSAGE = 30;
    SEND_VOICE_INPUT_MESSAGE = 31;
    GET_PLAYBACK_QUEUE_MESSAGE = 32;
    TRANSACTION_MESSAGE = 33;
    CRYPTO_PAIRING_MESSAGE = 34; 
    GAME_CONTROLLER_PROPERTIES_MESSAGE = 35;
    SET_READY_STATE_MESSAGE = 36;
    DEVICE_INFO_UPDATE_MESSAGE = 37;
    SET_CONNECTION_STATE_MESSAGE = 38;
    SEND_BUTTON_EVENT_MESSAGE = 39;
    SET_HILITE_MODE_MESSAGE = 40;
    WAKE_DEVICE_MESSAGE = 41;
    GENERIC_MESSAGE = 42;
    SEND_PACKED_VIRTUAL_TOUCH_EVENT_MESSAGE = 43;
    SEND_LYRICS_EVENT_MESSAGE = 44;
    SET_NOW_PLAYING_CLIENT_MESSAGE = 46;
    SET_NOW_PLAYING_PLAYER_MESSAGE = 47;
  }

  required Type type = 1; // Identifies which underlying message is filled in.
  optional string identifier = 2;
  optional string authenticationToken = 3;
  optional int32 errorCode = 4;
  optional uint64 timestamp = 5;

}

Identifiers

As MediaRemoteTV is an asynchronous protocol, identifiers are used to map a responses to requests. By setting identifier in the envelope message (ProtocolMessage) to a random UUID4 before sending it, the response will contain the same identifier upon reception.

Not all messages use identifiers, nor does all of them support it. A message will only contain an identifer if it was triggered by a request. No "events" (e.g. status updates about playing media) messages will contain identifiers. Also, some other messages implicitly does not support it, like CryptoPairingMessage. In the latter case, no messages of other types can occur simultaneously so identifiers are not needed.

An example of a message using an identifier can be seen below: DeviceInfoMessage

Initiating a Connection

After discovering the MediaRemoteTV service, you open a TCP connection to the discovered port, 49152 on tvOS version 9.2.1.

Once the connection is open, the Apple TV expects a DeviceInfoMessage to be sent.

syntax = "proto2";

import "ProtocolMessage.proto";

extend ProtocolMessage {
  optional DeviceInfoMessage deviceInfoMessage = 20;
}

message DeviceInfoMessage {
  required string uniqueIdentifier = 1; // Example: B8D8678C-9DA9-4D29-9338-5D6B827B8063
  required string name = 2; // Example: Jean's iPhone
  optional string localizedModelName = 3; // Example: iPhone
  required string systemBuildVersion = 4; // Example: 13F69
  required string applicationBundleIdentifier = 5; // Example: com.example.myremote
  optional string applicationBundleVersion = 6; // Example: 107
  required int32 protocolVersion = 7; // Example: 1
}

CLIENT -> SERVER

type: DEVICE_INFO_MESSAGE
identifier: "4D673CA3-03A8-4343-82CF-4FA2B6C0CFF1"
priority: 0
[deviceInfoMessage] {
  uniqueIdentifier: "B8D8678C-9DA9-4D29-9338-5D6B827B8063"
  name: "Jean\'s iPhone"
  localizedModelName: "iPhone"
  systemBuildVersion: "13F69"
  applicationBundleIdentifier: "com.example.myremote"
  applicationBundleVersion: "5"
  protocolVersion: 1
}

SERVER -> CLIENT

type: DEVICE_INFO_MESSAGE
identifier: "4D673CA3-03A8-4343-82CF-4FA2B6C0CFF1"
priority: 0
[deviceInfoMessage] {
  uniqueIdentifier: "B52D1E7B-66FD-4632-8FBB-644614325855"
  name: "Apple TV"
  systemBuildVersion: "13Y825"
  applicationBundleIdentifier: "com.apple.mediaremoted"
  protocolVersion: 1
}

This exchange allows the client and server to known whether they've already paired each other.

Then the pairing process begins.

results matching ""

    No results matching ""