Skip to content

Version 3

Overview

The protocol is a TCP protocol on port 46899.

The header packet structure is defined as:

struct HEADER {
   uint32_t size; //Little Endian
   uint8_t opcode;
} header;

For a packet with no body, only the header is sent. In this case, size = 1.

When a body is attached, it has the following format.

struct BODY {
   uint8_t bytes[header.size - 1];
};

The body is a JSON string, encoded using UTF-8. Note the total packet size is max 32KB. Consequently, the maximum body size is 32000 - 1.

Opcode Name Description
0 None Not used
Play 1 Sender message to play media content, body is PlayMessage
Pause 2 Sender message to pause media content, no body
Resume 3 Sender message to resume media content, no body
Stop 4 Sender message to stop media content, no body
Seek 5 Sender message to seek, body is SeekMessage
PlaybackUpdate 6 Receiver message to notify an updated playback state, body is PlaybackUpdateMessage
VolumeUpdate 7 Receiver message to notify when the volume has changed, body is VolumeUpdateMessage
SetVolume 8 Sender message to change volume, body is SetVolumeMessage
PlaybackError 9 Server message to notify the sender a playback error happened, body is PlaybackErrorMessage
SetSpeed 10 Sender message to change playback speed, body is SetSpeedMessage
Version 11 Message to notify the other of the current version, body is VersionMessage
Ping 12 Message to get the other party to pong, no body
Pong 13 Message to respond to a ping from the other party, no body
Initial 14 Message to notify the other party of device information and state, body is InitialSenderMessage if receiver or InitialReceiverMessage if sender
PlayUpdate 15 Receiver message to notify all senders when any device has sent a Play message, body is PlayUpdateMessage
SetPlaylistItem 16 Sender message to set the item index in a playlist to play content from, body is SetPlaylistItemMessage
SubscribeEvent 17 Sender message to subscribe to a receiver event, body is SubscribeEventMessage
UnsubscribeEvent 18 Sender message to unsubscribe to a receiver event, body is UnsubscribeEventMessage
Event 19 Receiver message to notify when a sender subscribed event has occurred, body is EventMessage

Connection establishment

When a sender or receiver establishes a connection with the other party, it must send a Version message to indicate which messages and protocol features are supported.

When there is a mismatch of support protocol versions among devices, the device with the higher version number must either error out/disconnect or use a downgraded feature set compatible with the other party's protocol version.

For protocol v3 and above, after determining the supported protocol version from the other party, the Initial message must be sent to the other party synchronize the connection state.

Device state synchronization

The protocol allows for multiple senders to connect to a single receiver. To synchronize the play/control state between all sender devices, the following messages are used: * Initial: Sent by the receiver upon connection establishment with a sender. Likewise the sender also sends the same message to the receiver upon connection establishment * PlayUpdate: Sent by the receiver when the played content has changed * PlaybackUpdate: Sent by the receiver whenever there is a change in the playback state of the media item * VolumeUpdate: Sent by the receiver when a change of volume has been made on the receiver or requested from another sender * PlaybackError: Sent by the receiver when a receiver-side play error has occurred

Bodies

PlayMessage

export enum MetadataType {
    Generic = 0,
}

export interface MetadataObject {
    type: MetadataType;
}

export class GenericMediaMetadata implements MetadataObject {
    readonly type = MetadataType.Generic;

    constructor(
        public title: string = null,
        public thumbnailUrl: string = null,
        public custom: any = null,
    ) {}
}

export class PlayMessage {
    constructor(
        public container: string,           // The MIME type (video/mp4)
        public url: string = null,          // The URL to load (optional)
        public content: string = null,      // The content to load (i.e. a DASH manifest, json content, optional)
        public time: number = null,         // The time to start playing in seconds
        public volume: number = null,       // The desired volume (0-1)
        public speed: number = null,        // The factor to multiply playback speed by (defaults to 1.0)
        public headers: { [key: string]: string } = null,  // HTTP request headers to add to the play request Map<string, string>
        public metadata: MetadataObject = null,
    ) {}
}
Remarks

Common media items that receivers are expected to support include video, audio, image media, and json data. Receivers can optionally support other types of content.

If the container field is application/json, the receiver may load the url or parse the content field according to the following supported content types.

export enum ContentType {
    Playlist = 0,
}

export interface ContentObject {
    contentType: ContentType;
}

export class MediaItem {
    constructor(
        public container: string,           // The MIME type (video/mp4)
        public url: string = null,          // The URL to load (optional)
        public content: string = null,      // The content to load (i.e. a DASH manifest, json content, optional)
        public time: number = null,         // The time to start playing in seconds
        public volume: number = null,       // The desired volume (0-1)
        public speed: number = null,        // The factor to multiply playback speed by (defaults to 1.0)
        public cache: boolean = null,       // Indicates if the receiver should preload the media item
        public showDuration: number = null, // Indicates how long the item content is presented on screen in seconds
        public headers: { [key: string]: string } = null,  // HTTP request headers to add to the play request Map<string, string>
        public metadata: MetadataObject = null,
    ) {}
}

