Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added RTMP support. fixes #559 #605

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/tech.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@ When adding additional Tech to a video player, make sure to add the supported te
techOrder: ["html5", "flash", "other supported tech"]
});

Flash Technology
==================
The Flash playback tech is a part of the default `techOrder`. You may notice undesirable playback behavior in browsers that are subject to using this playback tech, in particular when scrubbing and seeking within a video. This behavior is a result of Flash's progressive video playback.

Enabling Streaming Playback
--------------------------------
In order to force the Flash tech to choose streaming playback, you need to provide a valid streaming source **before other valid Flash video sources**. This is necessary because of the source selection algorithm, where playback tech chooses the first possible source object with a valid type. Valid streaming `type` values include `rtmp/mp4` and `rtmp/flv`. The streaming `src` value requires valid connection and stream strings, separated by an `&`. An example of supplying a streaming source through your HTML markup might look like:

<source src="rtmp://your.streaming.provider.net/cfx/st/&mp4:path/to/video.mp4" type="rtmp/mp4">
<source src="http://your.static.provider.net/path/to/video.mp4" type="video/mp4">
<source src="http://your.static.provider.net/path/to/video.webm" type="video/webm">

You may optionally use the last `/` as the separator between connection and stream strings, for example:

<source src="rtmp://your.streaming.provider.net/cfx/st/mp4:video.mp4" type="rtmp/mp4">

All four RTMP protocols are valid in the `src` (RTMP, RTMPT, RTMPE, and RTMPS).

Youtube Technology
==================
To add a youtube source to your video tag, use the following source:
Expand Down
20 changes: 19 additions & 1 deletion src/js/control-bar/progress-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,25 @@ vjs.SeekBar.prototype.updateARIAAttributes = function(){
};

vjs.SeekBar.prototype.getPercent = function(){
return this.player_.currentTime() / this.player_.duration();
var currentTime;
// Flash RTMP provider will not report the correct time
// immediately after a seek. This isn't noticeable if you're
// seeking while the video is playing, but it is if you seek
// while the video is paused.
if (this.player_.techName === 'Flash' && this.player_.seeking()) {
var cache = this.player_.getCache();
if (cache.lastSetCurrentTime) {
currentTime = cache.lastSetCurrentTime;
}
else {
currentTime = this.player_.currentTime();
}
}
else {
currentTime = this.player_.currentTime();
}

return currentTime / this.player_.duration();
};

