Blame view

trunk/research/players/srs_player/src/srs_player.as 21.0 KB
1 2 3
package
{
    import flash.display.Sprite;
winlin authored
4
    import flash.display.StageAlign;
5
    import flash.display.StageDisplayState;
winlin authored
6 7
    import flash.display.StageScaleMode;
    import flash.events.Event;
8 9
    import flash.events.FullScreenEvent;
    import flash.events.MouseEvent;
winlin authored
10
    import flash.events.NetStatusEvent;
11
    import flash.events.TimerEvent;
winlin authored
12
    import flash.external.ExternalInterface;
13
    import flash.media.SoundTransform;
winlin authored
14 15 16
    import flash.media.Video;
    import flash.net.NetConnection;
    import flash.net.NetStream;
17
    import flash.system.Security;
winlin authored
18 19
    import flash.ui.ContextMenu;
    import flash.ui.ContextMenuItem;
20
    import flash.utils.Timer;
winlin authored
21
    import flash.utils.setTimeout;
22
    
23 24
    import flashx.textLayout.formats.Float;
    
25 26
    public class srs_player extends Sprite
    {
27
        // user set id.
winlin authored
28
        private var js_id:String = null;
29
        // user set callback
winlin authored
30 31 32
        private var js_on_player_ready:String = null;
        private var js_on_player_metadata:String = null;
        private var js_on_player_timer:String = null;
winlin authored
33
        
34
        // play param url.
winlin authored
35
        private var user_url:String = null;
36
        // play param, user set width and height
winlin authored
37 38
        private var user_w:int = 0;
        private var user_h:int = 0;
39
        // user set dar den:num
winlin authored
40
        private var user_dar_den:int = 0;
41
        private var user_dar_num:int = 0;
42
        // user set fs(fullscreen) refer and percent.
winlin authored
43 44
        private var user_fs_refer:String = null;
        private var user_fs_percent:int = 0;
winlin authored
45
        
winlin authored
46 47 48 49 50 51
        // media specified.
        private var media_conn:NetConnection = null;
        private var media_stream:NetStream = null;
        private var media_video:Video = null;
        private var media_metadata:Object = {};
        private var media_timer:Timer = new Timer(300);
winlin authored
52
        
winlin authored
53
        // controls.
54 55
        // flash donot allow js to set to fullscreen,
        // only allow user click to enter fullscreen.
winlin authored
56
        private var control_fs_mask:Sprite = new Sprite();
57
        
58 59
        public function srs_player()
        {
winlin authored
60
            if (!this.stage) {
winlin authored
61
                this.addEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage);
winlin authored
62
            } else {
winlin authored
63
                this.system_on_add_to_stage(null);
winlin authored
64 65 66
            }
        }
        
winlin authored
67 68 69 70 71 72 73
        /**
        * system event callback, when this control added to stage.
        * the main function.
        */
        private function system_on_add_to_stage(evt:Event):void {
            this.removeEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage);
            
winlin authored
74 75 76
            this.stage.align = StageAlign.TOP_LEFT;
            this.stage.scaleMode = StageScaleMode.NO_SCALE;
            
winlin authored
77
            this.stage.addEventListener(FullScreenEvent.FULL_SCREEN, this.user_on_stage_fullscreen);
78
            
winlin authored
79 80 81
            this.addChild(this.control_fs_mask);
            this.control_fs_mask.buttonMode = true;
            this.control_fs_mask.addEventListener(MouseEvent.CLICK, user_on_click_video);
82
            
winlin authored
83 84 85 86 87 88 89 90 91
            this.contextMenu = new ContextMenu();
            this.contextMenu.hideBuiltInItems();
            
            var flashvars:Object = this.root.loaderInfo.parameters;
            
            if (!flashvars.hasOwnProperty("id")) {
                throw new Error("must specifies the id");
            }
            
winlin authored
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
            this.js_id = flashvars.id;
            this.js_on_player_ready = flashvars.on_player_ready;
            this.js_on_player_metadata = flashvars.on_player_metadata;
            this.js_on_player_timer = flashvars.on_player_timer;
            
            this.media_timer.addEventListener(TimerEvent.TIMER, this.system_on_timer);
            this.media_timer.start();
            
            flash.utils.setTimeout(this.system_on_js_ready, 0);
        }
        
        /**
         * system callack event, when js ready, register callback for js.
         * the actual main function.
         */
        private function system_on_js_ready():void {
            if (!flash.external.ExternalInterface.available) {
                trace("js not ready, try later.");
                flash.utils.setTimeout(this.system_on_js_ready, 100);
                return;
            }
113
            
winlin authored
114 115 116 117
            flash.external.ExternalInterface.addCallback("__play", this.js_call_play);
            flash.external.ExternalInterface.addCallback("__stop", this.js_call_stop);
            flash.external.ExternalInterface.addCallback("__pause", this.js_call_pause);
            flash.external.ExternalInterface.addCallback("__resume", this.js_call_resume);
winlin authored
118
            flash.external.ExternalInterface.addCallback("__set_dar", this.js_call_set_dar);
winlin authored
119 120
            flash.external.ExternalInterface.addCallback("__set_fs", this.js_call_set_fs_size);
            flash.external.ExternalInterface.addCallback("__set_bt", this.js_call_set_bt);
winlin authored
121
            
winlin authored
122
            flash.external.ExternalInterface.call(this.js_on_player_ready, this.js_id);
winlin authored
123 124
        }
        
