Skip to content
This repository has been archived by the owner on Mar 22, 2022. It is now read-only.

Fix data channel before connect #11

Merged
merged 4 commits into from
Jul 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion libs/Microsoft.MixedReality.WebRTC.Native/include/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ rtc::Thread* UnsafeGetWorkerThread();

extern "C" {

//
// Errors
//

using mrsResult = std::uint32_t;

constexpr const mrsResult MRS_SUCCESS{0};

// Peer conection (0x0xx)
constexpr const mrsResult MRS_E_INVALID_PEER_HANDLE{0x8000001};
constexpr const mrsResult MRS_E_PEER_NOT_INITIALIZED{0x8000002};

// Data (0x3xx)
constexpr const mrsResult MRS_E_SCTP_NOT_NEGOTIATED{0x8000301};
constexpr const mrsResult MRS_E_INVALID_DATA_CHANNEL_ID{0x8000302};

//
// Generic utilities
//
Expand Down Expand Up @@ -243,7 +259,7 @@ mrsPeerConnectionAddLocalAudioTrack(PeerConnectionHandle peerHandle) noexcept;
/// - If id >= 0, then it adds a new out-of-band negotiated channel with the
/// given ID, and it is the responsibility of the app to create a channel with
/// the same ID on the remote peer to be able to use the channel.
MRS_API bool MRS_CALL mrsPeerConnectionAddDataChannel(
MRS_API mrsResult MRS_CALL mrsPeerConnectionAddDataChannel(
PeerConnectionHandle peerHandle,
int id, // -1 for auto, >=0 for negotiated
const char* label, // optional, can be null or empty string
Expand Down
4 changes: 2 additions & 2 deletions libs/Microsoft.MixedReality.WebRTC.Native/src/api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ mrsPeerConnectionAddLocalAudioTrack(PeerConnectionHandle peerHandle) noexcept {
return false;
}

bool MRS_CALL mrsPeerConnectionAddDataChannel(
mrsResult MRS_CALL mrsPeerConnectionAddDataChannel(
PeerConnectionHandle peerHandle,
int id,
const char* label,
Expand All @@ -519,7 +519,7 @@ bool MRS_CALL mrsPeerConnectionAddDataChannel(
DataChannelBufferingCallback{buffering_callback, buffering_user_data},
DataChannelStateCallback{state_callback, state_user_data});
}
return false;
return MRS_E_INVALID_PEER_HANDLE;
}

void MRS_CALL mrsPeerConnectionRemoveLocalVideoTrack(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,12 @@ bool PeerConnection::AddDataChannel(
config.id = id;
} else {
// Valid IDs are 0-65535 (16 bits)
return false;
return MRS_E_INVALID_DATA_CHANNEL_ID;
}
if (!sctp_negotiated_) {
// Don't try to create a data channel without SCTP negotiation, it will get
// stuck in the kConnecting state forever.
return false;
return MRS_E_SCTP_NOT_NEGOTIATED;
}
std::string labelString;
if (label)
Expand Down Expand Up @@ -275,8 +275,8 @@ bool PeerConnection::CreateOffer() noexcept {
options.offer_to_receive_audio = true;
options.offer_to_receive_video = true;
}
if (!data_channel_from_id_.empty()) {
sctp_negotiated_ = true;
if (data_channel_from_id_.empty()) {
sctp_negotiated_ = false;
}
peer_->CreateOffer(this, options);
return true;
Expand All @@ -299,6 +299,9 @@ bool PeerConnection::SetRemoteDescription(const char* type,
const char* sdp) noexcept {
if (!peer_)
return false;
if (data_channel_from_id_.empty()) {
sctp_negotiated_ = false;
}
std::string sdp_type_str(type);
auto sdp_type = webrtc::SdpTypeFromString(sdp_type_str);
if (!sdp_type.has_value())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ class PeerConnection : public webrtc::PeerConnectionObserver,
/// data channel is created before the connection is established, which will
/// force the connection to negotiate the necessary SCTP information. See
/// https://stackoverflow.com/questions/43788872/how-are-data-channels-negotiated-between-two-peers-with-webrtc
bool sctp_negotiated_ = false;
bool sctp_negotiated_ = true;

private:
PeerConnection(const PeerConnection&) = delete;
Expand Down
57 changes: 47 additions & 10 deletions libs/Microsoft.MixedReality.WebRTC/PeerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -672,9 +672,8 @@ public void Dispose()
/// On UWP this requires the "webcam" capability.
/// See <see href="https://docs.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations"/>
/// for more details.
///
/// This method throws an exception if the peer connection is not initialized.
/// </remarks>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
public Task AddLocalVideoTrackAsync(VideoCaptureDevice device = default(VideoCaptureDevice), bool enableMrc = false)
{
ThrowIfConnectionNotOpen();
Expand All @@ -692,7 +691,7 @@ public void Dispose()
/// <summary>
/// Remove from the current connection the local video track added with <see cref="AddLocalAudioTrackAsync"/>.
/// </summary>
/// <remarks>This method throws an exception if the peer connection is not initialized.</remarks>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
public void RemoveLocalVideoTrack()
{
ThrowIfConnectionNotOpen();
Expand All @@ -707,9 +706,8 @@ public void RemoveLocalVideoTrack()
/// On UWP this requires the "microphone" capability.
/// See <see href="https://docs.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations"/>
/// for more details.
///
/// This method throws an exception if the peer connection is not initialized.
/// </remarks>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
public Task AddLocalAudioTrackAsync()
{
ThrowIfConnectionNotOpen();
Expand All @@ -727,7 +725,7 @@ public Task AddLocalAudioTrackAsync()
/// <summary>
/// Remove from the current connection the local audio track added with <see cref="AddLocalAudioTrackAsync"/>.
/// </summary>
/// <remarks>This method throws an exception if the peer connection is not initialized.</remarks>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
public void RemoveLocalAudioTrack()
{
ThrowIfConnectionNotOpen();
Expand Down Expand Up @@ -756,6 +754,9 @@ public void RemoveLocalAudioTrack()
/// <param name="reliable">Indicates whether data channel messages are reliably delivered
/// (see <see cref="DataChannel.Reliable"/>).</param>
/// <returns>Returns a task which completes once the data channel is created.</returns>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
/// <exception cref="InvalidOperationException">SCTP not negotiated.</exception>
/// <exception cref="ArgumentOutOfRangeException">Invalid data channel ID, must be in [0:65535].</exception>
public async Task<DataChannel> AddDataChannelAsync(ushort id, string label, bool ordered, bool reliable)
{
if (id < 0)
Expand Down Expand Up @@ -783,6 +784,9 @@ public async Task<DataChannel> AddDataChannelAsync(ushort id, string label, bool
/// <param name="reliable">Indicates whether data channel messages are reliably delivered
/// (see <see cref="DataChannel.Reliable"/>).</param>
/// <returns>Returns a task which completes once the data channel is created.</returns>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
djee-ms marked this conversation as resolved.
Show resolved Hide resolved
/// <exception cref="InvalidOperationException">SCTP not negotiated.</exception>
/// <exception cref="ArgumentOutOfRangeException">Invalid data channel ID, must be in [0:65535].</exception>
public async Task<DataChannel> AddDataChannelAsync(string label, bool ordered, bool reliable)
{
return await AddDataChannelAsyncImpl(-1, label, ordered, reliable);
Expand All @@ -798,6 +802,9 @@ public async Task<DataChannel> AddDataChannelAsync(string label, bool ordered, b
/// <param name="reliable">Indicates whether data channel messages are reliably delivered
/// (see <see cref="DataChannel.Reliable"/>).</param>
/// <returns>Returns a task which completes once the data channel is created.</returns>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
/// <exception cref="InvalidOperationException">SCTP not negotiated.</exception>
djee-ms marked this conversation as resolved.
Show resolved Hide resolved
/// <exception cref="ArgumentOutOfRangeException">Invalid data channel ID, must be in [0:65535].</exception>
private async Task<DataChannel> AddDataChannelAsyncImpl(int id, string label, bool ordered, bool reliable)
{
// Preconditions
Expand All @@ -821,13 +828,22 @@ private async Task<DataChannel> AddDataChannelAsyncImpl(int id, string label, bo
// Create the native channel
return await Task.Run(() =>
{
if (NativeMethods.PeerConnectionAddDataChannel(_nativePeerhandle, id, label, ordered, reliable,
args.MessageCallback, userData, args.BufferingCallback, userData, args.StateCallback, userData))
uint res = NativeMethods.PeerConnectionAddDataChannel(_nativePeerhandle, id, label, ordered, reliable,
args.MessageCallback, userData, args.BufferingCallback, userData, args.StateCallback, userData);
if (res == 0)
{
return dataChannel;
}
handle.Free();
return null;
if (res == 0x80000301) // MRS_E_SCTP_NOT_NEGOTIATED
{
throw new InvalidOperationException("Cannot add a first data channel after the connection handshake started. Call AddDataChannelAsync() before calling CreateOffer().");
}
if (res == 0x80000302) // MRS_E_INVALID_DATA_CHANNEL_ID
{
throw new ArgumentOutOfRangeException("id", id, "Invalid ID passed to AddDataChannelAsync().");
}
throw new Exception("AddDataChannelAsync() failed.");
});
}

Expand All @@ -854,24 +870,44 @@ internal void SendDataChannelMessage(int id, byte[] message)
/// <param name="sdpMid"></param>
/// <param name="sdpMlineindex"></param>
/// <param name="candidate"></param>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
public void AddIceCandidate(string sdpMid, int sdpMlineindex, string candidate)
{
ThrowIfConnectionNotOpen();
NativeMethods.PeerConnectionAddIceCandidate(_nativePeerhandle, sdpMid, sdpMlineindex, candidate);
}

/// <summary>
/// Create an SDP offer message as an attempt to establish a connection.
/// </summary>
/// <returns><c>true</c> if the offer was created successfully.</returns>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
public bool CreateOffer()
{
ThrowIfConnectionNotOpen();
return NativeMethods.PeerConnectionCreateOffer(_nativePeerhandle);
}

/// <summary>
/// Create an SDP answer message to a previously-received offer, to accept a connection.
/// </summary>
/// <returns><c>true</c> if the offer was created successfully.</returns>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
public bool CreateAnswer()
{
ThrowIfConnectionNotOpen();
return NativeMethods.PeerConnectionCreateAnswer(_nativePeerhandle);
}

/// <summary>
/// Pass the given SDP description received from the remote peer via signaling to the
/// underlying WebRTC implementation, which will parse and use it.
///
/// This must be called by the signaler when receiving a message.
/// </summary>
/// <param name="type">The type of SDP message ("offer", "answer", "ice")</param>
/// <param name="sdp">The content of the SDP message</param>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
public void SetRemoteDescription(string type, string sdp)
{
ThrowIfConnectionNotOpen();
Expand All @@ -885,6 +921,7 @@ public void SetRemoteDescription(string type, string sdp)
/// Utility to throw an exception if a method is called before the underlying
/// native peer connection has been initialized.
/// </summary>
/// <exception cref="InvalidOperationException">The peer connection is not intialized.</exception>
private void ThrowIfConnectionNotOpen()
{
lock (_openCloseLock)
Expand Down Expand Up @@ -1044,7 +1081,7 @@ public static extern void PeerConnectionRegisterARGBRemoteVideoFrameCallback(Int

[DllImport(dllPath, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi,
EntryPoint = "mrsPeerConnectionAddDataChannel")]
public static extern bool PeerConnectionAddDataChannel(IntPtr peerHandle, int id, string label,
public static extern uint PeerConnectionAddDataChannel(IntPtr peerHandle, int id, string label,
bool ordered, bool reliable, PeerConnectionDataChannelMessageCallback messageCallback,
IntPtr messageUserData, PeerConnectionDataChannelBufferingCallback bufferingCallback,
IntPtr bufferingUserData, PeerConnectionDataChannelStateCallback stateCallback,
Expand Down