export class PlaylistContent implements ContentObject {
    readonly contentType = ContentType.Playlist;

    constructor(
        public items: MediaItem[],
        public offset: number = null,         // Start position of the first item to play from the playlist
        public volume: number = null,         // The desired volume (0-1)
        public speed: number = null,          // The factor to multiply playback speed by (defaults to 1.0)
        public forwardCache: number = null,   // Count of media items should be pre-loaded forward from the current view index
        public backwardCache: number = null,  // Count of media items should be pre-loaded backward from the current view index
        public metadata: MetadataObject = null,
    ) {}
}

SeekMessage

Protocol

export class SeekMessage {
    constructor(
        public time: number,            // The time to seek to in seconds
    ) {}
}

PlaybackUpdateMessage

export class PlaybackUpdateMessage {
    constructor(
        public generationTime: number,   // The time the packet was generated (unix time milliseconds)
        public state: number,            // The playback state
        public time: number = null,      // The current time playing in seconds
        public duration: number = null,  // The duration in seconds
        public speed: number = null,     // The playback speed factor
        public itemIndex: number = null, // The playlist item index currently being played on receiver
    ) {}
}

The playback state are defined as follows.

export enum PlaybackState {
    Idle = 0,
    Playing = 1,
    Paused = 2,
}

VolumeUpdateMessage

export class VolumeUpdateMessage {
    constructor(
        public generationTime: number, // The time the packet was generated (unix time milliseconds)
        public volume: number,         // The current volume (0-1)
    ) {}
}

SetVolumeMessage

export class SetVolumeMessage {
    constructor(
        public volume: number,         // The desired volume (0-1)
    ) {}
}

PlaybackErrorMessage

export class PlaybackErrorMessage {
    constructor(
        public message: string
    ) {}
}

SetSpeedMessage

export class SetSpeedMessage {
    constructor(
        public speed: number,          // The factor to multiply playback speed by.
    ) {}
}

VersionMessage

export class VersionMessage {
    constructor(
        public version: number,          // Protocol version number (integer)
    ) {}
}

InitialSenderMessage

export class InitialSenderMessage {
    constructor(
        public displayName: string = null,
        public appName: string = null,
        public appVersion: string = null,
    ) {}
}

InitialReceiverMessage

export class InitialReceiverMessage {
    constructor(
        public displayName: string = null,
        public appName: string = null,
        public appVersion: string = null,
        public playData: PlayMessage = null,
    ) {}
}

PlayUpdateMessage

export class PlayUpdateMessage {
    constructor(
        public generationTime: number,
        public playData: PlayMessage = null,
    ) {}
}

SetPlaylistItemMessage

export class SetPlaylistItemMessage {
    constructor(
        public itemIndex: number,          // The playlist item index to play on receiver
    ) {}
}
Event Definitions

Senders have the ability to subscribe to events that occur on the receiver. The following event types are supported:

export interface EventSubscribeObject {
    type: EventType;
}

export enum EventType {
    MediaItemStart = 0,
    MediaItemEnd = 1,
    MediaItemChange = 2,
    KeyDown = 3,
    KeyUp = 4,
}

// Required supported keys for listener events defined below.
// Optionally supported key values list: https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
export enum KeyNames {
    Left = 'ArrowLeft',
    Right = 'ArrowRight',
    Up = 'ArrowUp',
    Down = 'ArrowDown',
    Ok = 'Enter',
}

export class MediaItemStartEvent implements EventSubscribeObject {
    readonly type = EventType.MediaItemStart;

    constructor() {}
}

export class MediaItemEndEvent implements EventSubscribeObject {
    readonly type = EventType.MediaItemEnd;

    constructor() {}
}

export class MediaItemChangeEvent implements EventSubscribeObject {
    readonly type = EventType.MediaItemChange;

    constructor() {}
}

export class KeyDownEvent implements EventSubscribeObject {
    readonly type = EventType.KeyDown;

    constructor(
        public keys: string[],
    ) {}
}

export class KeyUpEvent implements EventSubscribeObject {
    readonly type = EventType.KeyUp;

    constructor(
        public keys: string[],
    ) {}
}

SubscribeEventMessage

export class SubscribeEventMessage {
    constructor(
        public event: EventSubscribeObject,
    ) {}
}

UnsubscribeEventMessage

export class UnsubscribeEventMessage {
    constructor(
        public event: EventSubscribeObject,
    ) {}
}
Event Messages
export interface EventObject {
    type: EventType;
}

export class MediaItemEvent implements EventObject {
    constructor(
        public type: EventType,
        public item: MediaItem,
    ) {}
}

export class KeyEvent implements EventObject {
    constructor(
        public type: EventType,
        public key: string,
        public repeat: boolean,
        public handled: boolean,
    ) {}
}

EventMessage

export class EventMessage {
    constructor(
        public generationTime: number,
        public event: EventObject,
    ) {}
}