winlin authored
125 126 127 128 129
        /**
        * system callack event, timer to do some regular tasks.
        */
        private function system_on_timer(evt:TimerEvent):void {
            if (!this.media_stream) {
130 131 132 133 134 135
                trace("stream is null, ignore timer event.");
                return;
            }
            
            trace("notify js the timer event.");
            flash.external.ExternalInterface.call(
winlin authored
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
                this.js_on_player_timer, this.js_id, this.media_stream.time, this.media_stream.bufferLength);
        }
        
        /**
         * system callack event, when got metadata from stream.
         * or got video dimension change event(the DAR notification), to update the metadata manually.
         */
        private function system_on_metadata(metadata:Object):void {
            this.media_metadata = metadata;
            
            // for js.
            var obj:Object = __get_video_size_object();
            
            obj.server = 'srs';
            obj.contributor = 'winlin';
            
152 153
            if (srs_server != null) {
                obj.server = srs_server;
winlin authored
154
            }
155 156 157 158 159
            if (srs_primary != null) {
                obj.contributor = srs_primary;
            }
            if (srs_authors != null) {
                obj.contributor = srs_authors;
winlin authored
160 161 162 163 164 165
            }
            
            var code:int = flash.external.ExternalInterface.call(js_on_player_metadata, js_id, obj);
            if (code != 0) {
                throw new Error("callback on_player_metadata failed. code=" + code);
            }
166 167
        }
        
winlin authored
168 169 170 171
        /**
         * player callack event, when user click video to enter or leave fullscreen.
         */
        private function user_on_stage_fullscreen(evt:FullScreenEvent):void {
172
            if (!evt.fullScreen) {
winlin authored
173
                __execute_user_set_dar();
174
            } else {
winlin authored
175
                __execute_user_enter_fullscreen();
176 177 178
            }
        }
        
winlin authored
179 180 181 182 183 184
        /**
         * user event callback, js cannot enter the fullscreen mode, user must click to.
         */
        private function user_on_click_video(evt:MouseEvent):void {
            if (!this.stage.allowsFullScreen) {
                trace("donot allow fullscreen.");
winlin authored
185 186 187
                return;
            }
            
winlin authored
188 189 190 191 192 193
            // enter fullscreen to get the fullscreen size correctly.
            if (this.stage.displayState == StageDisplayState.FULL_SCREEN) {
                this.stage.displayState = StageDisplayState.NORMAL;
            } else {
                this.stage.displayState = StageDisplayState.FULL_SCREEN;
            }
winlin authored
194 195
        }
        
winlin authored
196 197 198
        /**
         * function for js to call: to pause the stream. ignore if not play.
         */
199
        private function js_call_pause():void {
winlin authored
200 201
            if (this.media_stream) {
                this.media_stream.pause();
winlin authored
202 203 204
            }
        }
        
winlin authored
205 206 207
        /**
         * function for js to call: to resume the stream. ignore if not play.
         */
