diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e717f5e
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+# http://editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c09026f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+.DS_Store
+*.log
+node_modules/
+dist/
+*~
+nbproject/private/
+build
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..9141dd6
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,11 @@
+{
+ "browser" : true,
+ "curly": true,
+ "eqeqeq": true,
+ "quotmark" : "single",
+ "trailing" : true,
+ "undef" : true,
+ "predef" : [
+ "videojs"
+ ]
+}
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..6c17f82
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,3 @@
+dist/
+test/
+*~
\ No newline at end of file
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..89a9142
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,84 @@
+'use strict';
+
+module.exports = function(grunt) {
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
+ '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
+ '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;' +
+ ' Licensed <%= pkg.license %> */\n',
+ clean: {
+ files: ['dist']
+ },
+ concat: {
+ options: {
+ banner: '<%= banner %>',
+ stripBanners: true
+ },
+ dist: {
+ src: 'lib/**/*.js',
+ dest: 'dist/<%= pkg.name %>.js'
+ }
+ },
+ uglify: {
+ options: {
+ banner: '<%= banner %>'
+ },
+ dist: {
+ src: '<%= concat.dist.dest %>',
+ dest: 'dist/<%= pkg.name %>.min.js'
+ }
+ },
+ qunit: {
+ files: 'test/**/*.html'
+ },
+ jshint: {
+ gruntfile: {
+ options: {
+ node: true
+ },
+ src: 'Gruntfile.js'
+ },
+ src: {
+ options: {
+ jshintrc: '.jshintrc'
+ },
+ src: ['lib/**/*.js']
+ },
+ test: {
+ options: {
+ jshintrc: '.jshintrc'
+ },
+ src: ['test/**/*.js']
+ }
+ },
+ watch: {
+ gruntfile: {
+ files: '<%= jshint.gruntfile.src %>',
+ tasks: ['jshint:gruntfile']
+ },
+ src: {
+ files: '<%= jshint.src.src %>',
+ tasks: ['jshint:src', 'qunit']
+ },
+ test: {
+ files: '<%= jshint.test.src %>',
+ tasks: ['jshint:test', 'qunit']
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-qunit');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-watch');
+
+ grunt.registerTask('default',
+ ['clean',
+ 'jshint',
+ 'qunit',
+ 'concat',
+ 'uglify']);
+};
diff --git a/LICENSE-Apache-2.0 b/LICENSE-Apache-2.0
new file mode 100644
index 0000000..6d480e1
--- /dev/null
+++ b/LICENSE-Apache-2.0
@@ -0,0 +1,13 @@
+Copyright 2015 Afterster
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5ae75cc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,88 @@
+# Video.js VLC
+
+Video.js VLC Tech plug-in
+
+A Video.js tech plugin that add VLC fallback.
+
+## Getting Started
+
+Once you've added the plugin script to your page, you can use it with any supported video:
+ * Include JavaScript files
+```html
+
+
+```
+ * And add this new tech to the player:
+```html
+data-setup='{ "techOrder": ["vlc"] }'
+```
+
+There's also a [working example](example.html) of the plugin you can check out if you're having trouble.
+
+## Documentation
+### Plugin Options
+
+This plugin doesn't have any option.
+
+### Supported content type
+
+Supported content type will depends on your VLC installation, but potentially it should support the following list:
+ * video/mp4(.mp4)
+ * video/mp4(.m4v)
+ * video/ogg(.ogv)
+ * video/webm(.webm)
+ * video/ogg(.ogg)
+ * video/ogg(.anx)
+ * audio/ogg(.ogg)
+ * audio/ogg(.oga)
+ * audio/mp3(.mp3)
+ * audio/mpeg(.mp3)
+ * application/mpegURL(.m3u8)
+ * application/mpegURL(.m3u)
+ * application/vnd.apple.mpegURL(.m3u8)
+ * application/vnd.apple.mpegURL(.m3u)
+ * application/x-mpegURL(.m3u8)
+ * application/x-mpegURL(.m3u)
+ * application/mpegURL(.m3u8)
+ * application/mpegURL(.m3u)
+ * application/vnd.apple.mpegURL(.m3u8)
+ * application/vnd.apple.mpegURL(.m3u)
+ * application/x-mpegURL(.m3u8)
+ * application/x-mpegURL(.m3u)
+ * audio/mpegURL(.m3u8)
+ * audio/mpegURL(.m3u)
+ * text/json(.json)
+ * text/jsonp(.jsonp)
+ * text/xml(.xml)
+ * application/json(.json)
+ * application/jsonp(.jsonp)
+ * application/xml(.xml)
+ * image/jpeg(.jpg)
+ * image/gif(.gif)
+ * image/png(.png)
+ * text/html(.html)
+ * video/flv(.flv)
+ * video/mp4(.mp4)
+ * video/mp4(.f4v)
+ * video/quicktime(.mov)
+ * video/mp4(.m4v)
+ * application/f4m+xml(.f4m)
+ * application/mpegURL(.m3u8)
+ * application/x-mpegURL(.m3u8)
+ * application/vnd.apple.mpegurl(.m3u8)
+ * application/vnd.ms-ss(.manifest)
+ * video/flv(.flv)
+ * video/mp4(.mp4)
+ * video/mp4(.f4v)
+ * video/quicktime(.mov)
+ * video/mp4(.m4v)
+ * application/f4m+xml(.f4m)
+ * audio/mp3(.mp3)
+ * audio/mp4(.m4a)
+ * audio/mpeg(.m4a)
+
+ As this cannot be determined in advance, this plug-in always return success on file type support test. It's why you should always put it at the end of techOrder property.
+
+## Release History
+
+ - 0.1.0: Initial release
diff --git a/example.html b/example.html
new file mode 100644
index 0000000..d5bc4b4
--- /dev/null
+++ b/example.html
@@ -0,0 +1,48 @@
+
+
+
+
+ Video.js VLC
+
+
+
+
+
+
+
+
+
+
+
+ You can see the Video.js VLC plugin in action below.
+ Look at the source of this page to see how to use it with your videos.
+
+
+
+
+
diff --git a/lib/videojs-vlc.js b/lib/videojs-vlc.js
new file mode 100644
index 0000000..abac4d5
--- /dev/null
+++ b/lib/videojs-vlc.js
@@ -0,0 +1,348 @@
+/*! videojs-vlc - v0.1.0 - 2015-1-10
+ * Video.js VLC Tech plug-in.
+ * Licensed under the Apache-2.0 license. */
+
+videojs.Vlc = videojs.MediaTechController.extend({
+ init: function (player, options, ready) {
+ videojs.MediaTechController.call(this, player, options, ready);
+
+ var source = options.source;
+
+ // Generate ID for vlc object
+ var objId = player.id() + '_vlc_api';
+
+ var self = this;
+ this.player_ = player;
+
+ // Merge default parames with ones passed in
+ var params = videojs.util.mergeOptions({
+ 'animationatstart': 'true',
+ 'transparentatstart': 'true',
+ 'windowless': 'true',
+ 'controls': 'false',
+ 'bgcolor': '#000000',
+ 'autostart': player.options.autoplay ? 'true' : 'false',
+ 'allowfullscreen': 'true',
+ 'text': 'Video.js VLC plug-in'
+ }, options.params);
+
+ if (source) {
+ params.source = source.src;
+ this.ready(function() {
+ this.setSrc(source.src);
+ });
+ }
+
+ // Merge default attributes with ones passed in
+ var attributes = videojs.util.mergeOptions({
+ 'id': objId,
+ 'name': objId, // Both ID and Name needed or xap to identify itself
+ 'class': 'vjs-tech'
+ }, options.attributes);
+
+ var parentEl = options.parentEl;
+ var placeHolder = this.el_ = videojs.Component.prototype.createEl('div', {id: player.id() + 'temp_vlc'});
+
+ // Add placeholder to player div
+ if (parentEl.firstChild) {
+ parentEl.insertBefore(placeHolder, parentEl.firstChild);
+ } else {
+ parentEl.appendChild(placeHolder);
+ }
+
+ // Having issues with VLC reloading on certain page actions (hide/resize/fullscreen) in certain browsers
+ // This allows resetting the playhead when we catch the reload
+ if (options.startTime) {
+ this.ready(function(){
+ this.load();
+ this.play();
+ this.currentTime(options.startTime);
+ });
+ }
+
+ player.ready(function() {
+ //player.trigger('loadstart');
+ });
+
+ this.el_ = videojs.Vlc.embed(placeHolder, params, attributes);
+ this.el_.tech = this;
+
+ // Add VLC events
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerOpening', function() {
+ player.trigger('loadstart');
+ });
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerBuffering', function() {
+ player.trigger('loadeddata');
+
+ // Notify video.js to refresh some data from VLC
+ player.trigger('volumechange');
+ });
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerPlaying', function() {
+ player.trigger('play');
+ player.trigger('playing');
+ });
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerPaused', function() {
+ player.trigger('pause');
+ });
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerStopped', function() {
+ player.trigger('pause');
+ player.trigger('ended');
+ });
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerEndReached', function() {
+ player.trigger('pause');
+ player.trigger('ended');
+ });
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerTimeChanged', function() {
+ player.trigger('timeupdate');
+ });
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerPositionChanged', function() {
+ player.trigger('progress');
+ });
+ videojs.Vlc.registerEvent(this.getApi(), 'MediaPlayerLengthChanged', function() {
+ player.trigger('durationchange');
+ });
+
+ // VLC plug-in doesn't have 'ready' event. We assume it is ready after few milliseconds
+ setTimeout(function() {
+ self.triggerReady();
+ }, 100);
+ }
+});
+
+videojs.Vlc.prototype.params = [];
+
+videojs.Vlc.prototype.dispose = function () {
+ if (this.el_) {
+ this.el_.parentNode.removeChild(this.el_);
+ }
+
+ videojs.MediaTechController.prototype.dispose.call(this);
+};
+
+videojs.Vlc.prototype.src = function (src) {
+ if (src === undefined) {
+ return this.currentSrc();
+ }
+
+ // Setting src through `src` not `setSrc` will be deprecated
+ return this.setSrc(src);
+};
+
+videojs.Vlc.prototype.setSrc = function(src){
+ src = videojs.Vlc.getAbsoluteURL(src);
+ this.getApi().playlist.items.clear();
+ this.getApi().playlist.add(src);
+};
+
+videojs.Vlc.prototype.currentSrc = function() {
+ if (this.currentSource_) {
+ return this.currentSource_.src;
+ }
+ else {
+ return this.getApi().playlist.items[this.getApi().playlist.currentItem];
+ }
+};
+
+videojs.Vlc.prototype.load = function() {
+ // Done automatically
+};
+
+videojs.Vlc.prototype.play = function() {
+ this.getApi().playlist.play();
+};
+
+videojs.Vlc.prototype.ended = function() {
+ var state = this.getApi().input.state;
+ return (state === 6 /* ENDED */ || state === 7 /* ERROR */);
+};
+
+videojs.Vlc.prototype.pause = function() {
+ this.getApi().playlist.pause();
+};
+
+videojs.Vlc.prototype.paused = function() {
+ var state = this.getApi().input.state;
+ return (state === 4 /* PAUSED */ || state === 6 /* ENDED */);
+};
+
+videojs.Vlc.prototype.currentTime = function() {
+ return (this.getApi().input.time / 1000);
+};
+
+videojs.Vlc.prototype.setCurrentTime = function(seconds) {
+ this.getApi().input.time = (seconds * 1000);
+};
+
+videojs.Vlc.prototype.duration = function () {
+ return (this.getApi().input.length / 1000);
+};
+
+videojs.Vlc.prototype.buffered = function () {
+ // Not supported
+ return [];
+};
+
+videojs.Vlc.prototype.volume = function () {
+ return this.getApi().audio.volume / 100;
+};
+
+videojs.Vlc.prototype.setVolume = function (percentAsDecimal) {
+ if (percentAsDecimal) {
+ this.getApi().audio.volume = percentAsDecimal * 100;
+ }
+};
+
+videojs.Vlc.prototype.muted = function () {
+ return this.getApi().audio.mute;
+};
+videojs.Vlc.prototype.setMuted = function (muted) {
+ this.getApi().audio.mute.mute = muted;
+};
+
+videojs.Vlc.prototype.supportsFullScreen = function () {
+ return true;
+};
+
+videojs.Vlc.prototype.enterFullScreen = function(){
+ this.getApi().video.fullscreen = true;
+ this.player_.trigger('fullscreenchange');
+};
+
+videojs.Vlc.prototype.exitFullScreen = function(){
+ this.getApi().video.fullscreen = false;
+ this.player_.trigger('fullscreenchange');
+};
+
+videojs.Vlc.prototype.getApi = function() {
+ return this.el_;
+};
+
+videojs.Vlc.registerEvent = function(vlc, event, handler) {
+ if (vlc) {
+ if (vlc.attachEvent) {
+ // Microsoft
+ vlc.attachEvent (event, handler);
+ } else if (vlc.addEventListener) {
+ // Mozilla: DOM level 2
+ vlc.addEventListener(event, handler, false);
+ } else {
+ // DOM level 0
+ vlc['on' + event] = handler;
+ }
+ }
+};
+
+videojs.Vlc.unregisterEvent = function(vlc, event, handler) {
+ if (vlc) {
+ if (vlc.detachEvent) {
+ // Microsoft
+ vlc.detachEvent (event, handler);
+ } else if (vlc.removeEventListener) {
+ // Mozilla: DOM level 2
+ vlc.removeEventListener(event, handler, false);
+ } else {
+ // DOM level 0
+ vlc['on' + event] = null;
+ }
+ }
+};
+
+videojs.Vlc.embed = function (placeHolder, params, attributes) {
+ var code = videojs.Vlc.getEmbedCode(params, attributes);
+ // Get element by embedding code and retrieving created element
+ var obj = videojs.Component.prototype.createEl('div', { innerHTML: code }).childNodes[0];
+ var par = placeHolder.parentNode;
+
+ placeHolder.parentNode.replaceChild(obj, placeHolder);
+
+ return obj;
+};
+
+videojs.Vlc.getEmbedCode = function(params, attributes) {
+
+ var objTag,
+ key,
+ paramsString = '',
+ attrsString = '';
+
+ if(window.ActiveXObject) {
+ objTag = '';
+ }
+
+ // Create Attributes string
+ for (key in attributes) {
+ attrsString += (key + '="' + attributes[key] + '" ');
+ }
+
+ return objTag + attrsString + '>' + paramsString + '';
+ } else {
+ objTag = '';
+ }
+};
+
+videojs.Vlc.getAbsoluteURL = function(url){
+ // Check if absolute URL
+ if (!url.match(/^https?:\/\//)) {
+ // Convert to absolute URL.
+ url = videojs.Component.prototype.createEl('div', {
+ innerHTML: 'x'
+ }).firstChild.href;
+ }
+ return url;
+};
+
+/* Vlc Support Testing */
+
+videojs.Vlc.isSupported = function () {
+ var vlc;
+
+ if(window.ActiveXObject) {
+ try {
+ vlc = new window.ActiveXObject('VideoLAN.VLCPlugin.2');
+ } catch(e) {}
+ }
+ else if(navigator.plugins && navigator.mimeTypes.length > 0) {
+ var name = 'VLC';
+ if (navigator.plugins && (navigator.plugins.length > 0)) {
+ for(var i=0;i always accept to play a source
+ return 'maybe';
+};
\ No newline at end of file
diff --git a/nbproject/project.properties b/nbproject/project.properties
new file mode 100644
index 0000000..db83daa
--- /dev/null
+++ b/nbproject/project.properties
@@ -0,0 +1,3 @@
+file.reference.dev-videojs-vlc=.
+files.encoding=UTF-8
+site.root.folder=${file.reference.dev-videojs-vlc}
diff --git a/nbproject/project.xml b/nbproject/project.xml
new file mode 100644
index 0000000..c84b24c
--- /dev/null
+++ b/nbproject/project.xml
@@ -0,0 +1,9 @@
+
+
+ org.netbeans.modules.web.clientproject
+
+
+ videojs-vlc
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7ff1e78
--- /dev/null
+++ b/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "videojs-vlc",
+ "version": "0.0.0",
+ "author": "Afterster",
+ "description": "Video.js VLC Tech plug-in",
+ "license": "Apache-2.0",
+ "keywords": [
+ "videojs",
+ "vlc",
+ "audio",
+ "video",
+ "player"
+ ],
+ "dependencies": {},
+ "devDependencies": {
+ "grunt-contrib-clean": "^0.4",
+ "grunt-contrib-concat": "^0.3",
+ "grunt-contrib-jshint": "^0.6",
+ "grunt-contrib-qunit": "^0.2",
+ "grunt-contrib-uglify": "^0.2",
+ "grunt-contrib-watch": "^0.4",
+
+ "video.js": "^4.5",
+ "qunitjs": "^1.12"
+ },
+ "peerDependencies": {
+ "video.js": "^4.5"
+ }
+}
diff --git a/test/index.html b/test/index.html
new file mode 100644
index 0000000..ca385de
--- /dev/null
+++ b/test/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+ Video.js VLC
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/videojs-vlc.test.js b/test/videojs-vlc.test.js
new file mode 100644
index 0000000..3240ed0
--- /dev/null
+++ b/test/videojs-vlc.test.js
@@ -0,0 +1,57 @@
+/*! videojs-vlc - v0.0.0 - 2015-1-10
+ * Copyright (c) 2015 Afterster
+ * Licensed under the Apache-2.0 license. */
+(function(window, videojs, qunit) {
+ 'use strict';
+
+ var realIsHtmlSupported,
+ player,
+
+ // local QUnit aliases
+ // http://api.qunitjs.com/
+
+ // module(name, {[setup][ ,teardown]})
+ module = qunit.module,
+ // test(name, callback)
+ test = qunit.test,
+ // ok(value, [message])
+ ok = qunit.ok,
+ // equal(actual, expected, [message])
+ equal = qunit.equal,
+ // strictEqual(actual, expected, [message])
+ strictEqual = qunit.strictEqual,
+ // deepEqual(actual, expected, [message])
+ deepEqual = qunit.deepEqual,
+ // notEqual(actual, expected, [message])
+ notEqual = qunit.notEqual,
+ // throws(block, [expected], [message])
+ throws = qunit.throws;
+
+ module('videojs-vlc', {
+ setup: function() {
+ // force HTML support so the tests run in a reasonable
+ // environment under phantomjs
+ realIsHtmlSupported = videojs.Html5.isSupported;
+ videojs.Html5.isSupported = function() {
+ return true;
+ };
+
+ // create a video element
+ var video = document.createElement('video');
+ document.querySelector('#qunit-fixture').appendChild(video);
+
+ // create a video.js player
+ player = videojs(video);
+
+ // initialize the plugin with the default options
+ //player.vlc();
+ },
+ teardown: function() {
+ videojs.Html5.isSupported = realIsHtmlSupported;
+ }
+ });
+
+ test('registers itself', function() {
+ ok(player, 'registered the plugin');
+ });
+})(window, window.videojs, window.QUnit);