Update native plugin dll for turn servers and video.

This CL was modified from work of sharifferdous@ (intern supervised by lliuu@)

BUG=webrtc:7389

Review-Url: https://codereview.webrtc.org/2987723002
Cr-Commit-Position: refs/heads/master@{#19146}
This commit is contained in:
gyzhou 2017-07-25 16:04:31 -07:00 committed by Commit Bot
parent 3b673c66a4
commit b38f38662f
8 changed files with 443 additions and 254 deletions

View File

@ -620,6 +620,8 @@ if (is_win) {
"unityplugin/simple_peer_connection.h",
"unityplugin/unity_plugin_apis.cc",
"unityplugin/unity_plugin_apis.h",
"unityplugin/video_observer.cc",
"unityplugin/video_observer.h",
]
if (!build_with_chromium && is_clang) {
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
@ -637,9 +639,6 @@ if (is_win) {
"../media:rtc_media_base",
"../modules/video_capture:video_capture_module",
"../pc:libjingle_peerconnection",
"../rtc_base:rtc_base",
"../rtc_base:rtc_base_approved",
"../rtc_base:rtc_json",
"../system_wrappers:field_trial_default",
"../system_wrappers:metrics_default",
]

View File

@ -5,200 +5,302 @@ 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.Collections.Generic;
using System.Runtime.InteropServices;
namespace SimplePeerConnectionM {
// This is a managed wrap up class for the native c style peer connection APIs.
// A class for ice candidate.
public class IceCandidate {
public IceCandidate(string candidate, int sdpMlineIndex, string sdpMid) {
mCandidate = candidate;
mSdpMlineIndex = sdpMlineIndex;
mSdpMid = sdpMid;
}
string mCandidate;
int mSdpMlineIndex;
string mSdpMid;
public string Candidate {
get { return mCandidate; }
set { mCandidate = value; }
}
public int SdpMlineIndex {
get { return mSdpMlineIndex; }
set { mSdpMlineIndex = value; }
}
public string SdpMid {
get { return mSdpMid; }
set { mSdpMid = value; }
}
}
// A managed wrapper 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";
private const string dllPath = "webrtc_unity_plugin";
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern int CreatePeerConnection();
//create a peerconnection with turn servers
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int CreatePeerConnection(string[] turnUrls, int noOfUrls,
string username, string credential);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern bool ClosePeerConnection(int peer_connection_id);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool ClosePeerConnection(int peerConnectionId);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern bool AddStream(int peer_connection_id, bool audio_only);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool AddStream(int peerConnectionId, bool audioOnly);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern bool AddDataChannel(int peer_connection_id);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool AddDataChannel(int peerConnectionId);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern bool CreateOffer(int peer_connection_id);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool CreateOffer(int peerConnectionId);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern bool CreateAnswer(int peer_connection_id);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool CreateAnswer(int peerConnectionId);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern bool SendDataViaDataChannel(int peer_connection_id, string data);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool SendDataViaDataChannel(int peerConnectionId, string data);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern bool SetAudioControl(int peer_connection_id, bool is_mute, bool is_record);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool SetAudioControl(int peerConnectionId, bool isMute, bool isRecord);
[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);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool RegisterOnLocalDataChannelReady(
int peerConnectionId, 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);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool RegisterOnDataFromDataChannelReady(
int peerConnectionId, 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);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool RegisterOnFailure(int peerConnectionId,
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);
private delegate void AudioBusReadyInternalDelegate(IntPtr data, int bitsPerSample,
int sampleRate, int numberOfChannels, int numberOfFrames);
public delegate void AudioBusReadyDelegate(int id, IntPtr data, int bitsPerSample,
int sampleRate, int numberOfChannels, int numberOfFrames);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool RegisterOnAudioBusReady(int peerConnectionId,
AudioBusReadyInternalDelegate callback);
// Video callbacks.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void I420FrameReadyInternalDelegate(
IntPtr dataY, IntPtr dataU, IntPtr dataV,
int strideY, int strideU, int strideV,
uint width, uint height);
public delegate void I420FrameReadyDelegate(int id,
IntPtr dataY, IntPtr dataU, IntPtr dataV,
int strideY, int strideU, int strideV,
uint width, uint height);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool RegisterOnLocalI420FrameReady(int peerConnectionId,
I420FrameReadyInternalDelegate callback);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool RegisterOnRemoteI420FrameReady(int peerConnectionId,
I420FrameReadyInternalDelegate 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);
private delegate void LocalSdpReadytoSendInternalDelegate(string type, string sdp);
public delegate void LocalSdpReadytoSendDelegate(int id, string type, string sdp);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool RegisterOnLocalSdpReadytoSend(int peerConnectionId,
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);
private delegate void IceCandiateReadytoSendInternalDelegate(
string candidate, int sdpMlineIndex, string sdpMid);
public delegate void IceCandiateReadytoSendDelegate(
int id, string candidate, int sdpMlineIndex, string sdpMid);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool RegisterOnIceCandiateReadytoSend(
int peerConnectionId, IceCandiateReadytoSendInternalDelegate callback);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern int ReceivedSdp(int peer_connection_id, string sdp);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool SetRemoteDescription(int peerConnectionId, string type, string sdp);
[DllImport(dll_path, CallingConvention = CallingConvention.Cdecl)]
private static extern bool ReceivedIceCandidate(int peer_connection_id, string ice_candidate);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern bool AddIceCandidate(int peerConnectionId, string sdp,
int sdpMlineindex, string sdpMid);
public void CreatePeerConnectionM() {
peer_connection_id_ = CreatePeerConnection();
public PeerConnectionM(List<string> turnUrls, string username, string credential) {
string[] urls = turnUrls != null ? turnUrls.ToArray() : null;
int length = turnUrls != null ? turnUrls.Count : 0;
mPeerConnectionId = CreatePeerConnection(urls, length, username, credential);
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;
public void ClosePeerConnection() {
ClosePeerConnection(mPeerConnectionId);
mPeerConnectionId = -1;
}
// Return -1 if Peerconnection is not available.
public int GetUniqueId() {
return peer_connection_id_;
return mPeerConnectionId;
}
public void AddStreamM(bool audio_only) {
AddStream(peer_connection_id_, audio_only);
public void AddStream(bool audioOnly) {
AddStream(mPeerConnectionId, audioOnly);
}
public void AddDataChannelM() {
AddDataChannel(peer_connection_id_);
public void AddDataChannel() {
AddDataChannel(mPeerConnectionId);
}
public void CreateOfferM() {
CreateOffer(peer_connection_id_);
public void CreateOffer() {
CreateOffer(mPeerConnectionId);
}
public void CreateAnswerM() {
CreateAnswer(peer_connection_id_);
public void CreateAnswer() {
CreateAnswer(mPeerConnectionId);
}
public void SendDataViaDataChannelM(string data) {
SendDataViaDataChannel(peer_connection_id_, data);
public void SendDataViaDataChannel(string data) {
SendDataViaDataChannel(mPeerConnectionId, data);
}
public void SetAudioControl(bool is_mute, bool is_record) {
SetAudioControl(peer_connection_id_, is_mute, is_record);
public void SetAudioControl(bool isMute, bool isRecord) {
SetAudioControl(mPeerConnectionId, isMute, isRecord);
}
public void ReceivedSdpM(string sdp) {
peer_connection_id_ = ReceivedSdp(peer_connection_id_, sdp);
RegisterCallbacks();
public void SetRemoteDescription(string type, string sdp) {
SetRemoteDescription(mPeerConnectionId, type, sdp);
}
public void ReceivedIceCandidateM(string ice_candidate) {
ReceivedIceCandidate(peer_connection_id_, ice_candidate);
public void AddIceCandidate(string candidate, int sdpMlineindex, string sdpMid) {
AddIceCandidate(mPeerConnectionId, candidate, sdpMlineindex, sdpMid);
}
private void RegisterCallbacks() {
localDataChannelReadyDelegate = new LocalDataChannelReadyInternalDelegate(
RaiseLocalDataChannelReady);
RegisterOnLocalDataChannelReady(mPeerConnectionId, localDataChannelReadyDelegate);
dataFromDataChannelReadyDelegate = new DataFromDataChannelReadyInternalDelegate(
RaiseDataFromDataChannelReady);
RegisterOnDataFromDataChannelReady(mPeerConnectionId, dataFromDataChannelReadyDelegate);
failureMessageDelegate = new FailureMessageInternalDelegate(RaiseFailureMessage);
RegisterOnFailure(mPeerConnectionId, failureMessageDelegate);
audioBusReadyDelegate = new AudioBusReadyInternalDelegate(RaiseAudioBusReady);
RegisterOnAudioBusReady(mPeerConnectionId, audioBusReadyDelegate);
localI420FrameReadyDelegate = new I420FrameReadyInternalDelegate(
RaiseLocalVideoFrameReady);
RegisterOnLocalI420FrameReady(mPeerConnectionId, localI420FrameReadyDelegate);
remoteI420FrameReadyDelegate = new I420FrameReadyInternalDelegate(
RaiseRemoteVideoFrameReady);
RegisterOnRemoteI420FrameReady(mPeerConnectionId, remoteI420FrameReadyDelegate);
localSdpReadytoSendDelegate = new LocalSdpReadytoSendInternalDelegate(
RaiseLocalSdpReadytoSend);
RegisterOnLocalSdpReadytoSend(mPeerConnectionId, localSdpReadytoSendDelegate);
iceCandiateReadytoSendDelegate =
new IceCandiateReadytoSendInternalDelegate(RaiseIceCandiateReadytoSend);
RegisterOnIceCandiateReadytoSend(
mPeerConnectionId, iceCandiateReadytoSendDelegate);
}
private void RaiseLocalDataChannelReady() {
if (OnLocalDataChannelReady != null)
OnLocalDataChannelReady(peer_connection_id_);
OnLocalDataChannelReady(mPeerConnectionId);
}
private void RaiseDataFromDataChannelReady(string data) {
if (OnDataFromDataChannelReady != null)
OnDataFromDataChannelReady(peer_connection_id_, data);
OnDataFromDataChannelReady(mPeerConnectionId, data);
}
private void RaiseFailureMessage(string msg) {
if (OnFailureMessage != null)
OnFailureMessage(peer_connection_id_, msg);
OnFailureMessage(mPeerConnectionId, msg);
}
private void RaiseAudioBusReady(IntPtr data, int bits_per_sample,
int sample_rate, int number_of_channels, int number_of_frames) {
private void RaiseAudioBusReady(IntPtr data, int bitsPerSample,
int sampleRate, int numberOfChannels, int numberOfFrames) {
if (OnAudioBusReady != null)
OnAudioBusReady(peer_connection_id_, data, bits_per_sample, sample_rate,
number_of_channels, number_of_frames);
OnAudioBusReady(mPeerConnectionId, data, bitsPerSample, sampleRate,
numberOfChannels, numberOfFrames);
}
private void RaiseLocalSdpReadytoSend(string msg) {
private void RaiseLocalVideoFrameReady(
IntPtr dataY, IntPtr dataU, IntPtr dataV,
int strideY, int strideU, int strideV,
uint width, uint height) {
if (OnLocalVideoFrameReady != null)
OnLocalVideoFrameReady(mPeerConnectionId, dataY, dataU, dataV, strideY, strideU, strideV,
width, height);
}
private void RaiseRemoteVideoFrameReady(
IntPtr dataY, IntPtr dataU, IntPtr dataV,
int strideY, int strideU, int strideV,
uint width, uint height) {
if (OnRemoteVideoFrameReady != null)
OnRemoteVideoFrameReady(mPeerConnectionId, dataY, dataU, dataV, strideY, strideU, strideV,
width, height);
}
private void RaiseLocalSdpReadytoSend(string type, string sdp) {
if (OnLocalSdpReadytoSend != null)
OnLocalSdpReadytoSend(peer_connection_id_, msg);
OnLocalSdpReadytoSend(mPeerConnectionId, type, sdp);
}
private void RaiseIceCandiateReadytoSend(string msg) {
private void RaiseIceCandiateReadytoSend(string candidate, int sdpMlineIndex, string sdpMid) {
if (OnIceCandiateReadytoSend != null)
OnIceCandiateReadytoSend(peer_connection_id_, msg);
OnIceCandiateReadytoSend(mPeerConnectionId, candidate, sdpMlineIndex, sdpMid);
}
private LocalDataChannelReadyInternalDelegate localDataChannelReadyDelegate_ = null;
public void AddQueuedIceCandidate(List<IceCandidate> iceCandidateQueue) {
if (iceCandidateQueue != null) {
foreach (IceCandidate ic in iceCandidateQueue) {
AddIceCandidate(mPeerConnectionId, ic.Candidate, ic.SdpMlineIndex, ic.SdpMid);
}
}
}
private LocalDataChannelReadyInternalDelegate localDataChannelReadyDelegate = null;
public event LocalDataChannelReadyDelegate OnLocalDataChannelReady;
private DataFromDataChannelReadyInternalDelegate dataFromDataChannelReadyDelegate_ = null;
private DataFromDataChannelReadyInternalDelegate dataFromDataChannelReadyDelegate = null;
public event DataFromDataChannelReadyDelegate OnDataFromDataChannelReady;
private FailureMessageInternalDelegate failureMessageDelegate_ = null;
private FailureMessageInternalDelegate failureMessageDelegate = null;
public event FailureMessageDelegate OnFailureMessage;
private AudioBusReadyInternalDelegate audioBusReadyDelegate_ = null;
private AudioBusReadyInternalDelegate audioBusReadyDelegate = null;
public event AudioBusReadyDelegate OnAudioBusReady;
private LocalSdpReadytoSendInternalDelegate localSdpReadytoSendDelegate_ = null;
private I420FrameReadyInternalDelegate localI420FrameReadyDelegate = null;
public event I420FrameReadyDelegate OnLocalVideoFrameReady;
private I420FrameReadyInternalDelegate remoteI420FrameReadyDelegate = null;
public event I420FrameReadyDelegate OnRemoteVideoFrameReady;
private LocalSdpReadytoSendInternalDelegate localSdpReadytoSendDelegate = null;
public event LocalSdpReadytoSendDelegate OnLocalSdpReadytoSend;
private IceCandiateReadytoSendInternalDelegate iceCandiateReadytoSendDelegate_ = null;
private IceCandiateReadytoSendInternalDelegate iceCandiateReadytoSendDelegate = null;
public event IceCandiateReadytoSendDelegate OnIceCandiateReadytoSend;
private int peer_connection_id_ = -1;
private int mPeerConnectionId = -1;
}
}

View File

@ -15,16 +15,6 @@
#include "webrtc/api/test/fakeconstraints.h"
#include "webrtc/media/engine/webrtcvideocapturerfactory.h"
#include "webrtc/modules/video_capture/video_capture_factory.h"
#include "webrtc/rtc_base/json.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";
@ -73,7 +63,11 @@ class DummySetSessionDescriptionObserver
} // namespace
bool SimplePeerConnection::InitializePeerConnection(bool is_receiver) {
bool SimplePeerConnection::InitializePeerConnection(const char** turn_urls,
const int no_of_urls,
const char* username,
const char* credential,
bool is_receiver) {
RTC_DCHECK(peer_connection_.get() == nullptr);
if (g_peer_connection_factory == nullptr) {
@ -92,21 +86,51 @@ bool SimplePeerConnection::InitializePeerConnection(bool is_receiver) {
}
g_peer_count++;
if (!CreatePeerConnection(is_receiver)) {
if (!CreatePeerConnection(turn_urls, no_of_urls, username, credential,
is_receiver)) {
DeletePeerConnection();
return false;
}
return peer_connection_.get() != nullptr;
}
bool SimplePeerConnection::CreatePeerConnection(bool is_receiver) {
bool SimplePeerConnection::CreatePeerConnection(const char** turn_urls,
const int no_of_urls,
const char* username,
const char* credential,
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);
local_video_observer_.reset(new VideoObserver());
remote_video_observer_.reset(new VideoObserver());
// Add the turn server.
if (turn_urls != nullptr) {
if (no_of_urls > 0) {
webrtc::PeerConnectionInterface::IceServer turn_server;
for (int i = 0; i < no_of_urls; i++) {
std::string url(turn_urls[i]);
if (url.length() > 0)
turn_server.urls.push_back(turn_urls[i]);
}
std::string user_name(username);
if (user_name.length() > 0)
turn_server.username = username;
std::string password(credential);
if (password.length() > 0)
turn_server.password = credential;
config_.servers.push_back(turn_server);
}
}
// Add the stun server.
webrtc::PeerConnectionInterface::IceServer stun_server;
stun_server.uri = GetPeerConnectionString();
config_.servers.push_back(stun_server);
webrtc::FakeConstraints constraints;
constraints.SetAllowDtlsSctpDataChannels();
@ -117,7 +141,7 @@ bool SimplePeerConnection::CreatePeerConnection(bool is_receiver) {
}
peer_connection_ = g_peer_connection_factory->CreatePeerConnection(
config, &constraints, nullptr, nullptr, this);
config_, &constraints, nullptr, nullptr, this);
return peer_connection_.get() != nullptr;
}
@ -160,13 +184,8 @@ void SimplePeerConnection::OnSuccess(
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());
OnLocalSdpReady(desc->type().c_str(), sdp.c_str());
}
void SimplePeerConnection::OnFailure(const std::string& error) {
@ -180,25 +199,27 @@ 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());
OnIceCandiateReady(sdp.c_str(), candidate->sdp_mline_index(),
candidate->sdp_mid().c_str());
}
void SimplePeerConnection::RegisterOnVideoFramReady(
VIDEOFRAMEREADY_CALLBACK callback) {
OnVideoFrameReady = callback;
void SimplePeerConnection::RegisterOnLocalI420FrameReady(
I420FRAMEREADY_CALLBACK callback) {
if (local_video_observer_)
local_video_observer_->SetVideoCallback(callback);
}
void SimplePeerConnection::RegisterOnRemoteI420FrameReady(
I420FRAMEREADY_CALLBACK callback) {
if (remote_video_observer_)
remote_video_observer_->SetVideoCallback(callback);
}
void SimplePeerConnection::RegisterOnLocalDataChannelReady(
@ -230,88 +251,47 @@ void SimplePeerConnection::RegisterOnIceCandiateReadytoSend(
OnIceCandiateReady = callback;
}
bool SimplePeerConnection::ReceivedSdp(const char* msg) {
bool SimplePeerConnection::SetRemoteDescription(const char* type,
const char* sdp) {
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;
}
std::string remote_desc(sdp);
std::string sdp_type(type);
webrtc::SdpParseError error;
webrtc::SessionDescriptionInterface* session_description(
webrtc::CreateSessionDescription(type, sdp, &error));
webrtc::CreateSessionDescription(sdp_type, remote_desc, &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;
LOG(INFO) << " Received session description :" << remote_desc;
peer_connection_->SetRemoteDescription(
DummySetSessionDescriptionObserver::Create(), session_description);
return true;
}
bool SimplePeerConnection::ReceivedIceCandidate(const char* ice_candidate) {
bool SimplePeerConnection::AddIceCandidate(const char* candidate,
const int sdp_mlineindex,
const char* sdp_mid) {
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()) {
std::unique_ptr<webrtc::IceCandidateInterface> ice_candidate(
webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, candidate, &error));
if (!ice_candidate.get()) {
LOG(WARNING) << "Can't parse received candidate message. "
<< "SdpParseError was: " << error.description;
return false;
}
if (!peer_connection_->AddIceCandidate(candidate.get())) {
if (!peer_connection_->AddIceCandidate(ice_candidate.get())) {
LOG(WARNING) << "Failed to apply the received candidate";
return false;
}
LOG(INFO) << " Received candidate :" << message;
LOG(INFO) << " Received candidate :" << candidate;
return true;
}
@ -348,7 +328,10 @@ void SimplePeerConnection::OnAddStream(
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) {
LOG(INFO) << __FUNCTION__ << " " << stream->label();
remote_stream_ = stream;
if (remote_video_observer_ && !remote_stream_->GetVideoTracks().empty()) {
remote_stream_->GetVideoTracks()[0]->AddOrUpdateSink(
remote_video_observer_.get(), rtc::VideoSinkWants());
}
SetAudioControl();
}
@ -402,9 +385,13 @@ void SimplePeerConnection::AddStreams(bool audio_only) {
rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
g_peer_connection_factory->CreateVideoTrack(
kVideoLabel, g_peer_connection_factory->CreateVideoSource(
OpenVideoCaptureDevice(), nullptr)));
std::move(capture), nullptr)));
stream->AddTrack(video_track);
if (local_video_observer_ && !stream->GetVideoTracks().empty()) {
stream->GetVideoTracks()[0]->AddOrUpdateSink(
local_video_observer_.get(), rtc::VideoSinkWants());
}
}
}

View File

@ -20,6 +20,7 @@
#include "webrtc/api/mediastreaminterface.h"
#include "webrtc/api/peerconnectioninterface.h"
#include "webrtc/examples/unityplugin/unity_plugin_apis.h"
#include "webrtc/examples/unityplugin/video_observer.h"
class SimplePeerConnection : public webrtc::PeerConnectionObserver,
public webrtc::CreateSessionDescriptionObserver,
@ -29,7 +30,11 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver,
SimplePeerConnection() {}
~SimplePeerConnection() {}
bool InitializePeerConnection(bool is_receiver);
bool InitializePeerConnection(const char** turn_urls,
const int no_of_urls,
const char* username,
const char* credential,
bool is_receiver);
void DeletePeerConnection();
void AddStreams(bool audio_only);
bool CreateDataChannel();
@ -39,7 +44,8 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver,
void SetAudioControl(bool is_mute, bool is_record);
// Register callback functions.
void RegisterOnVideoFramReady(VIDEOFRAMEREADY_CALLBACK callback);
void RegisterOnLocalI420FrameReady(I420FRAMEREADY_CALLBACK callback);
void RegisterOnRemoteI420FrameReady(I420FRAMEREADY_CALLBACK callback);
void RegisterOnLocalDataChannelReady(LOCALDATACHANNELREADY_CALLBACK callback);
void RegisterOnDataFromDataChannelReady(
DATAFROMEDATECHANNELREADY_CALLBACK callback);
@ -48,16 +54,18 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver,
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);
bool SetRemoteDescription(const char* type, const char* sdp);
bool AddIceCandidate(const char* sdp,
const int sdp_mlineindex,
const char* sdp_mid);
protected:
bool CreatePeerConnection(bool receiver);
// create a peerconneciton and add the turn servers info to the configuration.
bool CreatePeerConnection(const char** turn_urls,
const int no_of_urls,
const char* username,
const char* credential,
bool is_receiver);
void CloseDataChannel();
std::unique_ptr<cricket::VideoCapturer> OpenVideoCaptureDevice();
void SetAudioControl();
@ -103,9 +111,12 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver,
std::map<std::string, rtc::scoped_refptr<webrtc::MediaStreamInterface> >
active_streams_;
webrtc::MediaStreamInterface* remote_stream_ = nullptr;
std::unique_ptr<VideoObserver> local_video_observer_;
std::unique_ptr<VideoObserver> remote_video_observer_;
webrtc::MediaStreamInterface* remote_stream_ = nullptr;
webrtc::PeerConnectionInterface::RTCConfiguration config_;
VIDEOFRAMEREADY_CALLBACK OnVideoFrameReady = nullptr;
LOCALDATACHANNELREADY_CALLBACK OnLocalDataChannelReady = nullptr;
DATAFROMEDATECHANNELREADY_CALLBACK OnDataFromDataChannelReady = nullptr;
FAILURE_CALLBACK OnFailureMessage = nullptr;

View File

@ -21,12 +21,15 @@ static std::map<int, rtc::scoped_refptr<SimplePeerConnection>>
g_peer_connection_map;
} // namespace
int CreatePeerConnection() {
int CreatePeerConnection(const char** turn_urls,
const int no_of_urls,
const char* username,
const char* credential) {
g_peer_connection_map[g_peer_connection_id] =
new rtc::RefCountedObject<SimplePeerConnection>();
if (!g_peer_connection_map[g_peer_connection_id]->InitializePeerConnection(
false))
turn_urls, no_of_urls, username, credential, false))
return -1;
return g_peer_connection_id++;
@ -89,13 +92,45 @@ bool SetAudioControl(int peer_connection_id, bool is_mute, bool is_record) {
return true;
}
// Register callback functions.
bool RegisterOnVideoFramReady(int peer_connection_id,
VIDEOFRAMEREADY_CALLBACK callback) {
bool SetRemoteDescription(int peer_connection_id,
const char* type,
const char* sdp) {
if (!g_peer_connection_map.count(peer_connection_id))
return false;
g_peer_connection_map[peer_connection_id]->RegisterOnVideoFramReady(callback);
return g_peer_connection_map[peer_connection_id]->SetRemoteDescription(type,
sdp);
}
bool AddIceCandidate(const int peer_connection_id,
const char* candidate,
const int sdp_mlineindex,
const char* sdp_mid) {
if (!g_peer_connection_map.count(peer_connection_id))
return false;
return g_peer_connection_map[peer_connection_id]->AddIceCandidate(
candidate, sdp_mlineindex, sdp_mid);
}
// Register callback functions.
bool RegisterOnLocalI420FrameReady(int peer_connection_id,
I420FRAMEREADY_CALLBACK callback) {
if (!g_peer_connection_map.count(peer_connection_id))
return false;
g_peer_connection_map[peer_connection_id]->RegisterOnLocalI420FrameReady(
callback);
return true;
}
bool RegisterOnRemoteI420FrameReady(int peer_connection_id,
I420FRAMEREADY_CALLBACK callback) {
if (!g_peer_connection_map.count(peer_connection_id))
return false;
g_peer_connection_map[peer_connection_id]->RegisterOnRemoteI420FrameReady(
callback);
return true;
}
@ -158,28 +193,3 @@ bool 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);
}

View File

@ -15,16 +15,22 @@
#include <stdint.h>
// Defintions of callback functions.
typedef void (*VIDEOFRAMEREADY_CALLBACK)(uint8_t* buffer,
uint32_t width,
uint32_t height,
uint32_t stride);
// Definitions of callback functions.
typedef void (*I420FRAMEREADY_CALLBACK)(const uint8_t* data_y,
const uint8_t* data_u,
const uint8_t* data_v,
int stride_y,
int stride_u,
int stride_v,
uint32_t width,
uint32_t height);
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 (*LOCALSDPREADYTOSEND_CALLBACK)(const char* type, const char* sdp);
typedef void (*ICECANDIDATEREADYTOSEND_CALLBACK)(const char* candidate,
const int sdp_mline_index,
const char* sdp_mid);
typedef void (*AUDIOBUSREADY_CALLBACK)(const void* audio_data,
int bits_per_sample,
int sample_rate,
@ -34,7 +40,10 @@ typedef void (*AUDIOBUSREADY_CALLBACK)(const void* audio_data,
#define WEBRTC_PLUGIN_API __declspec(dllexport)
extern "C" {
// Create a peerconnection and return a unique peer connection id.
WEBRTC_PLUGIN_API int CreatePeerConnection();
WEBRTC_PLUGIN_API int CreatePeerConnection(const char** turn_urls,
const int no_of_urls,
const char* username,
const char* credential);
// 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
@ -54,11 +63,23 @@ WEBRTC_PLUGIN_API bool SendDataViaDataChannel(int peer_connection_id,
WEBRTC_PLUGIN_API bool SetAudioControl(int peer_connection_id,
bool is_mute,
bool is_record);
// Set remote sdp.
WEBRTC_PLUGIN_API bool SetRemoteDescription(int peer_connection_id,
const char* type,
const char* sdp);
// Add ice candidate.
WEBRTC_PLUGIN_API bool AddIceCandidate(const int peer_connection_id,
const char* candidate,
const int sdp_mlineindex,
const char* sdp_mid);
// Register callback functions.
WEBRTC_PLUGIN_API bool RegisterOnVideoFramReady(
WEBRTC_PLUGIN_API bool RegisterOnLocalI420FrameReady(
int peer_connection_id,
VIDEOFRAMEREADY_CALLBACK callback);
I420FRAMEREADY_CALLBACK callback);
WEBRTC_PLUGIN_API bool RegisterOnRemoteI420FrameReady(
int peer_connection_id,
I420FRAMEREADY_CALLBACK callback);
WEBRTC_PLUGIN_API bool RegisterOnLocalDataChannelReady(
int peer_connection_id,
LOCALDATACHANNELREADY_CALLBACK callback);
@ -75,9 +96,6 @@ WEBRTC_PLUGIN_API bool RegisterOnLocalSdpReadytoSend(
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_

View File

@ -0,0 +1,27 @@
/*
* 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/video_observer.h"
void VideoObserver::SetVideoCallback(I420FRAMEREADY_CALLBACK callback) {
std::lock_guard<std::mutex> lock(mutex);
OnI420FrameReady = callback;
}
void VideoObserver::OnFrame(const webrtc::VideoFrame& frame) {
std::unique_lock<std::mutex> lock(mutex);
rtc::scoped_refptr<webrtc::PlanarYuvBuffer> buffer(
frame.video_frame_buffer()->ToI420());
if (OnI420FrameReady) {
OnI420FrameReady(buffer->DataY(), buffer->DataU(), buffer->DataV(),
buffer->StrideY(), buffer->StrideU(), buffer->StrideV(),
frame.width(), frame.height());
}
}

View File

@ -0,0 +1,35 @@
/*
* 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_VIDEO_OBSERVER_H_
#define WEBRTC_EXAMPLES_UNITYPLUGIN_VIDEO_OBSERVER_H_
#include <mutex>
#include "webrtc/api/mediastreaminterface.h"
#include "webrtc/examples/unityplugin/unity_plugin_apis.h"
#include "webrtc/media/base/videosinkinterface.h"
class VideoObserver : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
public:
VideoObserver() {}
~VideoObserver() {}
void SetVideoCallback(I420FRAMEREADY_CALLBACK callback);
protected:
// VideoSinkInterface implementation
void OnFrame(const webrtc::VideoFrame& frame) override;
private:
I420FRAMEREADY_CALLBACK OnI420FrameReady = nullptr;
std::mutex mutex;
};
#endif // WEBRTC_EXAMPLES_UNITYPLUGIN_VIDEO_OBSERVER_H_