source: trunk/as3/com/jeroenwijering/models/SmoothModel.as @ 354

Revision 354, 6.1 KB checked in by jeroen, 4 years ago (diff)

made the HD plugin cleaner, added javascript URL sniffing to Sharing plugin, refactored models and added first implementation of SmoothModel that (more or less) seamlessly concatenated the chunks

  • Property svn:executable set to *
Line 
1/**
2* Model that smoothly plays back a video segmented in smaller chunks.
3**/
4package com.jeroenwijering.models {
5
6
7import com.jeroenwijering.events.*;
8import com.jeroenwijering.models.AbstractModel;
9import com.jeroenwijering.parsers.SmoothParser;
10import com.jeroenwijering.player.Model;
11
12import flash.display.Sprite;
13import flash.events.*;
14import flash.media.*;
15import flash.net.*;
16import flash.utils.*;
17
18
19public class SmoothModel extends AbstractModel {
20
21
22        /** Currently playing chunk. **/
23        private var chunk:Number;
24        /** Chunk that is up next. **/
25        private var next:Number;
26        /** All available video chunks. **/
27        private var chunks:Array;
28        /** Container that switches the two video objects. **/
29        private var container:Sprite;
30        /** Index metadata of the stream. **/
31        private var index:Object;
32        /** ID for the position interval. **/
33        private var interval:Number;
34        /** Currently active level. **/
35        private var level:Number = 0;
36        /** All available quality levels. **/
37        private var levels:Array;
38        /** Loader that loads the manifest XML file. **/
39        private var loader:URLLoader;
40        /** Soundtransform object. **/
41        private var transform:SoundTransform;
42
43
44        /** Constructor; sets up the connection and display. **/
45        public function SmoothModel(mod:Model):void {
46                super(mod);
47                container = new Sprite();
48                container.graphics.drawRect(0,0,320,240);
49                loader = new URLLoader();
50                loader.addEventListener(Event.COMPLETE, loaderHandler);
51                loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,errorHandler);
52                loader.addEventListener(IOErrorEvent.IO_ERROR,errorHandler);
53                transform = new SoundTransform();
54        };
55
56
57        /** Catch playback errors. **/
58        private function errorHandler(evt:ErrorEvent):void {
59                stop();
60                model.sendEvent(ModelEvent.ERROR,{message:evt.text});
61        };
62
63
64        /** Load content. **/
65        override public function load(itm:Object):void {
66                item = itm;
67                level = 0;
68                chunk = 0;
69                position = item['start'];
70                if(model.config['mute']) {
71                         transform.volume = 0;
72                } else {
73                         transform.volume = model.config['volume']/100;
74                }
75                loader.load(new URLRequest(item['file']));
76                model.mediaHandler(container);
77                model.sendEvent(ModelEvent.BUFFER,{percentage:0});
78                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.BUFFERING});
79        };
80
81
82        /** Load a chunk for playback. **/
83        private function loadChunk(chk:Number):void {
84                //var url:String = item['file'].substr(0,item['file'].length-3)+'mp4';
85                var url:String = 'http://h264.code-shop.com:8080/ccc.mp4';
86                url += '?start='+chunks[chk]['start'];
87                url += '&end='+chunks[chk]['end'];
88                chunks[chk].connection = new NetConnection();
89                chunks[chk].connection.connect(null);
90                chunks[chk].netstream = new NetStream(chunks[chk].connection);
91                chunks[chk].netstream.client = new Object();
92                chunks[chk].netstream.soundTransform = transform;
93                chunks[chk].netstream.play(url);
94                chunks[chk].netstream.pause();
95                chunks[chk].video = new Video(320,240);
96                chunks[chk].video.smoothing = model.config['smoothing'];
97                chunks[chk].video.visible = false;
98                chunks[chk].video.attachNetStream(chunks[chk].netstream);
99                container.addChild(chunks[chk].video);
100                next = chk;
101        };
102
103
104        /** Start playing a chunk. **/
105        private function playChunk(chk:Number=0):void {
106                chunks[chk].netstream.resume();
107                chunks[chk].video.visible = true;
108                chunk = chk;
109        };
110
111
112        /** Remove the current chunk. **/
113        private function killChunk(chk:Number):void {
114                if(chunks[chk] && chunks[chk].netstream) {
115                        chunks[chk].netstream.close();
116                }
117        };
118
119
120        /** Get the metadata, levels and chunks from the manifest. **/
121        private function loaderHandler(evt:Event):void {
122                var xml:XML = XML(evt.target.data);
123                index = SmoothParser.parseIndex(xml);
124                model.sendEvent(ModelEvent.META,index);
125                levels = SmoothParser.parseLevels(xml);
126                chunks = SmoothParser.parseChunks(xml);
127                if(levels.length == 0 || chunks.length == 0) {
128                        errorHandler(new ErrorEvent(ErrorEvent.ERROR,false,false,
129                                "SmoothStreaming manifest contains no quality levels or chunks."))
130                } else {
131                        setLevel();
132                        seek(item['start']);
133                }
134        };
135
136
137        /** Pause playback. **/
138        override public function pause():void {
139                chunks[chunk].netstream.pause();
140                clearInterval(interval);
141                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PAUSED});
142        };
143
144
145        /** Resume playing. **/
146        override public function play():void {
147                chunks[chunk].netstream.resume();
148                interval = setInterval(positionInterval,50);
149                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PLAYING});
150        };
151
152
153        /** Interval for the position progress **/
154        private function positionInterval():void {
155                var chk:Number = chunk;
156                var obj:Object = chunks[chunk];
157                position = Math.round((obj['start']+obj.netstream.time)*10)/10;
158                if(model.config['state'] != ModelStates.PLAYING) {
159                        model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.PLAYING});
160                }
161                if(position > obj['end']-0.2) {
162                        setTimeout(killChunk,2000,chk);
163                        playChunk(chk+1);
164                        setTimeout(loadChunk,1000,chk+2);
165                }
166                if(position < item['duration']) {
167                        model.sendEvent(ModelEvent.TIME,{position:position,duration:item['duration'],chunk:chk});
168                } else if (item['duration'] > 0) {
169                        obj.netstream.pause();
170                        clearInterval(interval);
171                        model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.COMPLETED});
172                }
173        };
174
175
176        /** Seek to a new position. **/
177        override public function seek(pos:Number):void {
178                position = pos;
179                clearInterval(interval);
180                killChunk(chunk);
181                killChunk(next);
182                for (var i:Number=0; i<chunks.length; i++) {
183                        if(chunks[i]['end'] > pos && chunks[i]['start'] <= pos) {
184                                loadChunk(i);
185                                playChunk(i);
186                                setTimeout(loadChunk,1000,i+1);
187                                break;
188                        }
189                }
190                interval = setInterval(positionInterval,50);
191                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.BUFFERING});
192        };
193
194
195        /** Set a specific stream level. **/
196        private function setLevel():void {
197                level = 0;
198                model.sendEvent(ModelEvent.META,levels[level]);
199        };
200
201
202        /** Destroy the video. **/
203        override public function stop():void {
204                killChunk(chunk);
205                killChunk(next);
206                clearInterval(interval);
207                position = item['start'];
208                model.sendEvent(ModelEvent.STATE,{newstate:ModelStates.IDLE});
209        };
210
211
212        /** Set the volume level. **/
213        override public function volume(vol:Number):void {
214                transform.volume = vol/100;
215                chunks[chunk].netstream.soundTransform = transform;
216        };
217
218
219};
220
221
222}
Note: See TracBrowser for help on using the repository browser.