前段时间听说flashplayer已经开始支持p2p了,对这块非常感兴趣于是开始玩flex,一玩下来不可自拔。用Stratus搭建p2p环境如此简单,双方只需要能连上Stratus服务器就能直接进行语音视频的聊天,不需要任何客户端。Adobe还真是NB,呵呵出了这么XX的东东,前途无可限量啊。
闲话少说,这几天小搞了下,用Mysql和flex弄了个简易随机视频聊天室,和有共同爱好的各位一起分享下。高手可以直接飞过哈,欢迎提出意见,毕竟刚玩没多久,问题很多。
准备工作:
1.我用的是Flash Builder 4开发的,默认用的是Flex SDK4.0
2.开发最好用flash payer 10 debug version(可以进行调试)
3.申请一个 Stratus develper key:8b0f114ef5a20c433d5c2a33-201aeea5601b(用这个也行-_-我申请的)或者在这申请
下面正式开始,程序主体主要分为3大块:
一、p2p语音视频功能模块
这个模块网上有很多教程了,Adobe官方的那个Sample就很好。我就是以此为基础进行开发的。可能有些朋友还不是很了解,为了每个人都能搞清楚,下面针对代码详细地进行下介绍。
由于是p2p模式,每个用户既是呼叫者又是被呼叫者。更具体点,举个例子有两个人A和B打电话。A呼叫B,此时A是呼叫者,B是被呼叫者。反之B是呼叫者,A是被呼叫者。因此在每个p2p模块中必须要有呼叫者和被呼叫者两个部分。
首先要连接上stratus服务器:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
private
var
netConnection:NetConnection;
//监听信息流
private
var
listenStream:NetStream;
//播放上面的监听流
private
var
controlStream:NetStream;
//对外发布信息流
private
var
outgoingStream:NetStream;
//接受外部发送的信息流
private
var
incomingStream:NetStream;
//Adobe stratus 服务器地址
//Developer Key,要自己申请
private
const
DEVKEY:
String
=
"8b0f114ef5a20c433d5c2a33-201aeea5601b"
;
//创建一个nectConnetion与stratus 服务器进行连接
netConnection=
new
NetConnection();
netConnection.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
//连接Adobe stratus 服务器
netConnection.connect(SERVER+DEVKEY);
|
被呼叫部分:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
//被呼叫者发布监听流,以便呼叫者连接
listenStream =
new
NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
listenStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
listenStream.publish(username);
//呼叫者一旦订阅上面的监听流,就会触发onPeerConnect事件
var
c:
Object
=
new
Object
();
c.onPeerConnect =
function
(caller:NetStream):
Boolean
{
if
(callState == CallReady)
{
//接受呼叫者发布的视频语音流
callState = CallRinging;
idManager.change(callState);
busyState = busyOn;
incomingStream =
new
NetStream(netConnection,caller.farID);
incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
video =
new
Video();
video.attachNetStream(incomingStream);
remoteVideoDisplay.addChild(video);
incomingStream.play(
"caller"
);
var
st:SoundTransform =
new
SoundTransform(speakerVolumeSlider.value);
incomingStream.soundTransform = st;
//处理呼叫的方法
var
i:
Object
=
new
Object
();
i.onIncomingCall =
function
(caller:
String
):
void
{
if
(callState != CallRinging)
{
txtInfo.text +=
"onIncomingCall: Wrong call state: "
+ callState +
"\n"
;
return
;
}
send_bn.enabled=
true
;
txtInfo.text += caller +
"已经成功与您连接上\n"
;
partnername = caller;
//outgoingStream.send("onCallAccepted", username);
callState = CallEstablished;
//idManager.change(callState);
}
i.onIm =
function
(caller:
String
,text:
String
):
void
{
txtMessage.text += caller+
": "
+text+
"\n"
;
}
i.onDisconnected =
function
(caller:
String
):
void
{
txtInfo.text += caller+
"和你断开连接\n"
;
send_bn.enabled=
false
;
stop();
}
incomingStream.client = i;
//对呼叫者发布自己的语音视频流
outgoingStream =
new
NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,callee_outgoingStreamHandler);
outgoingStream.attachCamera(camera);
outgoingStream.attachAudio(mic);
outgoingStream.publish(
"callee"
);
return
true
;
}
txtInfo.text +=
"onPeerConnect: all rejected due to state: "
+ callState +
"\n"
;
return
false
;
}
listenStream.client = c;
callState = CallReady;
|
呼叫部分:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
callState = CallCalling;
idManager.change(callState);
busyState = busyOn;
//播放被呼叫者的监听流
controlStream =
new
NetStream(netConnection,farPeerID);
controlStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
controlStream.play(partnername);
//对被呼叫者发布自己的视频语音流
outgoingStream =
new
NetStream(netConnection,NetStream.DIRECT_CONNECTIONS);
outgoingStream.addEventListener(NetStatusEvent.NET_STATUS,caller_outgoingStreamHandler);
outgoingStream.attachCamera(camera);
outgoingStream.attachAudio(mic);
outgoingStream.publish(
"caller"
);
txtInfo.text +=
"正在与"
+partnername+
"建立连接\n"
;
//播放被呼叫者的视频语音流
incomingStream =
new
NetStream(netConnection,farPeerID);
incomingStream.addEventListener(NetStatusEvent.NET_STATUS,statusHandler);
video =
new
Video();
video.attachNetStream(incomingStream);
remoteVideoDisplay.addChild(video);
incomingStream.play(
"callee"
);
var
st:SoundTransform =
new
SoundTransform(speakerVolumeSlider.value);
incomingStream.soundTransform = st;
//处理呼叫的方法
var
i:
Object
=
new
Object
();
i.onIm =
function
(callee:
String
,text:
String
):
void
{
txtMessage.text += callee+
": "
+text+
"\n"
;
}
i.onCallAccepted =
function
(callee:
String
):
void
{
if
(callState != CallCalling)
{
txtInfo.text +=
"连接失败"
;
return
;
}
send_bn.enabled=
true
;
txtInfo.text += callee+
"已经成功与您连接上\n"
;
callState = CallEstablished;
//idManager.change(callState);
}
i.onDisconnected =
function
(callee:
String
):
void
{
txtInfo.text += callee+
"和你断开连接\n"
;
send_bn.enabled=
false
;
stop();
}
incomingStream.client = i;
|
二、用户数据库及webservice接口
这个项目我使用MySQL进行用户数据库设计,用户端产生相应行为后(如登录,下线,聊天,空闲等)会在数据库中更新自己的状态,其他用户根据查询得到的结果进行操作。数据库结构见下图:
用户名 peerID 更新时间 在线状态 聊天状态

