mirror of
https://github.com/videojs/video.js.git
synced 2025-11-23 22:36:29 +02:00
Close GH-605: added RTMP support. fixes #559.
This commit is contained in:
committed by
Steve Heffernan
parent
5a6fa37623
commit
7ab3d190f2
18
docs/tech.md
18
docs/tech.md
@@ -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:
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
@@ -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 = '../';
|
||||
|
||||
48
test/unit/flash.js
Normal file
48
test/unit/flash.js
Normal file
@@ -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'));
|
||||
});
|
||||
@@ -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; };
|
||||
|
||||
Reference in New Issue
Block a user