source: trunk/as3/com/jeroenwijering/models/VideoModel.as @ 679

Revision 679, 6.6 KB checked in by jeroen, 3 years ago (diff)

fixed a bug in duration for MP3 and premature complete events for Video/HTTP/RTMP models

  • Property svn:executable set to *
Line 
1/**
2* Wrapper for playback of progressively downloaded video.
3**/
4package com.jeroenwijering.models {
5
6
7import com.jeroenwijering.events.*;
8import com.jeroenwijering.models.AbstractModel;
9import com.jeroenwijering.player.Model;
10import com.jeroenwijering.utils.*;
11
12import flash.events.*;
13import flash.media.*;
14import flash.net.*;
15import flash.utils.*;
16
17
18public class VideoModel extends AbstractModel {
19
20
21        /** Save if the bandwidth checkin already occurs. **/
22        private var bwcheck:Boolean;
23        /** Switch if the bandwidth is not enough. **/
24        private var bwswitch:Boolean = true;
25        /** NetConnection object for setup of the video stream. **/
26        private var connection:NetConnection;
27        /** ID for the position interval. **/
28        private var interval:Number;
29        /** Interval ID for the loading. **/
30        private var loading:Number;
31        /** NetStream instance that handles the stream IO. **/
32        private var stream:NetStream;
33        /** Sound control object. **/
34        private var transformer:SoundTransform;
35        /** Video object to be instantiated. **/
36        private var video:Video;
37
38
39        /** Constructor; sets up the connection and display. **/
40        public function VideoModel(mod:Model):void {
41                super(mod);
42                connection = new NetConnection();
43                connection.connect(null);
44                stream = new NetStream(connection);
45                stream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
46                stream.addEventListener(IOErrorEvent.IO_ERROR,errorHandler);
47                stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR,errorHandler);
48                stream.bufferTime = model.config['bufferlength'];
49                stream.client = new NetClient(this);
50                transformer = new SoundTransform();
51                video = new Video(320,240);
52                video.smoothing = model.config['smoothing'];
53                video.attachNetStream(stream);
54                addChild(video);
55        };
56
57
58        /** Catch security errors. **/
59        private function errorHandler(evt:ErrorEvent):void {
60                stop();
61                model.sendEvent(ModelEvent.ERROR,{message:evt.text});
62        };
63
64
65        /** Bandwidth is checked every four seconds as long as there's loading. **/
66        private function getBandwidth(old:Number):void {
67                var ldd:Number = stream.bytesLoaded;
68                var bdw:Number = Math.round((ldd-old)*4/1000);
69                if(ldd < stream.bytesTotal) {
70                        if(bdw > 0) { model.config['bandwidth'] = bdw; }
71                        if(bwswitch) {
72                                bwswitch = false;
73                                if(item['levels'] && getLevel() != model.config['level']) {
74                                        model.config['level'] = getLevel();
75                                        item['file'] = item['levels'][model.config['level']].url;
76                                        load(item);
77                                        return;
78                                }
79                        }
80                        setTimeout(getBandwidth,2000,ldd);
81                }
82        };
83
84
85        /** Return which level best fits the display width and connection bandwidth. **/
86        private function getLevel():Number {
87                var lvl:Number = item['levels'].length-1;
88                for (var i:Number=0; i<item['levels'].length; i++) {
89                        if(model.config['width'] >= item['levels'][i].width &&
90                                model.config['bandwidth'] >= item['levels'][i].bitrate) {
91                                lvl = i;
92                                break;
93                        }
94                }
95                return lvl;
96        };
97
98
99        /** Load content. **/
100        override public function load(itm:Object):void {
101                item = itm;
102                position = 0;
103                bwcheck = false;
104                if(item['levels']) {
105                        model.config['level'] = getLevel();
106                        item['file'] = item['levels'][model.config['level']].url;
107                }
108                stream.checkPolicyFile = true;
109                stream.play(item['file']);
110                clearInterval(interval);
111                interval = setInterval(positionInterval,100);
112                clearInterval(loading);
113                loading = setInterval(loadHandler,200);
114                model.config['mute'] == true ? volume(0): volume(model.config['volume']);
115                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.BUFFERING});
116                resize();
117        };
118
119
120        /** Interval for the loading progress **/
121        private function loadHandler():void {
122                var ldd:Number = stream.bytesLoaded;
123                var ttl:Number = stream.bytesTotal;
124                model.sendEvent(ModelEvent.LOADED,{loaded:ldd,total:ttl});
125                if(ldd && ldd == ttl) {
126                        clearInterval(loading);
127                }
128                if(ldd > 0 && !bwcheck) {
129                        bwcheck = true;
130                        setTimeout(getBandwidth,2000,ldd);
131                }
132        };
133
134
135        /** Get metadata information from netstream class. **/
136        public function onClientData(dat:Object):void {
137                if(dat.width) {
138                        video.width = dat.width;
139                        video.height = dat.height;
140                        resize();
141                }
142                if(dat.duration && (!item['duration'] || item['duration'] > dat.duration-5)) {
143                        item['duration'] = dat.duration;
144                }
145                model.sendEvent(ModelEvent.META,dat);
146        };
147
148
149        /** Pause playback. **/
150        override public function pause():void {
151                stream.pause();
152                clearInterval(interval);
153                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PAUSED});
154        };
155
156
157        /** Resume playing. **/
158        override public function play():void {
159                stream.resume();
160                interval = setInterval(positionInterval,100);
161                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PLAYING});
162        };
163
164
165        /** Interval for the position progress **/
166        private function positionInterval():void {
167                var pos:Number = Math.round(stream.time*10)/10;
168                var bfr:Number = stream.bufferLength/stream.bufferTime;
169                if(bfr < 0.5 && position < item['duration']-5 && model.config['state'] != ModelStates.BUFFERING) {
170                        model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.BUFFERING});
171                } else if (bfr > 1 && model.config['state'] != ModelStates.PLAYING) {
172                        model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PLAYING});
173                }
174                if(model.config['state'] != ModelStates.PLAYING) {
175                        return;
176                }
177                if(pos < item['duration']) {
178                        position = pos;
179                        model.sendEvent(ModelEvent.TIME,{position:pos,duration:item['duration']});
180                } else if (item['duration']) {
181                        stream.pause();
182                        clearInterval(interval);
183                        model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.COMPLETED});
184                }
185        };
186
187
188        /** Seek to a new position. **/
189        override public function seek(pos:Number):void {
190                if(stream && pos < stream.bytesLoaded/stream.bytesTotal*item['duration']) {
191                        position = pos;
192                        clearInterval(interval);
193                        stream.seek(position);
194                        play();
195                }
196        };
197
198
199        /** Receive NetStream status updates. **/
200        private function statusHandler(evt:NetStatusEvent):void {
201                switch (evt.info.code) {
202                        case "NetStream.Play.Stop":
203                                if(position > 1) {
204                                        clearInterval(interval);
205                                        model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.COMPLETED});
206                                }
207                                break;
208                        case "NetStream.Play.StreamNotFound":
209                                stop();
210                                model.sendEvent(ModelEvent.ERROR,{message:'Video not found or access denied: '+item['file']});
211                                break;
212                }
213                model.sendEvent(ModelEvent.META,{status:evt.info.code});
214        };
215
216
217        /** Destroy the video. **/
218        override public function stop():void {
219                if(stream.bytesLoaded < stream.bytesTotal) {
220                        stream.close();
221                } else {
222                        stream.pause();
223                }
224                clearInterval(loading);
225                clearInterval(interval);
226                position = 0;
227                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.IDLE});
228        };
229
230
231        /** Set the volume. **/
232        override public function volume(vol:Number):void {
233                transformer.volume = vol/100;
234                stream.soundTransform = transformer;
235        };
236
237
238};
239
240
241}
Note: See TracBrowser for help on using the repository browser.