package { import flash.events.NetStatusEvent; import flash.external.ExternalInterface; import flash.net.NetConnection; import flash.net.ObjectEncoding; import flash.utils.clearTimeout; import flash.utils.setTimeout; /** * SRS bandwidth check/test library, * user can copy this file and use it directly, * this library will export as callback functions, and js callback functions. * * Usage: * var bandwidth:SrsBandwidth = new SrsBandwidth(); * bandwidth.initialize(......); // required * bandwidth.check_bandwidth(......); // required * bandwidth.stop(); // optional * * @remark we donot use event, but use callback functions set by initialize. */ public class SrsBandwidth { /** * server notice client to do the downloading/play bandwidth test. */ public static const StatusSrsBwtcPlayStart:String = "srs.bwtc.play.start"; /** * server notice client to complete the downloading/play bandwidth test. */ public static const StatusSrsBwtcPlayStop:String = "srs.bwtc.play.stop"; /** * server notice client to do the uploading/publish bandwidth test. */ public static const StatusSrsBwtcPublishStart:String = "srs.bwtc.publish.start"; /** * server notice client to complete the uploading/publish bandwidth test. */ public static const StatusSrsBwtcPublishStop:String = "srs.bwtc.publish.stop"; /** * constructor, do nothing */ public function SrsBandwidth() { } /** * initialize the bandwidth test tool, the callbacks. null to ignore. * * the as callbacks. * @param as_on_ready, function():void, callback when bandwidth tool is ready to run. * @param as_on_status_change, function(code:String, data:String):void, where: * code can be: * "NetConnection.Connect.Failed", see NetStatusEvent(evt.info.code). * "NetConnection.Connect.Rejected", see NetStatusEvent(evt.info.code). * "NetConnection.Connect.Success", see NetStatusEvent(evt.info.code). * "NetConnection.Connect.Closed", see NetStatusEvent(evt.info.code). * SrsBandwidth.StatusSrsBwtcPlayStart, "srs.bwtc.play.start", when srs start test play bandwidth. * SrsBandwidth.StatusSrsBwtcPlayStop, "srs.bwtc.play.stop", when srs complete test play bandwidth. * SrsBandwidth.StatusSrsBwtcPublishStart, "srs.bwtc.publish.start", when srs start test publish bandwidth. * SrsBandwidth.StatusSrsBwtcPublishStop, "srs.bwtc.publish.stop", when srs complete test publish bandwidth. * data is extra parameter: * kbps, for code is SrsBandwidth.StatusSrsBwtcPlayStop or SrsBandwidth.StatusSrsBwtcPublishStop. * "", otherwise empty string. * @param as_on_progress_change, function(percent:Number):void, where: * percent, the progress percent, 0 means 0%, 100 means 100%. * @param as_on_srs_info, function(srs_server:String, srs_primary_authors:String, srs_id:String, srs_pid:String, srs_server_ip:String):void, where: * srs_server: the srs server info. * srs_primary_authors: the srs version info. * srs_id: the tracable log id, to direclty grep the log.. * srs_pid: the srs process id, to direclty grep the log. * srs_server_ip: the srs server ip, where client connected at. * @param as_on_complete, function(start_time:Number, end_time:Number, play_kbps:Number, publish_kbps:Number, play_bytes:Number, publish_bytes:Number, play_time:Number, publish_time:Number):void, where * start_time, the start timestamp, in ms. * end_time, the finish timestamp, in ms. * play_kbps, the play/downloading kbps. * publish_kbps, the publish/uploading kbps. * play_bytes, the bytes play/download from server, in bytes. * publish_bytes, the bytes publish/upload to server, in bytes. * play_time, the play/download duration time, in ms. * publish_time, the publish/upload duration time, in ms. * * the js callback id. * @param js_id, specifies the id of swfobject, used to identify the bandwidth object. * for all js callback, the first param always be the js_id, to identify the callback object. * * the js callbacks. * @param js_on_ready, function(js_id:String):void, callback when bandwidth tool is ready to run. * @param js_on_status_change, function(js_id:String, code:String, data:String):void * @param as_on_progress_change, function(js_id:String, percent:Number):void * @param as_on_srs_info, function(js_id:String, srs_server:String, srs_primary_authors:String, srs_id:String, srs_pid:String, srs_server_ip:String):void * @param as_on_complete, function(js_id:String, start_time:Number, end_time:Number, play_kbps:Number, publish_kbps:Number, play_bytes:Number, publish_bytes:Number, play_time:Number, publish_time:Number):void * * the js export functions. * @param js_export_check_bandwidth, function(url:String):void, for js to start bandwidth check, @see: check_bandwidth(url:String):void * @param js_export_stop, function():void, for js to stop bandwidth check, @see: stop():void * * @remark, all parameters can be null. * @remark, as and js callback use same parameter, except that the js calblack first parameter is js_id:String. */ public function initialize( as_on_ready:Function, as_on_status_change:Function, as_on_progress_change:Function, as_on_srs_info:Function, as_on_complete:Function, js_id:String, js_on_ready:String, js_on_status_change:String, js_on_progress_change:String, js_on_srs_info:String, js_on_complete:String, js_export_check_bandwidth:String, js_export_stop:String ):void { this.as_on_ready = as_on_ready; this.as_on_srs_info = as_on_srs_info; this.as_on_status_change = as_on_status_change; this.as_on_progress_change = as_on_progress_change; this.as_on_complete = as_on_complete; this.js_id = js_id; this.js_on_srs_info = js_on_srs_info; this.js_on_ready = js_on_ready; this.js_on_status_change = js_on_status_change; this.js_on_progress_change = js_on_progress_change; this.js_on_complete = js_on_complete; this.js_export_check_bandwidth = js_export_check_bandwidth; this.js_export_stop = js_export_stop; flash.utils.setTimeout(this.system_on_js_ready, 0); } /** * start check bandwidth. * @param url, a String indicates the url to check bandwidth, * format as: rtmp://server:port/app?key=xxx&&vhost=xxx * for example, rtmp://dev:1935/app?key=35c9b402c12a7246868752e2878f7e0e&vhost=bandcheck.srs.com * where the key and vhost must be config in SRS, like: * vhost bandcheck.srs.com { * enabled on; * chunk_size 65000; * bandcheck { * enabled on; * key "35c9b402c12a7246868752e2878f7e0e"; * interval 30; * limit_kbps 4000; * } * } * * @remark user must invoke this as method, or js exported method. */ public function check_bandwidth(url:String):void { this.js_call_check_bandwidth(url); } /** * stop check bancwidth. * @remark it's optional, however, user can abort the bandwidth check. */ public function stop():void { this.js_call_stop(); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////// Private Section, ignore please. /////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////// /////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * *********************************************************************** * private section, including private fields, method and embeded classes. * *********************************************************************** */ /** * as callback. */ private var as_on_ready:Function; private var as_on_srs_info:Function; private var as_on_status_change:Function; private var as_on_progress_change:Function; private var as_on_complete:Function; /** * js callback. */ private var js_id:String; private var js_on_ready:String; private var js_on_srs_info:String; private var js_on_status_change:String; private var js_on_progress_change:String; private var js_on_complete:String; /** * js export functions. */ private var js_export_check_bandwidth:String; private var js_export_stop:String; /** * srs debug infos */ private var srs_server:String = null; private var srs_primary_authors:String = null; private var srs_id:String = null; private var srs_pid:String = null; private var srs_server_ip:String = null; /** * the underlayer connection, to send call message to do the bandwidth * check/test with server. */ private var connection:NetConnection = null; /** * use timeout to sendout publish call packets. * when got stop publish packet from server, stop publish call loop. */ private var publish_timeout_handler:uint = 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; } if (this.js_export_check_bandwidth != null) { flash.external.ExternalInterface.addCallback(this.js_export_check_bandwidth, this.js_call_check_bandwidth); } if (this.js_export_stop != null) { flash.external.ExternalInterface.addCallback(this.js_export_stop, this.js_call_stop); } if (as_on_ready != null) { as_on_ready(); } if (js_on_ready != null) { flash.external.ExternalInterface.call(this.js_on_ready, this.js_id); } } private function js_call_check_bandwidth(url:String):void { js_call_stop(); __on_progress_change(0); // init connection connection = new NetConnection; connection.objectEncoding = ObjectEncoding.AMF0; connection.client = { onStatus: onStatus, // play onSrsBandCheckStartPlayBytes: onSrsBandCheckStartPlayBytes, onSrsBandCheckPlaying: onSrsBandCheckPlaying, onSrsBandCheckStopPlayBytes: onSrsBandCheckStopPlayBytes, // publish onSrsBandCheckStartPublishBytes: onSrsBandCheckStartPublishBytes, onSrsBandCheckStopPublishBytes: onSrsBandCheckStopPublishBytes, onSrsBandCheckFinished: onSrsBandCheckFinished }; connection.addEventListener(NetStatusEvent.NET_STATUS, onStatus); connection.connect(url); __on_progress_change(3); } private function js_call_stop():void { if (connection) { connection.close(); connection = null; } } /** * NetConnection callback this function, when recv server call "onSrsBandCheckStartPlayBytes" * then start @updatePlayProgressTimer for updating the progressbar * */ private function onSrsBandCheckStartPlayBytes(evt:Object):void{ var duration_ms:Number = evt.duration_ms; var interval_ms:Number = evt.interval_ms; trace("start play test, duration=" + duration_ms + ", interval=" + interval_ms); connection.call("onSrsBandCheckStartingPlayBytes", null); __on_status_change(SrsBandwidth.StatusSrsBwtcPlayStart); __on_progress_change(10); } private function onSrsBandCheckPlaying(evt:Object):void{ } private function onSrsBandCheckStopPlayBytes(evt:Object):void{ var duration_ms:Number = evt.duration_ms; var interval_ms:Number = evt.interval_ms; var duration_delta:Number = evt.duration_delta; var bytes_delta:Number = evt.bytes_delta; var kbps:Number = 0; if(duration_delta > 0){ kbps = bytes_delta * 8.0 / duration_delta; // b/ms == kbps } kbps = (int(kbps * 10))/10.0; flash.utils.setTimeout(stopPlayTest, 0); __on_status_change(SrsBandwidth.StatusSrsBwtcPlayStop, String(kbps)); __on_progress_change(40); } private function stopPlayTest():void{ connection.call("onSrsBandCheckStoppedPlayBytes", null); } /** * publishing methods. */ private function onSrsBandCheckStartPublishBytes(evt:Object):void{ var duration_ms:Number = evt.duration_ms; var interval_ms:Number = evt.interval_ms; connection.call("onSrsBandCheckStartingPublishBytes", null); flash.utils.setTimeout(publisher, 0); __on_status_change(SrsBandwidth.StatusSrsBwtcPublishStart); __on_progress_change(60); } private function publisher():void{ var data:Array = new Array(); var data_size:int = 100; for(var i:int; i < data_size; i++) { data.push("SrS band check data from client's publishing......"); } connection.call("onSrsBandCheckPublishing", null, data); publish_timeout_handler = flash.utils.setTimeout(publisher, 0); } private function onSrsBandCheckStopPublishBytes(evt:Object):void{ var duration_ms:Number = evt.duration_ms; var interval_ms:Number = evt.interval_ms; var duration_delta:Number = evt.duration_delta; var bytes_delta:Number = evt.bytes_delta; var kbps:Number = 0; if(duration_delta > 0){ kbps = bytes_delta * 8.0 / duration_delta; // b/ms == kbps } kbps = (int(kbps * 10))/10.0; stopPublishTest(); __on_progress_change(90); } private function stopPublishTest():void{ // the stop publish response packet can not send out, for the queue is full. //connection.call("onSrsBandCheckStoppedPublishBytes", null); // clear the timeout to stop the send loop. if (publish_timeout_handler > 0) { flash.utils.clearTimeout(publish_timeout_handler); publish_timeout_handler = 0; } } private function onSrsBandCheckFinished(evt:Object):void{ var code:Number = evt.code; var start_time:Number = evt.start_time; var end_time:Number = evt.end_time; var play_kbps:Number = evt.play_kbps; var publish_kbps:Number = evt.publish_kbps; var play_bytes:Number = evt.play_bytes; var play_time:Number = evt.play_time; var publish_bytes:Number = evt.publish_bytes; var publish_time:Number = evt.publish_time; if (this.as_on_complete != null) { this.as_on_complete(start_time, end_time, play_kbps, publish_kbps, play_bytes, publish_bytes, play_time, publish_time); } if (this.js_on_complete != null) { flash.external.ExternalInterface.call(this.js_on_complete, this.js_id, start_time, end_time, play_kbps, publish_kbps, play_bytes, publish_bytes, play_time, publish_time); } __on_progress_change(100); // when got finish packet, directly close connection. js_call_stop(); // the last final packet can not send out, for the queue is full. //connection.call("finalClientPacket", null); } /** * get NetConnection NetStatusEvent */ private function onStatus(evt:NetStatusEvent): void { trace(evt.info.code); if (evt.info.hasOwnProperty("data") && evt.info.data) { if (evt.info.data.hasOwnProperty("srs_server")) { srs_server = evt.info.data.srs_server; } if (evt.info.data.hasOwnProperty("srs_primary_authors")) { srs_primary_authors = evt.info.data.srs_primary_authors; } if (evt.info.data.hasOwnProperty("srs_id")) { srs_id = evt.info.data.srs_id; } if (evt.info.data.hasOwnProperty("srs_pid")) { srs_pid = evt.info.data.srs_pid; } if (evt.info.data.hasOwnProperty("srs_server_ip")) { srs_server_ip = evt.info.data.srs_server_ip; } if (this.as_on_srs_info != null) { this.as_on_srs_info(srs_server, srs_primary_authors, srs_id, srs_pid, srs_server_ip); } if (this.js_on_srs_info != null) { flash.external.ExternalInterface.call(this.js_on_srs_info, this.js_id, srs_server, srs_primary_authors, srs_id, srs_pid, srs_server_ip); } } if (evt.info.code) { __on_status_change(evt.info.code); } switch(evt.info.code){ case "NetConnection.Connect.Success": __on_progress_change(8); break; } } /** * invoke the callback. */ private function __on_progress_change(percent:Number):void { if (this.as_on_progress_change != null) { this.as_on_progress_change(percent); } if (this.js_on_progress_change != null) { flash.external.ExternalInterface.call(this.js_on_progress_change, this.js_id, percent); } } private function __on_status_change(code:String, data:String=""):void { if (this.as_on_status_change != null) { this.as_on_status_change(code, data); } if (this.js_on_status_change != null) { flash.external.ExternalInterface.call(this.js_on_status_change, this.js_id, code, data); } } } }