push camera to web

Table of Contents

To exchange data over a datachannel using libdatachannel in C++, we’ll need to set up a signaling server (in Python) to exchange the necessary WebRTC connection information (SDP and ICE candidates), and a STUN/TURN server using coturn for NAT traversal.

components:

  1. C++ Client and Server using libdatachannel
  • We’ll use libdatachannel to establish WebRTC peer-to-peer connections.
  • Data will be exchanged via the WebRTC DataChannel.
  1. Python Signaling Server
  • This server will handle the signaling process, exchanging WebRTC SDP offers/answers and ICE candidates between the peers.
  • For simplicity, we can use WebSockets for signaling.
  1. STUN/TURN Server using coturn
  • We’ll set up coturn as a STUN/TURN server to assist with NAT traversal.

Implementation steps

Step 1: STUN/TURN Server (coturn)

Install coturn using the package manager and configure it to act as a STUN/TURN server.

sudo apt-get install coturn

Configure /etc/turnserver.conf with:

listening-port=3478
fingerprint
lt-cred-mech
use-auth-secret
static-auth-secret=your-secret-key
realm=www.fanyamin.com
server-name=www.fanyamin.com

Start coturn:

sudo turnserver -v

Step 2: Python Signaling Server

  • install library
pip install websockets

Create a simple WebSocket signaling server in Python using websockets:

import asyncio
import websockets
import json

clients = {}

async def signaling(websocket, path):
    async for message in websocket:
        data = json.loads(message)
        peer_id = data['peer_id']
        if data['type'] == 'offer' or data['type'] == 'answer':
            recipient = clients.get(data['to'], None)
            if recipient:
                await recipient.send(json.dumps(data))
        elif data['type'] == 'register':
            clients[peer_id] = websocket

async def main():
    async with websockets.serve(signaling, "0.0.0.0", 8765):
        await asyncio.Future()

asyncio.run(main())

This WebSocket server will exchange SDP and ICE candidates between peers.

Step 3: C++ Client and Server using libdatachannel

You’ll need to install libdatachannel (you can use Conan if you prefer).

#include <rtc/rtc.hpp>
#include <iostream>

void setup_peer(rtc::PeerConnection &pc, const std::string &role) {
    // DataChannel for sending messages
    auto dc = pc.createDataChannel("data");

    dc->onOpen([role]() {
        std::cout << role << " DataChannel open!" << std::endl;
    });

    dc->onMessage([](std::variant<rtc::binary, std::string> message) {
        if (std::holds_alternative<std::string>(message)) {
            std::cout << "Received message: " << std::get<std::string>(message) << std::endl;
        }
    });
}

int main() {
    // Configure STUN server
    rtc::Configuration config;
    config.iceServers.emplace_back("stun:your-stun-server.com");

    rtc::PeerConnection pc(config);

    setup_peer(pc, "Client");

    // Exchange SDP via signaling server
    pc.onLocalDescription([](rtc::Description desc) {
        std::cout << "Local SDP: " << std::string(desc) << std::endl;
        // Send SDP to signaling server (use WebSocket)
    });

    pc.onGatheringStateChange([](rtc::PeerConnection::GatheringState state) {
        if (state == rtc::PeerConnection::GatheringState::Complete) {
            std::cout << "ICE gathering complete." << std::endl;
        }
    });

    // Connect to signaling server and exchange SDPs and ICE candidates
    // Implement the signaling logic using WebSockets.

    return 0;
}

Workflow

  1. Client and Server both connect to the Python signaling server via WebSockets.
  2. Client sends an SDP offer through the signaling server.
  3. Server responds with an SDP answer.
  4. Both peers exchange ICE candidates until a connection is established.
  5. Data is exchanged over the DataChannel.

Example

  1. Use gstreamer to push video
$ gst-launch-1.0 v4l2src device=/dev/video0 \
! video/x-raw,width=640,height=480 \
! videoconvert ! queue \
! x264enc tune=zerolatency bitrate=1000 key-int-max=30 \
! video/x-h264, profile=constrained-baseline \
! rtph264pay pt=96 mtu=1200 \
! udpsink host=127.0.0.1 port=6000
  • v4l2src 从摄像头捕获视频流, 分辨率为 640*480
  • x264enc 将视频流编码为 h264 codec
  • rtph264pay 将视频流打包为 RTP packet
  • udpsink 将 rtp packet 以 udp 包发送出去
  1. Use cpp/web client to receive the video

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>libdatachannel media sender example</title>
</head>
<body>

<p>Please enter the offer provided to you by the sender application: </p>
<textarea cols="80" rows="25"></textarea>
<button>Submit</button>

<video id="video-element" muted></video>

<script>
    document.querySelector('button').addEventListener('click',  async () => {
        const offer = JSON.parse(document.querySelector('textarea').value);
        const pc = new RTCPeerConnection({
            // Recommended for libdatachannel
            bundlePolicy: 'max-bundle',
        });

        pc.onicegatheringstatechange = (state) => {
            if (pc.iceGatheringState === 'complete') {
                // We only want to provide an answer once all of our candidates have been added to the SDP.
                const answer = pc.localDescription;
                document.querySelector('textarea').value = JSON.stringify({"type": answer.type, sdp: answer.sdp});
                document.querySelector('p').value = 'Please paste the answer in the sender application.';
                alert('Please paste the answer in the sender application.');
            }
        }

        pc.ontrack = (evt) => {
            const videoElement = document.getElementById('video-element');
            videoElement.srcObject = evt.streams[0];
            videoElement.play();
        };

        await pc.setRemoteDescription(offer);

        const answer = await pc.createAnswer();
        await pc.setLocalDescription(answer);
    })
</script>

</body>
</html>

Comments |0|

Legend *) Required fields are marked
**) You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Category: 似水流年