Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class WebRTCProviderManager : public Delegate, public WebRTCTransportProviderCon
// WebRTC Callbacks
void OnLocalDescription(const std::string & sdp, SDPType type, const uint16_t sessionId);
void OnConnectionStateChanged(bool connected, const uint16_t sessionId);
void OnTrickleICECandidate(const uint16_t sessionId);

WebrtcTransport * GetTransport(uint16_t sessionId);

Expand Down
19 changes: 18 additions & 1 deletion examples/camera-app/linux/include/webrtc-transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

using OnTransportLocalDescriptionCallback = std::function<void(const std::string & sdp, SDPType type, const int16_t sessionId)>;
using OnTransportConnectionStateCallback = std::function<void(bool connected, const int16_t sessionId)>;
using OnTransportICECandidateCallback = std::function<void(const int16_t sessionId)>;

// Derived class for WebRTC transport
class WebrtcTransport : public Transport
Expand Down Expand Up @@ -65,7 +66,8 @@ class WebrtcTransport : public Transport

~WebrtcTransport();

void SetCallbacks(OnTransportLocalDescriptionCallback onLocalDescription, OnTransportConnectionStateCallback onConnectionState);
void SetCallbacks(OnTransportLocalDescriptionCallback onLocalDescription, OnTransportConnectionStateCallback onConnectionState,
OnTransportICECandidateCallback onICECandidate = nullptr);

void MoveToState(const State targetState);
const char * GetStateStr() const;
Expand Down Expand Up @@ -107,6 +109,15 @@ class WebrtcTransport : public Transport

std::vector<std::string> GetCandidates() { return mLocalCandidates; }

// Drain candidates - returns all accumulated candidates and clears the internal list
// This prevents resending the same candidates multiple times during trickle ICE
std::vector<std::string> DrainCandidates()
{
std::vector<std::string> candidates = std::move(mLocalCandidates);
mLocalCandidates.clear();
return candidates;
}

void SetCandidates(std::vector<std::string> candidates) { mLocalCandidates = candidates; }

void AddRemoteCandidate(const std::string & candidate, const std::string & mid);
Expand All @@ -126,6 +137,9 @@ class WebrtcTransport : public Transport
void SetRequestArgs(const RequestArgs & args);
RequestArgs & GetRequestArgs();

void SetHasSentInitialICECandidates(bool hasSent) { mHasSentInitialICECandidates = hasSent; }
bool GetHasSentInitialICECandidates() const { return mHasSentInitialICECandidates; }

private:
CommandType mCommandType = CommandType::kUndefined;
State mState = State::Idle;
Expand All @@ -143,4 +157,7 @@ class WebrtcTransport : public Transport
RequestArgs mRequestArgs;
OnTransportLocalDescriptionCallback mOnLocalDescription = nullptr;
OnTransportConnectionStateCallback mOnConnectionState = nullptr;
OnTransportICECandidateCallback mOnICECandidate = nullptr;

bool mHasSentInitialICECandidates = false;
};
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ CHIP_ERROR WebRTCProviderManager::HandleSolicitOffer(const OfferRequestArgs & ar
[this](const std::string & sdp, SDPType type, const uint16_t sessionId) {
this->OnLocalDescription(sdp, type, sessionId);
},
[this](bool connected, const uint16_t sessionId) { this->OnConnectionStateChanged(connected, sessionId); });
[this](bool connected, const uint16_t sessionId) { this->OnConnectionStateChanged(connected, sessionId); },
[this](const uint16_t sessionId) { this->OnTrickleICECandidate(sessionId); });
}

transport->SetRequestArgs(requestArgs);
Expand Down Expand Up @@ -297,7 +298,8 @@ CHIP_ERROR WebRTCProviderManager::HandleProvideOffer(const ProvideOfferRequestAr
[this](const std::string & sdp, SDPType type, const uint16_t sessionId) {
this->OnLocalDescription(sdp, type, sessionId);
},
[this](bool connected, const uint16_t sessionId) { this->OnConnectionStateChanged(connected, sessionId); });
[this](bool connected, const uint16_t sessionId) { this->OnConnectionStateChanged(connected, sessionId); },
[this](const uint16_t sessionId) { this->OnTrickleICECandidate(sessionId); });
}

// Check resource availability before proceeding
Expand Down Expand Up @@ -367,6 +369,10 @@ CHIP_ERROR WebRTCProviderManager::HandleProvideAnswer(uint16_t sessionId, const
transport->MoveToState(WebrtcTransport::State::SendingICECandidates);
ScheduleICECandidatesSend(sessionId);

// Set the flag after successfully handling the answer and scheduling ICE candidates
transport->SetHasSentInitialICECandidates(true);
ChipLogProgress(Camera, "Initial ICE candidates batch will be sent for sessionID: %u, trickle ICE enabled", sessionId);

return CHIP_NO_ERROR;
}

Expand Down Expand Up @@ -407,6 +413,13 @@ CHIP_ERROR WebRTCProviderManager::HandleProvideICECandidates(uint16_t sessionId,
transport->MoveToState(WebrtcTransport::State::SendingICECandidates);
ScheduleICECandidatesSend(sessionId);

if (!transport->GetHasSentInitialICECandidates())
{
// Set the flag after successfully handling ICE candidates
transport->SetHasSentInitialICECandidates(true);
ChipLogProgress(Camera, "Initial ICE candidates batch will be sent for sessionID: %u, trickle ICE enabled", sessionId);
}

return CHIP_NO_ERROR;
}

Expand Down Expand Up @@ -710,6 +723,10 @@ void WebRTCProviderManager::OnDeviceConnected(void * context, Messaging::Exchang
}

err = self->SendEndCommand(exchangeMgr, sessionHandle, sessionId, endReason);

