winlin

refine player to support status change.

@@ -99,6 +99,7 @@ SrsPlayer.prototype.start = function(url) { @@ -99,6 +99,7 @@ SrsPlayer.prototype.start = function(url) {
99 flashvars.on_player_timer = "__srs_on_player_timer"; 99 flashvars.on_player_timer = "__srs_on_player_timer";
100 flashvars.on_player_empty = "__srs_on_player_empty"; 100 flashvars.on_player_empty = "__srs_on_player_empty";
101 flashvars.on_player_full = "__srs_on_player_full"; 101 flashvars.on_player_full = "__srs_on_player_full";
  102 + flashvars.on_player_status = "__srs_on_player_status";
102 103
103 var params = {}; 104 var params = {};
104 params.wmode = "opaque"; 105 params.wmode = "opaque";
@@ -146,17 +147,6 @@ SrsPlayer.prototype.play = function(url, volume) { @@ -146,17 +147,6 @@ SrsPlayer.prototype.play = function(url, volume) {
146 * stop play stream. 147 * stop play stream.
147 */ 148 */
148 SrsPlayer.prototype.stop = function() { 149 SrsPlayer.prototype.stop = function() {
149 - for (var i = 0; i < SrsPlayer.__players.length; i++) {  
150 - var player = SrsPlayer.__players[i];  
151 -  
152 - if (player.id != this.id) {  
153 - continue;  
154 - }  
155 -  
156 - SrsPlayer.__players.splice(i, 1);  
157 - break;  
158 - }  
159 -  
160 this.callbackObj.ref.__stop(); 150 this.callbackObj.ref.__stop();
161 } 151 }
162 /** 152 /**
@@ -301,6 +291,16 @@ SrsPlayer.prototype.on_player_empty = function(time) { @@ -301,6 +291,16 @@ SrsPlayer.prototype.on_player_empty = function(time) {
301 SrsPlayer.prototype.on_player_full = function(time) { 291 SrsPlayer.prototype.on_player_full = function(time) {
302 // ignore. 292 // ignore.
303 } 293 }
  294 +/**
  295 + * the callback when player status change.
  296 + * @param code the status code, "init", "connected", "play", "closed", "rejected", "failed".
  297 + * init => connected/rejected/failed
  298 + * connected => play/rejected => closed
  299 + * @param desc the description for the status.
  300 + */
  301 +SrsPlayer.prototype.on_player_status = function(code, desc) {
  302 + // ignore.
  303 +}
304 304
305 /** 305 /**
306 * helpers. 306 * helpers.
@@ -359,3 +359,21 @@ function __srs_on_player_full(id, time) { @@ -359,3 +359,21 @@ function __srs_on_player_full(id, time) {
359 player.__fluency.on_stream_full(time); 359 player.__fluency.on_stream_full(time);
360 player.on_player_full(time); 360 player.on_player_full(time);
361 } 361 }
  362 +function __srs_on_player_status(id, code, desc) {
  363 + var player = __srs_find_player(id);
  364 + player.on_player_status(code, desc);
  365 +
  366 + if (code != "closed") {
  367 + return;
  368 + }
  369 + for (var i = 0; i < SrsPlayer.__players.length; i++) {
  370 + var player = SrsPlayer.__players[i];
  371 +
  372 + if (player.id != this.id) {
  373 + continue;
  374 + }
  375 +
  376 + SrsPlayer.__players.splice(i, 1);
  377 + break;
  378 + }
  379 +}
@@ -514,6 +514,9 @@ @@ -514,6 +514,9 @@
514 select_buffer(0.5); 514 select_buffer(0.5);
515 this.play(url); 515 this.play(url);
516 }; 516 };
  517 + srs_player.on_player_status = function(code, desc) {
  518 + //console.log("[播放器状态] code=" + code + ", desc=" + desc);
  519 + };
517 srs_player.on_player_metadata = function(metadata) { 520 srs_player.on_player_metadata = function(metadata) {
518 $("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")"); 521 $("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")");
519 if (metadata.ip && metadata.pid && metadata.cid) { 522 if (metadata.ip && metadata.pid && metadata.cid) {
1 package 1 package
2 { 2 {
3 import flash.events.Event; 3 import flash.events.Event;
  4 + import flash.events.IOErrorEvent;
  5 + import flash.events.NetStatusEvent;
4 import flash.events.ProgressEvent; 6 import flash.events.ProgressEvent;
  7 + import flash.events.SecurityErrorEvent;
5 import flash.external.ExternalInterface; 8 import flash.external.ExternalInterface;
6 import flash.net.NetConnection; 9 import flash.net.NetConnection;
7 import flash.net.NetStream; 10 import flash.net.NetStream;
@@ -28,6 +31,8 @@ package @@ -28,6 +31,8 @@ package
28 // play param url. 31 // play param url.
29 private var user_url:String = null; 32 private var user_url:String = null;
30 33
  34 + private var conn:NetConnection = null;
  35 +
31 /** 36 /**
32 * create stream to play hls. 37 * create stream to play hls.
33 * @param m3u8_refresh_ratio, for example, 0.5, fetch m3u8 every 0.5*ts_duration. 38 * @param m3u8_refresh_ratio, for example, 0.5, fetch m3u8 every 0.5*ts_duration.
@@ -40,6 +45,7 @@ package @@ -40,6 +45,7 @@ package
40 this.m3u8_refresh_ratio = m3u8_refresh_ratio; 45 this.m3u8_refresh_ratio = m3u8_refresh_ratio;
41 this.ts_parse_async_interval = ts_parse_async_interval; 46 this.ts_parse_async_interval = ts_parse_async_interval;
42 hls = new HlsCodec(this); 47 hls = new HlsCodec(this);
  48 + this.conn = conn;
43 } 49 }
44 50
45 /** 51 /**
@@ -94,6 +100,8 @@ package @@ -94,6 +100,8 @@ package
94 refresh_ts(); 100 refresh_ts();
95 }) 101 })
96 } 102 }
  103 +
  104 + private var metadata:Object = null;
97 private function refresh_ts():void { 105 private function refresh_ts():void {
98 // all ts parsed. 106 // all ts parsed.
99 if (parsed_ts_seq_no >= hls.seq_no + hls.tsCount) { 107 if (parsed_ts_seq_no >= hls.seq_no + hls.tsCount) {
@@ -125,10 +133,15 @@ package @@ -125,10 +133,15 @@ package
125 //log("uv[" + k + "]=" + v); 133 //log("uv[" + k + "]=" + v);
126 } 134 }
127 135
  136 + // ignore when not changed.
  137 + if (!metadata || metadata.srs_server_ip != obj.srs_server_ip || metadata.srs_id != obj.srs_id || metadata.srs_pid != obj.srs_pid) {
128 if (client && client.hasOwnProperty("onMetaData")) { 138 if (client && client.hasOwnProperty("onMetaData")) {
  139 + log("got metadata for url " + uri);
129 client.onMetaData(obj); 140 client.onMetaData(obj);
130 } 141 }
131 } 142 }
  143 + metadata = obj;
  144 + }
132 145
133 download(uri, function(stream:ByteArray):void{ 146 download(uri, function(stream:ByteArray):void{
134 log("got ts seqno=" + parsed_ts_seq_no + ", " + stream.length + " bytes"); 147 log("got ts seqno=" + parsed_ts_seq_no + ", " + stream.length + " bytes");
@@ -177,6 +190,14 @@ package @@ -177,6 +190,14 @@ package
177 completed(stream); 190 completed(stream);
178 }); 191 });
179 192
  193 + url.addEventListener(IOErrorEvent.IO_ERROR, function(evt:IOErrorEvent):void{
  194 + onPlayFailed(evt);
  195 + });
  196 +
  197 + url.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(evt:SecurityErrorEvent):void{
  198 + onPlayRejected(evt);
  199 + });
  200 +
180 // we set to the query. 201 // we set to the query.
181 uri += ((uri.indexOf("?") == -1)? "?":"&") + "shp_xpsid=" + XPlaybackSessionId; 202 uri += ((uri.indexOf("?") == -1)? "?":"&") + "shp_xpsid=" + XPlaybackSessionId;
182 var r:URLRequest = new URLRequest(uri); 203 var r:URLRequest = new URLRequest(uri);
@@ -219,6 +240,34 @@ package @@ -219,6 +240,34 @@ package
219 log("FLV: sps/pps " + flvHeader.length + " bytes"); 240 log("FLV: sps/pps " + flvHeader.length + " bytes");
220 241
221 writeFlv(flvHeader); 242 writeFlv(flvHeader);
  243 + onPlayStart();
  244 + }
  245 +
  246 + private function onPlayStart():void {
  247 + log("dispatch NetStream.Play.Start.");
  248 + dispatchEvent(new NetStatusEvent(NetStatusEvent.NET_STATUS, false, false, {
  249 + code: "NetStream.Play.Start",
  250 + stream: user_url,
  251 + descrption: "play start"
  252 + }));
  253 + }
  254 +
  255 + private function onPlayFailed(evt:IOErrorEvent):void {
  256 + log("dispatch NetConnection.Connect.Failed.");
  257 + this.conn.dispatchEvent(new NetStatusEvent(NetStatusEvent.NET_STATUS, false, false, {
  258 + code: "NetConnection.Connect.Failed",
  259 + stream: user_url,
  260 + descrption: evt.text
  261 + }));
  262 + }
  263 +
  264 + private function onPlayRejected(evt:SecurityErrorEvent):void {
  265 + log("dispatch NetConnection.Connect.Rejected.");
  266 + this.conn.dispatchEvent(new NetStatusEvent(NetStatusEvent.NET_STATUS, false, false, {
  267 + code: "NetConnection.Connect.Rejected",
  268 + stream: user_url,
  269 + descrption: evt.text
  270 + }));
222 } 271 }
223 272
224 private function onFlvBody(uri:String, flv:ByteArray):void { 273 private function onFlvBody(uri:String, flv:ByteArray):void {
@@ -58,7 +58,18 @@ package @@ -58,7 +58,18 @@ package
58 return this.media_stream; 58 return this.media_stream;
59 } 59 }
60 60
  61 + private function dumps_object(obj:Object):String {
  62 + var smr:String = "";
  63 + for (var k:String in obj) {
  64 + smr += k + "=" + obj[k] + ", ";
  65 + }
  66 +
  67 + return smr;
  68 + }
  69 +
61 public function play(url:String):void { 70 public function play(url:String):void {
  71 + owner.on_player_status("init", "Ready to play");
  72 +
62 var streamName:String; 73 var streamName:String;
63 this.user_url = url; 74 this.user_url = url;
64 75
@@ -66,7 +77,8 @@ package @@ -66,7 +77,8 @@ package
66 this.media_conn.client = {}; 77 this.media_conn.client = {};
67 this.media_conn.client.onBWDone = function():void {}; 78 this.media_conn.client.onBWDone = function():void {};
68 this.media_conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void { 79 this.media_conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
69 - log("NetConnection: code=" + evt.info.code); 80 + log("NetConnection: type=" + evt.type + ", bub=" + evt.bubbles + ", can=" + evt.cancelable
  81 + + ", info is " + dumps_object(evt.info));
70 82
71 if (evt.info.hasOwnProperty("data") && evt.info.data) { 83 if (evt.info.hasOwnProperty("data") && evt.info.data) {
72 owner.on_player_metadata(evt.info.data); 84 owner.on_player_metadata(evt.info.data);
@@ -87,6 +99,20 @@ package @@ -87,6 +99,20 @@ package
87 owner.on_player_302(url); 99 owner.on_player_302(url);
88 return; 100 return;
89 } 101 }
  102 +
  103 + owner.on_player_status("rejected", "Server reject play");
  104 + close();
  105 + }
  106 +
  107 + if (evt.info.code == "NetConnection.Connect.Success") {
  108 + owner.on_player_status("connected", "Connected at server");
  109 + }
  110 + if (evt.info.code == "NetConnection.Connect.Closed") {
  111 + close();
  112 + }
  113 + if (evt.info.code == "NetConnection.Connect.Failed") {
  114 + owner.on_player_status("failed", "Connect to server failed.");
  115 + close();
90 } 116 }
91 117
92 // TODO: FIXME: failed event. 118 // TODO: FIXME: failed event.
@@ -100,7 +126,16 @@ package @@ -100,7 +126,16 @@ package
100 media_stream = new NetStream(media_conn); 126 media_stream = new NetStream(media_conn);
101 } 127 }
102 media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void { 128 media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
103 - log("NetStream: code=" + evt.info.code); 129 + log("NetStream: type=" + evt.type + ", bub=" + evt.bubbles + ", can=" + evt.cancelable
  130 + + ", info is " + dumps_object(evt.info));
  131 +
  132 + if (evt.info.code == "NetStream.Play.Start") {
  133 + owner.on_player_status("play", "Start to play stream");
  134 + }
  135 + if (evt.info.code == "NetStream.Play.StreamNotFound") {
  136 + owner.on_player_status("rejected", "Stream not found");
  137 + close();
  138 + }
104 139
105 if (evt.info.code == "NetStream.Video.DimensionChange") { 140 if (evt.info.code == "NetStream.Video.DimensionChange") {
106 owner.on_player_dimension_change(); 141 owner.on_player_dimension_change();
@@ -149,13 +184,22 @@ package @@ -149,13 +184,22 @@ package
149 } 184 }
150 185
151 public function close():void { 186 public function close():void {
  187 + var notify:Boolean = false;
  188 +
152 if (this.media_stream) { 189 if (this.media_stream) {
153 this.media_stream.close(); 190 this.media_stream.close();
154 this.media_stream = null; 191 this.media_stream = null;
  192 + notify = true;
155 } 193 }
  194 +
156 if (this.media_conn) { 195 if (this.media_conn) {
157 this.media_conn.close(); 196 this.media_conn.close();
158 this.media_conn = null; 197 this.media_conn = null;
  198 + notify = true;
  199 + }
  200 +
  201 + if (notify) {
  202 + owner.on_player_status("closed", "Server closed.");
159 } 203 }
160 } 204 }
161 205
@@ -33,6 +33,7 @@ package @@ -33,6 +33,7 @@ package
33 private var js_on_player_timer:String = null; 33 private var js_on_player_timer:String = null;
34 private var js_on_player_empty:String = null; 34 private var js_on_player_empty:String = null;
35 private var js_on_player_full:String = null; 35 private var js_on_player_full:String = null;
  36 + private var js_on_player_status:String = null;
36 37
37 // play param, user set width and height 38 // play param, user set width and height
38 private var user_w:int = 0; 39 private var user_w:int = 0;
@@ -105,6 +106,7 @@ package @@ -105,6 +106,7 @@ package
105 this.js_on_player_timer = flashvars.on_player_timer; 106 this.js_on_player_timer = flashvars.on_player_timer;
106 this.js_on_player_empty = flashvars.on_player_empty; 107 this.js_on_player_empty = flashvars.on_player_empty;
107 this.js_on_player_full = flashvars.on_player_full; 108 this.js_on_player_full = flashvars.on_player_full;
  109 + this.js_on_player_status = flashvars.on_player_status;
108 110
109 this.media_timer.addEventListener(TimerEvent.TIMER, this.system_on_timer); 111 this.media_timer.addEventListener(TimerEvent.TIMER, this.system_on_timer);
110 this.media_timer.start(); 112 this.media_timer.start();
@@ -498,6 +500,11 @@ package @@ -498,6 +500,11 @@ package
498 system_on_buffer_full(); 500 system_on_buffer_full();
499 } 501 }
500 502
  503 + public function on_player_status(code:String, desc:String):void {
  504 + log("[STATUS] code=" + code + ", desc=" + desc);
  505 + flash.external.ExternalInterface.call(this.js_on_player_status, this.js_id, code, desc);
  506 + }
  507 +
501 /** 508 /**
502 * get the "right" size of video, 509 * get the "right" size of video,
503 * 1. initialize with the original video object size. 510 * 1. initialize with the original video object size.