source: trunk/as3/com/jeroenwijering/models/HTTPModel.as @ 104

Revision 104, 8.2 KB checked in by jeroen, 5 years ago (diff)

added HD switch plugin (and fixed a bug in the LOAD event

  • Property svn:executable set to *
Line 
1/**
2* Wrapper for playback of http 'streaming' video.
3**/
4package com.jeroenwijering.models {
5
6
7import com.jeroenwijering.events.*;
8import com.jeroenwijering.models.ModelInterface;
9import com.jeroenwijering.player.Model;
10import com.jeroenwijering.utils.NetClient;
11import flash.events.*;
12import flash.display.DisplayObject;
13import flash.media.SoundTransform;
14import flash.media.Video;
15import flash.net.*;
16import flash.utils.clearInterval;
17import flash.utils.setInterval;
18
19
20public class HTTPModel implements ModelInterface {
21
22
23        /** reference to the model. **/
24        private var model:Model;
25        /** Video object to be instantiated. **/
26        private var video:Video;
27        /** NetConnection object for setup of the video stream. **/
28        private var connection:NetConnection;
29        /** NetStream instance that handles the stream IO. **/
30        private var stream:NetStream;
31        /** Sound control object. **/
32        private var transform:SoundTransform;
33        /** Interval ID for the time. **/
34        private var timeinterval:Number;
35        /** Interval ID for the loading. **/
36        private var loadinterval:Number;
37        /** Object with keyframe times and positions. **/
38        private var keyframes:Object;
39        /** Offset byteposition to start streaming. **/
40        private var offset:Number;
41        /** Offset timeposition for lighttpd streaming. **/
42        private var timeoffset:Number;
43        /** switch for first metadata run **/
44        private var metadata:Boolean;
45        /** switch for h264 streaming **/
46        private var h264:Boolean;
47        /** Byteposition to which the file has been loaded. **/
48        private var loaded:Number;
49
50
51        /** Constructor; sets up the connection and display. **/
52        public function HTTPModel(mod:Model):void {
53                model = mod;
54                connection = new NetConnection();
55                connection.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
56                connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,errorHandler);
57                connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR,errorHandler);
58                connection.connect(null);
59                stream = new NetStream(connection);
60                stream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
61                stream.addEventListener(IOErrorEvent.IO_ERROR,errorHandler);
62                stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR,metaHandler);
63                stream.bufferTime = model.config['bufferlength'];
64                stream.client = new NetClient(this);
65                video = new Video(320,240);
66                video.attachNetStream(stream);
67                transform = new SoundTransform();
68                stream.soundTransform = transform;
69                model.config['mute'] == true ? volume(0): volume(model.config['volume']);
70                quality(model.config['quality']);
71                offset = timeoffset = 0;
72        };
73
74
75        /** Catch security errors. **/
76        private function errorHandler(evt:ErrorEvent):void {
77                model.sendEvent(ModelEvent.ERROR,{message:evt.text});
78        };
79
80
81        /** Return a keyframe byteoffset or timeoffset. **/
82        private function getOffset(pos:Number,tme:Boolean=false):Number {
83                for (var i=0; i< keyframes.times.length; i++) {
84                        if(keyframes.times[i] <= pos && keyframes.times[i+1] >= pos) {
85                                if(tme == true) {
86                                        return keyframes.times[i];
87                                } else {
88                                        return keyframes.filepositions[i];
89                                }
90                        }
91                }
92                return 0;
93        };
94
95
96        /** Returns a key to add to the stream. **/
97        private function getToken():String {
98                return model.config['token'];
99        };
100
101
102        /** Load content. **/
103        public function load():void {
104                video.clear();
105                model.mediaHandler(video);
106                if(stream.bytesLoaded != stream.bytesTotal) {
107                        stream.close();
108                }
109                var url = model.playlist[model.config['item']]['file'];
110                var str = model.playlist[model.config['item']]['streamer'];
111                if(str == "lighttpd") {
112                        if(h264) {
113                                url +='?start='+timeoffset;
114                        } else {
115                                url += '?start='+offset;
116                        }
117                } else {
118                        if(str.indexOf('?') > -1) {
119                                url = str+"&file="+url+'&start='+offset;
120                        } else {
121                                url = str+"?file="+url+'&start='+offset;
122                        }
123                }
124                url += '&id='+model.config['id'];
125                url += '&client='+encodeURI(model.config['client']);
126                url += '&version='+encodeURI(model.config['version']);
127                url += '&width='+model.config['width'];
128                if(getToken()) { url += '&token='+getToken(); }
129                stream.play(url);
130                clearInterval(loadinterval);
131                clearInterval(timeinterval);
132                loadinterval = setInterval(loadHandler,200);
133                timeinterval = setInterval(timeHandler,100);
134                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.BUFFERING});
135        };
136
137
138        /** Interval for the loading progress **/
139        private function loadHandler():void {
140                loaded = stream.bytesLoaded;
141                var ttl = stream.bytesTotal;
142                if(loaded >= ttl && loaded > 0) {
143                        clearInterval(loadinterval);
144                }
145                //model.sendEvent(ModelEvent.LOADED,{loaded:loaded,total:ttl+offset,offset:offset});
146        };
147
148
149        /** Catch noncritical errors. **/
150        private function metaHandler(evt:ErrorEvent):void {
151                model.sendEvent(ModelEvent.META,{error:evt.text});
152        };
153
154
155        /** Get metadata information from netstream class. **/
156        public function onData(dat:Object):void {
157                if(dat.type == 'metadata') {
158                        if(dat.seekpoints && !h264) {
159                                h264 = true;
160                                keyframes = new Object();
161                                keyframes.times = new Array();
162                                keyframes.filepositions = new Array();
163                                for (var j in dat.seekpoints) {
164                                        keyframes.times[j] = Number(dat.seekpoints[j]['time']);
165                                        keyframes.filepositions[j] = Number(dat.seekpoints[j]['offset']);
166                                }
167                        } else if(dat.keyframes) {
168                                keyframes = dat.keyframes;
169                        }
170                        if(!metadata) {
171                                if(dat.width) {
172                                        video.width = dat.width;
173                                        video.height = dat.height;
174                                }
175                                model.sendEvent(ModelEvent.META,dat);
176                                if(model.playlist[model.config['item']]['start'] > 0 && !metadata) {
177                                        seek(model.playlist[model.config['item']]['start']);
178                                }
179                                metadata = true;
180                        }
181                } else {
182                        model.sendEvent(ModelEvent.META,dat);
183                }
184        };
185
186
187        /** Pause playback. **/
188        public function pause():void {
189                clearInterval(timeinterval);
190                stream.pause();
191                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PAUSED});
192        };
193
194
195        /** Resume playing. **/
196        public function play():void {
197                stream.resume();
198                timeinterval = setInterval(timeHandler,100);
199                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PLAYING});
200        };
201
202
203        /** Seek to a specific second. **/
204        public function seek(pos:Number):void {
205                clearInterval(timeinterval);
206                var off = getOffset(pos);
207                if(off < offset || off > offset+loaded) {
208                        offset = off;
209                        timeoffset = getOffset(pos,true);
210                        load();
211                } else {
212                        if(h264) {
213                                stream.seek(pos-timeoffset);
214                        } else {
215                                stream.seek(pos)
216                        }
217                        play();
218                }
219        };
220
221
222        /** Change the smoothing mode. **/
223        public function quality(qua:Boolean):void {
224                if(qua == true) {
225                        video.smoothing = true;
226                        video.deblocking = 3;
227                } else {
228                        video.smoothing = false;
229                        video.deblocking = 1;
230                }
231        };
232
233
234        /** Receive NetStream status updates. **/
235        private function statusHandler(evt:NetStatusEvent):void {
236                if(evt.info.code == "NetStream.Play.Stop") {
237                        if(model.config['state'] != ModelStates.COMPLETED) {
238                                clearInterval(timeinterval);
239                                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.COMPLETED});
240                        }
241                } else if(evt.info.code == "NetStream.Play.StreamNotFound") {
242                        stop();
243                        model.sendEvent(ModelEvent.ERROR,{message:"Video stream not found: " +
244                                model.playlist[model.config['item']]['file']});
245                } else {
246                        model.sendEvent(ModelEvent.META,{info:evt.info.code});
247                }
248        };
249
250
251        /** Destroy the HTTP stream. **/
252        public function stop():void {
253                clearInterval(loadinterval);
254                clearInterval(timeinterval);
255                offset = timeoffset = 0;
256                h264 = false;
257                keyframes = undefined;
258                metadata = false;
259                if(stream.bytesLoaded != stream.bytesTotal) {
260                        stream.close();
261                }
262                stream.pause();
263        };
264
265
266        /** Interval for the position progress **/
267        private function timeHandler():void {
268                var bfr = Math.round(stream.bufferLength/stream.bufferTime*100);
269                var pos = Math.round(stream.time*10)/10;
270                if (h264) { pos += timeoffset; }
271                var dur = model.playlist[model.config['item']]['duration'];
272                if(bfr<95 && pos < Math.abs(dur-stream.bufferTime*2)) {
273                        model.sendEvent(ModelEvent.BUFFER,{percentage:bfr});
274                        if(model.config['state'] != ModelStates.BUFFERING  && bfr < 10) {
275                                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.BUFFERING});
276                        }
277                } else if (model.config['state'] == ModelStates.BUFFERING) {
278                        model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PLAYING});
279                }
280                model.sendEvent(ModelEvent.TIME,{position:pos});
281        };
282
283
284        /** Set the volume level. **/
285        public function volume(vol:Number):void {
286                transform.volume = vol/100;
287                stream.soundTransform = transform;
288        };
289
290
291};
292
293
294}
Note: See TracBrowser for help on using the repository browser.