Changeset 1506
- Timestamp:
- 12/19/10 12:37:20 (2 years ago)
- Location:
- providers/adaptive
- Files:
-
- 8 edited
-
adaptive.swf (modified) (previous)
-
src/com/longtailvideo/jwplayer/media/AdaptiveProvider.as (modified) (1 diff)
-
src/com/longtailvideo/jwplayer/muxing/FLV.as (modified) (4 diffs)
-
src/com/longtailvideo/jwplayer/muxing/TS.as (modified) (1 diff)
-
src/com/longtailvideo/jwplayer/parsing/Level.as (modified) (1 diff)
-
src/com/longtailvideo/jwplayer/parsing/Manifest.as (modified) (3 diffs)
-
src/com/longtailvideo/jwplayer/parsing/Playlist.as (modified) (1 diff)
-
test/assets/settings.js (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
providers/adaptive/src/com/longtailvideo/jwplayer/media/AdaptiveProvider.as
r1504 r1506 2 2 3 3 4 import com.longtailvideo.jwplayer.events.MediaEvent; 5 import com.longtailvideo.jwplayer.media.MediaProvider; 6 import com.longtailvideo.jwplayer.model.PlayerConfig; 7 import com.longtailvideo.jwplayer.model.PlaylistItem; 8 import com.longtailvideo.jwplayer.player.PlayerState; 9 import com.longtailvideo.jwplayer.muxing.*; 10 import com.longtailvideo.jwplayer.parsing.*; 11 import com.longtailvideo.jwplayer.utils.*; 12 13 import flash.events.*; 14 import flash.media.*; 15 import flash.net.*; 16 import flash.system.*; 17 import flash.utils.*; 18 19 20 /** JW Player provider for playback of adaptive streams. **/ 21 public class AdaptiveProvider extends MediaProvider { 22 23 24 /** Video object to be instantiated. **/ 25 private var _video:Video; 26 /** NetConnection object for setup of the video _stream. **/ 27 private var _connection:NetConnection; 28 /** NetStream instance that handles the stream IO. **/ 29 private var _stream:NetStream; 30 /** Sound control object. **/ 31 private var _transformer:SoundTransform; 32 /** ID for the position interval. **/ 33 private var _positionInterval:Number; 34 /** Whether the buffer has filled **/ 35 private var _bufferFull:Boolean; 36 /** Save the buffer percentage. **/ 37 private var _bufferPercent:Number; 38 /** URLStream that loads the file. **/ 39 private var _urlStream:URLStream; 40 /** URLRequest for loading the stream. **/ 41 private var _urlRequest:URLRequest; 42 /** Reference to the ts. **/ 43 private var _ts:TS; 44 /** Reference to the M3U8 manifest. **/ 45 private var _manifest:Manifest; 46 47 48 /** Constructor; sets up the connection and display. **/ 49 public function AdaptiveProvider() { 50 super('adaptive'); 51 } 52 53 54 public override function initializeMediaProvider(cfg:PlayerConfig):void { 55 super.initializeMediaProvider(cfg); 56 _connection = new NetConnection(); 57 _connection.connect(null); 58 _stream = new NetStream(_connection); 59 _stream.addEventListener(IOErrorEvent.IO_ERROR, errorHandler); 60 _stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, errorHandler); 61 _stream.bufferTime = config.bufferlength; 62 _stream.client = new Object(); 63 _transformer = new SoundTransform(); 64 _urlStream = new URLStream(); 65 _urlStream.addEventListener(Event.COMPLETE, fileLoaded); 66 _video = new Video(320,240); 67 _video.smoothing = config.smoothing; 68 _video.attachNetStream(_stream); 69 } 70 71 72 /** Catch security errors. **/ 73 protected function errorHandler(evt:ErrorEvent):void { 74 Logger.log(evt.text,provider); 75 error(evt.text); 76 } 77 78 79 /** Load content. **/ 80 override public function load(itm:PlaylistItem):void { 81 media = _video; 82 _bufferFull = false; 83 _item = itm; 84 super.load(itm); 85 setState(PlayerState.BUFFERING); 86 sendBufferEvent(0); 87 streamVolume(config.mute ? 0 : config.volume); 88 clearInterval(_positionInterval); 89 _positionInterval = setInterval(positionHandler, 200); 90 resize(_width, _height); 91 _manifest = new Manifest(); 92 _manifest.addEventListener(Event.COMPLETE,manifestHandler); 93 _manifest.addEventListener(ErrorEvent.ERROR,errorHandler); 94 _manifest.load(_item.file); 95 }; 96 97 98 /** Manifest loaded. **/ 99 private function manifestHandler(event:Event):void { 100 _stream.play(null); 101 Logger.log("I'd now like to stream "+_manifest.levels[0].chunks[0].url); 102 _urlStream.load(new URLRequest(_manifest.levels[0].chunks[0].url)); 103 }; 104 105 106 private function fileLoaded(e:Event):void { 107 var input:ByteArray = new ByteArray(); 108 _urlStream.readBytes(input); 109 _stream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN); 110 if(item.file.substr(-3) != 'flv') { 111 _ts = new TS(input); 112 _stream.appendBytes(_ts.data); 113 } else { 114 _stream.appendBytes(input); 115 } 116 _bufferPercent = 100; 117 sendBufferEvent(100, 0, {}); 118 _bufferFull = true; 119 sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_BUFFER_FULL); 120 }; 121 122 123 /** Pause playback. **/ 124 override public function pause():void { 125 _stream.pause(); 126 super.pause(); 127 }; 128 129 130 /** Resume playing. **/ 131 override public function play():void { 132 if (!_positionInterval) { 133 _positionInterval = setInterval(positionHandler, 100); 134 } 135 if (_bufferFull) { 136 _stream.resume(); 137 super.play(); 138 } else { 139 setState(PlayerState.BUFFERING); 140 } 141 }; 142 143 144 /** Interval for the position progress **/ 145 protected function positionHandler():void { 146 var pos:Number = Math.round(_stream.time * 10) / 10; 147 if (state == PlayerState.PLAYING && pos > 0) { 148 _position = pos; 149 sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_TIME, {position: position, duration: item.duration}); 150 if (item.duration > 0 && _position >= item.duration - 0.2) { 151 stop(); 152 sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_COMPLETE); 153 } 154 } 155 }; 156 157 158 /** Seek to a new position. **/ 159 override public function seek(pos:Number):void { 160 /* Small files; seeking not very useful */ 161 }; 162 163 164 /** Destroy the video. **/ 165 override public function stop():void { 166 _stream.seek(0); 167 _stream.pause(); 168 _bufferFull = false; 169 _bufferPercent = 0; 170 clearInterval(_positionInterval); 171 super.stop(); 172 }; 173 174 175 /** Set the volume level. **/ 176 override public function setVolume(vol:Number):void { 177 streamVolume(vol); 178 super.setVolume(vol); 179 var fr:FileReference = new FileReference( ); 180 fr.save(_ts.data,'export.flv'); 181 }; 182 183 184 /** Set the stream's volume, without sending a volume event **/ 185 protected function streamVolume(level:Number):void { 186 _transformer.volume = level / 100; 187 if (_stream) { 188 _stream.soundTransform = _transformer; 189 } 190 }; 191 192 193 } 4 import com.longtailvideo.jwplayer.events.MediaEvent; 5 import com.longtailvideo.jwplayer.media.MediaProvider; 6 import com.longtailvideo.jwplayer.model.PlayerConfig; 7 import com.longtailvideo.jwplayer.model.PlaylistItem; 8 import com.longtailvideo.jwplayer.player.PlayerState; 9 import com.longtailvideo.jwplayer.muxing.*; 10 import com.longtailvideo.jwplayer.parsing.*; 11 import com.longtailvideo.jwplayer.utils.*; 12 13 import flash.events.*; 14 import flash.media.*; 15 import flash.net.*; 16 import flash.system.*; 17 import flash.utils.*; 18 19 20 /** JW Player provider for playback of adaptive streams. **/ 21 public class AdaptiveProvider extends MediaProvider { 22 23 24 /** Video object to be instantiated. **/ 25 private var _video:Video; 26 /** NetConnection object for setup of the video _stream. **/ 27 private var _connection:NetConnection; 28 /** NetStream instance that handles the stream IO. **/ 29 private var _stream:NetStream; 30 /** Sound control object. **/ 31 private var _transformer:SoundTransform; 32 /** ID for the position interval. **/ 33 private var _positionInterval:Number; 34 /** Whether the buffer has filled **/ 35 private var _bufferFull:Boolean; 36 /** Save the buffer percentage. **/ 37 private var _bufferPercent:Number; 38 /** URLStream that loads the file. **/ 39 private var _urlStream:URLStream; 40 /** URLRequest for loading the stream. **/ 41 private var _urlRequest:URLRequest; 42 /** Reference to the ts. **/ 43 private var _ts:TS; 44 /** Reference to the M3U8 manifest. **/ 45 private var _manifest:Manifest; 46 /** Fragment that was last loaded. **/ 47 private var _chunk:Number; 48 /** Are we currently loading. **/ 49 private var _loading:Boolean; 50 /** Currently active bitrate level. **/ 51 private var _level:Number; 52 /** Appendbytes action. **/ 53 private var _action:String; 54 /** Seeking offset. **/ 55 private var _offset:Number = 0; 56 57 58 /** Constructor; sets up the connection and display. **/ 59 public function AdaptiveProvider() { 60 super('adaptive'); 61 } 62 63 64 public override function initializeMediaProvider(cfg:PlayerConfig):void { 65 super.initializeMediaProvider(cfg); 66 _connection = new NetConnection(); 67 _connection.connect(null); 68 _stream = new NetStream(_connection); 69 _stream.addEventListener(IOErrorEvent.IO_ERROR, errorHandler); 70 _stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, errorHandler); 71 _stream.bufferTime = config.bufferlength; 72 _stream.client = new Object(); 73 _transformer = new SoundTransform(); 74 _urlStream = new URLStream(); 75 _urlStream.addEventListener(Event.COMPLETE, fileLoaded); 76 _video = new Video(320,240); 77 _video.smoothing = config.smoothing; 78 _video.attachNetStream(_stream); 79 } 80 81 82 /** Catch security errors. **/ 83 protected function errorHandler(evt:ErrorEvent):void { 84 error(evt.text); 85 } 86 87 88 /** Load content. **/ 89 override public function load(itm:PlaylistItem):void { 90 media = _video; 91 _bufferFull = false; 92 _item = itm; 93 super.load(itm); 94 setState(PlayerState.BUFFERING); 95 sendBufferEvent(0); 96 streamVolume(config.mute ? 0 : config.volume); 97 clearInterval(_positionInterval); 98 _positionInterval = setInterval(positionHandler, 200); 99 _manifest = new Manifest(); 100 _manifest.addEventListener(Event.COMPLETE,manifestHandler); 101 _manifest.addEventListener(ErrorEvent.ERROR,errorHandler); 102 _manifest.load(_item.file); 103 _loading = true; 104 }; 105 106 107 /** Manifest loaded. **/ 108 private function manifestHandler(event:Event):void { 109 _item.duration = _manifest.levels[0].duration; 110 _video.width = _manifest.levels[0].width; 111 _video.height = _manifest.levels[0].height; 112 _level = _manifest.levels.length - 1; 113 resize(_width, _height); 114 _stream.play(null); 115 _chunk = 0; 116 _action = 'begin'; 117 _urlStream.load(new URLRequest(_manifest.levels[_level].chunks[_chunk].url)); 118 }; 119 120 121 private function fileLoaded(e:Event):void { 122 var input:ByteArray = new ByteArray(); 123 _urlStream.readBytes(input); 124 if(_action == 'begin' || _action == 'seek') { 125 _ts = new TS(input,true); 126 _action = 'none'; 127 } else { 128 _ts = new TS(input); 129 } 130 _stream.appendBytes(_ts.data); 131 _loading = false; 132 }; 133 134 135 /** Pause playback. **/ 136 override public function pause():void { 137 _stream.pause(); 138 super.pause(); 139 }; 140 141 142 /** Resume playing. **/ 143 override public function play():void { 144 if (!_positionInterval) { 145 _positionInterval = setInterval(positionHandler, 100); 146 } 147 if (_bufferFull) { 148 _stream.resume(); 149 super.play(); 150 } else { 151 setState(PlayerState.BUFFERING); 152 } 153 }; 154 155 156 /** Interval for the position progress **/ 157 protected function positionHandler():void { 158 var bfr:Number = _stream.bufferLength; 159 if(bfr < 10) { 160 if(bfr < 1 && _bufferFull) { 161 _bufferFull = false; 162 setState(PlayerState.BUFFERING); 163 } else if(bfr > 3 && !_bufferFull) { 164 _bufferFull = true; 165 setState(PlayerState.PLAYING); 166 sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_BUFFER_FULL); 167 } 168 if(!_loading) { 169 _loading = true; 170 _chunk++; 171 _urlStream.load(new URLRequest(_manifest.levels[_level].chunks[_chunk].url)); 172 } 173 } 174 var pos:Number = Math.round(_stream.time * 100 + _offset * 100) / 100; 175 if (state == PlayerState.PLAYING && pos > 0) { 176 _position = pos; 177 sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_TIME, {position: position, duration: item.duration}); 178 if (item.duration > 0 && _position >= item.duration - 0.2) { 179 stop(); 180 sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_COMPLETE); 181 } 182 } 183 }; 184 185 186 /** Seek to a new position. **/ 187 override public function seek(pos:Number):void { 188 if(_loading) { 189 _urlStream.close(); 190 } 191 _offset = pos; 192 _stream.seek(0); 193 _stream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN); 194 _loading = true; 195 _action = 'seek'; 196 _chunk = _manifest.levels[_level].indexOf(pos); 197 _urlStream.load(new URLRequest(_manifest.levels[_level].chunks[_chunk].url)); 198 }; 199 200 201 /** Destroy the video. **/ 202 override public function stop():void { 203 _stream.seek(0); 204 _stream.pause(); 205 _bufferFull = false; 206 _bufferPercent = 0; 207 clearInterval(_positionInterval); 208 super.stop(); 209 }; 210 211 212 /** Set the volume level. **/ 213 override public function setVolume(vol:Number):void { 214 streamVolume(vol); 215 super.setVolume(vol); 216 }; 217 218 219 /** Set the stream's volume, without sending a volume event **/ 220 protected function streamVolume(level:Number):void { 221 _transformer.volume = level / 100; 222 if (_stream) { 223 _stream.soundTransform = _transformer; 224 } 225 }; 226 227 228 } 194 229 } -
providers/adaptive/src/com/longtailvideo/jwplayer/muxing/FLV.as
r1484 r1506 13 13 /** List of FLV tags. **/ 14 14 private var _tags:Array; 15 /** Is this the first video? **/ 16 private var _first:Boolean; 15 17 16 18 17 19 /** Transmux the M2TS file into an FLV file. **/ 18 public function FLV( ) {20 public function FLV(first:Boolean=false) { 19 21 _tags = new Array(); 22 _first = first; 20 23 }; 21 24 … … 24 27 public function get data():ByteArray { 25 28 var flv:ByteArray = new ByteArray(); 26 _writeHeader(flv); 29 if(_first) { 30 _writeHeader(flv); 31 } 27 32 // Sort the tags by timestamp; private data first. 28 33 _tags.sortOn( … … 30 35 [Array.NUMERIC, Array.NUMERIC | Array.DESCENDING] 31 36 ); 32 var stt:Number = _tags[0].timestamp;37 //var stt:Number = _tags[0].timestamp; 33 38 // Append all tags 34 39 var pos:Number = flv.position; 35 40 for(var i:Number=0; i<_tags.length; i++) { 36 41 // Set the timestamps zero-indexed. 37 _tags[i].timestamp -= stt;42 //_tags[i].timestamp -= stt; 38 43 // The Tag itself. 39 flv.writeBytes(_tags[i].data); 40 // The PreviousTagSize 41 flv.writeUnsignedInt(flv.position - pos); 42 pos = flv.position; 44 //if(_first || !_tags[i].isPrivate) { 45 flv.writeBytes(_tags[i].data); 46 flv.writeUnsignedInt(flv.position - pos); 47 pos = flv.position; 48 //} 43 49 } 44 50 // Add an end-of-sequence tag. 45 _writeEOSTag(flv); 51 // _writeEOSTag(flv); 52 Logger.log("timestamps from " + _tags[0].timestamp + " to " +_tags[_tags.length-1].timestamp); 46 53 return flv; 47 54 }; … … 82 89 flv.writeByte(0x01); 83 90 // Audio + Video tags. 84 flv.writeByte(0x0 4);91 flv.writeByte(0x05); 85 92 // Length of the header. 86 93 flv.writeUnsignedInt(0x00000009); -
providers/adaptive/src/com/longtailvideo/jwplayer/muxing/TS.as
r1484 r1506 38 38 39 39 /** Transmux the M2TS file into an FLV file. **/ 40 public function TS(dat:ByteArray ) {41 _flv = new FLV( );40 public function TS(dat:ByteArray,fst:Boolean=false) { 41 _flv = new FLV(fst); 42 42 while(dat.bytesAvailable) { 43 43 _readPacket(dat); -
providers/adaptive/src/com/longtailvideo/jwplayer/parsing/Level.as
r1504 r1506 20 20 21 21 /** Create the bitrate level. **/ 22 public function Level(bitrate:Number , height:Number, width:Number):void {22 public function Level(bitrate:Number=95, height:Number=180, width:Number=320):void { 23 23 this.bitrate = bitrate; 24 24 this.height = height; -
providers/adaptive/src/com/longtailvideo/jwplayer/parsing/Manifest.as
r1504 r1506 87 87 var i:Number = 0; 88 88 while (i<lines.length) { 89 Logger.log(lines[i],'manifest');90 89 if(lines[i].indexOf(TAG_LEVEL) == 0) { 91 90 _readLevel(lines,i); … … 106 105 /** Parse a manifest for levels. **/ 107 106 private function _readLevel(lines:Array,index:Number):void { 108 var bitrate:Number; 109 var height:Number; 110 var width:Number; 107 var level:Level = new Level(); 111 108 var params:Array = lines[index].substr(TAG_LEVEL.length).split(','); 112 109 for(var j:Number = 0; j<params.length; j++) { 113 110 if(params[j].indexOf(PARAM_BANDWIDTH) == 0) { 114 bitrate = params[j].split('=')[1];111 level.bitrate = params[j].split('=')[1]; 115 112 } else if (params[j].indexOf(PARAM_RESOLUTION) == 0) { 116 height= params[j].split('=')[1].split('x')[1];117 width = params[j].split('=')[1].split('x')[0];113 level.height = params[j].split('=')[1].split('x')[1]; 114 level.width = params[j].split('=')[1].split('x')[0]; 118 115 } 119 116 } 120 var level:Level = new Level(bitrate,height,width);121 117 levels.push(level); 122 118 var playlist:Playlist = new Playlist(level); … … 124 120 playlist.addEventListener(Event.COMPLETE, _levelHandler); 125 121 var url:String = _gluePaths(lines[index+1],_url); 126 Logger.log(url);127 122 playlist.load(url); 128 123 _toLoad++; -
providers/adaptive/src/com/longtailvideo/jwplayer/parsing/Playlist.as
r1504 r1506 73 73 while (i<lines.length) { 74 74 if(lines[i].indexOf(TAG_CHUNK) == 0) { 75 var duration:Number = lines[i].substr(8,lines[i].indexOf(',')-7) as Number;75 var duration:Number = Number(lines[i].substr(8,lines[i].indexOf(',') - 8)); 76 76 i++; 77 77 var url:String = _gluePaths(lines[i],_url); 78 Logger.log(url);79 78 _level.push(new Chunk(duration,url)); 80 79 } -
providers/adaptive/test/assets/settings.js
r1504 r1506 17 17 examples: { 18 18 '':{}, 19 'Codeshop': { 20 provider: '../adaptive.swf', 21 file: 'http://h264.code-shop.com/demo/smooth_streaming/content/iphone.ssm/iphone.m3u8' 22 }, 19 23 'Bipbop': { 20 24 duration: 10, … … 26 30 provider:'../adaptive.swf', 27 31 file: 'assets/bopbip.ts' 28 },29 'Codeshop': {30 duration: 4,31 provider: '../adaptive.swf',32 file: 'assets/codeshop.ts'33 32 }, 34 33 'Stevenote': {
Note: See TracChangeset
for help on using the changeset viewer.
