Flash Media Server 入門教程

文章來源: http://www.flexs.cn/post/fms_server_106.html

什麼是Flash Media Server ?

Flash大家庭裏的一員,這個東東以前叫Flash Communication Server,傳說中的FCS就是這個,現在改叫FMS了...
不見不知道哦,一見真可怕,adobe還有這麼cool的東東,自從見了她,讓我做些小東東的興趣全沒了,一心想研究這個

這東東能做什麼?

視頻錄製啊,flash聊天室啊,在線視頻會議啊啊, 網絡遊戲?

不管怎樣先安個試試吧

先到這裏下載免費的開發者版本吧
http://www.macromedia.com/software/flashmediaserver/
然後安裝,安裝時記得用戶名和密碼不要瞎填自己要記住,如果你已經瞎填了那就到 安裝目錄下\conf\fms.ini 裏找吧

偶裝到了c盤,找到目錄
C:\Program Files\Macromedia\Flash Media Server 2


先要打開服務器哦

你在本機裝的fms,本機就是服務器了哦!記得現在你的機器既是客戶端又是服務端哦
開始===程序===Macromedia===Flash Media Server 2
有兩個start****,都要打開,關時候兩個都要關,如果你怕麻煩直接找tool文件夾裏的批處理文件StartServerService.bat吧,雙擊他,他會幫你搞定地,想關就找StopServerService.bat。~

applications文件夾

你可以在裏邊建一個文件夾例如叫 FirstApp,這就建了一個Application,以後所有此項目服務器端的flv文件啊,共享文件啊都會在這裏邊,有時服務器端需要寫程序的時候裏邊會有個main.asc 文件,這個就是服務器端程序,服務器端程序目前只支持as1寫,是在服務器上執行的,你也可以用trace調試,怎麼trace管理服務器時候你會看到,你可能會建若干個app,在一臺服務器上同時運行,比如錄象的app,聊天室的app。。。這些所有的app都通過fms自帶的fms2_console.swf管理,現在就去看看如何管理服務器吧!

管理服務器~

服務器打開後就要管理了哦,點開fms2_console.swf,別看他只是個swf,後臺管理就靠他了,輸入你安裝時候的密碼和用戶,服務器地址寫 localhost 就好看到了吧,熟悉熟悉他吧


以後我們寫程序最常用的就是這個View Applications ,每當有客戶端連接服務器時,左邊就會列出連接的是哪個app,有多少連接 下圖爲連接applications文件夾下的bs文件夾(bs文件夾,因爲每個文件夾對應一個app,就是bs app),連接數爲1,458是實例名(實例名默認爲 _definst_ ,每個文件夾可以有若干實例,互相不影響,這個特性可以用來做聊天是的房間,以後再說)


選中某個app後,或者客戶端有連接,會看到當前打開的app的狀態,這個


Live Log 服務器端的trace就這裏看了 ,右邊依次是客戶端情況 , 共享對象,流,執行的情況(佔內存,cpu等),後邊兩個小按鈕,調試時候常用哦,reload和unload!
每當服務器端main.asc修改後一定記得reload或者unload一下,否則不會生效,unload會把窗口關閉,有客戶端連的時候還會自動打開
總有人告訴我,他的fms經常會出現詭異現象,比如連接不上,代碼已經刪了還會執行,一生氣連文件夾都刪了,還會執行,懷疑是自己的rp有問題。。
那不是rp問題,記住出現問題就reload !實在不行就去tools文件夾點StopServerService.bat

conf文件夾

還有重要的是conf文件夾了,裏邊是一些服務器端的配置文件,以後可能會用,先不用動。。。。

自此,安裝部分結束,go on...

有一種協議叫rtmp

客戶端和服務器端通信是使用協議rtmp的

現在在服務器端applications文件夾(當然偶的客戶端和服務器端是一臺機器了)裏建個test1文件夾,你的地址就爲

rtmp:/test1 或者 rtmp://localhost/test1

注意兩個地址中的 "/ "符號

打開flash

與服務器通信首先要建個NetConnection()
nc = new NetConnection();

連接

nc.connect("rtmp://localhost/test1");

怎麼知道連沒連上呢?順利連接服務器後會觸發一個onStatus事件,自己trace一下info.code

nc.onStatus = function(info) {
//trace(info)
//trace(info.code)
for (i in info) {
trace(i+": "+info[i]);
}
};

