winlin

refine srs player for hls, extract a HlsNetStream.

1 -package  
2 -{  
3 - public class Consts  
4 - {  
5 - // refresh every ts_fragment_seconds*M3u8RefreshRatio  
6 - public static var M3u8RefreshRatio:Number = 0.5;  
7 -  
8 - // parse ts every this ms.  
9 - public static var TsParseAsyncInterval:Number = 80;  
10 - }  
11 -}  
1 -package  
2 -{  
3 - import flash.utils.Dictionary;  
4 -  
5 - public class Dict  
6 - {  
7 - private var _dict:Dictionary;  
8 - private var _size:uint;  
9 -  
10 - public function Dict()  
11 - {  
12 - clear();  
13 - }  
14 -  
15 - /**  
16 - * get the underlayer dict.  
17 - * @remark for core-ng.  
18 - */  
19 - public function get dict():Dictionary  
20 - {  
21 - return _dict;  
22 - }  
23 -  
24 - public function has(key:Object):Boolean  
25 - {  
26 - return (key in _dict);  
27 - }  
28 -  
29 - public function get(key:Object):Object  
30 - {  
31 - return ((key in _dict) ? _dict[key] : null);  
32 - }  
33 -  
34 - public function set(key:Object, object:Object):void  
35 - {  
36 - if (!(key in _dict))  
37 - {  
38 - _size++;  
39 - }  
40 - _dict[key] = object;  
41 - }  
42 -  
43 - public function remove(key:Object):Object  
44 - {  
45 - var object:Object;  
46 - if (key in _dict)  
47 - {  
48 - object = _dict[key];  
49 - delete _dict[key];  
50 - _size--;  
51 - }  
52 - return (object);  
53 - }  
54 -  
55 - public function keys():Array  
56 - {  
57 - var array:Array = new Array(_size);  
58 - var index:int;  
59 - for (var key:Object in _dict)  
60 - {  
61 - var _local6:int = index++;  
62 - array[_local6] = key;  
63 - }  
64 - return (array);  
65 - }  
66 -  
67 - public function values():Array  
68 - {  
69 - var array:Array = new Array(_size);  
70 - var index:int;  
71 - for each (var value:Object in _dict)  
72 - {  
73 - var _local6:int = index++;  
74 - array[_local6] = value;  
75 - };  
76 - return (array);  
77 - }  
78 -  
79 - public function clear():void  
80 - {  
81 - _dict = new Dictionary();  
82 - _size = 0;  
83 - }  
84 -  
85 - public function toArray():Array  
86 - {  
87 - var array:Array = new Array(_size * 2);  
88 - var index:int;  
89 - for (var key:Object in _dict)  
90 - {  
91 - var _local6:int = index++;  
92 - array[_local6] = key;  
93 - var _local7:int = index++;  
94 - array[_local7] = _dict[key];  
95 - };  
96 - return (array);  
97 - }  
98 -  
99 - public function toObject():Object  
100 - {  
101 - return (toArray());  
102 - }  
103 -  
104 - public function fromObject(object:Object):void  
105 - {  
106 - clear();  
107 - var index:uint;  
108 - while (index < (object as Array).length) {  
109 - set((object as Array)[index], (object as Array)[(index + 1)]);  
110 - index += 2;  
111 - };  
112 - }  
113 -  
114 - public function get size():uint  
115 - {  
116 - return (_size);  
117 - }  
118 -  
119 - }  
120 -}  
1 -package  
2 -{  
3 - import flash.utils.ByteArray;  
4 -  
5 - /**  
6 - * a piece of flv, fetch from cdn or p2p.  
7 - */  
8 - public class FlvPiece  
9 - {  
10 - private var _pieceId:Number;  
11 - protected var _flv:ByteArray;  
12 - /**  
13 - * the private object for the channel,  
14 - * for example, the cdn channel will set to CdnEdge object.  
15 - */  
16 - private var _privateObject:Object;  
17 - /**  
18 - * when encoder error, this piece cannot be generated,  
19 - * and it should be skip. default to false.  
20 - */  
21 - private var _skip:Boolean;  
22 -  
23 - public function FlvPiece(pieceId:Number)  
24 - {  
25 - _pieceId = pieceId;  
26 - _flv = null;  
27 - _skip = false;  
28 - }  
29 -  
30 - /**  
31 - * when piece is fetch ok.  
32 - */  
33 - public function onPieceDone(flv:ByteArray):void  
34 - {  
35 - // save body.  
36 - _flv = flv;  
37 - }  
38 -  
39 - /**  
40 - * when piece is fetch error.  
41 - */  
42 - public function onPieceError():void  
43 - {  
44 - }  
45 -  
46 - /**  
47 - * when piece is empty.  
48 - */  
49 - public function onPieceEmpty():void  
50 - {  
51 - }  
52 -  
53 - /**  
54 - * destroy the object, set reference to null.  
55 - */  
56 - public function destroy():void  
57 - {  
58 - _privateObject = null;  
59 - _flv = null;  
60 - }  
61 -  
62 - public function get privateObject():Object  
63 - {  
64 - return _privateObject;  
65 - }  
66 -  
67 - public function set privateObject(v:Object):void  
68 - {  
69 - _privateObject = v;  
70 - }  
71 -  
72 - public function get skip():Boolean  
73 - {  
74 - return _skip;  
75 - }  
76 -  
77 - public function set skip(v:Boolean):void  
78 - {  
79 - _skip = v;  
80 - }  
81 -  
82 - public function get pieceId():Number  
83 - {  
84 - return _pieceId;  
85 - }  
86 -  
87 - public function get flv():ByteArray  
88 - {  
89 - return _flv;  
90 - }  
91 -  
92 - public function get completed():Boolean  
93 - {  
94 - return _flv != null;  
95 - }  
96 - }  
97 -}  
1 package 1 package
2 { 2 {
3 import flash.events.Event; 3 import flash.events.Event;
  4 + import flash.events.ProgressEvent;
  5 + import flash.external.ExternalInterface;
  6 + import flash.net.NetConnection;
  7 + import flash.net.NetStream;
  8 + import flash.net.NetStreamAppendBytesAction;
4 import flash.net.URLLoader; 9 import flash.net.URLLoader;
  10 + import flash.net.URLLoaderDataFormat;
5 import flash.net.URLRequest; 11 import flash.net.URLRequest;
  12 + import flash.net.URLRequestHeader;
6 import flash.net.URLRequestMethod; 13 import flash.net.URLRequestMethod;
  14 + import flash.net.URLStream;
  15 + import flash.net.URLVariables;
7 import flash.utils.ByteArray; 16 import flash.utils.ByteArray;
  17 + import flash.utils.setTimeout;
  18 +
  19 + // the NetStream to play hls or hls+.
  20 + public class HlsNetStream extends NetStream
  21 + {
  22 + private var hls:HlsCodec = null; // parse m3u8 and ts
  23 +
  24 + // for hls codec.
  25 + public var m3u8_refresh_ratio:Number;
  26 + public var ts_parse_async_interval:Number;
  27 +
  28 + // play param url.
  29 + private var user_url:String = null;
8 30
9 /** 31 /**
10 - * the hls main class. 32 + * create stream to play hls.
  33 + * @param m3u8_refresh_ratio, for example, 0.5, fetch m3u8 every 0.5*ts_duration.
  34 + * @param ts_parse_async_interval, for example, 80ms to parse ts async.
  35 + */
  36 + public function HlsNetStream(m3u8_refresh_ratio:Number, ts_parse_async_interval:Number, conn:NetConnection)
  37 + {
  38 + super(conn);
  39 +
  40 + this.m3u8_refresh_ratio = m3u8_refresh_ratio;
  41 + this.ts_parse_async_interval = ts_parse_async_interval;
  42 + hls = new HlsCodec(this);
  43 + }
  44 +
  45 + /**
  46 + * to play the hls stream.
  47 + * for example, HlsNetStream.play("http://ossrs.net:8080/live/livestream.m3u8").
  48 + * user can set the metadata callback by:
  49 + * var ns:NetStream = new HlsNetStream(...);
  50 + * ns.client = {};
  51 + * ns.client.onMetaData = system_on_metadata;
11 */ 52 */
12 - public class Hls 53 + public override function play(... params):void
13 { 54 {
  55 + super.play(null);
  56 + user_url = params[0] as String;
  57 + refresh_m3u8();
  58 + }
  59 +
  60 + /////////////////////////////////////////////////////////////////////////////////////////
  61 + ////////////////////////////Private Section//////////////////////////////////////////////
  62 + /////////////////////////////////////////////////////////////////////////////////////////
  63 +
  64 + private var parsed_ts_seq_no:Number = -1;
  65 + private function refresh_m3u8():void {
  66 + download(user_url, function(stream:ByteArray):void {
  67 + var m3u8:String = stream.toString();
  68 + hls.parse(user_url, m3u8);
  69 +
  70 + // redirect by variant m3u8.
  71 + if (hls.variant) {
  72 + var smu:String = hls.getTsUrl(0);
  73 + log("variant hls=" + user_url + ", redirect2=" + smu);
  74 + user_url = smu;
  75 + setTimeout(refresh_m3u8, 0);
  76 + return;
  77 + }
  78 +
  79 + // fetch from the last one.
  80 + if (parsed_ts_seq_no == -1) {
  81 + parsed_ts_seq_no = hls.seq_no + hls.tsCount - 1;
  82 + }
  83 +
  84 + // not changed.
  85 + if (parsed_ts_seq_no >= hls.seq_no + hls.tsCount) {
  86 + refresh_ts();
  87 + return;
  88 + }
  89 +
  90 + // parse each ts.
  91 + var nb_ts:Number = hls.seq_no + hls.tsCount - parsed_ts_seq_no;
  92 + log("m3u8 changed, got " + nb_ts + " new ts, count=" + hls.tsCount + ", seqno=" + hls.seq_no + ", parsed=" + parsed_ts_seq_no);
  93 +
  94 + refresh_ts();
  95 + })
  96 + }
  97 + private function refresh_ts():void {
  98 + // all ts parsed.
  99 + if (parsed_ts_seq_no >= hls.seq_no + hls.tsCount) {
  100 + var to:Number = 1000;
  101 + if (hls.tsCount > 0) {
  102 + to = hls.duration * 1000 / hls.tsCount * m3u8_refresh_ratio;
  103 + }
  104 + setTimeout(refresh_m3u8, to);
  105 + log("m3u8 not changed, retry after " + to.toFixed(2) + "ms");
  106 + return;
  107 + }
  108 +
  109 + // parse current ts.
  110 + var uri:String = hls.getTsUrl(parsed_ts_seq_no - hls.seq_no);
  111 +
  112 + // parse metadata from uri.
  113 + if (uri.indexOf("?") >= 0) {
  114 + var uv:URLVariables = new URLVariables(uri.substr(uri.indexOf("?") + 1));
  115 + var obj:Object = {};
  116 + for (var k:String in uv) {
  117 + var v:String = uv[k];
  118 + if (k == "shp_sip1") {
  119 + obj.srs_server_ip = v;
  120 + } else if (k == "shp_cid") {
  121 + obj.srs_id = v;
  122 + } else if (k == "shp_pid") {
  123 + obj.srs_pid = v;
  124 + }
  125 + //log("uv[" + k + "]=" + v);
  126 + }
  127 +
  128 + if (client && client.hasOwnProperty("onMetaData")) {
  129 + client.onMetaData(obj);
  130 + }
  131 + }
  132 +
  133 + download(uri, function(stream:ByteArray):void{
  134 + log("got ts seqno=" + parsed_ts_seq_no + ", " + stream.length + " bytes");
  135 +
  136 + var flv:FlvPiece = new FlvPiece(parsed_ts_seq_no);
  137 + var body:ByteArray = new ByteArray();
  138 + stream.position = 0;
  139 + hls.parseBodyAsync(flv, stream, body, function():void{
  140 + body.position = 0;
  141 + //log("ts parsed, seqno=" + parsed_ts_seq_no + ", flv=" + body.length + "B");
  142 + onFlvBody(uri, body);
  143 +
  144 + parsed_ts_seq_no++;
  145 + setTimeout(refresh_ts, 0);
  146 + });
  147 + });
  148 + }
  149 + private function download(uri:String, completed:Function):void {
  150 + // http get.
  151 + var url:URLStream = new URLStream();
  152 + var stream:ByteArray = new ByteArray();
  153 +
  154 + url.addEventListener(ProgressEvent.PROGRESS, function(evt:ProgressEvent):void {
  155 + if (url.bytesAvailable <= 0) {
  156 + return;
  157 + }
  158 +
  159 + //log(uri + " total=" + evt.bytesTotal + ", loaded=" + evt.bytesLoaded + ", available=" + url.bytesAvailable);
  160 + var bytes:ByteArray = new ByteArray();
  161 + url.readBytes(bytes, 0, url.bytesAvailable);
  162 + stream.writeBytes(bytes);
  163 + });
  164 +
  165 + url.addEventListener(Event.COMPLETE, function(evt:Event):void {
  166 + log(uri + " completed, total=" + stream.length + "bytes");
  167 + if (url.bytesAvailable <= 0) {
  168 + completed(stream);
  169 + return;
  170 + }
  171 +
  172 + //log(uri + " completed" + ", available=" + url.bytesAvailable);
  173 + var bytes:ByteArray = new ByteArray();
  174 + url.readBytes(bytes, 0, url.bytesAvailable);
  175 + stream.writeBytes(bytes);
  176 +
  177 + completed(stream);
  178 + });
  179 +
  180 + // we set to the query.
  181 + uri += ((uri.indexOf("?") == -1)? "?":"&") + "shp_xpsid=" + XPlaybackSessionId;
  182 + var r:URLRequest = new URLRequest(uri);
  183 + // seems flash not allow set this header.
  184 + // @remark disable it for it will cause security exception.
  185 + //r.requestHeaders.push(new URLRequestHeader("X-Playback-Session-Id", XPlaybackSessionId));
  186 +
  187 + log("start download " + uri);
  188 + url.load(r);
  189 + }
  190 +
  191 + // the uuid similar to Safari, to identify this play session.
  192 + // @see https://github.com/winlinvip/srs-plus/blob/bms/trunk/src/app/srs_app_http_stream.cpp#L45
  193 + public var XPlaybackSessionId:String = createRandomIdentifier(32);
  194 +
  195 + private function createRandomIdentifier(length:uint, radix:uint = 61):String {
  196 + var characters:Array = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  197 + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
  198 + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  199 + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
  200 + 'z');
  201 + var id:Array = new Array();
  202 + radix = (radix > 61) ? 61 : radix;
  203 + while (length--) {
  204 + id.push(characters[randomIntegerWithinRange(0, radix)]);
  205 + }
  206 + return id.join('');
  207 + }
  208 +
  209 + private function randomIntegerWithinRange(min:int, max:int):int {
  210 + return Math.floor(Math.random() * (1 + max - min) + min);
  211 + }
  212 +
  213 + // callback for hls.
  214 + public var flvHeader:ByteArray = null;
  215 + public function onSequenceHeader():void {
  216 + var s:NetStream = super;
  217 + s.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
  218 + s.appendBytes(flvHeader);
  219 + log("FLV: sps/pps " + flvHeader.length + " bytes");
  220 +
  221 + writeFlv(flvHeader);
  222 + }
  223 +
  224 + private function onFlvBody(uri:String, flv:ByteArray):void {
  225 + if (!flvHeader) {
  226 + return;
  227 + }
  228 +
  229 + var s:NetStream = super;
  230 + s.appendBytes(flv);
  231 + log("FLV: ts " + uri + " parsed to flv " + flv.length + " bytes");
  232 +
  233 + writeFlv(flv);
  234 + }
  235 +
  236 + private function writeFlv(data:ByteArray):void {
  237 + return;
  238 +
  239 + var r:URLRequest = new URLRequest("http://192.168.1.117:8088/api/v1/flv");
  240 + r.method = URLRequestMethod.POST;
  241 + r.data = data;
  242 +
  243 + var pf:URLLoader = new URLLoader();
  244 + pf.dataFormat = URLLoaderDataFormat.BINARY;
  245 + pf.load(r);
  246 + }
  247 +
  248 + private function log(msg:String):void {
  249 + msg = "[" + new Date() +"][srs-player] " + msg;
  250 +
  251 + trace(msg);
  252 +
  253 + if (!flash.external.ExternalInterface.available) {
  254 + return;
  255 + }
  256 +
  257 + ExternalInterface.call("console.log", msg);
  258 + }
  259 + }
  260 +}
  261 +
  262 +import flash.events.Event;
  263 +import flash.net.URLLoader;
  264 +import flash.net.URLRequest;
  265 +import flash.net.URLRequestMethod;
  266 +import flash.utils.ByteArray;
  267 +
  268 +/**
  269 + * the hls main class.
  270 + */
  271 +class HlsCodec
  272 +{
14 private var m3u8:M3u8; 273 private var m3u8:M3u8;
15 274
16 private var avc:SrsRawH264Stream; 275 private var avc:SrsRawH264Stream;
@@ -25,12 +284,12 @@ package @@ -25,12 +284,12 @@ package
25 private var video_sh_tag:ByteArray; 284 private var video_sh_tag:ByteArray;
26 private var audio_sh_tag:ByteArray; 285 private var audio_sh_tag:ByteArray;
27 286
28 - private var owner:M3u8Player; 287 + private var owner:HlsNetStream;
29 private var _log:ILogger = new TraceLogger("HLS"); 288 private var _log:ILogger = new TraceLogger("HLS");
30 289
31 public static const SRS_TS_PACKET_SIZE:int = 188; 290 public static const SRS_TS_PACKET_SIZE:int = 188;
32 291
33 - public function Hls(o:M3u8Player) 292 + public function HlsCodec(o:HlsNetStream)
34 { 293 {
35 owner = o; 294 owner = o;
36 m3u8 = new M3u8(this); 295 m3u8 = new M3u8(this);
@@ -155,7 +414,7 @@ package @@ -155,7 +414,7 @@ package
155 nb_parsed += each_parse; 414 nb_parsed += each_parse;
156 415
157 if (nb_parsed < ts_packets) { 416 if (nb_parsed < ts_packets) {
158 - flash.utils.setTimeout(aysncParse, Consts.TsParseAsyncInterval); 417 + flash.utils.setTimeout(aysncParse, owner.ts_parse_async_interval);
159 return; 418 return;
160 } 419 }
161 420
@@ -260,7 +519,7 @@ package @@ -260,7 +519,7 @@ package
260 { 519 {
261 for (var i:int = 0; (maxTsPackets == -1 || i < maxTsPackets) && data.bytesAvailable > 0; i++) { 520 for (var i:int = 0; (maxTsPackets == -1 || i < maxTsPackets) && data.bytesAvailable > 0; i++) {
262 var tsBytes:ByteArray = new ByteArray(); 521 var tsBytes:ByteArray = new ByteArray();
263 - data.readBytes(tsBytes, 0, Hls.SRS_TS_PACKET_SIZE); 522 + data.readBytes(tsBytes, 0, HlsCodec.SRS_TS_PACKET_SIZE);
264 context.decode(tsBytes, handler); 523 context.decode(tsBytes, handler);
265 } 524 }
266 } 525 }
@@ -280,7 +539,327 @@ package @@ -280,7 +539,327 @@ package
280 }); 539 });
281 loader.load(url); 540 loader.load(url);
282 } 541 }
  542 +}
  543 +
  544 +import flash.utils.Dictionary;
  545 +
  546 +class Dict
  547 +{
  548 + private var _dict:Dictionary;
  549 + private var _size:uint;
  550 +
  551 + public function Dict()
  552 + {
  553 + clear();
  554 + }
  555 +
  556 + /**
  557 + * get the underlayer dict.
  558 + * @remark for core-ng.
  559 + */
  560 + public function get dict():Dictionary
  561 + {
  562 + return _dict;
  563 + }
  564 +
  565 + public function has(key:Object):Boolean
  566 + {
  567 + return (key in _dict);
  568 + }
  569 +
  570 + public function get(key:Object):Object
  571 + {
  572 + return ((key in _dict) ? _dict[key] : null);
  573 + }
  574 +
  575 + public function set(key:Object, object:Object):void
  576 + {
  577 + if (!(key in _dict))
  578 + {
  579 + _size++;
  580 + }
  581 + _dict[key] = object;
  582 + }
  583 +
  584 + public function remove(key:Object):Object
  585 + {
  586 + var object:Object;
  587 + if (key in _dict)
  588 + {
  589 + object = _dict[key];
  590 + delete _dict[key];
  591 + _size--;
  592 + }
  593 + return (object);
  594 + }
  595 +
  596 + public function keys():Array
  597 + {
  598 + var array:Array = new Array(_size);
  599 + var index:int;
  600 + for (var key:Object in _dict)
  601 + {
  602 + var _local6:int = index++;
  603 + array[_local6] = key;
  604 + }
  605 + return (array);
  606 + }
  607 +
  608 + public function values():Array
  609 + {
  610 + var array:Array = new Array(_size);
  611 + var index:int;
  612 + for each (var value:Object in _dict)
  613 + {
  614 + var _local6:int = index++;
  615 + array[_local6] = value;
  616 + };
  617 + return (array);
  618 + }
  619 +
  620 + public function clear():void
  621 + {
  622 + _dict = new Dictionary();
  623 + _size = 0;
  624 + }
  625 +
  626 + public function toArray():Array
  627 + {
  628 + var array:Array = new Array(_size * 2);
  629 + var index:int;
  630 + for (var key:Object in _dict)
  631 + {
  632 + var _local6:int = index++;
  633 + array[_local6] = key;
  634 + var _local7:int = index++;
  635 + array[_local7] = _dict[key];
  636 + };
  637 + return (array);
  638 + }
  639 +
  640 + public function toObject():Object
  641 + {
  642 + return (toArray());
  643 + }
  644 +
  645 + public function fromObject(object:Object):void
  646 + {
  647 + clear();
  648 + var index:uint;
  649 + while (index < (object as Array).length) {
  650 + set((object as Array)[index], (object as Array)[(index + 1)]);
  651 + index += 2;
  652 + };
  653 + }
  654 +
  655 + public function get size():uint
  656 + {
  657 + return (_size);
283 } 658 }
  659 +
  660 +}
  661 +
  662 +import flash.utils.ByteArray;
  663 +
  664 +/**
  665 + * a piece of flv, fetch from cdn or p2p.
  666 + */
  667 +class FlvPiece
  668 +{
  669 + private var _pieceId:Number;
  670 + protected var _flv:ByteArray;
  671 + /**
  672 + * the private object for the channel,
  673 + * for example, the cdn channel will set to CdnEdge object.
  674 + */
  675 + private var _privateObject:Object;
  676 + /**
  677 + * when encoder error, this piece cannot be generated,
  678 + * and it should be skip. default to false.
  679 + */
  680 + private var _skip:Boolean;
  681 +
  682 + public function FlvPiece(pieceId:Number)
  683 + {
  684 + _pieceId = pieceId;
  685 + _flv = null;
  686 + _skip = false;
  687 + }
  688 +
  689 + /**
  690 + * when piece is fetch ok.
  691 + */
  692 + public function onPieceDone(flv:ByteArray):void
  693 + {
  694 + // save body.
  695 + _flv = flv;
  696 + }
  697 +
  698 + /**
  699 + * when piece is fetch error.
  700 + */
  701 + public function onPieceError():void
  702 + {
  703 + }
  704 +
  705 + /**
  706 + * when piece is empty.
  707 + */
  708 + public function onPieceEmpty():void
  709 + {
  710 + }
  711 +
  712 + /**
  713 + * destroy the object, set reference to null.
  714 + */
  715 + public function destroy():void
  716 + {
  717 + _privateObject = null;
  718 + _flv = null;
  719 + }
  720 +
  721 + public function get privateObject():Object
  722 + {
  723 + return _privateObject;
  724 + }
  725 +
  726 + public function set privateObject(v:Object):void
  727 + {
  728 + _privateObject = v;
  729 + }
  730 +
  731 + public function get skip():Boolean
  732 + {
  733 + return _skip;
  734 + }
  735 +
  736 + public function set skip(v:Boolean):void
  737 + {
  738 + _skip = v;
  739 + }
  740 +
  741 + public function get pieceId():Number
  742 + {
  743 + return _pieceId;
  744 + }
  745 +
  746 + public function get flv():ByteArray
  747 + {
  748 + return _flv;
  749 + }
  750 +
  751 + public function get completed():Boolean
  752 + {
  753 + return _flv != null;
  754 + }
  755 +}
  756 +
  757 +interface ILogger
  758 +{
  759 + function debug0(message:String, ... rest):void;
  760 + function debug(message:String, ... rest):void;
  761 + function info(message:String, ... rest):void;
  762 + function warn(message:String, ... rest):void;
  763 + function error(message:String, ... rest):void;
  764 + function fatal(message:String, ... rest):void;
  765 +}
  766 +
  767 +import flash.globalization.DateTimeFormatter;
  768 +import flash.external.ExternalInterface;
  769 +
  770 +class TraceLogger implements ILogger
  771 +{
  772 + private var _category:String;
  773 +
  774 + public function get category():String
  775 + {
  776 + return _category;
  777 + }
  778 + public function TraceLogger(category:String)
  779 + {
  780 + _category = category;
  781 + }
  782 + public function debug0(message:String, ...rest):void
  783 + {
  784 + }
  785 +
  786 + public function debug(message:String, ...rest):void
  787 + {
  788 + }
  789 +
  790 + public function info(message:String, ...rest):void
  791 + {
  792 + logMessage(LEVEL_INFO, message, rest);
  793 + }
  794 +
  795 + public function warn(message:String, ...rest):void
  796 + {
  797 + logMessage(LEVEL_WARN, message, rest);
  798 + }
  799 +
  800 + public function error(message:String, ...rest):void
  801 + {
  802 + logMessage(LEVEL_ERROR, message, rest);
  803 + }
  804 +
  805 + public function fatal(message:String, ...rest):void
  806 + {
  807 + logMessage(LEVEL_FATAL, message, rest);
  808 + }
  809 + protected function logMessage(level:String, message:String, params:Array):void
  810 + {
  811 + var msg:String = "";
  812 +
  813 + // add datetime
  814 + var date:Date = new Date();
  815 + var dtf:DateTimeFormatter = new DateTimeFormatter("UTC");
  816 + dtf.setDateTimePattern("yyyy-MM-dd HH:mm:ss");
  817 +
  818 + // TODO: FIXME: the SSS format not run, use date.milliseconds instead.
  819 + msg += '[' + dtf.format(date) + "." + date.milliseconds + ']';
  820 + msg += " [" + level + "] ";
  821 +
  822 + // add category and params
  823 + msg += "[" + category + "] " + applyParams(message, params);
  824 +
  825 + // trace the message
  826 + trace(msg);
  827 +
  828 + if (!flash.external.ExternalInterface.available) {
  829 + return;
  830 + }
  831 +
  832 + ExternalInterface.call("console.log", msg);
  833 + }
  834 + private function leadingZeros(x:Number):String
  835 + {
  836 + if (x < 10) {
  837 + return "00" + x.toString();
  838 + }
  839 +
  840 + if (x < 100) {
  841 + return "0" + x.toString();
  842 + }
  843 +
  844 + return x.toString();
  845 + }
  846 + private function applyParams(message:String, params:Array):String
  847 + {
  848 + var result:String = message;
  849 +
  850 + var numParams:int = params.length;
  851 +
  852 + for (var i:int = 0; i < numParams; i++) {
  853 + result = result.replace(new RegExp("\\{" + i + "\\}", "g"), params[i]);
  854 + }
  855 + return result;
  856 + }
  857 +
  858 + private static const LEVEL_DEBUG:String = "DEBUG";
  859 + private static const LEVEL_WARN:String = "WARN";
  860 + private static const LEVEL_INFO:String = "INFO";
  861 + private static const LEVEL_ERROR:String = "ERROR";
  862 + private static const LEVEL_FATAL:String = "FATAL";
284 } 863 }
285 864
286 import flash.utils.ByteArray; 865 import flash.utils.ByteArray;
@@ -304,7 +883,7 @@ class SrsTsHanlder implements ISrsTsHandler @@ -304,7 +883,7 @@ class SrsTsHanlder implements ISrsTsHandler
304 private var queue:Array; 883 private var queue:Array;
305 884
306 // hls data. 885 // hls data.
307 - private var _hls:Hls; 886 + private var _hls:HlsCodec;
308 private var _body:ByteArray; 887 private var _body:ByteArray;
309 private var _on_size_changed:Function; 888 private var _on_size_changed:Function;
310 private var _on_sequence_changed:Function; 889 private var _on_sequence_changed:Function;
@@ -316,7 +895,7 @@ class SrsTsHanlder implements ISrsTsHandler @@ -316,7 +895,7 @@ class SrsTsHanlder implements ISrsTsHandler
316 ph264_sps:ByteArray, ph264_pps:ByteArray, 895 ph264_sps:ByteArray, ph264_pps:ByteArray,
317 paac_specific_config:ByteArray, 896 paac_specific_config:ByteArray,
318 pvideo_sh_tag:ByteArray, paudio_sh_tag:ByteArray, 897 pvideo_sh_tag:ByteArray, paudio_sh_tag:ByteArray,
319 - hls:Hls, body:ByteArray, oszc:Function, oshc:Function) 898 + hls:HlsCodec, body:ByteArray, oszc:Function, oshc:Function)
320 { 899 {
321 _hls = hls; 900 _hls = hls;
322 _body = body; 901 _body = body;
@@ -2383,7 +2962,7 @@ interface ISrsTsHandler @@ -2383,7 +2962,7 @@ interface ISrsTsHandler
2383 */ 2962 */
2384 class SrsTsContext 2963 class SrsTsContext
2385 { 2964 {
2386 - private var _hls:Hls; 2965 + private var _hls:HlsCodec;
2387 2966
2388 // codec 2967 // codec
2389 // key, a Number indicates the pid, 2968 // key, a Number indicates the pid,
@@ -2393,7 +2972,7 @@ class SrsTsContext @@ -2393,7 +2972,7 @@ class SrsTsContext
2393 // whether hls pure audio stream. 2972 // whether hls pure audio stream.
2394 private var _pure_audio:Boolean; 2973 private var _pure_audio:Boolean;
2395 2974
2396 - public function SrsTsContext(hls:Hls) 2975 + public function SrsTsContext(hls:HlsCodec)
2397 { 2976 {
2398 _hls = hls; 2977 _hls = hls;
2399 _pure_audio = false; 2978 _pure_audio = false;
@@ -2620,7 +3199,7 @@ class SrsTsPacket @@ -2620,7 +3199,7 @@ class SrsTsPacket
2620 } 3199 }
2621 3200
2622 // calc the user defined data size for payload. 3201 // calc the user defined data size for payload.
2623 - var nb_payload:int = Hls.SRS_TS_PACKET_SIZE - (stream.position - pos); 3202 + var nb_payload:int = HlsCodec.SRS_TS_PACKET_SIZE - (stream.position - pos);
2624 3203
2625 // optional: payload. 3204 // optional: payload.
2626 if (adaption_field_control == SrsTsAdaptationFieldType.PayloadOnly 3205 if (adaption_field_control == SrsTsAdaptationFieldType.PayloadOnly
@@ -4583,7 +5162,7 @@ class SrsTsPayloadPMT extends SrsTsPayloadPSI @@ -4583,7 +5162,7 @@ class SrsTsPayloadPMT extends SrsTsPayloadPSI
4583 */ 5162 */
4584 class M3u8 5163 class M3u8
4585 { 5164 {
4586 - private var _hls:Hls; 5165 + private var _hls:HlsCodec;
4587 private var _log:ILogger = new TraceLogger("HLS"); 5166 private var _log:ILogger = new TraceLogger("HLS");
4588 5167
4589 private var _tses:Array; 5168 private var _tses:Array;
@@ -4593,7 +5172,7 @@ class M3u8 @@ -4593,7 +5172,7 @@ class M3u8
4593 // when variant, all ts url is sub m3u8 url. 5172 // when variant, all ts url is sub m3u8 url.
4594 private var _variant:Boolean; 5173 private var _variant:Boolean;
4595 5174
4596 - public function M3u8(hls:Hls) 5175 + public function M3u8(hls:HlsCodec)
4597 { 5176 {
4598 _hls = hls; 5177 _hls = hls;
4599 _tses = new Array(); 5178 _tses = new Array();
1 -package  
2 -{  
3 - public interface ILogger  
4 - {  
5 - function debug0(message:String, ... rest):void;  
6 - function debug(message:String, ... rest):void;  
7 - function info(message:String, ... rest):void;  
8 - function warn(message:String, ... rest):void;  
9 - function error(message:String, ... rest):void;  
10 - function fatal(message:String, ... rest):void;  
11 - }  
12 -}  
1 -package  
2 -{  
3 - import flash.net.NetStream;  
4 -  
5 - /**  
6 - * the player interface.  
7 - */  
8 - public interface IPlayer  
9 - {  
10 - /**  
11 - * initialize the player by flashvars for config.  
12 - * @param flashvars the config.  
13 - */  
14 - function init(flashvars:Object):void;  
15 -  
16 - /**  
17 - * get the NetStream to play the stream.  
18 - * @return the underlayer stream object.  
19 - */  
20 - function stream():NetStream;  
21 -  
22 - /**  
23 - * connect and play url.  
24 - * @param url the stream url to play.  
25 - */  
26 - function play(url:String):void;  
27 -  
28 - /**  
29 - * close the player.  
30 - */  
31 - function close():void;  
32 - }  
33 -}  
1 -package  
2 -{  
3 - import flash.display.Sprite;  
4 - import flash.display.StageAlign;  
5 - import flash.display.StageDisplayState;  
6 - import flash.display.StageScaleMode;  
7 - import flash.events.Event;  
8 - import flash.events.FullScreenEvent;  
9 - import flash.events.MouseEvent;  
10 - import flash.events.NetStatusEvent;  
11 - import flash.events.ProgressEvent;  
12 - import flash.events.TimerEvent;  
13 - import flash.external.ExternalInterface;  
14 - import flash.media.SoundTransform;  
15 - import flash.media.Video;  
16 - import flash.net.NetConnection;  
17 - import flash.net.NetStream;  
18 - import flash.net.NetStreamAppendBytesAction;  
19 - import flash.net.URLLoader;  
20 - import flash.net.URLLoaderDataFormat;  
21 - import flash.net.URLRequest;  
22 - import flash.net.URLRequestHeader;  
23 - import flash.net.URLRequestMethod;  
24 - import flash.net.URLStream;  
25 - import flash.net.URLVariables;  
26 - import flash.system.Security;  
27 - import flash.ui.ContextMenu;  
28 - import flash.ui.ContextMenuItem;  
29 - import flash.utils.ByteArray;  
30 - import flash.utils.Timer;  
31 - import flash.utils.getTimer;  
32 - import flash.utils.setTimeout;  
33 -  
34 - import flashx.textLayout.formats.Float;  
35 -  
36 - /**  
37 - * the m3u8 player.  
38 - */  
39 - public class M3u8Player implements IPlayer  
40 - {  
41 - private var js_id:String = null;  
42 -  
43 - // play param url.  
44 - private var user_url:String = null;  
45 -  
46 - private var media_stream:NetStream = null;  
47 - private var media_conn:NetConnection = null;  
48 -  
49 - private var owner:srs_player = null;  
50 - private var hls:Hls = null; // parse m3u8 and ts  
51 -  
52 - // the uuid similar to Safari, to identify this play session.  
53 - // @see https://github.com/winlinvip/srs-plus/blob/bms/trunk/src/app/srs_app_http_stream.cpp#L45  
54 - public var XPlaybackSessionId:String = createRandomIdentifier(32);  
55 - private function createRandomIdentifier(length:uint, radix:uint = 61):String {  
56 - var characters:Array = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',  
57 - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',  
58 - 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',  
59 - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',  
60 - 'z');  
61 - var id:Array = new Array();  
62 - radix = (radix > 61) ? 61 : radix;  
63 - while (length--) {  
64 - id.push(characters[randomIntegerWithinRange(0, radix)]);  
65 - }  
66 - return id.join('');  
67 - }  
68 - private function randomIntegerWithinRange(min:int, max:int):int {  
69 - return Math.floor(Math.random() * (1 + max - min) + min);  
70 - }  
71 -  
72 - // callback for hls.  
73 - public var flvHeader:ByteArray = null;  
74 - public function onSequenceHeader():void {  
75 - if (!media_stream) {  
76 - setTimeout(onSequenceHeader, 1000);  
77 - return;  
78 - }  
79 -  
80 - var s:NetStream = media_stream;  
81 - s.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);  
82 - s.appendBytes(flvHeader);  
83 - log("FLV: sps/pps " + flvHeader.length + " bytes");  
84 -  
85 - writeFlv(flvHeader);  
86 - }  
87 - public function onFlvBody(uri:String, flv:ByteArray):void {  
88 - if (!media_stream) {  
89 - return;  
90 - }  
91 -  
92 - if (!flvHeader) {  
93 - return;  
94 - }  
95 -  
96 - var s:NetStream = media_stream;  
97 - s.appendBytes(flv);  
98 - log("FLV: ts " + uri + " parsed to flv " + flv.length + " bytes");  
99 -  
100 - writeFlv(flv);  
101 - }  
102 - private function writeFlv(data:ByteArray):void {  
103 - return;  
104 -  
105 - var r:URLRequest = new URLRequest("http://192.168.1.117:8088/api/v1/flv");  
106 - r.method = URLRequestMethod.POST;  
107 - r.data = data;  
108 -  
109 - var pf:URLLoader = new URLLoader();  
110 - pf.dataFormat = URLLoaderDataFormat.BINARY;  
111 - pf.load(r);  
112 - }  
113 -  
114 - public function M3u8Player(o:srs_player) {  
115 - owner = o;  
116 - hls = new Hls(this);  
117 - }  
118 -  
119 - public function init(flashvars:Object):void {  
120 - this.js_id = flashvars.id;  
121 - }  
122 -  
123 - public function stream():NetStream {  
124 - return this.media_stream;  
125 - }  
126 -  
127 - // owner.on_player_metadata(evt.info.data);  
128 - public function play(url:String):void {  
129 - var streamName:String;  
130 - this.user_url = url;  
131 -  
132 - this.media_conn = new NetConnection();  
133 - this.media_conn.client = {};  
134 - this.media_conn.client.onBWDone = function():void {};  
135 - this.media_conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {  
136 - log("NetConnection: code=" + evt.info.code + ", data is " + evt.info.data);  
137 -  
138 - // TODO: FIXME: failed event.  
139 - if (evt.info.code != "NetConnection.Connect.Success") {  
140 - return;  
141 - }  
142 -  
143 - media_stream = new NetStream(media_conn);  
144 - media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {  
145 - log("NetStream: code=" + evt.info.code);  
146 -  
147 - if (evt.info.code == "NetStream.Video.DimensionChange") {  
148 - owner.on_player_dimension_change();  
149 - } else if (evt.info.code == "NetStream.Buffer.Empty") {  
150 - owner.on_player_buffer_empty();  
151 - } else if (evt.info.code == "NetStream.Buffer.Full") {  
152 - owner.on_player_buffer_full();  
153 - }  
154 -  
155 - // TODO: FIXME: failed event.  
156 - });  
157 -  
158 - // setup stream before play.  
159 - owner.on_player_before_play();  
160 -  
161 - media_stream.play(null);  
162 - refresh_m3u8();  
163 -  
164 - owner.on_player_play();  
165 - });  
166 -  
167 - this.media_conn.connect(null);  
168 - }  
169 -  
170 - public function close():void {  
171 - if (this.media_stream) {  
172 - this.media_stream.close();  
173 - this.media_stream = null;  
174 - }  
175 - if (this.media_conn) {  
176 - this.media_conn.close();  
177 - this.media_conn = null;  
178 - }  
179 - }  
180 -  
181 - private var parsed_ts_seq_no:Number = -1;  
182 - private function refresh_m3u8():void {  
183 - download(user_url, function(stream:ByteArray):void {  
184 - var m3u8:String = stream.toString();  
185 - hls.parse(user_url, m3u8);  
186 -  
187 - // redirect by variant m3u8.  
188 - if (hls.variant) {  
189 - var smu:String = hls.getTsUrl(0);  
190 - log("variant hls=" + user_url + ", redirect2=" + smu);  
191 - user_url = smu;  
192 - setTimeout(refresh_m3u8, 0);  
193 - return;  
194 - }  
195 -  
196 - // fetch from the last one.  
197 - if (parsed_ts_seq_no == -1) {  
198 - parsed_ts_seq_no = hls.seq_no + hls.tsCount - 1;  
199 - }  
200 -  
201 - // not changed.  
202 - if (parsed_ts_seq_no >= hls.seq_no + hls.tsCount) {  
203 - refresh_ts();  
204 - return;  
205 - }  
206 -  
207 - // parse each ts.  
208 - var nb_ts:Number = hls.seq_no + hls.tsCount - parsed_ts_seq_no;  
209 - log("m3u8 changed, got " + nb_ts + " new ts, count=" + hls.tsCount + ", seqno=" + hls.seq_no + ", parsed=" + parsed_ts_seq_no);  
210 -  
211 - refresh_ts();  
212 - })  
213 - }  
214 - private function refresh_ts():void {  
215 - // all ts parsed.  
216 - if (parsed_ts_seq_no >= hls.seq_no + hls.tsCount) {  
217 - var to:Number = 1000;  
218 - if (hls.tsCount > 0) {  
219 - to = hls.duration * 1000 / hls.tsCount * Consts.M3u8RefreshRatio;  
220 - }  
221 - setTimeout(refresh_m3u8, to);  
222 - log("m3u8 not changed, retry after " + to.toFixed(2) + "ms");  
223 - return;  
224 - }  
225 -  
226 - // parse current ts.  
227 - var uri:String = hls.getTsUrl(parsed_ts_seq_no - hls.seq_no);  
228 -  
229 - // parse metadata from uri.  
230 - if (uri.indexOf("?") >= 0) {  
231 - var uv:URLVariables = new URLVariables(uri.substr(uri.indexOf("?") + 1));  
232 - var obj:Object = {};  
233 - for (var k:String in uv) {  
234 - var v:String = uv[k];  
235 - if (k == "shp_sip1") {  
236 - obj.srs_server_ip = v;  
237 - } else if (k == "shp_cid") {  
238 - obj.srs_id = v;  
239 - } else if (k == "shp_pid") {  
240 - obj.srs_pid = v;  
241 - }  
242 - //log("uv[" + k + "]=" + v);  
243 - }  
244 - owner.on_player_metadata(obj);  
245 - }  
246 -  
247 - download(uri, function(stream:ByteArray):void{  
248 - log("got ts seqno=" + parsed_ts_seq_no + ", " + stream.length + " bytes");  
249 -  
250 - var flv:FlvPiece = new FlvPiece(parsed_ts_seq_no);  
251 - var body:ByteArray = new ByteArray();  
252 - stream.position = 0;  
253 - hls.parseBodyAsync(flv, stream, body, function():void{  
254 - body.position = 0;  
255 - //log("ts parsed, seqno=" + parsed_ts_seq_no + ", flv=" + body.length + "B");  
256 - onFlvBody(uri, body);  
257 -  
258 - parsed_ts_seq_no++;  
259 - setTimeout(refresh_ts, 0);  
260 - });  
261 - });  
262 - }  
263 - private function download(uri:String, completed:Function):void {  
264 - var url:URLStream = new URLStream();  
265 - var stream:ByteArray = new ByteArray();  
266 -  
267 - url.addEventListener(ProgressEvent.PROGRESS, function(evt:ProgressEvent):void {  
268 - if (url.bytesAvailable <= 0) {  
269 - return;  
270 - }  
271 -  
272 - //log(uri + " total=" + evt.bytesTotal + ", loaded=" + evt.bytesLoaded + ", available=" + url.bytesAvailable);  
273 - var bytes:ByteArray = new ByteArray();  
274 - url.readBytes(bytes, 0, url.bytesAvailable);  
275 - stream.writeBytes(bytes);  
276 - });  
277 -  
278 - url.addEventListener(Event.COMPLETE, function(evt:Event):void {  
279 - log(uri + " completed, total=" + stream.length + "bytes");  
280 - if (url.bytesAvailable <= 0) {  
281 - completed(stream);  
282 - return;  
283 - }  
284 -  
285 - //log(uri + " completed" + ", available=" + url.bytesAvailable);  
286 - var bytes:ByteArray = new ByteArray();  
287 - url.readBytes(bytes, 0, url.bytesAvailable);  
288 - stream.writeBytes(bytes);  
289 -  
290 - completed(stream);  
291 - });  
292 -  
293 - // we set to the query.  
294 - uri += ((uri.indexOf("?") == -1)? "?":"&") + "shp_xpsid=" + XPlaybackSessionId;  
295 - var r:URLRequest = new URLRequest(uri);  
296 - // seems flash not allow set this header.  
297 - r.requestHeaders.push(new URLRequestHeader("X-Playback-Session-Id", XPlaybackSessionId));  
298 -  
299 - log("start download " + uri);  
300 - url.load(r);  
301 - }  
302 -  
303 - private function log(msg:String):void {  
304 - Utility.log(js_id, msg);  
305 - }  
306 - }  
307 -}  
@@ -28,8 +28,14 @@ package @@ -28,8 +28,14 @@ package
28 * common player to play rtmp/flv stream, 28 * common player to play rtmp/flv stream,
29 * use system NetStream. 29 * use system NetStream.
30 */ 30 */
31 - public class CommonPlayer implements IPlayer 31 + public class Player
32 { 32 {
  33 + // refresh every ts_fragment_seconds*M3u8RefreshRatio
  34 + public static var M3u8RefreshRatio:Number = 0.5;
  35 +
  36 + // parse ts every this ms.
  37 + public static var TsParseAsyncInterval:Number = 80;
  38 +
33 private var js_id:String = null; 39 private var js_id:String = null;
34 40
35 // play param url. 41 // play param url.
@@ -40,7 +46,7 @@ package @@ -40,7 +46,7 @@ package
40 46
41 private var owner:srs_player = null; 47 private var owner:srs_player = null;
42 48
43 - public function CommonPlayer(o:srs_player) { 49 + public function Player(o:srs_player) {
44 owner = o; 50 owner = o;
45 } 51 }
46 52
@@ -88,7 +94,11 @@ package @@ -88,7 +94,11 @@ package
88 return; 94 return;
89 } 95 }
90 96
  97 + if (url.indexOf(".m3u8") > 0) {
  98 + media_stream = new HlsNetStream(M3u8RefreshRatio, TsParseAsyncInterval, media_conn);
  99 + } else {
91 media_stream = new NetStream(media_conn); 100 media_stream = new NetStream(media_conn);
  101 + }
92 media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void { 102 media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
93 log("NetStream: code=" + evt.info.code); 103 log("NetStream: code=" + evt.info.code);
94 104
1 -package  
2 -{  
3 - import flash.globalization.DateTimeFormatter;  
4 -  
5 - public class TraceLogger implements ILogger  
6 - {  
7 - private var _category:String;  
8 -  
9 - public function get category():String  
10 - {  
11 - return _category;  
12 - }  
13 - public function TraceLogger(category:String)  
14 - {  
15 - _category = category;  
16 - }  
17 - public function debug0(message:String, ...rest):void  
18 - {  
19 - }  
20 -  
21 - public function debug(message:String, ...rest):void  
22 - {  
23 - }  
24 -  
25 - public function info(message:String, ...rest):void  
26 - {  
27 - logMessage(LEVEL_INFO, message, rest);  
28 - }  
29 -  
30 - public function warn(message:String, ...rest):void  
31 - {  
32 - logMessage(LEVEL_WARN, message, rest);  
33 - }  
34 -  
35 - public function error(message:String, ...rest):void  
36 - {  
37 - logMessage(LEVEL_ERROR, message, rest);  
38 - }  
39 -  
40 - public function fatal(message:String, ...rest):void  
41 - {  
42 - logMessage(LEVEL_FATAL, message, rest);  
43 - }  
44 - protected function logMessage(level:String, message:String, params:Array):void  
45 - {  
46 - var msg:String = "";  
47 -  
48 - // add datetime  
49 - var date:Date = new Date();  
50 - var dtf:DateTimeFormatter = new DateTimeFormatter("UTC");  
51 - dtf.setDateTimePattern("yyyy-MM-dd HH:mm:ss");  
52 -  
53 - // TODO: FIXME: the SSS format not run, use date.milliseconds instead.  
54 - msg += '[' + dtf.format(date) + "." + date.milliseconds + ']';  
55 - msg += " [" + level + "] ";  
56 -  
57 - // add category and params  
58 - msg += "[" + category + "] " + applyParams(message, params);  
59 -  
60 - // trace the message  
61 - Utility.log("CORE", msg);  
62 - }  
63 - private function leadingZeros(x:Number):String  
64 - {  
65 - if (x < 10) {  
66 - return "00" + x.toString();  
67 - }  
68 -  
69 - if (x < 100) {  
70 - return "0" + x.toString();  
71 - }  
72 -  
73 - return x.toString();  
74 - }  
75 - private function applyParams(message:String, params:Array):String  
76 - {  
77 - var result:String = message;  
78 -  
79 - var numParams:int = params.length;  
80 -  
81 - for (var i:int = 0; i < numParams; i++) {  
82 - result = result.replace(new RegExp("\\{" + i + "\\}", "g"), params[i]);  
83 - }  
84 - return result;  
85 - }  
86 -  
87 - private static const LEVEL_DEBUG:String = "DEBUG";  
88 - private static const LEVEL_WARN:String = "WARN";  
89 - private static const LEVEL_INFO:String = "INFO";  
90 - private static const LEVEL_ERROR:String = "ERROR";  
91 - private static const LEVEL_FATAL:String = "FATAL";  
92 - }  
93 -}  
@@ -32,14 +32,16 @@ package @@ -32,14 +32,16 @@ package
32 * @param msg the log message. 32 * @param msg the log message.
33 */ 33 */
34 public static function log(js_id:String, msg:String):void { 34 public static function log(js_id:String, msg:String):void {
  35 + if (js_id) {
35 msg = "[" + new Date() +"][srs-player][" + js_id + "] " + msg; 36 msg = "[" + new Date() +"][srs-player][" + js_id + "] " + msg;
  37 + }
36 38
37 logData += msg + "\n"; 39 logData += msg + "\n";
38 40
39 trace(msg); 41 trace(msg);
40 42
41 if (!flash.external.ExternalInterface.available) { 43 if (!flash.external.ExternalInterface.available) {
42 - flash.utils.setTimeout(log, 300, msg); 44 + flash.utils.setTimeout(log, 300, null, msg);
43 return; 45 return;
44 } 46 }
45 47
@@ -58,7 +58,7 @@ package @@ -58,7 +58,7 @@ package
58 private var control_fs_mask:Sprite = new Sprite(); 58 private var control_fs_mask:Sprite = new Sprite();
59 59
60 // the common player to play stream. 60 // the common player to play stream.
61 - private var player:IPlayer = null; 61 + private var player:Player = null;
62 // the flashvars config. 62 // the flashvars config.
63 private var config:Object = null; 63 private var config:Object = null;
64 64
@@ -438,13 +438,7 @@ package @@ -438,13 +438,7 @@ package
438 } 438 }
439 439
440 // create player. 440 // create player.
441 - if (url.indexOf(".m3u8") > 0 && Utility.stringStartswith(url, "http://")) {  
442 - player = new M3u8Player(this);  
443 - log("create M3U8 player.");  
444 - } else {  
445 - player = new CommonPlayer(this);  
446 - log("create Common player.");  
447 - } 441 + player = new Player(this);
448 442
449 // init player by config. 443 // init player by config.
450 player.init(config); 444 player.init(config);