mirror of
				https://github.com/videojs/video.js.git
				synced 2025-10-31 00:08:01 +02:00 
			
		
		
		
	Close GH-605: added RTMP support. fixes #559.
This commit is contained in:
		
				
					committed by
					
						 Steve Heffernan
						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