API Discussion

From Second Screen Presentation Community Group

This page is intended to serve as an entry point for our discussion how to move the API forward. Google brought forward their wish to integrate a new use case, explained below. Fraunhofer expressed a need for supporting multiple displays, which is also factored in as a new requirement below. Let's discuss how we can integrate these two new use cases and while keeping a compact and easy to use API.

Use cases

The initial set of use cases was listed in the current Presentation API draft: http://webscreens.github.io/presentation-api/#use-cases

Presentations

A user is preparing a set of slides for a talk. Using a web based service, she is editing her slides and speaker notes on the primary screen, while the secondary larger screen shows a preview of the current slide. When the slides are done, her mobile phone allows her to access them from an online service while on the go. Coming to the conference, using wireless display technology, she would like to present her slides on the stage screen from her mobile phone. The phone's touch screen helps her to navigate slides and presents a slide preview, while the projector shows her slides to the audience.

Requirements: R1, R3, R4, R5, R7

Video and Image Sharing

Using an online video or image sharing service, a user would like to show memorable moments to her friends. Using a device with a small screen, it is impossible to show the content to a large group of people. Connecting an external TV screen or projector to her device - with a cable or wirelessly - the online sharing service now makes use of the connected display, allowing a wider audience to enjoy the content.

The web page shows UI elements that allow the user to trigger displaying content on the secondary display (e.g a "send to second screen" ) only if there is at least one secondary screen available.

Requirements: R1, R3, R4, R5, R7

Gaming

Splitting the gaming experience into a near screen controller and a large screen visual experience, new gaming experiences can be created. Accessing the local display on the small screen device and an external larger display allows for richer web-based gaming experiences.

Requirements: R1, R3, R4, R5, R7

New: Media Flinging to Multiple Screens

Akostiai (talk) 13:52, 7 February 2014 (UTC) This use case was updated to address the concerns discussed below.

Alice enters a video sharing site using a browser on her tablet. Next, Alice picks her favorite video from the site, and the video starts to play on her tablet. While the video is playing Alice clicks a button "Share on different screen". The browser provides a user interface that lists all the screens Alice has around her home, asking her to select one. The screens are identified by names that are familiar to Alice. Alice picks one screen from the list, "Alice's big TV", and the video playback continues seamlessly on the selected screen. Next she decides to switch the playback to a different screen. She clicks the same button "Share on different screen" provided by the site, and the browser presents the user interface that lists all the screens. Alice picks another screen from the list, "Alice's kitchen TV", and the playback resumes on that screen. Video site also provides a feature to see the action (Alice is watching a soccer game) from different angle. Alice clicks a button "Select screen for additional angle", and the browser asks Alice similarly to select the screen to be used for playback. Alice picks "Alice's Projector" and the soccer game is shown on the projector from a different angle, in parallel to the content being played back on "Alice's kitchen TV".

Anton Vayvod (talk) 16:06, 5 February 2014 (UTC)
Why don't the browser provide the complete UI for device selection eliminating the need to share screen names with the site? Similar to how the new Cast extension/SDK do it: https://developers.google.com/cast/docs/chrome_sender#Device_selection. I think we could also drop the permission vs. selection UI difference: since it's the browser that shows the devices list, we don't expose any information to the website. Selecting the device within the browser UI allows the site to present through it.
I also believe the same thing about device selection stands for all the previous use cases too.
Anton Vayvod (talk)

Note: In this use case the browser would make contact with a nearby presentation device, possibly running its own user agent, and interact and control it. Similar to Chromecast in its current form. Compared to the other use cases, the remote presentation device could act on its own and continue playback and operation after the initial browser that initiated the connection has disconnected. Even if there are two separate UAs running on primary and secondary device, the user expects a seamless interaction between the two devices, for example, she can control the secondary screen from the mobile device in her hands.

In order to allow the site to programmatically access multiple screens, the general yes/no availability is not sufficient, thus R2. To allow the site to provide a custom user interface for screen selection, informing the user which screen is used for playback, a human readable identifier for the screens is needed R8 - but the agreement is now that the UA, not the page, would present such a selection for the user to pick from multiple named screens.

Dominik Röttsches (talk) 12:08, 19 February 2014 (UTC) Comments regarding requirements R2 and R8 removed.

Requirements: R1, R3, R4, R5, R6, R7, R2, R8

(This use case combines Google's input to the mailing list, as well as Fraunhofer's requirement for having multiple screens enumerated.)