该项目只要进行简单的数据库操作就可以了,所以我使用了HTTPService组件和一个简单的php网页进行与数据库的交互。
HTTPService组件位于mx.rpc.http包下,当调用 HTTPService 对象的 send() 方法时,将发出对指定 URL 的 HTTP 请求,并且返回 HTTP 响应。可以选择向指定 URL 传递参数。项目中通过HTTPService组件传递用户端的参数到php网页,由php根据相应的参数操作数据库,返回相应的结果交由用户端执行。
HTTPService组件部分:
|
1
2
|
mHttpService =
new
HTTPService();
mHttpService.url = mWebServiceUrl;
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//添加对结果事件的侦听
mHttpService.addEventListener(
"result"
, httpResult);
mHttpService.addEventListener(
"fault"
, httpFault);
//实例化request请求对象并发送
var
request:
Object
=
new
Object
();
var
now:
Date
=
new
Date
();
request.time = now.getTime();
request.username = user;
request.identity = id;
request.online = x;
request.busy = x;
mHttpService.cancel();
mHttpService.send(request);
|
php网页部分:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<?php
$output =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
;
$output .=
"<result>"
;
if
( isset($_GET[
"username"
]) || isset($_GET[
"friends"
]) || isset($_GET[
"fetch"
]))
{
define(
"DATABASE_SERVER"
,
"localhost"
);
define(
"DATABASE_USERNAME"
,
"root"
);
define(
"DATABASE_PASSWORD"
,
"root"
);
define(
"DATABASE_NAME"
,
"root"
);
$mysql = mysql_connect( DATABASE_SERVER,
DATABASE_USERNAME,
DATABASE_PASSWORD )
or die( mysql_error() );
mysql_select_db( DATABASE_NAME );
}
//注册更新部分
if
( isset($_GET[
"username"
]) )
{
$user = mysql_real_escape_string( $_GET[
"username"
] );
$identity = mysql_real_escape_string( $_GET[
"identity"
] );
$online = mysql_real_escape_string( $_GET[
"online"
] );
$busy = mysql_real_escape_string( $_GET[
"busy"
] );
$query =
"SELECT * FROM registrations WHERE m_username='$user' ;"
;
$result = mysql_query( $query );
$isNew = ($result && mysql_num_rows($result) ==
0
);
if
( $isNew ) {
$query =
"INSERT INTO registrations SET "
;
$query .=
"m_username='$user', m_identity='$identity', m_updatetime=now(), m_online='$online', m_busy='$busy';"
;
}
else
{
$query =
"UPDATE registrations SET "
;
$query .=
" m_identity='$identity', m_updatetime=now(), m_online='$online', m_busy='$busy' where m_username='$user';"
;
}
$result = mysql_query( $query );
if
( $isNew )
$output .=
"<update>true</update>"
;
else
$output .=
"<update>false</update>"
;
}
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//查找用户部分
if
( isset($_GET[
"friends"
]) )
{
$friends = mysql_real_escape_string( $_GET[
"friends"
] );
$output .=
"<friend><user>$friends</user>"
;
$query =
"select * from registrations where m_username = '$friends' and TIMEDIFF(now(),m_updatetime) >0 ;"
;
$result = mysql_query( $query );
if
( $result ) {
while
( $item = mysql_fetch_assoc( $result ) ) {
$output .=
"<identity>"
.$item[
"m_identity"
].
"</identity>"
;
}
}
$output .=
"</friend>"
;
}
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
//获取返回结果
if
( isset( $_GET[
"fetch"
]) )
{
$time = mysql_real_escape_string( $_GET[
"time"
] );
$query =
"select m_number from registrations order by m_username asc ;"
;
$result =mysql_query($query);
if
( $result )
{
$num = mysql_num_rows($result);
$output .=
"<fetch><num>"
.$num.
"</num>"
;
}
$query =
"select m_username from registrations where m_online ='1' and m_busy = '0' order by m_username asc ;"
;
$result = mysql_query($query);
if
( $result )
{
$onlinenum = mysql_num_rows($result);
$output .=
"<onlinenum>"
.$onlinenum.
"</onlinenum><users>"
;
$i =
1
;
while
($row = mysql_fetch_row($result))
{
$output .=
"<user"
.$i.
">"
.$row[
0
].
"</user"
.$i.
">"
;
$i++;
}
$output .=
"</users></fetch>"
;";
}
}
$output .=
"</result>"
;
header(
"Content-Type: text/xml"
);
echo $output;
?>
|
数据库返回所有用户结果如下:
代码
本文介绍了如何使用Flex、Stratus、MySQL和PHP构建一个简易的P2P视频聊天室,包括P2P语音视频功能模块、用户数据库及Webservice接口的设计与实现。详细解释了如何实现P2P通话、数据库操作和前后端通信。

1730

被折叠的 条评论
为什么被折叠?