208
        private function js_call_resume():void {
winlin authored
209 210
            if (this.media_stream) {
                this.media_stream.resume();
winlin authored
211 212 213
            }
        }
        
214
        /**
215 216 217 218 219 220 221
        * to set the DAR, for example, DAR=16:9 where num=16,den=9.
        * @param num, for example, 16. 
        *       use metadata width if 0.
        *       use user specified width if -1.
        * @param den, for example, 9. 
        *       use metadata height if 0.
        *       use user specified height if -1.
222
         */
winlin authored
223
        private function js_call_set_dar(num:int, den:int):void {
winlin authored
224 225
            user_dar_num = num;
            user_dar_den = den;
226
            
winlin authored
227
            flash.utils.setTimeout(__execute_user_set_dar, 0);
228 229 230 231 232 233 234 235 236 237
        }
        
        /**
         * set the fullscreen size data.
         * @refer the refer fullscreen mode. it can be:
         *       video: use video orignal size.
         *       screen: use screen size to rescale video.
         * @param percent, the rescale percent, where
         *       100 means 100%.
         */
238
        private function js_call_set_fs_size(refer:String, percent:int):void {
winlin authored
239 240
            user_fs_refer = refer;
            user_fs_percent = percent;
241 242
        }
        
243 244 245 246 247
        /**
         * set the stream buffer time in seconds.
         * @buffer_time the buffer time in seconds.
         */
        private function js_call_set_bt(buffer_time:Number):void {
winlin authored
248 249
            if (this.media_stream) {
                this.media_stream.bufferTime = buffer_time;
250 251 252
            }
        }
        
winlin authored
253 254 255
        /**
         * function for js to call: to stop the stream. ignore if not play.
         */
256
        private function js_call_stop():void {
winlin authored
257 258 259 260
            if (this.media_video) {
                this.removeChild(this.media_video);
                this.media_video = null;
            }
winlin authored
261 262 263
            if (this.media_stream) {
                this.media_stream.close();
                this.media_stream = null;
winlin authored
264
            }
winlin authored
265 266 267
            if (this.media_conn) {
                this.media_conn.close();
                this.media_conn = null;
winlin authored
268 269 270
            }
        }
        
271 272
        // srs infos
        private var srs_server:String = null;
273 274
        private var srs_primary:String = null;
        private var srs_authors:String = null;
275
        private var srs_id:String = null;
276
        private var srs_pid:String = null;
277
        private var srs_server_ip:String = null;
278 279 280 281 282 283
        private function update_context_items():void {
            // for context menu
            var customItems:Array = [new ContextMenuItem("SrsPlayer")];
            if (srs_server != null) {
                customItems.push(new ContextMenuItem("Server: " + srs_server));
            }
284 285 286 287 288
            if (srs_primary != null) {
                customItems.push(new ContextMenuItem("PrimaryAuthors: " + srs_primary));
            }
            if (srs_authors != null) {
                customItems.push(new ContextMenuItem("Authors: " + srs_authors));
289
            }
290 291 292
            if (srs_server_ip != null) {
                customItems.push(new ContextMenuItem("SrsIp: " + srs_server_ip));
            }
293 294 295
            if (srs_pid != null) {
                customItems.push(new ContextMenuItem("SrsPid: " + srs_pid));
            }
296 297 298 299 300 301
            if (srs_id != null) {
                customItems.push(new ContextMenuItem("SrsId: " + srs_id));
            }
            contextMenu.customItems = customItems;
        }
        
winlin authored
302 303 304 305 306 307
        /**
         * function for js to call: to play the stream. stop then play.
         * @param url, the rtmp/http url to play.
         * @param _width, the player width.
         * @param _height, the player height.
         * @param buffer_time, the buffer time in seconds. recommend to >=0.5
308
         * @param volume, the volume, 0 is mute, 1 is 100%, 2 is 200%.
winlin authored
309
         */