完整代碼:


nc = new NetConnection();
nc.onStatus = function(info) {
trace(info.code);
if (info.code == "NetConnection.Connect.Success") {
trace("接通");
}
};
nc.connect("rtmp://localhost/test1");

注意:默認情況下服務器是允許你連接的,但只是默認,如果服務器拒絕你連接的話,上述代碼就不好用了。怎麼回事?我們看看連接的過程吧

連接過程

每當客戶端試圖連接服務器,一個NetConnection.connect(),服務器將會調用application.onConnect 來鑑定是不是允許客戶端連接,onConnect()方法返回null 或不返回則將進入未決狀態,直到onConnect方法中返回true或執行acceptConnection(client)則允許,返回false或執行rejectConnection(client)則拒絕,如圖(從左往右看)


服務器文件是以.asc形式存在的,可以在test1文件夾裏建一個main.asc


application.onConnect=function(client){
this.rejectConnection(client);

//this.acceptConnection(client)

}

這樣就拒絕連接了,動手試試,別忘了,服務器端改動的話,別忘了到fms2_console.swf去reload !不知道按哪個的到上邊找,每當有客戶端連接,那個reload按鈕的界面就會出來哦。

檢查是否uri錯誤

如果你的rtmp地址是從其他什麼地方傳過來的,可以順便檢查一下rtmp是否錯誤,下邊代碼如果地址是錯誤的就會trace出來


mync = new NetConnection();
mync.onStatus = function(info) {
if (info.code == "NetConnection.Connect.Success") {
trace("連接成功");
}
};
//正確的uri
//uri = "rtmp://localhost/connect";
//錯誤的uri
uri = "rtmpppppp://localhost/connect";
if (mync.connect(uri, "N神")) {
trace("嘗試連接服務器中。。");
} else {
trace("沒有嘗試連接服務器~是uri錯誤???");
}

ok 了,現在我們深入一點點。。看看連接上的一些細節問題

info.code:

連接後info.code會告訴你連接的狀態,上邊看的都是NetConnection.Connect.Success, 還有一些其他值,和這些值是什麼意思,自己看看。

值得注意的是。NetConnection.Connect.Rejected,收到這條消息的時候說明服務器端拒絕了你,接着馬上你會收到另一條,NetConnection.Connect.Closed,連接就關閉了~~~


mync = new NetConnection();
mync.onStatus = function(info) {
switch (info.code) {
case "NetConnection.Connect.Success" :
trace("連接成功");
break;
case "NetConnection.Connect.Failed" :
//關掉服務器的情況
trace("嘗試連接失敗,服務器有可能掛掉了 -_-b");
break;
case "NetConnection.Connect.Rejected" :
//注意這裏,服務器拒絕你的情況,如果遭到拒絕,將會調用兩次mync.onStatus,
//第一次"NetConnection.Connect.Rejected"第2次"NetConnection.Connect.Closed"
trace("遭到服務器拒絕");
trace("服務器返回信息:"+info.application.msg);
break;
case "NetConnection.Connect.Closed" :
trace("連接關閉");
break;
}
};
mync.connect("rtmp://localhost/connect", "N神");
//mync.connect("rtmp://localhost/connect","小新")

服務器端拒絕連接?好象見過。。。回頭找找。。。。。。。。哦在這裏


application.onConnect = function(client) {

this.rejectConnection(client);

}

我不能所有人都拒絕了。。我要把討厭的人拒絕了。。。
傳給服務器一個人名~~


mync.connect("rtmp://localhost/connect", "N神");

服務器看看是不是討厭的人。。


application.onConnect = function(client, name) {
trace(name);
if (name == "N神") {
//拒絕連接,並返回個錯誤對象{msg:"服務器不想"+name+"進去,哈哈~"},包含錯誤消息
application.rejectConnection(client, {msg:"服務器不想"+name+"進去,哈哈~"});
} else {
application.acceptConnection(client);
//成功不能返回客戶端信息
}
};

看客戶端的代碼。。

case "NetConnection.Connect.Rejected" :
//注意這裏,服務器拒絕你的情況,如果遭到拒絕,將會調用兩次mync.onStatus,
//第一次"NetConnection.Connect.Rejected"第2次"NetConnection.Connect.Closed"
trace("遭到服務器拒絕");
trace("服務器返回信息:"+info.application.msg);
break;
遭到服務器拒絕後會trace出服務器返回的錯誤消息,這是一個最基礎的與服務器交互的例子,以後還會有很多滴

