WebRTC一些记录

WebRTC一些记录

用python实现一个简单的信令服务器

# signaling_server.py
import asyncio
import json
import websockets
from aiohttp import web
from pprint import pprint

# 存储所有连接的客户端
clients = {}

async def register_client(websocket):
    # 将新连接的客户端存储在字典中
    pprint(websocket)
    clients[websocket] = {'peer_id': None, 'other_peer': None, 'event': asyncio.Event()}

async def unregister_client(websocket):
    # 从字典中移除断开连接的客户端
    if websocket in clients:
        other_peer = clients[websocket]['other_peer']
        if other_peer:
            await other_peer.send_str(json.dumps({'type': 'peer_disconnected'}))
            clients[other_peer]['other_peer'] = None
        del clients[websocket]

async def handle_client_message(websocket, message):
    # 解析客户端发送的消息
    data = json.loads(message)
    #pprint(data)
    peer_id = clients[websocket]['peer_id']

    if data['type'] == 'offer':
        # 存储offer和发送给另一个客户端
        clients[websocket]['offer'] = data['offer']
        await clients[websocket]['event'].wait()
        print("send to")
        pprint(clients[websocket]['other_peer'])
        await clients[websocket]['other_peer'].send_str(json.dumps({
            'type': 'offer',
            'offer': data['offer']
        }))
    elif data['type'] == 'answer':
        # 发送answer给另一个客户端
        await clients[websocket]['other_peer'].send_str(json.dumps({
            'type': 'answer',
            'answer': data['answer']
        }))
    elif data['type'] == 'candidate':
        # 发送ICE candidate给另一个客户端
        print("send candidate")
        pprint(data['candidate'])
        await clients[websocket]['other_peer'].send_str(json.dumps({
            'type': 'candidate',
            'candidate': data['candidate']
        }))
    elif data['type'] == 'register':
        # 注册客户端ID并尝试匹配两个客户端
        print("register........", data['peer_id'])
        clients[websocket]['peer_id'] = data['peer_id']
        if int(clients[websocket]['peer_id']) > 0:
            for client, info in clients.items():
                if client != websocket and int(info['peer_id']) == 0 and not info['other_peer']:
                    clients[client]['other_peer'] = websocket
                    clients[websocket]['other_peer'] = client
                    print("set peer")
                    pprint(client)
                    clients[client]['event'].set()
                    clients[websocket]['event'].set()
                    #await client.send(json.dumps({'type': 'other_peer_connected'}))
                    #await websocket.send(json.dumps({'type': 'other_peer_connected'}))
                    break


async def websocket_handler(request):
    # 使用aiohttp创建WebSocket连接
    ws = web.WebSocketResponse()
    await ws.prepare(request)

    await register_client(ws)
    try:
        async for msg in ws:
            if msg.type == web.WSMsgType.TEXT:
                await handle_client_message(ws, msg.data)
            else:
                break
    except websockets.exceptions.ConnectionClosed:
        await unregister_client(ws)



async def static_handler(request):
    # 提供静态文件服务
    return web.FileResponse(request.match_info['filename'])

app = web.Application()

# 添加WebSocket路由
app.add_routes([web.get('/ws', websocket_handler)])

# 添加静态文件服务路由
app.add_routes([web.get('/offer.html', static_handler)])

# 运行服务器
if __name__ == '__main__':
    # 静态文件目录
    app.router.add_static('/static/', path='./', name='static')
    web.run_app(app, host='0.0.0.0', port=8080)
offer.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebRTC Data Channel Offer</title>
</head>
<body>
    <h1>WebRTC Data Channel Offer</h1>
    <button id="connectButton">Connect to WebSocket Server</button>
    <textarea id="message" rows="4" cols="50"></textarea>
    <button id="sendButton">Send</button>
    <script>
        const connectButton = document.getElementById('connectButton');
        const sendButton = document.getElementById('sendButton');
        const messageInput = document.getElementById('message');

        let peerConnection;
        let dataChannel;
        let websocket;

        function createOffer() {
                        console.log('reg : ');
                        sendMessage({'type': 'register', 'peer_id': "0"});
            peerConnection = new RTCPeerConnection();

            dataChannel = peerConnection.createDataChannel('myDataChannel');

            peerConnection.onicecandidate = (event) => {
                if (event.candidate) {
                        console.log('send candidate: ');
                    sendMessage({'type': 'candidate', 'candidate': event.candidate});
                }
            };

                        dataChannel.onmessage = (event) => {
                                console.log('Received message:', event.data);
                        };


            peerConnection.createOffer().then((offer) => {
                return peerConnection.setLocalDescription(offer);
            }).then(() => {
                                console.log('send offer: ');
                sendMessage({'type': 'offer', 'offer': peerConnection.localDescription});
            }).catch((error) => {
                console.error('Error creating offer:', error);
            });
        }

        function sendMessage(message) {
            websocket.send(JSON.stringify(message));
        }

        sendButton.addEventListener('click', () => {
            const message = messageInput.value;
            dataChannel.send(message);
        });

        connectButton.addEventListener('click', () => {
            // 连接到WebSocket服务器
            websocket = new WebSocket('ws://10.3.10.192:8080/ws');
            websocket.onopen = (event) => {
                console.log('Connected to WebSocket server:', event);
                createOffer();
            };
            websocket.onmessage = (event) => {
                const message = JSON.parse(event.data);
                if (message.type === 'answer') {
                        console.log('recv answer:', message);
                    peerConnection.setRemoteDescription(new RTCSessionDescription(message.answer));
                } else if (message.type === 'candidate') {
                        console.log('recv candidate:', message);
                    peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate));
                }
            };
            websocket.onerror = (error) => {
                console.error('WebSocket error:', error);
            };
        });

    </script>
