The goal of this document is to define a common message format for media hosted within iframes where the parent interacts with the child iframe via postMessage.
This is based heavily off the HTML5 Video Spec, Vimeo's JavaScript API, Soundclouds Widget API and YouTube's Player API.
We have common API's for getting embed code via oEmbed. The next iteration of that is actually being able to interact with the media.
The most common use case is for video and audio hosted within iframes. The parent page would like to play/pause or listen to progress of the media. We make this available via a common set of json that is passed back between the parent and the child.
This specification is licensed under the Creative Commons Attribution License. You can learn more about the license here. The more people that refine this Spec, the better, so we tried to pick the least restrictive license we could.
Currently the following browsers support postMessage and hence can use this:
- FF3+
- IE8+
- Chrome
- Safari 5+
- Opera10+
There are two types of message type, events and methods.
These are play
, pause
and getDuration
are all examples of methods.
They have a common format:
{ context: 'player.js', version: 'version' method: 'methodName', value: 'methodValue', listener: 'listenerName', }
context
- The context of the postMessage data to avoid conflicts. Will always be
player.js
. version
- The version of the library that you are using. Currently this is
0.0.11
. method
- The method name that we wish to invoke.
value
- If the method sets an attribute, the value attribute should be set.
listener
(optional)A generic identifier that the child should echo back to the parent on returning data. This is not specifically a callback. The problem faced with using postMessage is that all message from child iframes are passed over the same pipe. If multiple clients are communicating with the child, the parent must be able to differentiate which messages are for it.
Without modifying the src of the iframe, we feel this is the best way to differentiate the events.
If the method is a getter, i.e. getDuration
the child frame will send the
following message to the parent.
{ context: 'player.js', version: '2.0', event: 'methodName', listener: 'listenerName', value: 'returnValue' }
The child frame will often fire a number of events, such as play
,
finish
and playProgress
. This defines what is passed back.
No events should be passed back unless the parent explicitly asks for them. To add a listener we send the following message:
{ context: 'player.js', version: 'version', method: 'addEventListener', value: 'event', listener: 'listenerName' }
When the event is fired, the parent will receive the following event data:
{ context: 'player.js', version: 'version', event: 'event', listener: 'listenerName', value: {} }
It's helpful to have a quick example of the JavaScript before moving forward.
// Play the video document.getElementById('#iframe').contentWindow.postMessage( JSON.stringify({ context: 'player.js', version: 'version', method: 'play' }) ); // Set up an event listener. var iframe = document.getElementById('#iframe'), origin = iframe.src.split('/', 3).join('/'); var played = function(){ console.log('played'); }; window.addEventListener('message', function(){ if (e.origin === origin){ var data = JSON.parse(e.data); if (data.context === 'player.js' && data.event === play){ played(); } } }); iframe.contentWindow.postMessage( JSON.stringify({ context: 'player.js', version: 'version', method: 'addEventListener', value: 'play' }); );
play
: voidPlay the media:
{ method: 'play' }
pause
: voidPause the media:
{ method: 'pause' }
getPaused
: booleanDetermine if the media is paused:
{ method: 'getPaused' }
mute
: voidMute the media:
{ method: 'mute' }
unmute
: voidUnmute the media:
{ method: 'unmute' }
getMuted
: booleanDetermine if the media is muted:
{ method: 'getMuted' }
setVolume
: voidSet the volume. Value needs to be between 0-100:
{ method: 'setVolume', value: 50 }
getVolume
: numberGet the volume. Value will be between 0-100:
{ method: 'getVolume', }
getDuration
: numberGet the duration of the media is seconds:
{ method: 'getDuration', }
setCurrentTime
: numberPerform a seek to a particular time in seconds:
{ method: 'setCurrentTime', value: 12 }
getCurrentTime
: numberGet the current time in seconds of the video:
{ method: 'getCurrentTime', }
setLoop
: booleanTell the media to loop continuously:
{ method: 'setLoop', value: true }
getLoop
: booleanReturn the loop attribute of the video:
{ method: 'getLoop', }
setPlaybackRate
: numberSet the playback rate. Value should be in the available playback rates. This function won't return an error if the value is not present.:
{ method: 'setPlaybackRate', value: 1.5 }
getPlaybackRate
: numberGet the current playback rate of the player.:
{ method: 'getPlaybackRate', }
setCurrentTime
: numberPerform a seek to a particular time in seconds:
{ method: 'setCurrentTime', value: 12 }
removeEventListener
: voidRemove an event listener. If the listener is specified it should remove only that listener, otherwise remove all listeners:
{ context: 'player.js', version: 'version', method: 'removeEventListener', value: 'event', listener: 'listenerName' }
addEventListener
: voidAdd an event listener:
{ context: 'player.js', version: 'version', method: 'addEventListener', value: 'event', listener: 'listenerName' }
Events that can be listened to.
ready
fired when the media is ready to receive commands. This is fired regardless of listening to the event.:
{ context: 'player.js', version: 'version', event: 'ready', value: { src: 'srcOfIframe', events: [ 'event1' ], methods: [ 'method1' ] } }
ready
sets the stage for the rest of the interactions with the iframe. There are a number of attributes in the value that helps us understand the compatibility of the embed.src
Echos back the src of the iframe to let the frontend know which frame is ready. If there are two iframes with the same source on the page, this will not work as expected. We recommend randomizing one aspect of the src to assure this does not happy. As an example, you can add a timestamp or a uuid:
<iframe src="....&_=1385393930268"></iframe>
The ideal solution would be to set a playerID or another unique identifier. However this would require building the iframe src, or reloading the iframe after it's been rendered.
methods
- A list of the methods that the iframe media supports.
events
- A list of events that the iframe media supports.
progress
Fires when the media is loading additional media for playback:
{ context: 'player.js', version: 'version', event: 'progress', value: { seconds: 10, duration: 40 } }
timeupdate
Fires during playback:
{ context: 'player.js', version: 'version', event: 'timeupdate', value: { seconds: 10, duration: 40 } }
play
Fires when the video starts to play:
{ context: 'player.js', version: 'version', event: 'play', }
pause
Fires when the video is paused:
{ context: 'player.js', version: 'version', event: 'pause', }
volumeChange
Fires when the volume of the player is changed by the user:
{ context: 'player.js', version: 'version', event: 'pause', value: { volume: 50 } }
ended
Fires when the video has ended:
{ context: 'player.js', version: 'version', event: 'ended', }
error
Fires when something goes wrong:
{ context: 'player.js', version: 'version', event: 'error', value: { code: -1 msg: "" } }
code
Default error codes are as follows:
-1
Undefined.1
Playback not supported by device or browser.2
Method not supported.