// Unset the flag when session closes
transport->SetHasSentInitialICECandidates(false);

// Release the Video and Audio Streams from the CameraAVStreamManagement
// cluster and update the reference counts.
self->ReleaseAudioVideoStreams(sessionId);
Expand Down Expand Up @@ -856,6 +873,13 @@ void WebRTCProviderManager::OnConnectionStateChanged(bool connected, const uint1
}
}

void WebRTCProviderManager::OnTrickleICECandidate(const uint16_t sessionId)
{
ChipLogProgress(Camera, "Trickle ICE candidate received for session %u", sessionId);
// Reuse the existing ICE candidates send mechanism
ScheduleICECandidatesSend(sessionId);
}

CHIP_ERROR WebRTCProviderManager::SendAnswerCommand(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle,
uint16_t sessionId)
{
Expand Down Expand Up @@ -905,7 +929,9 @@ CHIP_ERROR WebRTCProviderManager::SendICECandidatesCommand(Messaging::ExchangeMa
ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId);
return CHIP_ERROR_INTERNAL;
}
std::vector<std::string> localCandidates = transport->GetCandidates();
// Drain candidates to get all accumulated candidates and clear the list
// This prevents resending the same candidates during trickle ICE
std::vector<std::string> localCandidates = transport->DrainCandidates();
// Build the command
WebRTCTransportRequestor::Commands::ICECandidates::Type command;

Expand Down
12 changes: 11 additions & 1 deletion examples/camera-app/linux/src/webrtc-transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ WebrtcTransport::~WebrtcTransport()
}

void WebrtcTransport::SetCallbacks(OnTransportLocalDescriptionCallback onLocalDescription,
OnTransportConnectionStateCallback onConnectionState)
OnTransportConnectionStateCallback onConnectionState,
OnTransportICECandidateCallback onICECandidate)
{
mOnLocalDescription = onLocalDescription;
mOnConnectionState = onConnectionState;
mOnICECandidate = onICECandidate;
}

void WebrtcTransport::SetRequestArgs(const RequestArgs & args)
Expand Down Expand Up @@ -214,6 +216,14 @@ void WebrtcTransport::OnICECandidate(const std::string & candidate)
mLocalCandidates.push_back(candidate);
ChipLogProgress(Camera, "Local Candidate:");
ChipLogProgress(Camera, "%s", candidate.c_str());

// If we've already sent the initial batch of ICE candidates and we're back in Idle state,
// send this candidate immediately (trickle ICE)
if (mHasSentInitialICECandidates && mState == State::Idle && mOnICECandidate)
{
ChipLogProgress(Camera, "Sending trickle ICE candidate immediately for sessionID: %u", mRequestArgs.sessionId);
mOnICECandidate(mRequestArgs.sessionId);
}
}

void WebrtcTransport::OnConnectionStateChanged(bool connected)
Expand Down
26 changes: 24 additions & 2 deletions examples/camera-controller/webrtc-manager/WebRTCManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ CHIP_ERROR WebRTCManager::HandleAnswer(uint16_t sessionId, const std::string & s
[](chip::System::Layer * systemLayer, void * appState) {
auto * self = static_cast<WebRTCManager *>(appState);
self->ProvideICECandidates(self->mPendingSessionId);
// Mark that we've sent the initial batch, enabling trickle ICE
if (!self->mHasSentInitialICECandidates)
{
self->mHasSentInitialICECandidates = true;
ChipLogProgress(Camera, "Initial ICE candidates batch sent for session %u, trickle ICE enabled",
self->mPendingSessionId);
}
},
this);

Expand Down Expand Up @@ -241,8 +248,9 @@ void WebRTCManager::Disconnect()
audioTrack.reset();

// Clear state
mCurrentVideoStreamId = 0;
mPendingSessionId = 0;
mCurrentVideoStreamId = 0;
mPendingSessionId = 0;
mHasSentInitialICECandidates = false;
mLocalDescription.clear();
mLocalCandidates.clear();
}
Expand Down Expand Up @@ -284,6 +292,14 @@ CHIP_ERROR WebRTCManager::Connnect(Controller::DeviceCommissioner & commissioner
mLocalCandidates.push_back(candidateStr);
ChipLogProgress(Camera, "Local Candidate:");
ChipLogProgress(Camera, "%s", candidateStr.c_str());

// If we've already sent the initial batch, send this candidate immediately (trickle ICE)
if (mHasSentInitialICECandidates)
{
ChipLogProgress(Camera, "Sending trickle ICE candidate immediately for session %u", mPendingSessionId);
// Send only the new candidate(s) that were just added
ProvideICECandidates(mPendingSessionId);
}
});

mPeerConnection->onStateChange([this](rtc::PeerConnection::State state) {
Expand Down Expand Up @@ -464,6 +480,12 @@ CHIP_ERROR WebRTCManager::ProvideICECandidates(uint16_t sessionId)
{
ChipLogError(Camera, "Failed to send ICE candidates: %" CHIP_ERROR_FORMAT, err.Format());
}
else
{
// Clear the candidates after successful send to prevent resending them during trickle ICE
ChipLogProgress(Camera, "Sent %lu ICE candidate(s), clearing list", mLocalCandidates.size());
mLocalCandidates.clear();
}

return err;
}
3 changes: 3 additions & 0 deletions examples/camera-controller/webrtc-manager/WebRTCManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ class WebRTCManager
// Track the current video stream ID for the session
uint16_t mCurrentVideoStreamId = 0;

// Flag to track if initial ICE candidates batch has been sent
bool mHasSentInitialICECandidates = false;

// UDP socket for RTP forwarding
int mRTPSocket = -1;
int mAudioRTPSocket = -1;
Expand Down
Loading