├── .gitignore
├── lang
├── de.coffee
└── it.coffee
├── demo
├── font
│ ├── vjs.eot
│ ├── vjs.ttf
│ ├── vjs.woff
│ └── vjs.svg
├── index.html
└── video-js.css
├── screenshots
└── chromecast-player.jpg
├── src
├── videojs.chromecast.coffee
├── videojs.chromecast-tech.coffee
├── videojs.chromecast.less
└── videojs.chromecast-component.coffee
├── demo-server.js
├── CHANGELOG.md
├── bower.json
├── package.json
├── LICENSE.md
├── Gruntfile.coffee
├── README.md
└── dist
├── videojs.chromecast.min.css
├── videojs.chromecast.css
├── videojs.chromecast.min.js
└── videojs.chromecast.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | npm-debug.log
3 | node_modules
4 |
--------------------------------------------------------------------------------
/lang/de.coffee:
--------------------------------------------------------------------------------
1 | vjs.addLanguage "de",
2 | "CASTING TO": "WIEDERGABE AUF"
3 |
--------------------------------------------------------------------------------
/lang/it.coffee:
--------------------------------------------------------------------------------
1 | vjs.addLanguage "it",
2 | "CASTING TO": "PLAYBACK SU"
3 |
--------------------------------------------------------------------------------
/demo/font/vjs.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kim-company/videojs-chromecast/HEAD/demo/font/vjs.eot
--------------------------------------------------------------------------------
/demo/font/vjs.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kim-company/videojs-chromecast/HEAD/demo/font/vjs.ttf
--------------------------------------------------------------------------------
/demo/font/vjs.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kim-company/videojs-chromecast/HEAD/demo/font/vjs.woff
--------------------------------------------------------------------------------
/screenshots/chromecast-player.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kim-company/videojs-chromecast/HEAD/screenshots/chromecast-player.jpg
--------------------------------------------------------------------------------
/src/videojs.chromecast.coffee:
--------------------------------------------------------------------------------
1 | vjs.plugin "chromecast", (options) ->
2 | @chromecastComponent = new vjs.ChromecastComponent(@, options)
3 | @controlBar.addChild @chromecastComponent
4 |
--------------------------------------------------------------------------------
/demo-server.js:
--------------------------------------------------------------------------------
1 | var express = require("express");
2 | var app = express();
3 |
4 | app.use(express.static(__dirname));
5 |
6 | app.listen(3000);
7 | console.log("Listening on port 3000");
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | CHANGELOG
2 | =========
3 |
4 | ## HEAD (Unreleased)
5 | *(no changes)*
6 |
7 | ## 1.1.1 (13.04.2014)
8 | * The Chromecast will no longer stay paused after seeking. (#10)
9 | * If casting is ended while playing, the browser seeks to the last position and plays. (#10)
10 |
11 | ## 1.1.0 (18.02.2014)
12 | * Added `bower.json`. It can now be installed by calling `bower install videojs-chromecast`. (#8)
13 | * Added WebM and HLS support. (#9)
14 | * Fixed compatibility with Video.JS v4.12.0.
15 |
16 | ## 1.0.0 (22.09.2014)
17 | * First release
18 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "videojs-chromecast",
3 | "version": "1.1.1",
4 | "homepage": "https://github.com/kim-company/videojs-chromecast",
5 | "author": {
6 | "name": "KIM Keep In Mind GmbH, srl",
7 | "email": "info@keepinmind.info"
8 | },
9 | "description": "Videojs plugin for chromecast",
10 | "main": [
11 | "dist/videojs.chromecast.min.js",
12 | "dist/videojs.chromecast.min.css"
13 | ],
14 | "license": "MIT",
15 | "ignore": [
16 | "**/.*",
17 | "node_modules",
18 | "bower_components",
19 | "test",
20 | "tests"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "videojs-chromecast",
3 | "version": "1.1.1",
4 | "homepage": "https://github.com/kim-company/videojs-chromecast",
5 | "author": {
6 | "name": "KIM Keep In Mind GmbH, srl",
7 | "email": "info@keepinmind.info"
8 | },
9 | "license": "MIT",
10 | "engines": {
11 | "node": ">= 0.8.0"
12 | },
13 | "dependencies": {},
14 | "devDependencies": {
15 | "express": "~ 4.12.3",
16 | "grunt": "~ 0.4.0",
17 | "grunt-contrib-uglify": "~ 0.9.1",
18 | "grunt-contrib-clean": "~ 0.6.0",
19 | "grunt-contrib-cssmin": "~ 0.12.2",
20 | "grunt-contrib-coffee": "~ 0.13.0",
21 | "grunt-contrib-less": "~ 1.0.1",
22 | "grunt-banner": "~ 0.3.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 KIM Keep In Mind GmbH, srl
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Demo - VideoJS Chromecast Plugin
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
24 |
25 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 |
3 | # Initialize the configuration
4 | @initConfig
5 |
6 | pkg: @file.readJSON "package.json"
7 |
8 | banner: """
9 | /*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today('yyyy-mm-dd') %>
10 | * <%= pkg.homepage %>
11 | * Copyright (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %>; Licensed <%= pkg.license %> */
12 |
13 | """
14 |
15 | clean:
16 | src: "dist/*"
17 |
18 | coffee:
19 | compileJoined:
20 | options:
21 | join: true
22 | files:
23 | "dist/videojs.chromecast.js": [
24 | "lang/*"
25 | "src/videojs.chromecast.coffee"
26 | "src/videojs.chromecast-component.coffee"
27 | "src/videojs.chromecast-tech.coffee"
28 | ]
29 |
30 | uglify:
31 | options:
32 | compress:
33 | drop_console: true
34 | pure_funcs: ["vjs.log"]
35 | dist:
36 | src: "dist/videojs.chromecast.js"
37 | dest: "dist/videojs.chromecast.min.js"
38 |
39 | less:
40 | development:
41 | files:
42 | "dist/videojs.chromecast.css": "src/videojs.chromecast.less"
43 |
44 | cssmin:
45 | dist:
46 | src: "dist/videojs.chromecast.css"
47 | dest: "dist/videojs.chromecast.min.css"
48 |
49 | usebanner:
50 | options:
51 | position: "top"
52 | banner: "<%= banner %>"
53 | files:
54 | src: [
55 | "dist/videojs.chromecast.js"
56 | "dist/videojs.chromecast.min.js"
57 | "dist/videojs.chromecast.min.css"
58 | "dist/videojs.chromecast.css"
59 | ]
60 |
61 | # Load external Grunt task plugins
62 | @loadNpmTasks "grunt-contrib-clean"
63 | @loadNpmTasks "grunt-contrib-uglify"
64 | @loadNpmTasks "grunt-contrib-cssmin"
65 | @loadNpmTasks "grunt-contrib-coffee"
66 | @loadNpmTasks "grunt-contrib-less"
67 | @loadNpmTasks "grunt-banner"
68 |
69 | # Default task
70 | @registerTask "default", ["clean", "coffee", "uglify", "less", "cssmin", "usebanner"]
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [VideoJS](http://www.videojs.com) Chromecast Plugin
2 | Displays a Chromecast button in the control bar. The button is only shown if the [Google Cast extension](https://chrome.google.com/webstore/detail/google-cast/boadgeojelhgndaghljhdicfkmllpafd) is installed and a Chromecast is currently available.
3 |
4 | 
5 |
6 | ## Getting started
7 | **NOTE:** The Chromecast Plugin won't work if you open the index.html in the browser. It must run on a webserver.
8 |
9 | 1. Add `data-cast-api-enabled="true"` in your `` Tag.
10 | 2. Include `videojs.chromecast.css` and `videojs.chromecast.js` in the ``.
11 | 3. Initialize the VideoJS Player with the Chromecast Plugin like the [configuration example](#configuration-example).
12 | 4. When a Chromecast is available in your network, you should see the cast button in the controlbar.
13 |
14 | If you are not able to configure the player, check out the [demo directory](https://github.com/kim-company/videojs-chromecast/tree/master/demo).
15 |
16 | #### Configuration example
17 | ```javascript
18 | videojs("my_player_id", {
19 | plugins: {
20 | chromecast: {
21 | appId: "AppID of your Chromecast App",
22 | metadata: {
23 | title: "Title",
24 | subtitle: "Subtitle"
25 | }
26 | }
27 | }
28 | });
29 | ```
30 |
31 | ## Contributing
32 | [](https://david-dm.org/kim-company/videojs-chromecast/)
33 |
34 | Ensure that you have installed [Node.js](http://www.nodejs.org) and [npm](http://www.npmjs.org/)
35 |
36 | Test that Grunt's CLI is installed by running `grunt --version`. If the command isn't found, run `npm install -g grunt-cli`. For more information about installing Grunt, see the [getting started guide](http://gruntjs.com/getting-started).
37 |
38 | 1. Fork and clone the repository.
39 | 2. Run `npm install` to install the dependencies.
40 | 3. Run `grunt` to grunt this project.
41 |
42 | #### You can test your changes with the included demo
43 | 1. Run `node demo-server.js` to start the server.
44 | 2. See `http://localhost:3000/demo/` in your browser.
45 |
--------------------------------------------------------------------------------
/src/videojs.chromecast-tech.coffee:
--------------------------------------------------------------------------------
1 | class vjs.ChromecastTech extends vjs.MediaTechController
2 | @isSupported = ->
3 | @player_.chromecastComponent.apiInitialized
4 |
5 | @canPlaySource = (source) ->
6 | source.type is "video/mp4" or
7 | source.type is "video/webm" or
8 | source.type is "application/x-mpegURL" or
9 | source.type is "application/vnd.apple.mpegURL"
10 |
11 | constructor: (player, options, ready) ->
12 | @featuresVolumeControl = true
13 | @movingMediaElementInDOM = false
14 | @featuresFullscreenResize = false
15 | @featuresProgressEvents = true
16 |
17 | @receiver = options.source.receiver
18 |
19 | super player, options, ready
20 |
21 | @triggerReady()
22 |
23 | createEl: ->
24 | element = document.createElement "div"
25 | element.id = "#{@player_.id_}_chromecast_api"
26 | element.className = "vjs-tech vjs-tech-chromecast"
27 | element.innerHTML = """
28 |
29 |
35 | """
36 |
37 | element.player = @player_
38 | vjs.insertFirst element, @player_.el()
39 |
40 | element
41 |
42 | ###
43 | MEDIA PLAYER EVENTS
44 | ###
45 |
46 | play: ->
47 | @player_.chromecastComponent.play()
48 | @player_.onPlay()
49 |
50 | pause: ->
51 | @player_.chromecastComponent.pause()
52 | @player_.onPause()
53 |
54 | paused: ->
55 | @player_.chromecastComponent.paused
56 |
57 | currentTime: ->
58 | @player_.chromecastComponent.currentMediaTime
59 |
60 | setCurrentTime: (seconds) ->
61 | @player_.chromecastComponent.seekMedia seconds
62 |
63 | volume: ->
64 | @player_.chromecastComponent.currentVolume
65 |
66 | setVolume: (volume) ->
67 | @player_.chromecastComponent.setMediaVolume volume, false
68 |
69 | muted: ->
70 | @player_.chromecastComponent.muted
71 |
72 | setMuted: (muted) ->
73 | @player_.chromecastComponent.setMediaVolume @player_.chromecastComponent.currentVolume, muted
74 |
75 | supportsFullScreen: ->
76 | false
77 |
--------------------------------------------------------------------------------
/dist/videojs.chromecast.min.css:
--------------------------------------------------------------------------------
1 | /*! videojs-chromecast - v1.1.1 - 2015-04-15
2 | * https://github.com/kim-company/videojs-chromecast
3 | * Copyright (c) 2015 KIM Keep In Mind GmbH, srl; Licensed MIT */
4 |
5 | @font-face{font-family:chromecast;src:url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAS8AAsAAAAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgCCL8tmNtYXAAAAFoAAAATAAAAEwaVcxXZ2FzcAAAAbQAAAAIAAAACAAAABBnbHlmAAABvAAAAPwAAAD8H/uKE2hlYWQAAAK4AAAANgAAADYCPa1TaGhlYQAAAvAAAAAkAAAAJASAAoRobXR4AAADFAAAABQAAAAUA54AAGxvY2EAAAMoAAAADAAAAAwAKACSbWF4cAAAAzQAAAAgAAAAIAAKAD5uYW1lAAADVAAAAUUAAAFFVxmm7nBvc3QAAAScAAAAIAAAACAAAwAAAAMCAAGQAAUAAAFMAWYAAABHAUwBZgAAAPUAGQCEAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA5gAB4P/g/+AB4AAgAAAAAQAAAAAAAAAAAAAAIAAAAAAAAgAAAAMAAAAUAAMAAQAAABQABAA4AAAACgAIAAIAAgABACDmAP/9//8AAAAAACDmAP/9//8AAf/jGgQAAwABAAAAAAAAAAAAAAABAAH//wAPAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAQAAP/gAp4B4AAUAB0ALAA7AAATFR4BFx4BFzUhESEeARceARchESEDFTMuAScuASc1FR4BFx4BFzMuAScuASc1FR4BFx4BFzMuAScuAScnBw4GBw0GAg3+rwIFAgIDAQF3/YknSwMOCgoYDhYnDw8VBDUFHRYWOiEpSBwbJAU0BCwjI1s0AeDZAQMCAgUCs/6RBg0HBg4HAdn+S0sOGAoKDgNeNQQVDw8nFiE6FhYdBVw0BSQbHEgpNFsjIywEAAEAAAAAAAD1+MbOXw889QALAgAAAAAA0EC2SwAAAADQQLZLAAD/4AKeAeAAAAAIAAIAAAAAAAAAAQAAAeD/4AAAAp4AAAAAAp4AAQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAABAAAAAp4AAAAAAAAACgAUAB4AfgABAAAABQA8AAQAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoANABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoANABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")format("woff");font-weight:400;font-style:normal}.vjs-chromecast-button{float:right!important;cursor:pointer;width:3em!important}.vjs-chromecast-button:before{content:"\e600";font-family:chromecast!important}.vjs-chromecast-button.connected{color:#66A8CC}.vjs-tech-chromecast .casting-image{position:absolute;top:0;right:0;left:0;bottom:0;background-color:#000;background-repeat:no-repeat;background-size:contain;background-position:center}.vjs-tech-chromecast .casting-overlay{position:absolute;top:0;right:0;left:0;bottom:0;background-color:#000;opacity:.6;cursor:default}.vjs-tech-chromecast .casting-overlay .casting-information{position:absolute;left:15px;bottom:50px;right:15px;height:50px;color:#FFF}.vjs-tech-chromecast .casting-overlay .casting-information .casting-icon{font-family:chromecast!important;font-size:44px;line-height:50px;margin-right:10px;float:left;width:58px;height:50px}.vjs-tech-chromecast .casting-overlay .casting-information .casting-description{height:50px;font-size:20px;line-height:20px}.vjs-tech-chromecast .casting-overlay .casting-information .casting-description small{font-size:11px}
--------------------------------------------------------------------------------
/src/videojs.chromecast.less:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "chromecast";
3 | src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAS8AAsAAAAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgCCL8tmNtYXAAAAFoAAAATAAAAEwaVcxXZ2FzcAAAAbQAAAAIAAAACAAAABBnbHlmAAABvAAAAPwAAAD8H/uKE2hlYWQAAAK4AAAANgAAADYCPa1TaGhlYQAAAvAAAAAkAAAAJASAAoRobXR4AAADFAAAABQAAAAUA54AAGxvY2EAAAMoAAAADAAAAAwAKACSbWF4cAAAAzQAAAAgAAAAIAAKAD5uYW1lAAADVAAAAUUAAAFFVxmm7nBvc3QAAAScAAAAIAAAACAAAwAAAAMCAAGQAAUAAAFMAWYAAABHAUwBZgAAAPUAGQCEAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA5gAB4P/g/+AB4AAgAAAAAQAAAAAAAAAAAAAAIAAAAAAAAgAAAAMAAAAUAAMAAQAAABQABAA4AAAACgAIAAIAAgABACDmAP/9//8AAAAAACDmAP/9//8AAf/jGgQAAwABAAAAAAAAAAAAAAABAAH//wAPAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAQAAP/gAp4B4AAUAB0ALAA7AAATFR4BFx4BFzUhESEeARceARchESEDFTMuAScuASc1FR4BFx4BFzMuAScuASc1FR4BFx4BFzMuAScuAScnBw4GBw0GAg3+rwIFAgIDAQF3/YknSwMOCgoYDhYnDw8VBDUFHRYWOiEpSBwbJAU0BCwjI1s0AeDZAQMCAgUCs/6RBg0HBg4HAdn+S0sOGAoKDgNeNQQVDw8nFiE6FhYdBVw0BSQbHEgpNFsjIywEAAEAAAAAAAD1+MbOXw889QALAgAAAAAA0EC2SwAAAADQQLZLAAD/4AKeAeAAAAAIAAIAAAAAAAAAAQAAAeD/4AAAAp4AAAAAAp4AAQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAABAAAAAp4AAAAAAAAACgAUAB4AfgABAAAABQA8AAQAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoANABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoANABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") format("woff");
4 | font-weight: normal;
5 | font-style: normal;
6 | }
7 |
8 | // ChromecastComponent
9 | .vjs-chromecast-button {
10 | float: right !important;
11 | cursor: pointer;
12 | width: 3em !important;
13 |
14 | &:before {
15 | content: "\e600";
16 | font-family: "chromecast" !important;
17 | }
18 |
19 | &.connected {
20 | color: #66A8CC;
21 | }
22 | }
23 |
24 | // ChromecastTech
25 | .vjs-tech-chromecast {
26 | .casting-image {
27 | position: absolute;
28 | top: 0; right: 0;
29 | left: 0; bottom: 0;
30 | background-color: #000;
31 | background-repeat: no-repeat;
32 | background-size: contain;
33 | background-position: center;
34 | }
35 |
36 | .casting-overlay {
37 | position: absolute;
38 | top: 0; right: 0;
39 | left: 0; bottom: 0;
40 | background-color: #000;
41 | opacity: 0.6;
42 | cursor: default;
43 |
44 | .casting-information {
45 | position: absolute;
46 | left: 15px; bottom: 50px; right: 15px;
47 | height: 50px;
48 | color: #FFF;
49 |
50 | .casting-icon {
51 | font-family: "chromecast" !important;
52 | font-size: 44px;
53 | line-height: 50px;
54 | margin-right: 10px;
55 | float: left;
56 | width: 58px;
57 | height: 50px;
58 | }
59 |
60 | .casting-description {
61 | height: 50px;
62 | font-size: 20px;
63 | line-height: 20px;
64 |
65 | small { font-size: 11px }
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/dist/videojs.chromecast.css:
--------------------------------------------------------------------------------
1 | /*! videojs-chromecast - v1.1.1 - 2015-04-15
2 | * https://github.com/kim-company/videojs-chromecast
3 | * Copyright (c) 2015 KIM Keep In Mind GmbH, srl; Licensed MIT */
4 |
5 | @font-face {
6 | font-family: "chromecast";
7 | src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAS8AAsAAAAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgCCL8tmNtYXAAAAFoAAAATAAAAEwaVcxXZ2FzcAAAAbQAAAAIAAAACAAAABBnbHlmAAABvAAAAPwAAAD8H/uKE2hlYWQAAAK4AAAANgAAADYCPa1TaGhlYQAAAvAAAAAkAAAAJASAAoRobXR4AAADFAAAABQAAAAUA54AAGxvY2EAAAMoAAAADAAAAAwAKACSbWF4cAAAAzQAAAAgAAAAIAAKAD5uYW1lAAADVAAAAUUAAAFFVxmm7nBvc3QAAAScAAAAIAAAACAAAwAAAAMCAAGQAAUAAAFMAWYAAABHAUwBZgAAAPUAGQCEAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA5gAB4P/g/+AB4AAgAAAAAQAAAAAAAAAAAAAAIAAAAAAAAgAAAAMAAAAUAAMAAQAAABQABAA4AAAACgAIAAIAAgABACDmAP/9//8AAAAAACDmAP/9//8AAf/jGgQAAwABAAAAAAAAAAAAAAABAAH//wAPAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAQAAP/gAp4B4AAUAB0ALAA7AAATFR4BFx4BFzUhESEeARceARchESEDFTMuAScuASc1FR4BFx4BFzMuAScuASc1FR4BFx4BFzMuAScuAScnBw4GBw0GAg3+rwIFAgIDAQF3/YknSwMOCgoYDhYnDw8VBDUFHRYWOiEpSBwbJAU0BCwjI1s0AeDZAQMCAgUCs/6RBg0HBg4HAdn+S0sOGAoKDgNeNQQVDw8nFiE6FhYdBVw0BSQbHEgpNFsjIywEAAEAAAAAAAD1+MbOXw889QALAgAAAAAA0EC2SwAAAADQQLZLAAD/4AKeAeAAAAAIAAIAAAAAAAAAAQAAAeD/4AAAAp4AAAAAAp4AAQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAABAAAAAp4AAAAAAAAACgAUAB4AfgABAAAABQA8AAQAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoANABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoANABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") format("woff");
8 | font-weight: normal;
9 | font-style: normal;
10 | }
11 | .vjs-chromecast-button {
12 | float: right !important;
13 | cursor: pointer;
14 | width: 3em !important;
15 | }
16 | .vjs-chromecast-button:before {
17 | content: "\e600";
18 | font-family: "chromecast" !important;
19 | }
20 | .vjs-chromecast-button.connected {
21 | color: #66A8CC;
22 | }
23 | .vjs-tech-chromecast .casting-image {
24 | position: absolute;
25 | top: 0;
26 | right: 0;
27 | left: 0;
28 | bottom: 0;
29 | background-color: #000;
30 | background-repeat: no-repeat;
31 | background-size: contain;
32 | background-position: center;
33 | }
34 | .vjs-tech-chromecast .casting-overlay {
35 | position: absolute;
36 | top: 0;
37 | right: 0;
38 | left: 0;
39 | bottom: 0;
40 | background-color: #000;
41 | opacity: 0.6;
42 | cursor: default;
43 | }
44 | .vjs-tech-chromecast .casting-overlay .casting-information {
45 | position: absolute;
46 | left: 15px;
47 | bottom: 50px;
48 | right: 15px;
49 | height: 50px;
50 | color: #FFF;
51 | }
52 | .vjs-tech-chromecast .casting-overlay .casting-information .casting-icon {
53 | font-family: "chromecast" !important;
54 | font-size: 44px;
55 | line-height: 50px;
56 | margin-right: 10px;
57 | float: left;
58 | width: 58px;
59 | height: 50px;
60 | }
61 | .vjs-tech-chromecast .casting-overlay .casting-information .casting-description {
62 | height: 50px;
63 | font-size: 20px;
64 | line-height: 20px;
65 | }
66 | .vjs-tech-chromecast .casting-overlay .casting-information .casting-description small {
67 | font-size: 11px;
68 | }
69 |
--------------------------------------------------------------------------------
/src/videojs.chromecast-component.coffee:
--------------------------------------------------------------------------------
1 | class vjs.ChromecastComponent extends vjs.Button
2 | buttonText: "Chromecast"
3 | inactivityTimeout: 2000
4 |
5 | apiInitialized: false
6 | apiSession: null
7 | apiMedia: null
8 |
9 | casting: false
10 | paused: true
11 | muted: false
12 | currentVolume: 1
13 | currentMediaTime: 0
14 |
15 | timer: null
16 | timerStep: 1000
17 |
18 | constructor: (player, @settings) ->
19 | super player, @settings
20 |
21 | @disable() unless player.controls()
22 | @hide()
23 | @initializeApi()
24 |
25 | initializeApi: ->
26 | # Check if the browser is Google Chrome
27 | return unless vjs.IS_CHROME
28 |
29 | # If the Cast APIs arent available yet, retry in 1000ms
30 | if not chrome.cast or not chrome.cast.isAvailable
31 | vjs.log "Cast APIs not available. Retrying..."
32 | setTimeout @initializeApi.bind(@), 1000
33 | return
34 |
35 | vjs.log "Cast APIs are available"
36 |
37 | appId = @settings.appId or chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID
38 | sessionRequest = new chrome.cast.SessionRequest(appId)
39 |
40 | apiConfig = new chrome.cast.ApiConfig(sessionRequest, @sessionJoinedListener, @receiverListener.bind(this))
41 |
42 | chrome.cast.initialize apiConfig, @onInitSuccess.bind(this), @castError
43 |
44 | sessionJoinedListener: (session) ->
45 | console.log "Session joined"
46 |
47 | receiverListener: (availability) ->
48 | @show() if availability is "available"
49 |
50 | onInitSuccess: ->
51 | @apiInitialized = true
52 |
53 | castError: (castError) ->
54 | vjs.log "Cast Error: #{JSON.stringify(castError)}"
55 |
56 | doLaunch: ->
57 | vjs.log "Cast video: #{@player_.currentSrc()}"
58 | if @apiInitialized
59 | chrome.cast.requestSession @onSessionSuccess.bind(this), @castError
60 | else
61 | vjs.log "Session not initialized"
62 |
63 | onSessionSuccess: (session) ->
64 | vjs.log "Session initialized: #{session.sessionId}"
65 |
66 | @apiSession = session
67 | @addClass "connected"
68 |
69 | mediaInfo = new chrome.cast.media.MediaInfo @player_.currentSrc(), @player_.currentType()
70 |
71 | if @settings.metadata
72 | mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata()
73 |
74 | for key, value of @settings.metadata
75 | mediaInfo.metadata[key] = value
76 |
77 | if @player_.options_.poster
78 | image = new chrome.cast.Image(@player_.options_.poster)
79 | mediaInfo.metadata.images = [image]
80 |
81 | loadRequest = new chrome.cast.media.LoadRequest(mediaInfo)
82 | loadRequest.autoplay = true
83 | loadRequest.currentTime = @player_.currentTime()
84 |
85 | @apiSession.loadMedia loadRequest, @onMediaDiscovered.bind(this), @castError
86 | @apiSession.addUpdateListener @onSessionUpdate.bind(this)
87 |
88 | onMediaDiscovered: (media) ->
89 | @apiMedia = media
90 | @apiMedia.addUpdateListener @onMediaStatusUpdate.bind(this)
91 |
92 | @startProgressTimer @incrementMediaTime.bind(this)
93 |
94 | @player_.loadTech "ChromecastTech",
95 | receiver: @apiSession.receiver.friendlyName
96 |
97 | @casting = true
98 | @paused = @player_.paused()
99 |
100 | # Always show the controlbar
101 | @inactivityTimeout = @player_.options_.inactivityTimeout
102 | @player_.options_.inactivityTimeout = 0
103 | @player_.userActive true
104 |
105 | onSessionUpdate: (isAlive) ->
106 | return unless @apiMedia
107 |
108 | @onStopAppSuccess() if not isAlive
109 |
110 | onMediaStatusUpdate: (isAlive) ->
111 | return unless @apiMedia
112 |
113 | @currentMediaTime = @apiMedia.currentTime
114 |
115 | switch @apiMedia.playerState
116 | when chrome.cast.media.PlayerState.IDLE
117 | @currentMediaTime = 0
118 | @trigger "timeupdate"
119 | @onStopAppSuccess()
120 | when chrome.cast.media.PlayerState.PAUSED
121 | return if @paused
122 | @player_.pause()
123 | @paused = true
124 | when chrome.cast.media.PlayerState.PLAYING
125 | return unless @paused
126 | @player_.play()
127 | @paused = false
128 |
129 | startProgressTimer: (callback) ->
130 | if @timer
131 | clearInterval @timer
132 | @timer = null
133 |
134 | @timer = setInterval(callback.bind(this), @timerStep)
135 |
136 | play: ->
137 | return unless @apiMedia
138 | if @paused
139 | @apiMedia.play null, @mediaCommandSuccessCallback.bind(this, "Playing: " + @apiMedia.sessionId), @onError
140 | @paused = false
141 |
142 | pause: ->
143 | return unless @apiMedia
144 |
145 | unless @paused
146 | @apiMedia.pause null, @mediaCommandSuccessCallback.bind(this, "Paused: " + @apiMedia.sessionId), @onError
147 | @paused = true
148 |
149 | seekMedia: (position) ->
150 | request = new chrome.cast.media.SeekRequest()
151 | request.currentTime = position
152 | # Make sure playback resumes. videoWasPlaying does not survive minification.
153 | request.resumeState = chrome.cast.media.ResumeState.PLAYBACK_START if @player_.controlBar.progressControl.seekBar.videoWasPlaying
154 |
155 | @apiMedia.seek request, @onSeekSuccess.bind(this, position), @onError
156 |
157 | onSeekSuccess: (position) ->
158 | @currentMediaTime = position
159 |
160 | setMediaVolume: (level, mute) ->
161 | return unless @apiMedia
162 |
163 | volume = new chrome.cast.Volume()
164 | volume.level = level
165 | volume.muted = mute
166 |
167 | @currentVolume = volume.level
168 | @muted = mute
169 |
170 | request = new chrome.cast.media.VolumeRequest()
171 | request.volume = volume
172 |
173 | @apiMedia.setVolume request, @mediaCommandSuccessCallback.bind(this, "Volume changed"), @onError
174 | @player_.trigger "volumechange"
175 |
176 | incrementMediaTime: ->
177 | return unless @apiMedia.playerState is chrome.cast.media.PlayerState.PLAYING
178 |
179 | if @currentMediaTime < @apiMedia.media.duration
180 | @currentMediaTime += 1
181 | @trigger "timeupdate"
182 | else
183 | @currentMediaTime = 0
184 | clearInterval @timer
185 |
186 | mediaCommandSuccessCallback: (information, event) ->
187 | vjs.log information
188 |
189 | onError: ->
190 | vjs.log "error"
191 |
192 | # Stops the casting on the Chromecast
193 | stopCasting: ->
194 | @apiSession.stop @onStopAppSuccess.bind(this), @onError
195 |
196 | # Callback when the app has been successfully stopped
197 | onStopAppSuccess: ->
198 | clearInterval @timer
199 | @casting = false
200 | @removeClass "connected"
201 |
202 | @player_.src @player_.options_["sources"]
203 |
204 | # Resume playback if not paused when casting is stopped
205 | unless @paused
206 | @player_.one 'seeked', ->
207 | @player_.play()
208 | @player_.currentTime(@currentMediaTime)
209 |
210 | # Hide the default HTML5 player controls.
211 | @player_.tech.setControls(false)
212 |
213 | # Enable user activity timeout
214 | @player_.options_.inactivityTimeout = @inactivityTimeout
215 |
216 | @apiMedia = null
217 | @apiSession = null
218 |
219 | buildCSSClass: ->
220 | super + "vjs-chromecast-button"
221 |
222 | onClick: ->
223 | super
224 | if @casting then @stopCasting() else @doLaunch()
225 |
--------------------------------------------------------------------------------
/dist/videojs.chromecast.min.js:
--------------------------------------------------------------------------------
1 | /*! videojs-chromecast - v1.1.1 - 2015-04-15
2 | * https://github.com/kim-company/videojs-chromecast
3 | * Copyright (c) 2015 KIM Keep In Mind GmbH, srl; Licensed MIT */
4 |
5 | (function(){var a=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a},b={}.hasOwnProperty;vjs.addLanguage("de",{"CASTING TO":"WIEDERGABE AUF"}),vjs.addLanguage("it",{"CASTING TO":"PLAYBACK SU"}),vjs.plugin("chromecast",function(a){return this.chromecastComponent=new vjs.ChromecastComponent(this,a),this.controlBar.addChild(this.chromecastComponent)}),vjs.ChromecastComponent=function(b){function c(a,b){this.settings=b,c.__super__.constructor.call(this,a,this.settings),a.controls()||this.disable(),this.hide(),this.initializeApi()}return a(c,b),c.prototype.buttonText="Chromecast",c.prototype.inactivityTimeout=2e3,c.prototype.apiInitialized=!1,c.prototype.apiSession=null,c.prototype.apiMedia=null,c.prototype.casting=!1,c.prototype.paused=!0,c.prototype.muted=!1,c.prototype.currentVolume=1,c.prototype.currentMediaTime=0,c.prototype.timer=null,c.prototype.timerStep=1e3,c.prototype.initializeApi=function(){var a,b,c;if(vjs.IS_CHROME)return chrome.cast&&chrome.cast.isAvailable?(b=this.settings.appId||chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,c=new chrome.cast.SessionRequest(b),a=new chrome.cast.ApiConfig(c,this.sessionJoinedListener,this.receiverListener.bind(this)),chrome.cast.initialize(a,this.onInitSuccess.bind(this),this.castError)):void setTimeout(this.initializeApi.bind(this),1e3)},c.prototype.sessionJoinedListener=function(a){return void 0},c.prototype.receiverListener=function(a){return"available"===a?this.show():void 0},c.prototype.onInitSuccess=function(){return this.apiInitialized=!0},c.prototype.castError=function(a){return vjs.log("Cast Error: "+JSON.stringify(a))},c.prototype.doLaunch=function(){return this.apiInitialized?chrome.cast.requestSession(this.onSessionSuccess.bind(this),this.castError):vjs.log("Session not initialized")},c.prototype.onSessionSuccess=function(a){var b,c,d,e,f,g;if(this.apiSession=a,this.addClass("connected"),e=new chrome.cast.media.MediaInfo(this.player_.currentSrc(),this.player_.currentType()),this.settings.metadata){e.metadata=new chrome.cast.media.GenericMediaMetadata,f=this.settings.metadata;for(c in f)g=f[c],e.metadata[c]=g;this.player_.options_.poster&&(b=new chrome.cast.Image(this.player_.options_.poster),e.metadata.images=[b])}return d=new chrome.cast.media.LoadRequest(e),d.autoplay=!0,d.currentTime=this.player_.currentTime(),this.apiSession.loadMedia(d,this.onMediaDiscovered.bind(this),this.castError),this.apiSession.addUpdateListener(this.onSessionUpdate.bind(this))},c.prototype.onMediaDiscovered=function(a){return this.apiMedia=a,this.apiMedia.addUpdateListener(this.onMediaStatusUpdate.bind(this)),this.startProgressTimer(this.incrementMediaTime.bind(this)),this.player_.loadTech("ChromecastTech",{receiver:this.apiSession.receiver.friendlyName}),this.casting=!0,this.paused=this.player_.paused(),this.inactivityTimeout=this.player_.options_.inactivityTimeout,this.player_.options_.inactivityTimeout=0,this.player_.userActive(!0)},c.prototype.onSessionUpdate=function(a){return this.apiMedia?a?void 0:this.onStopAppSuccess():void 0},c.prototype.onMediaStatusUpdate=function(a){if(this.apiMedia)switch(this.currentMediaTime=this.apiMedia.currentTime,this.apiMedia.playerState){case chrome.cast.media.PlayerState.IDLE:return this.currentMediaTime=0,this.trigger("timeupdate"),this.onStopAppSuccess();case chrome.cast.media.PlayerState.PAUSED:if(this.paused)return;return this.player_.pause(),this.paused=!0;case chrome.cast.media.PlayerState.PLAYING:if(!this.paused)return;return this.player_.play(),this.paused=!1}},c.prototype.startProgressTimer=function(a){return this.timer&&(clearInterval(this.timer),this.timer=null),this.timer=setInterval(a.bind(this),this.timerStep)},c.prototype.play=function(){return this.apiMedia&&this.paused?(this.apiMedia.play(null,this.mediaCommandSuccessCallback.bind(this,"Playing: "+this.apiMedia.sessionId),this.onError),this.paused=!1):void 0},c.prototype.pause=function(){return this.apiMedia?this.paused?void 0:(this.apiMedia.pause(null,this.mediaCommandSuccessCallback.bind(this,"Paused: "+this.apiMedia.sessionId),this.onError),this.paused=!0):void 0},c.prototype.seekMedia=function(a){var b;return b=new chrome.cast.media.SeekRequest,b.currentTime=a,this.player_.controlBar.progressControl.seekBar.videoWasPlaying&&(b.resumeState=chrome.cast.media.ResumeState.PLAYBACK_START),this.apiMedia.seek(b,this.onSeekSuccess.bind(this,a),this.onError)},c.prototype.onSeekSuccess=function(a){return this.currentMediaTime=a},c.prototype.setMediaVolume=function(a,b){var c,d;if(this.apiMedia)return d=new chrome.cast.Volume,d.level=a,d.muted=b,this.currentVolume=d.level,this.muted=b,c=new chrome.cast.media.VolumeRequest,c.volume=d,this.apiMedia.setVolume(c,this.mediaCommandSuccessCallback.bind(this,"Volume changed"),this.onError),this.player_.trigger("volumechange")},c.prototype.incrementMediaTime=function(){return this.apiMedia.playerState===chrome.cast.media.PlayerState.PLAYING?this.currentMediaTime\n",a.player=this.player_,vjs.insertFirst(a,this.player_.el()),a},c.prototype.play=function(){return this.player_.chromecastComponent.play(),this.player_.onPlay()},c.prototype.pause=function(){return this.player_.chromecastComponent.pause(),this.player_.onPause()},c.prototype.paused=function(){return this.player_.chromecastComponent.paused},c.prototype.currentTime=function(){return this.player_.chromecastComponent.currentMediaTime},c.prototype.setCurrentTime=function(a){return this.player_.chromecastComponent.seekMedia(a)},c.prototype.volume=function(){return this.player_.chromecastComponent.currentVolume},c.prototype.setVolume=function(a){return this.player_.chromecastComponent.setMediaVolume(a,!1)},c.prototype.muted=function(){return this.player_.chromecastComponent.muted},c.prototype.setMuted=function(a){return this.player_.chromecastComponent.setMediaVolume(this.player_.chromecastComponent.currentVolume,a)},c.prototype.supportsFullScreen=function(){return!1},c}(vjs.MediaTechController)}).call(this);
--------------------------------------------------------------------------------
/demo/font/vjs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/dist/videojs.chromecast.js:
--------------------------------------------------------------------------------
1 | /*! videojs-chromecast - v1.1.1 - 2015-04-15
2 | * https://github.com/kim-company/videojs-chromecast
3 | * Copyright (c) 2015 KIM Keep In Mind GmbH, srl; Licensed MIT */
4 |
5 | (function() {
6 | var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
7 | hasProp = {}.hasOwnProperty;
8 |
9 | vjs.addLanguage("de", {
10 | "CASTING TO": "WIEDERGABE AUF"
11 | });
12 |
13 | vjs.addLanguage("it", {
14 | "CASTING TO": "PLAYBACK SU"
15 | });
16 |
17 | vjs.plugin("chromecast", function(options) {
18 | this.chromecastComponent = new vjs.ChromecastComponent(this, options);
19 | return this.controlBar.addChild(this.chromecastComponent);
20 | });
21 |
22 | vjs.ChromecastComponent = (function(superClass) {
23 | extend(ChromecastComponent, superClass);
24 |
25 | ChromecastComponent.prototype.buttonText = "Chromecast";
26 |
27 | ChromecastComponent.prototype.inactivityTimeout = 2000;
28 |
29 | ChromecastComponent.prototype.apiInitialized = false;
30 |
31 | ChromecastComponent.prototype.apiSession = null;
32 |
33 | ChromecastComponent.prototype.apiMedia = null;
34 |
35 | ChromecastComponent.prototype.casting = false;
36 |
37 | ChromecastComponent.prototype.paused = true;
38 |
39 | ChromecastComponent.prototype.muted = false;
40 |
41 | ChromecastComponent.prototype.currentVolume = 1;
42 |
43 | ChromecastComponent.prototype.currentMediaTime = 0;
44 |
45 | ChromecastComponent.prototype.timer = null;
46 |
47 | ChromecastComponent.prototype.timerStep = 1000;
48 |
49 | function ChromecastComponent(player, settings) {
50 | this.settings = settings;
51 | ChromecastComponent.__super__.constructor.call(this, player, this.settings);
52 | if (!player.controls()) {
53 | this.disable();
54 | }
55 | this.hide();
56 | this.initializeApi();
57 | }
58 |
59 | ChromecastComponent.prototype.initializeApi = function() {
60 | var apiConfig, appId, sessionRequest;
61 | if (!vjs.IS_CHROME) {
62 | return;
63 | }
64 | if (!chrome.cast || !chrome.cast.isAvailable) {
65 | vjs.log("Cast APIs not available. Retrying...");
66 | setTimeout(this.initializeApi.bind(this), 1000);
67 | return;
68 | }
69 | vjs.log("Cast APIs are available");
70 | appId = this.settings.appId || chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID;
71 | sessionRequest = new chrome.cast.SessionRequest(appId);
72 | apiConfig = new chrome.cast.ApiConfig(sessionRequest, this.sessionJoinedListener, this.receiverListener.bind(this));
73 | return chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.castError);
74 | };
75 |
76 | ChromecastComponent.prototype.sessionJoinedListener = function(session) {
77 | return console.log("Session joined");
78 | };
79 |
80 | ChromecastComponent.prototype.receiverListener = function(availability) {
81 | if (availability === "available") {
82 | return this.show();
83 | }
84 | };
85 |
86 | ChromecastComponent.prototype.onInitSuccess = function() {
87 | return this.apiInitialized = true;
88 | };
89 |
90 | ChromecastComponent.prototype.castError = function(castError) {
91 | return vjs.log("Cast Error: " + (JSON.stringify(castError)));
92 | };
93 |
94 | ChromecastComponent.prototype.doLaunch = function() {
95 | vjs.log("Cast video: " + (this.player_.currentSrc()));
96 | if (this.apiInitialized) {
97 | return chrome.cast.requestSession(this.onSessionSuccess.bind(this), this.castError);
98 | } else {
99 | return vjs.log("Session not initialized");
100 | }
101 | };
102 |
103 | ChromecastComponent.prototype.onSessionSuccess = function(session) {
104 | var image, key, loadRequest, mediaInfo, ref, value;
105 | vjs.log("Session initialized: " + session.sessionId);
106 | this.apiSession = session;
107 | this.addClass("connected");
108 | mediaInfo = new chrome.cast.media.MediaInfo(this.player_.currentSrc(), this.player_.currentType());
109 | if (this.settings.metadata) {
110 | mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
111 | ref = this.settings.metadata;
112 | for (key in ref) {
113 | value = ref[key];
114 | mediaInfo.metadata[key] = value;
115 | }
116 | if (this.player_.options_.poster) {
117 | image = new chrome.cast.Image(this.player_.options_.poster);
118 | mediaInfo.metadata.images = [image];
119 | }
120 | }
121 | loadRequest = new chrome.cast.media.LoadRequest(mediaInfo);
122 | loadRequest.autoplay = true;
123 | loadRequest.currentTime = this.player_.currentTime();
124 | this.apiSession.loadMedia(loadRequest, this.onMediaDiscovered.bind(this), this.castError);
125 | return this.apiSession.addUpdateListener(this.onSessionUpdate.bind(this));
126 | };
127 |
128 | ChromecastComponent.prototype.onMediaDiscovered = function(media) {
129 | this.apiMedia = media;
130 | this.apiMedia.addUpdateListener(this.onMediaStatusUpdate.bind(this));
131 | this.startProgressTimer(this.incrementMediaTime.bind(this));
132 | this.player_.loadTech("ChromecastTech", {
133 | receiver: this.apiSession.receiver.friendlyName
134 | });
135 | this.casting = true;
136 | this.paused = this.player_.paused();
137 | this.inactivityTimeout = this.player_.options_.inactivityTimeout;
138 | this.player_.options_.inactivityTimeout = 0;
139 | return this.player_.userActive(true);
140 | };
141 |
142 | ChromecastComponent.prototype.onSessionUpdate = function(isAlive) {
143 | if (!this.apiMedia) {
144 | return;
145 | }
146 | if (!isAlive) {
147 | return this.onStopAppSuccess();
148 | }
149 | };
150 |
151 | ChromecastComponent.prototype.onMediaStatusUpdate = function(isAlive) {
152 | if (!this.apiMedia) {
153 | return;
154 | }
155 | this.currentMediaTime = this.apiMedia.currentTime;
156 | switch (this.apiMedia.playerState) {
157 | case chrome.cast.media.PlayerState.IDLE:
158 | this.currentMediaTime = 0;
159 | this.trigger("timeupdate");
160 | return this.onStopAppSuccess();
161 | case chrome.cast.media.PlayerState.PAUSED:
162 | if (this.paused) {
163 | return;
164 | }
165 | this.player_.pause();
166 | return this.paused = true;
167 | case chrome.cast.media.PlayerState.PLAYING:
168 | if (!this.paused) {
169 | return;
170 | }
171 | this.player_.play();
172 | return this.paused = false;
173 | }
174 | };
175 |
176 | ChromecastComponent.prototype.startProgressTimer = function(callback) {
177 | if (this.timer) {
178 | clearInterval(this.timer);
179 | this.timer = null;
180 | }
181 | return this.timer = setInterval(callback.bind(this), this.timerStep);
182 | };
183 |
184 | ChromecastComponent.prototype.play = function() {
185 | if (!this.apiMedia) {
186 | return;
187 | }
188 | if (this.paused) {
189 | this.apiMedia.play(null, this.mediaCommandSuccessCallback.bind(this, "Playing: " + this.apiMedia.sessionId), this.onError);
190 | return this.paused = false;
191 | }
192 | };
193 |
194 | ChromecastComponent.prototype.pause = function() {
195 | if (!this.apiMedia) {
196 | return;
197 | }
198 | if (!this.paused) {
199 | this.apiMedia.pause(null, this.mediaCommandSuccessCallback.bind(this, "Paused: " + this.apiMedia.sessionId), this.onError);
200 | return this.paused = true;
201 | }
202 | };
203 |
204 | ChromecastComponent.prototype.seekMedia = function(position) {
205 | var request;
206 | request = new chrome.cast.media.SeekRequest();
207 | request.currentTime = position;
208 | if (this.player_.controlBar.progressControl.seekBar.videoWasPlaying) {
209 | request.resumeState = chrome.cast.media.ResumeState.PLAYBACK_START;
210 | }
211 | return this.apiMedia.seek(request, this.onSeekSuccess.bind(this, position), this.onError);
212 | };
213 |
214 | ChromecastComponent.prototype.onSeekSuccess = function(position) {
215 | return this.currentMediaTime = position;
216 | };
217 |
218 | ChromecastComponent.prototype.setMediaVolume = function(level, mute) {
219 | var request, volume;
220 | if (!this.apiMedia) {
221 | return;
222 | }
223 | volume = new chrome.cast.Volume();
224 | volume.level = level;
225 | volume.muted = mute;
226 | this.currentVolume = volume.level;
227 | this.muted = mute;
228 | request = new chrome.cast.media.VolumeRequest();
229 | request.volume = volume;
230 | this.apiMedia.setVolume(request, this.mediaCommandSuccessCallback.bind(this, "Volume changed"), this.onError);
231 | return this.player_.trigger("volumechange");
232 | };
233 |
234 | ChromecastComponent.prototype.incrementMediaTime = function() {
235 | if (this.apiMedia.playerState !== chrome.cast.media.PlayerState.PLAYING) {
236 | return;
237 | }
238 | if (this.currentMediaTime < this.apiMedia.media.duration) {
239 | this.currentMediaTime += 1;
240 | return this.trigger("timeupdate");
241 | } else {
242 | this.currentMediaTime = 0;
243 | return clearInterval(this.timer);
244 | }
245 | };
246 |
247 | ChromecastComponent.prototype.mediaCommandSuccessCallback = function(information, event) {
248 | return vjs.log(information);
249 | };
250 |
251 | ChromecastComponent.prototype.onError = function() {
252 | return vjs.log("error");
253 | };
254 |
255 | ChromecastComponent.prototype.stopCasting = function() {
256 | return this.apiSession.stop(this.onStopAppSuccess.bind(this), this.onError);
257 | };
258 |
259 | ChromecastComponent.prototype.onStopAppSuccess = function() {
260 | clearInterval(this.timer);
261 | this.casting = false;
262 | this.removeClass("connected");
263 | this.player_.src(this.player_.options_["sources"]);
264 | if (!this.paused) {
265 | this.player_.one('seeked', function() {
266 | return this.player_.play();
267 | });
268 | }
269 | this.player_.currentTime(this.currentMediaTime);
270 | this.player_.tech.setControls(false);
271 | this.player_.options_.inactivityTimeout = this.inactivityTimeout;
272 | this.apiMedia = null;
273 | return this.apiSession = null;
274 | };
275 |
276 | ChromecastComponent.prototype.buildCSSClass = function() {
277 | return ChromecastComponent.__super__.buildCSSClass.apply(this, arguments) + "vjs-chromecast-button";
278 | };
279 |
280 | ChromecastComponent.prototype.onClick = function() {
281 | ChromecastComponent.__super__.onClick.apply(this, arguments);
282 | if (this.casting) {
283 | return this.stopCasting();
284 | } else {
285 | return this.doLaunch();
286 | }
287 | };
288 |
289 | return ChromecastComponent;
290 |
291 | })(vjs.Button);
292 |
293 | vjs.ChromecastTech = (function(superClass) {
294 | extend(ChromecastTech, superClass);
295 |
296 | ChromecastTech.isSupported = function() {
297 | return this.player_.chromecastComponent.apiInitialized;
298 | };
299 |
300 | ChromecastTech.canPlaySource = function(source) {
301 | return source.type === "video/mp4" || source.type === "video/webm" || source.type === "application/x-mpegURL" || source.type === "application/vnd.apple.mpegURL";
302 | };
303 |
304 | function ChromecastTech(player, options, ready) {
305 | this.featuresVolumeControl = true;
306 | this.movingMediaElementInDOM = false;
307 | this.featuresFullscreenResize = false;
308 | this.featuresProgressEvents = true;
309 | this.receiver = options.source.receiver;
310 | ChromecastTech.__super__.constructor.call(this, player, options, ready);
311 | this.triggerReady();
312 | }
313 |
314 | ChromecastTech.prototype.createEl = function() {
315 | var element;
316 | element = document.createElement("div");
317 | element.id = this.player_.id_ + "_chromecast_api";
318 | element.className = "vjs-tech vjs-tech-chromecast";
319 | element.innerHTML = "\n";
320 | element.player = this.player_;
321 | vjs.insertFirst(element, this.player_.el());
322 | return element;
323 | };
324 |
325 |
326 | /*
327 | MEDIA PLAYER EVENTS
328 | */
329 |
330 | ChromecastTech.prototype.play = function() {
331 | this.player_.chromecastComponent.play();
332 | return this.player_.onPlay();
333 | };
334 |
335 | ChromecastTech.prototype.pause = function() {
336 | this.player_.chromecastComponent.pause();
337 | return this.player_.onPause();
338 | };
339 |
340 | ChromecastTech.prototype.paused = function() {
341 | return this.player_.chromecastComponent.paused;
342 | };
343 |
344 | ChromecastTech.prototype.currentTime = function() {
345 | return this.player_.chromecastComponent.currentMediaTime;
346 | };
347 |
348 | ChromecastTech.prototype.setCurrentTime = function(seconds) {
349 | return this.player_.chromecastComponent.seekMedia(seconds);
350 | };
351 |
352 | ChromecastTech.prototype.volume = function() {
353 | return this.player_.chromecastComponent.currentVolume;
354 | };
355 |
356 | ChromecastTech.prototype.setVolume = function(volume) {
357 | return this.player_.chromecastComponent.setMediaVolume(volume, false);
358 | };
359 |
360 | ChromecastTech.prototype.muted = function() {
361 | return this.player_.chromecastComponent.muted;
362 | };
363 |
364 | ChromecastTech.prototype.setMuted = function(muted) {
365 | return this.player_.chromecastComponent.setMediaVolume(this.player_.chromecastComponent.currentVolume, muted);
366 | };
367 |
368 | ChromecastTech.prototype.supportsFullScreen = function() {
369 | return false;
370 | };
371 |
372 | return ChromecastTech;
373 |
374 | })(vjs.MediaTechController);
375 |
376 | }).call(this);
377 |
--------------------------------------------------------------------------------
/demo/video-js.css:
--------------------------------------------------------------------------------
1 | /*!
2 | Video.js Default Styles (http://videojs.com)
3 | Version 4.12.5
4 | Create your own skin at http://designer.videojs.com
5 | */
6 | /* SKIN
7 | ================================================================================
8 | The main class name for all skin-specific styles. To make your own skin,
9 | replace all occurrences of 'vjs-default-skin' with a new name. Then add your new
10 | skin name to your video tag instead of the default skin.
11 | e.g.