mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 19:13:13 +00:00
1112 lines
42 KiB
C++
1112 lines
42 KiB
C++
/*
|
|
*
|
|
* Copyright (c) 2025 Project CHIP Authors
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "webrtc-provider-manager.h"
|
|
|
|
#include "webrtc-kvs_esp_port.h"
|
|
#include "webrtc-kvs_esp_port_utils.h"
|
|
#include <app/server/Server.h>
|
|
#include <controller/InvokeInteraction.h>
|
|
#include <iostream>
|
|
#include <lib/support/logging/CHIPLogging.h>
|
|
#include <webrtc-transport.h>
|
|
#include <webrtc_bridge.h>
|
|
|
|
using namespace chip;
|
|
using namespace chip::app;
|
|
using namespace chip::app::Clusters;
|
|
using namespace chip::app::Clusters::WebRTCTransportProvider;
|
|
|
|
extern bool deferred_offer;
|
|
|
|
namespace {
|
|
|
|
// Constants
|
|
constexpr uint16_t kMaxConcurrentWebRTCSessions = 3;
|
|
|
|
} // namespace
|
|
|
|
void WebRTCProviderManager::SetCameraDevice(CameraDeviceInterface * aCameraDevice)
|
|
{
|
|
mCameraDevice = aCameraDevice;
|
|
}
|
|
|
|
void WebRTCProviderManager::Init()
|
|
{
|
|
ChipLogProgress(Camera, "Initializing WebRTC PeerConnection");
|
|
// Register our handler for signaling messages
|
|
webrtc_bridge_register_handler((webrtc_bridge_msg_cb_t) &webrtc_bridge_message_received_cb);
|
|
}
|
|
|
|
void WebRTCProviderManager::CloseConnection()
|
|
{
|
|
// Clean up all the Webrtc Transports
|
|
mWebrtcTransportMap.clear();
|
|
mSessionIdMap.clear();
|
|
}
|
|
|
|
void WebRTCProviderManager::SetWebRTCTransportProvider(WebRTCTransportProviderCluster * webRTCTransportProvider)
|
|
{
|
|
mWebRTCTransportProvider = webRTCTransportProvider;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::HandleSolicitOffer(const OfferRequestArgs & args, WebRTCSessionStruct & outSession, bool & outDeferredOffer)
|
|
{
|
|
// Initialize a new WebRTC session from the SolicitOfferRequestArgs
|
|
outSession.id = args.sessionId;
|
|
outSession.peerNodeID = args.peerNodeId;
|
|
outSession.peerEndpointID = args.originatingEndpointId;
|
|
outSession.streamUsage = args.streamUsage;
|
|
outSession.fabricIndex = args.fabricIndex;
|
|
uint16_t videoStreamID = 0;
|
|
uint16_t audioStreamID = 0;
|
|
|
|
// Resolve or allocate a VIDEO stream
|
|
if (args.videoStreamId.HasValue())
|
|
{
|
|
if (args.videoStreamId.Value().IsNull())
|
|
{
|
|
// TODO: Automatically select the closest matching video stream for the
|
|
// StreamUsage requested by looking at the and the server MAY allocate a
|
|
// new video stream if there are available resources.
|
|
}
|
|
else
|
|
{
|
|
outSession.videoStreamID = args.videoStreamId.Value();
|
|
videoStreamID = args.videoStreamId.Value().Value();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outSession.videoStreamID.SetNull();
|
|
}
|
|
|
|
// Resolve or allocate an AUDIO stream
|
|
if (args.audioStreamId.HasValue())
|
|
{
|
|
if (args.audioStreamId.Value().IsNull())
|
|
{
|
|
// TODO: Automatically select the closest matching audio stream for the
|
|
// StreamUsage requested and the server MAY allocate a new audio stream if
|
|
// there are available resources.
|
|
}
|
|
else
|
|
{
|
|
outSession.audioStreamID = args.audioStreamId.Value();
|
|
audioStreamID = args.audioStreamId.Value().Value();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outSession.audioStreamID.SetNull();
|
|
}
|
|
|
|
outDeferredOffer = deferred_offer;
|
|
|
|
ChipLogError(Camera, "Deferred Offer is set to: %d", outDeferredOffer);
|
|
|
|
WebrtcTransport * transport = GetTransport(args.sessionId);
|
|
WebrtcTransport::RequestArgs requestArgs;
|
|
requestArgs.sessionId = args.sessionId;
|
|
requestArgs.fabricIndex = args.fabricIndex;
|
|
requestArgs.peerNodeId = args.peerNodeId;
|
|
requestArgs.originatingEndpointId = args.originatingEndpointId;
|
|
requestArgs.videoStreamId = videoStreamID;
|
|
requestArgs.audioStreamId = audioStreamID;
|
|
requestArgs.peerId = ScopedNodeId(args.peerNodeId, args.fabricIndex);
|
|
|
|
if (transport == nullptr)
|
|
{
|
|
mWebrtcTransportMap[args.sessionId] = std::unique_ptr<WebrtcTransport>(new WebrtcTransport());
|
|
mSessionIdMap[ScopedNodeId(args.peerNodeId, args.fabricIndex)] = args.sessionId;
|
|
transport = mWebrtcTransportMap[args.sessionId].get();
|
|
transport->SetCallbacks(
|
|
[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); });
|
|
}
|
|
|
|
transport->SetRequestArgs(requestArgs);
|
|
|
|
// Check resource availability before proceeding
|
|
// If we cannot allocate resources, send End command with OutOfResources
|
|
// reason
|
|
if (mWebrtcTransportMap.size() > kMaxConcurrentWebRTCSessions)
|
|
{
|
|
ChipLogProgress(Camera, "Resource exhaustion detected: maximum WebRTC sessions (%u)", kMaxConcurrentWebRTCSessions);
|
|
|
|
transport->SetCommandType(WebrtcTransport::CommandType::kEnd);
|
|
transport->MoveToState(WebrtcTransport::State::SendingEnd);
|
|
|
|
// The resource exhaustion happens internally in the DUT, but it still
|
|
// creates a session and then sends an End command with OutOfResources
|
|
// reason.
|
|
ScheduleEndSend(args.sessionId);
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
transport->Start();
|
|
transport->AddTracks();
|
|
|
|
// Acquire the Video and Audio Streams from the CameraAVStreamManagement
|
|
// cluster and update the reference counts.
|
|
AcquireAudioVideoStreams(args.sessionId);
|
|
|
|
transport->MoveToState(WebrtcTransport::State::SendingOffer);
|
|
|
|
ChipLogProgress(Camera, "Generate and set the SDP");
|
|
if (transport->GetPeerConnection())
|
|
transport->GetPeerConnection()->CreateOffer(args.sessionId);
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
void WebRTCProviderManager::RegisterWebrtcTransport(uint16_t sessionId)
|
|
{
|
|
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogProgress(Camera,
|
|
"WebRTC Transport is null for sessionId %u. Failed to "
|
|
"Register WebRTC Transport",
|
|
sessionId);
|
|
return;
|
|
}
|
|
|
|
WebrtcTransport::RequestArgs args = transport->GetRequestArgs();
|
|
}
|
|
|
|
void WebRTCProviderManager::UnregisterWebrtcTransport(uint16_t sessionId)
|
|
{
|
|
ChipLogProgress(Camera, "UnregisterWebrtcTransport called for sessionId: %u", sessionId);
|
|
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogProgress(Camera,
|
|
"WebRTC Transport is null for sessionId %u. Already "
|
|
"unregistered or not found",
|
|
sessionId);
|
|
return;
|
|
}
|
|
ChipLogProgress(Camera, "Successfully unregistered transport for sessionId: %u", sessionId);
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::HandleProvideOffer(const ProvideOfferRequestArgs & args, WebRTCSessionStruct & outSession)
|
|
{
|
|
ChipLogError(Zcl, "Handle Provide Offer Delegate called");
|
|
// Initialize a new WebRTC session from the SolicitOfferRequestArgs
|
|
outSession.id = args.sessionId;
|
|
outSession.peerNodeID = args.peerNodeId;
|
|
outSession.peerEndpointID = args.originatingEndpointId;
|
|
outSession.streamUsage = args.streamUsage;
|
|
outSession.fabricIndex = args.fabricIndex;
|
|
uint16_t videoStreamID = 0;
|
|
uint16_t audioStreamID = 0;
|
|
|
|
// Resolve or allocate a VIDEO stream
|
|
if (args.videoStreamId.HasValue())
|
|
{
|
|
if (args.videoStreamId.Value().IsNull())
|
|
{
|
|
// TODO: Automatically select the closest matching video stream for the
|
|
// StreamUsage requested.
|
|
}
|
|
else
|
|
{
|
|
outSession.videoStreamID = args.videoStreamId.Value();
|
|
videoStreamID = args.videoStreamId.Value().Value();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outSession.videoStreamID.SetNull();
|
|
}
|
|
|
|
// Resolve or allocate an AUDIO stream
|
|
if (args.audioStreamId.HasValue())
|
|
{
|
|
if (args.audioStreamId.Value().IsNull())
|
|
{
|
|
// TODO: Automatically select the closest matching audio stream for the
|
|
// StreamUsage requested
|
|
}
|
|
else
|
|
{
|
|
outSession.audioStreamID = args.audioStreamId.Value();
|
|
audioStreamID = args.audioStreamId.Value().Value();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outSession.audioStreamID.SetNull();
|
|
}
|
|
|
|
// Process the SDP Offer, begin the ICE Candidate gathering phase, create the
|
|
// SDP Answer, and invoke Answer.
|
|
WebrtcTransport::RequestArgs requestArgs;
|
|
requestArgs.sessionId = args.sessionId;
|
|
requestArgs.fabricIndex = args.fabricIndex;
|
|
requestArgs.peerNodeId = args.peerNodeId;
|
|
requestArgs.originatingEndpointId = args.originatingEndpointId;
|
|
requestArgs.videoStreamId = videoStreamID;
|
|
requestArgs.audioStreamId = audioStreamID;
|
|
requestArgs.peerId = ScopedNodeId(args.peerNodeId, args.fabricIndex);
|
|
|
|
WebrtcTransport * transport = GetTransport(args.sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
mWebrtcTransportMap[args.sessionId] = std::unique_ptr<WebrtcTransport>(new WebrtcTransport());
|
|
mSessionIdMap[ScopedNodeId(args.peerNodeId, args.fabricIndex)] = args.sessionId;
|
|
transport = mWebrtcTransportMap[args.sessionId].get();
|
|
transport->SetCallbacks(
|
|
[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); });
|
|
}
|
|
|
|
// Check resource availability before proceeding
|
|
// If we cannot allocate resources, respond with a response status of
|
|
// RESOURCE_EXHAUSTED
|
|
if (mWebrtcTransportMap.size() > kMaxConcurrentWebRTCSessions)
|
|
{
|
|
ChipLogProgress(Camera,
|
|
"Resource exhaustion detected in ProvideOffer: maximum "
|
|
"WebRTC sessions (%u)",
|
|
kMaxConcurrentWebRTCSessions);
|
|
return CHIP_IM_GLOBAL_STATUS(ResourceExhausted);
|
|
}
|
|
|
|
transport->SetRequestArgs(requestArgs);
|
|
transport->Start();
|
|
transport->AddTracks();
|
|
|
|
// Acquire the Video and Audio Streams from the CameraAVStreamManagement
|
|
// cluster and update the reference counts.
|
|
AcquireAudioVideoStreams(args.sessionId);
|
|
|
|
transport->MoveToState(WebrtcTransport::State::SendingAnswer);
|
|
|
|
if (transport->GetPeerConnection())
|
|
{
|
|
transport->GetPeerConnection()->SetRemoteDescription(args.sdp, SDPType::Offer);
|
|
transport->GetPeerConnection()->CreateAnswer();
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::HandleProvideAnswer(uint16_t sessionId, const std::string & sdpAnswer)
|
|
{
|
|
ChipLogProgress(Camera, "HandleProvideAnswer called with sessionId: %u", sessionId);
|
|
|
|
// Check if the provided sessionId matches your current sessions
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "Session ID %u does not match the current sessions", sessionId);
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (sdpAnswer.empty())
|
|
{
|
|
ChipLogError(Camera, "Provided SDP Answer is empty for session ID %u", sessionId);
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!transport->GetPeerConnection())
|
|
{
|
|
ChipLogError(Camera,
|
|
"Cannot set remote description: mPeerConnection is null for "
|
|
"session ID %u",
|
|
sessionId);
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
transport->GetPeerConnection()->SetRemoteDescription(sdpAnswer, SDPType::Answer);
|
|
|
|
transport->MoveToState(WebrtcTransport::State::SendingICECandidates);
|
|
ScheduleICECandidatesSend(sessionId);
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::HandleProvideICECandidates(uint16_t sessionId, const std::vector<ICECandidateStruct> & candidates)
|
|
{
|
|
ChipLogProgress(Camera, "HandleProvideICECandidates called with sessionId: %u", sessionId);
|
|
|
|
// Check if the provided sessionId matches your current sessions
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "Session ID %u does not match the current sessions", sessionId);
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (!transport->GetPeerConnection())
|
|
{
|
|
ChipLogError(Camera,
|
|
"Cannot process ICE candidates: PeerConnection is null for "
|
|
"session ID %u",
|
|
sessionId);
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
if (candidates.empty())
|
|
{
|
|
ChipLogError(Camera, "Candidate list is empty. At least one candidate is expected.");
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
for (const auto & candidate : candidates)
|
|
{
|
|
ChipLogProgress(Camera, "Applying candidate: %s",
|
|
std::string(candidate.candidate.begin(), candidate.candidate.end()).c_str());
|
|
std::string mid =
|
|
candidate.SDPMid.IsNull() ? "" : std::string(candidate.SDPMid.Value().begin(), candidate.SDPMid.Value().end());
|
|
transport->AddRemoteCandidate(std::string(candidate.candidate.begin(), candidate.candidate.end()), mid);
|
|
}
|
|
|
|
// Schedule sending Ice Candidates when remote candidates are received. This
|
|
// keeps the exchange simple
|
|
transport->MoveToState(WebrtcTransport::State::SendingICECandidates);
|
|
ScheduleICECandidatesSend(sessionId);
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::HandleEndSession(uint16_t sessionId, WebRTCEndReasonEnum reasonCode,
|
|
DataModel::Nullable<uint16_t> videoStreamID,
|
|
DataModel::Nullable<uint16_t> audioStreamID)
|
|
{
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "Session ID %u does not match the current sessions", sessionId);
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
if (transport != nullptr)
|
|
{
|
|
ChipLogProgress(Camera, "Delete Webrtc Transport for the session: %u", sessionId);
|
|
|
|
// Release the Video and Audio Streams from the CameraAVStreamManagement
|
|
// cluster and update the reference counts.
|
|
// TODO: Lookup the sessionID to get the Video/Audio StreamID
|
|
ReleaseAudioVideoStreams(sessionId);
|
|
|
|
UnregisterWebrtcTransport(sessionId);
|
|
mWebrtcTransportMap.erase(sessionId);
|
|
WebrtcTransport::RequestArgs args = transport->GetRequestArgs();
|
|
mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex));
|
|
}
|
|
|
|
if (transport->ClosePeerConnection())
|
|
{
|
|
ChipLogProgress(Camera, "Closing peer connection: %u", sessionId);
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::ValidateStreamUsage(StreamUsageEnum streamUsage, Optional<DataModel::Nullable<uint16_t>> & videoStreamId,
|
|
Optional<DataModel::Nullable<uint16_t>> & audioStreamId)
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController();
|
|
|
|
return avsmController.ValidateStreamUsage(streamUsage, videoStreamId, audioStreamId);
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::ValidateVideoStreamID(uint16_t videoStreamId)
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController();
|
|
|
|
return avsmController.ValidateVideoStreamID(videoStreamId);
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::ValidateAudioStreamID(uint16_t audioStreamId)
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController();
|
|
|
|
return avsmController.ValidateAudioStreamID(audioStreamId);
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::IsStreamUsageSupported(StreamUsageEnum streamUsage)
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
auto & hal = mCameraDevice->GetCameraHALInterface();
|
|
auto & streamUsagePriorities = hal.GetStreamUsagePriorities();
|
|
|
|
// Check if the streamUsage is in the StreamUsagePriorities list
|
|
for (const auto & usage : streamUsagePriorities)
|
|
{
|
|
if (usage == streamUsage)
|
|
{
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
ChipLogError(Camera, "StreamUsage %u not found in StreamUsagePriorities", to_underlying(streamUsage));
|
|
return CHIP_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::IsHardPrivacyModeActive(bool & isActive)
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController();
|
|
|
|
return avsmController.IsHardPrivacyModeActive(isActive);
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::IsSoftRecordingPrivacyModeActive(bool & isActive)
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController();
|
|
|
|
return avsmController.IsSoftRecordingPrivacyModeActive(isActive);
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::IsSoftLivestreamPrivacyModeActive(bool & isActive)
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController();
|
|
|
|
return avsmController.IsSoftLivestreamPrivacyModeActive(isActive);
|
|
}
|
|
|
|
bool WebRTCProviderManager::HasAllocatedVideoStreams()
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return false;
|
|
}
|
|
|
|
auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController();
|
|
|
|
return avsmController.HasAllocatedVideoStreams();
|
|
}
|
|
|
|
bool WebRTCProviderManager::HasAllocatedAudioStreams()
|
|
{
|
|
if (mCameraDevice == nullptr)
|
|
{
|
|
ChipLogError(Camera, "CameraDeviceInterface not initialized");
|
|
return false;
|
|
}
|
|
|
|
auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController();
|
|
|
|
return avsmController.HasAllocatedAudioStreams();
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::ValidateSFrameConfig(uint16_t cipherSuite, size_t baseKeyLength)
|
|
{
|
|
// Define supported cipher suites and their expected key lengths
|
|
// Based on SFrame RFC:
|
|
// https://datatracker.ietf.org/doc/html/draft-ietf-sframe-enc 0x0001:
|
|
// AES-128-GCM-SHA256 (16 byte key) 0x0002: AES-256-GCM-SHA512 (32 byte key)
|
|
constexpr uint16_t kCipherSuite_AES_128_GCM = 0x0001;
|
|
constexpr uint16_t kCipherSuite_AES_256_GCM = 0x0002;
|
|
constexpr size_t kAES_128_KeyLength = 16;
|
|
constexpr size_t kAES_256_KeyLength = 32;
|
|
|
|
size_t expectedKeyLength = 0;
|
|
|
|
// Validate cipher suite and determine expected key length
|
|
switch (cipherSuite)
|
|
{
|
|
case kCipherSuite_AES_128_GCM:
|
|
expectedKeyLength = kAES_128_KeyLength;
|
|
break;
|
|
case kCipherSuite_AES_256_GCM:
|
|
expectedKeyLength = kAES_256_KeyLength;
|
|
break;
|
|
default:
|
|
ChipLogError(Camera, "Unsupported SFrame cipher suite 0x%04X", cipherSuite);
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
// Validate base key length matches the expected length for the cipher suite
|
|
if (baseKeyLength != expectedKeyLength)
|
|
{
|
|
ChipLogError(Camera,
|
|
"SFrame base key length mismatch - expected %u bytes for "
|
|
"cipher suite 0x%04X, got %u bytes",
|
|
static_cast<unsigned int>(expectedKeyLength), cipherSuite, static_cast<unsigned int>(baseKeyLength));
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::IsUTCTimeNull(bool & isNull)
|
|
{
|
|
// TODO: The implementation SHALL:
|
|
// - Read the UTCTime attribute from the Time Synchronization cluster
|
|
// (0x0038)
|
|
// - Return whether the attribute is null or has a valid value
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
void WebRTCProviderManager::ScheduleOfferSend(uint16_t sessionId)
|
|
{
|
|
ChipLogProgress(Camera, "ScheduleOfferSend called.");
|
|
|
|
DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() {
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WebrtcTransport::RequestArgs args = transport->GetRequestArgs();
|
|
ChipLogProgress(Camera, "Sending Offer command to node " ChipLogFormatX64, ChipLogValueX64(args.peerNodeId));
|
|
|
|
transport->SetCommandType(WebrtcTransport::CommandType::kOffer);
|
|
|
|
// Attempt to find or establish a CASE session to the target PeerId.
|
|
CASESessionManager * caseSessionMgr = Server::GetInstance().GetCASESessionManager();
|
|
VerifyOrDie(caseSessionMgr != nullptr);
|
|
|
|
// WebRTC Answer requires a large payload session establishment.
|
|
caseSessionMgr->FindOrEstablishSession(args.peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback,
|
|
TransportPayloadCapability::kLargePayload);
|
|
});
|
|
}
|
|
|
|
void WebRTCProviderManager::ScheduleAnswerSend(uint16_t sessionId)
|
|
{
|
|
ChipLogProgress(Camera, "ScheduleAnswerSend called.");
|
|
|
|
DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() {
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs();
|
|
chip::ScopedNodeId peerId = requestArgs.peerId;
|
|
ChipLogProgress(Camera, "Sending Answer command to node " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
|
|
|
|
transport->SetCommandType(WebrtcTransport::CommandType::kAnswer);
|
|
|
|
// Attempt to find or establish a CASE session to the target PeerId.
|
|
CASESessionManager * caseSessionMgr = Server::GetInstance().GetCASESessionManager();
|
|
VerifyOrDie(caseSessionMgr != nullptr);
|
|
|
|
// WebRTC Answer requires a large payload session establishment.
|
|
caseSessionMgr->FindOrEstablishSession(peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback,
|
|
TransportPayloadCapability::kLargePayload);
|
|
});
|
|
}
|
|
|
|
void WebRTCProviderManager::ScheduleEndSend(uint16_t sessionId)
|
|
{
|
|
ChipLogProgress(Camera, "ScheduleEndSend called.");
|
|
|
|
DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() {
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs();
|
|
chip::ScopedNodeId peerId = requestArgs.peerId;
|
|
ChipLogProgress(Camera, "Sending End command to node " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
|
|
|
|
transport->SetCommandType(WebrtcTransport::CommandType::kEnd);
|
|
|
|
// Attempt to find or establish a CASE session to the target PeerId.
|
|
CASESessionManager * caseSessionMgr = Server::GetInstance().GetCASESessionManager();
|
|
VerifyOrDie(caseSessionMgr != nullptr);
|
|
|
|
// WebRTC Answer requires a large payload session establishment.
|
|
caseSessionMgr->FindOrEstablishSession(peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback,
|
|
TransportPayloadCapability::kLargePayload);
|
|
});
|
|
}
|
|
|
|
void WebRTCProviderManager::ScheduleICECandidatesSend(uint16_t sessionId)
|
|
{
|
|
ChipLogProgress(Camera, "ScheduleICECandidatesSend called.");
|
|
|
|
DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() {
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs();
|
|
chip::ScopedNodeId peerId = requestArgs.peerId;
|
|
ChipLogProgress(Camera, "Sending ICECandidates command to node " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
|
|
|
|
transport->SetCommandType(WebrtcTransport::CommandType::kICECandidates);
|
|
|
|
// Attempt to find or establish a CASE session to the target PeerId.
|
|
CASESessionManager * caseSessionMgr = Server::GetInstance().GetCASESessionManager();
|
|
VerifyOrDie(caseSessionMgr != nullptr);
|
|
|
|
// WebRTC Answer requires a large payload session establishment.
|
|
caseSessionMgr->FindOrEstablishSession(peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback,
|
|
TransportPayloadCapability::kLargePayload);
|
|
});
|
|
}
|
|
|
|
void WebRTCProviderManager::OnDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr,
|
|
const SessionHandle & sessionHandle)
|
|
{
|
|
WebRTCProviderManager * self = reinterpret_cast<WebRTCProviderManager *>(context);
|
|
VerifyOrReturn(self != nullptr, ChipLogError(Camera, "OnDeviceConnected:: context is null"));
|
|
|
|
// Derive sessionId from sessionHandle by looking up the peer ScopedNodeId
|
|
// (NodeId + FabricIndex)
|
|
ScopedNodeId peerScopedNodeId = sessionHandle->GetPeer();
|
|
auto sessionIt = self->mSessionIdMap.find(peerScopedNodeId);
|
|
if (sessionIt == self->mSessionIdMap.end())
|
|
{
|
|
ChipLogError(Camera,
|
|
"OnDeviceConnected:: no session found for peer ScopedNodeId: "
|
|
"[%d:" ChipLogFormatX64 "]",
|
|
peerScopedNodeId.GetFabricIndex(), ChipLogValueX64(peerScopedNodeId.GetNodeId()));
|
|
return;
|
|
}
|
|
|
|
uint16_t sessionId = sessionIt->second;
|
|
WebrtcTransport * transport = self->GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "OnDeviceConnected:: transport not found for sessionId: %u", sessionId);
|
|
return;
|
|
}
|
|
|
|
ChipLogProgress(Camera,
|
|
"CASE session established, sending command with Command "
|
|
"Type: %d, for sessionID: %u",
|
|
static_cast<int>(transport->GetCommandType()), sessionId);
|
|
|
|
CHIP_ERROR err = CHIP_NO_ERROR;
|
|
|
|
switch (transport->GetCommandType())
|
|
{
|
|
case WebrtcTransport::CommandType::kOffer:
|
|
err = self->SendOfferCommand(exchangeMgr, sessionHandle, sessionId);
|
|
transport->MoveToState(WebrtcTransport::State::Idle);
|
|
break;
|
|
|
|
case WebrtcTransport::CommandType::kAnswer:
|
|
err = self->SendAnswerCommand(exchangeMgr, sessionHandle, sessionId);
|
|
transport->MoveToState(WebrtcTransport::State::Idle);
|
|
break;
|
|
case WebrtcTransport::CommandType::kICECandidates:
|
|
err = self->SendICECandidatesCommand(exchangeMgr, sessionHandle, sessionId);
|
|
transport->MoveToState(WebrtcTransport::State::Idle);
|
|
break;
|
|
case WebrtcTransport::CommandType::kEnd: {
|
|
// Determine the end reason - check if it's due to privacy mode or resource
|
|
// exhaustion
|
|
WebRTCEndReasonEnum endReason = WebRTCEndReasonEnum::kOutOfResources;
|
|
|
|
if (self->mSoftLiveStreamPrivacyEnabled)
|
|
{
|
|
endReason = WebRTCEndReasonEnum::kPrivacyMode;
|
|
}
|
|
|
|
err = self->SendEndCommand(exchangeMgr, sessionHandle, sessionId, endReason);
|
|
// Release the Video and Audio Streams from the CameraAVStreamManagement
|
|
// cluster and update the reference counts.
|
|
self->ReleaseAudioVideoStreams(sessionId);
|
|
self->UnregisterWebrtcTransport(sessionId);
|
|
WebrtcTransport::RequestArgs args = transport->GetRequestArgs();
|
|
self->mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex));
|
|
|
|
transport->MoveToState(WebrtcTransport::State::Idle);
|
|
|
|
// remove from current sessions list
|
|
self->mWebRTCTransportProvider->RemoveSession(sessionId);
|
|
self->mWebrtcTransportMap.erase(sessionId);
|
|
break;
|
|
}
|
|
default:
|
|
err = CHIP_ERROR_INVALID_ARGUMENT;
|
|
break;
|
|
}
|
|
|
|
if (err != CHIP_NO_ERROR)
|
|
{
|
|
ChipLogError(Camera, "OnDeviceConnected::SendCommand failed: %" CHIP_ERROR_FORMAT, err.Format());
|
|
}
|
|
}
|
|
|
|
void WebRTCProviderManager::OnDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR err)
|
|
{
|
|
LogErrorOnFailure(err);
|
|
WebRTCProviderManager * self = reinterpret_cast<WebRTCProviderManager *>(context);
|
|
VerifyOrReturn(self != nullptr, ChipLogError(Camera, "OnDeviceConnectionFailure: context is null"));
|
|
}
|
|
|
|
WebrtcTransport * WebRTCProviderManager::GetTransport(uint16_t sessionId)
|
|
{
|
|
WebrtcTransport * transport = nullptr;
|
|
if (mWebrtcTransportMap.find(sessionId) != mWebrtcTransportMap.end())
|
|
{
|
|
transport = mWebrtcTransportMap[sessionId].get();
|
|
}
|
|
|
|
return transport;
|
|
}
|
|
|
|
void WebRTCProviderManager::LiveStreamPrivacyModeChanged(bool privacyModeEnabled)
|
|
{
|
|
mSoftLiveStreamPrivacyEnabled = privacyModeEnabled;
|
|
|
|
if (privacyModeEnabled)
|
|
{
|
|
WebrtcTransport * transport = nullptr;
|
|
uint16_t sessionId = 0;
|
|
for (auto & mapEntry : mWebrtcTransportMap)
|
|
{
|
|
sessionId = mapEntry.first;
|
|
|
|
transport = (WebrtcTransport *) mapEntry.second.get();
|
|
|
|
if (transport == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
transport->MoveToState(WebrtcTransport::State::SendingEnd);
|
|
|
|
ScheduleEndSend(sessionId);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ChipLogProgress(Camera, "Privacy mode is disabled");
|
|
}
|
|
}
|
|
|
|
CHIP_ERROR
|
|
WebRTCProviderManager::SendOfferCommand(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle,
|
|
uint16_t sessionId)
|
|
{
|
|
auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) {
|
|
ChipLogProgress(Camera, "Offer command succeeds");
|
|
};
|
|
|
|
auto onFailure = [](CHIP_ERROR error) { ChipLogError(Camera, "Offer command failed: %" CHIP_ERROR_FORMAT, error.Format()); };
|
|
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "SendOfferCommand failed, WebTransport not found for sessionId: %u", sessionId);
|
|
return CHIP_ERROR_INTERNAL;
|
|
}
|
|
|
|
// Build the command
|
|
WebRTCTransportRequestor::Commands::Offer::Type command;
|
|
command.webRTCSessionID = sessionId;
|
|
std::string localSdp = transport->GetLocalDescription();
|
|
command.sdp = CharSpan::fromCharString(localSdp.c_str());
|
|
|
|
WebrtcTransport::RequestArgs args = transport->GetRequestArgs();
|
|
// Now invoke the command using the found session handle
|
|
return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, args.originatingEndpointId, command, onSuccess, onFailure,
|
|
/* timedInvokeTimeoutMs = */ NullOptional,
|
|
/* responseTimeout = */ NullOptional,
|
|
/* outCancelFn = */ nullptr, /*allowLargePayload = */ true);
|
|
}
|
|
|
|
void WebRTCProviderManager::OnLocalDescription(const std::string & sdp, SDPType type, const uint16_t sessionId)
|
|
{
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "SendOfferCommand failed, WebTransport not found for sessionId: %u", sessionId);
|
|
return;
|
|
}
|
|
|
|
WebrtcTransport::State state = transport->GetState();
|
|
if (state == WebrtcTransport::State::SendingAnswer && type != SDPType::Answer)
|
|
{
|
|
return;
|
|
}
|
|
const char * typeStr = (type == SDPType::Offer) ? "offer" : "answer";
|
|
std::string localSdp = sdp;
|
|
ChipLogProgress(Camera, "Local Description (%s):", typeStr);
|
|
ChipLogProgress(Camera, "%s", localSdp.c_str());
|
|
ChipLogProgress(Camera, "Size of SDP: %zu", localSdp.size());
|
|
|
|
switch (state)
|
|
{
|
|
case WebrtcTransport::State::SendingOffer:
|
|
ScheduleOfferSend(sessionId);
|
|
break;
|
|
case WebrtcTransport::State::SendingAnswer:
|
|
ScheduleAnswerSend(sessionId);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WebRTCProviderManager::OnConnectionStateChanged(bool connected, const uint16_t sessionId)
|
|
{
|
|
ChipLogProgress(Camera, "Connection state changed for session %u: %s", sessionId, connected ? "connected" : "disconnected");
|
|
|
|
if (connected)
|
|
{
|
|
RegisterWebrtcTransport(sessionId);
|
|
}
|
|
else
|
|
{
|
|
// Schedule cleanup on Matter thread to ensure proper locking when calling
|
|
// RemoveSession. Safe to capture 'this' by value: WebRTCProviderManager is
|
|
// a member of the global CameraDevice object which has static storage
|
|
// duration and lives for the entire program lifetime.
|
|
DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() {
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogProgress(Camera,
|
|
"Transport not found for session %u during disconnect; "
|
|
"session may have already been cleaned up",
|
|
sessionId);
|
|
return;
|
|
}
|
|
|
|
// Connection was closed/disconnected by the peer - clean up the session
|
|
ChipLogProgress(Camera, "Peer connection closed for session %u, cleaning up resources", sessionId);
|
|
|
|
// Release the Video and Audio Streams from the CameraAVStreamManagement
|
|
// cluster and update the reference counts.
|
|
ReleaseAudioVideoStreams(sessionId);
|
|
|
|
// Capture args before unregistering in case the transport is invalidated
|
|
WebrtcTransport::RequestArgs args = transport->GetRequestArgs();
|
|
|
|
// Unregister the transport from the media controller
|
|
UnregisterWebrtcTransport(sessionId);
|
|
|
|
// Remove from session maps
|
|
mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex));
|
|
|
|
// Remove from current sessions list in the WebRTC Transport Provider
|
|
// This MUST be called on the Matter thread with the stack lock held
|
|
if (mWebRTCTransportProvider != nullptr)
|
|
{
|
|
mWebRTCTransportProvider->RemoveSession(sessionId);
|
|
}
|
|
|
|
// Finally, remove and destroy the transport
|
|
mWebrtcTransportMap.erase(sessionId);
|
|
|
|
ChipLogProgress(Camera, "Session %u cleanup completed", sessionId);
|
|
});
|
|
}
|
|
}
|
|
CHIP_ERROR WebRTCProviderManager::SendAnswerCommand(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle,
|
|
uint16_t sessionId)
|
|
{
|
|
auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) {
|
|
ChipLogProgress(Camera, "Answer command succeeds");
|
|
};
|
|
|
|
auto onFailure = [](CHIP_ERROR error) { ChipLogError(Camera, "Answer command failed: %" CHIP_ERROR_FORMAT, error.Format()); };
|
|
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "SendOfferCommand failed, WebTransport not found for sessionId: %u", sessionId);
|
|
return CHIP_ERROR_INTERNAL;
|
|
}
|
|
|
|
// Build the command
|
|
WebRTCTransportRequestor::Commands::Answer::Type command;
|
|
command.webRTCSessionID = sessionId;
|
|
std::string localSdp = transport->GetLocalDescription();
|
|
command.sdp = CharSpan::fromCharString(localSdp.c_str());
|
|
|
|
WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs();
|
|
// Now invoke the command using the found session handle
|
|
return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, onSuccess,
|
|
onFailure,
|
|
/* timedInvokeTimeoutMs = */ NullOptional,
|
|
/* responseTimeout = */ NullOptional,
|
|
/* outCancelFn = */ nullptr, /*allowLargePayload = */ true);
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::SendICECandidatesCommand(Messaging::ExchangeManager & exchangeMgr,
|
|
const SessionHandle & sessionHandle, uint16_t sessionId)
|
|
{
|
|
auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) {
|
|
ChipLogProgress(Camera, "ICECandidates command succeeds");
|
|
};
|
|
|
|
auto onFailure = [](CHIP_ERROR error) {
|
|
ChipLogError(Camera, "ICECandidates command failed: %" CHIP_ERROR_FORMAT, error.Format());
|
|
};
|
|
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId);
|
|
return CHIP_ERROR_INTERNAL;
|
|
}
|
|
std::vector<std::string> localCandidates = transport->GetCandidates();
|
|
// Build the command
|
|
WebRTCTransportRequestor::Commands::ICECandidates::Type command;
|
|
|
|
if (localCandidates.empty())
|
|
{
|
|
ChipLogError(Camera, "No local ICE candidates to send");
|
|
return CHIP_ERROR_INCORRECT_STATE;
|
|
}
|
|
|
|
std::vector<ICECandidateStruct> iceCandidateStructList;
|
|
for (const auto & candidate : localCandidates)
|
|
{
|
|
ICECandidateStruct iceCandidate = { CharSpan::fromCharString(candidate.c_str()) };
|
|
iceCandidateStructList.push_back(iceCandidate);
|
|
}
|
|
|
|
command.webRTCSessionID = sessionId;
|
|
command.ICECandidates = DataModel::List<const ICECandidateStruct>(iceCandidateStructList.data(), iceCandidateStructList.size());
|
|
|
|
WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs();
|
|
// Now invoke the command using the found session handle
|
|
return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, onSuccess,
|
|
onFailure,
|
|
/* timedInvokeTimeoutMs = */ NullOptional,
|
|
/* responseTimeout = */ NullOptional,
|
|
/* outCancelFn = */ nullptr, /*allowLargePayload = */ true);
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::SendEndCommand(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle,
|
|
uint16_t sessionId, WebRTCEndReasonEnum endReason)
|
|
{
|
|
auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) {
|
|
ChipLogProgress(Camera, "End command succeeds");
|
|
};
|
|
|
|
auto onFailure = [](CHIP_ERROR error) { ChipLogError(Camera, "End command failed: %" CHIP_ERROR_FORMAT, error.Format()); };
|
|
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId);
|
|
return CHIP_ERROR_INTERNAL;
|
|
}
|
|
|
|
// Build the command
|
|
WebRTCTransportRequestor::Commands::End::Type command;
|
|
|
|
command.webRTCSessionID = sessionId;
|
|
command.reason = endReason;
|
|
|
|
WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs();
|
|
// Now invoke the command using the found session handle
|
|
return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, onSuccess,
|
|
onFailure,
|
|
/* timedInvokeTimeoutMs = */ NullOptional,
|
|
/* responseTimeout = */ NullOptional,
|
|
/* outCancelFn = */ nullptr, /*allowLargePayload = */ true);
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::AcquireAudioVideoStreams(uint16_t sessionId)
|
|
{
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId);
|
|
return CHIP_ERROR_INTERNAL;
|
|
}
|
|
|
|
WebrtcTransport::RequestArgs args = transport->GetRequestArgs();
|
|
return mCameraDevice->GetCameraAVStreamMgmtDelegate().OnTransportAcquireAudioVideoStreams(args.audioStreamId,
|
|
args.videoStreamId);
|
|
}
|
|
|
|
CHIP_ERROR WebRTCProviderManager::ReleaseAudioVideoStreams(uint16_t sessionId)
|
|
{
|
|
WebrtcTransport * transport = GetTransport(sessionId);
|
|
if (transport == nullptr)
|
|
{
|
|
ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId);
|
|
return CHIP_ERROR_INTERNAL;
|
|
}
|
|
|
|
WebrtcTransport::RequestArgs args = transport->GetRequestArgs();
|
|
// TODO: Use passed in audio/video stream ids corresponding to a sessionId.
|
|
return mCameraDevice->GetCameraAVStreamMgmtDelegate().OnTransportReleaseAudioVideoStreams(args.audioStreamId,
|
|
args.videoStreamId);
|
|
}
|