An example of Unity native plugin of webrtc for Windows OS
Unity native plugin has to use Pinvoke technology in its APIs This plugin dll can also be used by Windows C# applications other than Unity. BUG=webrtc:7389 Review-Url: https://codereview.webrtc.org/2823783002 Cr-Commit-Position: refs/heads/master@{#18108}
This commit is contained in:
@ -603,6 +603,40 @@ if (is_linux || is_win) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_win) {
|
||||||
|
rtc_shared_library("webrtc_unity_plugin") {
|
||||||
|
testonly = true
|
||||||
|
sources = [
|
||||||
|
"unityplugin/simple_peer_connection.cc",
|
||||||
|
"unityplugin/simple_peer_connection.h",
|
||||||
|
"unityplugin/unity_plugin_apis.cc",
|
||||||
|
"unityplugin/unity_plugin_apis.h",
|
||||||
|
]
|
||||||
|
if (!build_with_chromium && is_clang) {
|
||||||
|
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
|
||||||
|
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
|
||||||
|
}
|
||||||
|
cflags = [ "/wd4245" ]
|
||||||
|
configs += [
|
||||||
|
"//build/config/win:windowed",
|
||||||
|
":peerconnection_client_warnings_config",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
"//webrtc/api:libjingle_peerconnection_test_api",
|
||||||
|
"//webrtc/api:video_frame_api",
|
||||||
|
"//webrtc/base:rtc_base",
|
||||||
|
"//webrtc/base:rtc_base_approved",
|
||||||
|
"//webrtc/base:rtc_json",
|
||||||
|
"//webrtc/media:rtc_media",
|
||||||
|
"//webrtc/media:rtc_media_base",
|
||||||
|
"//webrtc/modules/video_capture:video_capture_module",
|
||||||
|
"//webrtc/pc:libjingle_peerconnection",
|
||||||
|
"//webrtc/system_wrappers:field_trial_default",
|
||||||
|
"//webrtc/system_wrappers:metrics_default",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!build_with_chromium) {
|
if (!build_with_chromium) {
|
||||||
# Doesn't build within Chrome on Win.
|
# Doesn't build within Chrome on Win.
|
||||||
rtc_executable("stun_prober") {
|
rtc_executable("stun_prober") {
|
||||||
|
|||||||
1
webrtc/examples/unityplugin/OWNERS
Normal file
1
webrtc/examples/unityplugin/OWNERS
Normal file
@ -0,0 +1 @@
|
|||||||
|
gyzhou@chromium.org
|
||||||
204
webrtc/examples/unityplugin/README
Normal file
204
webrtc/examples/unityplugin/README
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
This directory contains an example Unity native plugin for Windows OS.
|
||||||
|
The APIs use Platform Invoke (P/Invoke) technology as required by Unity native plugin.
|
||||||
|
This plugin dll can also be used by Windows C# applications other than Unity.
|
||||||
|
|
||||||
|
An example of wrapping native plugin into a C# managed class in Unity is given as following:
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace SimplePeerConnectionM {
|
||||||
|
// This is a managed wrap up class for the native c style peer connection APIs.
|
||||||
|
public class PeerConnectionM {
|
||||||
|
//private const string dll_path = "SimplePeerConnection";
|
||||||
|
private const string dll_path = "webrtc_unity_plugin";
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern int CreatePeerConnection();
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool ClosePeerConnection(int peer_connection_id);
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool AddStream(int peer_connection_id, bool audio_only);
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool AddDataChannel(int peer_connection_id);
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool CreateOffer(int peer_connection_id);
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool CreateAnswer(int peer_connection_id);
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool SendDataViaDataChannel(int peer_connection_id, string data);
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool SetAudioControl(int peer_connection_id, bool is_mute, bool is_record);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate void LocalDataChannelReadyInternalDelegate();
|
||||||
|
public delegate void LocalDataChannelReadyDelegate(int id);
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool RegisterOnLocalDataChannelReady(int peer_connection_id, LocalDataChannelReadyInternalDelegate callback);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate void DataFromDataChannelReadyInternalDelegate(string s);
|
||||||
|
public delegate void DataFromDataChannelReadyDelegate(int id, string s);
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool RegisterOnDataFromDataChannelReady(int peer_connection_id, DataFromDataChannelReadyInternalDelegate callback);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate void FailureMessageInternalDelegate(string msg);
|
||||||
|
public delegate void FailureMessageDelegate(int id, string msg);
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool RegisterOnFailure(int peer_connection_id, FailureMessageInternalDelegate callback);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate void AudioBusReadyInternalDelegate(IntPtr data, int bits_per_sample,
|
||||||
|
int sample_rate, int number_of_channels, int number_of_frames);
|
||||||
|
public delegate void AudioBusReadyDelegate(int id, IntPtr data, int bits_per_sample,
|
||||||
|
int sample_rate, int number_of_channels, int number_of_frames);
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool RegisterOnAudioBusReady(int peer_connection_id, AudioBusReadyInternalDelegate callback);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate void LocalSdpReadytoSendInternalDelegate(string s);
|
||||||
|
public delegate void LocalSdpReadytoSendDelegate(int id, string s);
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool RegisterOnLocalSdpReadytoSend(int peer_connection_id, LocalSdpReadytoSendInternalDelegate callback);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate void IceCandiateReadytoSendInternalDelegate(string s);
|
||||||
|
public delegate void IceCandiateReadytoSendDelegate(int id, string s);
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool RegisterOnIceCandiateReadytoSend(int peer_connection_id, IceCandiateReadytoSendInternalDelegate callback);
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern int ReceivedSdp(int peer_connection_id, string sdp);
|
||||||
|
|
||||||
|
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern bool ReceivedIceCandidate(int peer_connection_id, string ice_candidate);
|
||||||
|
|
||||||
|
public void CreatePeerConnectionM() {
|
||||||
|
peer_connection_id_ = CreatePeerConnection();
|
||||||
|
RegisterCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterCallbacks() {
|
||||||
|
localDataChannelReadyDelegate_ = new LocalDataChannelReadyInternalDelegate(RaiseLocalDataChannelReady);
|
||||||
|
RegisterOnLocalDataChannelReady(peer_connection_id_, localDataChannelReadyDelegate_);
|
||||||
|
|
||||||
|
dataFromDataChannelReadyDelegate_ = new DataFromDataChannelReadyInternalDelegate(RaiseDataFromDataChannelReady);
|
||||||
|
RegisterOnDataFromDataChannelReady(peer_connection_id_, dataFromDataChannelReadyDelegate_);
|
||||||
|
|
||||||
|
failureMessageDelegate_ = new FailureMessageInternalDelegate(RaiseFailureMessage);
|
||||||
|
RegisterOnFailure(peer_connection_id_, failureMessageDelegate_);
|
||||||
|
|
||||||
|
audioBusReadyDelegate_ = new AudioBusReadyInternalDelegate(RaiseAudioBusReady);
|
||||||
|
RegisterOnAudioBusReady(peer_connection_id_, audioBusReadyDelegate_);
|
||||||
|
|
||||||
|
localSdpReadytoSendDelegate_ = new LocalSdpReadytoSendInternalDelegate(RaiseLocalSdpReadytoSend);
|
||||||
|
RegisterOnLocalSdpReadytoSend(peer_connection_id_, localSdpReadytoSendDelegate_);
|
||||||
|
|
||||||
|
iceCandiateReadytoSendDelegate_ = new IceCandiateReadytoSendInternalDelegate(RaiseIceCandiateReadytoSend);
|
||||||
|
RegisterOnIceCandiateReadytoSend(peer_connection_id_, iceCandiateReadytoSendDelegate_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClosePeerConnectionM() {
|
||||||
|
ClosePeerConnection(peer_connection_id_);
|
||||||
|
peer_connection_id_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return -1 if Peerconnection is not available.
|
||||||
|
public int GetUniqueId() {
|
||||||
|
return peer_connection_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddStreamM(bool audio_only) {
|
||||||
|
AddStream(peer_connection_id_, audio_only);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddDataChannelM() {
|
||||||
|
AddDataChannel(peer_connection_id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateOfferM() {
|
||||||
|
CreateOffer(peer_connection_id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateAnswerM() {
|
||||||
|
CreateAnswer(peer_connection_id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendDataViaDataChannelM(string data) {
|
||||||
|
SendDataViaDataChannel(peer_connection_id_, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAudioControl(bool is_mute, bool is_record) {
|
||||||
|
SetAudioControl(peer_connection_id_, is_mute, is_record);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReceivedSdpM(string sdp) {
|
||||||
|
peer_connection_id_ = ReceivedSdp(peer_connection_id_, sdp);
|
||||||
|
RegisterCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReceivedIceCandidateM(string ice_candidate) {
|
||||||
|
ReceivedIceCandidate(peer_connection_id_, ice_candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseLocalDataChannelReady() {
|
||||||
|
if (OnLocalDataChannelReady != null)
|
||||||
|
OnLocalDataChannelReady(peer_connection_id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseDataFromDataChannelReady(string data) {
|
||||||
|
if (OnDataFromDataChannelReady != null)
|
||||||
|
OnDataFromDataChannelReady(peer_connection_id_, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseFailureMessage(string msg) {
|
||||||
|
if (OnFailureMessage != null)
|
||||||
|
OnFailureMessage(peer_connection_id_, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseAudioBusReady(IntPtr data, int bits_per_sample,
|
||||||
|
int sample_rate, int number_of_channels, int number_of_frames) {
|
||||||
|
if (OnAudioBusReady != null)
|
||||||
|
OnAudioBusReady(peer_connection_id_, data, bits_per_sample, sample_rate,
|
||||||
|
number_of_channels, number_of_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseLocalSdpReadytoSend(string msg) {
|
||||||
|
if (OnLocalSdpReadytoSend != null)
|
||||||
|
OnLocalSdpReadytoSend(peer_connection_id_, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseIceCandiateReadytoSend(string msg) {
|
||||||
|
if (OnIceCandiateReadytoSend != null)
|
||||||
|
OnIceCandiateReadytoSend(peer_connection_id_, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDataChannelReadyInternalDelegate localDataChannelReadyDelegate_ = null;
|
||||||
|
public event LocalDataChannelReadyDelegate OnLocalDataChannelReady;
|
||||||
|
|
||||||
|
private DataFromDataChannelReadyInternalDelegate dataFromDataChannelReadyDelegate_ = null;
|
||||||
|
public event DataFromDataChannelReadyDelegate OnDataFromDataChannelReady;
|
||||||
|
|
||||||
|
private FailureMessageInternalDelegate failureMessageDelegate_ = null;
|
||||||
|
public event FailureMessageDelegate OnFailureMessage;
|
||||||
|
|
||||||
|
private AudioBusReadyInternalDelegate audioBusReadyDelegate_ = null;
|
||||||
|
public event AudioBusReadyDelegate OnAudioBusReady;
|
||||||
|
|
||||||
|
private LocalSdpReadytoSendInternalDelegate localSdpReadytoSendDelegate_ = null;
|
||||||
|
public event LocalSdpReadytoSendDelegate OnLocalSdpReadytoSend;
|
||||||
|
|
||||||
|
private IceCandiateReadytoSendInternalDelegate iceCandiateReadytoSendDelegate_ = null;
|
||||||
|
public event IceCandiateReadytoSendDelegate OnIceCandiateReadytoSend;
|
||||||
|
|
||||||
|
private int peer_connection_id_ = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
514
webrtc/examples/unityplugin/simple_peer_connection.cc
Normal file
514
webrtc/examples/unityplugin/simple_peer_connection.cc
Normal file
@ -0,0 +1,514 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "webrtc/examples/unityplugin/simple_peer_connection.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "webrtc/api/test/fakeconstraints.h"
|
||||||
|
#include "webrtc/base/json.h"
|
||||||
|
#include "webrtc/media/engine/webrtcvideocapturerfactory.h"
|
||||||
|
#include "webrtc/modules/video_capture/video_capture_factory.h"
|
||||||
|
|
||||||
|
// Names used for a IceCandidate JSON object.
|
||||||
|
const char kCandidateSdpMidName[] = "sdpMid";
|
||||||
|
const char kCandidateSdpMlineIndexName[] = "sdpMLineIndex";
|
||||||
|
const char kCandidateSdpName[] = "candidate";
|
||||||
|
|
||||||
|
// Names used for a SessionDescription JSON object.
|
||||||
|
const char kSessionDescriptionTypeName[] = "type";
|
||||||
|
const char kSessionDescriptionSdpName[] = "sdp";
|
||||||
|
|
||||||
|
// Names used for media stream labels.
|
||||||
|
const char kAudioLabel[] = "audio_label";
|
||||||
|
const char kVideoLabel[] = "video_label";
|
||||||
|
const char kStreamLabel[] = "stream_label";
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static int g_peer_count = 0;
|
||||||
|
static std::unique_ptr<rtc::Thread> g_worker_thread;
|
||||||
|
static std::unique_ptr<rtc::Thread> g_signaling_thread;
|
||||||
|
static rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface>
|
||||||
|
g_peer_connection_factory;
|
||||||
|
|
||||||
|
std::string GetEnvVarOrDefault(const char* env_var_name,
|
||||||
|
const char* default_value) {
|
||||||
|
std::string value;
|
||||||
|
const char* env_var = getenv(env_var_name);
|
||||||
|
if (env_var)
|
||||||
|
value = env_var;
|
||||||
|
|
||||||
|
if (value.empty())
|
||||||
|
value = default_value;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetPeerConnectionString() {
|
||||||
|
return GetEnvVarOrDefault("WEBRTC_CONNECT", "stun:stun.l.google.com:19302");
|
||||||
|
}
|
||||||
|
|
||||||
|
class DummySetSessionDescriptionObserver
|
||||||
|
: public webrtc::SetSessionDescriptionObserver {
|
||||||
|
public:
|
||||||
|
static DummySetSessionDescriptionObserver* Create() {
|
||||||
|
return new rtc::RefCountedObject<DummySetSessionDescriptionObserver>();
|
||||||
|
}
|
||||||
|
virtual void OnSuccess() { LOG(INFO) << __FUNCTION__; }
|
||||||
|
virtual void OnFailure(const std::string& error) {
|
||||||
|
LOG(INFO) << __FUNCTION__ << " " << error;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DummySetSessionDescriptionObserver() {}
|
||||||
|
~DummySetSessionDescriptionObserver() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool SimplePeerConnection::InitializePeerConnection(bool is_receiver) {
|
||||||
|
RTC_DCHECK(peer_connection_.get() == nullptr);
|
||||||
|
|
||||||
|
if (g_peer_connection_factory == nullptr) {
|
||||||
|
g_worker_thread.reset(new rtc::Thread());
|
||||||
|
g_worker_thread->Start();
|
||||||
|
g_signaling_thread.reset(new rtc::Thread());
|
||||||
|
g_signaling_thread->Start();
|
||||||
|
|
||||||
|
g_peer_connection_factory = webrtc::CreatePeerConnectionFactory(
|
||||||
|
g_worker_thread.get(), g_worker_thread.get(), g_signaling_thread.get(),
|
||||||
|
nullptr, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
if (!g_peer_connection_factory.get()) {
|
||||||
|
DeletePeerConnection();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_peer_count++;
|
||||||
|
if (!CreatePeerConnection(is_receiver)) {
|
||||||
|
DeletePeerConnection();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return peer_connection_.get() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplePeerConnection::CreatePeerConnection(bool is_receiver) {
|
||||||
|
RTC_DCHECK(g_peer_connection_factory.get() != nullptr);
|
||||||
|
RTC_DCHECK(peer_connection_.get() == nullptr);
|
||||||
|
|
||||||
|
webrtc::PeerConnectionInterface::RTCConfiguration config;
|
||||||
|
webrtc::PeerConnectionInterface::IceServer server;
|
||||||
|
server.uri = GetPeerConnectionString();
|
||||||
|
config.servers.push_back(server);
|
||||||
|
|
||||||
|
webrtc::FakeConstraints constraints;
|
||||||
|
constraints.SetAllowDtlsSctpDataChannels();
|
||||||
|
|
||||||
|
if (is_receiver) {
|
||||||
|
constraints.SetMandatoryReceiveAudio(true);
|
||||||
|
constraints.SetMandatoryReceiveVideo(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
peer_connection_ = g_peer_connection_factory->CreatePeerConnection(
|
||||||
|
config, &constraints, nullptr, nullptr, this);
|
||||||
|
|
||||||
|
return peer_connection_.get() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::DeletePeerConnection() {
|
||||||
|
g_peer_count--;
|
||||||
|
|
||||||
|
CloseDataChannel();
|
||||||
|
peer_connection_ = nullptr;
|
||||||
|
active_streams_.clear();
|
||||||
|
|
||||||
|
if (g_peer_count == 0) {
|
||||||
|
g_peer_connection_factory = nullptr;
|
||||||
|
g_signaling_thread.reset();
|
||||||
|
g_worker_thread.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplePeerConnection::CreateOffer() {
|
||||||
|
if (!peer_connection_.get())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
peer_connection_->CreateOffer(this, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplePeerConnection::CreateAnswer() {
|
||||||
|
if (!peer_connection_.get())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
peer_connection_->CreateAnswer(this, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::OnSuccess(
|
||||||
|
webrtc::SessionDescriptionInterface* desc) {
|
||||||
|
peer_connection_->SetLocalDescription(
|
||||||
|
DummySetSessionDescriptionObserver::Create(), desc);
|
||||||
|
|
||||||
|
std::string sdp;
|
||||||
|
desc->ToString(&sdp);
|
||||||
|
|
||||||
|
Json::StyledWriter writer;
|
||||||
|
Json::Value jmessage;
|
||||||
|
jmessage[kSessionDescriptionTypeName] = desc->type();
|
||||||
|
jmessage[kSessionDescriptionSdpName] = sdp;
|
||||||
|
|
||||||
|
if (OnLocalSdpReady)
|
||||||
|
OnLocalSdpReady(writer.write(jmessage).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::OnFailure(const std::string& error) {
|
||||||
|
LOG(LERROR) << error;
|
||||||
|
|
||||||
|
if (OnFailureMessage)
|
||||||
|
OnFailureMessage(error.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::OnIceCandidate(
|
||||||
|
const webrtc::IceCandidateInterface* candidate) {
|
||||||
|
LOG(INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index();
|
||||||
|
|
||||||
|
Json::StyledWriter writer;
|
||||||
|
Json::Value jmessage;
|
||||||
|
|
||||||
|
jmessage[kCandidateSdpMidName] = candidate->sdp_mid();
|
||||||
|
jmessage[kCandidateSdpMlineIndexName] = candidate->sdp_mline_index();
|
||||||
|
std::string sdp;
|
||||||
|
if (!candidate->ToString(&sdp)) {
|
||||||
|
LOG(LS_ERROR) << "Failed to serialize candidate";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jmessage[kCandidateSdpName] = sdp;
|
||||||
|
|
||||||
|
if (OnIceCandiateReady)
|
||||||
|
OnIceCandiateReady(writer.write(jmessage).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::RegisterOnVideoFramReady(
|
||||||
|
VIDEOFRAMEREADY_CALLBACK callback) {
|
||||||
|
OnVideoFrameReady = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::RegisterOnLocalDataChannelReady(
|
||||||
|
LOCALDATACHANNELREADY_CALLBACK callback) {
|
||||||
|
OnLocalDataChannelReady = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::RegisterOnDataFromDataChannelReady(
|
||||||
|
DATAFROMEDATECHANNELREADY_CALLBACK callback) {
|
||||||
|
OnDataFromDataChannelReady = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::RegisterOnFailure(FAILURE_CALLBACK callback) {
|
||||||
|
OnFailureMessage = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::RegisterOnAudioBusReady(
|
||||||
|
AUDIOBUSREADY_CALLBACK callback) {
|
||||||
|
OnAudioReady = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::RegisterOnLocalSdpReadytoSend(
|
||||||
|
LOCALSDPREADYTOSEND_CALLBACK callback) {
|
||||||
|
OnLocalSdpReady = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::RegisterOnIceCandiateReadytoSend(
|
||||||
|
ICECANDIDATEREADYTOSEND_CALLBACK callback) {
|
||||||
|
OnIceCandiateReady = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplePeerConnection::ReceivedSdp(const char* msg) {
|
||||||
|
if (!peer_connection_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string message(msg);
|
||||||
|
|
||||||
|
Json::Reader reader;
|
||||||
|
Json::Value jmessage;
|
||||||
|
if (!reader.parse(message, jmessage)) {
|
||||||
|
LOG(WARNING) << "Received unknown message. " << message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string type;
|
||||||
|
std::string json_object;
|
||||||
|
|
||||||
|
rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, &type);
|
||||||
|
if (type.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string sdp;
|
||||||
|
if (!rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionSdpName,
|
||||||
|
&sdp)) {
|
||||||
|
LOG(WARNING) << "Can't parse received session description message.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
webrtc::SdpParseError error;
|
||||||
|
webrtc::SessionDescriptionInterface* session_description(
|
||||||
|
webrtc::CreateSessionDescription(type, sdp, &error));
|
||||||
|
if (!session_description) {
|
||||||
|
LOG(WARNING) << "Can't parse received session description message. "
|
||||||
|
<< "SdpParseError was: " << error.description;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG(INFO) << " Received session description :" << message;
|
||||||
|
peer_connection_->SetRemoteDescription(
|
||||||
|
DummySetSessionDescriptionObserver::Create(), session_description);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplePeerConnection::ReceivedIceCandidate(const char* ice_candidate) {
|
||||||
|
if (!peer_connection_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string message(ice_candidate);
|
||||||
|
|
||||||
|
Json::Reader reader;
|
||||||
|
Json::Value jmessage;
|
||||||
|
if (!reader.parse(message, jmessage)) {
|
||||||
|
LOG(WARNING) << "Received unknown message. " << message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string type;
|
||||||
|
std::string json_object;
|
||||||
|
|
||||||
|
rtc::GetStringFromJsonObject(jmessage, kSessionDescriptionTypeName, &type);
|
||||||
|
if (!type.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string sdp_mid;
|
||||||
|
int sdp_mlineindex = 0;
|
||||||
|
std::string sdp;
|
||||||
|
if (!rtc::GetStringFromJsonObject(jmessage, kCandidateSdpMidName, &sdp_mid) ||
|
||||||
|
!rtc::GetIntFromJsonObject(jmessage, kCandidateSdpMlineIndexName,
|
||||||
|
&sdp_mlineindex) ||
|
||||||
|
!rtc::GetStringFromJsonObject(jmessage, kCandidateSdpName, &sdp)) {
|
||||||
|
LOG(WARNING) << "Can't parse received message.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
webrtc::SdpParseError error;
|
||||||
|
std::unique_ptr<webrtc::IceCandidateInterface> candidate(
|
||||||
|
webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error));
|
||||||
|
if (!candidate.get()) {
|
||||||
|
LOG(WARNING) << "Can't parse received candidate message. "
|
||||||
|
<< "SdpParseError was: " << error.description;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!peer_connection_->AddIceCandidate(candidate.get())) {
|
||||||
|
LOG(WARNING) << "Failed to apply the received candidate";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG(INFO) << " Received candidate :" << message;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::SetAudioControl(bool is_mute, bool is_record) {
|
||||||
|
is_mute_audio_ = is_mute;
|
||||||
|
is_record_audio_ = is_record;
|
||||||
|
|
||||||
|
SetAudioControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::SetAudioControl() {
|
||||||
|
if (!remote_stream_)
|
||||||
|
return;
|
||||||
|
webrtc::AudioTrackVector tracks = remote_stream_->GetAudioTracks();
|
||||||
|
if (tracks.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
webrtc::AudioTrackInterface* audio_track = tracks[0];
|
||||||
|
std::string id = audio_track->id();
|
||||||
|
if (is_record_audio_)
|
||||||
|
audio_track->AddSink(this);
|
||||||
|
else
|
||||||
|
audio_track->RemoveSink(this);
|
||||||
|
|
||||||
|
for (auto& track : tracks) {
|
||||||
|
if (is_mute_audio_)
|
||||||
|
track->set_enabled(false);
|
||||||
|
else
|
||||||
|
track->set_enabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::OnAddStream(
|
||||||
|
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) {
|
||||||
|
LOG(INFO) << __FUNCTION__ << " " << stream->label();
|
||||||
|
remote_stream_ = stream;
|
||||||
|
|
||||||
|
SetAudioControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<cricket::VideoCapturer>
|
||||||
|
SimplePeerConnection::OpenVideoCaptureDevice() {
|
||||||
|
std::vector<std::string> device_names;
|
||||||
|
{
|
||||||
|
std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> info(
|
||||||
|
webrtc::VideoCaptureFactory::CreateDeviceInfo());
|
||||||
|
if (!info) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
int num_devices = info->NumberOfDevices();
|
||||||
|
for (int i = 0; i < num_devices; ++i) {
|
||||||
|
const uint32_t kSize = 256;
|
||||||
|
char name[kSize] = {0};
|
||||||
|
char id[kSize] = {0};
|
||||||
|
if (info->GetDeviceName(i, name, kSize, id, kSize) != -1) {
|
||||||
|
device_names.push_back(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cricket::WebRtcVideoDeviceCapturerFactory factory;
|
||||||
|
std::unique_ptr<cricket::VideoCapturer> capturer;
|
||||||
|
for (const auto& name : device_names) {
|
||||||
|
capturer = factory.Create(cricket::Device(name, 0));
|
||||||
|
if (capturer) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return capturer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::AddStreams(bool audio_only) {
|
||||||
|
if (active_streams_.find(kStreamLabel) != active_streams_.end())
|
||||||
|
return; // Already added.
|
||||||
|
|
||||||
|
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream =
|
||||||
|
g_peer_connection_factory->CreateLocalMediaStream(kStreamLabel);
|
||||||
|
|
||||||
|
rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
|
||||||
|
g_peer_connection_factory->CreateAudioTrack(
|
||||||
|
kAudioLabel, g_peer_connection_factory->CreateAudioSource(nullptr)));
|
||||||
|
std::string id = audio_track->id();
|
||||||
|
stream->AddTrack(audio_track);
|
||||||
|
|
||||||
|
if (!audio_only) {
|
||||||
|
std::unique_ptr<cricket::VideoCapturer> capture = OpenVideoCaptureDevice();
|
||||||
|
if (capture) {
|
||||||
|
rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
|
||||||
|
g_peer_connection_factory->CreateVideoTrack(
|
||||||
|
kVideoLabel, g_peer_connection_factory->CreateVideoSource(
|
||||||
|
OpenVideoCaptureDevice(), nullptr)));
|
||||||
|
|
||||||
|
stream->AddTrack(video_track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!peer_connection_->AddStream(stream)) {
|
||||||
|
LOG(LS_ERROR) << "Adding stream to PeerConnection failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::pair<std::string,
|
||||||
|
rtc::scoped_refptr<webrtc::MediaStreamInterface>>
|
||||||
|
MediaStreamPair;
|
||||||
|
active_streams_.insert(MediaStreamPair(stream->label(), stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplePeerConnection::CreateDataChannel() {
|
||||||
|
struct webrtc::DataChannelInit init;
|
||||||
|
init.ordered = true;
|
||||||
|
init.reliable = true;
|
||||||
|
data_channel_ = peer_connection_->CreateDataChannel("Hello", &init);
|
||||||
|
if (data_channel_.get()) {
|
||||||
|
data_channel_->RegisterObserver(this);
|
||||||
|
LOG(LS_INFO) << "Succeeds to create data channel";
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
LOG(LS_INFO) << "Fails to create data channel";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::CloseDataChannel() {
|
||||||
|
if (data_channel_.get()) {
|
||||||
|
data_channel_->UnregisterObserver();
|
||||||
|
data_channel_->Close();
|
||||||
|
}
|
||||||
|
data_channel_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplePeerConnection::SendDataViaDataChannel(const std::string& data) {
|
||||||
|
if (!data_channel_.get()) {
|
||||||
|
LOG(LS_INFO) << "Data channel is not established";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
webrtc::DataBuffer buffer(data);
|
||||||
|
data_channel_->Send(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peerconnection observer
|
||||||
|
void SimplePeerConnection::OnDataChannel(
|
||||||
|
rtc::scoped_refptr<webrtc::DataChannelInterface> channel) {
|
||||||
|
channel->RegisterObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimplePeerConnection::OnStateChange() {
|
||||||
|
if (data_channel_) {
|
||||||
|
webrtc::DataChannelInterface::DataState state = data_channel_->state();
|
||||||
|
if (state == webrtc::DataChannelInterface::kOpen) {
|
||||||
|
if (OnLocalDataChannelReady)
|
||||||
|
OnLocalDataChannelReady();
|
||||||
|
LOG(LS_INFO) << "Data channel is open";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A data buffer was successfully received.
|
||||||
|
void SimplePeerConnection::OnMessage(const webrtc::DataBuffer& buffer) {
|
||||||
|
size_t size = buffer.data.size();
|
||||||
|
char* msg = new char[size + 1];
|
||||||
|
memcpy(msg, buffer.data.data(), size);
|
||||||
|
msg[size] = 0;
|
||||||
|
if (OnDataFromDataChannelReady)
|
||||||
|
OnDataFromDataChannelReady(msg);
|
||||||
|
delete[] msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AudioTrackSinkInterface implementation.
|
||||||
|
void SimplePeerConnection::OnData(const void* audio_data,
|
||||||
|
int bits_per_sample,
|
||||||
|
int sample_rate,
|
||||||
|
size_t number_of_channels,
|
||||||
|
size_t number_of_frames) {
|
||||||
|
if (OnAudioReady)
|
||||||
|
OnAudioReady(audio_data, bits_per_sample, sample_rate,
|
||||||
|
static_cast<int>(number_of_channels),
|
||||||
|
static_cast<int>(number_of_frames));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> SimplePeerConnection::GetRemoteAudioTrackSsrcs() {
|
||||||
|
std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> receivers =
|
||||||
|
peer_connection_->GetReceivers();
|
||||||
|
|
||||||
|
std::vector<uint32_t> ssrcs;
|
||||||
|
for (const auto& receiver : receivers) {
|
||||||
|
if (receiver->media_type() != cricket::MEDIA_TYPE_AUDIO)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::vector<webrtc::RtpEncodingParameters> params =
|
||||||
|
receiver->GetParameters().encodings;
|
||||||
|
|
||||||
|
for (const auto& param : params) {
|
||||||
|
uint32_t ssrc = param.ssrc.value_or(0);
|
||||||
|
if (ssrc > 0)
|
||||||
|
ssrcs.push_back(ssrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssrcs;
|
||||||
|
}
|
||||||
125
webrtc/examples/unityplugin/simple_peer_connection.h
Normal file
125
webrtc/examples/unityplugin/simple_peer_connection.h
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_EXAMPLES_UNITYPLUGIN_SIMPLE_PEER_CONNECTION_H_
|
||||||
|
#define WEBRTC_EXAMPLES_UNITYPLUGIN_SIMPLE_PEER_CONNECTION_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/api/datachannelinterface.h"
|
||||||
|
#include "webrtc/api/mediastreaminterface.h"
|
||||||
|
#include "webrtc/api/peerconnectioninterface.h"
|
||||||
|
#include "webrtc/examples/unityplugin/unity_plugin_apis.h"
|
||||||
|
|
||||||
|
class SimplePeerConnection : public webrtc::PeerConnectionObserver,
|
||||||
|
public webrtc::CreateSessionDescriptionObserver,
|
||||||
|
public webrtc::DataChannelObserver,
|
||||||
|
public webrtc::AudioTrackSinkInterface {
|
||||||
|
public:
|
||||||
|
SimplePeerConnection() {}
|
||||||
|
~SimplePeerConnection() {}
|
||||||
|
|
||||||
|
bool InitializePeerConnection(bool is_receiver);
|
||||||
|
void DeletePeerConnection();
|
||||||
|
void AddStreams(bool audio_only);
|
||||||
|
bool CreateDataChannel();
|
||||||
|
bool CreateOffer();
|
||||||
|
bool CreateAnswer();
|
||||||
|
bool SendDataViaDataChannel(const std::string& data);
|
||||||
|
void SetAudioControl(bool is_mute, bool is_record);
|
||||||
|
|
||||||
|
// Register callback functions.
|
||||||
|
void RegisterOnVideoFramReady(VIDEOFRAMEREADY_CALLBACK callback);
|
||||||
|
void RegisterOnLocalDataChannelReady(LOCALDATACHANNELREADY_CALLBACK callback);
|
||||||
|
void RegisterOnDataFromDataChannelReady(
|
||||||
|
DATAFROMEDATECHANNELREADY_CALLBACK callback);
|
||||||
|
void RegisterOnFailure(FAILURE_CALLBACK callback);
|
||||||
|
void RegisterOnAudioBusReady(AUDIOBUSREADY_CALLBACK callback);
|
||||||
|
void RegisterOnLocalSdpReadytoSend(LOCALSDPREADYTOSEND_CALLBACK callback);
|
||||||
|
void RegisterOnIceCandiateReadytoSend(
|
||||||
|
ICECANDIDATEREADYTOSEND_CALLBACK callback);
|
||||||
|
bool ReceivedSdp(const char* sdp);
|
||||||
|
bool ReceivedIceCandidate(const char* ice_candidate);
|
||||||
|
|
||||||
|
bool SetHeadPosition(float x, float y, float z);
|
||||||
|
bool SetHeadRotation(float rx, float ry, float rz, float rw);
|
||||||
|
bool SetRemoteAudioPosition(float x, float y, float z);
|
||||||
|
bool SetRemoteAudioRotation(float rx, float ry, float rz, float rw);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool CreatePeerConnection(bool receiver);
|
||||||
|
void CloseDataChannel();
|
||||||
|
std::unique_ptr<cricket::VideoCapturer> OpenVideoCaptureDevice();
|
||||||
|
void SetAudioControl();
|
||||||
|
|
||||||
|
// PeerConnectionObserver implementation.
|
||||||
|
void OnSignalingChange(
|
||||||
|
webrtc::PeerConnectionInterface::SignalingState new_state) override {}
|
||||||
|
void OnAddStream(
|
||||||
|
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override;
|
||||||
|
void OnRemoveStream(
|
||||||
|
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {}
|
||||||
|
void OnDataChannel(
|
||||||
|
rtc::scoped_refptr<webrtc::DataChannelInterface> channel) override;
|
||||||
|
void OnRenegotiationNeeded() override {}
|
||||||
|
void OnIceConnectionChange(
|
||||||
|
webrtc::PeerConnectionInterface::IceConnectionState new_state) override {}
|
||||||
|
void OnIceGatheringChange(
|
||||||
|
webrtc::PeerConnectionInterface::IceGatheringState new_state) override {}
|
||||||
|
void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override;
|
||||||
|
void OnIceConnectionReceivingChange(bool receiving) override {}
|
||||||
|
|
||||||
|
// CreateSessionDescriptionObserver implementation.
|
||||||
|
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override;
|
||||||
|
void OnFailure(const std::string& error) override;
|
||||||
|
|
||||||
|
// DataChannelObserver implementation.
|
||||||
|
void OnStateChange() override;
|
||||||
|
void OnMessage(const webrtc::DataBuffer& buffer) override;
|
||||||
|
|
||||||
|
// AudioTrackSinkInterface implementation.
|
||||||
|
void OnData(const void* audio_data,
|
||||||
|
int bits_per_sample,
|
||||||
|
int sample_rate,
|
||||||
|
size_t number_of_channels,
|
||||||
|
size_t number_of_frames) override;
|
||||||
|
|
||||||
|
// Get remote audio tracks ssrcs.
|
||||||
|
std::vector<uint32_t> GetRemoteAudioTrackSsrcs();
|
||||||
|
|
||||||
|
private:
|
||||||
|
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
|
||||||
|
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel_;
|
||||||
|
std::map<std::string, rtc::scoped_refptr<webrtc::MediaStreamInterface> >
|
||||||
|
active_streams_;
|
||||||
|
|
||||||
|
webrtc::MediaStreamInterface* remote_stream_ = nullptr;
|
||||||
|
|
||||||
|
VIDEOFRAMEREADY_CALLBACK OnVideoFrameReady = nullptr;
|
||||||
|
LOCALDATACHANNELREADY_CALLBACK OnLocalDataChannelReady = nullptr;
|
||||||
|
DATAFROMEDATECHANNELREADY_CALLBACK OnDataFromDataChannelReady = nullptr;
|
||||||
|
FAILURE_CALLBACK OnFailureMessage = nullptr;
|
||||||
|
AUDIOBUSREADY_CALLBACK OnAudioReady = nullptr;
|
||||||
|
|
||||||
|
LOCALSDPREADYTOSEND_CALLBACK OnLocalSdpReady = nullptr;
|
||||||
|
ICECANDIDATEREADYTOSEND_CALLBACK OnIceCandiateReady = nullptr;
|
||||||
|
|
||||||
|
bool is_mute_audio_ = false;
|
||||||
|
bool is_record_audio_ = false;
|
||||||
|
|
||||||
|
// disallow copy-and-assign
|
||||||
|
SimplePeerConnection(const SimplePeerConnection&) = delete;
|
||||||
|
SimplePeerConnection& operator=(const SimplePeerConnection&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WEBRTC_EXAMPLES_UNITYPLUGIN_SIMPLE_PEER_CONNECTION_H_
|
||||||
185
webrtc/examples/unityplugin/unity_plugin_apis.cc
Normal file
185
webrtc/examples/unityplugin/unity_plugin_apis.cc
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "webrtc/examples/unityplugin/unity_plugin_apis.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "webrtc/examples/unityplugin/simple_peer_connection.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static int g_peer_connection_id = 1;
|
||||||
|
static std::map<int, rtc::scoped_refptr<SimplePeerConnection>>
|
||||||
|
g_peer_connection_map;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int CreatePeerConnection() {
|
||||||
|
g_peer_connection_map[g_peer_connection_id] =
|
||||||
|
new rtc::RefCountedObject<SimplePeerConnection>();
|
||||||
|
|
||||||
|
if (!g_peer_connection_map[g_peer_connection_id]->InitializePeerConnection(
|
||||||
|
false))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return g_peer_connection_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClosePeerConnection(int peer_connection_id) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->DeletePeerConnection();
|
||||||
|
g_peer_connection_map.erase(peer_connection_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddStream(int peer_connection_id, bool audio_only) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->AddStreams(audio_only);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddDataChannel(int peer_connection_id) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return g_peer_connection_map[peer_connection_id]->CreateDataChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateOffer(int peer_connection_id) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return g_peer_connection_map[peer_connection_id]->CreateOffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateAnswer(int peer_connection_id) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return g_peer_connection_map[peer_connection_id]->CreateAnswer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SendDataViaDataChannel(int peer_connection_id, const char* data) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string s(data);
|
||||||
|
g_peer_connection_map[peer_connection_id]->SendDataViaDataChannel(s);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetAudioControl(int peer_connection_id, bool is_mute, bool is_record) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->SetAudioControl(is_mute,
|
||||||
|
is_record);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register callback functions.
|
||||||
|
bool RegisterOnVideoFramReady(int peer_connection_id,
|
||||||
|
VIDEOFRAMEREADY_CALLBACK callback) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->RegisterOnVideoFramReady(callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterOnLocalDataChannelReady(int peer_connection_id,
|
||||||
|
LOCALDATACHANNELREADY_CALLBACK callback) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->RegisterOnLocalDataChannelReady(
|
||||||
|
callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterOnDataFromDataChannelReady(
|
||||||
|
int peer_connection_id,
|
||||||
|
DATAFROMEDATECHANNELREADY_CALLBACK callback) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->RegisterOnDataFromDataChannelReady(
|
||||||
|
callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterOnFailure(int peer_connection_id, FAILURE_CALLBACK callback) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->RegisterOnFailure(callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterOnAudioBusReady(int peer_connection_id,
|
||||||
|
AUDIOBUSREADY_CALLBACK callback) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->RegisterOnAudioBusReady(callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Singnaling channel related functions.
|
||||||
|
bool RegisterOnLocalSdpReadytoSend(int peer_connection_id,
|
||||||
|
LOCALSDPREADYTOSEND_CALLBACK callback) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->RegisterOnLocalSdpReadytoSend(
|
||||||
|
callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegisterOnIceCandiateReadytoSend(
|
||||||
|
int peer_connection_id,
|
||||||
|
ICECANDIDATEREADYTOSEND_CALLBACK callback) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
g_peer_connection_map[peer_connection_id]->RegisterOnIceCandiateReadytoSend(
|
||||||
|
callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReceivedSdp(int peer_connection_id, const char* sdp) {
|
||||||
|
// Create a peer_connection if no one exists.
|
||||||
|
int id = -1;
|
||||||
|
if (g_peer_connection_map.count(peer_connection_id)) {
|
||||||
|
id = peer_connection_id;
|
||||||
|
} else {
|
||||||
|
id = g_peer_connection_id++;
|
||||||
|
g_peer_connection_map[id] =
|
||||||
|
new rtc::RefCountedObject<SimplePeerConnection>();
|
||||||
|
if (!g_peer_connection_map[id]->InitializePeerConnection(true))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_peer_connection_map[id]->ReceivedSdp(sdp);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReceivedIceCandidate(int peer_connection_id, const char* ice_candidate) {
|
||||||
|
if (!g_peer_connection_map.count(peer_connection_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return g_peer_connection_map[peer_connection_id]->ReceivedIceCandidate(
|
||||||
|
ice_candidate);
|
||||||
|
}
|
||||||
83
webrtc/examples/unityplugin/unity_plugin_apis.h
Normal file
83
webrtc/examples/unityplugin/unity_plugin_apis.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This file provides an example of unity native plugin APIs.
|
||||||
|
|
||||||
|
#ifndef WEBRTC_EXAMPLES_UNITYPLUGIN_UNITY_PLUGIN_APIS_H_
|
||||||
|
#define WEBRTC_EXAMPLES_UNITYPLUGIN_UNITY_PLUGIN_APIS_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Defintions of callback functions.
|
||||||
|
typedef void (*VIDEOFRAMEREADY_CALLBACK)(uint8_t* buffer,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t stride);
|
||||||
|
typedef void (*LOCALDATACHANNELREADY_CALLBACK)();
|
||||||
|
typedef void (*DATAFROMEDATECHANNELREADY_CALLBACK)(const char* msg);
|
||||||
|
typedef void (*FAILURE_CALLBACK)(const char* msg);
|
||||||
|
typedef void (*LOCALSDPREADYTOSEND_CALLBACK)(const char* msg);
|
||||||
|
typedef void (*ICECANDIDATEREADYTOSEND_CALLBACK)(const char* msg);
|
||||||
|
typedef void (*AUDIOBUSREADY_CALLBACK)(const void* audio_data,
|
||||||
|
int bits_per_sample,
|
||||||
|
int sample_rate,
|
||||||
|
int number_of_channels,
|
||||||
|
int number_of_frames);
|
||||||
|
|
||||||
|
#define WEBRTC_PLUGIN_API __declspec(dllexport)
|
||||||
|
extern "C" {
|
||||||
|
// Create a peerconnection and return a unique peer connection id.
|
||||||
|
WEBRTC_PLUGIN_API int CreatePeerConnection();
|
||||||
|
// Close a peerconnection.
|
||||||
|
WEBRTC_PLUGIN_API bool ClosePeerConnection(int peer_connection_id);
|
||||||
|
// Add a audio stream. If audio_only is true, the stream only has an audio
|
||||||
|
// track and no video track.
|
||||||
|
WEBRTC_PLUGIN_API bool AddStream(int peer_connection_id, bool audio_only);
|
||||||
|
// Add a data channel to peer connection.
|
||||||
|
WEBRTC_PLUGIN_API bool AddDataChannel(int peer_connection_id);
|
||||||
|
// Create a peer connection offer.
|
||||||
|
WEBRTC_PLUGIN_API bool CreateOffer(int peer_connection_id);
|
||||||
|
// Create a peer connection answer.
|
||||||
|
WEBRTC_PLUGIN_API bool CreateAnswer(int peer_connection_id);
|
||||||
|
// Send data through data channel.
|
||||||
|
WEBRTC_PLUGIN_API bool SendDataViaDataChannel(int peer_connection_id,
|
||||||
|
const char* data);
|
||||||
|
// Set audio control. If is_mute=true, no audio will playout. If is_record=true,
|
||||||
|
// AUDIOBUSREADY_CALLBACK will be called every 10 ms.
|
||||||
|
WEBRTC_PLUGIN_API bool SetAudioControl(int peer_connection_id,
|
||||||
|
bool is_mute,
|
||||||
|
bool is_record);
|
||||||
|
|
||||||
|
// Register callback functions.
|
||||||
|
WEBRTC_PLUGIN_API bool RegisterOnVideoFramReady(
|
||||||
|
int peer_connection_id,
|
||||||
|
VIDEOFRAMEREADY_CALLBACK callback);
|
||||||
|
WEBRTC_PLUGIN_API bool RegisterOnLocalDataChannelReady(
|
||||||
|
int peer_connection_id,
|
||||||
|
LOCALDATACHANNELREADY_CALLBACK callback);
|
||||||
|
WEBRTC_PLUGIN_API bool RegisterOnDataFromDataChannelReady(
|
||||||
|
int peer_connection_id,
|
||||||
|
DATAFROMEDATECHANNELREADY_CALLBACK callback);
|
||||||
|
WEBRTC_PLUGIN_API bool RegisterOnFailure(int peer_connection_id,
|
||||||
|
FAILURE_CALLBACK callback);
|
||||||
|
WEBRTC_PLUGIN_API bool RegisterOnAudioBusReady(int peer_connection_id,
|
||||||
|
AUDIOBUSREADY_CALLBACK callback);
|
||||||
|
WEBRTC_PLUGIN_API bool RegisterOnLocalSdpReadytoSend(
|
||||||
|
int peer_connection_id,
|
||||||
|
LOCALSDPREADYTOSEND_CALLBACK callback);
|
||||||
|
WEBRTC_PLUGIN_API bool RegisterOnIceCandiateReadytoSend(
|
||||||
|
int peer_connection_id,
|
||||||
|
ICECANDIDATEREADYTOSEND_CALLBACK callback);
|
||||||
|
WEBRTC_PLUGIN_API int ReceivedSdp(int peer_connection_id, const char* sdp);
|
||||||
|
WEBRTC_PLUGIN_API bool ReceivedIceCandidate(int peer_connection_id,
|
||||||
|
const char* ice_candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WEBRTC_EXAMPLES_UNITYPLUGIN_UNITY_PLUGIN_APIS_H_
|
||||||
Reference in New Issue
Block a user