source: trunk/fl5/src/com/longtailvideo/jwplayer/media/HTTPMediaProvider.as @ 525

Revision 525, 9.4 KB checked in by zach, 4 years ago (diff)

Fixing HTTPMediaProvider buffer, time, and resize issues

Line 
1/**
2 * Manages playback of http streaming flv.
3 **/
4package com.longtailvideo.jwplayer.media {
5        import com.longtailvideo.jwplayer.events.MediaEvent;
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.utils.NetClient;
10       
11        import flash.events.*;
12        import flash.media.*;
13        import flash.net.*;
14        import flash.utils.*;
15       
16       
17        public class HTTPMediaProvider extends MediaProvider {
18                /** NetConnection object for setup of the video stream. **/
19                protected var connection:NetConnection;
20                /** NetStream instance that handles the stream IO. **/
21                protected var stream:NetStream;
22                /** Video object to be instantiated. **/
23                protected var video:Video;
24                /** Sound control object. **/
25                protected var transformer:SoundTransform;
26                /** ID for the position interval. **/
27                protected var interval:Number;
28                /** Interval ID for the loading. **/
29                protected var loadinterval:Number;
30                /** Save whether metadata has already been sent. **/
31                protected var meta:Boolean;
32                /** Object with keyframe times and positions. **/
33                protected var keyframes:Object;
34                /** Offset in bytes of the last seek. **/
35                protected var byteoffset:Number;
36                /** Offset in seconds of the last seek. **/
37                protected var timeoffset:Number;
38                /** Boolean for mp4 / flv streaming. **/
39                protected var mp4:Boolean;
40                /** Load offset for bandwidth checking. **/
41                protected var loadtimer:Number;
42                /** Variable that takes reloading into account. **/
43                protected var iterator:Number;
44                /** Start parameter. **/
45                private var startparam:String = 'start';
46               
47               
48                /** Constructor; sets up the connection and display. **/
49                public function HTTPMediaProvider() {   
50                }
51               
52                public override function initializeMediaProvider(cfg:PlayerConfig):void {
53                        super.initializeMediaProvider(cfg);
54                        _provider = 'http';
55                        connection = new NetConnection();
56                        connection.connect(null);
57                        stream = new NetStream(connection);
58                        stream.checkPolicyFile = true;
59                        stream.addEventListener(NetStatusEvent.NET_STATUS, statusHandler);
60                        stream.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
61                        stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, errorHandler);
62                        stream.bufferTime = _config.bufferlength;
63                        stream.client = new NetClient(this);
64                        video = new Video(320, 240);
65                        video.smoothing = _config.smoothing;
66                        video.attachNetStream(stream);
67                        transformer = new SoundTransform();
68                        byteoffset = timeoffset = 0;
69                }
70               
71               
72                /** Convert seekpoints to keyframes. **/
73                protected function convertSeekpoints(dat:Object):Object {
74                        var kfr:Object = new Object();
75                        kfr.times = new Array();
76                        kfr.filepositions = new Array();
77                        for (var j:String in dat) {
78                                kfr.times[j] = Number(dat[j]['time']);
79                                kfr.filepositions[j] = Number(dat[j]['offset']);
80                        }
81                        return kfr;
82                }
83               
84               
85                /** Catch security errors. **/
86                protected function errorHandler(evt:ErrorEvent):void {
87                        error(evt.text);
88                }
89               
90               
91                /** Return a keyframe byteoffset or timeoffset. **/
92                protected function getOffset(pos:Number, tme:Boolean = false):Number {
93                        if (!keyframes) {
94                                return 0;
95                        }
96                        for (var i:Number = 0; i < keyframes.times.length - 1; i++) {
97                                if (keyframes.times[i] <= pos && keyframes.times[i + 1] >= pos) {
98                                        break;
99                                }
100                        }
101                        if (tme == true) {
102                                return keyframes.times[i];
103                        } else {
104                                return keyframes.filepositions[i];
105                        }
106                }
107               
108               
109                /** Create the video request URL. **/
110                protected function getURL():String {
111                        var url:String = item.file;
112                        var off:Number = byteoffset;
113                        if (getConfigProperty('startparam') as String) {
114                                startparam = getConfigProperty('startparam');
115                        }
116                        if (item.streamer) {
117                                if(item['streamer'].indexOf('/') > 0) {
118                                        url = item.streamer;
119                                        url = getURLConcat(url,'file',item.file);
120                                } else {
121                                        startparam = item.streamer;
122                                }
123                        }
124                        if (mp4) {
125                                off = timeoffset;
126                        } else if (startparam == 'starttime') {
127                                startparam = 'start';
128                        }
129                        if (off > 0) {
130                                url = getURLConcat(url, startparam, off);
131                        }
132                        return url;
133                }
134               
135               
136                /** Concatenate a parameter to the url. **/
137                private function getURLConcat(url:String, prm:String, val:*):String {
138                        if (url.indexOf('?') > -1) {
139                                return url + '&' + prm + '=' + val;
140                        } else {
141                                return url + '?' + prm + '=' + val;
142                        }
143                }
144               
145               
146                /** Load content. **/
147                override public function load(itm:PlaylistItem):void {
148                        _item = itm;
149                        _position = timeoffset;
150                        if (stream.bytesLoaded + byteoffset < stream.bytesTotal) {
151                                stream.close();
152                        }
153                        media = video;
154                        stream.play(getURL());
155                        iterator = 0;
156                        clearInterval(interval);
157                        interval = setInterval(positionInterval, 100);
158                        clearInterval(loadinterval);
159                        loadinterval = setInterval(loadHandler, 200);
160                        setState(PlayerState.BUFFERING);
161                        sendBufferEvent(0);
162                        sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_LOADED);
163                        _config.mute == true ? setVolume(0) : setVolume(_config.volume);
164                }
165               
166               
167                /** Interval for the loading progress **/
168                protected function loadHandler():void {
169                        var ldd:Number = stream.bytesLoaded;
170                        var ttl:Number = stream.bytesTotal;
171                        var pct:Number = timeoffset / (item.duration + 0.001);
172                        var off:Number = Math.round(ttl * pct / (1 - pct));
173                        ttl += off;
174                        try {
175                                sendBufferEvent(Math.round(ldd / ttl * 100));
176                        } catch (err:Error) {
177                                error(err.getStackTrace());
178                        }
179                        if (ldd + off >= ttl && ldd > 0) {
180                                clearInterval(loadinterval);
181                        }
182                        if (!loadtimer) {
183                                loadtimer = setTimeout(loadTimeout, 3000);
184                        }
185                }
186               
187               
188                /** timeout for checking the bitrate. **/
189                protected function loadTimeout():void {
190                        var obj:Object = new Object();
191                        obj.bandwidth = Math.round(stream.bytesLoaded / 1024 / 3 * 8);
192                        if (item.duration) {
193                                obj.bitrate = Math.round(stream.bytesTotal / 1024 * 8 / item.duration);
194                        }
195                        sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_META, {metadata: obj});
196                }
197               
198               
199                /** Get metadata information from netstream class. **/
200                public function onData(dat:Object):void {
201                        if (dat.width) {
202                                video.width = dat.width;
203                                video.height = dat.height;
204                                resize(_width, _height);
205                        }
206                        if (dat.duration) {
207                                item.duration = dat.duration;
208                        }
209                        if (dat['type'] == 'metadata' && !meta) {
210                                meta = true;
211                                if (dat.seekpoints) {
212                                        mp4 = true;
213                                        keyframes = convertSeekpoints(dat.seekpoints);
214                                } else {
215                                        mp4 = false;
216                                        keyframes = dat.keyframes;
217                                }
218                                if (item.start > 0) {
219                                        seek(item.start);
220                                }
221                        }
222                        sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_META, {metadata: dat});
223                }
224               
225               
226                /** Pause playback. **/
227                override public function pause():void {
228                        stream.pause();
229                        clearInterval(interval);
230                        super.pause();
231                }
232               
233               
234                /** Resume playing. **/
235                override public function play():void {
236                        stream.resume();
237                        interval = setInterval(positionInterval, 100);
238                        super.play();
239                }
240               
241               
242                /** Interval for the position progress **/
243                protected function positionInterval():void {
244                        iterator++;
245                        if (iterator > 10) {
246                                _position = Math.round(stream.time * 10) / 10;
247                                if (mp4) {
248                                        _position += timeoffset;
249                                }
250                        }
251                        var bfr:Number = Math.round(stream.bufferLength / stream.bufferTime * 100);
252                        if (bfr < 95 && position < Math.abs(item.duration - stream.bufferTime - 1)) {
253                                if (state == PlayerState.PLAYING && bfr < 25) {
254                                        stream.pause();
255                                        setState(PlayerState.BUFFERING);
256                                }
257                                sendBufferEvent(bfr);
258                        } else if (bfr > 95 && state == PlayerState.BUFFERING) {
259                                sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_BUFFER_FULL);
260                        }
261                       
262                        var bufferPercent:Number = stream.bytesLoaded / stream.bytesTotal * 100;
263                        if (state == PlayerState.BUFFERING) {
264                                sendBufferEvent(bufferPercent);
265                        } else if (position < item.duration) {
266                                if (state == PlayerState.PLAYING && position >= 0) {
267                                        sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_TIME, {position: position, duration: item.duration, bufferPercent:bufferPercent});
268                                }
269                        } else if (item.duration > 0) {
270                                // Playback completed
271                                complete();
272                        }
273                }
274               
275               
276                /** Seek to a specific second. **/
277                override public function seek(pos:Number):void {
278                        var off:Number = getOffset(pos);
279                        super.seek(pos);
280                        clearInterval(interval);
281                        if (off < byteoffset || off >= byteoffset + stream.bytesLoaded) {
282                                timeoffset = _position = getOffset(pos, true);
283                                byteoffset = off;
284                                load(item);
285                        } else {
286                                if (state == PlayerState.PAUSED) {
287                                        stream.resume();
288                                }
289                                _position = pos;
290                                if (mp4) {
291                                        stream.seek(getOffset(position - timeoffset, true));
292                                } else {
293                                        stream.seek(getOffset(position, true));
294                                }
295                                play();
296                        }
297                }
298               
299               
300                /** Receive NetStream status updates. **/
301                protected function statusHandler(evt:NetStatusEvent):void {
302                        switch (evt.info.code) {
303                                case "NetStream.Play.Stop":
304                                        if (state != PlayerState.BUFFERING) {
305                                                complete();
306                                        }
307                                        break;
308                                case "NetStream.Play.StreamNotFound":
309                                        stop();
310                                        error('Video not found: ' + item.file);
311                                        break;
312                        }
313                        sendMediaEvent(MediaEvent.JWPLAYER_MEDIA_META, {metadata: {status: evt.info.code}});
314                }
315               
316               
317                /** Destroy the HTTP stream. **/
318                override public function stop():void {
319                        if (stream.bytesLoaded + byteoffset < stream.bytesTotal) {
320                                stream.close();
321                        } else {
322                                stream.pause();
323                        }
324                        clearInterval(interval);
325                        clearInterval(loadinterval);
326                        byteoffset = timeoffset = 0;
327                        keyframes = undefined;
328                        meta = false;
329                        super.stop();
330                }
331               
332               
333                /** Set the volume level. **/
334                override public function setVolume(vol:Number):void {
335                        transformer.volume = vol / 100;
336                        stream.soundTransform = transformer;
337                        super.setVolume(vol);
338                }
339        }
340}
Note: See TracBrowser for help on using the repository browser.