下邊進入新一層次。。。

視頻,流

這個比較重要,但超簡單,網上播放電影,在線錄製,在線播放,視頻會議,視頻電話,全靠他了 ,下邊我們先做一個最簡單 錄製和播放

錄製視頻

打開flash,新建一個fla,Ctrl + L 打開library,右鍵新建視頻,確定。

拉到舞臺上起個名叫my_video

第一幀開始加代碼

//從麥和設像頭顯示視頻顯示在my_video上

my_video.attachVideo(Camera.get());
my_video.attachAudio(Microphone.get());

//像以前一樣連接

nc = new NetConnection();
nc.connect("rtmp://localhost/aaaa"); //注意這裏,Applications文件夾裏要有aaaa文件夾哦!

//可以理解爲在nc連接上綁一個流

nsOut = new NetStream(nc);

//在流上加麥克風和視頻頭

nsOut.attachVideo(Camera.get());
nsOut.attachAudio(Microphone.get());

//發佈 2.flv
nsOut.publish("2", "record");

第一個參數是文件名,後一個參數要"record"纔是錄製


把fla發佈一下, 錄一會兒,把視頻關掉,打開你的

叉盤:\Program Files\Macromedia\Flash Media Server 2\applications\aaaa\

是不是多了個streams\_definst_

打開C:\Program Files\Macromedia\Flash Media Server 2\applications\aaaa\streams\_definst_

看見2.flv了吧。。

這裏下載原文件 : http://www.nshen.net/blog/attachments/200601/25_163617_c1.fla

播放flv

用fms播放的flv目前是無法下載的,這可以保護你的版權 :)

打開flash,新建一個fla ,Ctrl + L 打開library,右鍵新建視頻,確定。

拉到舞臺上,這回起個名叫view ,我們來播放你剛纔錄的那個視頻

nc = new NetConnection();
nc.connect("rtmp://localhost/aaaa");
res = new NetStream(nc);

//view元件要加載res流

view.attachVideo(res);
view.attachAudio(res);

//播放

res.play("2");

原文件 : http://www.nshen.net/blog/attachments/200602/13_223604_p1.fla

現場流

上邊做的都是先錄製好了視頻,然後才播放,網上的實時視頻會議,視頻電話是怎麼做的呢?總不能先錄好再播放吧?
這要用到現場流,現場流是指你連接到服務器後,你在發佈的同時,其他人就可以實時的看到你
很難嗎?看看吧,把上邊錄製視頻的例子拿下來
nsOut.publish("2", "record"); 這句改成 nsOut.publish("2", "live");
把"record" 改成"live"後,就不會生成flv了,取而代之的是一個看不到的實時的視頻流
ok 了,發佈,這就是直播端了,同時再發布上邊那個播放端,已經可以實時看見你了吧。現在你是用本機測試,等你有了服務器。其他人也能同時看見你了哦

到這裏你已經可以自己做一個網頁上的直播了,發佈端不要讓別人看到,讓別人看你的播放端就好了 :) 至於爲什麼要用兩個swf,因爲目前爲止你還不知道怎麼樣跟服務器之間傳遞消息,這樣做可以避免這些東西,等你把後邊的東西全都學完就可以在一個swf裏,寫個視頻會議之類的東東了

遠程共享

遠程共享?

共享的概念就是讓每個連接到服務器的swf都能實時的得到服務器端共享的數據。
一個人更改了這些數據,其他人都會看得到通知。可以想象聊天室裏的發言,一個人發了以後其他人都可以看到。


共享對象

共享對象,說英文大概你比較熟ha~ SharedObject, 恩flash中有兩種sharedObject,local sharedobject (LSO) 和 remote sharedobject (RSO),也就是本地共享和遠程共享,偶們討論遠程的,不過之前你最好先去了解了解本地的,對你有好處...

RSO在服務器端是以文件形式存儲的,擴展名是.fso,爲什麼不是.rso?....我也想問呢- -b

代碼

初始化RSO需要先與服務器建立一個連接,續上節 ,我們先與服務器建立一個連接


//初始化遠程共享要利用nc通道
var myNC = new NetConnection();
myNC.onStatus = function(info) {
if (info.code == "NetConnection.Connect.Success") {
//成功則利用此nc初始化rso
initRSO(this);
}
};

