Sync playback across multiple devices
Sync API enables multiple devices to communicate and coordinate their behavior with each other. The communication is facilitated by the server. At any given moment, one of the devices is chosen by the server to be the master and all other devices become slaves.
Uses:
- Play content in sync on multiple devices
- Videowall - gapless video playback still available
All methods
Methods/Events | Description | Supported since |
---|---|---|
connect() | Connect to the sync server. | 1.0.32 |
close() | Closes the connection to the sync server. | 1.0.32 |
init() | Join sync group (DEPRECATED) | 1.0.32 |
joinGroup() | Join sync group | 5.7.0 |
wait() | Wait for other devices in the network for proper sync | 1.0.32 |
cancelWait() | Cancel pending waits | 5.12.0 |
setValue() | Broadcast values for all master devices (DEPRECATED) | 2.0.0 |
broadcastValue() | Used for broadcast values for all master devices | 5.7.0 |
onValue() | Event called when device receives value broadcasted by another device in the network | 2.0.0 |
onStatus() | Event called when device is connected and periodic every 30s from sync server - report connected devices | 2.1.0 |
onClosed() | Event called when synchronizer is closed | 5.12.0 |
connect()
Connect to the sync server. This initializes the connection and is mandatory to call, since synchronization is an optional feature and doesn’t get initialized by default to save resources and bandwidth. You can optionally specify a custom sync server URI in case you are running the sync server in a custom location.
Parameters
Param | Type | Required | Description |
---|---|---|---|
engine | String | No | Synchronization engine to use |
uri | String | No | Address of sync server. Only relevant for sync-server . If omitted, the default server will be used. |
Engine
Engine | Description |
---|---|
sync-server | (Default) Use external sync server. Device will connect to the server via websocket. |
p2p-local | Synchronize directly with other devices in the local network via UDP and TCP. |
udp | (Deprecated - use p2p-local) Synchronize directly with other devices in the local network via UDP. |
All devices, that should be synchronized together, must select the same engine. Otherwise they won't be able to communicate with each other.
Javascript example
// use default engine
await sos.sync.connect().then(() => {
// do other things once connected
});
// use sync-server engine and default server
await sos.sync.connect({ engine: 'sync-server' }).then(() => {
// do other things once connected
});
// use sync-server engine and custom server
await sos.sync.connect({
engine: 'sync-server',
uri: syncServerUri,
}).then(() => {
// do other things once connected
});
// use sync-server engine and custom server
await sos.sync.connect({
engine: 'sync-server',
uri: syncServerUri,
}).then(() => {
// do other things once connected
});
// use p2p-local engine
await sos.sync.connect({ engine: 'p2p-local' }).then(() => {
// do other things once connected
});
// call with syncServerUri as direct argument, now deprecated
// this way it will automatically pick sync-server engine
await sos.sync.connect(syncServerUri).then(() => {
// do other things once connected
});
close()
Closes the connection to the sync server. Recommended to call this method after the synchronization is not required any longer.
Javascript example
await sos.sync.close().then(() => {
// do other things once closed
})
init()
This method is deprecated and will be removed in the future. Use joinGroup()
instead. These two methods function identically.
Once the user is connected to the server, the initialization of the sync group is required. Before any communication takes place, all participating devices have to be connected and recognize one another. Recommended to call this method early.
Parameters
Param | Type | Required | Description |
---|---|---|---|
groupName | String | No | By default, all devices will be synced together. To create Groups of devices, independent from each other, specify group name |
deviceIdentification | String | No | Is identification of device connected to groupName. |
Javascript example
await sos.sync.init('someRandomNameGroup', 'device1');
joinGroup()
Once we're connected, we have to join a sync group. Before any communication takes place, all participating devices have to be connected and recognize one another. Recommended to call this method early.
Parameters
Param | Type | Required | Description |
---|---|---|---|
groupName | String | No | By default, all devices will be synced together. To create Groups of devices, independent from each other, specify group name |
deviceIdentification | String | No | Is identification of device connected to groupName. |
Javascript example
await sos.sync.joinGroup({
groupName: 'someRandomNameGroup',
deviceIdentification: 'device1',
});
wait()
One way to synchronize devices is to make them wait for each other at a certain moment. This would be most commonly used before the device hits “play” on a video, to make it wait for other devices so they all start playing the video at the same time.
This method returns a promise that resolves once all the devices meet and are ready to continue together. Any action that results in visible synchronized behavior should be triggered immediately after and any related background preparations should be called before to prevent delays.
Sometimes devices might go out of sync due to unpredictable conditions like loss of internet connection. To ensure re-sync of an out of sync device, you can pass some data as the first argument. This can be any data that informs the whole group about what content is about to play next. Once all devices are ready, data from the master device is passed to everyone and the rest of the data is ignored. Therefore, when implementing your applet you should rely on the result data and not the data that is passed to the wait method as an argument.
Parameters
Param | Type | Required | Description |
---|---|---|---|
data | Any | No | Information about what content is about to play so all the devices display the same content. |
groupName | String | No | If joinGroup is called with custom group name, the same group name as the second argument has to be passed. |
timeout | Number | No | Wait timeout on other devices |
Returns
Promise that resolves to data picked from the first argument of the master device
cancelWait()
Sometimes it's necessary to cancel a pending wait.
One such situation would be when the group has to make a sudden change in content or another behavior but
there's a risk that part of the group already called wait()
and is waiting for the rest but the rest will never call it at this point.
In order to gracefully cleanup any pending activity, use this method.
Any pending wait will be canceled and the promise will be rejected with an error.
Parameters
Param | Type | Required | Description |
---|---|---|---|
groupName | String | No | If joinGroup is called with custom group name, the same group name as the second argument has to be passed. |
Javascript example
sos.sync.wait('someData', 'someRandomNameGroup').catch((err) => {
// this will happend once cancelWait is called
console.error('wait failed', err);
});
// this will cause above wait promise to reject
await sos.sync.cancelWait('someRandomNameGroup');
Option Broadcast values:
Another way to synchronize devices is to broadcast some values within the group so, in turn, all devices can react to it. Unlike option wait, this is more useful in use cases when the synchronization is based around the content itself, rather than timing.
All Methods
Method | Type | Description | Supported since |
---|---|---|---|
setValue() | method | Used for broadcast values for all master devices | 2.0.0 |
onValue() | event | Event called when device receive any value | 2.0.0 |
onStatus() | event | Event called when device is connected and periodic every 30s from sync server - report connected devices | 2.1.0 |
setValue()
This method is deprecated and will be removed in the future. Use broadcastValue()
instead. These two methods function identically.
This method can be called by any device to broadcast a value to the whole group.
Parameters
Param | Type | Required | Description |
---|---|---|---|
key | String | Yes | Values are recognized based their key so different types of values can be broadcasted independently within the group |
value | Any | Yes | The value to be broadcasted |
groupName | String | No | If joinGroup is called with custom group name, the same group name has to be passed as the third argument |
Javascript example
await sos.sync.setValue('some-key', 'some-value', 'some-group');
broadcastValue()
This method can be called by any device to broadcast a value to the whole group.
Parameters
Param | Type | Required | Description |
---|---|---|---|
key | String | Yes | Values are recognized based their key so different types of values can be broadcasted independently within the group |
value | Any | Yes | The value to be broadcasted |
groupName | String | No | If joinGroup is called with custom group name, the same group name has to be passed as the third argument |
Javascript example
await sos.sync.broadcastValue({
key: 'some-key',
value: 'some-value',
groupName: 'some-group'
});
Event onValue()
Every device should register a listener to receive and process broadcasted values.
Javascript example
await sos.sync.onValue((key, value, groupName) => {
// do something with the new value
});
Event onStatus()
Is used for receiving information about connected device to sync server. Emitted every 30s, or when device connect or disconnect from server.
Javascript example
await sos.sync.onStatus((connectedPeers) => {
// do something with the new value
});
Since you never know which device is the master at the moment, you should always rely only on the values received by the listener, not the values that the particular device itself is broadcasting.
Event onClosed()
Emitted when synchronizer is closed for whatever reason.
If it closed because close()
was called, it will emit without any arguments.
if it closed because of an error, it will emit with an error object as the first argument.
Javascript example
await sos.sync.onClosed((error) => {
if (error) {
console.error('Synchronizer closed with an error', error);
} else {
console.info('Synchronizer closed');
}
});
Usage with Typescript
You can also use all these methods with signageOS TypeScript.
connect(syncServerUri?: string): Promise<void>;
close(): Promise<void>;
/** @deprecated use joinGroup */
init(groupName?: string, deviceIdentification?: string): Promise<void>;
joinGroup(args: {
groupName?: string;
deviceIdentification?: string;
}): Promise<void>;
wait(data?: any, groupName?: string): Promise<any>;
cancelWait(groupName?: string): Promise<void>;
/** @deprecated use broadcastValue */
setValue(key: string, value: any, groupName?: string): Promise<void>;
broadcastValue(args: {
groupName?: string;
key: string;
value: any;
}): Promise<void>;
onValue(listener: (
key: string,
value: any,
groupName?: string
) => void): void;
onStatus(listener: (
connectedPeers: string[],
groupName?: string
) => void): void;
onClosed(listener: (
error?: Error;
) => void): void;
Errors
Although we are doing our best, following errors may occur when working with the synchronizer.
Code | Type | Message |
---|---|---|
51101 | InternalSynchronizerError | Synchronizer couldn't connect to the server. |
51102 | InternalSynchronizerError | The connection wasn't closed correctly. |
51103 | InternalSynchronizerError | The group initialization failed. |
51104 | InternalSynchronizerError | Wait failed for unexpected reason. |
51105 | InternalSynchronizerError | Synchronizer couldn't set the value. |