| /* |
| * 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. |
| * |
| */ |
| |
| #pragma once |
| |
| #include <app-common/zap-generated/cluster-objects.h> |
| #include <app/CommandSender.h> |
| #include <app/clusters/webrtc-transport-requestor-server/webrtc-transport-requestor-cluster.h> |
| #include <controller/CHIPDeviceController.h> |
| |
| /** |
| * @brief This class handles sending CHIP commands for WebRTCTransportProvider cluster, including |
| * sending a ProvideOffer command to a remote camera device. |
| * |
| * The class acts as a command sender and implements the `chip::app::CommandSender::Callback` interface |
| * to handle responses, errors, and completion events for the commands it sends. It relies on a |
| * DeviceCommissioner to manage the lower-level device connection logic. |
| */ |
| class WebRTCProviderClient : public chip::app::CommandSender::Callback |
| { |
| public: |
| using ICECandidateStruct = chip::app::Clusters::Globals::Structs::ICECandidateStruct::Type; |
| using StreamUsageEnum = chip::app::Clusters::Globals::StreamUsageEnum; |
| |
| /** |
| * @brief Construct a new WebRTCProviderClient object. Also initializes callbacks |
| * for device connection success/failure events. |
| */ |
| WebRTCProviderClient() : |
| mOnConnectedCallback(OnDeviceConnected, this), mOnConnectionFailureCallback(OnDeviceConnectionFailure, this) |
| {} |
| |
| /** |
| * @brief Initializes the WebRTCProviderClient with a ScopedNodeId, an EndpointId, and an optional |
| * pointer to the WebRTCTransportRequestorServer. |
| * |
| * @param peerId The PeerId (fabric + nodeId) for the remote device. |
| * @param endpointId The Matter endpoint on the remote device for WebRTCTransportProvider cluster. |
| * @param requestorServer Pointer to a WebRTCTransportRequestorServer instance. |
| */ |
| void Init(const chip::ScopedNodeId & peerId, chip::EndpointId endpointId, |
| chip::app::Clusters::WebRTCTransportRequestor::WebRTCTransportRequestorServer * requestorServer); |
| |
| /** |
| * @brief Sends a SolicitOffer command to the remote device. |
| * |
| * This method populates the SolicitOffer command parameters, requests that the Provider initiates |
| * a new session with the Offer / Answer flow. |
| * |
| * @param streamUsage An enum value describing how the stream is intended to be used. |
| * @param originatingEndpointId The endpoint ID that initiates the offer. |
| * @param videoStreamId Optional Video stream ID if relevant. |
| * @param audioStreamId Optional Audio stream ID if relevant. |
| * |
| * @return CHIP_NO_ERROR on success, or an appropriate CHIP_ERROR on failure. |
| */ |
| CHIP_ERROR |
| SolicitOffer(StreamUsageEnum streamUsage, chip::EndpointId originatingEndpointId, |
| chip::Optional<chip::app::DataModel::Nullable<uint16_t>> videoStreamId, |
| chip::Optional<chip::app::DataModel::Nullable<uint16_t>> audioStreamId); |
| |
| /** |
| * @brief Sends a ProvideOffer command to the remote device. |
| * |
| * This method populates the ProvideOffer command parameters, buffers the SDP locally to ensure |
| * the data remains valid for the asynchronous command flow, and then requests a device connection |
| * to eventually send the command. |
| * |
| * @param webRTCSessionId Nullable ID for the WebRTC session. |
| * @param sdp The raw SDP (Session Description Protocol) data as a standard string. |
| * @param streamUsage An enum value describing how the stream is intended to be used. |
| * @param originatingEndpointId The endpoint ID that initiates the offer. |
| * @param videoStreamId Optional Video stream ID if relevant. |
| * @param audioStreamId Optional Audio stream ID if relevant. |
| * |
| * @return CHIP_NO_ERROR on success, or an appropriate CHIP_ERROR on failure. |
| */ |
| CHIP_ERROR |
| ProvideOffer(chip::app::DataModel::Nullable<uint16_t> webRTCSessionId, std::string sdp, StreamUsageEnum streamUsage, |
| chip::EndpointId originatingEndpointId, chip::Optional<chip::app::DataModel::Nullable<uint16_t>> videoStreamId, |
| chip::Optional<chip::app::DataModel::Nullable<uint16_t>> audioStreamId); |
| |
| /** |
| * @brief Sends a ProvideAnswer command to the remote device. |
| * |
| * Invoke this after you have received an *Offer* and generated the local SDP answer for the same WebRTC session. |
| * |
| * @param webRTCSessionId The unique identifier for the established WebRTC session negotiated in the earlier *Offer*. |
| * @param sdp The raw SDP (Session Description Protocol) data as a standard string. |
| * |
| * @return CHIP_NO_ERROR on success, or an appropriate CHIP_ERROR on failure. |
| */ |
| CHIP_ERROR ProvideAnswer(uint16_t webRTCSessionId, const std::string & sdp); |
| |
| /** |
| * @brief Sends a ProvideICECandidates command to the remote device. |
| * |
| * This method populates the ProvideICECandidates command parameters, packages the provided ICE |
| * candidate strings, and queues them for sending to the target device. This is typically used |
| * to inform the remote side about potential network endpoints it can use to establish or |
| * enhance a WebRTC session. |
| * |
| * @param webRTCSessionId The unique identifier for the WebRTC session to which these |
| * ICE candidates apply. |
| * @param ICECandidates A list of ICE candidate structs. |
| * |
| * @return CHIP_NO_ERROR on success, or an appropriate CHIP_ERROR on failure. |
| */ |
| CHIP_ERROR ProvideICECandidates(uint16_t webRTCSessionId, const std::vector<std::string> & iceCandidates); |
| |
| /** |
| * @brief Notify WebRTCProviderClient that the Offer command has been received. |
| * |
| * Hanlde the stream requestor with WebRTC session details. It is sent following the receipt of a SolicitOffer |
| * command or a re-Offer initiated by the Provider. |
| * |
| * @param webRTCSessionId The unique identifier for the WebRTC session. |
| */ |
| void HandleOfferReceived(uint16_t webRTCSessionId); |
| |
| /** |
| * @brief Notify WebRTCProviderClient that the Answer command has been received. |
| * |
| * This allows the client to take appropriate transitions upon receiving |
| * confirmation that the remote decryptor has been received. |
| * |
| * @param webRTCSessionId The unique identifier for the WebRTC session. |
| */ |
| void HandleAnswerReceived(uint16_t webRTCSessionId); |
| |
| /////////// CommandSender Callback Interface ///////// |
| virtual void OnResponse(chip::app::CommandSender * client, const chip::app::ConcreteCommandPath & path, |
| const chip::app::StatusIB & status, chip::TLV::TLVReader * data) override; |
| |
| virtual void OnError(const chip::app::CommandSender * client, CHIP_ERROR error) override; |
| |
| virtual void OnDone(chip::app::CommandSender * client) override; |
| |
| private: |
| static constexpr uint16_t kMaxDeviceLabelLength = 64; |
| |
| enum class CommandType : uint8_t |
| { |
| kUndefined = 0, |
| kSolicitOffer = 1, |
| kProvideOffer = 2, |
| kProvideAnswer = 3, |
| kProvideICECandidates = 4, |
| kEndSession = 5, |
| }; |
| |
| enum class State : uint8_t |
| { |
| Idle, ///< Default state, no communication initiated yet |
| Connecting, ///< Waiting for OnDeviceConnected or OnDeviceConnectionFailure callbacks to be called |
| AwaitingResponse, ///< Waiting for command response from camera |
| AwaitingOffer, ///< Waiting for Offer command from camera |
| AwaitingAnswer, ///< Waiting for Answer command from camera |
| }; |
| |
| void MoveToState(const State targetState); |
| const char * GetStateStr() const; |
| |
| template <class T> |
| CHIP_ERROR SendCommand(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle, |
| chip::CommandId commandId, const T & requestData) |
| { |
| mCommandSender = |
| std::make_unique<chip::app::CommandSender>(this, &exchangeMgr, |
| /* isTimedRequest = */ false, |
| /* suppressResponse = */ false, sessionHandle->AllowsLargePayload()); |
| VerifyOrReturnError(mCommandSender != nullptr, CHIP_ERROR_NO_MEMORY); |
| |
| chip::app::CommandPathParams commandPath = { mEndpointId, chip::app::Clusters::WebRTCTransportProvider::Id, commandId, |
| chip::app::CommandPathFlags::kEndpointIdValid }; |
| |
| chip::app::CommandSender::AddRequestDataParameters addRequestDataParams(chip::NullOptional); |
| ReturnErrorOnFailure(mCommandSender->AddRequestData(commandPath, requestData, addRequestDataParams)); |
| |
| return mCommandSender->SendCommandRequest(sessionHandle); |
| } |
| |
| CHIP_ERROR SendCommandForType(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle, |
| CommandType commandType); |
| |
| static void OnDeviceConnected(void * context, chip::Messaging::ExchangeManager & exchangeMgr, |
| const chip::SessionHandle & sessionHandle); |
| |
| static void OnDeviceConnectionFailure(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR error); |
| |
| static void OnSessionEstablishTimeout(chip::System::Layer * systemLayer, void * appState); |
| |
| void HandleSolicitOfferResponse(chip::TLV::TLVReader & data); |
| |
| void HandleProvideOfferResponse(chip::TLV::TLVReader & data); |
| |
| // Private data members |
| chip::ScopedNodeId mPeerId; |
| chip::EndpointId mEndpointId = chip::kInvalidEndpointId; |
| CommandType mCommandType = CommandType::kUndefined; |
| uint16_t mCurrentSessionId = 0; |
| std::unique_ptr<chip::app::CommandSender> mCommandSender; |
| |
| State mState = State::Idle; |
| |
| chip::app::Clusters::WebRTCTransportRequestor::WebRTCTransportRequestorServer * mRequestorServer = nullptr; |
| |
| // Data needed to send the WebRTCTransportProvider commands |
| chip::app::Clusters::WebRTCTransportProvider::Commands::SolicitOffer::Type mSolicitOfferData; |
| chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideOffer::Type mProvideOfferData; |
| chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideAnswer::Type mProvideAnswerData; |
| chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideICECandidates::Type mProvideICECandidatesData; |
| StreamUsageEnum mCurrentStreamUsage = StreamUsageEnum::kUnknownEnumValue; |
| |
| // We store the SDP here so that mProvideOfferData.sdp points to a stable buffer. |
| std::string mSdpString; |
| |
| // Store the ICECandidates here to use to send asynchronously. |
| std::vector<std::string> mClientICECandidates; |
| std::vector<ICECandidateStruct> mICECandidateStructList; |
| |
| chip::Callback::Callback<chip::OnDeviceConnected> mOnConnectedCallback; |
| chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnConnectionFailureCallback; |
| }; |