跟以前的代碼一樣,只是連接成功後多了一個initRSO()函數,看不懂的回去重頭再看一下。。。

下邊是initRSO了,跟連接結構差不多


function initRSO(NC) {
//在服務器上建立myRSO.fso文件,第2個參數指定nc通道,第3個指定文件在服務器上持久保留,即使服務器重啓了,還是有
my_rso = SharedObject.getRemote("myRSO", NC.uri, true);
my_rso.onSync = function(list) {
//list 是一個對象數組 ,類似這種[{name:"x",code:"success"},{name:"y",code:"success"}] ,下邊會詳細講
//初始成功
};
my_rso.connect(NC); //連接
}

瞭解了吧,看一個完整的例子

畫一個mc起名叫mc,在第一幀上寫代碼,


//初始化遠程共享要利用nc通道
var myNC = new NetConnection();
myNC.onStatus = function(info) {
if (info.code == "NetConnection.Connect.Success") {
//成功則利用此nc初始化rso
initRSO(this);
}
};
myNC.connect("rtmp://localhost/test1"); //不會不知道要建test1文件夾吧

function initRSO(NC) {
my_rso = SharedObject.getRemote("myRSO", NC.uri, true);
//onSync是回調函數,每次服務器端so數據有改變,這裏都會有反映!這裏的意思每當有人按鼠標,這裏都會有反映,我們讀取so的data下的值就可以了
my_rso.onSync = function() {
mc._x=this.data.x
mc._y=this.data.y
};
my_rso.connect(NC);
}

onMouseDown = function () {
//改變so的數據
my_rso.data.x = _root._xmouse
my_rso.data.y = _root._ymouse
};

然後發佈設置中設置只允許網絡,發佈看看

現在你可以開多個播放器窗口,點其中一個,看看其他的窗口變不變

源文件: http://www.nshen.net/blog/attachments/200601/11_145320_test3.fla

連接流程


再寫一個,看起來很像在做網遊~


mync = new NetConnection();
mync.onStatus = function(info) {
if (info.code == "NetConnection.Connect.Success") {
initRSO();
}
if (info.code == "NetConnection.Connect.Closed") {
trace("關閉");
}
};

function initRSO() {
my_RSO = SharedObject.getRemote("myRSO", mync.uri, true);
trace(my_RSO);
my_RSO.onSync = function() {
mc._x = this.data.hero.x;
};
my_RSO.connect(mync);
}
mync.connect("rtmp:/my_app/test1");

mc.onEnterFrame = function() {
my_RSO.data.hero.x = this._x;
if (Key.isDown(Key.LEFT)) {
this._x -= 5;
}
if (Key.isDown(Key.RIGHT)) {
this._x += 5;
}
};

寫個簡單的聊天室

很簡單的東西,基本上就是共享對象的運用,沒有用到服務器端,大型聊天室可能不會這麼做,這個只適用於初學者 :/

註釋很詳細,不說多了,可以直接下載原文件

http://www.nshen.net/blog/attachments/200602/smallchat.fla

代碼:


//用戶名
myname="遊客"
//建立連接
var myNC = new NetConnection();
myNC.connect("rtmp://localhost/smallchat");

//搞到rso
Talk_SO = SharedObject.getRemote("Talk", myNC.uri, false);
Talk_SO.onSync = function() {
//先把聊天文本框清空
remoteText.text = "";
//把聊天列表顯示出來,talklist的格式就是[誰誰說:啊啊啊,誰誰誰說:2222]
var t = this.data.talklist;
for (var i = 0; i<t.length; i++) {
writeln(t[i]);
}
};
Talk_SO.connect(myNC);

//發消息函數
function post() {
//如果不存在talklist就建一個,這裏沒用server端,是個技巧
if (Talk_SO.data.talklist[0] == undefined) {
Talk_SO.data.talklist = [];
}
//限制數組長度,是個隊列。保證裏邊有5條消息,當然也可以更多,但如果沒有限制,flash會垮的
if (Talk_SO.data.talklist.length>=5) {
Talk_SO.data.talklist.shift();
}
//把消息裝到so裏
Talk_SO.data.talklist.push(myname+"說:"+meText.text);
meText.text = "";
}
//文字顯示,換行
function writeln(msg) {
remoteText.text += msg+"\n";
remoteText.vPosition =remoteText.maxVPosition
}
//-----------------------------------------------
Btn.onRelease = function() {
post();
};
this.onKeyDown = function() {
if (Key.isDown(Key.ENTER)) {
post();
}
};
Key.addListener(this);