Requirements

From these use cases, we can derive and define a couple of requirements, split into functional and non-functional ones:

Functional Requirements

  • Discovery / Availability
    • R1: The UA must provide a way to find out whether at least one secondary screen is available.
    • R2: The UA must provide a means of listing available screens that the user has permitted listing for the given site.
    • R8: The UA must allow identification of available screens by a human readable name / identifier.

Multi-Screen enumeration and named identification removed, after discussion on the mailing list, cmp. http://lists.w3.org/Archives/Public/public-webscreens/2014Feb/0021.html :


  • Launching Presentation
    • R3: The UA must provide a means of start sending content to the secondary screen.
  • Resuming Presentation
    • R4: The UA must be able to resume an existing session with content being displayed on the secondary screen.
  • Communication
    • R5: The UA must enable exchanging data between the primary and the secondary screen in order to have a control channel between the primary and secondary page.
    • R6: The UA must not make assumptions about the the execution locality of the user agent of the remote page it communicates with (i.e. the secondary page might run on a remote user agent and thus the link between the two pages' UA must be loosely coupled).
  • Signaling Disconnection
    • R7: The UA must signal disconnection from the presentation page to the primary page and vice versa.

Non-Functional Requirements

  • Power Saving Friendly
    • All API design decisions must be analyzed from a power efficiency point of view. Especially when using wireless display technologies or querying availability anything over a radio channel, care needs to be taken to design the API in a way that does not pose obstacles to using radio resources in an efficient way. For example, powering up the wireless display detection only when needed.

Open Questions

  • If we agree on multiple screen enumeration - is a human-readable name sufficient or would there be a use case and requirement for attempting to persistently identify displays?
Anton Vayvod (talk) 16:06, 5 February 2014 (UTC) I believe that human readable name should be sufficient.

API Usage Examples

Usage on Primary Screen

Incorporating the old and newer requirements we suggest the following snippet as a new approach. Below the example code you'll find a walkthrough explanation.

<button disabled>Show</button>
var presentation = navigator.presentation,
    showButton = document.querySelector('button');
    
presentation.onavailablechange = function(e) {
  showButton.disabled = !e.available;
  showButton.onclick = show;
};

function show() {
  var session = presentation.requestSession('http://example.org/');
  
  session.onstatechange = function() {
    switch (session.state) {
      case 'connected':
        session.postMessage(/*...*/);
        session.onmessage = function() { /*...*/ };
        break;
      case 'disconnected':
        console.log('Disconnected.');
        break;
    }
  };
}
  • The availability monitoring for secondary screens begins when the page adds an event listener for the availablechange event on the navigator.presentation object. If there are already available screens when the page adds the first event listener for the event, the UA synthesizes a single availablechange event to signal the availability.

--Anton Vayvod (talk) 14:44, 15 April 2014 (UTC) It was brought to my attention that having an event firing immediately after the site registers a listener is a bad practice. I suggest we add the navigator.presentation.available boolean back that would be checked by the site on load. --Anton Vayvod (talk) 14:44, 15 April 2014 (UTC)

  • The "Show" button's disabled state (initially disabled) informs the user of the availability of secondary screen(s), and the button's state is updated if the availability changes. (The rationale of putting the actual boolean information into a property of the event e.available is to allow the implementation to optimize power consumption for network discovery of remote wireless screens. If this information was provided in a globally accessible flag, the network discovery could never be suspended for keeping the flag up to date.)
  • Once the user clicks the "Show" button, the show() function is called.
  • By calling navigator.presentation.requestSession(url), the script on the page tries to launch or resume a presentation on a secondary screen. Based on the url argument, the UA looks for existing sessions and available screens, and presents a screen picker user interface to the user. Out of the list of existing sessions or available screens the user selects one item. If an existing session was selected, the session is resumed by establishing a communication channel. If a new screen was selected, the UA connects to the selected screen, brings up a new window on the selected screen, starts to show the content denoted by the url argument, and the UA establishes a communication channel with this window.
  • When navigator.presentation.requestSession(url) function is called, the UA immediately returns a session object to the script which represents a handle to the current presentation session, used for communication and state handling.
  • If the user has selected a screen, the content is shown and a communication channel was established, the state of the session object changes from "disconnected" to "connected", which signals the presentation session is up and running, and the opener page can communicate with the presentation page. For communication with the presentation page, the session object's postMessage() is used to send, and the onmessage event handler to receive messages.
  • If the user cancels the screen selection and never selects a screen, no state transition happens and the session object remains in the "disconnected" state.

Open Questions

  • Do we need to insert into the description an additional permission prompt to grant the page access to the "one ore more screens are available" Information?
  • If there are already connected screens when the page subscribes to the onavailablechange event, we can handle this in two ways: We can synthesize one initial event to notify the page about available screens as soon as the first event handler is installed (as described). Or we can add another message like navigator.presentation.getAvailable(function(available) { } ); to notify the page about available screens using this one-time asynchronous getter. Which way should we go?
  • Do we still need to pass an options parameter to the requestSession() call in order to specify continuous playback or would we leave this an implementation detail to the UA? Given that there is a close() function and disconnect notifications through the state property, this should be sufficient to implement the continuous playback use case.
  • Do we need an additional state like resumed in order to identify resumed session? It seems that this could be handled on the page level. The opener page could ask the presentation page whether it is "new" or "resumed".

Usage on Remote Screen

For addressing the requirement of communication between originating page and presentation page/screen, we can now use the same session object on the remote side.

navigator.presentation.onpresent = function(e) {
  // Communicate with opener page.
  e.session.postMessage(/*...*/);
  e.session.onmessage = function() {/*...*/};

  e.session.onstatechange = function() {
    switch (this.state) {
      case "disconnected":
        // Handle disconnection from opener page.
    }
  };
};
  • When the content denoted by the url argument in the requestSession() example above is loaded, the page on the presentation screen receives a present event, with a session property representing the session. This session is a similar object as in the first example. Here, its initial state is "connected", which means we can use it to communicate with the opener page using postMessage() and onmessage.
  • We can also monitor the connection state by listening for statechange events. When the state changes to "disconnected" the page is made aware of the fact that communication with the opener page was lost, but it can continue to display the current content. The communication can be re-established when a new present event fires on the navigator.presentation object.

Open Questions

  • Do we need an onclose event, or could this be replaced by an application level message exchange protocol that sends keepalive signals? The new proposal provides disconnection information.
  • Since MessagePort can be closed by close() method, if the consumer closes the message port of a presentation by accident, there is no way to tell presentation that its message port was closed. The new proposal exposes the typical MessagePort functions but is not itself a MessagePort, avoiding the accidental close() issue.
  • Should investigate if the vanilla BroadcastChannel addresses the communication requirements. Needs at least a unique identifier for each PresentationScreen object, also the same-origin policy applies. Example:
// Running on the primary screen.

function communicate(selected) {
  var channel = new BroadcastChannel(selected.id);
  channel.onmessage = function (e) {
    if (e.data == "play") {
      console.log("Playback started on " + selected.name);
    }
  };
}

// Running on the remote screen.

navigator.presentation.onpresent = function (e) {
  var channel = new BroadcastChannel(e.id);
  playVideo();
  channel.postMessage("play");
};
Dominik Röttsches (talk) 12:05, 17 February 2014 (UTC) BroadcastChannel does not seem to have any onclosed or disconnection notification either, similar to the issue with MessagePorts.

WebIDL

NavigatorPresentation interface

interface NavigatorPresentation : EventTarget {
  PresentationSession requestSession(DOMString url);
  attribute EventHandler onavailablechange;
  attribute EventHandler onpresent;
};

partial interface Navigator {
  readonly attribute NavigatorPresentation presentation;
};

AvailableChangeEvent interface

  • Fired at the primary screen's NavigatorPresentation object, when screen availability changes.
[Constructor(DOMString type, optional AvailableChangeEventInit eventInitDict)]
interface AvailableChangeEvent : Event {
  readonly attribute boolean available;
};

dictionary AvailableChangeEventInit : EventInit {
  boolean available;
};

PresentEvent interface

  • Fired at the secondary screen's NavigatorPresentation object, when the presentation session is established.
[Constructor(DOMString type, optional PresentEventInit eventInitDict)]
interface PresentEvent : Event {
  readonly attribute PresentationSession session;
};

dictionary PresentEventInit : EventInit {
  PresentationSession session;
};

PresentationSession interface

  • An object representing the established presentation session.
enum PresentationSessionState { "connected", "disconnected" /*, "resumed" */ };

interface PresentationSession : EventTarget {
  readonly attribute PresentationSessionState state;
  void postMessage(DOMString message);
  void close();
  attribute EventHandler onmessage;
  attribute EventHandler onstatechange;
};