310
        private function js_call_play(url:String, _width:int, _height:int, buffer_time:Number, volume:Number):void {
winlin authored
311 312 313 314
            this.user_url = url;
            this.user_w = _width;
            this.user_h = _height;
            trace("start to play url: " + this.user_url + ", w=" + this.user_w + ", h=" + this.user_h);
315
            
winlin authored
316 317
            js_call_stop();
            
winlin authored
318 319 320 321
            this.media_conn = new NetConnection();
            this.media_conn.client = {};
            this.media_conn.client.onBWDone = function():void {};
            this.media_conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
winlin authored
322
                trace ("NetConnection: code=" + evt.info.code);
323
                
324 325
                if (evt.info.hasOwnProperty("data") && evt.info.data) {
                    if (evt.info.data.hasOwnProperty("srs_server")) {
326
                        srs_server = evt.info.data.srs_server;
327
                    }
328 329 330 331 332
                    if (evt.info.data.hasOwnProperty("srs_primary")) {
                        srs_primary = evt.info.data.srs_primary;
                    }
                    if (evt.info.data.hasOwnProperty("srs_authors")) {
                        srs_authors = evt.info.data.srs_authors;
333 334 335
                    }
                    if (evt.info.data.hasOwnProperty("srs_id")) {
                        srs_id = evt.info.data.srs_id;
336
                    }
337 338 339
                    if (evt.info.data.hasOwnProperty("srs_pid")) {
                        srs_pid = evt.info.data.srs_pid;
                    }
340 341 342
                    if (evt.info.data.hasOwnProperty("srs_server_ip")) {
                        srs_server_ip = evt.info.data.srs_server_ip;
                    }
343
                    update_context_items();
344 345
                }
                
346
                // TODO: FIXME: failed event.
winlin authored
347 348 349 350
                if (evt.info.code != "NetConnection.Connect.Success") {
                    return;
                }
                
winlin authored
351
                media_stream = new NetStream(media_conn);
352
                media_stream.soundTransform = new SoundTransform(volume);
winlin authored
353 354 355 356
                media_stream.bufferTime = buffer_time;
                media_stream.client = {};
                media_stream.client.onMetaData = system_on_metadata;
                media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
winlin authored
357
                    trace ("NetStream: code=" + evt.info.code);
358 359
                    
                    if (evt.info.code == "NetStream.Video.DimensionChange") {
winlin authored
360
                        system_on_metadata(media_metadata);
361
                    }
362 363
                    
                    // TODO: FIXME: failed event.
winlin authored
364
                });
365
                
366
                if (url.indexOf("http") == 0) {
winlin authored
367
                    media_stream.play(url);
368
                } else {
369
                    var streamName:String = url.substr(url.lastIndexOf("/") + 1);
winlin authored
370
                    media_stream.play(streamName);
371
                }
winlin authored
372
                
winlin authored
373 374 375 376 377 378
                media_video = new Video();
                media_video.width = _width;
                media_video.height = _height;
                media_video.attachNetStream(media_stream);
                media_video.smoothing = true;
                addChild(media_video);
379
                
winlin authored
380
                __draw_black_background(_width, _height);
381 382
                
                // lowest layer, for mask to cover it.
winlin authored
383
                setChildIndex(media_video, 0);
winlin authored
384 385
            });
            
386
            if (url.indexOf("http") == 0) {
winlin authored
387
                this.media_conn.connect(null);
388
            } else {
winlin authored
389
                var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/"));
390
                this.media_conn.connect(tcUrl);
391 392 393 394 395 396 397 398 399
            }
        }
        
        /**
        * get the "right" size of video,
        * 1. initialize with the original video object size.
        * 2. override with metadata size if specified.
        * 3. override with codec size if specified.
        */
winlin authored
400
        private function __get_video_size_object():Object {
401
            var obj:Object = {
winlin authored
402 403
                width: media_video.width,
                height: media_video.height
404 405
            };
            
406
            // override with metadata size.
winlin authored
407 408
            if (this.media_metadata.hasOwnProperty("width")) {
                obj.width = this.media_metadata.width;
409
            }
winlin authored
410 411
            if (this.media_metadata.hasOwnProperty("height")) {
                obj.height = this.media_metadata.height;
412 413
            }
            
414
            // override with codec size.
winlin authored
415 416
            if (media_video.videoWidth > 0) {
                obj.width = media_video.videoWidth;
417
            }
winlin authored
418 419
            if (media_video.videoHeight > 0) {
                obj.height = media_video.videoHeight;
420 421
            }
            
422 423 424 425 426 427
            return obj;
        }
        
        /**
        * execute the enter fullscreen action.
        */
