基于stratus +flex+MySQL的简易在线随机视频聊天室的开发

本文介绍了如何使用Flex、Stratus、MySQL和PHP构建一个简易的P2P视频聊天室,包括P2P语音视频功能模块、用户数据库及Webservice接口的设计与实现。详细解释了如何实现P2P通话、数据库操作和前后端通信。

前段时间听说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 服务器地址
private const SERVER: String = "rtmfp://stratus.adobe.com/" ;
//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;
?>

数据库返回所有用户结果如下:

代码
复制代码
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值