browser/modules/CastingApps.jsm
author tbirdbld
Fri, 13 Feb 2015 17:51:11 -0500
branchGECKO360b1_2015021317_RELBRANCH
changeset 243774 fa98dcdc9406fa36b418b7dc97ebb6a8b7d50ef2
parent 233372 ed68a14922feb18b720c52b5f1f6d539c34e99aa
child 245731 a7770ec46f0401c0a855e68f656ad8362be77ffc
permissions -rw-r--r--
Added THUNDERBIRD_36_0b1_RELEASE THUNDERBIRD_36_0b1_BUILD2 tag(s) for changeset bbb250fc79b7. DONTBUILD CLOSED TREE a=release

// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["CastingApps"];

const { classes: Cc, interfaces: Ci, utils: Cu } = Components;

Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm");


var CastingApps = {
  _sendEventToVideo: function (element, data) {
    let event = element.ownerDocument.createEvent("CustomEvent");
    event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(data));
    element.dispatchEvent(event);
  },

  makeURI: function (url, charset, baseURI) {
    return Services.io.newURI(url, charset, baseURI);
  },

  getVideo: function (element) {
    if (!element) {
      return null;
    }

    let extensions = SimpleServiceDiscovery.getSupportedExtensions();
    let types = SimpleServiceDiscovery.getSupportedMimeTypes();

    // Grab the poster attribute from the <video>
    let posterURL = element.poster;

    // First, look to see if the <video> has a src attribute
    let sourceURL = element.src;

    // If empty, try the currentSrc
    if (!sourceURL) {
      sourceURL = element.currentSrc;
    }

    if (sourceURL) {
      // Use the file extension to guess the mime type
      let sourceURI = this.makeURI(sourceURL, null, this.makeURI(element.baseURI));
      if (this.allowableExtension(sourceURI, extensions)) {
        return { element: element, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI};
      }
    }

    // Next, look to see if there is a <source> child element that meets
    // our needs
    let sourceNodes = element.getElementsByTagName("source");
    for (let sourceNode of sourceNodes) {
      let sourceURI = this.makeURI(sourceNode.src, null, this.makeURI(sourceNode.baseURI));

      // Using the type attribute is our ideal way to guess the mime type. Otherwise,
      // fallback to using the file extension to guess the mime type
      if (this.allowableMimeType(sourceNode.type, types) || this.allowableExtension(sourceURI, extensions)) {
        return { element: element, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: sourceNode.type };
      }
    }

    return null;
  },

  sendVideoToService: function (videoElement, service) {
    if (!service)
      return;

    let video = this.getVideo(videoElement);
    if (!video) {
      return;
    }

    // Make sure we have a player app for the given service
    let app = SimpleServiceDiscovery.findAppForService(service);
    if (!app)
      return;

    video.title = videoElement.ownerDocument.defaultView.top.document.title;
    if (video.element) {
      // If the video is currently playing on the device, pause it
      if (!video.element.paused) {
        video.element.pause();
      }
    }

    app.stop(() => {
      app.start(started => {
        if (!started) {
          Cu.reportError("CastingApps: Unable to start app");
          return;
        }

        app.remoteMedia(remoteMedia => {
          if (!remoteMedia) {
            Cu.reportError("CastingApps: Failed to create remotemedia");
            return;
          }

          this.session = {
            service: service,
            app: app,
            remoteMedia: remoteMedia,
            data: {
              title: video.title,
              source: video.source,
              poster: video.poster
            },
            videoRef: Cu.getWeakReference(video.element)
          };
        }, this);
      });
    });
  },

  getServicesForVideo: function (videoElement) {
    let video = this.getVideo(videoElement);
    if (!video) {
      return {};
    }

    let filteredServices = SimpleServiceDiscovery.services.filter(service => {
      return this.allowableExtension(video.sourceURI, service.extensions) ||
             this.allowableMimeType(video.type, service.types);
    });

    return filteredServices;
  },

  // RemoteMedia callback API methods
  onRemoteMediaStart: function (remoteMedia) {
    if (!this.session) {
      return;
    }

    remoteMedia.load(this.session.data);

    let video = this.session.videoRef.get();
    if (video) {
      this._sendEventToVideo(video, { active: true });
    }
  },

  onRemoteMediaStop: function (remoteMedia) {
  },

  onRemoteMediaStatus: function (remoteMedia) {
  },

  allowableExtension: function (uri, extensions) {
    return (uri instanceof Ci.nsIURL) && extensions.indexOf(uri.fileExtension) != -1;
  },

  allowableMimeType: function (type, types) {
    return types.indexOf(type) != -1;
  }
};