no message

master
zcy 2021-10-21 16:40:28 +08:00
parent 770cda1e03
commit 27b515b763
6 changed files with 765 additions and 0 deletions

View File

@ -0,0 +1,7 @@
cmake_install.cmake
CMakeCache.txt
Makefile
sample
build/*
CMakeFiles/*
xcode/*

View File

@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 2.8)
include_directories("${LIBWEBRTC_PATH}/include")
include_directories("${LIBWEBRTC_PATH}/include/webrtc")
link_directories("${LIBWEBRTC_PATH}/lib")
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# macOS
file(READ ${LIBWEBRTC_PATH}/exports_libwebrtc.txt webrtc_libs)
string(REGEX REPLACE "lib([^.]+).a[\r\n]*" "\\1;" webrtc_libs "${webrtc_libs}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWEBRTC_MAC=1 -DWEBRTC_POSIX=1")
find_library(CORE_FOUNDATION CoreFoundation)
list(APPEND webrtc_libs ${CORE_FOUNDATION})
find_library(FOUNDATION Foundation)
list(APPEND webrtc_libs ${FOUNDATION})
find_library(CORE_AUDIO CoreAudio)
list(APPEND webrtc_libs ${CORE_AUDIO})
find_library(AUDIO_TOOLBOX AudioToolbox)
list(APPEND webrtc_libs ${AUDIO_TOOLBOX})
find_library(CORE_GRAPHICS CoreGraphics)
list(APPEND webrtc_libs ${CORE_GRAPHICS})
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm")
# Linux
list(APPEND webrtc_libs "-Wl,--start-group")
file(READ ${LIBWEBRTC_PATH}/exports_libwebrtc.txt webrtc_read_tmp)
string(REGEX REPLACE "lib([^.]+)\.a[\r\n]*" "\\1;" webrtc_read_tmp "${webrtc_read_tmp}")
list(APPEND webrtc_libs "${webrtc_read_tmp}")
list(APPEND webrtc_libs "-Wl,--end-group")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWEBRTC_LINUX=1 -DWEBRTC_POSIX=1 -D_GLIBCXX_USE_CXX11_ABI=0")
pkg_search_module(X11 REQUIRED x11)
list(APPEND webrtc_libs ${X11_LIBRARIES})
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Wno-unused-parameter")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g")
add_executable(sample
main.cpp
)
target_link_libraries(sample
${webrtc_libs}
)

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Yuji Ito
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,120 @@
# WebRTC C++ sample
Sample program for using WebRTC(DataChannel) on C++.
# Requirement
* Mac OSX
* Please download libwebrtc(It is precompiled chrome's WebRTC) and unarchive it.(https://github.com/llamerada-jp/libwebrtc)
# Compile
```sh
$ cd <path to work>
$ git clone --depth 1 https://github.com/llamerada-jp/webrtc-cpp-sample.git
$ cd webrtc-cpp-sample
$ git submodule init
$ git submodule update
$ cmake -DLIBWEBRTC_PATH=<path to unarchived webrtc> .
$ make
```
# Run
This sample use two consoles to try interprocess communication by WebRTC.
It maybe cannot communicate over NAT each other, because it does not use ICE server.
## Connection
memo : On this sample, Some commands requireing parameter need line of only a semicolon after parameter.
At CONSOLE-1.
```sh
$ cd <path to work>
$ ./sample
0x7fff791c9000:Main thread
0x700000081000:RTC thread
sdp1
0x700000081000:PeerConnectionObserver::RenegotiationNeeded
0x700000081000:CreateSessionDescriptionObserver::OnSuccess
0x700000081000:PeerConnectionObserver::SignalingChange(1)
Offer SDP:begin
<Copy displayed string to the clipboard as STRING-A.>
Offer SDP:end
0x700000081000:SetSessionDescriptionObserver::OnSuccess
0x700000081000:PeerConnectionObserver::IceGatheringChange(1)
0x700000081000:PeerConnectionObserver::IceCandidate
0x700000081000:PeerConnectionObserver::IceCandidate
0x700000081000:PeerConnectionObserver::IceCandidate
0x700000081000:PeerConnectionObserver::IceGatheringChange(2)
sdp3
<Paste STRING-B that it displayed on CONSOLE-2.>
;
0x700000081000:PeerConnectionObserver::SignalingChange(0)
0x700000081000:PeerConnectionObserver::IceConnectionChange(1)
0x700000081000:SetSessionDescriptionObserver::OnSuccess
ice1
<Copy displayed string to the clipboard as STRING-C.>
0x700000081000:PeerConnectionObserver::IceConnectionChange(2)
0x700000081000:PeerConnectionObserver::IceConnectionChange(3)
0x700000081000:DataChannelObserver::StateChange
0x700000081000:PeerConnectionObserver::DataChannel(0x7fd8cb608750, 0x7fd8cb71bef0)
ice2
<Paste STRING-D that it displayed on CONSOLE-2.>
;
```
At CONSOLE-2.
```sh
$ cd <path to work>
$ ./sample
0x7fff791c9000:Main thread
0x700000081000:RTC thread
sdp2
<Paste STRING-A that it displayed on CONSOLE-1.>
;
0x700000081000:PeerConnectionObserver::RenegotiationNeeded
0x700000081000:PeerConnectionObserver::SignalingChange(3)
0x700000081000:SetSessionDescriptionObserver::OnSuccess
0x700000081000:CreateSessionDescriptionObserver::OnSuccess
0x700000081000:PeerConnectionObserver::SignalingChange(0)
Answer SDP:begin
<Copy displayed string to the clipboard as STRING-B.>
Answer SDP:end
0x700000081000:SetSessionDescriptionObserver::OnSuccess
0x700000081000:PeerConnectionObserver::IceGatheringChange(1)
0x700000081000:PeerConnectionObserver::IceCandidate
0x700000081000:PeerConnectionObserver::IceCandidate
0x700000081000:PeerConnectionObserver::IceCandidate
0x700000081000:PeerConnectionObserver::IceGatheringChange(2)
ice2
<Paste STRING-C that it displayed on CONSOLE-1.>
;
0x700000081000:PeerConnectionObserver::IceConnectionChange(1)
0x700000081000:PeerConnectionObserver::IceConnectionChange(2)
0x700000081000:DataChannelObserver::StateChange
0x700000081000:PeerConnectionObserver::DataChannel(0x7fa739e0c0d0, 0x7fa739e08b80)
ice1
<Copy displayed string to the clipboard as STRING-D.>
```
## Send message
You can send messages, after connection is enabled.
```
send
Hello world.
;
```
## Quit
You can watch sequence of quit by typing of "quit".
```
quit
```
EOD

View File

@ -0,0 +1,399 @@
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
// 環境に合わせてマクロ定義が必要
//#define WEBRTC_ANDROID 1
//#define WEBRTC_IOS 1
//#define WEBRTC_LINUX 1
#define WEBRTC_MAC 1
#define WEBRTC_POSIX 1
//#define WEBRTC_WIN 1
// WebRTC関連のヘッダ
#include <webrtc/api/audio_codecs/builtin_audio_decoder_factory.h>
#include <webrtc/api/audio_codecs/builtin_audio_encoder_factory.h>
#include <webrtc/api/peerconnectioninterface.h>
#include <webrtc/rtc_base/flags.h>
#include <webrtc/rtc_base/physicalsocketserver.h>
#include <webrtc/rtc_base/ssladapter.h>
#include <webrtc/rtc_base/thread.h>
// picojsonはコピペ用データ構造を作るために使う
#include "picojson/picojson.h"
class Connection {
public:
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection;
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel;
std::string sdp_type;
picojson::array ice_array;
// Offer/Answerの作成が成功したら、LocalDescriptionとして設定 & 相手に渡す文字列として表示
void onSuccessCSD(webrtc::SessionDescriptionInterface* desc) {
peer_connection->SetLocalDescription(ssdo, desc);
std::string sdp;
desc->ToString(&sdp);
std::cout << sdp_type << " SDP:begin" << std::endl << sdp << sdp_type << " SDP:end" << std::endl;
}
// ICEを取得したら、表示用JSON配列の末尾に追加
void onIceCandidate(const webrtc::IceCandidateInterface* candidate) {
picojson::object ice;
std::string candidate_str;
candidate->ToString(&candidate_str);
ice.insert(std::make_pair("candidate", picojson::value(candidate_str)));
ice.insert(std::make_pair("sdpMid", picojson::value(candidate->sdp_mid())));
ice.insert(std::make_pair("sdpMLineIndex", picojson::value(static_cast<double>(candidate->sdp_mline_index()))));
ice_array.push_back(picojson::value(ice));
}
class PCO : public webrtc::PeerConnectionObserver {
private:
Connection& parent;
public:
PCO(Connection& parent) : parent(parent) {
}
void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {
std::cout << std::this_thread::get_id() << ":"
<< "PeerConnectionObserver::SignalingChange(" << new_state << ")" << std::endl;
};
void OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {
std::cout << std::this_thread::get_id() << ":"
<< "PeerConnectionObserver::AddStream" << std::endl;
};
void OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {
std::cout << std::this_thread::get_id() << ":"
<< "PeerConnectionObserver::RemoveStream" << std::endl;
};
void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) override {
std::cout << std::this_thread::get_id() << ":"
<< "PeerConnectionObserver::DataChannel(" << data_channel
<< ", " << parent.data_channel.get() << ")" << std::endl;
// Answer送信側は、onDataChannelでDataChannelの接続を受け付ける
parent.data_channel = data_channel;
parent.data_channel->RegisterObserver(&parent.dco);
};
void OnRenegotiationNeeded() override {
std::cout << std::this_thread::get_id() << ":"
<< "PeerConnectionObserver::RenegotiationNeeded" << std::endl;
};
void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) override {
std::cout << std::this_thread::get_id() << ":"
<< "PeerConnectionObserver::IceConnectionChange(" << new_state << ")" << std::endl;
};
void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) override {
std::cout << std::this_thread::get_id() << ":"
<< "PeerConnectionObserver::IceGatheringChange(" << new_state << ")" << std::endl;
};
void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override {
std::cout << std::this_thread::get_id() << ":"
<< "PeerConnectionObserver::IceCandidate" << std::endl;
parent.onIceCandidate(candidate);
};
};
class DCO : public webrtc::DataChannelObserver {
private:
Connection& parent;
public:
DCO(Connection& parent) : parent(parent) {
}
// 接続状況が変化した時に発火する。切断は発火タイミングで値を確認して検知可能
void OnStateChange() override {
std::cout << std::this_thread::get_id() << ":"
<< "DataChannelObserver::StateChange" << std::endl;
};
// メッセージ受信
void OnMessage(const webrtc::DataBuffer& buffer) override {
std::cout << std::this_thread::get_id() << ":"
<< "DataChannelObserver::Message" << std::endl;
std::cout << std::string(buffer.data.data<char>(), buffer.data.size()) << std::endl;
};
void OnBufferedAmountChange(uint64_t previous_amount) override {
std::cout << std::this_thread::get_id() << ":"
<< "DataChannelObserver::BufferedAmountChange(" << previous_amount << ")" << std::endl;
};
};
class CSDO : public webrtc::CreateSessionDescriptionObserver {
private:
Connection& parent;
public:
CSDO(Connection& parent) : parent(parent) {
}
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override {
std::cout << std::this_thread::get_id() << ":"
<< "CreateSessionDescriptionObserver::OnSuccess" << std::endl;
parent.onSuccessCSD(desc);
};
void OnFailure(const std::string& error) override {
std::cout << std::this_thread::get_id() << ":"
<< "CreateSessionDescriptionObserver::OnFailure" << std::endl << error << std::endl;
};
};
class SSDO : public webrtc::SetSessionDescriptionObserver {
private:
Connection& parent;
public:
SSDO(Connection& parent) : parent(parent) {
}
void OnSuccess() override {
std::cout << std::this_thread::get_id() << ":"
<< "SetSessionDescriptionObserver::OnSuccess" << std::endl;
};
void OnFailure(const std::string& error) override {
std::cout << std::this_thread::get_id() << ":"
<< "SetSessionDescriptionObserver::OnFailure" << std::endl << error << std::endl;
};
};
PCO pco;
DCO dco;
rtc::scoped_refptr<CSDO> csdo;
rtc::scoped_refptr<SSDO> ssdo;
Connection() :
pco(*this),
dco(*this),
csdo(new rtc::RefCountedObject<CSDO>(*this)),
ssdo(new rtc::RefCountedObject<SSDO>(*this)) {
}
};
std::unique_ptr<rtc::Thread> thread;
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_connection_factory;
webrtc::PeerConnectionInterface::RTCConfiguration configuration;
Connection connection;
rtc::PhysicalSocketServer socket_server;
class CustomRunnable : public rtc::Runnable {
public:
void Run(rtc::Thread* subthread) override {
peer_connection_factory = webrtc::CreatePeerConnectionFactory(
webrtc::CreateBuiltinAudioEncoderFactory(),
webrtc::CreateBuiltinAudioDecoderFactory());
if (peer_connection_factory.get() == nullptr) {
std::cout << "Error on CreatePeerConnectionFactory." << std::endl;
return;
}
subthread->Run();
}
};
void cmd_sdp1() {
connection.peer_connection = peer_connection_factory->CreatePeerConnection(configuration, nullptr, nullptr, &connection.pco);
webrtc::DataChannelInit config;
// DataChannelの設定
connection.data_channel = connection.peer_connection->CreateDataChannel("data_channel", &config);
connection.data_channel->RegisterObserver(&connection.dco);
if (connection.peer_connection.get() == nullptr) {
peer_connection_factory = nullptr;
std::cout << "Error on CreatePeerConnection." << std::endl;
return;
}
connection.sdp_type = "Offer"; // 表示用の文字列、webrtcの動作には関係ない
connection.peer_connection->CreateOffer(connection.csdo, nullptr);
}
void cmd_sdp2(const std::string& parameter) {
connection.peer_connection = peer_connection_factory->CreatePeerConnection(configuration, nullptr, nullptr, &connection.pco);
if (connection.peer_connection.get() == nullptr) {
peer_connection_factory = nullptr;
std::cout << "Error on CreatePeerConnection." << std::endl;
return;
}
webrtc::SdpParseError error;
webrtc::SessionDescriptionInterface* session_description(
webrtc::CreateSessionDescription("offer", parameter, &error));
if (session_description == nullptr) {
std::cout << "Error on CreateSessionDescription." << std::endl
<< error.line << std::endl
<< error.description << std::endl;
std::cout << "Offer SDP:begin" << std::endl << parameter << std::endl << "Offer SDP:end" << std::endl;
}
connection.peer_connection->SetRemoteDescription(connection.ssdo, session_description);
connection.sdp_type = "Answer"; // 表示用の文字列、webrtcの動作には関係ない
connection.peer_connection->CreateAnswer(connection.csdo, nullptr);
}
void cmd_sdp3(const std::string& parameter) {
webrtc::SdpParseError error;
webrtc::SessionDescriptionInterface* session_description(
webrtc::CreateSessionDescription("answer", parameter, &error));
if (session_description == nullptr) {
std::cout << "Error on CreateSessionDescription." << std::endl
<< error.line << std::endl
<< error.description << std::endl;
std::cout << "Answer SDP:begin" << std::endl << parameter << std::endl << "Answer SDP:end" << std::endl;
}
connection.peer_connection->SetRemoteDescription(connection.ssdo, session_description);
}
void cmd_ice1() {
std::cout << picojson::value(connection.ice_array).serialize(true) << std::endl;
connection.ice_array.clear();
}
void cmd_ice2(const std::string& parameter) {
picojson::value v;
std::string err = picojson::parse(v, parameter);
if (!err.empty()) {
std::cout << "Error on parse json : " << err << std::endl;
return;
}
webrtc::SdpParseError err_sdp;
for (auto& ice_it : v.get<picojson::array>()) {
picojson::object& ice_json = ice_it.get<picojson::object>();
webrtc::IceCandidateInterface* ice =
CreateIceCandidate(ice_json.at("sdpMid").get<std::string>(),
static_cast<int>(ice_json.at("sdpMLineIndex").get<double>()),
ice_json.at("candidate").get<std::string>(),
&err_sdp);
if (!err_sdp.line.empty() && !err_sdp.description.empty()) {
std::cout << "Error on CreateIceCandidate" << std::endl
<< err_sdp.line << std::endl
<< err_sdp.description << std::endl;
return;
}
connection.peer_connection->AddIceCandidate(ice);
}
}
void cmd_send(const std::string& parameter) {
webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(parameter.c_str(), parameter.size()), true);
std::cout << "Send(" << connection.data_channel->state() << ")" << std::endl;
connection.data_channel->Send(buffer);
}
void cmd_quit() {
// スレッドを活かしながらCloseしないと、別スレッドからのイベント待ちになり終了できなくなる
connection.peer_connection->Close();
connection.peer_connection = nullptr;
connection.data_channel = nullptr;
peer_connection_factory = nullptr;
// リソースを開放したらスレッドを止めてOK
thread->Quit();
}
int main(int argc, char* argv[]) {
// 第三引数にtrueを指定すると、WebRTC関連の引数をargvから削除してくれるらしい
rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
rtc::FlagList::Print(nullptr, false);
std::cout << std::this_thread::get_id() << ":"
<< "Main thread" << std::endl;
// GoogleのSTUNサーバを利用
webrtc::PeerConnectionInterface::IceServer ice_server;
ice_server.uri = "stun:stun.l.google.com:19302";
configuration.servers.push_back(ice_server);
thread.reset(new rtc::Thread(&socket_server));
rtc::InitializeSSL();
CustomRunnable runnable;
thread->Start(&runnable);
std::string line;
std::string command;
std::string parameter;
bool is_cmd_mode = true;
while (std::getline(std::cin, line)) {
if (is_cmd_mode) {
if (line == "") {
continue;
} else if (line == "sdp1") {
cmd_sdp1();
} else if (line == "sdp2") {
command = "sdp2";
is_cmd_mode = false;
} else if (line == "sdp3") {
command = "sdp3";
is_cmd_mode = false;
} else if (line == "ice1") {
cmd_ice1();
} else if (line == "ice2") {
command = "ice2";
is_cmd_mode = false;
} else if (line == "send") {
command = "send";
is_cmd_mode = false;
} else if (line == "quit") {
cmd_quit();
break;
} else {
std::cout << "?" << line << std::endl;
}
} else {
if (line == ";") {
if (command == "sdp2") {
cmd_sdp2(parameter);
} else if (command == "sdp3") {
cmd_sdp3(parameter);
} else if (command == "ice2") {
cmd_ice2(parameter);
} else if (command == "send") {
cmd_send(parameter);
}
parameter = "";
is_cmd_mode = true;
} else {
parameter += line + "\n";
}
}
}
thread.reset();
rtc::CleanupSSL();
return 0;
}

View File

@ -0,0 +1,168 @@
<!DOCTYPE html>
<html>
<head>
<title>WebRTC C++ sample</title>
</head>
<body>
<div>
<textarea id="stdout" cols="100" rows="32" readonly>output</textarea>
</div>
<div>
<button type="button" onclick="sdp1();">sdp1</button>
<button type="button" onclick="sdp2();">sdp2</button>
<button type="button" onclick="sdp3();">sdp3</button>
<button type="button" onclick="ice1();">ice1</button>
<button type="button" onclick="ice2();">ice2</button>
<button type="button" onclick="send();">send</button>
<button type="button" onclick="quit();">quit</button>
</div>
<div>
<textarea id="stdin" cols="100" rows="8"></textarea>
</div>
</body>
<script type="text/javascript">
var dataChannel = null;
var peerConnection = null;
var pcConfig = {
iceServers:[
{url:'stun:stun.l.google.com:19302'}
]
};
var iceArray = [];
function output(log) {
var stdout = document.getElementById('stdout');
stdout.value = stdout.value + log + '\n';
}
function input() {
var stdin = document.getElementById('stdin');
var input = stdin.value;
stdin.value = '';
return input;
}
function onDataChannel(evt) {
output('onDataChannel');
dataChannel = evt.channel;
setDataChannelEvents(dataChannel);
}
function onIceCandidate(evt) {
if (evt.candidate) {
iceArray.push(evt.candidate);
} else {
output("end of ice candidate" + evt.eventPhase);
}
}
function onIceConnectionStateChange(evt) {
output("onIceConnectionStateChange:" + peerConnection.iceConnectionState);
}
function setDataChannelEvents(dataChannel) {
dataChannel.onerror = function (error) {
ouptput('Data Channel onerror:' + error);
};
dataChannel.onmessage = function (event) {
output('Data Channel onmessage:' + event.data);
output(String.fromCharCode.apply("", new Uint8Array(event.data)));
};
dataChannel.onopen = function () {
output('Data Channel onopen');
};
dataChannel.onclose = function () {
output('Data Channel onclose');
};
}
function sdp1() {
function onOfferSuccess(sessionDescription) {
peerConnection.setLocalDescription(sessionDescription);
output('createOffer -> onOfferSuccess');
output('Offer SDP:begin');
output(sessionDescription.sdp);
output('Offer SDP:end');
}
function onOfferFailure() {
output('createOffer -> onOfferFailure');
}
peerConnection = new RTCPeerConnection(pcConfig);
peerConnection.onicecandidate = onIceCandidate;
peerConnection.ondatachannel = onDataChannel;
peerConnection.oniceconnectionstatechange = onIceConnectionStateChange;
var dataChannelOptions = {
ordered: true, // 順序を保証する
maxRetransmitTime: 3000 // ミリ秒
};
dataChannel = peerConnection.createDataChannel('data_channel', dataChannelOptions);
setDataChannelEvents(dataChannel);
peerConnection.createOffer(onOfferSuccess, onOfferFailure, null);
}
function sdp2() {
function onAnswerSuccess(sessionDescription) {
peerConnection.setLocalDescription(sessionDescription);
output('createAnswer -> onAnswerSuccess');
output('Answer SDP:begin');
output(sessionDescription.sdp);
output('Answer SDP:end');
}
function onAnswerFailure() {
output('createAnswer -> onAnswerFailure');
}
var sdp = new RTCSessionDescription({
type: 'offer',
sdp: input()
});
peerConnection = new RTCPeerConnection(pcConfig);
peerConnection.onicecandidate = onIceCandidate;
peerConnection.ondatachannel = onDataChannel;
peerConnection.oniceconnectionstatechange = onIceConnectionStateChange;
peerConnection.setRemoteDescription(sdp);
peerConnection.createAnswer(onAnswerSuccess, onAnswerFailure, null);
}
function sdp3() {
var sdp = new RTCSessionDescription({
type: 'answer',
sdp: input()
});
peerConnection.setRemoteDescription(sdp);
}
function ice1() {
output('ICE:begin');
output(JSON.stringify(iceArray));
iceArray = [];
output('ICE:end');
}
function ice2() {
var ices = JSON.parse(input());
for (var ice of ices) {
var iceObj = new RTCIceCandidate(ice);
peerConnection.addIceCandidate(iceObj);
}
}
function send() {
var data = input();
dataChannel.send(data);
}
function quit() {
dataChannel.close();
dataChannel = null;
peerConnection.close();
peerConnection = null;
}
</script>
</html>