</body>
</html>
answer.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebRTC Data Channel Answer</title>
</head>
<body>
    <h1>WebRTC Data Channel Answer</h1>
    <button id="connectButton">Connect to WebSocket Server</button>
    <textarea id="message" rows="4" cols="50"></textarea>
    <button id="sendButton">Send</button>
    <script>
        const connectButton = document.getElementById('connectButton');
        const sendButton = document.getElementById('sendButton');
        const messageInput = document.getElementById('message');

        let peerConnection;
        let dataChannel;
        let websocket;

        function createAnswer(offer) {
            peerConnection = new RTCPeerConnection();

            peerConnection.ondatachannel = (event) => {
                dataChannel = event.channel;
                dataChannel.onmessage = (event) => {
                    console.log('Received message:', event.data);
                };
            };

            peerConnection.onicecandidate = (event) => {
                if (event.candidate) {
                    console.log('send candidate:');
                    sendMessage({'type': 'candidate', 'candidate': event.candidate});
                }
            };

            peerConnection.setRemoteDescription(offer).then(() => {
                                console.log('setRemoteDescription:');
                return peerConnection.createAnswer();
            }).then((answer) => {
                return peerConnection.setLocalDescription(answer);
            }).then(() => {
                                console.log('send answer:');
                sendMessage({'type': 'answer', 'answer': peerConnection.localDescription});
            }).catch((error) => {
                console.error('Error creating answer:', error);
            });
        }

        function sendMessage(message) {
            websocket.send(JSON.stringify(message));
        }

        sendButton.addEventListener('click', () => {
            const message = messageInput.value;
                        console.log('send text :', message);
            dataChannel.send(message);
        });

        connectButton.addEventListener('click', () => {
            // 连接到WebSocket服务器
            websocket = new WebSocket('ws://10.3.10.192:8080/ws');
            websocket.onopen = (event) => {
                console.log('Connected to WebSocket server:', event);
                                sendMessage({'type': 'register', 'peer_id': "1"});
            };
            websocket.onmessage = (event) => {
                const message = JSON.parse(event.data);
                if (message.type === 'offer') {
                                        console.log('recv offer:');
                    createAnswer(new RTCSessionDescription(message.offer));
                } else if (message.type === 'candidate') {
                                        console.log('recv candidate:');
                    peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate));
                }
            };
            websocket.onerror = (error) => {
                console.error('WebSocket error:', error);
            };
        });

    </script>
</body>
</html>
python 的客户端

import asyncio
from pprint import pprint
import json
from aiortc import RTCPeerConnection, RTCSessionDescription, RTCIceCandidate
from aiortc.contrib.media import MediaBlackhole
import websockets

# 配置信令服务器的URL
SIGNALLING_SERVER = 'ws://10.3.10.192:8080/ws'

# 创建一个PeerConnection实例
pc = RTCPeerConnection()

# 创建一个数据通道
data_channel = pc.createDataChannel('DataChannel')

@data_channel.on('message')
def on_message(message):
        print('收到消息:', message)

@data_channel.on("open")
def on_open():
        data_channel.send("1231231231223")
        print("on datachannel")

async def connect_signalling_server():
        # 连接到信令服务器
        async with websockets.connect(SIGNALLING_SERVER) as ws:
                # 发送candidate
                @pc.on('icecandidate')
                def on_icecandidate(candidate):
                        if candidate:
                                print('发送candidate:', candidate)
                                ws.send(json.dumps({
                                                'type': 'candidate',
                                                'candidate':{
                                                        'type': 'candidate',
                                                        'candidate': candidate.to_dict()
                                                }
                                                }))


                await ws.send(json.dumps({'type': 'register', 'peer_id': "0"}))

                offer = await pc.createOffer()
                await pc.setLocalDescription(offer)

                out = pc.localDescription
                # 发送offer到信令服务器
                print('发送offer')
                await ws.send(json.dumps({
                        'type': 'offer',
                        'offer':{
                                'type': out.type,
                                'sdp': out.sdp
                        }
                }))

                # 接收信令服务器发送的消息
                while True:
                        message = await ws.recv()
                        data = json.loads(message)

                        if data['type'] == 'answer':
                                # 接收到answer,设置远程描述
                                print('接收到answer')
                                sdp_data = data['answer']
                                pprint(data)
                                await pc.setRemoteDescription(RTCSessionDescription(sdp=sdp_data['sdp'], type=sdp_data['type']))
                        elif data['type'] == 'offer':
                                # 接收到offer,设置远程描述并创建answer
                                print('接收到offer')
                                pprint(data)
                                await pc.setRemoteDescription(RTCSessionDescription(sdp=data['sdp'], type=data['type']))
                                answer = await pc.createAnswer()
                                await pc.setLocalDescription(answer)

                                # 发送answer到信令服务器
                                print('发送answer')
                                await ws.send(json.dumps({
                                        'type': answer.type,
                                        'sdp': answer.sdp
                                        }))
                        elif data['type'] == 'candidate':
                                # 接收到candidate,将其添加到PeerConnection
                                print('接收到candidate')
                                pprint(data)
                                sdp_data = data['candidate']
                                #candidate = RTCIceCandidate(**sdp_data['candidate'])
                                #await pc.addIceCandidate(candidate)

# 运行客户端
asyncio.get_event_loop().run_until_complete(connect_signalling_server())

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Back To Top