winlin authored
428 429
        private function __execute_user_enter_fullscreen():void {
            if (!user_fs_refer || user_fs_percent <= 0) {
430
                return;
431
            }
432 433
            
            // change to video size if refer to video.
winlin authored
434
            var obj:Object = __get_video_size_object();
435 436
            
            // get the DAR
winlin authored
437
            var den:int = user_dar_den;
438
            var num:int = user_dar_num;
439
            
440 441
            if (den == 0) {
                den = obj.height;
442
            }
443 444
            if (den == -1) {
                den = this.stage.fullScreenHeight;
445 446
            }
            
447 448
            if (num == 0) {
                num = obj.width;
449
            }
450 451
            if (num == -1) {
                num = this.stage.fullScreenWidth;
452 453 454
            }
                
            // for refer is screen.
winlin authored
455
            if (user_fs_refer == "screen") {
456 457 458 459
                obj = {
                    width: this.stage.fullScreenWidth,
                    height: this.stage.fullScreenHeight
                };
460
            }
461 462
            
            // rescale to fs
winlin authored
463
            __update_video_size(num, den, obj.width * user_fs_percent / 100, obj.height * user_fs_percent / 100, this.stage.fullScreenWidth, this.stage.fullScreenHeight);
464 465 466 467 468
        }
        
        /**
         * for user set dar, or leave fullscreen to recover the dar.
         */
winlin authored
469
        private function __execute_user_set_dar():void {
470
            // get the DAR
winlin authored
471
            var den:int = user_dar_den;
472
            var num:int = user_dar_num;
473
            
winlin authored
474
            var obj:Object = __get_video_size_object();
475
            
476 477
            if (den == 0) {
                den = obj.height;
478
            }
479 480
            if (den == -1) {
                den = this.user_h;
481 482
            }
            
483 484
            if (num == 0) {
                num = obj.width;
485
            }
486 487
            if (num == -1) {
                num = this.user_w;
488 489
            }
            
winlin authored
490
            __update_video_size(num, den, this.user_w, this.user_h, this.user_w, this.user_h);
491 492 493 494 495 496 497 498 499 500 501
        }
        
        /**
        * update the video width and height, 
        * according to the specifies DAR(den:num) and max size(w:h).
        * set the position of video(x,y) specifies by size(sw:sh),
        * and update the bg to size(sw:sh).
        * @param _num/_den the DAR. use to rescale the player together with paper size.
        * @param _w/_h the video draw paper size. used to rescale the player together with DAR.
        * @param _sw/_wh the stage size, >= paper size. used to center the player.
        */
winlin authored
502
        private function __update_video_size(_num:int, _den:int, _w:int, _h:int, _sw:int, _sh:int):void {
503
            if (!this.media_video || _den <= 0 || _num <= 0) {
504 505 506 507 508
                return;
            }
            
            // set DAR.
            // calc the height by DAR
509
            var _height:int = _w * _den / _num;
510
            if (_height <= _h) {
winlin authored
511 512
                this.media_video.width = _w;
                this.media_video.height = _height;
513 514
            } else {
                // height overflow, calc the width by DAR
515
                var _width:int = _h * _num / _den;
516
                
winlin authored
517 518
                this.media_video.width = _width;
                this.media_video.height = _h;
519 520 521
            }
            
            // align center.
winlin authored
522 523
            this.media_video.x = (_sw - this.media_video.width) / 2;
            this.media_video.y = (_sh - this.media_video.height) / 2;
524
            
winlin authored
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
            __draw_black_background(_sw, _sh);
        }
        
        /**
        * draw black background and draw the fullscreen mask.
        */
        private function __draw_black_background(_width:int, _height:int):void {
            // draw black bg.
            this.graphics.beginFill(0x00, 1.0);
            this.graphics.drawRect(0, 0, _width, _height);
            this.graphics.endFill();
            
            // draw the fs mask.
            this.control_fs_mask.graphics.beginFill(0xff0000, 0);
            this.control_fs_mask.graphics.drawRect(0, 0, _width, _height);
            this.control_fs_mask.graphics.endFill();
541
        }
542 543
    }
}