vjs.SeekBar.prototype.onMouseDown = function(event){
Expand Down
94 changes: 87 additions & 7 deletions src/js/media/flash.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,14 @@ vjs.Flash = vjs.MediaTechController.extend({

// If source was supplied pass as a flash var.
if (source) {
flashVars['src'] = encodeURIComponent(vjs.getAbsoluteURL(source.src));
if (source.type && vjs.Flash.isStreamingType(source.type)) {
var parts = vjs.Flash.streamToParts(source.src);
flashVars['rtmpConnection'] = encodeURIComponent(parts.connection);
flashVars['rtmpStream'] = encodeURIComponent(parts.stream);
}
else {
flashVars['src'] = encodeURIComponent(vjs.getAbsoluteURL(source.src));
}
}

// Add placeholder to player div
Expand Down Expand Up @@ -224,10 +231,16 @@ vjs.Flash.prototype.pause = function(){
};

vjs.Flash.prototype.src = function(src){
// Make sure source URL is abosolute.
src = vjs.getAbsoluteURL(src);

this.el_.vjs_src(src);
if (vjs.Flash.isStreamingSrc(src)) {
src = vjs.Flash.streamToParts(src);
this.setRtmpConnection(src.connection);
this.setRtmpStream(src.stream);
}
else {
// Make sure source URL is abosolute.
src = vjs.getAbsoluteURL(src);
this.el_.vjs_src(src);
}

// Currently the SWF doesn't autoplay if you load a source later.
// e.g. Load player w/ no source, wait 2s, set src.
Expand All @@ -237,6 +250,20 @@ vjs.Flash.prototype.src = function(src){
}
};

vjs.Flash.prototype.currentSrc = function(){
var src = this.el_.vjs_getProperty('currentSrc');
// no src, check and see if RTMP
if (src == null) {
var connection = this.rtmpConnection(),
stream = this.rtmpStream();

if (connection && stream) {
src = vjs.Flash.streamFromParts(connection, stream);
}
}
return src;
};

vjs.Flash.prototype.load = function(){
this.el_.vjs_load();
};
Expand All @@ -260,7 +287,7 @@ vjs.Flash.prototype.enterFullScreen = function(){

// Create setters and getters for attributes
var api = vjs.Flash.prototype,
readWrite = 'preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(','),
readWrite = 'rtmpConnection,rtmpStream,preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(','),
readOnly = 'error,currentSrc,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks'.split(',');
// Overridden: buffered

Expand Down Expand Up @@ -301,7 +328,7 @@ vjs.Flash.isSupported = function(){
};

vjs.Flash.canPlaySource = function(srcObj){
if (srcObj.type in vjs.Flash.formats) { return 'maybe'; }
if (srcObj.type in vjs.Flash.formats || srcObj.type in vjs.Flash.streamingFormats) { return 'maybe'; }
};

vjs.Flash.formats = {
Expand All @@ -311,6 +338,11 @@ vjs.Flash.formats = {
'video/m4v': 'MP4'
};

vjs.Flash.streamingFormats = {
'rtmp/mp4': 'MP4',
'rtmp/flv': 'FLV'
};

vjs.Flash['onReady'] = function(currSwf){
var el = vjs.el(currSwf);

Expand Down Expand Up @@ -447,3 +479,51 @@ vjs.Flash.getEmbedCode = function(swf, flashVars, params, attributes){

return objTag + attrsString + '>' + paramsString + '</object>';
};

vjs.Flash.streamFromParts = function(connection, stream) {
return connection + '&' + stream;
};

vjs.Flash.streamToParts = function(src) {
var parts = {
connection: '',
stream: ''
};

if (! src) {
return parts;
}

// Look for the normal URL separator we expect, '&'.
// If found, we split the URL into two pieces around the
// first '&'.
var connEnd = src.indexOf('&');
var streamBegin;
if (connEnd !== -1) {
streamBegin = connEnd + 1;
}
else {
// If there's not a '&', we use the last '/' as the delimiter.
connEnd = streamBegin = src.lastIndexOf('/') + 1;
if (connEnd === 0) {
// really, there's not a '/'?
connEnd = streamBegin = src.length;
}
}
parts.connection = src.substring(0, connEnd);
parts.stream = src.substring(streamBegin, src.length);

return parts;
};

vjs.Flash.isStreamingType = function(srcType) {
return srcType in vjs.Flash.streamingFormats;
};

// RTMP has four variations, any string starting
// with one of these protocols should be valid
vjs.Flash.RTMP_RE = /^rtmp[set]?:\/\//i;

vjs.Flash.isStreamingSrc = function(src) {
return vjs.Flash.RTMP_RE.test(src);
};
1 change: 1 addition & 0 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ vjs.Player.prototype.usingNativeControls = function(bool){

vjs.Player.prototype.error = function(){ return this.techGet('error'); };
vjs.Player.prototype.ended = function(){ return this.techGet('ended'); };
vjs.Player.prototype.seeking = function(){ return this.techGet('seeking'); };

// When the player is first initialized, trigger activity so components
// like the control bar show themselves if needed
Expand Down
Binary file modified src/swf/video-js.swf
Binary file not shown.
3 changes: 2 additions & 1 deletion test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
'test/unit/core.js',
'test/unit/media.html5.js',
'test/unit/controls.js',
'test/unit/plugins.js'
'test/unit/plugins.js',
'test/unit/flash.js'
];

var projectRoot = '../';
Expand Down
48 changes: 48 additions & 0 deletions test/unit/flash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module('Flash');

var streamToPartsAndBack = function(url) {
var parts = vjs.Flash.streamToParts(url);
return vjs.Flash.streamFromParts(parts.connection, parts.stream);
};

test('test using both streamToParts and streamFromParts', function() {
ok('rtmp://myurl.com/&isthis' === streamToPartsAndBack('rtmp://myurl.com/isthis'));
ok('rtmp://myurl.com/&isthis' === streamToPartsAndBack('rtmp://myurl.com/&isthis'));
ok('rtmp://myurl.com/isthis/&andthis' === streamToPartsAndBack('rtmp://myurl.com/isthis/andthis'));
});

test('test streamToParts', function() {
var parts = vjs.Flash.streamToParts('http://myurl.com/streaming&/is/fun');
ok(parts.connection === 'http://myurl.com/streaming');
ok(parts.stream === '/is/fun');

parts = vjs.Flash.streamToParts('http://myurl.com/&streaming&/is/fun');
ok(parts.connection === 'http://myurl.com/');
ok(parts.stream === 'streaming&/is/fun');

parts = vjs.Flash.streamToParts('http://myurl.com/streaming/is/fun');
ok(parts.connection === 'http://myurl.com/streaming/is/');
ok(parts.stream === 'fun');

parts = vjs.Flash.streamToParts('whatisgoingonhere');
ok(parts.connection === 'whatisgoingonhere');
ok(parts.stream === '');

parts = vjs.Flash.streamToParts();
ok(parts.connection === '');
ok(parts.stream === '');
});

test('test isStreamingSrc', function() {
var isStreamingSrc = vjs.Flash.isStreamingSrc;
ok(isStreamingSrc('rtmp://streaming.is/fun'));
ok(isStreamingSrc('rtmps://streaming.is/fun'));
ok(isStreamingSrc('rtmpe://streaming.is/fun'));
ok(isStreamingSrc('rtmpt://streaming.is/fun'));
// test invalid protocols
ok(!isStreamingSrc('rtmp:streaming.is/fun'));
ok(!isStreamingSrc('rtmpz://streaming.is/fun'));
ok(!isStreamingSrc('http://streaming.is/fun'));
ok(!isStreamingSrc('https://streaming.is/fun'));
ok(!isStreamingSrc('file://streaming.is/fun'));
});
1 change: 1 addition & 0 deletions test/unit/mediafaker.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ vjs.MediaFaker.prototype.createEl = function(){
};

vjs.MediaFaker.prototype.currentTime = function(){ return 0; };
vjs.MediaFaker.prototype.seeking = function(){ return false; };
vjs.MediaFaker.prototype.volume = function(){ return 0; };
vjs.MediaFaker.prototype.muted = function(){ return false; };
vjs.MediaFaker.prototype.pause = function(){ return false; };
Expand Down