forked from w3c/remote-playback
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1106 lines (1090 loc) · 51.2 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>
Remote Playback API
</title>
<script src="https://www.w3.org/Tools/respec/respec-w3c-common" async
class="remove">
</script>
<script class="remove">
var respecConfig = {
specStatus: 'ED',
edDraftURI: 'https://w3c.github.io/remote-playback/',
shortName: 'remote-playback',
editors: [
{
w3cid: 68811,
name: 'Anton Vayvod',
company: 'Google',
},
{
w3cid: 45389,
name: 'Mounir Lamouri',
company: 'Google',
},
],
sotdAfterWGinfo: true,
// previousMaturity: 'WD',
// previousPublishDate: '2015-11-02',
otherLinks: [
{
key: 'Version history',
data: [
{
value: 'GitHub w3c/remote-playback/commits',
href: 'https://github.com/w3c/remote-playback/commits/'
}
]
},
{
key: 'Participate',
data: [
{
value: 'GitHub w3c/remote-playback',
href: 'https://github.com/w3c/remote-playback/'
},
{
value: 'File an issue',
href: 'https://github.com/w3c/remote-playback/issues/new'
},
{
value: 'Open issues',
href: 'https://github.com/w3c/remote-playback/issues/'
},
{
value: 'Mailing-list ([email protected])',
href: 'https://lists.w3.org/Archives/Public/public-secondscreen/'
}
]
}
],
wg: 'Second Screen Presentation Working Group',
wgURI: 'https://www.w3.org/2014/secondscreen/',
wgPublicList: 'public-secondscreen',
wgPatentURI: 'https://www.w3.org/2004/01/pp-impl/74168/status',
issueBase: "https://www.github.com/w3c/remote-playback/issues/",
githubAPI: "https://api.github.com/repos/w3c/remote-playback",
};
</script>
<style>
/* Note formatting taken from Presentation API spec for consistency in the
Second Screen WG */
.note { border-left-style: solid; border-left-width: 0.25em; background: none repeat scroll 0 0 #E9FBE9; border-color: #52E052; }
.note em, .warning em, .note i, .warning i { font-style: normal; }
p.note, div.note { padding: 0.5em 2em; }
span.note { padding: 0 2em; }
.note p:first-child { margin-top: 0; }
.note p:last-child { margin-bottom: 0; }
p.note:before { content: 'NOTE: '; }
.non-normative { border-left-style: solid; border-left-width: 0.25em; background: none repeat scroll 0 0 #E9FBE9; border-color: #52E052; }
p.non-normative:before { content: 'Non-normative: '; font-weight: bolder;}
p.non-normative, div.non-normative { padding: 0.5em 2em; }
.algorithm li {
margin-bottom: 0.5em;
}
.interface dd, .parameters dt {
margin-bottom: 0.5em;
}
code { color: orangered; }
table { border-collapse: collapse; border-style: hidden hidden none hidden; }
table thead, table tbody { border-bottom: solid; }
table td, table th { border-left: solid; border-right: solid; border-bottom: solid thin; vertical-align: top; padding: 0.2em; }
dfn { font-weight: bolder; font-style: normal; }
.copyright { font-size: small; }
.issue[id^='issue-'] > *:not([role='heading']) { display: none; }
</style>
</head>
<body>
<section id="abstract">
<p>
This specification defines an API extending the <code><a>HTMLMediaElement</a></code>
that enables controlling remote playback of media from a web page.
</p>
</section>
<section id="sotd">
<p>
This document is a <strong>work in progress</strong> and is subject to
change. It builds on the group's experience on presenting web content on
external presentation-type displays, and re-uses patterns and design
considerations from the Presentation API specification whenever
appropriate [[PRESENTATION-API]]. In case of issues or concerns, it is
possible to <a href='https://github.com/w3c/remote-playback/issues/new'>file a bug</a>
or send an email to the <a href='https://lists.w3.org/Archives/Public/public-secondscreen/'>mailing list</a>.
For small editorial changes like typos, sending a pull request is appreciated.
</p>
</section>
<section id='conformance'>
<p>
This specification defines conformance criteria that apply to a single
product: the <dfn>user agent</dfn> that implements the interfaces that
it contains.
</p>
<p>
Implementations that use ECMAScript to expose the APIs defined in this
specification MUST implement them in a manner consistent with the
ECMAScript Bindings defined in the Web IDL specification [[!WEBIDL]].
</p>
</section>
<section class="informative">
<h2>
Introduction
</h2>
<p>
This specification aims to make <a>remote playback devices</a> such as
connected TVs, projectors or audio-only speakers, available to the Web and
takes into account playback devices that are attached using wired (HDMI, DVI,
or similar) and wireless technologies (Miracast, Chromecast, DLNA, AirPlay, or
similar).
</p>
<p>
Devices with limited screen size or quiet speakers lack the ability to
playback media content to a larger audience, for example, a group of
colleagues in a conference room, or friends and family at home. Playing media
content on an external larger and/or louder <a>remote playback device</a> helps
to improve the perceived quality and impact of the played media.
</p>
<p>
At its core, this specification enables a page that acts as the <a>browsing context</a>
to initiate and control remote playback of a particular media element on a selected
<a>remote playback device</a>. How the remoting is initiated and controlled
is left to the UA in order to allow the use of <a>remote playback devices</a> that
can be attached in a wide variety of ways. For example, when a <a>remote playback device</a>
is attached using HDMI or Miracast, the same UA that acts as the
<a>browsing context</a> renders the remote media. Instead of playing the media on the same
device, however, it can use whatever means the operating system provides for using
the external <a>remote playback device</a>. In such a case, both the <a>browsing context</a> and
the media player run on the same UA and the operating system is used to route the player
output to the <a>remote playback device</a>. This is commonly referred to as the
<dfn><b id="media-mirroring">media mirroring</b></dfn> case. This specification imposes no requirements on the
<a>remote playback devices</a> connected in such a manner.
</p>
<p>
If the <a>remote playback device</a> is able to play the media and
communicate with the <a>browsing context</a> but is unable to fetch the
media, the <a>browsing context</a> needs to fetch the media data and pass
it on to the <a>remote playback device</a> for rendering. This is
commonly referred to as <dfn><b id="media-remoting">media remoting</b></dfn> case.
</p>
<p>
If the <a>remote playback device</a> is able to fetch and play the media and
communicate with the <a>browsing context</a>, the <a>browsing context</a> does not
need to fetch or render the remoted media. In this case, the UA acts as a proxy
that requests the <a>remote playback device</a> to play the media itself by passing the necessary
data like the media source. This is commonly referred to as the <dfn><b id="media-flinging">media flinging</b></dfn> case.
This way of attaching to displays could be enhanced in the future by defining a standard protocol for
delivering these types of messages that remote playback devices could choose to implement.
</p>
<p>
The API defined here is intended to be used with UAs that attach to
<a>remote playback device</a> devices through any of the above means.
</p>
</section>
<section class='informative'>
<h2>Use cases and requirements</h2>
<p>
The use cases and requirements of this specification are captured in a
separate document available <a href='https://github.com/w3c/remote-playback/blob/gh-pages/use-cases.md'>here</a>.
</p>
</section>
<section>
<h2>Dependencies</h2>
<p>
The following concepts and interfaces are defined in [[!HTML]]:
</p>
<ul>
<li>
<dfn><a href='https://html.spec.whatwg.org/multipage/embedded-content.html#htmlmediaelement'>HTMLMediaElement</a></dfn>,
<dfn><a href='https://html.spec.whatwg.org/multipage/embedded-content.html#media-element'>media element</a></dfn>,
<dfn data-lt="resource selection algorithm"><a href="https://html.spec.whatwg.org/multipage/embedded-content.html#concept-media-load-algorithm">selecting media resource</a></dfn>,
<dfn data-lt="currentSrc"><a
href="https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-currentsrc">current source</a></dfn> and
<dfn data-lt="media resources"><a href="https://html.spec.whatwg.org/multipage/embedded-content.html#location-of-the-media-resource">media resource</a></dfn>
</li>
<li>
<dfn><a href='https://html.spec.whatwg.org/multipage/browsers.html#browsing-context'>browsing context</a></dfn> and
<dfn><a href='https://html.spec.whatwg.org/multipage/browsers.html#allowed-to-show-a-popup'>allowed to show a popup</a></dfn>
</li>
<li>
<dfn><a href='https://html.spec.whatwg.org/multipage/webappapis.html#event-handlers'>event handler</a></dfn>,
<dfn><a href='https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-event-type'>event handler event type</a></dfn>,
<dfn><a href='https://html.spec.whatwg.org/multipage/webappapis.html#fire-a-simple-event'>fire a simple event</a></dfn> and
<dfn><a href='https://html.spec.whatwg.org/multipage/webappapis.html#queue-a-task'>queue a task</a></dfn>
</li>
<li>
<dfn data-lt='fire|fires'><a href='https://html.spec.whatwg.org/multipage/infrastructure.html#concept-event-fire'>firing an event</a></dfn> and
<dfn><a href='https://html.spec.whatwg.org/multipage/infrastructure.html#in-parallel'>in parallel</a></dfn>,
</li>
<li>
<dfn><a href='https://html.spec.whatwg.org/multipage/scripting.html#cereactions'><code>[CEReactions]</code></a></dfn>
</li>
</ul>
<p>
The term <dfn><a href="https://url.spec.whatwg.org/#url">URL</a></dfn>
is defined in the WHATWG URL standard [[!URL]].
</p>
<p>
The term <dfn>throw</dfn> in this specification is used as defined in
[[!WEBIDL]].
</p>
<p>
The following exception names are defined by [[!WEBIDL]] and used by
this specification:
</p>
<ul>
<li>
<dfn><code>InvalidAccessError</code></dfn>
</li>
<li>
<dfn><code>InvalidStateError</code></dfn>
</li>
<li>
<dfn><code>NotAllowedError</code></dfn>
</li>
<li>
<dfn><code>NotFoundError</code></dfn>
</li>
<li>
<dfn><code>NotSupportedError</code></dfn>
</li>
<li>
<dfn><code>OperationError</code></dfn>
</li>
</ul>
</section>
<section>
<h2>
Examples
</h2>
<p>
This section shows code examples that highlight the usage of the main features of the Remote Playback API.
In these examples, <code>player.html</code> implements the player page controlling the remote playback and
<code>media.ext</code> is the media file to be played remotely. Both the page and the media are served from the
domain <code>https://example.org</code>. Please refer to the comments in the code examples for further details.
</p>
<section>
<h3>
Monitor availability of remote playback devices example
</h3>
<pre class="example highlight">
<!-- player.html -->
<!-- The video element with custom controls that supports remote playback. -->
<video id="videoElement" src="https://example.org/media.ext" />
<button id="deviceBtn" style="display: none;">Pick device</button>
<script>
// The "Pick device" button is visible if at least one remote playback device is available.
var deviceBtn = document.getElementById("deviceBtn");
var videoElem = document.getElementById("videoElement");
function availabilityCallback(available) {
// Show or hide the device picker button depending on device availability.
deviceBtn.style.display = available ? "inline" : "none";
}
videoElem.remote.watchAvailability(availabilityCallback).catch(function() {
// Availability monitoring is not supported by the platform, so discovery of
// remote playback devices will happen only after remote.connect() is called.
// Pretend the devices are available for simplicity; or, one could implement
// a third state for the button.
deviceBtn.style.display = "inline";
});
</script>
</pre>
</section>
<section>
<h3>
Starting remote playback of a video example
</h3>
<pre class="example highlight">
<!-- player.html -->
<script>
devicesBtn.onclick = function() {
// Request the user to select a remote playback device.
videoElem.remote.prompt()
// Update the UI and monitor the connected state.
.then(updateRemotePlaybackState);
// Otherwise, the user cancelled the selection UI or no screens were found.
};
<script>
</pre>
</section>
<section>
<h3>
Monitoring remote playback state changes
</h3>
<pre class="example highlight">
<!-- player.html -->
<script>
// The remote playback may be initiated by the user agent,
// so check the initial state to sync the UI with it.
if (videoElem.remote.state == "disconnected")
switchToLocalUI();
else
switchToRemoteUI();
videoElem.remote.onconnecting = switchToRemoteUI;
videoElem.remote.onconnect = swithToRemoteUI;
videoElem.remote.ondisconnect = switchToLocalUI;
// Handles both 'connecting' and 'connected' state. Calling more than once
// is a no-op.
function switchToRemoteUI() {
// Indicate that the state is 'connecting' or 'connected' to the user.
// For example, hide the video element as only controls are needed.
videoElem.style.display = "none";
// Stop monitoring the availability of remote playback devices.
videoElem.remote.cancelWatchAvailability();
};
function switchToLocalUI() {
// Show the video element.
videoElem.style.display = "inline";
// Start watching the device availability again.
videoElem.remote.watchAvailability(availabilityCallback);
};
<script>
</pre>
</section>
</section>
<section>
<h2>API</h2>
<section>
<h3>Common idioms</h3>
<p>
A <dfn>local playback device</dfn> is the device the <a>browsing context</a> is running on along with the default
video/audio outputs the device has.
<div class="note">
A <a>local playback device</a> might have extra outputs, like an external display or
speakers/headphones. As long as the switch of what output to use happens outside of the <a>user agent</a> on
the system level, the playback is considered to happen on a <a>local playback device</a> for the purpose of
this spec.
</div>
</p>
<p>
A <dfn data-lt="remote playback devices">remote playback device</dfn> is any other device
but the <a>local playback device</a> that the <a>browsing context</a> can use to play media on.
</p>
<p>
A <dfn>media element state</dfn> is the set of all single <a>media element</a> properties
observable by the page and/or the user via the <a>user agent</a> implementation. The new properties introduced
by this spec are not considered part of the <a>media element state</a> for convenience.
<div class="example">
For example, the <code>paused</code> attribute or the pause/resume button reflecting that state on the default
controls of the media element would be a part of <a>media element state</a>.
</div>
</p>
<p>
A <dfn>local playback state</dfn> is the user agent implementation of <a>media element state</a>
for the particular <a>media element</a> for playback on the <a>local playback device</a>.
</p>
<p>
A <dfn>remote playback state</dfn> is the user agent implementation of <a>media element state</a>
for the particular <a>media element</a> for playback on the certain <a>remote playback device</a>.
</p>
<p class="note">
For a good user experience it is important that the <a>media element state</a>
doesn't change unexpectedly when the <code><a for="RemotePlayback">state</a></code> changes.
It is also important that <a>remote playback state</a> is in sync with the <a>media element state</a> so when
the media is paused on the <a>remote playback device</a> it looks paused to both the user and the page.
</p>
</section>
<section>
<h3><code><a>RemotePlayback</a></code> interface</h3>
<pre class='idl'>
interface RemotePlayback : EventTarget {
Promise<long> watchAvailability(RemotePlaybackAvailabilityCallback callback);
Promise<void> cancelWatchAvailability(optional long id);
readonly attribute RemotePlaybackState state;
attribute EventHandler onconnecting;
attribute EventHandler onconnect;
attribute EventHandler ondisconnect;
Promise<void> prompt();
};
enum RemotePlaybackState {
"connecting",
"connected",
"disconnected"
};
callback RemotePlaybackAvailabilityCallback = void(boolean available);
</pre>
<section>
<h4>
Observing remote playback device availability
</h4>
<p>
A <a><code>RemotePlaybackAvailabilityCallback</code></a> is the way for the page to obtain the
<dfn>remote playback device availability</dfn> for the corresponding <a>media element</a>. If the user
agent can <a>monitor the list of available remote playback devices</a> in the background (without
a pending request to <code><a for="RemotePlayback">prompt</a>()</code>), the
<a><code>RemotePlaybackAvailabilityCallback</a></code> behavior defined below MUST be implemented by
the user agent. Otherwise, the promise returned by <code><a for="RemotePlayback">watchAvailability</a>()</code>
MUST be rejected with <a>NotSupportedError</a>.
</p>
<section>
<h5>
The set of availability callbacks
</h5>
<p>
The <a>user agent</a> MUST keep track of the <dfn>set of
availability callbacks</dfn> registered with each <a>media element</a>
through the <code><a for="RemotePlayback">watchAvailability</a>()</code> method. The
<a>set of availability callbacks</a> for each <a>RemotePlayback</a> object is represented as a set of tuples
<em>(<var>callbackId</var>, <var>callback</var>)</em>, initially empty, where:
<ol>
<li>
<var>callbackId</var> is an id unique to the <a>RemotePlayback</a> object;
</li>
<li>
<var>callback</var> is a <a>RemotePlaybackAvailabilityCallback</a> object;
</li>
</ol>
</p>
<p>
Since there's one and only one <a>RemotePlayback</a> object per each <a>media element</a>, <a>set of
availability callbacks</a> of a <a>media element</a> is the same set as the <a>set of availability
callbacks</a> of the <a>RemotePlayback</a> object referred to by the element's <a
for="HTMLMediaElement">remote</a> property.
</p>
<p>
The combined set of all <a data-lt="set of availability callbacks">sets of availability callbacks</a> of
all <a>RemotePlayback</a> objects known to the <a>browsing context</a> is referred to as
<dfn>global set of availability callbacks</dfn>.
</p>
</section>
<section>
<h5>
The list of available remote playback devices
</h5>
<p>
The <a>user agent</a> MUST keep a <dfn>list of available remote playback devices</dfn>.
This list contains <a>remote playback devices</a> and is populated based on an implementation
specific discovery mechanism. It is set to the most recent result of the algorithm to <a>monitor
the list of available remote playback devices</a>.
</p>
<p>
The <a>user agent</a> MAY not support running the algorithm to <a>monitor the list of available remote
playback devices</a> continuously, for example, because of platform or power consumption restrictions.
In this case the promise returned by <code><a for="RemotePlayback">watchAvailability</a>()</code>
MUST be rejected with <a>NotSupportedError</a>, the <a>global set of availability callbacks</a> will be empty
and the algorithm to <a>monitor the list of available remote playback devices</a> will only run as part
of the <a>initiate remote playback</a> algorithm.
</p>
<p>
When the <a>global set of availability callbacks</a> is not empty, the <a>user agent</a> MUST <a>monitor the
list of available remote playback devices</a> continuously, so that pages can keep track of the last
value received via the registered callbacks to offer remote playback only when there are available devices.
</p>
<p class="note">
User agents SHOULD NOT <a>monitor the list of available remote playback devices</a> when possible, to satisfy the
<a href="https://github.com/w3c/remote-playback/blob/gh-pages/use-cases.md#power-saving-friendly">
power saving non-functional requirement</a>. For example, the <a>user agent</a> MAY not run the
monitoring algorithm when the <a>global set of availability callbacks</a> is empty or each page that has
<a data-lt="media element">media elements</a> with non-empty <a>set of availability callbacks</a> is not in
the foreground.
</p>
<p>
Some <a>remote playback devices</a> may only be able to play a subset of
<a for="media resource">media resources</a> because of functional, security or hardware limitations.
Examples are set-top boxes, smart TVs or networked speakers capable of rendering only certain formats of video
and/or audio. We say that such a device is a <dfn>compatible remote playback device</dfn> for a
<a>media resource</a> if the <a>user agent</a> can reasonably guarantee that
the remote playback of the media specified by the resource will succeed on that device.
</p>
<p>
The <a>media resources</a> of a <a>media element</a>, that were considered by the user agent
to find a <a>compatible remote playback device</a>, are called the <dfn>availability sources set</dfn>.
</p>
<p>
The <a>media resource</a> of a <a>media element</a>, that is used to <a>initiate remote playback</a>
on the selected <a>remote playback device</a> is called <dfn>remote playback source</dfn>. Remote
playback source MUST belong to <a>availability sources set</a>.
</p>
<p>
The mechanism of picking the <a>availability sources set</a> and the <a>remote playback source</a>
is implementation-specific. For example, the user agent MUST either use the
<a for="media element">currentSrc</a> of the <a>media element</a> for both availability monitoring
and remote playback or use all the media resources associated with the media element as the
<a>availability sources set</a> and pick one of the resources as the <a>remote playback source</a>
after user selects the <a>remote playback device</a>.
</p>
<p>
Remote playback is said to be <dfn>unavailable</dfn> for the <a>media element</a> if the
<a>list of available remote playback devices</a> is empty or none of them is compatible
with any source from <a>availability sources set</a> for the <a>media element</a>. The remote playback is
said to be <dfn>available</dfn> otherwise. A <code>boolean</code> set to <code>false</code> if the remote
playback is <a>unavailable</a> for the <a>media element</a> or <code>true</code> if it is <a>available</a>
is called <dfn>availability</dfn> for the <a>media element</a>.
</p>
</section>
<section>
<h5>
Getting the <a>remote playback devices</a> availability information
</h5>
<p>
When the <code><dfn for="RemotePlayback">watchAvailability</dfn>()</code> method is
called, the user agent MUST run the following steps:
</p>
<dl>
<dt>
Input
</dt>
<dd>
<var>callback</var>, the callback that will get fired with availability information.
</dd>
<dt>
Output
</dt>
<dd>
<var>promise</var>, a promise.
</dd>
</dl>
<ol>
<li>
Let <var>promise</var> be a new promise.
</li>
<li>
Return <var>promise</var>, and run the following steps below:
</li>
<li>
If the <a for="HTMLMediaElement">disableRemotePlayback</a> attribute is present
for the <a>media element</a>, reject the <var>promise</var> with <a>InvalidStateError</a>
and abort all the remaining steps.
</li>
<li>
If the user agent is unable to <a>monitor the list of available
remote playback devices</a> for the entire lifetime of the <a>browsing context</a>
(for instance, because the user has disabled this feature), then run the following
steps in parallel:
<ol>
<li>
Fulfill <var>promise</var>.
</li>
<li>
<a>Queue a task</a> to invoke the <var>callback</var> with <code>false</code> as its
argument.
</li>
<li>Abort all remaining steps.
</li>
</ol>
</li>
<li>If the user agent is unable to continuously <a>monitor the list
of available remote playback devices</a> but can do it for a short period of time
when <a data-lt="initiate remote playback">initiating remote playback</a>, then:
<ol>
<li>
Reject <var>promise</var> with a <a>NotSupportedError</a>
exception.
</li>
<li>Abort all remaining steps.
</li>
</ol>
</li>
<li>
Let <var>callbackId</var> be a number unique to the <a>media element</a> that will identify the
<var>callback</var>.
</li>
<li>
Create a tuple <em>(<var>callbackId</var>, <var>callback</var>)</em> and add it to the
<a>set of availability callbacks</a> for this <a>media element</a>.
</li>
<li>
Fulfill <var>promise</var> with the <var>callbackId</var> and run the following steps <a>in parallel</a>:
<ol>
<li>
<a>Queue a task</a> to invoke the <var>callback</var> with the current <a>availability</a> for the
<a>media element</a>.
</li>
<li>
Run the algorithm to <a>monitor the list of available remote playback devices</a>.
</li>
</ol>
</li>
</ol>
</section>
<section>
<h5>
Monitoring the list of available remote playback devices
</h5>
<p>
If the <a>set of availability callbacks</a> is non-empty, or there is
a pending request to <a for="RemotePlayback" data-lt="prompt">initiate remote playback</a>,
the <a>user agent</a> MUST <dfn>monitor the list of available remote playback devices</dfn> by
running the following steps:
</p>
<ol>
<li>
Retrieve available remote playback devices (using an implementation specific mechanism)
and let <var>newDevices</var> be this list.
</li>
<li>
For each <a>media element</a> known to the <a>browsing context</a>:
<ol>
<li>
If the <a for="HTMLMediaElement">disableRemotePlayback</a> attribute is present
for <var>mediaElement</var>, abort all the remaining steps for this tuple and continue to the next
one.
</li>
<li>
Set <var>newAvailabilityValue</var> to the value of <a>availability</a> for the <a>media element</a>
calculated using the <var>newDevices</var> list instead of the <a>list of available remote playback
devices</a>.
</li>
<li>
If the current <a>availability</a> is not equal to <var>newAvailabilityValue</var>, then
for each <em>(<var>callbackId</var>, <var>callback</var>)</em> of the element's
<a>set of availability callbacks</a>:
<ol>
<li>
<a>Queue a task</a> to invoke <var>callback</var> with <var>newAvailabilityValue</var> as its argument.
</li>
</ol>
</li>
</ol>
</li>
<li>
Set the <a>list of available remote playback devices</a> to the value of <var>newDevices</var>.
</li>
</ol>
</section>
<section>
<h5>
Stop observing remote playback devices availability
</h5>
<p>
When a <code><a for="RemotePlayback">cancelWatchAvailability</a>()</code> method is called,
the <a>user agent</a> MUST run the following steps:
</p>
<dl>
<dt>
Input
</dt>
<dd>
<var>id</var>, the callback identifier.
</dd>
<dt>
Output
</dt>
<dd>
<var>promise</var>, a promise.
</dd>
</dl>
<ol>
<li>
Let <var>promise</var> be a new promise.
</li>
<li>
Return <var>promise</var>, and run the following steps below:
</li>
<li>
If the <a for="HTMLMediaElement">disableRemotePlayback</a> attribute is present
for the <a>media element</a>, reject <var>promise</var> with <a>InvalidStateError</a>
and abort all the remaining steps.
</li>
<li>
If the parameter <var>id</var> is <code>undefined</code>, clear the <a>set of availability callbacks</a>.
</li>
<li>
Otherwise, if <var>id</var> matches the <var>callbackId</var> for any entry in the
<a>set of availability callbacks</a>, remove the entry from the set.
</li>
<li>
Otherwise, reject <var>promise</var> with <a>NotFoundError</a> and abort all the remaining steps.
</li>
<li>
If the <a>set of availability callbacks</a> is now empty and there is no pending request to
<a for="RemotePlabyack" data-lt="prompt">initiate remote playback</a>, cancel any pending task
to <a>monitor the list of available remote playback devices</a> for power saving purposes.
</li>
<li>
Fulfill <var>promise</var>.
</li>
</ol>
<div class="note">
The mechanism used to monitor <a>remote playback devices</a> availability and determine the
compatibility of a <a>remote playback device</a> with the selected <a>availability sources set</a>
is left to the user agent.
</div>
</section>
</section>
<section>
<h4>
Prompt user for changing remote playback statee
</h4>
<p>
When the <code><dfn for="RemotePlayback">prompt</dfn>()</code>
method is called, the <a>user agent</a> MUST run the following steps:
</p>
<dl>
<dt>
Input
</dt>
<dd>
None, but the algorithm references the <a>media element</a>, its <a data-lt="RemotePlayback">remote</a>
property and its <a>availability sources set</a>.
</dd>
<dt>
Output
</dt>
<dd>
A promise.
</dd>
</dl>
<ol>
<li>
Let <var>promise</var> be a new promise.
</li>
<li>
Return <var>promise</var> and continue running these steps
<a>in parallel</a>.
</li>
<li>
If the <a for="HTMLMediaElement">disableRemotePlayback</a> attribute is present
for the <a>media element</a>, reject the <var>promise</var> with <a>InvalidStateError</a>
and abort all the remaining steps.
</li>
<li>
If there is already an unsettled promise from a previous
call to <a for="RemotePlayback">prompt</a> for the same
<a>media element</a> or even for the same <a>browsing context</a>,
the user agent MAY reject <var>promise</var> with an <a>OperationError</a>
exception and abort all remaining steps.
<div class="note">
The rationale here is that the user agent might use the UI that's modal to either the
<a>media element</a> or the <a>browsing context</a>.
In such a case, the second call to <code><a for="RemotePlayback">prompt</a>()</code>
would not be able to show any UI.
</div>
</li>
<li>
OPTIONALLY, if the <a>user agent</a> knows a priori that showing the UI for this particular <a>media
element</a> is not feasible, reject <var>promise</var> with a <a>NotSupportedError</a> and abort all
remaining steps.
<div class="note">
An example of such scenario could be when the user agent only supports <a>media flinging</a> case
while the media element's source is not a <a>URL</a> that could be passed over to any <a>remote playback device</a>.
</div>
</li>
<li>
If the algorithm isn't <a>allowed to show a popup</a>, reject <var>promise</var>
with an <a>InvalidAccessError</a> exception and abort these steps.
</li>
<li>
If the <a>user agent</a> needs to show the <a>list of available remote playback devices</a> and
is not <a data-lt="monitor the list of available remote playback devices">
monitoring the list of available remote playback devices</a>, run the steps to
<a>monitor the list of available remote playback devices</a> <a>in parallel</a>.
</li>
<li>
If the <a>list of available remote playback devices</a> is empty and will remain so
before the request for user permission is completed, reject
<var>promise</var> with a <a>NotFoundError</a> exception and abort all remaining steps.
</li>
<li>
If the <code><a for="RemotePlayback">state</a></code> is <code>disconnected</code> and
<a>availability</a> for the <a>media element</a> is <code>false</code>, reject <var>promise</var> with
a <a>NotSupportedError</a> exception and abort all remaining steps.
</li>
<li>
If the user picked a <a>remote playback device</a> <var>device</var> to
<dfn>initiate remote playback</dfn> with, the <a>user agent</a> MUST run the following steps:
<ol>
<li>
Set the <a for="RemotePlayback">state</a> of the <var>remote</var> object to
<a for="RemotePlaybackState">connecting</a>.
</li>
<li>
Fulfill <var>promise</var>.
</li>
<li>
<a>Queue a task</a> to <a>fire a simple event</a> with the name <a>connecting</a> at the
<a data-lt="RemotePlayback">remote</a> property of the <a>media element</a>.
The event must not bubble, must not be cancelable, and has no default action.
</li>
<li>
<a>Establish a connection with the remote playback device</a> <var>device</var> for the <a>media element</a>.
</li>
</ol>
<p class="note">
By picking a <a>remote playback device</a> the user <em>grants permission</em> to use the device.
</p>
</li>
<li>
Otherwise, if the user chose to disconnect from the <a>remote playback device</a> <var>device</var>,
the <a>user agent</a> MUST run the following steps:
<ol>
<li>
Fulfill <var>promise</var>.
</li>
<li>
Run the <a>disconnect from remote playback device</a> algorithm for the <var>device</var>.
</li>
</ol>
</li>
<li>
Otherwise, the user is considered to <em>deny permission</em> to use the device, so reject
<var>promise</var> with <a>NotAllowedError</a> exception and hide the UI shown by the <a>user agent</a>
</li>
</ol>
<div class="note">
The details of implementing the UI and device selection are left to the user agent;
for example it MAY show the user a dialog and allow the user to select an available device
(<em>granting permission</em>), or cancel the selection (<em>denying permission</em>).
</div>
<div class="note">
The algorithm to select the <a>remote playback source</a> for a selected device
depends on the user agent and supported <a>remote playback device</a> types. For example,
in case of <a>media mirroring</a> the user agent can simply follow the <a>HTMLMediaElement</a>'s
<a>resource selection algorithm</a>. However, if <a>media remoting</a> or <a>media flinging</a> is used,
the best media source can depend on the selected <a>remote playback device</a> fetch and
playback capabilities.
</div>
</section>
<section>
<h4>
The <code><a for="RemotePlayback">state</a></code> attribute
</h4>
<p>
The <dfn for="RemotePlayback"><code>state</code></dfn> attribute represents the
<a>RemotePlayback</a> connection's current state. It can take one of
the values of <a>RemotePlaybackState</a> depending on the
connection state:
</p>
<ul dfn-for="RemotePlaybackState">
<li>
<dfn>connecting</dfn> means that the user agent is attempting to
<a>initiate remote playback</a> with the selected <a>remote playback device</a>.
This is the initial state when the <code>promise</code> returned by
<code><a for="RemotePlayback">prompt</a>()</code> is fulfilled with <code>true</code>.
The local playback of the media element continues in this
state and media commands still take effect on the <a>local playback state</a>.
</li>
<li>
<dfn>connected</dfn> means that the transition from local to remote playback has finished
and all media commands now take effect on the <a>remote playback state</a>.
</li>
<li>
<dfn>disconnected</dfn> means that the remote playback
has not been <a data-lt="initiate remote playback">initiated</a>, has failed to initiate or has been
stopped. All media commands will take effect on the <a>local playback state</a>. The remote playback
can be initiated through a call to <code><a for="RemotePlayback">prompt</a>()</code>.
</li>
</ul>
</section>
<section>
<h4>
Establishing a connection with a remote playback device
</h4>
<p>
When the <a>user agent</a> is to <dfn>establish a connection with the remote playback device</dfn>,
it MUST run the following steps:
</p>
<dl>
<dt>
Input
</dt>
<dd>
<var>remote</var>, the <a>RemotePlayback</a> object that is to be connected.
</dd>
<dd>
<var>device</var>, the <a>remote playback device</a> to connect to.
</dd>
</dl>
<ol>
<li>
If the <a for="RemotePlayback">state</a> of <var>remote</var> is not equal to
<a for="RemotePlaybackState">connecting</a>, abort all the remaining steps.
</li>
<li>
Request connection of <var>remote</var> to <var>device</var>. The implementation of this step is specific
to the <a>user agent</a>.
</li>
<li>
If connection completes successfully, <a>queue a task</a> to run the following steps:
<ol>
<li>
Set the <a for="RemotePlayback">state</a> of <var>remote</var> to
<a for="RemotePlaybackState">connected</a>.
</li>
<li>
<a>Fire a simple event</a> named <a>connect</a> at <var>remote</var>.
</li>
<li>
Synchronize the current <a>media element state</a> with the <a>remote playback state</a>.
Implementation is specific to <a>user agent</a>.
</li>
</ol>
</li>
<li>
If connection fails, <a>queue a task</a> to run the following steps:
<ol>
<li>
Set the <a for="RemotePlayback" data-lt="state">remote playback state</a> of <var>remote</var> to
<a for="RemotePlaybackState">disconnected</a>.
</li>
<li>
<a>Fire a simple event</a> named <a>disconnect</a> at <var>remote</var>.
</li>
</ol>
</li>
</ol>
<div class="note">
The mechanism that is used to connect the user agent with the <a>remote playback device</a> and
play the <a>remote playback source</a> is an implementation choice of the user agent. The
connection will likely have to provide a two-way messaging abstraction capable of carrying media commands to
the remote playback device and receiving media playback state in order to keep the <a>media element state</a> and
<a>remote playback state</a> in sync (unless <a>media mirroring</a> is used).
</div>
</section>
<section>
<h4>Media commands and media playback state</h4>
<p>
The <a>HTMLMediaElement</a> interface interacts with the remotely
played media as soon as the connection with the <a>remote playback
device</a> is established.
</p>
<p>
In particular, as soon as the <a for="RemotePlayback">state</a> of a
<a>RemotePlayback</a> object has changed to
<a for="RemotePlaybackState">connected</a>, the <a>user agent</a>
MUST send all the media commands issued on the
<a>HTMLMediaElement</a> object with which the <a>RemotePlayback</a>
object is associated to the <a>remote playback device</a> in order
to change the <a>remote playback state</a> vs the <a>local playback state</a>.
</p>
<p>
Similarly, the <a>user agent</a> MUST reflect all updates of the <a>remote playback state</a>
received from the <a>remote playback device</a> on the <a>media element state</a>.
</p>
<p>
If sending any command fails, the <a>user agent</a> MAY <a>disconnect from
remote playback device</a>.
</p>
</section>
<section>
<h4>Disconnecting from remote playback device</h4>
<p>
When the <a>user agent</a> is to <dfn>disconnect from remote playback device</dfn>,
it MUST do the following:
</p>
<dl>
<dt>
Input
</dt>
<dd>
<var>remote</var>, the <a>RemotePlayback</a> object representing the playback to be stopped.
</dd>
<dd>
<var>device</var>, the <a>remote playback device</a> to disconnect from.
</dd>
</dl>
<ol>
<li>
If the <a for="RemotePlayback"><code>state</code></a> of <var>remote</var> is
<code>disconnected</code>, abort all remaining steps.
</li>
<li>
<a>Queue a task</a> to run the following steps:
<ol>
<li>
Request disconnection of <var>remote</var> from the <var>device</var>. Implementation
is <a>user agent</a> specific.