1 // -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 this.EXPORTED_SYMBOLS = ["CastingApps"];
8 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
10 Cu.import("resource://gre/modules/Services.jsm");
11 Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm");
15 _sendEventToVideo: function (element, data) {
16 let event = element.ownerDocument.createEvent("CustomEvent");
17 event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(data));
18 element.dispatchEvent(event);
21 makeURI: function (url, charset, baseURI) {
22 return Services.io.newURI(url, charset, baseURI);
25 getVideo: function (element) {
30 let extensions = SimpleServiceDiscovery.getSupportedExtensions();
31 let types = SimpleServiceDiscovery.getSupportedMimeTypes();
33 // Grab the poster attribute from the <video>
34 let posterURL = element.poster;
36 // First, look to see if the <video> has a src attribute
37 let sourceURL = element.src;
39 // If empty, try the currentSrc
41 sourceURL = element.currentSrc;
45 // Use the file extension to guess the mime type
46 let sourceURI = this.makeURI(sourceURL, null, this.makeURI(element.baseURI));
47 if (this.allowableExtension(sourceURI, extensions)) {
48 return { element: element, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI};
52 // Next, look to see if there is a <source> child element that meets
54 let sourceNodes = element.getElementsByTagName("source");
55 for (let sourceNode of sourceNodes) {
56 let sourceURI = this.makeURI(sourceNode.src, null, this.makeURI(sourceNode.baseURI));
58 // Using the type attribute is our ideal way to guess the mime type. Otherwise,
59 // fallback to using the file extension to guess the mime type
60 if (this.allowableMimeType(sourceNode.type, types) || this.allowableExtension(sourceURI, extensions)) {
61 return { element: element, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: sourceNode.type };
68 sendVideoToService: function (videoElement, service) {
72 let video = this.getVideo(videoElement);
77 // Make sure we have a player app for the given service
78 let app = SimpleServiceDiscovery.findAppForService(service);
82 video.title = videoElement.ownerDocument.defaultView.top.document.title;
84 // If the video is currently playing on the device, pause it
85 if (!video.element.paused) {
86 video.element.pause();
91 app.start(started => {
93 Cu.reportError("CastingApps: Unable to start app");
97 app.remoteMedia(remoteMedia => {
99 Cu.reportError("CastingApps: Failed to create remotemedia");
106 remoteMedia: remoteMedia,
109 source: video.source,
112 videoRef: Cu.getWeakReference(video.element)
119 getServicesForVideo: function (videoElement) {
120 let video = this.getVideo(videoElement);
125 let filteredServices = SimpleServiceDiscovery.services.filter(service => {
126 return this.allowableExtension(video.sourceURI, service.extensions) ||
127 this.allowableMimeType(video.type, service.types);
130 return filteredServices;
133 getServicesForMirroring: function () {
134 return SimpleServiceDiscovery.services.filter(service => service.mirror);
137 // RemoteMedia callback API methods
138 onRemoteMediaStart: function (remoteMedia) {
143 remoteMedia.load(this.session.data);
145 let video = this.session.videoRef.get();
147 this._sendEventToVideo(video, { active: true });
151 onRemoteMediaStop: function (remoteMedia) {
154 onRemoteMediaStatus: function (remoteMedia) {
157 allowableExtension: function (uri, extensions) {
158 return (uri instanceof Ci.nsIURL) && extensions.indexOf(uri.fileExtension) != -1;
161 allowableMimeType: function (type, types) {
162 return types.indexOf(type) != -1;