深入onSync

onSync有個list參數,這個開始有些難度了。不想動腦的可以跳過沒影響,只是以後寫出的程序效率會低點 :(

看代碼:


my_rso = SharedObject.getRemote("myRSO", NC.uri, true);
my_rso.onSync = function(list) {//.......};
my_rso.connect(NC); //連接

在onSync回調中我們可以知道我們的my_rso被改變了,但my_rso裏具體什麼改變了呢? 我們就要分析這個 list 參數 了

list參數其實是一個對象數組 ,首先它是一個數組,裏邊裝了很多對象(Object),每一個對象都包括了SharedObject中一個插槽(slot)的改動信息。我暫時給他起名叫插槽信息對象。。。這名字太猥褻了。。但我就這麼叫了。。

插槽信息對象包含兩個屬性,name 和 code,偶爾還會有個oldValue?我不太常用,不說它

name 描述被改變的屬性名

code 描述該屬性的改變方式 ,有可能爲以下幾種值:"success" , "change" , "delete" , "reject" , "clear" ,具體含義後邊說

說白了這個插槽信息對象大概就是這麼個樣子:

{name:"x",code:"success"}

表示x屬性被修改成功

要得到這些插槽信息對象就要for in 這個list參數

for (var i in list) {

list[i] 就是插槽信息對象

}

要分析具體so哪改變了,就是分析list[i],比如

if(list[i].code=="change") trace("list[i].name"+被+"change了")

if(list[i].code=="delete") trace("list[i].name"+被+"delete")

“change”是啥?“delete”是啥?

"success" , "change" , "delete" , "reject" , "clear" 具體含義:

success : 表示當前影片修改so的插槽獲得了成功

change : 表示so的插槽被別人修改,或填加

也就是說,你修改so的某個屬性成功了會收到 "success" ,與此同時其他影片會收到 "change"

reject : 拒絕修改

例如發生在兩個或多個客戶端同時要修改一個so的插槽,這時候fms會只讓一個client修改,並返回"success" 其他的會收到"reject"

delete , clear : 這個好理解,一個是刪除,一個是清空,看例子:

比如服務器端刪除某個so


so = SharedObject.get("某個so");
so.lock( );
var names = so.getPropertyNames( );
for (i in names) {
so.setProperty(names[i], null);
}
so.unlock( );

這樣client端會收到 若干個插槽信息對象,所有的code都爲"delete",表示若干個item被刪除

然而這樣:


so = SharedObject.get("某個so");
so.clear( );

client端就只會收到一個插槽信息對象,code屬性爲“clear”。

client端與server端直接交互

看完了SO,看一下client與server端如何直接進行交互的

原文件在這裏:
http://www.nshen.net/blog/attachments/200602/25_152648_csc.rar

1. 客戶端呼叫服務器

fla:


//客戶端呼叫server端msgfromclient函數,並將返回值trace出來
mync = new NetConnection();
mync.connect("rtmp://localhost/connect");
//返回值接收對象
var resObj = new Object();
resObj.onResult = function(val):Void {
trace("val"+val);
};
/*
我們用mync去call服務器端的msgfromclient函數,resObj是返回接收對象,當服務器有返回值後,會自動直接調用這個對象的onResult處理函數,後邊可以傳遞給server無數個參數,這裏只傳一個字符串
*/
mync.call("msgfromclient", resObj, "第一個call");


服務器端代碼是放在main.asc裏的,你可以到你的application下的connect目錄下建一個main.asc,寫代碼

main.asc:


//要把函數定義到Client上!!
application.onConnect = function(client) {
/* 在這裏定義也可以,在Client.prototype裏定義也是可以的
client.msgfromclient=function(what){
trace(what+"進來了")

var aa="呼叫成功並返回結果"
return aa
}
*/
application.acceptConnection(client);
};

Client.prototype.msgfromclient=function(what){
trace(what+"進來了")

var aa="呼叫成功並返回結果"
return aa

}


現在去試一下吧。。。成功了的話,再繼續.......

2. 服務器端呼叫指定的客戶端

fla:


//server呼叫client端
//要把函數定義到nc上!!
//
mync = new NetConnection();
mync.onStatus = function(info) {
if (info.code == "NetConnection.Connect.Success") {
trace("連接成功");
}
};
mync.connect("rtmp://localhost/connect");
mync.msgfromserver = function(msg) {
trace(msg);
};

main.asc:


application.onConnect = function(client) {

application.acceptConnection(client);
//這裏呼叫剛連線成功的客戶
//跟client呼叫server基本一樣,服務器一般很少讓client端返回值所以第2個參數設爲null
client.call("msgfromserver",null,"服務器叫你啊")
};

3. 服務器端呼叫所有的客戶端(廣播)

有些時候需要服務器廣播數據給所有連接上的客戶端,這裏就用到了廣播的概念

廣播其實SharedObject的時候已經講過了一種實現,就是把數據放到remote SharedObject中,當數據改變了,自然所有客戶端都會onSync
這裏再講一種用call來實現的:

下邊是很常見的一個情況,當某人下線了的時候要通知所有客戶端,某某已經下線了

server端:

application.onDisconnect=function(newClient){
//遍歷客戶端列表,分別call他們
for(var i=0;i<application.clients.length;i++) {
application.clients[i].call("client_fun",null,sendvar);
}
}

Client端:

nc.client_fun=function(myvar){....... }

這個自己完善一下吧,這裏就不貼fla了

還有:

關於廣播,不只有服務器端廣播給所有客戶,還有可能某一個客戶端對所有客戶端直接進行廣播,當然上邊的例子你如果都看懂了的話,你已經可以自己做某一個客戶端對所有客戶端的廣播了。怎麼做?

第1步 某一個客戶端呼叫服務器
第2步 服務器廣播給所有客戶端

這樣就形成了,某客戶端對所有客戶端的廣播,當然如果你能細心的耐心的看看幫助的話,你會發現Shared Object 和 NetStream都有send方法就是做這件事的,而且更爲簡潔,服務端不用寫代碼 :)

好了,看到這裏,fms常用的大部分概念都說到了,這個教程也就基本結束了,想學更多的,你可能需要多翻翻手冊,多找找教程,英文有不少很好的教程進階,下邊我也會貼些資源。

其他需要注意的問題:

* 中文編碼:

有些時候我們用flash去讀取外部的php,asp.....文件裏的中文顯示在flash裏會出現亂碼的情況,爲了解決在flash裏顯示中文很多教程裏通常都直接加了一句System.useCodepage=true
問題就在這,顯示不了外部中文是因爲flash內默認用Unicode編碼,外部的文件大多都是gb2312,加上這句System.useCodepage=true代表強制flash使用系統默認的gb2312,這樣flash就顯示正確了,但fms服務器端默認也是用unicode的,這樣客戶端跟服務器端不同編碼有時就會出錯了,搜了一下server字典好象沒有System.useCodepage=true了。。。所以解決辦法就是去掉System.useCodepage=true,在外部php或asp中把編碼轉成utf-8,至於怎麼轉,不知道,問你的asp或php程序員吧 ,另外不要用記事本編輯你的asc文件。。即使編輯最後要一定另存爲utf-8格式。

* 判斷影片播放結束


ns.onStatus=function(info){
if(info.code=="NetStream.Play.Stop")trace("結束")
}

乍看好象沒錯,但是如果設置了緩衝以後(setBufferTime)就不好用了,仔細研究了一下原因4這樣的

監視onStatus(info) ,info.code:

開始播放

NetStream.Play.Start (其實還沒播放)

然後緩衝(根據setBufferTime設置的秒數緩。。)

NetStream.Buffer.Full (緩衝裝滿了,這纔開始播放)

然後播放完了

NetStream.Play.Stop (其實還沒播放完)

注意了,然後還要播放緩衝 - -b

NetStream.Buffer.Empty (緩衝空了,這才播放完了。。)

羣裏的kinglong兄比較聰明~,先Stop的時候做個記號,然後再滿足Empty纔算播放完,也就是兩個條件,因爲網速慢也會Empty。。。好辦法
但我看了一下幫助,好象有一個專門的事件通知播放結束
ns.onPlayStatus=function(info){
if(info.code=="NetStream.Play.Complete")trace("感謝觀看幫助")
}

* 防火牆,端口

默認安裝的話默認端口是1935,管理是1111端口,記得防火牆要把1935和1111端口打開。
發佈了8 篇原創文章 · 獲贊 0 · 訪問量 2423
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章