(janus, out var op))
131 | {
132 | return op;
133 | }
134 | else
135 | {
136 | return JanusOperationsEnum.unknown;
137 | }
138 | }
139 | set
140 | {
141 | janus = value.ToString();
142 | }
143 | }
144 | }
145 |
146 | ///
147 | /// Response for /info.
148 | /// TODO: Numerous additional properties are available from the Janus info response.
149 | ///
150 | public class ServerInfo
151 | {
152 | public string name { get; set; }
153 | public int version { get; set; }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/gstreamer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
22 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/libwebrtc/webrtc_lib_link_test_libevent.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 The WebRTC Project Authors. All rights reserved.
3 | *
4 | * Use of this source code is governed by a BSD-style license
5 | * that can be found in the LICENSE file in the root of the source
6 | * tree. An additional intellectual property rights grant can be found
7 | * in the file PATENTS. All contributing project authors may
8 | * be found in the AUTHORS file in the root of the source tree.
9 | */
10 |
11 | #include "api/audio_codecs/audio_decoder_factory_template.h"
12 | #include "api/audio_codecs/audio_encoder_factory_template.h"
13 | #include "api/audio_codecs/opus/audio_decoder_opus.h"
14 | #include "api/audio_codecs/opus/audio_encoder_opus.h"
15 | #include "api/call/call_factory_interface.h"
16 | #include "api/create_peerconnection_factory.h"
17 | #include "api/peer_connection_interface.h"
18 | #include "api/rtc_event_log/rtc_event_log_factory.h"
19 | #include "api/stats/rtcstats_objects.h"
20 | #include "api/task_queue/default_task_queue_factory.h"
21 | #include "api/video_codecs/builtin_video_decoder_factory.h"
22 | #include "api/video_codecs/builtin_video_encoder_factory.h"
23 | #include "media/engine/webrtc_media_engine.h"
24 | #include "modules/audio_device/include/audio_device.h"
25 | #include "modules/audio_processing/include/audio_processing.h"
26 | #include
27 |
28 | namespace webrtc {
29 |
30 | cricket::MediaEngineDependencies CreateSomeMediaDeps(
31 | TaskQueueFactory* task_queue_factory) {
32 | cricket::MediaEngineDependencies media_deps;
33 | media_deps.task_queue_factory = task_queue_factory;
34 | media_deps.adm = AudioDeviceModule::CreateForTest(
35 | AudioDeviceModule::kDummyAudio, task_queue_factory);
36 | media_deps.audio_encoder_factory =
37 | webrtc::CreateAudioEncoderFactory();
38 | media_deps.audio_decoder_factory =
39 | webrtc::CreateAudioDecoderFactory();
40 | media_deps.video_encoder_factory = CreateBuiltinVideoEncoderFactory();
41 | media_deps.video_decoder_factory = webrtc::CreateBuiltinVideoDecoderFactory();
42 | media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create();
43 | return media_deps;
44 | }
45 |
46 | webrtc::PeerConnectionFactoryDependencies CreateSomePcfDeps() {
47 | webrtc::PeerConnectionFactoryDependencies pcf_deps;
48 | pcf_deps.task_queue_factory = CreateDefaultTaskQueueFactory();
49 | pcf_deps.signaling_thread = rtc::Thread::Current();
50 | pcf_deps.network_thread = rtc::Thread::Current();
51 | pcf_deps.worker_thread = rtc::Thread::Current();
52 | pcf_deps.call_factory = webrtc::CreateCallFactory();
53 | pcf_deps.event_log_factory = std::make_unique(
54 | pcf_deps.task_queue_factory.get());
55 | auto media_deps = CreateSomeMediaDeps(pcf_deps.task_queue_factory.get());
56 | pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
57 | return pcf_deps;
58 | }
59 |
60 | // NOTE: These "test cases" should pull in as much of WebRTC as possible to make
61 | // sure most commonly used symbols are actually in libwebrtc.a. It's entirely
62 | // possible these tests won't work at all times (maybe crash even), but that's
63 | // fine.
64 | void TestCase1ModularFactory() {
65 | auto pcf_deps = CreateSomePcfDeps();
66 | auto peer_connection_factory =
67 | webrtc::CreateModularPeerConnectionFactory(std::move(pcf_deps));
68 | webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
69 | auto peer_connection = peer_connection_factory->CreatePeerConnection(
70 | rtc_config, nullptr, nullptr, nullptr);
71 | printf("peer_connection=%s\n", peer_connection == nullptr ? "nullptr" : "ok");
72 | }
73 |
74 | void TestCase2RegularFactory() {
75 | auto task_queue_factory = CreateDefaultTaskQueueFactory();
76 | auto media_deps = CreateSomeMediaDeps(task_queue_factory.get());
77 |
78 | auto peer_connection_factory = webrtc::CreatePeerConnectionFactory(
79 | rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
80 | std::move(media_deps.adm), std::move(media_deps.audio_encoder_factory),
81 | std::move(media_deps.audio_decoder_factory),
82 | std::move(media_deps.video_encoder_factory),
83 | std::move(media_deps.video_decoder_factory), nullptr, nullptr);
84 | webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
85 | auto peer_connection = peer_connection_factory->CreatePeerConnection(
86 | rtc_config, nullptr, nullptr, nullptr);
87 | printf("peer_connection=%s\n", peer_connection == nullptr ? "nullptr" : "ok");
88 | }
89 |
90 | } // namespace webrtc
91 |
92 | int main(int argc, char** argv) {
93 | webrtc::TestCase1ModularFactory();
94 | webrtc::TestCase2RegularFactory();
95 | printf("libevent version %s.\n", event_get_version());
96 | return 0;
97 | }
98 |
--------------------------------------------------------------------------------
/pion/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "log"
9 | "net/http"
10 | "strings"
11 | "sync"
12 |
13 | "github.com/pion/webrtc/v3"
14 | )
15 |
16 | var (
17 | pcs []*webrtc.PeerConnection
18 | pcsLock sync.RWMutex
19 | )
20 |
21 | func offer(w http.ResponseWriter, r *http.Request) {
22 | var offer webrtc.SessionDescription
23 | if err := json.NewDecoder(r.Body).Decode(&offer); err != nil {
24 | log.Fatal(err)
25 | }
26 |
27 | pc, err := webrtc.NewPeerConnection(webrtc.Configuration{})
28 | if err != nil {
29 | log.Fatal(err)
30 | }
31 |
32 | pcsLock.Lock()
33 | pcs = append(pcs, pc)
34 | pcsLock.Unlock()
35 |
36 | // // Set the handler for ICE connection state
37 | // // This will notify you when the peer has connected/disconnected
38 | pc.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
39 | fmt.Printf("ICE connection state has changed to %s.\n", connectionState.String())
40 |
41 | if connectionState == webrtc.ICEConnectionStateFailed {
42 | pcsLock.Lock()
43 | defer pcsLock.Unlock()
44 |
45 | for i := range pcs {
46 | if pcs[i] == pc {
47 | if err = pc.Close(); err != nil {
48 | log.Fatal(err)
49 | }
50 |
51 | pcs = append(pcs[:i], pcs[i+1:]...)
52 | }
53 | }
54 | }
55 | })
56 |
57 | audioTrack := &echoTrack{id: "audio", streamID: "pion", kind: webrtc.RTPCodecTypeAudio}
58 | if _, err = pc.AddTrack(audioTrack); err != nil {
59 | log.Fatal(err)
60 | }
61 |
62 | videoTrack := &echoTrack{id: "video", streamID: "pion", kind: webrtc.RTPCodecTypeVideo}
63 | if _, err = pc.AddTrack(videoTrack); err != nil {
64 | log.Fatal(err)
65 | }
66 |
67 | pc.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
68 | outTrack := videoTrack
69 | if strings.HasPrefix(track.Codec().MimeType, "audio") {
70 | outTrack = audioTrack
71 | }
72 |
73 | fmt.Printf("Track has started, of type %d: %s \n", track.PayloadType(), track.Codec().MimeType)
74 | for {
75 | rtp, _, err := track.ReadRTP()
76 | if err != nil {
77 | if err == io.EOF {
78 | return
79 | }
80 |
81 | log.Fatal(err)
82 | }
83 |
84 | rtp.SSRC = uint32(outTrack.ctx.SSRC())
85 | if _, err = outTrack.ctx.WriteStream().WriteRTP(&rtp.Header, rtp.Payload); err != nil {
86 | log.Fatal(err)
87 | }
88 | }
89 |
90 | })
91 |
92 | pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
93 | fmt.Printf("Peer connection state has changed to %s.\n", state.String())
94 | })
95 |
96 | pc.OnSignalingStateChange(func(state webrtc.SignalingState) {
97 | fmt.Printf("Signaling state has changed to %s.\n", state.String())
98 | })
99 |
100 | if err := pc.SetRemoteDescription(offer); err != nil {
101 | log.Fatal(err)
102 | }
103 |
104 | answer, err := pc.CreateAnswer(nil)
105 | if err != nil {
106 | log.Fatal(err)
107 | } else if err = pc.SetLocalDescription(answer); err != nil {
108 | log.Fatal(err)
109 | }
110 |
111 | response, err := json.Marshal(pc.LocalDescription())
112 | if err != nil {
113 | log.Fatal(err)
114 | }
115 |
116 | w.Header().Set("Content-Type", "application/json")
117 | if _, err := w.Write(response); err != nil {
118 | log.Fatal(err)
119 | }
120 |
121 | }
122 |
123 | func main() {
124 | certFile := flag.String("cert-file", "", "SSL certificate file (for HTTPS)")
125 | keyFile := flag.String("key-file", "", "SSL key file (for HTTPS)")
126 | host := flag.String("host", "0.0.0.0", "Host for HTTP server (default: 0.0.0.0)")
127 | port := flag.Int("port", 8080, "Port for HTTP server (default: 8080)")
128 | flag.Parse()
129 |
130 | pcs = []*webrtc.PeerConnection{}
131 |
132 | http.Handle("/", http.FileServer(http.Dir("../html")))
133 | http.HandleFunc("/offer", offer)
134 |
135 | addr := fmt.Sprintf("%s:%d", *host, *port)
136 |
137 | fmt.Printf("Listening on %s...\n", addr)
138 |
139 | if *keyFile != "" && *certFile != "" {
140 | log.Fatal(http.ListenAndServeTLS(addr, *certFile, *keyFile, nil))
141 | } else {
142 | log.Fatal(http.ListenAndServe(addr, nil))
143 | }
144 | }
145 |
146 | // echoTrack just passes through RTP packets
147 | // in a real application you will want to confirm that
148 | // the receiver supports the codec and updating PayloadTypes if needed
149 | type echoTrack struct {
150 | id, streamID string
151 | kind webrtc.RTPCodecType
152 | ctx webrtc.TrackLocalContext
153 | }
154 |
155 | func (e *echoTrack) Bind(c webrtc.TrackLocalContext) (webrtc.RTPCodecParameters, error) {
156 | e.ctx = c
157 | return c.CodecParameters()[0], nil
158 | }
159 |
160 | func (e *echoTrack) Unbind(webrtc.TrackLocalContext) error { return nil }
161 | func (e *echoTrack) ID() string { return e.id }
162 | func (e *echoTrack) StreamID() string { return e.streamID }
163 | func (e *echoTrack) Kind() webrtc.RTPCodecType { return e.kind }
164 |
--------------------------------------------------------------------------------
/.github/workflows/datachannel_echo-test.yml:
--------------------------------------------------------------------------------
1 | name: WebRTC Data Channel Interoperability Test
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 | repository_dispatch:
11 | types: [datachannel-test-command]
12 |
13 | # Allows you to run this workflow manually from the Actions tab
14 | workflow_dispatch:
15 |
16 | jobs:
17 | # Job Description: Performs the Data Channel Test between each combination of the client and server for each library.
18 | interoptests:
19 | runs-on: ubuntu-latest
20 |
21 | services:
22 |
23 | echo-test-server:
24 | image: ghcr.io/sipsorcery/${{ matrix.server }}-webrtc-echo
25 | credentials:
26 | username: ${{ github.actor }}
27 | password: ${{ secrets.CR_PAT }}
28 | #ports:
29 | # - 8080:8080
30 | options: "--name echo-server"
31 |
32 | outputs:
33 | result_libdatachannel_libdatachannel: ${{ steps.set-output.outputs.result_libdatachannel_libdatachannel }}
34 | result_libdatachannel_sipsorcery: ${{ steps.set-output.outputs.result_libdatachannel_sipsorcery }}
35 | result_libdatachannel_werift: ${{ steps.set-output.outputs.result_libdatachannel_werift }}
36 | result_sipsorcery_libdatachannel: ${{ steps.set-output.outputs.result_sipsorcery_libdatachannel }}
37 | result_sipsorcery_sipsorcery: ${{ steps.set-output.outputs.result_sipsorcery_sipsorcery }}
38 | result_sipsorcery_werift: ${{ steps.set-output.outputs.result_sipsorcery_werift }}
39 | result_werift_libdatachannel: ${{ steps.set-output.outputs.result_werift_libdatachannel }}
40 | result_werift_sipsorcery: ${{ steps.set-output.outputs.result_werift_sipsorcery }}
41 | result_werift_werift: ${{ steps.set-output.outputs.result_werift_werift }}
42 |
43 | strategy:
44 | matrix:
45 | server: ["libdatachannel", "sipsorcery", "werift"]
46 | client: ["libdatachannel", "sipsorcery", "werift"]
47 |
48 | steps:
49 |
50 | - name: Data Channel test for server ${{ matrix.server }} and ${{ matrix.client }} client
51 | id: check_connection
52 | run: |
53 | docker run --entrypoint "/client.sh" --network ${{ job.container.network }} ghcr.io/sipsorcery/${{ matrix.client }}-webrtc-echo "-s http://echo-server:8080/offer -t 1"
54 | result=$?
55 | echo "Check connection for ${{ matrix.server }} server and ${{ matrix.client }} client result $result."
56 | echo "::set-output name=TEST_RESULT::$result"
57 | continue-on-error: true
58 |
59 | - name: Set output results
60 | id: set-output
61 | run: |
62 | echo "result_${{ matrix.server }}_${{ matrix.client }}=${{ steps.check_connection.outputs.TEST_RESULT }}" >> "$GITHUB_OUTPUT"
63 |
64 | # Job Description: Collates the results of the interop tests into a mark down table.
65 | collate:
66 | runs-on: ubuntu-latest
67 | needs: [interoptests]
68 | steps:
69 | - uses: actions/checkout@v2
70 | - uses: actions/setup-python@v2
71 | with:
72 | python-version: "3.x"
73 | - name: Create results file from interop test outputs
74 | run: |
75 | echo "Collating...."
76 | echo "raw results=${{ toJSON(needs.interoptests.outputs) }}"
77 |
78 | python --version
79 |
80 | echo '${{ toJSON(needs.interoptests.outputs) }}' | python3 test/collate-results.py > DataChannel_Echo_test_results.md
81 |
82 | # Display the results file
83 | cat DataChannel_Echo_test_results.md
84 |
85 | - name: Replace Data Channel Echo Test Results in README
86 | run: |
87 | # Read the new content from DataChannel_Echo_test_results.md
88 | new_content="$( README.tmp && mv -f README.tmp README.md
106 |
107 | cat README.md
108 |
109 | - name: Commit the results to the git repository
110 | if: github.event_name != 'pull_request'
111 | run: |
112 | git config user.name github-actions
113 | git config user.email github-actions@github.com
114 | git commit DataChannel_Echo_test_results.md README.md -m "Automated data channel echo test results."
115 | git pull --rebase
116 | git push
117 |
--------------------------------------------------------------------------------
/PeerConnection_test_results.md:
--------------------------------------------------------------------------------
1 | Test run at 2025-07-18 21:01:46.707472
2 |
3 | | Server | aiortc | libdatachannel | pion | sipsorcery | webrtc-rs | werift |
4 | |--------|--------|--------|--------|--------|--------|--------|
5 | | aiortc |  |  |  |  |  |  |
6 | | gstreamer |  |  |  |  |  |  |
7 | | janus |  |  |  |  |  |  |
8 | | kurento |  |  |  |  |  |  |
9 | | libdatachannel |  |  |  |  |  |  |
10 | | libwebrtc |  |  |  |  |  |  |
11 | | pion |  |  |  |  |  |  |
12 | | sipsorcery |  |  |  |  |  |  |
13 | | webrtc-rs |  |  |  |  |  |  |
14 | | werift |  |  |  |  |  |  |
15 |
--------------------------------------------------------------------------------
/libwebrtc/PcObserver.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * Filename: PcObserver.h
3 | *
4 | * Description:
5 | * Observer to receive notifications of peer connection lifetime events.
6 | *
7 | * Author:
8 | * Aaron Clauson (aaron@sipsorcery.com)
9 | *
10 | * History:
11 | * 08 Mar 2021 Aaron Clauson Created, Dublin, Ireland.
12 | * 21 Dec 2024 Aaron Clauson Updated for libwebrtc version m132.
13 | *
14 | * License: Public Domain (no warranty, use at own risk)
15 | /******************************************************************************/
16 |
17 | #ifndef __PEER_CONNECTION_OBSERVER__
18 | #define __PEER_CONNECTION_OBSERVER__
19 |
20 | #include
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | class PcObserver :
29 | public webrtc::PeerConnectionObserver
30 | {
31 | public:
32 | void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state);
33 | void OnDataChannel(rtc::scoped_refptr data_channel);
34 | void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state);
35 | void OnIceCandidate(const webrtc::IceCandidateInterface* candidate);
36 | void OnAddTrack(
37 | rtc::scoped_refptr receiver,
38 | const std::vector>& streams);
39 | void OnTrack(
40 | rtc::scoped_refptr transceiver);
41 | void OnConnectionChange(
42 | webrtc::PeerConnectionInterface::PeerConnectionState new_state);
43 | };
44 |
45 | class SetRemoteSdpObserver :
46 | public webrtc::SetRemoteDescriptionObserverInterface
47 | {
48 | public:
49 | static rtc::scoped_refptr Create() {
50 | return rtc::scoped_refptr(new rtc::RefCountedObject());
51 | }
52 |
53 | void OnSetRemoteDescriptionComplete(webrtc::RTCError error)
54 | {
55 | std::cout << "OnSetRemoteDescriptionComplete ok ? " << std::boolalpha << error.ok() << "." << std::endl;
56 | }
57 | };
58 |
59 | class CreateSdpObserver :
60 | public webrtc::SetLocalDescriptionObserverInterface
61 | {
62 | public:
63 |
64 | static rtc::scoped_refptr Create(std::mutex& mtx, std::condition_variable& cv, bool& isReady) {
65 | return rtc::scoped_refptr(new rtc::RefCountedObject(mtx, cv, isReady));
66 | }
67 |
68 | CreateSdpObserver(std::mutex& mtx, std::condition_variable& cv, bool& isReady)
69 | : _mtx(mtx), _cv(cv), _isReady(isReady) {
70 | std::cout << "CreateSdpObserver Constructor." << std::endl;
71 | }
72 |
73 | ~CreateSdpObserver() {
74 | std::cout << "CreateSdpObserver Destructor." << std::endl;
75 | }
76 |
77 | void OnSetLocalDescriptionComplete(webrtc::RTCError error) {
78 | std::cout << "OnSetLocalDescriptionComplete." << std::endl;
79 |
80 | if (!error.ok()) {
81 | std::cerr << "OnSetLocalDescription error. " << error.message() << std::endl;
82 | }
83 |
84 | std::unique_lock lck(_mtx);
85 | _isReady = true;
86 | _cv.notify_all();
87 | }
88 |
89 | private:
90 | std::mutex& _mtx;
91 | std::condition_variable& _cv;
92 | bool& _isReady;
93 | };
94 |
95 | class SetRemoteDescriptionObserver : public webrtc::SetSessionDescriptionObserver {
96 | public:
97 |
98 | static rtc::scoped_refptr Create() {
99 | return rtc::scoped_refptr(new rtc::RefCountedObject());
100 | }
101 |
102 | SetRemoteDescriptionObserver() {
103 | std::cout << "SetRemoteDescriptionObserver Constructor." << std::endl;
104 | }
105 |
106 | void OnSuccess() override {
107 | std::cout << "SetRemoteDescriptionObserver::OnSuccess" << std::endl;
108 | }
109 |
110 | void OnFailure(webrtc::RTCError error) override {
111 | std::cerr << "SetRemoteDescriptionObserver::OnFailure: " << error.message() << std::endl;
112 | }
113 | };
114 |
115 | class SetLocalDescriptionObserver : public webrtc::SetSessionDescriptionObserver {
116 | public:
117 |
118 | static rtc::scoped_refptr Create() {
119 | return rtc::scoped_refptr(new rtc::RefCountedObject());
120 | }
121 |
122 | void OnSuccess() override {
123 | std::cout << "SetLocalDescriptionObserver::OnSuccess" << std::endl;
124 | }
125 |
126 | void OnFailure(webrtc::RTCError error) override {
127 | std::cerr << "SetLocalDescriptionObserver::OnFailure: " << error.message() << std::endl;
128 | }
129 | };
130 |
131 | class CreateAnswerObserver : public webrtc::CreateSessionDescriptionObserver {
132 | public:
133 |
134 | static std::unique_ptr Create() {
135 | return std::unique_ptr();
136 | }
137 |
138 | void OnSuccess(webrtc::SessionDescriptionInterface* desc) override {
139 | std::cout << "CreateAnswerObserver::OnSuccess" << std::endl;
140 | }
141 |
142 | void OnFailure(webrtc::RTCError error) override {
143 | std::cerr << "CreateAnswerObserver::OnFailure: " << error.message() << std::endl;
144 | }
145 | };
146 |
147 | #endif
--------------------------------------------------------------------------------
/libwebrtc/HttpSimpleServer.cpp:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * Filename: HttpSimpleServer.cpp
3 | *
4 | * Description: See header file.
5 | *
6 | * Author:
7 | * Aaron Clauson (aaron@sipsorcery.com)
8 | *
9 | * History:
10 | * 08 Mar 2021 Aaron Clauson Created, Dublin, Ireland.
11 | * 21 Dec 2024 Aaron Clauson Updated for libwebrtc version m132.
12 | *
13 | * License: Public Domain (no warranty, use at own risk)
14 | /******************************************************************************/
15 |
16 | #include "HttpSimpleServer.h"
17 |
18 | #include
19 |
20 | PcFactory* HttpSimpleServer::_pcFactory = nullptr;
21 |
22 | HttpSimpleServer::HttpSimpleServer() :
23 | _isDisposed(false)
24 | {
25 | #ifdef _WIN32
26 | evthread_use_windows_threads();
27 | #endif
28 |
29 | /* Initialise libevent HTTP server. */
30 | _evtBase = event_base_new();
31 | if (!_evtBase) {
32 | throw std::runtime_error("HttpSimpleServer couldn't create an event_base instance.");
33 | }
34 |
35 | _httpSvr = evhttp_new(_evtBase);
36 | if (!_httpSvr) {
37 | throw std::runtime_error("HttpSimpleServer couldn't create an evhttp instance.");
38 | }
39 |
40 | _signalEvent = evsignal_new(_evtBase, SIGINT, HttpSimpleServer::OnSignal, (void*)_evtBase);
41 | if (!_signalEvent) {
42 | throw std::runtime_error("HttpSimpleServer couldn't create an event instance with evsignal_new.");
43 | }
44 |
45 | int addResult = event_add(_signalEvent, NULL);
46 | if (addResult < 0) {
47 | std::cerr << "Failed to add signal event handler." << std::endl;
48 | }
49 | }
50 |
51 | HttpSimpleServer::~HttpSimpleServer() {
52 | if (!_isDisposed) {
53 | _isDisposed = true;
54 | event_base_loopexit(_evtBase, nullptr);
55 | evhttp_free(_httpSvr);
56 | event_free(_signalEvent);
57 | event_base_free(_evtBase);
58 | }
59 | }
60 |
61 | void HttpSimpleServer::Init(const char* httpServerAddress, int httpServerPort, const char* offerPath) {
62 |
63 | int res = evhttp_bind_socket(_httpSvr, httpServerAddress, httpServerPort);
64 | if (res != 0) {
65 | throw std::runtime_error("HttpSimpleServer failed to start HTTP server on " +
66 | std::string(httpServerAddress) + ":" + std::to_string(httpServerPort) + ".");
67 | }
68 |
69 | evhttp_set_allowed_methods(_httpSvr,
70 | EVHTTP_REQ_GET |
71 | EVHTTP_REQ_POST |
72 | EVHTTP_REQ_OPTIONS);
73 |
74 | std::cout << "Waiting for SDP offer on http://"
75 | + std::string(httpServerAddress) + ":" + std::to_string(httpServerPort) + offerPath << std::endl;
76 |
77 | res = evhttp_set_cb(_httpSvr, offerPath, HttpSimpleServer::OnHttpRequest, NULL);
78 | if (res != 0) {
79 | throw std::runtime_error("HttpSimpleServer failed to set request callback.");
80 | }
81 | }
82 |
83 | void HttpSimpleServer::Run() {
84 | event_base_dispatch(_evtBase);
85 | }
86 |
87 | void HttpSimpleServer::Stop() {
88 | this->~HttpSimpleServer();
89 | }
90 |
91 | void HttpSimpleServer::SetPeerConnectionFactory(PcFactory* pcFactory) {
92 | _pcFactory = pcFactory;
93 | }
94 |
95 | /**
96 | * The handler function for an incoming HTTP request. This is the start of the
97 | * handling for any WebRTC peer that wishes to establish a connection. The incoming
98 | * request MUST have an SDP offer in its body.
99 | * @param[in] req: the HTTP request received from the remote client.
100 | * @param[in] arg: not used.
101 | */
102 | void HttpSimpleServer::OnHttpRequest(struct evhttp_request* req, void* arg)
103 | {
104 | const char* uri = evhttp_request_get_uri(req);
105 | evbuffer* http_req_body = nullptr;
106 | size_t http_req_body_len{ 0 };
107 | char* http_req_buffer = nullptr;
108 | struct evbuffer* resp_buffer;
109 |
110 | printf("Received HTTP request for %s.\n", uri);
111 |
112 | if (req->type == EVHTTP_REQ_OPTIONS) {
113 | evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
114 | evhttp_add_header(req->output_headers, "Access-Control-Allow-Methods", "POST");
115 | evhttp_add_header(req->output_headers, "Access-Control-Allow-Headers", "content-type");
116 | evhttp_send_reply(req, 200, "OK", NULL);
117 | }
118 | else {
119 |
120 | resp_buffer = evbuffer_new();
121 | if (!resp_buffer) {
122 | fprintf(stderr, "Failed to create HTTP response buffer.\n");
123 | }
124 |
125 | evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
126 |
127 | http_req_body = evhttp_request_get_input_buffer(req);
128 | http_req_body_len = evbuffer_get_length(http_req_body);
129 |
130 | if (http_req_body_len > 0) {
131 | http_req_buffer = static_cast(calloc(http_req_body_len, sizeof(char)));
132 |
133 | evbuffer_copyout(http_req_body, http_req_buffer, http_req_body_len);
134 |
135 | printf("HTTP request body length %zu.\n", http_req_body_len);
136 |
137 | std::string offerJson(http_req_buffer, http_req_body_len);
138 | //std::cout << offerJson << std::endl;
139 |
140 | if (_pcFactory != nullptr) {
141 | std::string answer = _pcFactory->CreatePeerConnection(http_req_buffer, http_req_body_len);
142 | std::cout << "Answer: " << answer << std::endl;
143 | evhttp_add_header(req->output_headers, "Content-type", "application/json");
144 | evbuffer_add_printf(resp_buffer, "%s", answer.c_str());
145 | evhttp_send_reply(req, 200, "OK", resp_buffer);
146 | }
147 | else {
148 | evbuffer_add_printf(resp_buffer, "No handler");
149 | evhttp_send_reply(req, 400, "Bad Request", resp_buffer);
150 | }
151 | }
152 | else {
153 | evbuffer_add_printf(resp_buffer, "Request was missing the SDP offer.");
154 | evhttp_send_reply(req, 400, "Bad Request", resp_buffer);
155 | }
156 | }
157 | }
158 |
159 | void HttpSimpleServer::OnSignal(evutil_socket_t sig, short events, void* user_data)
160 | {
161 | event_base* base = static_cast(user_data);
162 |
163 | std::cout << "Caught an interrupt signal; calling loop exit." << std::endl;
164 |
165 | event_base_loopexit(base, nullptr);
166 | }
167 |
--------------------------------------------------------------------------------
/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
22 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | | Source |
147 | Echo |
148 |
149 |
150 |
151 | |
152 |
153 | |
154 |
155 |
156 | |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/libwebrtc/README.md:
--------------------------------------------------------------------------------
1 | ## Building the libwebrtc builder docker image
2 |
3 | This image gets as far as producing `libwebrtc-full.a` so that it can be used for application builds.
4 |
5 | `docker build -t libwebrtc-builder:m132 -f Dockerfile-Builder --progress=plain .`
6 |
7 | If the build fails:
8 |
9 | 1. Comment out the failing steps, generally that will be the ones from the `gn gen` command on.
10 |
11 | 2. `docker run -it --init --rm libwebrtc-builder:m132`
12 |
13 | ## Building echo application docker image
14 |
15 | The application image. It builds the application on an instance of the builder image and then copies the binary to a new ubuntu image and installs the required shared library packages.
16 |
17 | `docker build -t libwebrtc-webrtc-echo:m132 --progress=plain .`
18 |
19 | If the build fails:
20 |
21 | 1. Comment out the failing steps, generally that will be the ones from the `cmake` command on.
22 |
23 | 2. `docker run -it --init --rm libwebrtc-webrtc-echo:m132`
24 |
25 | ## Running echo application docker image
26 |
27 | `docker run -it --init --rm -p 8080:8080 libwebrtc-webrtc-echo:m132`
28 |
29 | ## Generate Ninja (GN) Reference
30 |
31 | The options supplied to the gn command are critical for buiding a working webrtc.lib (and equivalent object files on linux) as well as ensuring all the required symbols are included.
32 |
33 | gn --help # Get all command line args for gn.
34 | gn args out/Default --list > gnargs.txt # See what options are available for controlling the libwebrtc build.
35 |
36 | https://gn.googlesource.com/gn/+/main/docs/reference.md
37 |
38 | ## Building webrtc.lib on Windows
39 |
40 | Follow the standard instructions at https://webrtc.github.io/webrtc-org/native-code/development/ and then use the steps below. Pay particular attention to the `--args` argument supplied to the `gn` command.
41 |
42 | It seems the latest version of the build instructions are available directly in the source tree at https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/development/.
43 |
44 | ````
45 | src> git checkout branch-heads/4430 -b m90 # See https://chromiumdash.appspot.com/branches for remote branch info.
46 | src> set PATH=C:\Tools\depot_tools;%PATH%
47 | src> glient sync
48 | src> rmdir /q /s out\Default
49 | src> gn gen out/Default --args="is_clang=false use_lld=false" --ide=vs # See https://gn.googlesource.com/gn/+/master/docs/reference.md#IDE-options for more info.
50 | src> gn args out/Default --list # Check use_lld is false. is_clang always shows true but if the false option is not set then linker errors when using webrtc.lib.
51 | src> gn args out/Default --list --short --overrides-only
52 | src> gn clean out/Default # If previous compilation.
53 | src> ninja -C out/Default
54 | ````
55 |
56 | Update Dec 2024 for version m132.
57 |
58 | NOTE: Only the clang build chain is now supported for the libwebrtc build. To use webrtc.lib with Visual Studio the clang build chain can be installed via the Visual Studio Installer and then selected as the option in the project settings.
59 |
60 | Install the Google depot tools as per https://webrtc.googlesource.com/src/+/main/docs/native-code/development/prerequisite-sw/.
61 |
62 | In the webrtc-checkout directory, e.g. c:\dev\webrtc-checkout:
63 |
64 | ````
65 | c:\dev\webrtc-checkout\src> set PATH=C:\Tools\depot_tools;%PATH%
66 | c:\dev\webrtc-checkout\src> git checkout branch-heads/6834 -b m132 # See https://chromiumdash.appspot.com/branches for remote branch info.
67 | c:\dev\webrtc-checkout\src> gclient sync -D
68 | c:\dev\webrtc-checkout\src> SET DEPOT_TOOLS_WIN_TOOLCHAIN=0 # Use Visual Studio installed clang toolchain instead of devtools one.
69 | c:\dev\webrtc-checkout\src> rmdir /q /s out\Default
70 | # Setting use_custom_libcxx=false is critical. If it is true, the build uses the libc++ standard library from the third-party source tree,
71 | # making it incompatible when linking with Visual Studio. By setting it to false, the MSVC standard library will be used (e.g msvcp140.dll),
72 | # ensuring that Visual Studio can properly resolve all the required symbols.
73 | c:\dev\webrtc-checkout\src> gn gen out/Default --args="is_debug=false rtc_include_tests=false treat_warnings_as_errors=false use_custom_libcxx=false use_rtti=true"
74 | c:\dev\webrtc-checkout\src> autoninja -C out/Default # DON'T use "ninja all -C out/Default", as listed on the build instructions site, it attempts to build everything in the out/Default directory rather than just libwebrtc.
75 | # The resultant webrtc.lib should be in out/Default/obj.
76 | # Note as an added bonus vcpkg installed binaries that are build the the standard msbuild toolcahin, e.g. cl and ld, are compatible with the clang and lld-ld linker.
77 | ````
78 |
79 | ## Building libwebrtc-full.a on Ubuntu
80 |
81 | Follow the standard instructions at https://webrtc.github.io/webrtc-org/native-code/development/ and then use the steps below. Pay particular attention to the `--args` argument supplied to the `gn` command.
82 |
83 | It seems the latest version of the build instructions are available directly in the source tree at https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/development/.
84 |
85 | To update to the latest source or build a specific branch (see https://chromiumdash.appspot.com/branches for Chromium branch names):
86 |
87 | ````
88 | git pull origin master
89 | ````
90 | or
91 | ````
92 | git checkout branch-heads/4430 -b m90
93 | ````
94 |
95 | And then to build:
96 |
97 | First time:
98 |
99 | ````
100 | src$ gclient sync
101 | src$ build/install-build-deps.sh
102 | ````
103 |
104 | Subsequently:
105 |
106 | ````
107 | src$ gclient sync
108 | ````
109 |
110 | or if there are problems with `gclient sync`:
111 |
112 | ````
113 | src$ gclient sync --force
114 | ````
115 |
116 | And then:
117 |
118 | ````
119 | src$ gn gen out/Default --args="use_custom_libcxx=false"
120 | src$ gn args out/Default --list --short --overrides-only
121 | src$ ninja -C out/Default
122 | src$ out/Default/webrtc_lib_link_test
123 | src$ cd out/Default
124 | src$ ar crs out/Default/libwebrtc-full.a $(find out/Default/obj -name '*\.o')
125 | sudo apt install software-properties-common
126 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test
127 | sudo apt update
128 | sudo apt install libstdc++-9-dev libevent-dev
129 | ````
130 |
131 | ## Support Group
132 |
133 | The main place to get support for building libwebrtc is the Google Group at https://groups.google.com/u/2/g/discuss-webrtc.
134 |
--------------------------------------------------------------------------------
/pion/client/main.go:
--------------------------------------------------------------------------------
1 | // +build !js
2 |
3 | package main
4 |
5 | import (
6 | "bytes"
7 | "context"
8 | "encoding/json"
9 | "fmt"
10 | "io/ioutil"
11 | "net/http"
12 | "os"
13 | "strings"
14 | "time"
15 |
16 | "github.com/pion/webrtc/v3"
17 | )
18 |
19 | var ECHO_TEST_SERVER_URL = "http://localhost:8080/offer"
20 | var CONNECTION_ATTEMPT_TIMEOUT_SECONDS = 10
21 | var SUCCESS_RETURN_VALUE = 0
22 | var ERROR_RETURN_VALUE = 1
23 |
24 | func main() {
25 |
26 | echoTestServerURL := ECHO_TEST_SERVER_URL
27 | if len(os.Args) > 1 {
28 | echoTestServerURL = os.Args[1]
29 | println("Echo test server URL set to:", echoTestServerURL)
30 | }
31 |
32 | connectResult := false
33 | defer func() {
34 | if err := recover(); err != nil {
35 | fmt.Println(err)
36 | }
37 | fmt.Println("Panic returning status:", ERROR_RETURN_VALUE)
38 | os.Exit(ERROR_RETURN_VALUE)
39 | }()
40 |
41 | ctx, cancel := context.WithTimeout(context.Background(),
42 | time.Duration(CONNECTION_ATTEMPT_TIMEOUT_SECONDS)*time.Second)
43 | defer cancel()
44 |
45 | // Everything below is the Pion WebRTC API! Thanks for using it ❤️.
46 |
47 | // Create a MediaEngine object to configure the supported codec
48 | m := webrtc.MediaEngine{}
49 |
50 | // Setup the codecs you want to use.
51 | // We'll use a VP8 and Opus but you can also define your own
52 | if err := m.RegisterCodec(webrtc.RTPCodecParameters{
53 | RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
54 | PayloadType: 96,
55 | }, webrtc.RTPCodecTypeVideo); err != nil {
56 | panic(err)
57 | }
58 |
59 | // Create the API object with the MediaEngine
60 | api := webrtc.NewAPI(webrtc.WithMediaEngine(&m))
61 |
62 | // Prepare the configuration
63 | config := webrtc.Configuration{
64 | ICEServers: []webrtc.ICEServer{
65 | {
66 | URLs: []string{"stun:stun.l.google.com:19302"},
67 | },
68 | },
69 | }
70 | // Create a new RTCPeerConnection
71 | peerConnection, err := api.NewPeerConnection(config)
72 | if err != nil {
73 | panic(err)
74 | }
75 |
76 | // Create Track that we send video back to browser on
77 | outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: "video/vp8"}, "video", "pion")
78 | if err != nil {
79 | panic(err)
80 | }
81 |
82 | // Add this newly created track to the PeerConnection
83 | rtpSender, err := peerConnection.AddTrack(outputTrack)
84 | if err != nil {
85 | panic(err)
86 | }
87 |
88 | // Read incoming RTCP packets
89 | // Before these packets are retuned they are processed by interceptors. For things
90 | // like NACK this needs to be called.
91 | go func() {
92 | rtcpBuf := make([]byte, 1500)
93 | for {
94 | if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
95 | return
96 | }
97 | }
98 | }()
99 |
100 | // Add ICE candidates to the local offer (simulates non-trickle).
101 | peerConnection.OnICECandidate(func(c *webrtc.ICECandidate) {
102 | if c == nil {
103 | //fmt.Println(peerConnection.LocalDescription())
104 | }
105 | })
106 |
107 | offer, err := peerConnection.CreateOffer(nil)
108 | if err != nil {
109 | panic(err)
110 | }
111 |
112 | // Sets the LocalDescription, and starts our UDP listeners
113 | if err = peerConnection.SetLocalDescription(offer); err != nil {
114 | panic(err)
115 | }
116 |
117 | // Set the handler for ICE connection state
118 | // This will notify you when the peer has connected/disconnected
119 | peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
120 | fmt.Printf("ICE connection state has changed %s.\n", connectionState.String())
121 | })
122 |
123 | peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
124 | fmt.Printf("Peer connection state has changed to %s.\n", state.String())
125 | if state == webrtc.PeerConnectionStateConnected {
126 | connectResult = true
127 | peerConnection.Close()
128 | cancel()
129 | } else if state == webrtc.PeerConnectionStateDisconnected ||
130 | state == webrtc.PeerConnectionStateFailed ||
131 | state == webrtc.PeerConnectionStateClosed {
132 | connectResult = true
133 | cancel()
134 | }
135 | })
136 |
137 | peerConnection.OnSignalingStateChange(func(state webrtc.SignalingState) {
138 | fmt.Printf("Signaling state has changed to %s.\n", state.String())
139 | })
140 |
141 | // Create an answer
142 | /* answer, err := peerConnection.CreateAnswer(nil)
143 | if err != nil {
144 | panic(err)
145 | } */
146 |
147 | // Create channel that is blocked until ICE Gathering is complete
148 | gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
149 |
150 | // Block until ICE Gathering is complete, disabling trickle ICE
151 | // we do this because we only can exchange one signaling message
152 | // in a production application you should exchange ICE Candidates via OnICECandidate
153 | <-gatherComplete
154 |
155 | fmt.Printf("Attempting to POST offer to %s.\n", echoTestServerURL)
156 |
157 | // POST the offer to the echo server.
158 | offerWithIce := peerConnection.LocalDescription()
159 | offerBuf := new(bytes.Buffer)
160 | json.NewEncoder(offerBuf).Encode(offerWithIce)
161 | req, err := http.NewRequest("POST", echoTestServerURL, offerBuf)
162 | req.Header.Set("Content-Type", "application/json")
163 |
164 | client := &http.Client{
165 | Timeout: time.Duration(CONNECTION_ATTEMPT_TIMEOUT_SECONDS) * time.Second}
166 | resp, err := client.Do(req)
167 | if err != nil {
168 | panic(err)
169 | }
170 | defer resp.Body.Close()
171 |
172 | fmt.Println("POST offer response Status:", resp.Status)
173 | //fmt.Println("response Headers:", resp.Header)
174 | body, _ := ioutil.ReadAll(resp.Body)
175 | //fmt.Println("response Body:", string(body))
176 |
177 | // Set the remote SessionDescription
178 | var answer webrtc.SessionDescription
179 | err = json.NewDecoder(strings.NewReader(string(body))).Decode(&answer)
180 | if err != nil {
181 | panic(err)
182 | }
183 |
184 | err = peerConnection.SetRemoteDescription(answer)
185 | if err != nil {
186 | panic(err)
187 | }
188 |
189 | // Output the answer in base64 so we can paste it in browser
190 | //fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
191 |
192 | select {
193 | case <-ctx.Done():
194 | fmt.Println("Context done.")
195 | }
196 |
197 | if connectResult {
198 | fmt.Println("Connection attempt successful returning status:", SUCCESS_RETURN_VALUE)
199 | os.Exit(SUCCESS_RETURN_VALUE)
200 | } else {
201 | fmt.Println("Connection attempt failed returning status:", ERROR_RETURN_VALUE)
202 | os.Exit(ERROR_RETURN_VALUE)
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/libdatachannel/client.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * libdatachannel echo client
3 | * Copyright (c) 2021 Paul-Louis Ageneau
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include