From 40fee02e572a8bef027c20a70c04be0f3f63d5e4 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Wed, 26 Feb 2025 16:13:44 +0530 Subject: [PATCH 01/40] initial implementation of pushav server --- src/app/chip_data_model.gni | 1 + .../push-av-stream-transport-server.cpp | 369 ++++++++++++++++++ .../push-av-stream-transport-server.h | 290 ++++++++++++++ src/app/common/templates/config-data.yaml | 1 + src/app/zap_cluster_list.json | 4 +- .../app-common/zap-generated/callback.h | 53 +++ 6 files changed, 717 insertions(+), 1 deletion(-) create mode 100644 src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp create mode 100644 src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index ccdf551fc9e267..846827c7663b49 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -468,6 +468,7 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/closure-control-cluster-objects.h", ] } else if (cluster == "camera-av-stream-management-server") { + } else if (cluster == "push-av-stream-transport-server") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", "${_app_root}/clusters/${cluster}/${cluster}.h", diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp new file mode 100644 index 00000000000000..f8d58ec89f5111 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -0,0 +1,369 @@ +/** + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PUSH_TRANSPORT_CONNECTION_ID 65535 + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::PushAvStreamTransport; +using namespace chip::app::Clusters::PushAvStreamTransport::Structs; +using namespace chip::app::Clusters::PushAvStreamTransport::Attributes; +using namespace Protocols::InteractionModel; + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +PushAvStreamTransportServer::PushAvStreamTransportServer(EndpointId aEndpointId, PushAvStreamTransportDelegate & aDelegate, + const BitFlags aFeature, + PersistentStorageDelegate & aPersistentStorage) : + CommandHandlerInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), + AttributeAccessInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), mDelegate(aDelegate), + mEndpointId(aEndpointId), mFeature(aFeature) +{ + mDelegate.SetPushAvStreamTransportServer(this); +} + +PushAvStreamTransportServer::~PushAvStreamTransportServer() +{ + // Explicitly set the PushAvStreamTransportServer pointer in the Delegate to null. + + mDelegate.SetPushAvStreamTransportServer(nullptr); + + // Unregister command handler and attribute access interfaces + CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(this); + AttributeAccessInterfaceRegistry::Instance().Unregister(this); +} + +CHIP_ERROR PushAvStreamTransportServer::Init() +{ + LoadPersistentAttributes(); + + VerifyOrReturnError(AttributeAccessInterfaceRegistry::Instance().Register(this), CHIP_ERROR_INTERNAL); + ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(this)); + return CHIP_NO_ERROR; +} + +bool PushAvStreamTransportServer::HasFeature(Feature feature) const +{ + return mFeature.Has(feature); +} + +CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder) +{ + for (const auto & currentConnections : mCurrentConnections) + { + ReturnErrorOnFailure(encoder.Encode(currentConnections)); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PushAvStreamTransportServer::AddStreamTransportConnection(const uint16_t transportConnectionId) +{ + mCurrentConnections.push_back(transportConnectionId); + auto path = ConcreteAttributePath(mEndpointId, PushAvStreamTransport::Id, Attributes::CurrentConnections::Id); + mDelegate.OnAttributeChanged(Attributes::CurrentConnections::Id); + MatterReportingAttributeChangeCallback(path); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t transportConnectionId) +{ + mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), + [&](const uint16_t connectionID) { return connectionID == transportConnectionId; }), + mCurrentConnections.end()); + auto path = ConcreteAttributePath(mEndpointId, PushAvStreamTransport::Id, Attributes::CurrentConnections::Id); + mDelegate.OnAttributeChanged(Attributes::CurrentConnections::Id); + MatterReportingAttributeChangeCallback(path); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PushAvStreamTransportServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + VerifyOrDie(aPath.mClusterId == PushAvStreamTransport::Id); + ChipLogError(Zcl, "Push AV Stream Transport: Reading"); + + switch (aPath.mAttributeId) + { + case FeatureMap::Id: + ReturnErrorOnFailure(aEncoder.Encode(mFeature)); + break; + + case SupportedContainerFormats::Id: + ReturnErrorOnFailure(aEncoder.Encode(mSupportedContainerFormats)); + break; + + case SupportedIngestMethods::Id: + ReturnErrorOnFailure(aEncoder.Encode(mSupportedIngestMethods)); + break; + + case CurrentConnections::Id: + ReturnErrorOnFailure(aEncoder.EncodeList( + [this](const auto & encoder) -> CHIP_ERROR { return this->ReadAndEncodeCurrentConnections(encoder); })); + break; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PushAvStreamTransportServer::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) +{ + VerifyOrDie(aPath.mClusterId == PushAvStreamTransport::Id); + + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); +} + +void PushAvStreamTransportServer::LoadPersistentAttributes() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Load currentConnections + mDelegate.LoadCurrentConnections(mCurrentConnections); + + // Signal delegate that all persistent configuration attributes have been loaded. + mDelegate.PersistentAttributesLoadedCallback(); +} + +// CommandHandlerInterface +void PushAvStreamTransportServer::InvokeCommand(HandlerContext & handlerContext) +{ + ChipLogDetail(Zcl, "PushAV: InvokeCommand"); + switch (handlerContext.mRequestPath.mCommandId) + { + case Commands::AllocatePushTransport::Id: + ChipLogDetail(Zcl, "PushAVStreamTransport: Allocating Push Transport"); + + HandleCommand( + handlerContext, + [this](HandlerContext & ctx, const auto & commandData) { HandleAllocatePushTransport(ctx, commandData); }); + + break; + + case Commands::DeallocatePushTransport::Id: + ChipLogDetail(Zcl, "PushAVStreamTransport: Deallocating Push Transport"); + + HandleCommand( + handlerContext, + [this](HandlerContext & ctx, const auto & commandData) { HandleDeallocatePushTransport(ctx, commandData); }); + + break; + + case Commands::ModifyPushTransport::Id: + ChipLogDetail(Zcl, "PushAVStreamTransport: Modifying Push Transport"); + + HandleCommand( + handlerContext, + [this](HandlerContext & ctx, const auto & commandData) { HandleModifyPushTransport(ctx, commandData); }); + + break; + + case Commands::SetTransportStatus::Id: + ChipLogDetail(Zcl, "PushAVStreamTransport: Setting Push Transport Status"); + + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleSetTransportStatus(ctx, commandData); }); + + break; + + case Commands::ManuallyTriggerTransport::Id: + ChipLogDetail(Zcl, "PushAVStreamTransport: Manually Triggered Push Transport"); + + HandleCommand( + handlerContext, + [this](HandlerContext & ctx, const auto & commandData) { HandleManuallyTriggerTransport(ctx, commandData); }); + + break; + + case Commands::FindTransport::Id: + ChipLogDetail(Zcl, "PushAVStreamTransport: Finding Push Transport"); + + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleFindTransport(ctx, commandData); }); + + break; + } +} + +bool PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) +{ + for (auto & id : mCurrentConnections) + { + if (id == connectionID) + return true; + } + return false; +} + +uint16_t PushAvStreamTransportServer::GenerateConnectionID() +{ + static uint16_t assignedConnectionID = 0; + uint16_t nextConnectionID; + + if (assignedConnectionID == MAX_PUSH_TRANSPORT_CONNECTION_ID) + nextConnectionID = 0; + else + nextConnectionID = assignedConnectionID + 1; + + while (FindStreamTransportConnection(nextConnectionID) != false) + { + if (nextConnectionID == MAX_PUSH_TRANSPORT_CONNECTION_ID) + nextConnectionID = 0; + else + nextConnectionID = nextConnectionID + 1; + } + return nextConnectionID; +} + +void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, + const Commands::AllocatePushTransport::DecodableType & commandData) +{ + Status status = Status::Success; + Commands::AllocatePushTransportResponse::Type response; + auto & transportOptions = commandData.transportOptions; + + uint16_t connectionID = GenerateConnectionID(); + TransportStatusEnum outTranportStatus = TransportStatusEnum::kUnknownEnumValue; + + // call the delegate + status = mDelegate.AllocatePushTransport(connectionID, transportOptions, outTranportStatus); + + if (status == Status::Success) + { + response.connectionID = connectionID; + response.transportOptions = transportOptions; + response.transportStatus = outTranportStatus; + + // add connection to CurrentConnections + AddStreamTransportConnection(connectionID); + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + } + else + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + } +} + +void PushAvStreamTransportServer::HandleDeallocatePushTransport( + HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & commandData) +{ + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + + // Call the delegate + status = mDelegate.DeallocatePushTransport(connectionID); + + if (status == Status::Success) + // Remove connection form CurrentConnections + RemoveStreamTransportConnection(connectionID); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); +} + +void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx, + const Commands::ModifyPushTransport::DecodableType & commandData) +{ + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + auto & outTransportOptions = commandData.transportOptions; + + // Call the delegate + status = mDelegate.ModifyPushTransport(connectionID, outTransportOptions); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); +} + +void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, + const Commands::SetTransportStatus::DecodableType & commandData) +{ + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + auto & transportOptions = commandData.transportOptions; + + // Call the delegate + status = mDelegate.SetTransportStatus(connectionID, transportOptions); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); +} + +void PushAvStreamTransportServer::HandleManuallyTriggerTransport( + HandlerContext & ctx, const Commands::ManuallyTriggerTransport::DecodableType & commandData) +{ + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + auto & activationReason = commandData.activationReason; + auto & timeControl = commandData.timeControl; + + // Call the delegate + status = mDelegate.ManuallyTriggerTransport(connectionID, activationReason, timeControl); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); +} + +void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, + const Commands::FindTransport::DecodableType & commandData) +{ + Status status = Status::Success; + Commands::FindTransportResponse::Type response; + + Optional> connectionID = commandData.connectionID; + + DataModel::List outStreamConfigurations; + + // Call the delegate + status = mDelegate.FindTransport(connectionID, outStreamConfigurations); + if (status == Status::Success) + { + response.streamConfigurations = outStreamConfigurations; + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + } + else + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + } +} + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip + +/** @brief Push AV Stream Transport Cluster Server Init + * + * Server Init + * + */ +void MatterPushAvStreamTransportPluginServerInitCallback() {} diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h new file mode 100644 index 00000000000000..fe4f60944bbf53 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -0,0 +1,290 @@ +/* + * + * Copyright (c) 2023 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 +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +using MetadataOptionsStruct = Structs::MetadataOptionsStruct::Type; +using CMAFContainerOptionsStruct = Structs::CMAFContainerOptionsStruct::Type; +using ContainerOptionsStruct = Structs::ContainerOptionsStruct::Type; +using TransportZoneOptionsStruct = Structs::TransportZoneOptionsStruct::Type; +using TransportTriggerOptionsStruct = Structs::TransportTriggerOptionsStruct::Type; +using TransportMotionTriggerTimeControlStruct = Structs::TransportMotionTriggerTimeControlStruct::Type; +using TransportOptionsStruct = Structs::TransportOptionsStruct::Type; +using TransportConfigurationStruct = Structs::TransportConfigurationStruct::Type; + +class PushAvStreamTransportServer; + +/** @brief + * Defines interfaces for implementing application-specific logic for various aspects of the PushAvStreamTransport Cluster. + * Specifically, it defines interfaces for the command handling and loading of the allocated streams. + */ +class PushAvStreamTransportDelegate +{ +public: + PushAvStreamTransportDelegate() = default; + + virtual ~PushAvStreamTransportDelegate() = default; + + /** + * @brief Handle Command Delegate for stream transport allocation with the provided transport configuration option. + * + * @param connectionID[in] Indicates the connectionID to allocate. + * + * @param transportOptions[in] represent the configuration options of the transport to be allocated + * + * @param outTransportStatus[out] represent the status of the transport allocation + * + * @return Success if the allocation is successful and a PushTransportConnectionID was + * produced; otherwise, the command SHALL be rejected with an appropriate + * error. + */ + virtual Protocols::InteractionModel::Status AllocatePushTransport(uint16_t & connectionID, + const TransportOptionsStruct & transportOptions, + TransportStatusEnum & outTransportStatus) = 0; + /** + * @brief Handle Command Delegate for Stream transport deallocation for the + * provided connectionID. + * + * @param connectionID[in] Indicates the connectionID to deallocate. + * + * @return Success if the transport deallocation is successful; otherwise, the command SHALL be rejected with an appropriate + * error. + * + */ + virtual Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t & connectionID) = 0; + /** + * @brief Handle Command Delegate for Stream transport modification. + * + * @param connectionID [in] Indicates the connectionID of the stream transport to modify. + * + * @param outTransportOptions [out] represents the Trigger Options to modify. + * + * @return Success if the stream transport modification is successful; otherwise, the command SHALL be rejected with an + * appropriate error. + */ + virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t & connectionID, + const TransportOptionsStruct & outTransportOptions) = 0; + + /** + * @brief Handle Command Delegate for Stream transport modification. + * + * @param connectionID [in] Indicates the connectionID of the stream transport to set status for. + * + * @param transportStatus [in] represent the new transport status to apply. + * + * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with an + * appropriate error. + */ + virtual Protocols::InteractionModel::Status SetTransportStatus(const uint16_t & connectionID, + TransportStatusEnum transportStatus) = 0; + + /** + * @brief Handle Command Delegate to request the Node to manually start the specified push transport. + * + * @param connectionID [in] Indicates the connectionID of the stream transport to set trigger for. + * + * @param activationReason [in] Provide information as to why the transport was started or stopped. + * + * @param timeControl [in] Configuration to control the life cycle of a triggered transport. + * + * @return Success if the stream transport trigger is successful; otherwise, the command SHALL be rejected with an +appropriate + * error. + */ + virtual Protocols::InteractionModel::Status + ManuallyTriggerTransport(const uint16_t & connectionID, TriggerActivationReasonEnum activationReason, + const TransportMotionTriggerTimeControlStruct & timeControl) = 0; + + /** + * @brief Handle Command Delegate to get the Stream Options Configuration for the specified push transport. + * + * @param connectionID [in] Indicates the allocated connectionID to get the Stream Options Configuration of. + * + * @param outtransportConfigurations [out] Single item list of mapped transport configuration or list if connectionID is + * NULL. + * + * @return Success if the transport is already allocated; otherwise, the command SHALL be rejected with an appropriate + * error. + * + */ + virtual Protocols::InteractionModel::Status + FindTransport(const Optional> & connectionID, + DataModel::List & outtransportConfigurations) = 0; + + /** + * @brief Delegate callback for notifying change in an attribute. + * + */ + virtual void OnAttributeChanged(AttributeId attributeId) = 0; + + /** + * Delegate functions to load the allocated transport connections. + * The delegate application is responsible for creating and persisting these connections ( based on the Allocation commands ). + * These Load APIs would be used to load the pre-allocated transport connections context information into the cluster server + * list, at initialization. Once loaded, the cluster server would be serving Reads on these attributes. The list is updatable + * via the Add/Remove functions for the respective transport connections. + */ + virtual Protocols::InteractionModel::Status LoadCurrentConnections(std::vector & currentConnections) = 0; + + /** + * @brief Callback into the delegate once persistent attributes managed by + * the Cluster have been loaded from Storage. + */ + virtual Protocols::InteractionModel::Status PersistentAttributesLoadedCallback() = 0; + +private: + friend class PushAvStreamTransportServer; + + PushAvStreamTransportServer * mPushAvStreamTransportServer = nullptr; + + /** + * This method is used by the SDK to set the PushAvStreamTransportServer pointer member in the delegate. + * This is done in the constructor during the instantiation of the PushAvStreamTransportServer object. + * + * @param aPushAvStreamTransportServer A pointer to the PushAvStreamTransportServer object related to this delegate object. + */ + void SetPushAvStreamTransportServer(PushAvStreamTransportServer * aPushAvStreamTransportServer) + { + mPushAvStreamTransportServer = aPushAvStreamTransportServer; + } + +protected: + PushAvStreamTransportServer * GetPushAvStreamTransportServer() const { return mPushAvStreamTransportServer; } +}; + +class PushAvStreamTransportServer : private AttributeAccessInterface, private CommandHandlerInterface +{ +public: + /** + * Creates a Push AV Stream Transport server instance. The Init() function needs to be called for this instance to be registered + * and called by the interaction model at the appropriate times. + * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. + * @param aDelegate A reference to the delegate to be used by this server. + * Note: the caller must ensure that the delegate lives throughout the instance's lifetime. + */ + PushAvStreamTransportServer(EndpointId endpointId, PushAvStreamTransportDelegate & delegate, const BitFlags aFeature, + PersistentStorageDelegate & aPersistentStorage); + + ~PushAvStreamTransportServer() override; + + /** + * @brief Initialise the Push AV Stream Transport server instance. + * This function must be called after defining an PushAvStreamTransportServer class object. + * @return Returns an error if the given endpoint and cluster ID have not been enabled in zap or if the + * CommandHandler or AttributeHandler registration fails, else returns CHIP_NO_ERROR. + * This method also checks if the feature setting is valid, if invalid it will return CHIP_ERROR_INVALID_ARGUMENT. + */ + CHIP_ERROR Init(); + + bool HasFeature(Feature feature) const; + + // Attribute Getters + BitMask GetSupportedContainerFormats() const { return mSupportedContainerFormats; } + BitMask GetSupportedIngestMethods() const { return mSupportedIngestMethods; } + + // Helper functions + bool FindStreamTransportConnection(const uint16_t connectionID); + uint16_t GenerateConnectionID(); + + // Add/Remove Management functions for transport + CHIP_ERROR AddStreamTransportConnection(const uint16_t transportConnectionId); + + CHIP_ERROR RemoveStreamTransportConnection(const uint16_t transportConnectionId); + +private: + PushAvStreamTransportDelegate & mDelegate; + EndpointId mEndpointId; + const BitFlags mFeature; + + // Attributes + BitMask mSupportedContainerFormats; + BitMask mSupportedIngestMethods; + // lists + std::vector mCurrentConnections; + + // Utility function to set and persist attributes + template + CHIP_ERROR SetAttributeIfDifferent(T & currentValue, const T & newValue, AttributeId attributeId, bool shouldPersist = true) + { + if (currentValue != newValue) + { + currentValue = newValue; + auto path = ConcreteAttributePath(mEndpointId, PushAvStreamTransport::Id, attributeId); + if (shouldPersist) + { + ReturnErrorOnFailure(GetSafeAttributePersistenceProvider()->WriteScalarValue(path, currentValue)); + } + mDelegate.OnAttributeChanged(attributeId); + MatterReportingAttributeChangeCallback(path); + } + return CHIP_NO_ERROR; + } + + /** + * IM-level implementation of read + * @return appropriately mapped CHIP_ERROR if applicable + */ + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + + /** + * IM-level implementation of write + * @return appropriately mapped CHIP_ERROR if applicable + */ + CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; + + /** + * Helper function that loads all the persistent attributes from the KVS. + */ + void LoadPersistentAttributes(); + + // Helpers to read list items via delegate APIs + CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder); + + /** + * @brief Inherited from CommandHandlerInterface + */ + void InvokeCommand(HandlerContext & ctx) override; + + void HandleAllocatePushTransport(HandlerContext & ctx, const Commands::AllocatePushTransport::DecodableType & req); + + void HandleDeallocatePushTransport(HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & req); + + void HandleModifyPushTransport(HandlerContext & ctx, const Commands::ModifyPushTransport::DecodableType & req); + + void HandleSetTransportStatus(HandlerContext & ctx, const Commands::SetTransportStatus::DecodableType & req); + + void HandleManuallyTriggerTransport(HandlerContext & ctx, const Commands::ManuallyTriggerTransport::DecodableType & req); + + void HandleFindTransport(HandlerContext & ctx, const Commands::FindTransport::DecodableType & req); +}; + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index 15f02f4a8802e6..f6e057f1040961 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -38,6 +38,7 @@ CommandHandlerInterfaceOnlyClusters: - Microwave Oven Control - Chime - Camera AV Stream Management + - Push AV Stream Transport - Commissioner Control - Commodity Price - Energy EVSE diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index 6b5d08a8531f64..bb7d635adac998 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -252,7 +252,9 @@ "NITROGEN_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER": [ "concentration-measurement-server" ], - "PUSH_AV_STREAM_TRANSPORT_CLUSTER": [], + "PUSH_AV_STREAM_TRANSPORT_CLUSTER": [ + "push-av-stream-transport-server" + ], "SAMPLE_MEI_CLUSTER": ["sample-mei-server"], "OCCUPANCY_SENSING_CLUSTER": ["occupancy-sensor-server"], "ON_OFF_CLUSTER": ["on-off-server"], diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 61bebcbc4720a2..6bd941ccbea9eb 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -7847,6 +7847,59 @@ bool emberAfCommodityTariffClusterGetTariffComponentCallback( bool emberAfCommodityTariffClusterGetDayEntryCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::CommodityTariff::Commands::GetDayEntry::DecodableType & commandData); + * @brief WebRTC Transport Provider Cluster SolicitOffer Command callback (from client) + */ +bool emberAfWebRTCTransportProviderClusterSolicitOfferCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportProvider::Commands::SolicitOffer::DecodableType & commandData); +/** + * @brief WebRTC Transport Provider Cluster ProvideOffer Command callback (from client) + */ +bool emberAfWebRTCTransportProviderClusterProvideOfferCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideOffer::DecodableType & commandData); +/** + * @brief WebRTC Transport Provider Cluster ProvideAnswer Command callback (from client) + */ +bool emberAfWebRTCTransportProviderClusterProvideAnswerCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideAnswer::DecodableType & commandData); +/** + * @brief WebRTC Transport Provider Cluster ProvideICECandidate Command callback (from client) + */ +bool emberAfWebRTCTransportProviderClusterProvideICECandidateCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideICECandidate::DecodableType & commandData); +/** + * @brief WebRTC Transport Provider Cluster EndSession Command callback (from client) + */ +bool emberAfWebRTCTransportProviderClusterEndSessionCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportProvider::Commands::EndSession::DecodableType & commandData); +/** + * @brief WebRTC Transport Requestor Cluster Offer Command callback (from client) + */ +bool emberAfWebRTCTransportRequestorClusterOfferCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportRequestor::Commands::Offer::DecodableType & commandData); +/** + * @brief WebRTC Transport Requestor Cluster Answer Command callback (from client) + */ +bool emberAfWebRTCTransportRequestorClusterAnswerCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportRequestor::Commands::Answer::DecodableType & commandData); +/** + * @brief WebRTC Transport Requestor Cluster ICECandidates Command callback (from client) + */ +bool emberAfWebRTCTransportRequestorClusterICECandidatesCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportRequestor::Commands::ICECandidates::DecodableType & commandData); +/** + * @brief WebRTC Transport Requestor Cluster End Command callback (from client) + */ +bool emberAfWebRTCTransportRequestorClusterEndCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::WebRTCTransportRequestor::Commands::End::DecodableType & commandData); /** * @brief TLS Certificate Management Cluster ProvisionRootCertificate Command callback (from client) */ From 7f09b47dae594567f896986217bb7bdf671d0312 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 27 Feb 2025 09:03:59 +0000 Subject: [PATCH 02/40] Restyled by prettier-json --- src/app/zap_cluster_list.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index bb7d635adac998..2b60dc74530379 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -252,9 +252,7 @@ "NITROGEN_DIOXIDE_CONCENTRATION_MEASUREMENT_CLUSTER": [ "concentration-measurement-server" ], - "PUSH_AV_STREAM_TRANSPORT_CLUSTER": [ - "push-av-stream-transport-server" - ], + "PUSH_AV_STREAM_TRANSPORT_CLUSTER": ["push-av-stream-transport-server"], "SAMPLE_MEI_CLUSTER": ["sample-mei-server"], "OCCUPANCY_SENSING_CLUSTER": ["occupancy-sensor-server"], "ON_OFF_CLUSTER": ["on-off-server"], From 558b68e1d72297a3e5c5f8bb788e8e4505006a34 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Tue, 4 Mar 2025 14:55:28 +0530 Subject: [PATCH 03/40] add push av cluster to camera-app and some fixes --- .../camera-common/camera-app.matter | 205 +++++++- .../camera-app/camera-common/camera-app.zap | 489 ++++++++++++++++++ .../push-av-stream-transport-server.cpp | 61 +-- .../push-av-stream-transport-server.h | 61 +-- 4 files changed, 744 insertions(+), 72 deletions(-) diff --git a/examples/camera-app/camera-common/camera-app.matter b/examples/camera-app/camera-common/camera-app.matter index 671b974070a11b..52f4f8effc57c1 100644 --- a/examples/camera-app/camera-common/camera-app.matter +++ b/examples/camera-app/camera-common/camera-app.matter @@ -2169,7 +2169,11 @@ provisional cluster CameraAvStreamManagement = 1361 { kJPEG = 0; } +<<<<<<< HEAD shared enum StreamUsageEnum : enum8 { +======= + enum StreamUsageEnum : enum8 { +>>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) kInternal = 0; kRecording = 1; kAnalysis = 2; @@ -2205,7 +2209,10 @@ provisional cluster CameraAvStreamManagement = 1361 { kWatermark = 0x40; kOnScreenDisplay = 0x80; kLocalStorage = 0x100; +<<<<<<< HEAD kHighDynamicRange = 0x200; +======= +>>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) } struct VideoResolutionStruct { @@ -2274,24 +2281,74 @@ provisional cluster CameraAvStreamManagement = 1361 { struct VideoSensorParamsStruct { int16u sensorWidth = 0; int16u sensorHeight = 1; +<<<<<<< HEAD int16u maxFPS = 2; optional int16u maxHDRFPS = 3; } shared struct ViewportStruct { +======= + boolean HDRCapable = 2; + int16u maxFPS = 3; + int16u maxHDRFPS = 4; + } + + struct ViewportStruct { +>>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) int16u x1 = 0; int16u y1 = 1; int16u x2 = 2; int16u y2 = 3; } +<<<<<<< HEAD +======= + info event VideoStreamChanged = 0 { + int16u videoStreamID = 0; + optional StreamUsageEnum streamUsage = 1; + optional VideoCodecEnum videoCodec = 2; + optional int16u minFrameRate = 3; + optional int16u maxFrameRate = 4; + optional VideoResolutionStruct minResolution = 5; + optional VideoResolutionStruct maxResolution = 6; + optional int32u minBitRate = 7; + optional int32u maxBitRate = 8; + optional int16u minFragmentLen = 9; + optional int16u maxFragmentLen = 10; + } + + info event AudioStreamChanged = 1 { + int16u audioStreamID = 0; + optional StreamUsageEnum streamUsage = 1; + optional AudioCodecEnum audioCodec = 2; + optional int8u channelCount = 3; + optional int32u sampleRate = 4; + optional int32u bitRate = 5; + optional int8u bitDepth = 6; + } + + info event SnapshotStreamChanged = 2 { + int16u snapshotStreamID = 0; + optional ImageCodecEnum imageCodec = 1; + optional int16u frameRate = 2; + optional int32u bitRate = 3; + optional VideoResolutionStruct minResolution = 4; + optional VideoResolutionStruct maxResolution = 5; + optional int8u quality = 6; + } + +>>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) readonly attribute optional int8u maxConcurrentVideoEncoders = 0; readonly attribute optional int32u maxEncodedPixelRate = 1; readonly attribute optional VideoSensorParamsStruct videoSensorParams = 2; readonly attribute optional boolean nightVisionCapable = 3; readonly attribute optional VideoResolutionStruct minViewport = 4; readonly attribute optional RateDistortionTradeOffPointsStruct rateDistortionTradeOffPoints[] = 5; +<<<<<<< HEAD readonly attribute int32u maxContentBufferSize = 6; +======= + readonly attribute optional int32u maxContentBufferSize = 6; +>>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) readonly attribute optional AudioCapabilitiesStruct microphoneCapabilities = 7; readonly attribute optional AudioCapabilitiesStruct speakerCapabilities = 8; readonly attribute optional TwoWayTalkSupportTypeEnum twoWayTalkSupport = 9; @@ -2299,7 +2356,11 @@ provisional cluster CameraAvStreamManagement = 1361 { readonly attribute int32u maxNetworkBandwidth = 11; readonly attribute optional int16u currentFrameRate = 12; attribute access(read: manage, write: manage) optional boolean HDRModeEnabled = 13; +<<<<<<< HEAD readonly attribute StreamUsageEnum supportedStreamUsages[] = 14; +======= + readonly attribute fabric_idx fabricsUsingCamera[] = 14; +>>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) readonly attribute optional VideoStreamStruct allocatedVideoStreams[] = 15; readonly attribute optional AudioStreamStruct allocatedAudioStreams[] = 16; readonly attribute optional SnapshotStreamStruct allocatedSnapshotStreams[] = 17; @@ -2669,6 +2730,121 @@ provisional cluster Chime = 1366 { command PlayChimeSound(): DefaultSuccess = 0; } +/** This Cluster is used to manage TLS Client Certificates and to provision + TLS endpoints with enough information to facilitate subsequent connection. */ +provisional cluster TlsCertificateManagement = 2049 { + revision 1; + + struct TLSCertStruct { + int16u caid = 0; + long_octet_string<3000> certificate = 1; + } + + struct TLSClientCertificateDetailStruct { + int16u ccdid = 0; + long_octet_string<3000> clientCertificate = 1; + octet_string intermediateCertificates[] = 2; + } + + readonly attribute int8u maxRootCertificates = 0; + readonly attribute int8u currentRootCertificates = 1; + readonly attribute int8u maxClientCertificates = 2; + readonly attribute int8u currentClientCertificates = 3; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct ProvisionRootCertificateRequest { + long_octet_string<3000> certificate = 0; + nullable int16u caid = 1; + } + + response struct ProvisionRootCertificateResponse = 1 { + int16u caid = 0; + } + + request struct FindRootCertificateRequest { + nullable int16u caid = 0; + } + + response struct FindRootCertificateResponse = 3 { + TLSCertStruct certificateDetails[] = 0; + } + + request struct LookupRootCertificateRequest { + octet_string<64> fingerprint = 0; + } + + response struct LookupRootCertificateResponse = 5 { + int16u caid = 0; + } + + request struct RemoveRootCertificateRequest { + int16u caid = 0; + } + + request struct TLSClientCSRRequest { + octet_string nonce = 0; + } + + response struct TLSClientCSRResponse = 8 { + int16u ccdid = 0; + octet_string csr = 1; + octet_string nonce = 2; + } + + request struct ProvisionClientCertificateRequest { + int16u ccdid = 0; + TLSClientCertificateDetailStruct clientCertificateDetails = 1; + } + + response struct ProvisionClientCertificateResponse = 10 { + int16u ccdid = 0; + } + + request struct FindClientCertificateRequest { + int16u ccdid = 0; + } + + response struct FindClientCertificateResponse = 12 { + TLSClientCertificateDetailStruct certificateDetails[] = 0; + } + + request struct LookupClientCertificateRequest { + octet_string<64> fingerprint = 0; + } + + response struct LookupClientCertificateResponse = 14 { + int16u ccdid = 0; + } + + request struct RemoveClientCertificateRequest { + int16u ccdid = 0; + } + + /** This command SHALL provision the provided certificate for the passed in CAID. */ + command access(invoke: administer) ProvisionRootCertificate(ProvisionRootCertificateRequest): ProvisionRootCertificateResponse = 0; + /** This command SHALL return the TLSCertStruct for the passed in CAID. */ + command FindRootCertificate(FindRootCertificateRequest): FindRootCertificateResponse = 2; + /** This command SHALL return the CAID for the passed in fingerprint. */ + command LookupRootCertificate(LookupRootCertificateRequest): LookupRootCertificateResponse = 4; + /** This command SHALL be generated to request the server removes the certificate provisioned to the provided Certificate Authority ID. */ + command access(invoke: administer) RemoveRootCertificate(RemoveRootCertificateRequest): DefaultSuccess = 6; + /** This command SHALL be generated to request the Node generates a Certificate Signing Request. */ + command access(invoke: administer) TLSClientCSR(TLSClientCSRRequest): TLSClientCSRResponse = 7; + /** This command SHALL be generated to request the Node provisions the provided Client Certificate Details. */ + command access(invoke: administer) ProvisionClientCertificate(ProvisionClientCertificateRequest): ProvisionClientCertificateResponse = 9; + /** This command SHALL return the TLSClientCertificateDetailStruct for the passed in CCDID. */ + command FindClientCertificate(FindClientCertificateRequest): FindClientCertificateResponse = 11; + /** This command SHALL return the CCDID for the passed in Fingerprint. */ + command LookupClientCertificate(LookupClientCertificateRequest): LookupClientCertificateResponse = 13; + /** This command SHALL be generated to request the Node removes the certificate provisioned to the provided Client Certificate Details ID. */ + command access(invoke: administer) RemoveClientCertificate(RemoveClientCertificateRequest): DefaultSuccess = 15; +} + endpoint 0 { device type ma_rootdevice = 22, version 3; device type ma_otarequestor = 18, version 1; @@ -3186,6 +3362,33 @@ endpoint 1 { handle command PlayChimeSound; } -} + server cluster TlsCertificateManagement { + ram attribute maxRootCertificates; + ram attribute currentRootCertificates; + ram attribute maxClientCertificates; + ram attribute currentClientCertificates; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + handle command ProvisionRootCertificate; + handle command ProvisionRootCertificateResponse; + handle command FindRootCertificate; + handle command FindRootCertificateResponse; + handle command LookupRootCertificate; + handle command LookupRootCertificateResponse; + handle command RemoveRootCertificate; + handle command TLSClientCSR; + handle command TLSClientCSRResponse; + handle command ProvisionClientCertificate; + handle command ProvisionClientCertificateResponse; + handle command FindClientCertificate; + handle command FindClientCertificateResponse; + handle command LookupClientCertificate; + handle command LookupClientCertificateResponse; + handle command RemoveClientCertificate; + } +} diff --git a/examples/camera-app/camera-common/camera-app.zap b/examples/camera-app/camera-common/camera-app.zap index 4cdaaebdcb1fe2..9fcb6616414ab5 100644 --- a/examples/camera-app/camera-common/camera-app.zap +++ b/examples/camera-app/camera-common/camera-app.zap @@ -6090,6 +6090,210 @@ } ] }, + { + "name": "Push AV Stream Transport", + "code": 1365, + "mfgCode": null, + "define": "PUSH_AV_STREAM_TRANSPORT_CLUSTER", + "side": "server", + "enabled": 1, + "commands": [ + { + "name": "AllocatePushTransport", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "AllocatePushTransportResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "DeallocatePushTransport", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ModifyPushTransport", + "code": 3, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "SetTransportStatus", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ManuallyTriggerTransport", + "code": 5, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "FindTransport", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "FindTransportResponse", + "code": 7, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "SupportedContainerFormats", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "SupportedContainerFormatsBitmap", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SupportedIngestMethods", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "SupportedIngestMethodsBitmap", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentConnections", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Chime", "code": 1366, @@ -6238,6 +6442,291 @@ "reportableChange": 0 } ] + }, + { + "name": "TLS Certificate Management", + "code": 2049, + "mfgCode": null, + "define": "TLS_CERTIFICATE_MANAGEMENT_CLUSTER", + "side": "server", + "enabled": 1, + "apiMaturity": "provisional", + "commands": [ + { + "name": "ProvisionRootCertificate", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ProvisionRootCertificateResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "FindRootCertificate", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "FindRootCertificateResponse", + "code": 3, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "LookupRootCertificate", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "LookupRootCertificateResponse", + "code": 5, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "RemoveRootCertificate", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TLSClientCSR", + "code": 7, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "TLSClientCSRResponse", + "code": 8, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ProvisionClientCertificate", + "code": 9, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ProvisionClientCertificateResponse", + "code": 10, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "FindClientCertificate", + "code": 11, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "FindClientCertificateResponse", + "code": 12, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "LookupClientCertificate", + "code": 13, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "LookupClientCertificateResponse", + "code": 14, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "RemoveClientCertificate", + "code": 15, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "MaxRootCertificates", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentRootCertificates", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "MaxClientCertificates", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentClientCertificates", + "code": 3, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] } ] } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index f8d58ec89f5111..7fcd452a6b48ba 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -44,24 +44,16 @@ namespace Clusters { namespace PushAvStreamTransport { PushAvStreamTransportServer::PushAvStreamTransportServer(EndpointId aEndpointId, PushAvStreamTransportDelegate & aDelegate, - const BitFlags aFeature, + const BitFlags aFeatures, PersistentStorageDelegate & aPersistentStorage) : CommandHandlerInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), - AttributeAccessInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), mDelegate(aDelegate), - mEndpointId(aEndpointId), mFeature(aFeature) -{ - mDelegate.SetPushAvStreamTransportServer(this); -} + AttributeAccessInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), mDelegate(aDelegate), mFeature(aFeatures) +{} PushAvStreamTransportServer::~PushAvStreamTransportServer() { - // Explicitly set the PushAvStreamTransportServer pointer in the Delegate to null. + Shutdown(); - mDelegate.SetPushAvStreamTransportServer(nullptr); - - // Unregister command handler and attribute access interfaces - CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(this); - AttributeAccessInterfaceRegistry::Instance().Unregister(this); } CHIP_ERROR PushAvStreamTransportServer::Init() @@ -73,6 +65,12 @@ CHIP_ERROR PushAvStreamTransportServer::Init() return CHIP_NO_ERROR; } +void PushAvStreamTransportServer::Shutdown() +{ // Unregister command handler and attribute access interfaces + CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(this); + AttributeAccessInterfaceRegistry::Instance().Unregister(this); +} + bool PushAvStreamTransportServer::HasFeature(Feature feature) const { return mFeature.Has(feature); @@ -91,7 +89,7 @@ CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const At CHIP_ERROR PushAvStreamTransportServer::AddStreamTransportConnection(const uint16_t transportConnectionId) { mCurrentConnections.push_back(transportConnectionId); - auto path = ConcreteAttributePath(mEndpointId, PushAvStreamTransport::Id, Attributes::CurrentConnections::Id); + auto path = ConcreteAttributePath(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, Attributes::CurrentConnections::Id); mDelegate.OnAttributeChanged(Attributes::CurrentConnections::Id); MatterReportingAttributeChangeCallback(path); @@ -103,7 +101,7 @@ CHIP_ERROR PushAvStreamTransportServer::RemoveStreamTransportConnection(const ui mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), [&](const uint16_t connectionID) { return connectionID == transportConnectionId; }), mCurrentConnections.end()); - auto path = ConcreteAttributePath(mEndpointId, PushAvStreamTransport::Id, Attributes::CurrentConnections::Id); + auto path = ConcreteAttributePath(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, Attributes::CurrentConnections::Id); mDelegate.OnAttributeChanged(Attributes::CurrentConnections::Id); MatterReportingAttributeChangeCallback(path); @@ -113,7 +111,7 @@ CHIP_ERROR PushAvStreamTransportServer::RemoveStreamTransportConnection(const ui CHIP_ERROR PushAvStreamTransportServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { VerifyOrDie(aPath.mClusterId == PushAvStreamTransport::Id); - ChipLogError(Zcl, "Push AV Stream Transport: Reading"); + ChipLogError(Zcl, "Push AV Stream Transport[ep=%d]: Reading",AttributeAccessInterface::GetEndpointId().Value()); switch (aPath.mAttributeId) { @@ -228,21 +226,18 @@ bool PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t c uint16_t PushAvStreamTransportServer::GenerateConnectionID() { - static uint16_t assignedConnectionID = 0; + static uint16_t lastAssignedConnectionID = 0; uint16_t nextConnectionID; - if (assignedConnectionID == MAX_PUSH_TRANSPORT_CONNECTION_ID) - nextConnectionID = 0; - else - nextConnectionID = assignedConnectionID + 1; - - while (FindStreamTransportConnection(nextConnectionID) != false) + do { - if (nextConnectionID == MAX_PUSH_TRANSPORT_CONNECTION_ID) + if (lastAssignedConnectionID == MAX_PUSH_TRANSPORT_CONNECTION_ID) nextConnectionID = 0; else - nextConnectionID = nextConnectionID + 1; - } + nextConnectionID = lastAssignedConnectionID + 1; + } while (FindStreamTransportConnection(nextConnectionID)); + + lastAssignedConnectionID = nextConnectionID; return nextConnectionID; } @@ -253,17 +248,17 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c Commands::AllocatePushTransportResponse::Type response; auto & transportOptions = commandData.transportOptions; - uint16_t connectionID = GenerateConnectionID(); - TransportStatusEnum outTranportStatus = TransportStatusEnum::kUnknownEnumValue; + uint16_t connectionID = GenerateConnectionID(); + TransportStatusEnum outTransportStatus = TransportStatusEnum::kUnknownEnumValue; // call the delegate - status = mDelegate.AllocatePushTransport(connectionID, transportOptions, outTranportStatus); + status = mDelegate.AllocatePushTransport(connectionID, transportOptions, outTransportStatus); if (status == Status::Success) { response.connectionID = connectionID; response.transportOptions = transportOptions; - response.transportStatus = outTranportStatus; + response.transportStatus = outTransportStatus; // add connection to CurrentConnections AddStreamTransportConnection(connectionID); @@ -308,12 +303,12 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, const Commands::SetTransportStatus::DecodableType & commandData) { - Status status = Status::Success; - uint16_t connectionID = commandData.connectionID; - auto & transportOptions = commandData.transportOptions; + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + auto & transportStatus = commandData.transportStatus; // Call the delegate - status = mDelegate.SetTransportStatus(connectionID, transportOptions); + status = mDelegate.SetTransportStatus(connectionID, transportStatus); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index fe4f60944bbf53..4cacef957eed2f 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2025 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,10 +38,8 @@ using TransportMotionTriggerTimeControlStruct = Structs::TransportMotionTriggerT using TransportOptionsStruct = Structs::TransportOptionsStruct::Type; using TransportConfigurationStruct = Structs::TransportConfigurationStruct::Type; -class PushAvStreamTransportServer; - /** @brief - * Defines interfaces for implementing application-specific logic for various aspects of the PushAvStreamTransport Cluster. + * Defines interfaces for implementing application-specific logic for various aspects of the PushAvStreamTransport Delegate. * Specifically, it defines interfaces for the command handling and loading of the allocated streams. */ class PushAvStreamTransportDelegate @@ -64,7 +62,7 @@ class PushAvStreamTransportDelegate * produced; otherwise, the command SHALL be rejected with an appropriate * error. */ - virtual Protocols::InteractionModel::Status AllocatePushTransport(uint16_t & connectionID, + virtual Protocols::InteractionModel::Status AllocatePushTransport(uint16_t connectionID, const TransportOptionsStruct & transportOptions, TransportStatusEnum & outTransportStatus) = 0; /** @@ -77,7 +75,7 @@ class PushAvStreamTransportDelegate * error. * */ - virtual Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t & connectionID) = 0; + virtual Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID) = 0; /** * @brief Handle Command Delegate for Stream transport modification. * @@ -88,7 +86,7 @@ class PushAvStreamTransportDelegate * @return Success if the stream transport modification is successful; otherwise, the command SHALL be rejected with an * appropriate error. */ - virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t & connectionID, + virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, const TransportOptionsStruct & outTransportOptions) = 0; /** @@ -101,7 +99,7 @@ class PushAvStreamTransportDelegate * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with an * appropriate error. */ - virtual Protocols::InteractionModel::Status SetTransportStatus(const uint16_t & connectionID, + virtual Protocols::InteractionModel::Status SetTransportStatus(const uint16_t connectionID, TransportStatusEnum transportStatus) = 0; /** @@ -118,7 +116,7 @@ appropriate * error. */ virtual Protocols::InteractionModel::Status - ManuallyTriggerTransport(const uint16_t & connectionID, TriggerActivationReasonEnum activationReason, + ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, const TransportMotionTriggerTimeControlStruct & timeControl) = 0; /** @@ -158,24 +156,6 @@ appropriate */ virtual Protocols::InteractionModel::Status PersistentAttributesLoadedCallback() = 0; -private: - friend class PushAvStreamTransportServer; - - PushAvStreamTransportServer * mPushAvStreamTransportServer = nullptr; - - /** - * This method is used by the SDK to set the PushAvStreamTransportServer pointer member in the delegate. - * This is done in the constructor during the instantiation of the PushAvStreamTransportServer object. - * - * @param aPushAvStreamTransportServer A pointer to the PushAvStreamTransportServer object related to this delegate object. - */ - void SetPushAvStreamTransportServer(PushAvStreamTransportServer * aPushAvStreamTransportServer) - { - mPushAvStreamTransportServer = aPushAvStreamTransportServer; - } - -protected: - PushAvStreamTransportServer * GetPushAvStreamTransportServer() const { return mPushAvStreamTransportServer; } }; class PushAvStreamTransportServer : private AttributeAccessInterface, private CommandHandlerInterface @@ -188,7 +168,7 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co * @param aDelegate A reference to the delegate to be used by this server. * Note: the caller must ensure that the delegate lives throughout the instance's lifetime. */ - PushAvStreamTransportServer(EndpointId endpointId, PushAvStreamTransportDelegate & delegate, const BitFlags aFeature, + PushAvStreamTransportServer(EndpointId endpointId, PushAvStreamTransportDelegate & delegate, const BitFlags aFeatures, PersistentStorageDelegate & aPersistentStorage); ~PushAvStreamTransportServer() override; @@ -202,24 +182,20 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co */ CHIP_ERROR Init(); + /** + * @brief + * Unregisters the command handler and attribute interface, releasing resources. + */ + void Shutdown(); + bool HasFeature(Feature feature) const; // Attribute Getters BitMask GetSupportedContainerFormats() const { return mSupportedContainerFormats; } BitMask GetSupportedIngestMethods() const { return mSupportedIngestMethods; } - // Helper functions - bool FindStreamTransportConnection(const uint16_t connectionID); - uint16_t GenerateConnectionID(); - - // Add/Remove Management functions for transport - CHIP_ERROR AddStreamTransportConnection(const uint16_t transportConnectionId); - - CHIP_ERROR RemoveStreamTransportConnection(const uint16_t transportConnectionId); - private: PushAvStreamTransportDelegate & mDelegate; - EndpointId mEndpointId; const BitFlags mFeature; // Attributes @@ -266,6 +242,15 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co // Helpers to read list items via delegate APIs CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder); + // Helper functions + bool FindStreamTransportConnection(const uint16_t connectionID); + uint16_t GenerateConnectionID(); + + // Add/Remove Management functions for transport + CHIP_ERROR AddStreamTransportConnection(const uint16_t transportConnectionId); + + CHIP_ERROR RemoveStreamTransportConnection(const uint16_t transportConnectionId); + /** * @brief Inherited from CommandHandlerInterface */ From c2ff51c602fc5549fb87a4efed90935bc8831ade Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Thu, 3 Apr 2025 20:23:30 +0530 Subject: [PATCH 04/40] Address review comments --- .../push-av-stream-transport-server.cpp | 148 +++++++++++++----- .../push-av-stream-transport-server.h | 54 +++---- 2 files changed, 132 insertions(+), 70 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 7fcd452a6b48ba..6baf34d283a256 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -37,23 +37,21 @@ using namespace chip::app::Clusters::PushAvStreamTransport; using namespace chip::app::Clusters::PushAvStreamTransport::Structs; using namespace chip::app::Clusters::PushAvStreamTransport::Attributes; using namespace Protocols::InteractionModel; +using chip::Protocols::InteractionModel::Status; namespace chip { namespace app { namespace Clusters { namespace PushAvStreamTransport { -PushAvStreamTransportServer::PushAvStreamTransportServer(EndpointId aEndpointId, PushAvStreamTransportDelegate & aDelegate, - const BitFlags aFeatures, - PersistentStorageDelegate & aPersistentStorage) : +PushAvStreamTransportServer::PushAvStreamTransportServer(EndpointId aEndpointId, PushAvStreamTransportDelegate & aDelegate) : CommandHandlerInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), - AttributeAccessInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), mDelegate(aDelegate), mFeature(aFeatures) + AttributeAccessInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), mDelegate(aDelegate) {} PushAvStreamTransportServer::~PushAvStreamTransportServer() { Shutdown(); - } CHIP_ERROR PushAvStreamTransportServer::Init() @@ -71,11 +69,6 @@ void PushAvStreamTransportServer::Shutdown() AttributeAccessInterfaceRegistry::Instance().Unregister(this); } -bool PushAvStreamTransportServer::HasFeature(Feature feature) const -{ - return mFeature.Has(feature); -} - CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder) { for (const auto & currentConnections : mCurrentConnections) @@ -89,7 +82,8 @@ CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const At CHIP_ERROR PushAvStreamTransportServer::AddStreamTransportConnection(const uint16_t transportConnectionId) { mCurrentConnections.push_back(transportConnectionId); - auto path = ConcreteAttributePath(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, Attributes::CurrentConnections::Id); + auto path = ConcreteAttributePath(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, + Attributes::CurrentConnections::Id); mDelegate.OnAttributeChanged(Attributes::CurrentConnections::Id); MatterReportingAttributeChangeCallback(path); @@ -101,7 +95,8 @@ CHIP_ERROR PushAvStreamTransportServer::RemoveStreamTransportConnection(const ui mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), [&](const uint16_t connectionID) { return connectionID == transportConnectionId; }), mCurrentConnections.end()); - auto path = ConcreteAttributePath(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, Attributes::CurrentConnections::Id); + auto path = ConcreteAttributePath(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, + Attributes::CurrentConnections::Id); mDelegate.OnAttributeChanged(Attributes::CurrentConnections::Id); MatterReportingAttributeChangeCallback(path); @@ -111,44 +106,28 @@ CHIP_ERROR PushAvStreamTransportServer::RemoveStreamTransportConnection(const ui CHIP_ERROR PushAvStreamTransportServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { VerifyOrDie(aPath.mClusterId == PushAvStreamTransport::Id); - ChipLogError(Zcl, "Push AV Stream Transport[ep=%d]: Reading",AttributeAccessInterface::GetEndpointId().Value()); + ChipLogError(Zcl, "Push AV Stream Transport[ep=%d]: Reading", AttributeAccessInterface::GetEndpointId().Value()); - switch (aPath.mAttributeId) + if (aPath.mClusterId == PushAvStreamTransport::Id && aPath.mAttributeId == Attributes::CurrentConnections::Id) { - case FeatureMap::Id: - ReturnErrorOnFailure(aEncoder.Encode(mFeature)); - break; - - case SupportedContainerFormats::Id: - ReturnErrorOnFailure(aEncoder.Encode(mSupportedContainerFormats)); - break; - case SupportedIngestMethods::Id: - ReturnErrorOnFailure(aEncoder.Encode(mSupportedIngestMethods)); - break; - - case CurrentConnections::Id: ReturnErrorOnFailure(aEncoder.EncodeList( [this](const auto & encoder) -> CHIP_ERROR { return this->ReadAndEncodeCurrentConnections(encoder); })); - break; } return CHIP_NO_ERROR; } -CHIP_ERROR PushAvStreamTransportServer::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) -{ - VerifyOrDie(aPath.mClusterId == PushAvStreamTransport::Id); - - return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); -} - void PushAvStreamTransportServer::LoadPersistentAttributes() { CHIP_ERROR err = CHIP_NO_ERROR; // Load currentConnections - mDelegate.LoadCurrentConnections(mCurrentConnections); + err = mDelegate.LoadCurrentConnections(mCurrentConnections); + if (err != CHIP_NO_ERROR) + { + ChipLogDetail(Zcl, "PushAVStreamTransport: Unable to load allocated connections from the KVS."); + } // Signal delegate that all persistent configuration attributes have been loaded. mDelegate.PersistentAttributesLoadedCallback(); @@ -211,14 +190,18 @@ void PushAvStreamTransportServer::InvokeCommand(HandlerContext & handlerContext) handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleFindTransport(ctx, commandData); }); break; + default: + // Mark unrecognized command as UnsupportedCommand + handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, Status::UnsupportedCommand); + break; } } bool PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) { - for (auto & id : mCurrentConnections) + for (auto & connection : mCurrentConnections) { - if (id == connectionID) + if (connection.connectionID == connectionID) return true; } return false; @@ -244,15 +227,67 @@ uint16_t PushAvStreamTransportServer::GenerateConnectionID() void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, const Commands::AllocatePushTransport::DecodableType & commandData) { - Status status = Status::Success; Commands::AllocatePushTransportResponse::Type response; auto & transportOptions = commandData.transportOptions; + FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); + uint16_t ep = emberAfGetClusterServerEndpointIndex(transportOptions.endpointID, TlsCertificateManagement::Id, + MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_CLIENT_ENDPOINT_COUNT); + + if (ep == kEmberInvalidEndpointIndex) + { + auto status = StatusCodeEnum::kInvalidTLSEndpoint; + ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid TLSEndpointId not found"); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + return; + } + + if (transportOptions.ingestMethod == IngestMethodsEnum::kUnknownEnumValue) + { + auto status = StatusCodeEnum::kUnsupportedIngestMethod; + ChipLogError(Zcl, "HandleAllocatePushTransport: Ingest method not supported"); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + return; + } + + if (transportOptions.containerFormat == ContainerFormatEnum::kUnknownEnumValue) + { + auto status = StatusCodeEnum::kUnsupportedContainerFormat; + ChipLogError(Zcl, "HandleAllocatePushTransport: Container format not supported"); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + return; + } + + if (transportOptions.triggerOptions.triggerType == TransportTriggerTypeEnum::kUnknownEnumValue) + { + auto status = StatusCodeEnum::kInvalidTriggerType; + ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Trigger type"); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + return; + } + + // Todo: Validate MotionZones list in the TransportTriggerOptionsStruct field + + // Validate the StreamUsageEnum as per resource management and stream priorities. + CHIP_ERROR err = + mDelegate.ValidateStreamUsage(transportOptions.streamUsage, transportOptions.videoStreamID, transportOptions.audioStreamID); + if (err != CHIP_NO_ERROR) + { + auto status = StatusCodeEnum::kInvalidStream; + ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Stream"); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + return; + } + uint16_t connectionID = GenerateConnectionID(); TransportStatusEnum outTransportStatus = TransportStatusEnum::kUnknownEnumValue; + TransportConfigurationStruct outTransportConfiguration; + outTransportConfiguration.connectionID = connectionID; + outTransportConfiguration.transportStatus = TransportStatusEnum::kInactive; + outTransportConfiguration.transportOptions = transportOptions; // call the delegate - status = mDelegate.AllocatePushTransport(connectionID, transportOptions, outTransportStatus); + Status status = mDelegate.AllocatePushTransport(transportOptions, outTransportConfiguration); if (status == Status::Success) { @@ -277,6 +312,13 @@ void PushAvStreamTransportServer::HandleDeallocatePushTransport( Status status = Status::Success; uint16_t connectionID = commandData.connectionID; + if (!FindStreamTransportConnection(connectionID)) + { + ChipLogError(Zcl, "HandleDeallocatePushTransport: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + // Call the delegate status = mDelegate.DeallocatePushTransport(connectionID); @@ -294,6 +336,13 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx uint16_t connectionID = commandData.connectionID; auto & outTransportOptions = commandData.transportOptions; + if (!FindStreamTransportConnection(connectionID)) + { + ChipLogError(Zcl, "HandleModifyPushTransport: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + // Call the delegate status = mDelegate.ModifyPushTransport(connectionID, outTransportOptions); @@ -307,6 +356,13 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, uint16_t connectionID = commandData.connectionID; auto & transportStatus = commandData.transportStatus; + if (!FindStreamTransportConnection(connectionID)) + { + ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + // Call the delegate status = mDelegate.SetTransportStatus(connectionID, transportStatus); @@ -321,6 +377,13 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( auto & activationReason = commandData.activationReason; auto & timeControl = commandData.timeControl; + if (!FindStreamTransportConnection(connectionID)) + { + ChipLogError(Zcl, "HandleManuallyTriggerTransport: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + // Call the delegate status = mDelegate.ManuallyTriggerTransport(connectionID, activationReason, timeControl); @@ -339,6 +402,13 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, // Call the delegate status = mDelegate.FindTransport(connectionID, outStreamConfigurations); + + if (connectionID.HasValue() && outStreamConfigurations.size() == 0) + { + ChipLogError(Zcl, "HandleFindTransport: No allocated transport for connectionID."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } if (status == Status::Success) { response.streamConfigurations = outStreamConfigurations; diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 4cacef957eed2f..09e5c1b57fadd2 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -135,6 +135,24 @@ appropriate FindTransport(const Optional> & connectionID, DataModel::List & outtransportConfigurations) = 0; + /** + * @brief Validates the requested stream usage against the camera's resource management + * and stream priority policies. + * + * The implementation SHALL ensure: + * - The requested stream usage (streamUsage) is allowed given the current allocation of + * camera resources (e.g. CPU, memory, network bandwidth) and the prioritized stream list. + * + * @param[in] streamUsage The desired usage type for the stream (e.g. live view, recording, etc.). + * @param[in] videoStreamId Optional identifier for the requested video stream. + * @param[in] audioStreamId Optional identifier for the requested audio stream. + * + * @return CHIP_ERROR CHIP_NO_ERROR if the stream usage is valid; an appropriate error code otherwise. + */ + virtual CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, + const Optional> & videoStreamId, + const Optional> & audioStreamId) = 0; + /** * @brief Delegate callback for notifying change in an attribute. * @@ -148,14 +166,13 @@ appropriate * list, at initialization. Once loaded, the cluster server would be serving Reads on these attributes. The list is updatable * via the Add/Remove functions for the respective transport connections. */ - virtual Protocols::InteractionModel::Status LoadCurrentConnections(std::vector & currentConnections) = 0; + virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; /** * @brief Callback into the delegate once persistent attributes managed by * the Cluster have been loaded from Storage. */ - virtual Protocols::InteractionModel::Status PersistentAttributesLoadedCallback() = 0; - + virtual CHIP_ERROR PersistentAttributesLoadedCallback() = 0; }; class PushAvStreamTransportServer : private AttributeAccessInterface, private CommandHandlerInterface @@ -168,8 +185,7 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co * @param aDelegate A reference to the delegate to be used by this server. * Note: the caller must ensure that the delegate lives throughout the instance's lifetime. */ - PushAvStreamTransportServer(EndpointId endpointId, PushAvStreamTransportDelegate & delegate, const BitFlags aFeatures, - PersistentStorageDelegate & aPersistentStorage); + PushAvStreamTransportServer(EndpointId endpointId, PushAvStreamTransportDelegate & delegate); ~PushAvStreamTransportServer() override; @@ -196,31 +212,12 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co private: PushAvStreamTransportDelegate & mDelegate; - const BitFlags mFeature; // Attributes BitMask mSupportedContainerFormats; BitMask mSupportedIngestMethods; // lists - std::vector mCurrentConnections; - - // Utility function to set and persist attributes - template - CHIP_ERROR SetAttributeIfDifferent(T & currentValue, const T & newValue, AttributeId attributeId, bool shouldPersist = true) - { - if (currentValue != newValue) - { - currentValue = newValue; - auto path = ConcreteAttributePath(mEndpointId, PushAvStreamTransport::Id, attributeId); - if (shouldPersist) - { - ReturnErrorOnFailure(GetSafeAttributePersistenceProvider()->WriteScalarValue(path, currentValue)); - } - mDelegate.OnAttributeChanged(attributeId); - MatterReportingAttributeChangeCallback(path); - } - return CHIP_NO_ERROR; - } + std::vector mCurrentConnections; /** * IM-level implementation of read @@ -228,12 +225,6 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co */ CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; - /** - * IM-level implementation of write - * @return appropriately mapped CHIP_ERROR if applicable - */ - CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; - /** * Helper function that loads all the persistent attributes from the KVS. */ @@ -243,6 +234,7 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder); // Helper functions + bool FindStreamTransportConnection(const uint16_t connectionID); uint16_t GenerateConnectionID(); From f25fe397f104e48c320a972e444c53c3a9220814 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 11 Apr 2025 03:29:53 +0530 Subject: [PATCH 05/40] Check cluster specific code --- .../push-av-stream-transport-server.cpp | 175 +++++++++++++----- .../push-av-stream-transport-server.h | 43 +++-- 2 files changed, 145 insertions(+), 73 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 6baf34d283a256..088a3f15ae9f68 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -79,28 +79,49 @@ CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const At return CHIP_NO_ERROR; } -CHIP_ERROR PushAvStreamTransportServer::AddStreamTransportConnection(const uint16_t transportConnectionId) +PushAvStreamTransportServer::UpsertResultEnum +PushAvStreamTransportServer::UpsertStreamTransportConnection(const TransportConfigurationStruct & transportConfiguration) { - mCurrentConnections.push_back(transportConnectionId); - auto path = ConcreteAttributePath(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, - Attributes::CurrentConnections::Id); - mDelegate.OnAttributeChanged(Attributes::CurrentConnections::Id); - MatterReportingAttributeChangeCallback(path); + UpsertResultEnum result; + auto it = + std::find_if(mCurrentConnections.begin(), mCurrentConnections.end(), + [id = transportConfiguration.connectionID](const auto & existing) { return existing.connectionID == id; }); - return CHIP_NO_ERROR; + if (it != mCurrentConnections.end()) + { + *it = transportConfiguration; + result = UpsertResultEnum::kUpdated; + } + else + { + mCurrentConnections.push_back(transportConfiguration); + result = UpsertResultEnum::kInserted; + } + + MatterReportingAttributeChangeCallback(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, + PushAvStreamTransport::Attributes::CurrentConnections::Id); + + return result; } -CHIP_ERROR PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t transportConnectionId) +void PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t transportConnectionId) { + size_t originalSize = mCurrentConnections.size(); + + // Erase-Remove idiom mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), - [&](const uint16_t connectionID) { return connectionID == transportConnectionId; }), + [transportConnectionId](const TransportConfigurationStruct & s) { + return s.connectionID == transportConnectionId; + }), mCurrentConnections.end()); - auto path = ConcreteAttributePath(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, - Attributes::CurrentConnections::Id); - mDelegate.OnAttributeChanged(Attributes::CurrentConnections::Id); - MatterReportingAttributeChangeCallback(path); - return CHIP_NO_ERROR; + // If a connection was removed, the size will be smaller. + if (mCurrentConnections.size() < originalSize) + { + // Notify the stack that the CurrentConnections attribute has changed. + MatterReportingAttributeChangeCallback(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, + PushAvStreamTransport::Attributes::CurrentConnections::Id); + } } CHIP_ERROR PushAvStreamTransportServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) @@ -197,14 +218,14 @@ void PushAvStreamTransportServer::InvokeCommand(HandlerContext & handlerContext) } } -bool PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) +TransportConfigurationStruct * PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) { - for (auto & connection : mCurrentConnections) + for (auto & transportConnection : mCurrentConnections) { - if (connection.connectionID == connectionID) - return true; + if (transportConnection.connectionID == connectionID) + return &transportConnection; } - return false; + return nullptr; } uint16_t PushAvStreamTransportServer::GenerateConnectionID() @@ -236,7 +257,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (ep == kEmberInvalidEndpointIndex) { - auto status = StatusCodeEnum::kInvalidTLSEndpoint; + auto status = static_cast(StatusCodeEnum::kInvalidTLSEndpoint); ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid TLSEndpointId not found"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; @@ -244,7 +265,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (transportOptions.ingestMethod == IngestMethodsEnum::kUnknownEnumValue) { - auto status = StatusCodeEnum::kUnsupportedIngestMethod; + auto status = static_cast(StatusCodeEnum::kUnsupportedIngestMethod); ChipLogError(Zcl, "HandleAllocatePushTransport: Ingest method not supported"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; @@ -252,7 +273,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (transportOptions.containerFormat == ContainerFormatEnum::kUnknownEnumValue) { - auto status = StatusCodeEnum::kUnsupportedContainerFormat; + auto status = static_cast(StatusCodeEnum::kUnsupportedContainerFormat); ChipLogError(Zcl, "HandleAllocatePushTransport: Container format not supported"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; @@ -260,7 +281,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (transportOptions.triggerOptions.triggerType == TransportTriggerTypeEnum::kUnknownEnumValue) { - auto status = StatusCodeEnum::kInvalidTriggerType; + auto status = static_cast(StatusCodeEnum::kInvalidTriggerType); ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Trigger type"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; @@ -273,30 +294,29 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c mDelegate.ValidateStreamUsage(transportOptions.streamUsage, transportOptions.videoStreamID, transportOptions.audioStreamID); if (err != CHIP_NO_ERROR) { - auto status = StatusCodeEnum::kInvalidStream; + auto status = static_cast(StatusCodeEnum::kInvalidStream); ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Stream"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } - uint16_t connectionID = GenerateConnectionID(); - TransportStatusEnum outTransportStatus = TransportStatusEnum::kUnknownEnumValue; + uint16_t connectionID = GenerateConnectionID(); + TransportConfigurationStruct outTransportConfiguration; - outTransportConfiguration.connectionID = connectionID; + outTransportConfiguration.connectionID = connectionID; outTransportConfiguration.transportStatus = TransportStatusEnum::kInactive; - outTransportConfiguration.transportOptions = transportOptions; - // call the delegate + /** + * delegate should set the TransportOptions fields to the new values. + * Persistently store the resulting TransportConfigurationStruct and map it to the ConnectionID + */ Status status = mDelegate.AllocatePushTransport(transportOptions, outTransportConfiguration); if (status == Status::Success) { - response.connectionID = connectionID; - response.transportOptions = transportOptions; - response.transportStatus = outTransportStatus; - // add connection to CurrentConnections - AddStreamTransportConnection(connectionID); + UpsertStreamTransportConnection(outTransportConfiguration); + response.transportConfiguration = outTransportConfiguration; ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } @@ -332,9 +352,9 @@ void PushAvStreamTransportServer::HandleDeallocatePushTransport( void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx, const Commands::ModifyPushTransport::DecodableType & commandData) { - Status status = Status::Success; - uint16_t connectionID = commandData.connectionID; - auto & outTransportOptions = commandData.transportOptions; + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + auto & transportOptions = commandData.transportOptions; if (!FindStreamTransportConnection(connectionID)) { @@ -344,7 +364,7 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx } // Call the delegate - status = mDelegate.ModifyPushTransport(connectionID, outTransportOptions); + status = mDelegate.ModifyPushTransport(connectionID, transportOptions); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } @@ -352,19 +372,31 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, const Commands::SetTransportStatus::DecodableType & commandData) { - Status status = Status::Success; - uint16_t connectionID = commandData.connectionID; - auto & transportStatus = commandData.transportStatus; + Status status = Status::Success; + DataModel::Nullable connectionID = commandData.connectionID; + auto & transportStatus = commandData.transportStatus; + std::vector connectionIDList; - if (!FindStreamTransportConnection(connectionID)) + if (connectionID.IsNull()) + { + for (auto & transportConnection : mCurrentConnections) + { + connectionIDList.push_back(transportConnection.connectionID); + } + } + else if (!FindStreamTransportConnection(connectionID.Value())) { ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } + else + { + connectionIDList.push_back(connectionID.Value()); + } // Call the delegate - status = mDelegate.SetTransportStatus(connectionID, transportStatus); + status = mDelegate.SetTransportStatus(connectionIDList); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } @@ -377,13 +409,45 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( auto & activationReason = commandData.activationReason; auto & timeControl = commandData.timeControl; - if (!FindStreamTransportConnection(connectionID)) + TransportConfigurationStruct * transportConfiguration = FindStreamTransportConnection(connectionID); + + if (!transportConfiguration) { ChipLogError(Zcl, "HandleManuallyTriggerTransport: ConnectionID Not Found."); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } + if (transportConfiguration->transportStatus == TransportStatusEnum::kInactive) + { + auto status = static_cast(StatusCodeEnum::kInvalidTransportStatus); + ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Transport status"); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + return; + } + if (transportConfiguration->transportOptions.HasValue()) + { + if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kContinuous) + { + { + auto status = static_cast(StatusCodeEnum::kInvalidTriggerType); + ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Trigger type"); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + return; + } + } + else if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == + TransportTriggerTypeEnum::kCommand && + !timeControl.HasValue()) + { + { + ChipLogError(Zcl, "HandleManuallyTriggerTransport: Time control field not present"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + return; + } + } + } + // Call the delegate status = mDelegate.ManuallyTriggerTransport(connectionID, activationReason, timeControl); @@ -398,20 +462,29 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, Optional> connectionID = commandData.connectionID; - DataModel::List outStreamConfigurations; - - // Call the delegate - status = mDelegate.FindTransport(connectionID, outStreamConfigurations); + DataModel::List outTransportConfigurations; - if (connectionID.HasValue() && outStreamConfigurations.size() == 0) + if (connectionID.Value().IsNull()) { - ChipLogError(Zcl, "HandleFindTransport: No allocated transport for connectionID."); + if (mCurrentConnections.size() == 0) + { + ChipLogError(Zcl, "HandleFindTransport: ConnectionID not found"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + } + else if (!FindStreamTransportConnection(connectionID.Value().Value())) + { + ChipLogError(Zcl, "HandleFindTransport: ConnectionID not found"); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } + + // Call the delegate + status = mDelegate.FindTransport(connectionID,outTransportConfigurations); if (status == Status::Success) { - response.streamConfigurations = outStreamConfigurations; + response.transportConfigurations = outTransportConfigurations; ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 09e5c1b57fadd2..569785322986d4 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -38,6 +38,8 @@ using TransportMotionTriggerTimeControlStruct = Structs::TransportMotionTriggerT using TransportOptionsStruct = Structs::TransportOptionsStruct::Type; using TransportConfigurationStruct = Structs::TransportConfigurationStruct::Type; +using TransportOptionsDecodeableStruct = Structs::TransportOptionsStruct::DecodableType; + /** @brief * Defines interfaces for implementing application-specific logic for various aspects of the PushAvStreamTransport Delegate. * Specifically, it defines interfaces for the command handling and loading of the allocated streams. @@ -52,19 +54,16 @@ class PushAvStreamTransportDelegate /** * @brief Handle Command Delegate for stream transport allocation with the provided transport configuration option. * - * @param connectionID[in] Indicates the connectionID to allocate. - * * @param transportOptions[in] represent the configuration options of the transport to be allocated * - * @param outTransportStatus[out] represent the status of the transport allocation + * @param outTransporConfiguration[out] represent the configuration of the transport to be allocated * * @return Success if the allocation is successful and a PushTransportConnectionID was * produced; otherwise, the command SHALL be rejected with an appropriate * error. */ - virtual Protocols::InteractionModel::Status AllocatePushTransport(uint16_t connectionID, - const TransportOptionsStruct & transportOptions, - TransportStatusEnum & outTransportStatus) = 0; + virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsDecodeableStruct & transportOptions, + TransportConfigurationStruct & outTransporConfiguration) = 0; /** * @brief Handle Command Delegate for Stream transport deallocation for the * provided connectionID. @@ -81,27 +80,23 @@ class PushAvStreamTransportDelegate * * @param connectionID [in] Indicates the connectionID of the stream transport to modify. * - * @param outTransportOptions [out] represents the Trigger Options to modify. + * @param transportOptions [out] represents the Trigger Options to modify. * * @return Success if the stream transport modification is successful; otherwise, the command SHALL be rejected with an * appropriate error. */ virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, - const TransportOptionsStruct & outTransportOptions) = 0; + const TransportOptionsDecodeableStruct & transportOptions) = 0; /** * @brief Handle Command Delegate for Stream transport modification. * - * @param connectionID [in] Indicates the connectionID of the stream transport to set status for. - * - * @param transportStatus [in] represent the new transport status to apply. + * @param connectionIDList [in] represent the list of connectionIDs for which new transport status to apply. * * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with an * appropriate error. */ - virtual Protocols::InteractionModel::Status SetTransportStatus(const uint16_t connectionID, - TransportStatusEnum transportStatus) = 0; - + virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList) = 0; /** * @brief Handle Command Delegate to request the Node to manually start the specified push transport. * @@ -117,7 +112,7 @@ appropriate */ virtual Protocols::InteractionModel::Status ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, - const TransportMotionTriggerTimeControlStruct & timeControl) = 0; + const Optional & timeControl) = 0; /** * @brief Handle Command Delegate to get the Stream Options Configuration for the specified push transport. @@ -133,7 +128,7 @@ appropriate */ virtual Protocols::InteractionModel::Status FindTransport(const Optional> & connectionID, - DataModel::List & outtransportConfigurations) = 0; + DataModel::List & outtransportConfigurations) = 0; /** * @brief Validates the requested stream usage against the camera's resource management @@ -166,7 +161,7 @@ appropriate * list, at initialization. Once loaded, the cluster server would be serving Reads on these attributes. The list is updatable * via the Add/Remove functions for the respective transport connections. */ - virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; + virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; /** * @brief Callback into the delegate once persistent attributes managed by @@ -211,6 +206,12 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co BitMask GetSupportedIngestMethods() const { return mSupportedIngestMethods; } private: + enum class UpsertResultEnum : uint8_t + { + kInserted = 0x00, + kUpdated = 0x01, + }; + PushAvStreamTransportDelegate & mDelegate; // Attributes @@ -234,14 +235,12 @@ class PushAvStreamTransportServer : private AttributeAccessInterface, private Co CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder); // Helper functions - - bool FindStreamTransportConnection(const uint16_t connectionID); uint16_t GenerateConnectionID(); - + TransportConfigurationStruct * FindStreamTransportConnection(const uint16_t connectionID); // Add/Remove Management functions for transport - CHIP_ERROR AddStreamTransportConnection(const uint16_t transportConnectionId); + UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStruct & transportConfiguration); - CHIP_ERROR RemoveStreamTransportConnection(const uint16_t transportConnectionId); + void RemoveStreamTransportConnection(const uint16_t connectionID); /** * @brief Inherited from CommandHandlerInterface From 8f22e903447437753d9fef9c0ae5fd056f137258 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 11 Apr 2025 03:35:04 +0530 Subject: [PATCH 06/40] remove conflict to camera example app --- .../camera-common/camera-app.matter | 205 +------- .../camera-app/camera-common/camera-app.zap | 489 ------------------ .../app-common/zap-generated/callback.h | 53 -- 3 files changed, 1 insertion(+), 746 deletions(-) diff --git a/examples/camera-app/camera-common/camera-app.matter b/examples/camera-app/camera-common/camera-app.matter index 52f4f8effc57c1..671b974070a11b 100644 --- a/examples/camera-app/camera-common/camera-app.matter +++ b/examples/camera-app/camera-common/camera-app.matter @@ -2169,11 +2169,7 @@ provisional cluster CameraAvStreamManagement = 1361 { kJPEG = 0; } -<<<<<<< HEAD shared enum StreamUsageEnum : enum8 { -======= - enum StreamUsageEnum : enum8 { ->>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) kInternal = 0; kRecording = 1; kAnalysis = 2; @@ -2209,10 +2205,7 @@ provisional cluster CameraAvStreamManagement = 1361 { kWatermark = 0x40; kOnScreenDisplay = 0x80; kLocalStorage = 0x100; -<<<<<<< HEAD kHighDynamicRange = 0x200; -======= ->>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) } struct VideoResolutionStruct { @@ -2281,74 +2274,24 @@ provisional cluster CameraAvStreamManagement = 1361 { struct VideoSensorParamsStruct { int16u sensorWidth = 0; int16u sensorHeight = 1; -<<<<<<< HEAD int16u maxFPS = 2; optional int16u maxHDRFPS = 3; } shared struct ViewportStruct { -======= - boolean HDRCapable = 2; - int16u maxFPS = 3; - int16u maxHDRFPS = 4; - } - - struct ViewportStruct { ->>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) int16u x1 = 0; int16u y1 = 1; int16u x2 = 2; int16u y2 = 3; } -<<<<<<< HEAD -======= - info event VideoStreamChanged = 0 { - int16u videoStreamID = 0; - optional StreamUsageEnum streamUsage = 1; - optional VideoCodecEnum videoCodec = 2; - optional int16u minFrameRate = 3; - optional int16u maxFrameRate = 4; - optional VideoResolutionStruct minResolution = 5; - optional VideoResolutionStruct maxResolution = 6; - optional int32u minBitRate = 7; - optional int32u maxBitRate = 8; - optional int16u minFragmentLen = 9; - optional int16u maxFragmentLen = 10; - } - - info event AudioStreamChanged = 1 { - int16u audioStreamID = 0; - optional StreamUsageEnum streamUsage = 1; - optional AudioCodecEnum audioCodec = 2; - optional int8u channelCount = 3; - optional int32u sampleRate = 4; - optional int32u bitRate = 5; - optional int8u bitDepth = 6; - } - - info event SnapshotStreamChanged = 2 { - int16u snapshotStreamID = 0; - optional ImageCodecEnum imageCodec = 1; - optional int16u frameRate = 2; - optional int32u bitRate = 3; - optional VideoResolutionStruct minResolution = 4; - optional VideoResolutionStruct maxResolution = 5; - optional int8u quality = 6; - } - ->>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) readonly attribute optional int8u maxConcurrentVideoEncoders = 0; readonly attribute optional int32u maxEncodedPixelRate = 1; readonly attribute optional VideoSensorParamsStruct videoSensorParams = 2; readonly attribute optional boolean nightVisionCapable = 3; readonly attribute optional VideoResolutionStruct minViewport = 4; readonly attribute optional RateDistortionTradeOffPointsStruct rateDistortionTradeOffPoints[] = 5; -<<<<<<< HEAD readonly attribute int32u maxContentBufferSize = 6; -======= - readonly attribute optional int32u maxContentBufferSize = 6; ->>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) readonly attribute optional AudioCapabilitiesStruct microphoneCapabilities = 7; readonly attribute optional AudioCapabilitiesStruct speakerCapabilities = 8; readonly attribute optional TwoWayTalkSupportTypeEnum twoWayTalkSupport = 9; @@ -2356,11 +2299,7 @@ provisional cluster CameraAvStreamManagement = 1361 { readonly attribute int32u maxNetworkBandwidth = 11; readonly attribute optional int16u currentFrameRate = 12; attribute access(read: manage, write: manage) optional boolean HDRModeEnabled = 13; -<<<<<<< HEAD readonly attribute StreamUsageEnum supportedStreamUsages[] = 14; -======= - readonly attribute fabric_idx fabricsUsingCamera[] = 14; ->>>>>>> 0d4f9fefb9 (add push av cluster to camera-app and some fixes) readonly attribute optional VideoStreamStruct allocatedVideoStreams[] = 15; readonly attribute optional AudioStreamStruct allocatedAudioStreams[] = 16; readonly attribute optional SnapshotStreamStruct allocatedSnapshotStreams[] = 17; @@ -2730,121 +2669,6 @@ provisional cluster Chime = 1366 { command PlayChimeSound(): DefaultSuccess = 0; } -/** This Cluster is used to manage TLS Client Certificates and to provision - TLS endpoints with enough information to facilitate subsequent connection. */ -provisional cluster TlsCertificateManagement = 2049 { - revision 1; - - struct TLSCertStruct { - int16u caid = 0; - long_octet_string<3000> certificate = 1; - } - - struct TLSClientCertificateDetailStruct { - int16u ccdid = 0; - long_octet_string<3000> clientCertificate = 1; - octet_string intermediateCertificates[] = 2; - } - - readonly attribute int8u maxRootCertificates = 0; - readonly attribute int8u currentRootCertificates = 1; - readonly attribute int8u maxClientCertificates = 2; - readonly attribute int8u currentClientCertificates = 3; - readonly attribute command_id generatedCommandList[] = 65528; - readonly attribute command_id acceptedCommandList[] = 65529; - readonly attribute event_id eventList[] = 65530; - readonly attribute attrib_id attributeList[] = 65531; - readonly attribute bitmap32 featureMap = 65532; - readonly attribute int16u clusterRevision = 65533; - - request struct ProvisionRootCertificateRequest { - long_octet_string<3000> certificate = 0; - nullable int16u caid = 1; - } - - response struct ProvisionRootCertificateResponse = 1 { - int16u caid = 0; - } - - request struct FindRootCertificateRequest { - nullable int16u caid = 0; - } - - response struct FindRootCertificateResponse = 3 { - TLSCertStruct certificateDetails[] = 0; - } - - request struct LookupRootCertificateRequest { - octet_string<64> fingerprint = 0; - } - - response struct LookupRootCertificateResponse = 5 { - int16u caid = 0; - } - - request struct RemoveRootCertificateRequest { - int16u caid = 0; - } - - request struct TLSClientCSRRequest { - octet_string nonce = 0; - } - - response struct TLSClientCSRResponse = 8 { - int16u ccdid = 0; - octet_string csr = 1; - octet_string nonce = 2; - } - - request struct ProvisionClientCertificateRequest { - int16u ccdid = 0; - TLSClientCertificateDetailStruct clientCertificateDetails = 1; - } - - response struct ProvisionClientCertificateResponse = 10 { - int16u ccdid = 0; - } - - request struct FindClientCertificateRequest { - int16u ccdid = 0; - } - - response struct FindClientCertificateResponse = 12 { - TLSClientCertificateDetailStruct certificateDetails[] = 0; - } - - request struct LookupClientCertificateRequest { - octet_string<64> fingerprint = 0; - } - - response struct LookupClientCertificateResponse = 14 { - int16u ccdid = 0; - } - - request struct RemoveClientCertificateRequest { - int16u ccdid = 0; - } - - /** This command SHALL provision the provided certificate for the passed in CAID. */ - command access(invoke: administer) ProvisionRootCertificate(ProvisionRootCertificateRequest): ProvisionRootCertificateResponse = 0; - /** This command SHALL return the TLSCertStruct for the passed in CAID. */ - command FindRootCertificate(FindRootCertificateRequest): FindRootCertificateResponse = 2; - /** This command SHALL return the CAID for the passed in fingerprint. */ - command LookupRootCertificate(LookupRootCertificateRequest): LookupRootCertificateResponse = 4; - /** This command SHALL be generated to request the server removes the certificate provisioned to the provided Certificate Authority ID. */ - command access(invoke: administer) RemoveRootCertificate(RemoveRootCertificateRequest): DefaultSuccess = 6; - /** This command SHALL be generated to request the Node generates a Certificate Signing Request. */ - command access(invoke: administer) TLSClientCSR(TLSClientCSRRequest): TLSClientCSRResponse = 7; - /** This command SHALL be generated to request the Node provisions the provided Client Certificate Details. */ - command access(invoke: administer) ProvisionClientCertificate(ProvisionClientCertificateRequest): ProvisionClientCertificateResponse = 9; - /** This command SHALL return the TLSClientCertificateDetailStruct for the passed in CCDID. */ - command FindClientCertificate(FindClientCertificateRequest): FindClientCertificateResponse = 11; - /** This command SHALL return the CCDID for the passed in Fingerprint. */ - command LookupClientCertificate(LookupClientCertificateRequest): LookupClientCertificateResponse = 13; - /** This command SHALL be generated to request the Node removes the certificate provisioned to the provided Client Certificate Details ID. */ - command access(invoke: administer) RemoveClientCertificate(RemoveClientCertificateRequest): DefaultSuccess = 15; -} - endpoint 0 { device type ma_rootdevice = 22, version 3; device type ma_otarequestor = 18, version 1; @@ -3362,33 +3186,6 @@ endpoint 1 { handle command PlayChimeSound; } +} - server cluster TlsCertificateManagement { - ram attribute maxRootCertificates; - ram attribute currentRootCertificates; - ram attribute maxClientCertificates; - ram attribute currentClientCertificates; - callback attribute generatedCommandList; - callback attribute acceptedCommandList; - callback attribute attributeList; - ram attribute featureMap default = 0; - ram attribute clusterRevision default = 1; - handle command ProvisionRootCertificate; - handle command ProvisionRootCertificateResponse; - handle command FindRootCertificate; - handle command FindRootCertificateResponse; - handle command LookupRootCertificate; - handle command LookupRootCertificateResponse; - handle command RemoveRootCertificate; - handle command TLSClientCSR; - handle command TLSClientCSRResponse; - handle command ProvisionClientCertificate; - handle command ProvisionClientCertificateResponse; - handle command FindClientCertificate; - handle command FindClientCertificateResponse; - handle command LookupClientCertificate; - handle command LookupClientCertificateResponse; - handle command RemoveClientCertificate; - } -} diff --git a/examples/camera-app/camera-common/camera-app.zap b/examples/camera-app/camera-common/camera-app.zap index 9fcb6616414ab5..4cdaaebdcb1fe2 100644 --- a/examples/camera-app/camera-common/camera-app.zap +++ b/examples/camera-app/camera-common/camera-app.zap @@ -6090,210 +6090,6 @@ } ] }, - { - "name": "Push AV Stream Transport", - "code": 1365, - "mfgCode": null, - "define": "PUSH_AV_STREAM_TRANSPORT_CLUSTER", - "side": "server", - "enabled": 1, - "commands": [ - { - "name": "AllocatePushTransport", - "code": 0, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "AllocatePushTransportResponse", - "code": 1, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "DeallocatePushTransport", - "code": 2, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "ModifyPushTransport", - "code": 3, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "SetTransportStatus", - "code": 4, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "ManuallyTriggerTransport", - "code": 5, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "FindTransport", - "code": 6, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "FindTransportResponse", - "code": 7, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - } - ], - "attributes": [ - { - "name": "SupportedContainerFormats", - "code": 0, - "mfgCode": null, - "side": "server", - "type": "SupportedContainerFormatsBitmap", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "SupportedIngestMethods", - "code": 1, - "mfgCode": null, - "side": "server", - "type": "SupportedIngestMethodsBitmap", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "CurrentConnections", - "code": 2, - "mfgCode": null, - "side": "server", - "type": "array", - "included": 1, - "storageOption": "External", - "singleton": 0, - "bounded": 0, - "defaultValue": null, - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "GeneratedCommandList", - "code": 65528, - "mfgCode": null, - "side": "server", - "type": "array", - "included": 1, - "storageOption": "External", - "singleton": 0, - "bounded": 0, - "defaultValue": null, - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "AcceptedCommandList", - "code": 65529, - "mfgCode": null, - "side": "server", - "type": "array", - "included": 1, - "storageOption": "External", - "singleton": 0, - "bounded": 0, - "defaultValue": null, - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "AttributeList", - "code": 65531, - "mfgCode": null, - "side": "server", - "type": "array", - "included": 1, - "storageOption": "External", - "singleton": 0, - "bounded": 0, - "defaultValue": null, - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "FeatureMap", - "code": 65532, - "mfgCode": null, - "side": "server", - "type": "bitmap32", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "0", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "ClusterRevision", - "code": 65533, - "mfgCode": null, - "side": "server", - "type": "int16u", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "1", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - } - ] - }, { "name": "Chime", "code": 1366, @@ -6442,291 +6238,6 @@ "reportableChange": 0 } ] - }, - { - "name": "TLS Certificate Management", - "code": 2049, - "mfgCode": null, - "define": "TLS_CERTIFICATE_MANAGEMENT_CLUSTER", - "side": "server", - "enabled": 1, - "apiMaturity": "provisional", - "commands": [ - { - "name": "ProvisionRootCertificate", - "code": 0, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "ProvisionRootCertificateResponse", - "code": 1, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "FindRootCertificate", - "code": 2, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "FindRootCertificateResponse", - "code": 3, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "LookupRootCertificate", - "code": 4, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "LookupRootCertificateResponse", - "code": 5, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "RemoveRootCertificate", - "code": 6, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "TLSClientCSR", - "code": 7, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "TLSClientCSRResponse", - "code": 8, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "ProvisionClientCertificate", - "code": 9, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "ProvisionClientCertificateResponse", - "code": 10, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "FindClientCertificate", - "code": 11, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "FindClientCertificateResponse", - "code": 12, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "LookupClientCertificate", - "code": 13, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "LookupClientCertificateResponse", - "code": 14, - "mfgCode": null, - "source": "server", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "RemoveClientCertificate", - "code": 15, - "mfgCode": null, - "source": "client", - "isIncoming": 1, - "isEnabled": 1 - } - ], - "attributes": [ - { - "name": "MaxRootCertificates", - "code": 0, - "mfgCode": null, - "side": "server", - "type": "int8u", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "CurrentRootCertificates", - "code": 1, - "mfgCode": null, - "side": "server", - "type": "int8u", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "MaxClientCertificates", - "code": 2, - "mfgCode": null, - "side": "server", - "type": "int8u", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "CurrentClientCertificates", - "code": 3, - "mfgCode": null, - "side": "server", - "type": "int8u", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "GeneratedCommandList", - "code": 65528, - "mfgCode": null, - "side": "server", - "type": "array", - "included": 1, - "storageOption": "External", - "singleton": 0, - "bounded": 0, - "defaultValue": null, - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "AcceptedCommandList", - "code": 65529, - "mfgCode": null, - "side": "server", - "type": "array", - "included": 1, - "storageOption": "External", - "singleton": 0, - "bounded": 0, - "defaultValue": null, - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "AttributeList", - "code": 65531, - "mfgCode": null, - "side": "server", - "type": "array", - "included": 1, - "storageOption": "External", - "singleton": 0, - "bounded": 0, - "defaultValue": null, - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "FeatureMap", - "code": 65532, - "mfgCode": null, - "side": "server", - "type": "bitmap32", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "0", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "ClusterRevision", - "code": 65533, - "mfgCode": null, - "side": "server", - "type": "int16u", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "1", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - } - ] } ] } diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 6bd941ccbea9eb..61bebcbc4720a2 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -7847,59 +7847,6 @@ bool emberAfCommodityTariffClusterGetTariffComponentCallback( bool emberAfCommodityTariffClusterGetDayEntryCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::CommodityTariff::Commands::GetDayEntry::DecodableType & commandData); - * @brief WebRTC Transport Provider Cluster SolicitOffer Command callback (from client) - */ -bool emberAfWebRTCTransportProviderClusterSolicitOfferCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportProvider::Commands::SolicitOffer::DecodableType & commandData); -/** - * @brief WebRTC Transport Provider Cluster ProvideOffer Command callback (from client) - */ -bool emberAfWebRTCTransportProviderClusterProvideOfferCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideOffer::DecodableType & commandData); -/** - * @brief WebRTC Transport Provider Cluster ProvideAnswer Command callback (from client) - */ -bool emberAfWebRTCTransportProviderClusterProvideAnswerCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideAnswer::DecodableType & commandData); -/** - * @brief WebRTC Transport Provider Cluster ProvideICECandidate Command callback (from client) - */ -bool emberAfWebRTCTransportProviderClusterProvideICECandidateCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportProvider::Commands::ProvideICECandidate::DecodableType & commandData); -/** - * @brief WebRTC Transport Provider Cluster EndSession Command callback (from client) - */ -bool emberAfWebRTCTransportProviderClusterEndSessionCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportProvider::Commands::EndSession::DecodableType & commandData); -/** - * @brief WebRTC Transport Requestor Cluster Offer Command callback (from client) - */ -bool emberAfWebRTCTransportRequestorClusterOfferCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportRequestor::Commands::Offer::DecodableType & commandData); -/** - * @brief WebRTC Transport Requestor Cluster Answer Command callback (from client) - */ -bool emberAfWebRTCTransportRequestorClusterAnswerCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportRequestor::Commands::Answer::DecodableType & commandData); -/** - * @brief WebRTC Transport Requestor Cluster ICECandidates Command callback (from client) - */ -bool emberAfWebRTCTransportRequestorClusterICECandidatesCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportRequestor::Commands::ICECandidates::DecodableType & commandData); -/** - * @brief WebRTC Transport Requestor Cluster End Command callback (from client) - */ -bool emberAfWebRTCTransportRequestorClusterEndCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::WebRTCTransportRequestor::Commands::End::DecodableType & commandData); /** * @brief TLS Certificate Management Cluster ProvisionRootCertificate Command callback (from client) */ From 14a243d7d1bc0960233472538afd28f8bb3d3c33 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 10 Apr 2025 22:11:31 +0000 Subject: [PATCH 07/40] Restyled by clang-format --- .../push-av-stream-transport-server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 088a3f15ae9f68..af678f6f9edd59 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -481,7 +481,7 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, } // Call the delegate - status = mDelegate.FindTransport(connectionID,outTransportConfigurations); + status = mDelegate.FindTransport(connectionID, outTransportConfigurations); if (status == Status::Success) { response.transportConfigurations = outTransportConfigurations; From 2e0099bd7eee5e161684c8e9c7224e2bdc83c1d5 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 18 Apr 2025 01:55:46 +0530 Subject: [PATCH 08/40] added delegate implementation in all_clusters_app --- .../all-clusters-app.matter | 387 ++++++++++++++++++ .../all-clusters-common/all-clusters-app.zap | 284 ++++++++++++- .../push-av-stream-transport-delegate-impl.h | 77 ++++ ...push-av-stream-transport-delegate-impl.cpp | 174 ++++++++ examples/all-clusters-app/linux/BUILD.gn | 1 + src/app/chip_data_model.gni | 1 - .../push-av-stream-transport-server.cpp | 6 +- .../push-av-stream-transport-server.h | 4 +- .../zcl/zcl-with-test-extensions.json | 5 +- src/app/zap-templates/zcl/zcl.json | 5 +- .../app-common/zap-generated/callback.h | 36 -- 11 files changed, 934 insertions(+), 46 deletions(-) create mode 100644 examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h create mode 100644 examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 8f38a0739d2f5f..5b7b9f1c9a2fc8 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -7439,6 +7439,372 @@ provisional cluster CameraAvSettingsUserLevelManagement = 1362 { command DPTZRelativeMove(DPTZRelativeMoveRequest): DefaultSuccess = 6; } +/** This cluster implements the upload of Audio and Video streams from the Push AV Stream Transport Cluster using suitable push-based transports. */ +provisional cluster PushAvStreamTransport = 1365 { + revision 1; + + enum ContainerFormatEnum : enum8 { + kCMAF = 0; + } + + enum IngestMethodsEnum : enum8 { + kCMAFIngest = 0; + } + + enum StatusCodeEnum : enum8 { + kInvalidTLSEndpoint = 2; + kInvalidStream = 3; + kInvalidURL = 4; + kInvalidZone = 5; + kUnsupportedContainerFormat = 6; + kUnsupportedIngestMethod = 7; + kInvalidTriggerType = 8; + kInvalidTransportStatus = 9; + } + + shared enum StreamUsageEnum : enum8 { + kInternal = 0; + kRecording = 1; + kAnalysis = 2; + kLiveView = 3; + } + + enum TransportStatusEnum : enum8 { + kActive = 0; + kInactive = 1; + } + + enum TransportTriggerTypeEnum : enum8 { + kCommand = 0; + kMotion = 1; + kContinuous = 2; + } + + enum TriggerActivationReasonEnum : enum8 { + kUserInitiated = 0; + kAutomation = 1; + kEmergency = 2; + } + + bitmap Feature : bitmap32 { + kPerZoneSensitivity = 0x1; + kMetadata = 0x2; + } + + bitmap SupportedContainerFormatsBitmap : bitmap8 { + kCMAF = 0x1; + } + + bitmap SupportedIngestMethodsBitmap : bitmap8 { + kCMAFIngest = 0x1; + } + + struct TransportMotionTriggerTimeControlStruct { + int16u initialDuration = 0; + int16u augmentationDuration = 1; + elapsed_s maxDuration = 2; + int16u blindDuration = 3; + } + + struct TransportZoneOptionsStruct { + nullable int16u zone = 1; + optional int8u sensitivity = 2; + } + + struct TransportTriggerOptionsStruct { + TransportTriggerTypeEnum triggerType = 0; + optional nullable TransportZoneOptionsStruct motionZones[] = 1; + optional nullable int8u motionSensitivity = 2; + optional TransportMotionTriggerTimeControlStruct motionTimeControl = 3; + optional int16u maxPreRollLen = 4; + } + + struct CMAFContainerOptionsStruct { + int16u chunkDuration = 0; + optional octet_string<16> CENCKey = 1; + optional boolean metadataEnabled = 2; + } + + struct ContainerOptionsStruct { + ContainerFormatEnum containerType = 0; + optional CMAFContainerOptionsStruct CMAFContainerOptions = 1; + } + + struct TransportOptionsStruct { + StreamUsageEnum streamUsage = 0; + optional nullable int16u videoStreamID = 1; + optional nullable int16u audioStreamID = 2; + int16u endpointID = 3; + long_char_string<2000> url = 4; + TransportTriggerOptionsStruct triggerOptions = 5; + IngestMethodsEnum ingestMethod = 6; + ContainerFormatEnum containerFormat = 7; + ContainerOptionsStruct containerOptions = 8; + optional epoch_s expiryTime = 9; + } + + fabric_scoped struct TransportConfigurationStruct { + int16u connectionID = 0; + TransportStatusEnum transportStatus = 1; + optional TransportOptionsStruct transportOptions = 2; + fabric_idx fabricIndex = 254; + } + + info event PushTransportBegin = 0 { + int16u connectionID = 0; + TransportTriggerTypeEnum triggerType = 1; + optional TriggerActivationReasonEnum activationReason = 2; + } + + info event PushTransportEnd = 1 { + int16u connectionID = 0; + TransportTriggerTypeEnum triggerType = 1; + optional TriggerActivationReasonEnum activationReason = 2; + } + + readonly attribute SupportedContainerFormatsBitmap supportedContainerFormats = 0; + readonly attribute SupportedIngestMethodsBitmap supportedIngestMethods = 1; + readonly attribute TransportConfigurationStruct currentConnections[] = 2; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct AllocatePushTransportRequest { + TransportOptionsStruct transportOptions = 0; + } + + response struct AllocatePushTransportResponse = 1 { + TransportConfigurationStruct transportConfiguration = 0; + } + + request struct DeallocatePushTransportRequest { + int16u connectionID = 0; + } + + request struct ModifyPushTransportRequest { + int16u connectionID = 0; + TransportOptionsStruct transportOptions = 1; + } + + request struct SetTransportStatusRequest { + nullable int16u connectionID = 0; + TransportStatusEnum transportStatus = 1; + } + + request struct ManuallyTriggerTransportRequest { + int16u connectionID = 0; + TriggerActivationReasonEnum activationReason = 1; + optional TransportMotionTriggerTimeControlStruct timeControl = 2; + } + + request struct FindTransportRequest { + optional nullable int16u connectionID = 0; + } + + response struct FindTransportResponse = 7 { + TransportConfigurationStruct transportConfigurations[] = 0; + } + + /** This command SHALL allocate a transport and return a PushTransportConnectionID. */ + fabric command access(invoke: manage) AllocatePushTransport(AllocatePushTransportRequest): AllocatePushTransportResponse = 0; + /** This command SHALL be generated to request the Node deallocates the specified transport. */ + fabric command access(invoke: manage) DeallocatePushTransport(DeallocatePushTransportRequest): DefaultSuccess = 2; + /** This command is used to request the Node modifies the configuration of the specified push transport. */ + fabric command access(invoke: manage) ModifyPushTransport(ModifyPushTransportRequest): DefaultSuccess = 3; + /** This command SHALL be generated to request the Node modifies the Transport Status of a specified transport or all transports. */ + fabric command access(invoke: manage) SetTransportStatus(SetTransportStatusRequest): DefaultSuccess = 4; + /** This command SHALL be generated to request the Node to manually start the specified push transport. */ + fabric command ManuallyTriggerTransport(ManuallyTriggerTransportRequest): DefaultSuccess = 5; + /** This command SHALL return the Transport Configuration for the specified push transport or all allocated transports for the fabric if null. */ + fabric command FindTransport(FindTransportRequest): FindTransportResponse = 6; +} + +/** This cluster implements the upload of Audio and Video streams from the Push AV Stream Transport Cluster using suitable push-based transports. */ +provisional cluster PushAvStreamTransport = 1365 { + revision 1; + + enum ContainerFormatEnum : enum8 { + kCMAF = 0; + } + + enum IngestMethodsEnum : enum8 { + kCMAFIngest = 0; + } + + enum StatusCodeEnum : enum8 { + kInvalidTLSEndpoint = 2; + kInvalidStream = 3; + kInvalidURL = 4; + kInvalidZone = 5; + kUnsupportedContainerFormat = 6; + kUnsupportedIngestMethod = 7; + kInvalidTriggerType = 8; + kInvalidTransportStatus = 9; + } + + shared enum StreamUsageEnum : enum8 { + kInternal = 0; + kRecording = 1; + kAnalysis = 2; + kLiveView = 3; + } + + enum TransportStatusEnum : enum8 { + kActive = 0; + kInactive = 1; + } + + enum TransportTriggerTypeEnum : enum8 { + kCommand = 0; + kMotion = 1; + kContinuous = 2; + } + + enum TriggerActivationReasonEnum : enum8 { + kUserInitiated = 0; + kAutomation = 1; + kEmergency = 2; + } + + bitmap Feature : bitmap32 { + kPerZoneSensitivity = 0x1; + kMetadata = 0x2; + } + + bitmap SupportedContainerFormatsBitmap : bitmap8 { + kCMAF = 0x1; + } + + bitmap SupportedIngestMethodsBitmap : bitmap8 { + kCMAFIngest = 0x1; + } + + struct TransportMotionTriggerTimeControlStruct { + int16u initialDuration = 0; + int16u augmentationDuration = 1; + elapsed_s maxDuration = 2; + int16u blindDuration = 3; + } + + struct TransportZoneOptionsStruct { + nullable int16u zone = 1; + optional int8u sensitivity = 2; + } + + struct TransportTriggerOptionsStruct { + TransportTriggerTypeEnum triggerType = 0; + optional nullable TransportZoneOptionsStruct motionZones[] = 1; + optional nullable int8u motionSensitivity = 2; + optional TransportMotionTriggerTimeControlStruct motionTimeControl = 3; + optional int16u maxPreRollLen = 4; + } + + struct CMAFContainerOptionsStruct { + int16u chunkDuration = 0; + optional octet_string<16> CENCKey = 1; + optional boolean metadataEnabled = 2; + } + + struct ContainerOptionsStruct { + ContainerFormatEnum containerType = 0; + optional CMAFContainerOptionsStruct CMAFContainerOptions = 1; + } + + struct TransportOptionsStruct { + StreamUsageEnum streamUsage = 0; + optional nullable int16u videoStreamID = 1; + optional nullable int16u audioStreamID = 2; + int16u endpointID = 3; + long_char_string<2000> url = 4; + TransportTriggerOptionsStruct triggerOptions = 5; + IngestMethodsEnum ingestMethod = 6; + ContainerFormatEnum containerFormat = 7; + ContainerOptionsStruct containerOptions = 8; + optional epoch_s expiryTime = 9; + } + + fabric_scoped struct TransportConfigurationStruct { + int16u connectionID = 0; + TransportStatusEnum transportStatus = 1; + optional TransportOptionsStruct transportOptions = 2; + fabric_idx fabricIndex = 254; + } + + info event PushTransportBegin = 0 { + int16u connectionID = 0; + TransportTriggerTypeEnum triggerType = 1; + optional TriggerActivationReasonEnum activationReason = 2; + } + + info event PushTransportEnd = 1 { + int16u connectionID = 0; + TransportTriggerTypeEnum triggerType = 1; + optional TriggerActivationReasonEnum activationReason = 2; + } + + readonly attribute SupportedContainerFormatsBitmap supportedContainerFormats = 0; + readonly attribute SupportedIngestMethodsBitmap supportedIngestMethods = 1; + readonly attribute TransportConfigurationStruct currentConnections[] = 2; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct AllocatePushTransportRequest { + TransportOptionsStruct transportOptions = 0; + } + + response struct AllocatePushTransportResponse = 1 { + TransportConfigurationStruct transportConfiguration = 0; + } + + request struct DeallocatePushTransportRequest { + int16u connectionID = 0; + } + + request struct ModifyPushTransportRequest { + int16u connectionID = 0; + TransportOptionsStruct transportOptions = 1; + } + + request struct SetTransportStatusRequest { + nullable int16u connectionID = 0; + TransportStatusEnum transportStatus = 1; + } + + request struct ManuallyTriggerTransportRequest { + int16u connectionID = 0; + TriggerActivationReasonEnum activationReason = 1; + optional TransportMotionTriggerTimeControlStruct timeControl = 2; + } + + request struct FindTransportRequest { + optional nullable int16u connectionID = 0; + } + + response struct FindTransportResponse = 7 { + TransportConfigurationStruct transportConfigurations[] = 0; + } + + /** This command SHALL allocate a transport and return a PushTransportConnectionID. */ + fabric command access(invoke: manage) AllocatePushTransport(AllocatePushTransportRequest): AllocatePushTransportResponse = 0; + /** This command SHALL be generated to request the Node deallocates the specified transport. */ + fabric command access(invoke: manage) DeallocatePushTransport(DeallocatePushTransportRequest): DefaultSuccess = 2; + /** This command is used to request the Node modifies the configuration of the specified push transport. */ + fabric command access(invoke: manage) ModifyPushTransport(ModifyPushTransportRequest): DefaultSuccess = 3; + /** This command SHALL be generated to request the Node modifies the Transport Status of a specified transport or all transports. */ + fabric command access(invoke: manage) SetTransportStatus(SetTransportStatusRequest): DefaultSuccess = 4; + /** This command SHALL be generated to request the Node to manually start the specified push transport. */ + fabric command ManuallyTriggerTransport(ManuallyTriggerTransportRequest): DefaultSuccess = 5; + /** This command SHALL return the Transport Configuration for the specified push transport or all allocated transports for the fabric if null. */ + fabric command FindTransport(FindTransportRequest): FindTransportResponse = 6; +} + /** This cluster provides facilities to configure and play Chime sounds, such as those used in a doorbell. */ provisional cluster Chime = 1366 { revision 1; @@ -8513,6 +8879,7 @@ endpoint 1 { device type ma_onofflight = 256, version 1; binding cluster OnOff; + binding cluster PushAvStreamTransport; server cluster Identify { ram attribute identifyTime default = 0x0000; @@ -9787,6 +10154,26 @@ endpoint 1 { handle command DPTZRelativeMove; } + server cluster PushAvStreamTransport { + ram attribute supportedContainerFormats; + ram attribute supportedIngestMethods; + callback attribute currentConnections; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command AllocatePushTransport; + handle command AllocatePushTransportResponse; + handle command DeallocatePushTransport; + handle command ModifyPushTransport; + handle command SetTransportStatus; + handle command ManuallyTriggerTransport; + handle command FindTransport; + handle command FindTransportResponse; + } + server cluster Chime { callback attribute installedChimeSounds; callback attribute selectedChime; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index 501ad53f16c5a5..465dba98b96499 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -21195,7 +21195,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21355,7 +21355,7 @@ "code": 4, "mfgCode": null, "side": "server", - "type": "uint8s", + "type": "int8u", "included": 1, "storageOption": "RAM", "singleton": 0, @@ -21512,6 +21512,286 @@ } ] }, + { + "name": "Push AV Stream Transport", + "code": 1365, + "mfgCode": null, + "define": "PUSH_AV_STREAM_TRANSPORT_CLUSTER", + "side": "client", + "enabled": 1, + "apiMaturity": "provisional", + "commands": [ + { + "name": "AllocatePushTransport", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "AllocatePushTransportResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "DeallocatePushTransport", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ModifyPushTransport", + "code": 3, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "SetTransportStatus", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "ManuallyTriggerTransport", + "code": 5, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "FindTransport", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "FindTransportResponse", + "code": 7, + "mfgCode": null, + "source": "server", + "isIncoming": 1, + "isEnabled": 1 + } + ] + }, + { + "name": "Push AV Stream Transport", + "code": 1365, + "mfgCode": null, + "define": "PUSH_AV_STREAM_TRANSPORT_CLUSTER", + "side": "server", + "enabled": 1, + "apiMaturity": "provisional", + "commands": [ + { + "name": "AllocatePushTransport", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "AllocatePushTransportResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + }, + { + "name": "DeallocatePushTransport", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ModifyPushTransport", + "code": 3, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "SetTransportStatus", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ManuallyTriggerTransport", + "code": 5, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "FindTransport", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "FindTransportResponse", + "code": 7, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "SupportedContainerFormats", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "SupportedContainerFormatsBitmap", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SupportedIngestMethods", + "code": 1, + "mfgCode": null, + "side": "server", + "type": "SupportedIngestMethodsBitmap", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "CurrentConnections", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ] + }, { "name": "Chime", "code": 1366, diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h new file mode 100644 index 00000000000000..4625ad622d3b0e --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -0,0 +1,77 @@ +/* + * + * 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 +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +struct PushAvStream +{ + uint16_t id; + TransportConfigurationStruct transportConfig; +}; + +/** + * The application delegate to define the options & implement commands. + */ +class PushAvStreamTransportManager : public PushAvStreamTransportDelegate +{ +public: + Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsDecodeableStruct & transportOptions, + TransportConfigurationStruct & outTransporConfiguration); + Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID); + Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, + const TransportOptionsDecodeableStruct & transportOptions); + Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus); + + Protocols::InteractionModel::Status + ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, + const Optional & timeControl); + Protocols::InteractionModel::Status + FindTransport(const Optional> & connectionID, + DataModel::List & outtransportConfigurations); + + CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, + const Optional> & videoStreamId, + const Optional> & audioStreamId); + + void OnAttributeChanged(AttributeId attributeId); + CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); + CHIP_ERROR PersistentAttributesLoadedCallback(); + + void Init(); + PushAvStreamTransportManager() = default; + + ~PushAvStreamTransportManager() = default; + +private: + std::vector pushavStreams; + std::vector configList; +}; + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp new file mode 100644 index 00000000000000..a493ff1f504719 --- /dev/null +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -0,0 +1,174 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::DataModel; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::PushAvStreamTransport; +using chip::Protocols::InteractionModel::Status; + +// Global pointer to overall PushAV Stream Transport implementing the Cluster delegate. +std::unique_ptr sPushAvStramTransportInstance; +// Global pointer to PushAV Stream Transport Server SDK cluster; +std::unique_ptr sPushAvStramTransportClusterServerInstance; + +Protocols::InteractionModel::Status +PushAvStreamTransportManager::AllocatePushTransport(const TransportOptionsDecodeableStruct & transportOptions, + TransportConfigurationStruct & outTransporConfiguration) +{ + PushAvStream stream{ outTransporConfiguration.connectionID, outTransporConfiguration }; + + /*Store the allocated stream persistently*/ + pushavStreams.push_back(stream); + + return Status::Success; +} + +Protocols::InteractionModel::Status PushAvStreamTransportManager::DeallocatePushTransport(const uint16_t connectionID) +{ + pushavStreams.erase(std::remove_if(pushavStreams.begin(), pushavStreams.end(), + [connectionID](const PushAvStream & stream) { return stream.id == connectionID; }), + pushavStreams.end()); + return Status::Success; +} + +Protocols::InteractionModel::Status +PushAvStreamTransportManager::ModifyPushTransport(const uint16_t connectionID, + const TransportOptionsDecodeableStruct & transportOptions) +{ + for (PushAvStream & stream : pushavStreams) + { + if (stream.id == connectionID) + { + ChipLogError(Zcl, "Modified Push AV Stream with ID: %d", connectionID); + return Status::Success; + } + } + ChipLogError(Zcl, "Allocated Push AV Stream with ID: %d not found", connectionID); + return Status::NotFound; +} + +Protocols::InteractionModel::Status PushAvStreamTransportManager::SetTransportStatus(const std::vector connectionIDList, + TransportStatusEnum transportStatus) +{ + for (PushAvStream & stream : pushavStreams) + { + for(uint16_t connectionID : connectionIDList) + { + if (stream.id == connectionID) + { + stream.transportConfig.transportStatus = transportStatus; + ChipLogError(Zcl, "Set Transport Status for Push AV Stream with ID: %d", connectionID); + + } + else + { + return Status::NotFound; + } + } + } + return Status::Success; +} + +Protocols::InteractionModel::Status + PushAvStreamTransportManager::ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, + const Optional & timeControl) +{ + // TODO: Validates the requested stream usage against the camera's resource management and stream priority policies. + return Status::Success; +} + +Protocols::InteractionModel::Status + PushAvStreamTransportManager::FindTransport(const Optional> & connectionID, + DataModel::List & outtransportConfigurations) +{ + configList.clear(); + for (PushAvStream & stream : pushavStreams) + { + if (connectionID.Value().IsNull()) + { + configList.push_back(stream.transportConfig); + } + else if (connectionID.Value().Value() == stream.id) + { + configList.push_back(stream.transportConfig); + + } + } + outtransportConfigurations = DataModel::List(configList.data(),configList.size()); + return Status::Success; +} + +CHIP_ERROR +PushAvStreamTransportManager::ValidateStreamUsage(StreamUsageEnum streamUsage, + const Optional> & videoStreamId, + const Optional> & audioStreamId) +{ + // TODO: Validates the requested stream usage against the camera's resource management and stream priority policies. + return CHIP_NO_ERROR; +} + +void PushAvStreamTransportManager::OnAttributeChanged(AttributeId attributeId) +{ + ChipLogProgress(Zcl, "Attribute changed for AttributeId = " ChipLogFormatMEI, ChipLogValueMEI(attributeId)); +} + +void PushAvStreamTransportManager::Init() +{ + return; +} +CHIP_ERROR PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) +{ + ChipLogError(Zcl, "Push AV Current Connections loaded"); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +PushAvStreamTransportManager::PersistentAttributesLoadedCallback() +{ + ChipLogError(Zcl, "Persistent attributes loaded"); + + return CHIP_NO_ERROR; +} + +void emberAfPushAvStreamTransportClusterInitCallback(EndpointId endpoint) +{ + VerifyOrReturn( + endpoint == 1, // this cluster is only enabled for endpoint 1. + ChipLogError(Zcl, "Push AV Stream Transport cluster delegate is not implemented for endpoint with id %d.", endpoint)); + + VerifyOrReturn(!sPushAvStramTransportInstance && !sPushAvStramTransportClusterServerInstance); + + sPushAvStramTransportInstance = std::make_unique(); + sPushAvStramTransportInstance->Init(); + + sPushAvStramTransportClusterServerInstance = + std::make_unique(endpoint, *sPushAvStramTransportInstance.get()); + sPushAvStramTransportClusterServerInstance->Init(); +} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 1647cffbf392b6..14aab15d754547 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -55,6 +55,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/oven-modes.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/oven-operational-state-delegate.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/power-topology-stub.cpp", + "${chip_root}/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/resource-monitoring-delegates.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/rvc-modes.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/rvc-operational-state-delegate-impl.cpp", diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 846827c7663b49..ccdf551fc9e267 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -468,7 +468,6 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/closure-control-cluster-objects.h", ] } else if (cluster == "camera-av-stream-management-server") { - } else if (cluster == "push-av-stream-transport-server") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", "${_app_root}/clusters/${cluster}/${cluster}.h", diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index af678f6f9edd59..ec533383a404ae 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -45,8 +45,8 @@ namespace Clusters { namespace PushAvStreamTransport { PushAvStreamTransportServer::PushAvStreamTransportServer(EndpointId aEndpointId, PushAvStreamTransportDelegate & aDelegate) : - CommandHandlerInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), - AttributeAccessInterface(MakeOptional(aEndpointId), CameraAvStreamManagement::Id), mDelegate(aDelegate) + CommandHandlerInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), + AttributeAccessInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), mDelegate(aDelegate) {} PushAvStreamTransportServer::~PushAvStreamTransportServer() @@ -396,7 +396,7 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, } // Call the delegate - status = mDelegate.SetTransportStatus(connectionIDList); + status = mDelegate.SetTransportStatus(connectionIDList, transportStatus); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 569785322986d4..939b9eb813c2d4 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -29,7 +29,6 @@ namespace app { namespace Clusters { namespace PushAvStreamTransport { -using MetadataOptionsStruct = Structs::MetadataOptionsStruct::Type; using CMAFContainerOptionsStruct = Structs::CMAFContainerOptionsStruct::Type; using ContainerOptionsStruct = Structs::ContainerOptionsStruct::Type; using TransportZoneOptionsStruct = Structs::TransportZoneOptionsStruct::Type; @@ -92,11 +91,12 @@ class PushAvStreamTransportDelegate * @brief Handle Command Delegate for Stream transport modification. * * @param connectionIDList [in] represent the list of connectionIDs for which new transport status to apply. + * @param transportStatus [in] represents the updated status of the connection(s). * * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with an * appropriate error. */ - virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList) = 0; + virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus) = 0; /** * @brief Handle Command Delegate to request the Node to manually start the specified push transport. * diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index c623416002f78e..69f80c927e3010 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -748,7 +748,10 @@ "StatusLightBrightness", "FeatureMap" ], - "Camera AV Settings User Level Management": ["MPTZPosition"] + "Camera AV Settings User Level Management": ["MPTZPosition"], + "Push AV Stream Transport" :[ + "CurrentConnections" + ] }, "mandatoryDeviceTypes": "0x0016", "defaultReportingPolicy": "mandatory", diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index 7e32b1a2987302..3a0754247fcab1 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -742,7 +742,10 @@ "StatusLightBrightness", "FeatureMap" ], - "Camera AV Settings User Level Management": ["MPTZPosition"] + "Camera AV Settings User Level Management": ["MPTZPosition"], + "Push AV Stream Transport" :[ + "CurrentConnections" + ] }, "mandatoryDeviceTypes": "0x0016", "defaultReportingPolicy": "mandatory", diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 61bebcbc4720a2..0fa4bbd7bd781e 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -7799,42 +7799,6 @@ bool emberAfZoneManagementClusterGetTwoDCartesianZoneCallback( bool emberAfZoneManagementClusterRemoveZoneCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::ZoneManagement::Commands::RemoveZone::DecodableType & commandData); -/** - * @brief Push AV Stream Transport Cluster AllocatePushTransport Command callback (from client) - */ -bool emberAfPushAvStreamTransportClusterAllocatePushTransportCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::PushAvStreamTransport::Commands::AllocatePushTransport::DecodableType & commandData); -/** - * @brief Push AV Stream Transport Cluster DeallocatePushTransport Command callback (from client) - */ -bool emberAfPushAvStreamTransportClusterDeallocatePushTransportCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::PushAvStreamTransport::Commands::DeallocatePushTransport::DecodableType & commandData); -/** - * @brief Push AV Stream Transport Cluster ModifyPushTransport Command callback (from client) - */ -bool emberAfPushAvStreamTransportClusterModifyPushTransportCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::PushAvStreamTransport::Commands::ModifyPushTransport::DecodableType & commandData); -/** - * @brief Push AV Stream Transport Cluster SetTransportStatus Command callback (from client) - */ -bool emberAfPushAvStreamTransportClusterSetTransportStatusCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::PushAvStreamTransport::Commands::SetTransportStatus::DecodableType & commandData); -/** - * @brief Push AV Stream Transport Cluster ManuallyTriggerTransport Command callback (from client) - */ -bool emberAfPushAvStreamTransportClusterManuallyTriggerTransportCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::PushAvStreamTransport::Commands::ManuallyTriggerTransport::DecodableType & commandData); -/** - * @brief Push AV Stream Transport Cluster FindTransport Command callback (from client) - */ -bool emberAfPushAvStreamTransportClusterFindTransportCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::PushAvStreamTransport::Commands::FindTransport::DecodableType & commandData); /** * @brief Commodity Tariff Cluster GetTariffComponent Command callback (from client) */ From e92ecc2f8208f37310aea411f65a61dc2faf93f0 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Fri, 25 Apr 2025 09:17:11 +0000 Subject: [PATCH 09/40] Restyled by clang-format --- .../push-av-stream-transport-delegate-impl.h | 8 ++++---- .../push-av-stream-transport-delegate-impl.cpp | 16 +++++++--------- .../push-av-stream-transport-server.h | 3 ++- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index 4625ad622d3b0e..3de7cc1f3e5631 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -44,7 +44,8 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID); Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, const TransportOptionsDecodeableStruct & transportOptions); - Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus); + Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, + TransportStatusEnum transportStatus); Protocols::InteractionModel::Status ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, @@ -53,9 +54,8 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate FindTransport(const Optional> & connectionID, DataModel::List & outtransportConfigurations); - CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, - const Optional> & videoStreamId, - const Optional> & audioStreamId); + CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, const Optional> & videoStreamId, + const Optional> & audioStreamId); void OnAttributeChanged(AttributeId attributeId); CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index a493ff1f504719..d070384aa6d7ac 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -78,13 +78,12 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::SetTransportSt { for (PushAvStream & stream : pushavStreams) { - for(uint16_t connectionID : connectionIDList) + for (uint16_t connectionID : connectionIDList) { if (stream.id == connectionID) { stream.transportConfig.transportStatus = transportStatus; ChipLogError(Zcl, "Set Transport Status for Push AV Stream with ID: %d", connectionID); - } else { @@ -95,17 +94,17 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::SetTransportSt return Status::Success; } -Protocols::InteractionModel::Status - PushAvStreamTransportManager::ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, - const Optional & timeControl) +Protocols::InteractionModel::Status PushAvStreamTransportManager::ManuallyTriggerTransport( + const uint16_t connectionID, TriggerActivationReasonEnum activationReason, + const Optional & timeControl) { // TODO: Validates the requested stream usage against the camera's resource management and stream priority policies. return Status::Success; } Protocols::InteractionModel::Status - PushAvStreamTransportManager::FindTransport(const Optional> & connectionID, - DataModel::List & outtransportConfigurations) +PushAvStreamTransportManager::FindTransport(const Optional> & connectionID, + DataModel::List & outtransportConfigurations) { configList.clear(); for (PushAvStream & stream : pushavStreams) @@ -117,10 +116,9 @@ Protocols::InteractionModel::Status else if (connectionID.Value().Value() == stream.id) { configList.push_back(stream.transportConfig); - } } - outtransportConfigurations = DataModel::List(configList.data(),configList.size()); + outtransportConfigurations = DataModel::List(configList.data(), configList.size()); return Status::Success; } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 939b9eb813c2d4..890f056a461cd6 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -96,7 +96,8 @@ class PushAvStreamTransportDelegate * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with an * appropriate error. */ - virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus) = 0; + virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, + TransportStatusEnum transportStatus) = 0; /** * @brief Handle Command Delegate to request the Node to manually start the specified push transport. * From d26464a4629750f7be6ad06fe089d6a9548f90a0 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Fri, 25 Apr 2025 09:17:18 +0000 Subject: [PATCH 10/40] Restyled by prettier-json --- src/app/zap-templates/zcl/zcl-with-test-extensions.json | 4 +--- src/app/zap-templates/zcl/zcl.json | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index 39d89eca8066b2..0b34ef3a59486d 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -761,9 +761,7 @@ "FeatureMap" ], "Camera AV Settings User Level Management": ["MPTZPosition"], - "Push AV Stream Transport" :[ - "CurrentConnections" - ] + "Push AV Stream Transport": ["CurrentConnections"] }, "mandatoryDeviceTypes": "0x0016", "defaultReportingPolicy": "mandatory", diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index 61c3847ace373d..e18c7732bc3c5e 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -755,9 +755,7 @@ "FeatureMap" ], "Camera AV Settings User Level Management": ["MPTZPosition"], - "Push AV Stream Transport" :[ - "CurrentConnections" - ] + "Push AV Stream Transport": ["CurrentConnections"] }, "mandatoryDeviceTypes": "0x0016", "defaultReportingPolicy": "mandatory", From 2bf0317740e183d99b32ac1e5c995614ca52b363 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Sat, 26 Apr 2025 01:36:39 +0530 Subject: [PATCH 11/40] all-clusters-app build fix --- .../src/push-av-stream-transport-delegate-impl.cpp | 2 +- .../push-av-stream-transport-server.cpp | 14 +++++++------- .../push-av-stream-transport-server.h | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index d070384aa6d7ac..752a227b3c002a 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -167,6 +167,6 @@ void emberAfPushAvStreamTransportClusterInitCallback(EndpointId endpoint) sPushAvStramTransportInstance->Init(); sPushAvStramTransportClusterServerInstance = - std::make_unique(endpoint, *sPushAvStramTransportInstance.get()); + std::make_unique(*sPushAvStramTransportInstance.get(), endpoint); sPushAvStramTransportClusterServerInstance->Init(); } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index ec533383a404ae..832050a2853f4a 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -44,9 +44,10 @@ namespace app { namespace Clusters { namespace PushAvStreamTransport { -PushAvStreamTransportServer::PushAvStreamTransportServer(EndpointId aEndpointId, PushAvStreamTransportDelegate & aDelegate) : +PushAvStreamTransportServer::PushAvStreamTransportServer(PushAvStreamTransportDelegate & aDelegate, EndpointId aEndpointId) : + AttributeAccessInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), CommandHandlerInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), - AttributeAccessInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), mDelegate(aDelegate) + mDelegate(aDelegate) {} PushAvStreamTransportServer::~PushAvStreamTransportServer() @@ -251,7 +252,6 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c Commands::AllocatePushTransportResponse::Type response; auto & transportOptions = commandData.transportOptions; - FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); uint16_t ep = emberAfGetClusterServerEndpointIndex(transportOptions.endpointID, TlsCertificateManagement::Id, MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_CLIENT_ENDPOINT_COUNT); @@ -420,9 +420,9 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( if (transportConfiguration->transportStatus == TransportStatusEnum::kInactive) { - auto status = static_cast(StatusCodeEnum::kInvalidTransportStatus); + auto clusterStatus = static_cast(StatusCodeEnum::kInvalidTransportStatus); ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Transport status"); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; } if (transportConfiguration->transportOptions.HasValue()) @@ -430,9 +430,9 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kContinuous) { { - auto status = static_cast(StatusCodeEnum::kInvalidTriggerType); + auto clusterStatus = static_cast(StatusCodeEnum::kInvalidTriggerType); ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Trigger type"); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; } } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 890f056a461cd6..4fcfd5f1a6a1e7 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -171,17 +171,17 @@ appropriate virtual CHIP_ERROR PersistentAttributesLoadedCallback() = 0; }; -class PushAvStreamTransportServer : private AttributeAccessInterface, private CommandHandlerInterface +class PushAvStreamTransportServer : public AttributeAccessInterface, public CommandHandlerInterface { public: /** * Creates a Push AV Stream Transport server instance. The Init() function needs to be called for this instance to be registered * and called by the interaction model at the appropriate times. - * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. * @param aDelegate A reference to the delegate to be used by this server. + * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. * Note: the caller must ensure that the delegate lives throughout the instance's lifetime. */ - PushAvStreamTransportServer(EndpointId endpointId, PushAvStreamTransportDelegate & delegate); + PushAvStreamTransportServer(PushAvStreamTransportDelegate & delegate, EndpointId endpointId); ~PushAvStreamTransportServer() override; From e6a206007a3c7cdad12bbbe5d0c1ffb05d0fe4ec Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Fri, 25 Apr 2025 20:26:26 +0000 Subject: [PATCH 12/40] Restyled by clang-format --- .../push-av-stream-transport-server.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 832050a2853f4a..2b1245a47feec3 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -46,8 +46,7 @@ namespace PushAvStreamTransport { PushAvStreamTransportServer::PushAvStreamTransportServer(PushAvStreamTransportDelegate & aDelegate, EndpointId aEndpointId) : AttributeAccessInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), - CommandHandlerInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), - mDelegate(aDelegate) + CommandHandlerInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), mDelegate(aDelegate) {} PushAvStreamTransportServer::~PushAvStreamTransportServer() @@ -252,8 +251,8 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c Commands::AllocatePushTransportResponse::Type response; auto & transportOptions = commandData.transportOptions; - uint16_t ep = emberAfGetClusterServerEndpointIndex(transportOptions.endpointID, TlsCertificateManagement::Id, - MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_CLIENT_ENDPOINT_COUNT); + uint16_t ep = emberAfGetClusterServerEndpointIndex(transportOptions.endpointID, TlsCertificateManagement::Id, + MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_CLIENT_ENDPOINT_COUNT); if (ep == kEmberInvalidEndpointIndex) { From ece1e97bf8a06f2312a963487ad954866c156248 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Sat, 26 Apr 2025 02:16:05 +0530 Subject: [PATCH 13/40] ci fix to include vector --- scripts/tools/check_includes_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/tools/check_includes_config.py b/scripts/tools/check_includes_config.py index 94b3742ca6d873..c515e09344bad5 100644 --- a/scripts/tools/check_includes_config.py +++ b/scripts/tools/check_includes_config.py @@ -142,6 +142,7 @@ 'src/app/clusters/camera-av-stream-management-server/camera-av-stream-management-server.h': {'vector'}, 'src/app/clusters/camera-av-settings-user-level-management-server/camera-av-settings-user-level-management-server.h': {'string', 'vector'}, 'src/app/clusters/webrtc-transport-requestor-server/webrtc-transport-requestor-server.h': {'string', 'vector'}, + 'src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h': {'vector'}, 'src/credentials/attestation_verifier/FileAttestationTrustStore.h': {'vector'}, 'src/credentials/attestation_verifier/FileAttestationTrustStore.cpp': {'string'}, 'src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.cpp': {'fstream'}, From d27306db9d1a62f3b1ed4c2337c5234b90f812f1 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Sat, 26 Apr 2025 11:02:22 +0530 Subject: [PATCH 14/40] update all-cluster-app to spec changes --- .../all-clusters-app.matter | 6 ++-- ...push-av-stream-transport-delegate-impl.cpp | 2 +- .../push-av-stream-transport-server.cpp | 29 +++++++++++-------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index da4959992e80b3..bd72eea6e01757 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -7508,11 +7508,10 @@ provisional cluster PushAvStreamTransport = 1365 { optional epoch_s expiryTime = 9; } - fabric_scoped struct TransportConfigurationStruct { + struct TransportConfigurationStruct { int16u connectionID = 0; TransportStatusEnum transportStatus = 1; optional TransportOptionsStruct transportOptions = 2; - fabric_idx fabricIndex = 254; } info event PushTransportBegin = 0 { @@ -7691,11 +7690,10 @@ provisional cluster PushAvStreamTransport = 1365 { optional epoch_s expiryTime = 9; } - fabric_scoped struct TransportConfigurationStruct { + struct TransportConfigurationStruct { int16u connectionID = 0; TransportStatusEnum transportStatus = 1; optional TransportOptionsStruct transportOptions = 2; - fabric_idx fabricIndex = 254; } info event PushTransportBegin = 0 { diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index 752a227b3c002a..e1fb924d986cbb 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -138,7 +138,7 @@ void PushAvStreamTransportManager::OnAttributeChanged(AttributeId attributeId) void PushAvStreamTransportManager::Init() { - return; + ChipLogError(Zcl, "Push AV Stream Transport Initialized"); } CHIP_ERROR PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) { diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 2b1245a47feec3..1de7f24a2b5762 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -231,18 +231,23 @@ TransportConfigurationStruct * PushAvStreamTransportServer::FindStreamTransportC uint16_t PushAvStreamTransportServer::GenerateConnectionID() { static uint16_t lastAssignedConnectionID = 0; - uint16_t nextConnectionID; - do { + uint16_t nextConnectionID; if (lastAssignedConnectionID == MAX_PUSH_TRANSPORT_CONNECTION_ID) + { nextConnectionID = 0; + } else - nextConnectionID = lastAssignedConnectionID + 1; - } while (FindStreamTransportConnection(nextConnectionID)); - - lastAssignedConnectionID = nextConnectionID; - return nextConnectionID; + { + nextConnectionID = static_cast(lastAssignedConnectionID + 1); + } + lastAssignedConnectionID = nextConnectionID; + if (FindStreamTransportConnection(nextConnectionID) == nullptr) + { + return nextConnectionID; + } + } while (true); } void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, @@ -257,7 +262,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (ep == kEmberInvalidEndpointIndex) { auto status = static_cast(StatusCodeEnum::kInvalidTLSEndpoint); - ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid TLSEndpointId not found"); + ChipLogError(Zcl, "HandleAllocatePushTransport: Valid TLSEndpointId not found"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } @@ -331,7 +336,7 @@ void PushAvStreamTransportServer::HandleDeallocatePushTransport( Status status = Status::Success; uint16_t connectionID = commandData.connectionID; - if (!FindStreamTransportConnection(connectionID)) + if (FindStreamTransportConnection(connectionID) == nullptr) { ChipLogError(Zcl, "HandleDeallocatePushTransport: ConnectionID Not Found."); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); @@ -355,7 +360,7 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx uint16_t connectionID = commandData.connectionID; auto & transportOptions = commandData.transportOptions; - if (!FindStreamTransportConnection(connectionID)) + if (FindStreamTransportConnection(connectionID) == nullptr) { ChipLogError(Zcl, "HandleModifyPushTransport: ConnectionID Not Found."); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); @@ -383,7 +388,7 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, connectionIDList.push_back(transportConnection.connectionID); } } - else if (!FindStreamTransportConnection(connectionID.Value())) + else if (FindStreamTransportConnection(connectionID.Value()) == nullptr) { ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); @@ -410,7 +415,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( TransportConfigurationStruct * transportConfiguration = FindStreamTransportConnection(connectionID); - if (!transportConfiguration) + if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleManuallyTriggerTransport: ConnectionID Not Found."); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); From 11f781a14ec1ce010b4182f2d9ef06d9c063cbf6 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Mon, 28 Apr 2025 01:59:17 +0530 Subject: [PATCH 15/40] Address review comments --- .../push-av-stream-transport-delegate-impl.h | 10 +- ...push-av-stream-transport-delegate-impl.cpp | 44 +++- .../push-av-stream-transport-server.cpp | 210 ++++++++++++++---- .../push-av-stream-transport-server.h | 53 ++++- .../zcl/zcl-with-test-extensions.json | 3 +- src/app/zap-templates/zcl/zcl.json | 3 +- 6 files changed, 247 insertions(+), 76 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index 3de7cc1f3e5631..bb79d361fa7cae 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -31,6 +31,7 @@ struct PushAvStream { uint16_t id; TransportConfigurationStruct transportConfig; + PushAvStreamTransportStatusEnum status; }; /** @@ -50,15 +51,16 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate Protocols::InteractionModel::Status ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, const Optional & timeControl); - Protocols::InteractionModel::Status - FindTransport(const Optional> & connectionID, - DataModel::List & outtransportConfigurations); + Protocols::InteractionModel::Status FindTransport(const Optional> & connectionID); CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, const Optional> & videoStreamId, const Optional> & audioStreamId); + CHIP_ERROR ValidateBandwidthLimit(StreamUsageEnum streamUsage, const Optional> & videoStreamId, + const Optional> & audioStreamId); + PushAvStreamTransportStatusEnum GetTransportStatus(const uint16_t connectionID); void OnAttributeChanged(AttributeId attributeId); - CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); + CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); CHIP_ERROR PersistentAttributesLoadedCallback(); void Init(); diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index e1fb924d986cbb..0b881a3af942f1 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -41,7 +41,7 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::AllocatePushTransport(const TransportOptionsDecodeableStruct & transportOptions, TransportConfigurationStruct & outTransporConfiguration) { - PushAvStream stream{ outTransporConfiguration.connectionID, outTransporConfiguration }; + PushAvStream stream{ outTransporConfiguration.connectionID, outTransporConfiguration, PushAvStreamTransportStatusEnum::kIdle }; /*Store the allocated stream persistently*/ pushavStreams.push_back(stream); @@ -85,10 +85,6 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::SetTransportSt stream.transportConfig.transportStatus = transportStatus; ChipLogError(Zcl, "Set Transport Status for Push AV Stream with ID: %d", connectionID); } - else - { - return Status::NotFound; - } } } return Status::Success; @@ -99,12 +95,19 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::ManuallyTrigge const Optional & timeControl) { // TODO: Validates the requested stream usage against the camera's resource management and stream priority policies. + for (PushAvStream & stream : pushavStreams) + { + if (stream.id == connectionID) + { + stream.status = PushAvStreamTransportStatusEnum::kBusy; + ChipLogError(Zcl, "Transport triggered for Push AV Stream with ID: %d", connectionID); + } + } return Status::Success; } Protocols::InteractionModel::Status -PushAvStreamTransportManager::FindTransport(const Optional> & connectionID, - DataModel::List & outtransportConfigurations) +PushAvStreamTransportManager::FindTransport(const Optional> & connectionID) { configList.clear(); for (PushAvStream & stream : pushavStreams) @@ -115,20 +118,40 @@ PushAvStreamTransportManager::FindTransport(const Optional(configList.data(), configList.size()); return Status::Success; } +CHIP_ERROR PushAvStreamTransportManager::ValidateBandwidthLimit(StreamUsageEnum streamUsage, + const Optional> & videoStreamId, + const Optional> & audioStreamId) +{ + // TODO: Validates the requested stream usage against the camera's resource management. + return CHIP_ERROR_NOT_IMPLEMENTED; +} + CHIP_ERROR PushAvStreamTransportManager::ValidateStreamUsage(StreamUsageEnum streamUsage, const Optional> & videoStreamId, const Optional> & audioStreamId) { // TODO: Validates the requested stream usage against the camera's resource management and stream priority policies. - return CHIP_NO_ERROR; + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +PushAvStreamTransportStatusEnum PushAvStreamTransportManager::GetTransportStatus(const uint16_t connectionID) +{ + for (PushAvStream & stream : pushavStreams) + { + if (stream.id == connectionID) + { + return stream.status; + } + } + return PushAvStreamTransportStatusEnum::kUnknown; } void PushAvStreamTransportManager::OnAttributeChanged(AttributeId attributeId) @@ -140,7 +163,8 @@ void PushAvStreamTransportManager::Init() { ChipLogError(Zcl, "Push AV Stream Transport Initialized"); } -CHIP_ERROR PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) +CHIP_ERROR +PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) { ChipLogError(Zcl, "Push AV Current Connections loaded"); diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 1de7f24a2b5762..a79b94010e31d5 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -73,19 +73,20 @@ CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const At { for (const auto & currentConnections : mCurrentConnections) { - ReturnErrorOnFailure(encoder.Encode(currentConnections)); + ReturnErrorOnFailure(encoder.Encode(currentConnections.transportConfiguration)); } return CHIP_NO_ERROR; } -PushAvStreamTransportServer::UpsertResultEnum -PushAvStreamTransportServer::UpsertStreamTransportConnection(const TransportConfigurationStruct & transportConfiguration) +PushAvStreamTransportServer::UpsertResultEnum PushAvStreamTransportServer::UpsertStreamTransportConnection( + const TransportConfigurationStructWithFabricIndex & transportConfiguration) { UpsertResultEnum result; - auto it = - std::find_if(mCurrentConnections.begin(), mCurrentConnections.end(), - [id = transportConfiguration.connectionID](const auto & existing) { return existing.connectionID == id; }); + auto it = std::find_if(mCurrentConnections.begin(), mCurrentConnections.end(), + [id = transportConfiguration.transportConfiguration.connectionID](const auto & existing) { + return existing.transportConfiguration.connectionID == id; + }); if (it != mCurrentConnections.end()) { @@ -110,8 +111,8 @@ void PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t // Erase-Remove idiom mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), - [transportConnectionId](const TransportConfigurationStruct & s) { - return s.connectionID == transportConnectionId; + [transportConnectionId](const TransportConfigurationStructWithFabricIndex & s) { + return s.transportConfiguration.connectionID == transportConnectionId; }), mCurrentConnections.end()); @@ -218,11 +219,12 @@ void PushAvStreamTransportServer::InvokeCommand(HandlerContext & handlerContext) } } -TransportConfigurationStruct * PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) +TransportConfigurationStructWithFabricIndex * +PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) { for (auto & transportConnection : mCurrentConnections) { - if (transportConnection.connectionID == connectionID) + if (transportConnection.transportConfiguration.connectionID == connectionID) return &transportConnection; } return nullptr; @@ -267,6 +269,8 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c return; } + // Todo: Match Fabric index of TLSEndpointID + if (transportOptions.ingestMethod == IngestMethodsEnum::kUnknownEnumValue) { auto status = static_cast(StatusCodeEnum::kUnsupportedIngestMethod); @@ -283,6 +287,9 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c return; } + // Todo: Check combination of Ingest method, Container format in Supported Formats + // https://github.com/CHIP-Specifications/connectedhomeip-spec/pull/11504 + if (transportOptions.triggerOptions.triggerType == TransportTriggerTypeEnum::kUnknownEnumValue) { auto status = static_cast(StatusCodeEnum::kInvalidTriggerType); @@ -292,9 +299,18 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c } // Todo: Validate MotionZones list in the TransportTriggerOptionsStruct field + // Validate Bandwidth Requirement + CHIP_ERROR err = mDelegate.ValidateBandwidthLimit(transportOptions.streamUsage, transportOptions.videoStreamID, + transportOptions.audioStreamID); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "HandleAllocatePushTransport: Resource Exhausted"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); + return; + } // Validate the StreamUsageEnum as per resource management and stream priorities. - CHIP_ERROR err = + err = mDelegate.ValidateStreamUsage(transportOptions.streamUsage, transportOptions.videoStreamID, transportOptions.audioStreamID); if (err != CHIP_NO_ERROR) { @@ -319,7 +335,9 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (status == Status::Success) { // add connection to CurrentConnections - UpsertStreamTransportConnection(outTransportConfiguration); + FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); + TransportConfigurationStructWithFabricIndex transportConfiguration({ outTransportConfiguration, peerFabricIndex }); + UpsertStreamTransportConnection(transportConfiguration); response.transportConfiguration = outTransportConfiguration; ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); @@ -333,10 +351,17 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c void PushAvStreamTransportServer::HandleDeallocatePushTransport( HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & commandData) { - Status status = Status::Success; - uint16_t connectionID = commandData.connectionID; + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + TransportConfigurationStructWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); + if (transportConfiguration == nullptr) + { + ChipLogError(Zcl, "HandleDeallocatePushTransport: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } - if (FindStreamTransportConnection(connectionID) == nullptr) + if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) { ChipLogError(Zcl, "HandleDeallocatePushTransport: ConnectionID Not Found."); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); @@ -360,16 +385,55 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx uint16_t connectionID = commandData.connectionID; auto & transportOptions = commandData.transportOptions; - if (FindStreamTransportConnection(connectionID) == nullptr) + TransportConfigurationStructWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); + + if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleModifyPushTransport: ConnectionID Not Found."); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } + if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) + { + ChipLogError(Zcl, "HandleModifyPushTransport: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + + if (mDelegate.GetTransportStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) + { + ChipLogError(Zcl, "HandleModifyPushTransport: Connection is Busy"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Busy); + return; + } + // Call the delegate status = mDelegate.ModifyPushTransport(connectionID, transportOptions); + if (status == Status::Success) + { + if (transportConfiguration->transportConfiguration.transportOptions.HasValue()) + { + TransportOptionsStruct transportOptionToModify = + transportConfiguration->transportConfiguration.transportOptions.Value(); + transportOptionToModify.streamUsage = transportOptions.streamUsage; + transportOptionToModify.videoStreamID = transportOptions.videoStreamID; + transportOptionToModify.audioStreamID = transportOptions.audioStreamID; + transportOptionToModify.endpointID = transportOptions.endpointID; + transportOptionToModify.url = transportOptions.url; + transportOptionToModify.triggerOptions.triggerType = transportOptions.triggerOptions.triggerType; + // Todo: copy motion zones + transportOptionToModify.triggerOptions.motionSensitivity = transportOptions.triggerOptions.motionSensitivity; + transportOptionToModify.triggerOptions.motionTimeControl = transportOptions.triggerOptions.motionTimeControl; + transportOptionToModify.triggerOptions.maxPreRollLen = transportOptions.triggerOptions.maxPreRollLen; + transportOptionToModify.ingestMethod = transportOptions.ingestMethod; + transportOptionToModify.containerFormat = transportOptions.containerFormat; + transportOptionToModify.containerOptions = transportOptions.containerOptions; + transportOptionToModify.expiryTime = transportOptions.expiryTime; + } + } + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } @@ -385,23 +449,34 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, { for (auto & transportConnection : mCurrentConnections) { - connectionIDList.push_back(transportConnection.connectionID); + if (transportConnection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) + { + transportConnection.transportConfiguration.transportStatus = transportStatus; + connectionIDList.push_back(transportConnection.transportConfiguration.connectionID); + } } } - else if (FindStreamTransportConnection(connectionID.Value()) == nullptr) - { - ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } else { + TransportConfigurationStructWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID.Value()); + if (transportConfiguration == nullptr) + { + ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) + { + ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + + transportConfiguration->transportConfiguration.transportStatus = transportStatus; connectionIDList.push_back(connectionID.Value()); } - // Call the delegate status = mDelegate.SetTransportStatus(connectionIDList, transportStatus); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } @@ -413,7 +488,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( auto & activationReason = commandData.activationReason; auto & timeControl = commandData.timeControl; - TransportConfigurationStruct * transportConfiguration = FindStreamTransportConnection(connectionID); + TransportConfigurationStructWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); if (transportConfiguration == nullptr) { @@ -422,39 +497,51 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( return; } - if (transportConfiguration->transportStatus == TransportStatusEnum::kInactive) + if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) + { + ChipLogError(Zcl, "HandleManuallyTriggerTransport: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + + if (mDelegate.GetTransportStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) + { + ChipLogError(Zcl, "HandleManuallyTriggerTransport: Connection is Busy"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Busy); + return; + } + + if (transportConfiguration->transportConfiguration.transportStatus == TransportStatusEnum::kInactive) { auto clusterStatus = static_cast(StatusCodeEnum::kInvalidTransportStatus); ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Transport status"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; } - if (transportConfiguration->transportOptions.HasValue()) + if (transportConfiguration->transportConfiguration.transportOptions.HasValue()) { - if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kContinuous) + if (transportConfiguration->transportConfiguration.transportOptions.Value().triggerOptions.triggerType == + TransportTriggerTypeEnum::kContinuous) { - { - auto clusterStatus = static_cast(StatusCodeEnum::kInvalidTriggerType); - ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Trigger type"); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); - return; - } + + auto clusterStatus = static_cast(StatusCodeEnum::kInvalidTriggerType); + ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Trigger type"); + ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); + return; } - else if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == - TransportTriggerTypeEnum::kCommand && - !timeControl.HasValue()) + if (transportConfiguration->transportConfiguration.transportOptions.Value().triggerOptions.triggerType == + TransportTriggerTypeEnum::kCommand && + !timeControl.HasValue()) { - { - ChipLogError(Zcl, "HandleManuallyTriggerTransport: Time control field not present"); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - return; - } + + ChipLogError(Zcl, "HandleManuallyTriggerTransport: Time control field not present"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + return; } } // Call the delegate status = mDelegate.ManuallyTriggerTransport(connectionID, activationReason, timeControl); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } @@ -466,6 +553,8 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, Optional> connectionID = commandData.connectionID; + static std::vector transportConfigurations{}; + DataModel::List outTransportConfigurations; if (connectionID.Value().IsNull()) @@ -476,16 +565,39 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } + + for (auto & connection : mCurrentConnections) + { + if (connection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) + { + transportConfigurations.push_back(connection.transportConfiguration); + } + } } - else if (!FindStreamTransportConnection(connectionID.Value().Value())) + else { - ChipLogError(Zcl, "HandleFindTransport: ConnectionID not found"); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; + TransportConfigurationStructWithFabricIndex * transportConfiguration = + FindStreamTransportConnection(connectionID.Value().Value()); + if (transportConfiguration == nullptr) + { + ChipLogError(Zcl, "HandleFindTransport: ConnectionID not found"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) + { + ChipLogError(Zcl, "HandleFindTransport: ConnectionID Not Found."); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + return; + } + transportConfigurations.push_back(transportConfiguration->transportConfiguration); } + outTransportConfigurations = + DataModel::List(transportConfigurations.data(), transportConfigurations.size()); + // Call the delegate - status = mDelegate.FindTransport(connectionID, outTransportConfigurations); + status = mDelegate.FindTransport(connectionID); if (status == Status::Success) { response.transportConfigurations = outTransportConfigurations; diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 4fcfd5f1a6a1e7..a79fe0a182a41e 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -39,6 +39,19 @@ using TransportConfigurationStruct = Structs::TransportConfigurationS using TransportOptionsDecodeableStruct = Structs::TransportOptionsStruct::DecodableType; +struct TransportConfigurationStructWithFabricIndex +{ + TransportConfigurationStruct transportConfiguration; + chip::FabricIndex fabricIndex; +}; + +enum class PushAvStreamTransportStatusEnum : uint8_t +{ + kBusy = 0x00, + kIdle = 0x01, + kUnknown = 0x02 +}; + /** @brief * Defines interfaces for implementing application-specific logic for various aspects of the PushAvStreamTransport Delegate. * Specifically, it defines interfaces for the command handling and loading of the allocated streams. @@ -119,17 +132,29 @@ appropriate * @brief Handle Command Delegate to get the Stream Options Configuration for the specified push transport. * * @param connectionID [in] Indicates the allocated connectionID to get the Stream Options Configuration of. - * - * @param outtransportConfigurations [out] Single item list of mapped transport configuration or list if connectionID is - * NULL. - * * @return Success if the transport is already allocated; otherwise, the command SHALL be rejected with an appropriate * error. * */ - virtual Protocols::InteractionModel::Status - FindTransport(const Optional> & connectionID, - DataModel::List & outtransportConfigurations) = 0; + virtual Protocols::InteractionModel::Status FindTransport(const Optional> & connectionID) = 0; + + /** + * @brief Validates the bandwidth requirement against the camera's resource management + * + * The implementation SHALL ensure: + * - The requested stream usage (streamUsage) is allowed given the current allocation of + * camera resources (e.g. CPU, memory, network bandwidth). + * + * @param[in] streamUsage The desired usage type for the stream (e.g. live view, recording, etc.). + * @param[in] videoStreamId Optional identifier for the requested video stream. + * @param[in] audioStreamId Optional identifier for the requested audio stream. + * + * @return CHIP_ERROR CHIP_NO_ERROR if the stream usage is valid; an appropriate error code otherwise. + */ + + virtual CHIP_ERROR ValidateBandwidthLimit(StreamUsageEnum streamUsage, + const Optional> & videoStreamId, + const Optional> & audioStreamId) = 0; /** * @brief Validates the requested stream usage against the camera's resource management @@ -149,6 +174,8 @@ appropriate const Optional> & videoStreamId, const Optional> & audioStreamId) = 0; + virtual PushAvStreamTransportStatusEnum GetTransportStatus(const uint16_t connectionID) = 0; + /** * @brief Delegate callback for notifying change in an attribute. * @@ -162,7 +189,7 @@ appropriate * list, at initialization. Once loaded, the cluster server would be serving Reads on these attributes. The list is updatable * via the Add/Remove functions for the respective transport connections. */ - virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; + virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; /** * @brief Callback into the delegate once persistent attributes managed by @@ -216,10 +243,14 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm PushAvStreamTransportDelegate & mDelegate; // Attributes + // Todo: Add SupportedFormats attribute form https://github.com/CHIP-Specifications/connectedhomeip-spec/pull/11504 BitMask mSupportedContainerFormats; BitMask mSupportedIngestMethods; // lists - std::vector mCurrentConnections; + /*Moved from TransportConfigurationStruct to TransportConfigurationStructWithFabricIndex + * to perform fabric index checks + */ + std::vector mCurrentConnections; /** * IM-level implementation of read @@ -237,9 +268,9 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm // Helper functions uint16_t GenerateConnectionID(); - TransportConfigurationStruct * FindStreamTransportConnection(const uint16_t connectionID); + TransportConfigurationStructWithFabricIndex * FindStreamTransportConnection(const uint16_t connectionID); // Add/Remove Management functions for transport - UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStruct & transportConfiguration); + UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStructWithFabricIndex & transportConfiguration); void RemoveStreamTransportConnection(const uint16_t connectionID); diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index 77da4e9fe7cb7f..18d63c689e4541 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -767,7 +767,8 @@ "FeatureMap" ], "Camera AV Settings User Level Management": ["MPTZPosition"], - "Push AV Stream Transport": ["CurrentConnections"] + "Push AV Stream Transport": ["CurrentConnections"], + "Soil Measurement": ["SoilMoistureMeasurementLimits"] }, "mandatoryDeviceTypes": "0x0016", "defaultReportingPolicy": "mandatory", diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index bb54aef27467bc..c04ab1357a5095 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -760,8 +760,9 @@ "StatusLightBrightness", "FeatureMap" ], + "Push AV Stream Transport": ["CurrentConnections"], "Camera AV Settings User Level Management": ["MPTZPosition"], - "Push AV Stream Transport": ["CurrentConnections"] + "Soil Measurement": ["SoilMoistureMeasurementLimits"] }, "mandatoryDeviceTypes": "0x0016", "defaultReportingPolicy": "mandatory", From 592e3f7c17c07a7da176b8d0bc0b184e6c090949 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Wed, 14 May 2025 17:43:30 +0530 Subject: [PATCH 16/40] Update to latest spec changes --- .../all-clusters-app.matter | 79 ++++---- .../all-clusters-common/all-clusters-app.zap | 56 ++---- .../push-av-stream-transport-delegate-impl.h | 15 +- ...push-av-stream-transport-delegate-impl.cpp | 66 +++---- .../push-av-stream-transport-server/BUILD.gn | 15 ++ .../app_config_dependent_sources.cmake | 21 ++ .../app_config_dependent_sources.gni | 17 ++ .../push-av-stream-transport-server.cpp | 182 ++++++++++-------- .../push-av-stream-transport-server.h | 144 +++++++++++--- .../zcl/zcl-with-test-extensions.json | 1 - src/app/zap-templates/zcl/zcl.json | 1 - 11 files changed, 366 insertions(+), 231 deletions(-) create mode 100644 src/app/clusters/push-av-stream-transport-server/BUILD.gn create mode 100644 src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake create mode 100644 src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.gni diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 682ab5c653c2e8..3bb1668758d8a7 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -7453,7 +7453,7 @@ provisional cluster PushAvStreamTransport = 1365 { revision 1; enum ContainerFormatEnum : enum8 { - kCMAF = 0; + kCMAF = 0 [spec_name = "CMAF"]; } enum IngestMethodsEnum : enum8 { @@ -7465,10 +7465,9 @@ provisional cluster PushAvStreamTransport = 1365 { kInvalidStream = 3; kInvalidURL = 4; kInvalidZone = 5; - kUnsupportedContainerFormat = 6; - kUnsupportedIngestMethod = 7; - kInvalidTriggerType = 8; - kInvalidTransportStatus = 9; + kInvalidCombination = 6; + kInvalidTriggerType = 7; + kInvalidTransportStatus = 8; } shared enum StreamUsageEnum : enum8 { @@ -7500,14 +7499,6 @@ provisional cluster PushAvStreamTransport = 1365 { kMetadata = 0x2; } - bitmap SupportedContainerFormatsBitmap : bitmap8 { - kCMAF = 0x1; - } - - bitmap SupportedIngestMethodsBitmap : bitmap8 { - kCMAFIngest = 0x1; - } - struct TransportMotionTriggerTimeControlStruct { int16u initialDuration = 0; int16u augmentationDuration = 1; @@ -7516,8 +7507,8 @@ provisional cluster PushAvStreamTransport = 1365 { } struct TransportZoneOptionsStruct { - nullable int16u zone = 1; - optional int8u sensitivity = 2; + nullable int16u zone = 0; + optional int8u sensitivity = 1; } struct TransportTriggerOptionsStruct { @@ -7532,6 +7523,7 @@ provisional cluster PushAvStreamTransport = 1365 { int16u chunkDuration = 0; optional octet_string<16> CENCKey = 1; optional boolean metadataEnabled = 2; + optional octet_string<16> CENCKeyID = 3; } struct ContainerOptionsStruct { @@ -7547,9 +7539,8 @@ provisional cluster PushAvStreamTransport = 1365 { long_char_string<2000> url = 4; TransportTriggerOptionsStruct triggerOptions = 5; IngestMethodsEnum ingestMethod = 6; - ContainerFormatEnum containerFormat = 7; - ContainerOptionsStruct containerOptions = 8; - optional epoch_s expiryTime = 9; + ContainerOptionsStruct containerOptions = 7; + optional epoch_s expiryTime = 8; } struct TransportConfigurationStruct { @@ -7558,6 +7549,11 @@ provisional cluster PushAvStreamTransport = 1365 { optional TransportOptionsStruct transportOptions = 2; } + struct SupportedFormatStruct { + ContainerFormatEnum containerFormat = 0; + IngestMethodsEnum ingestMethod = 1; + } + info event PushTransportBegin = 0 { int16u connectionID = 0; TransportTriggerTypeEnum triggerType = 1; @@ -7570,12 +7566,10 @@ provisional cluster PushAvStreamTransport = 1365 { optional TriggerActivationReasonEnum activationReason = 2; } - readonly attribute SupportedContainerFormatsBitmap supportedContainerFormats = 0; - readonly attribute SupportedIngestMethodsBitmap supportedIngestMethods = 1; - readonly attribute TransportConfigurationStruct currentConnections[] = 2; + readonly attribute SupportedFormatStruct supportedFormats[] = 0; + readonly attribute TransportConfigurationStruct currentConnections[] = 1; readonly attribute command_id generatedCommandList[] = 65528; readonly attribute command_id acceptedCommandList[] = 65529; - readonly attribute event_id eventList[] = 65530; readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; @@ -7635,7 +7629,7 @@ provisional cluster PushAvStreamTransport = 1365 { revision 1; enum ContainerFormatEnum : enum8 { - kCMAF = 0; + kCMAF = 0 [spec_name = "CMAF"]; } enum IngestMethodsEnum : enum8 { @@ -7647,10 +7641,9 @@ provisional cluster PushAvStreamTransport = 1365 { kInvalidStream = 3; kInvalidURL = 4; kInvalidZone = 5; - kUnsupportedContainerFormat = 6; - kUnsupportedIngestMethod = 7; - kInvalidTriggerType = 8; - kInvalidTransportStatus = 9; + kInvalidCombination = 6; + kInvalidTriggerType = 7; + kInvalidTransportStatus = 8; } shared enum StreamUsageEnum : enum8 { @@ -7682,14 +7675,6 @@ provisional cluster PushAvStreamTransport = 1365 { kMetadata = 0x2; } - bitmap SupportedContainerFormatsBitmap : bitmap8 { - kCMAF = 0x1; - } - - bitmap SupportedIngestMethodsBitmap : bitmap8 { - kCMAFIngest = 0x1; - } - struct TransportMotionTriggerTimeControlStruct { int16u initialDuration = 0; int16u augmentationDuration = 1; @@ -7698,8 +7683,8 @@ provisional cluster PushAvStreamTransport = 1365 { } struct TransportZoneOptionsStruct { - nullable int16u zone = 1; - optional int8u sensitivity = 2; + nullable int16u zone = 0; + optional int8u sensitivity = 1; } struct TransportTriggerOptionsStruct { @@ -7714,6 +7699,7 @@ provisional cluster PushAvStreamTransport = 1365 { int16u chunkDuration = 0; optional octet_string<16> CENCKey = 1; optional boolean metadataEnabled = 2; + optional octet_string<16> CENCKeyID = 3; } struct ContainerOptionsStruct { @@ -7729,9 +7715,8 @@ provisional cluster PushAvStreamTransport = 1365 { long_char_string<2000> url = 4; TransportTriggerOptionsStruct triggerOptions = 5; IngestMethodsEnum ingestMethod = 6; - ContainerFormatEnum containerFormat = 7; - ContainerOptionsStruct containerOptions = 8; - optional epoch_s expiryTime = 9; + ContainerOptionsStruct containerOptions = 7; + optional epoch_s expiryTime = 8; } struct TransportConfigurationStruct { @@ -7740,6 +7725,11 @@ provisional cluster PushAvStreamTransport = 1365 { optional TransportOptionsStruct transportOptions = 2; } + struct SupportedFormatStruct { + ContainerFormatEnum containerFormat = 0; + IngestMethodsEnum ingestMethod = 1; + } + info event PushTransportBegin = 0 { int16u connectionID = 0; TransportTriggerTypeEnum triggerType = 1; @@ -7752,12 +7742,10 @@ provisional cluster PushAvStreamTransport = 1365 { optional TriggerActivationReasonEnum activationReason = 2; } - readonly attribute SupportedContainerFormatsBitmap supportedContainerFormats = 0; - readonly attribute SupportedIngestMethodsBitmap supportedIngestMethods = 1; - readonly attribute TransportConfigurationStruct currentConnections[] = 2; + readonly attribute SupportedFormatStruct supportedFormats[] = 0; + readonly attribute TransportConfigurationStruct currentConnections[] = 1; readonly attribute command_id generatedCommandList[] = 65528; readonly attribute command_id acceptedCommandList[] = 65529; - readonly attribute event_id eventList[] = 65530; readonly attribute attrib_id attributeList[] = 65531; readonly attribute bitmap32 featureMap = 65532; readonly attribute int16u clusterRevision = 65533; @@ -10183,8 +10171,7 @@ endpoint 1 { } server cluster PushAvStreamTransport { - ram attribute supportedContainerFormats; - ram attribute supportedIngestMethods; + callback attribute supportedFormats; callback attribute currentConnections; callback attribute generatedCommandList; callback attribute acceptedCommandList; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index d3abb0fb7726fb..97dac5e96c8644 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -1582,7 +1582,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21583,32 +21583,16 @@ ], "attributes": [ { - "name": "SupportedContainerFormats", + "name": "SupportedFormats", "code": 0, "mfgCode": null, "side": "server", - "type": "SupportedContainerFormatsBitmap", - "included": 1, - "storageOption": "RAM", - "singleton": 0, - "bounded": 0, - "defaultValue": "", - "reportable": 1, - "minInterval": 1, - "maxInterval": 65534, - "reportableChange": 0 - }, - { - "name": "SupportedIngestMethods", - "code": 1, - "mfgCode": null, - "side": "server", - "type": "SupportedIngestMethodsBitmap", + "type": "array", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21616,7 +21600,7 @@ }, { "name": "CurrentConnections", - "code": 2, + "code": 1, "mfgCode": null, "side": "server", "type": "array", @@ -21877,10 +21861,10 @@ "side": "server", "type": "MeterTypeEnum", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21893,10 +21877,10 @@ "side": "server", "type": "char_string", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21909,10 +21893,10 @@ "side": "server", "type": "char_string", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21925,10 +21909,10 @@ "side": "server", "type": "char_string", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21944,7 +21928,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21960,7 +21944,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21976,7 +21960,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21992,7 +21976,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -22005,10 +21989,10 @@ "side": "server", "type": "bitmap32", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "0", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index bb79d361fa7cae..9a219955f7eac2 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -30,8 +30,9 @@ namespace PushAvStreamTransport { struct PushAvStream { uint16_t id; - TransportConfigurationStruct transportConfig; - PushAvStreamTransportStatusEnum status; + TransportOptionsStruct transportOptions; + TransportStatusEnum transportStatus; + PushAvStreamTransportStatusEnum connectionStatus; }; /** @@ -40,18 +41,19 @@ struct PushAvStream class PushAvStreamTransportManager : public PushAvStreamTransportDelegate { public: - Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsDecodeableStruct & transportOptions, - TransportConfigurationStruct & outTransporConfiguration); + Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, + const uint16_t connectionID); Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID); Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, - const TransportOptionsDecodeableStruct & transportOptions); + const TransportOptionsStruct & transportOptions); Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus); Protocols::InteractionModel::Status ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, const Optional & timeControl); - Protocols::InteractionModel::Status FindTransport(const Optional> & connectionID); + + bool validateUrl(std::string url); CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, const Optional> & videoStreamId, const Optional> & audioStreamId); @@ -70,7 +72,6 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate private: std::vector pushavStreams; - std::vector configList; }; } // namespace PushAvStreamTransport diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index 0b881a3af942f1..0ab561a0f75d59 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -38,10 +38,9 @@ std::unique_ptr sPushAvStramTransportInstance; std::unique_ptr sPushAvStramTransportClusterServerInstance; Protocols::InteractionModel::Status -PushAvStreamTransportManager::AllocatePushTransport(const TransportOptionsDecodeableStruct & transportOptions, - TransportConfigurationStruct & outTransporConfiguration) +PushAvStreamTransportManager::AllocatePushTransport(const TransportOptionsStruct & transportOptions, const uint16_t connectionID) { - PushAvStream stream{ outTransporConfiguration.connectionID, outTransporConfiguration, PushAvStreamTransportStatusEnum::kIdle }; + PushAvStream stream{ connectionID, transportOptions, TransportStatusEnum::kInactive, PushAvStreamTransportStatusEnum::kIdle }; /*Store the allocated stream persistently*/ pushavStreams.push_back(stream); @@ -58,14 +57,13 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::DeallocatePush } Protocols::InteractionModel::Status -PushAvStreamTransportManager::ModifyPushTransport(const uint16_t connectionID, - const TransportOptionsDecodeableStruct & transportOptions) +PushAvStreamTransportManager::ModifyPushTransport(const uint16_t connectionID, const TransportOptionsStruct & transportOptions) { for (PushAvStream & stream : pushavStreams) { if (stream.id == connectionID) { - ChipLogError(Zcl, "Modified Push AV Stream with ID: %d", connectionID); + ChipLogProgress(Zcl, "Modified Push AV Stream with ID: %d", connectionID); return Status::Success; } } @@ -82,8 +80,8 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::SetTransportSt { if (stream.id == connectionID) { - stream.transportConfig.transportStatus = transportStatus; - ChipLogError(Zcl, "Set Transport Status for Push AV Stream with ID: %d", connectionID); + stream.transportStatus = transportStatus; + ChipLogProgress(Zcl, "Set Transport Status for Push AV Stream with ID: %d", connectionID); } } } @@ -99,27 +97,8 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::ManuallyTrigge { if (stream.id == connectionID) { - stream.status = PushAvStreamTransportStatusEnum::kBusy; - ChipLogError(Zcl, "Transport triggered for Push AV Stream with ID: %d", connectionID); - } - } - return Status::Success; -} - -Protocols::InteractionModel::Status -PushAvStreamTransportManager::FindTransport(const Optional> & connectionID) -{ - configList.clear(); - for (PushAvStream & stream : pushavStreams) - { - if (connectionID.Value().IsNull()) - { - configList.push_back(stream.transportConfig); - } - else if (connectionID.Value().Value() == stream.id) - { - ChipLogError(Zcl, "Transport Found for Push AV Stream with ID: %d", connectionID.Value().Value()); - configList.push_back(stream.transportConfig); + stream.connectionStatus = PushAvStreamTransportStatusEnum::kBusy; + ChipLogProgress(Zcl, "Transport triggered for Push AV Stream with ID: %d", connectionID); } } return Status::Success; @@ -130,7 +109,13 @@ CHIP_ERROR PushAvStreamTransportManager::ValidateBandwidthLimit(StreamUsageEnum const Optional> & audioStreamId) { // TODO: Validates the requested stream usage against the camera's resource management. - return CHIP_ERROR_NOT_IMPLEMENTED; + // Returning CHIP_NO_ERROR to pass through checks in the Server Implementation. + return CHIP_NO_ERROR; +} + +bool PushAvStreamTransportManager::validateUrl(std::string url) +{ + return true; } CHIP_ERROR @@ -139,7 +124,8 @@ PushAvStreamTransportManager::ValidateStreamUsage(StreamUsageEnum streamUsage, const Optional> & audioStreamId) { // TODO: Validates the requested stream usage against the camera's resource management and stream priority policies. - return CHIP_ERROR_NOT_IMPLEMENTED; + // Returning CHIP_NO_ERROR to pass through checks in the Server Implementation. + return CHIP_NO_ERROR; } PushAvStreamTransportStatusEnum PushAvStreamTransportManager::GetTransportStatus(const uint16_t connectionID) @@ -148,7 +134,7 @@ PushAvStreamTransportStatusEnum PushAvStreamTransportManager::GetTransportStatus { if (stream.id == connectionID) { - return stream.status; + return stream.connectionStatus; } } return PushAvStreamTransportStatusEnum::kUnknown; @@ -161,12 +147,12 @@ void PushAvStreamTransportManager::OnAttributeChanged(AttributeId attributeId) void PushAvStreamTransportManager::Init() { - ChipLogError(Zcl, "Push AV Stream Transport Initialized"); + ChipLogProgress(Zcl, "Push AV Stream Transport Initialized"); } CHIP_ERROR PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) { - ChipLogError(Zcl, "Push AV Current Connections loaded"); + ChipLogProgress(Zcl, "Push AV Current Connections loaded"); return CHIP_NO_ERROR; } @@ -174,7 +160,7 @@ PushAvStreamTransportManager::LoadCurrentConnections(std::vector(); sPushAvStramTransportInstance->Init(); + BitFlags features; + sPushAvStramTransportClusterServerInstance = - std::make_unique(*sPushAvStramTransportInstance.get(), endpoint); + std::make_unique(*sPushAvStramTransportInstance.get(), endpoint, features); sPushAvStramTransportClusterServerInstance->Init(); } + +void emberAfPushAvStreamTransportClusterShutdownCallback(EndpointId endpoint) +{ + sPushAvStramTransportClusterServerInstance = nullptr; + sPushAvStramTransportInstance = nullptr; +} diff --git a/src/app/clusters/push-av-stream-transport-server/BUILD.gn b/src/app/clusters/push-av-stream-transport-server/BUILD.gn new file mode 100644 index 00000000000000..a309e9b59916ff --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/BUILD.gn @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. +group("push-av-stream-transport-server") { +} diff --git a/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake new file mode 100644 index 00000000000000..f7ada4d5188470 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake @@ -0,0 +1,21 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +# This is the equivalent to app_config_dependent_sources.gni +TARGET_SOURCES( + ${APP_TARGET} + PRIVATE + "${CLUSTER_DIR}/push-av-stream-transport-server.cpp" + "${CLUSTER_DIR}/push-av-stream-transport-server.h" +) \ No newline at end of file diff --git a/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.gni b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.gni new file mode 100644 index 00000000000000..786feca9b86f84 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.gni @@ -0,0 +1,17 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. +app_config_dependent_sources = [ + "push-av-stream-transport-server.cpp", + "push-av-stream-transport-server.h", +] diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index a79b94010e31d5..0d655fd4fe3d1a 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -28,7 +28,7 @@ #include #include -#define MAX_PUSH_TRANSPORT_CONNECTION_ID 65535 +static constexpr uint16_t kMaxConnectionId = 65535; // This is also invalid connectionID using namespace chip; using namespace chip::app; @@ -44,9 +44,11 @@ namespace app { namespace Clusters { namespace PushAvStreamTransport { -PushAvStreamTransportServer::PushAvStreamTransportServer(PushAvStreamTransportDelegate & aDelegate, EndpointId aEndpointId) : +PushAvStreamTransportServer::PushAvStreamTransportServer(PushAvStreamTransportDelegate & aDelegate, EndpointId aEndpointId, + const BitFlags aFeatures) : AttributeAccessInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), - CommandHandlerInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), mDelegate(aDelegate) + CommandHandlerInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), mDelegate(aDelegate), mFeatures(aFeatures), + mSupportedFormats{ SupportedFormatStruct{ ContainerFormatEnum::kCmaf, IngestMethodsEnum::kCMAFIngest } } {} PushAvStreamTransportServer::~PushAvStreamTransportServer() @@ -69,6 +71,21 @@ void PushAvStreamTransportServer::Shutdown() AttributeAccessInterfaceRegistry::Instance().Unregister(this); } +bool PushAvStreamTransportServer::HasFeature(Feature feature) const +{ + return mFeatures.Has(feature); +} + +CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder) +{ + for (const auto & supportsFormat : mSupportedFormats) + { + ReturnErrorOnFailure(encoder.Encode(supportsFormat)); + } + + return CHIP_NO_ERROR; +} + CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder) { for (const auto & currentConnections : mCurrentConnections) @@ -128,13 +145,23 @@ void PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t CHIP_ERROR PushAvStreamTransportServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { VerifyOrDie(aPath.mClusterId == PushAvStreamTransport::Id); - ChipLogError(Zcl, "Push AV Stream Transport[ep=%d]: Reading", AttributeAccessInterface::GetEndpointId().Value()); + ChipLogProgress(Zcl, "Push AV Stream Transport[ep=%d]: Reading", AttributeAccessInterface::GetEndpointId().Value()); - if (aPath.mClusterId == PushAvStreamTransport::Id && aPath.mAttributeId == Attributes::CurrentConnections::Id) + switch (aPath.mAttributeId) { + case FeatureMap::Id: + ReturnErrorOnFailure(aEncoder.Encode(mFeatures)); + break; + case SupportedFormats::Id: + ReturnErrorOnFailure(aEncoder.EncodeList( + [this](const auto & encoder) -> CHIP_ERROR { return this->ReadAndEncodeSupportedFormats(encoder); })); + break; + + case CurrentConnections::Id: ReturnErrorOnFailure(aEncoder.EncodeList( [this](const auto & encoder) -> CHIP_ERROR { return this->ReadAndEncodeCurrentConnections(encoder); })); + break; } return CHIP_NO_ERROR; @@ -232,24 +259,19 @@ PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connec uint16_t PushAvStreamTransportServer::GenerateConnectionID() { - static uint16_t lastAssignedConnectionID = 0; - do + static uint16_t lastID = 0; + + for (uint16_t i = 0; i < kMaxConnectionId; ++i) { - uint16_t nextConnectionID; - if (lastAssignedConnectionID == MAX_PUSH_TRANSPORT_CONNECTION_ID) - { - nextConnectionID = 0; - } - else - { - nextConnectionID = static_cast(lastAssignedConnectionID + 1); - } - lastAssignedConnectionID = nextConnectionID; - if (FindStreamTransportConnection(nextConnectionID) == nullptr) + uint16_t candidateID = static_cast((lastID + i + 1) % kMaxConnectionId); // Wrap from 0 to 65534 + if (FindStreamTransportConnection(candidateID) == nullptr) { - return nextConnectionID; + lastID = candidateID; + return candidateID; } - } while (true); + } + + return kMaxConnectionId; // All 0 to 65534 IDs are in use } void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, @@ -258,38 +280,38 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c Commands::AllocatePushTransportResponse::Type response; auto & transportOptions = commandData.transportOptions; - uint16_t ep = emberAfGetClusterServerEndpointIndex(transportOptions.endpointID, TlsCertificateManagement::Id, - MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_CLIENT_ENDPOINT_COUNT); + // Todo: TLSEndpointID Validation - if (ep == kEmberInvalidEndpointIndex) + bool isFormatSupported = false; + IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; + ContainerOptionsStruct containerOptions = commandData.transportOptions.containerOptions; + + for (auto & supportsFormat : mSupportedFormats) { - auto status = static_cast(StatusCodeEnum::kInvalidTLSEndpoint); - ChipLogError(Zcl, "HandleAllocatePushTransport: Valid TLSEndpointId not found"); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); - return; + if ((supportsFormat.ingestMethod == ingestMethod) && (supportsFormat.containerFormat == containerOptions.containerType)) + { + isFormatSupported = true; + } } - // Todo: Match Fabric index of TLSEndpointID - - if (transportOptions.ingestMethod == IngestMethodsEnum::kUnknownEnumValue) + if (isFormatSupported == false) { - auto status = static_cast(StatusCodeEnum::kUnsupportedIngestMethod); - ChipLogError(Zcl, "HandleAllocatePushTransport: Ingest method not supported"); + auto status = static_cast(StatusCodeEnum::kInvalidCombination); + ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Format Combination"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } - if (transportOptions.containerFormat == ContainerFormatEnum::kUnknownEnumValue) + bool isValidUrl = mDelegate.validateUrl(std::string(transportOptions.url.data(), transportOptions.url.size())); + + if (isValidUrl == false) { - auto status = static_cast(StatusCodeEnum::kUnsupportedContainerFormat); - ChipLogError(Zcl, "HandleAllocatePushTransport: Container format not supported"); + auto status = static_cast(StatusCodeEnum::kInvalidURL); + ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Url"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } - // Todo: Check combination of Ingest method, Container format in Supported Formats - // https://github.com/CHIP-Specifications/connectedhomeip-spec/pull/11504 - if (transportOptions.triggerOptions.triggerType == TransportTriggerTypeEnum::kUnknownEnumValue) { auto status = static_cast(StatusCodeEnum::kInvalidTriggerType); @@ -322,20 +344,25 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c uint16_t connectionID = GenerateConnectionID(); + if (connectionID == kMaxConnectionId) + { + ChipLogError(Zcl, "HandleAllocatePushTransport: Max Connections Exhausted"); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); + return; + } + TransportConfigurationStruct outTransportConfiguration; outTransportConfiguration.connectionID = connectionID; outTransportConfiguration.transportStatus = TransportStatusEnum::kInactive; - /** - * delegate should set the TransportOptions fields to the new values. - * Persistently store the resulting TransportConfigurationStruct and map it to the ConnectionID - */ - Status status = mDelegate.AllocatePushTransport(transportOptions, outTransportConfiguration); + TransportOptionsStorage transportOptionArgs(transportOptions); + Status status = mDelegate.AllocatePushTransport(transportOptionArgs, connectionID); if (status == Status::Success) { // add connection to CurrentConnections FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); + outTransportConfiguration.transportOptions.SetValue(transportOptionArgs); TransportConfigurationStructWithFabricIndex transportConfiguration({ outTransportConfiguration, peerFabricIndex }); UpsertStreamTransportConnection(transportConfiguration); response.transportConfiguration = outTransportConfiguration; @@ -408,29 +435,30 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx return; } + TransportOptionsStruct transportOptionArgs; + transportOptionArgs.streamUsage = transportOptions.streamUsage; + transportOptionArgs.videoStreamID = transportOptions.videoStreamID; + transportOptionArgs.audioStreamID = transportOptions.audioStreamID; + transportOptionArgs.endpointID = transportOptions.endpointID; + transportOptionArgs.url = transportOptions.url; + transportOptionArgs.triggerOptions.triggerType = transportOptions.triggerOptions.triggerType; + // Todo: copy motion zones + transportOptionArgs.triggerOptions.motionSensitivity = transportOptions.triggerOptions.motionSensitivity; + transportOptionArgs.triggerOptions.motionTimeControl = transportOptions.triggerOptions.motionTimeControl; + transportOptionArgs.triggerOptions.maxPreRollLen = transportOptions.triggerOptions.maxPreRollLen; + transportOptionArgs.ingestMethod = transportOptions.ingestMethod; + transportOptionArgs.containerOptions = transportOptions.containerOptions; + transportOptionArgs.expiryTime = transportOptions.expiryTime; + // Call the delegate - status = mDelegate.ModifyPushTransport(connectionID, transportOptions); + status = mDelegate.ModifyPushTransport(connectionID, transportOptionArgs); if (status == Status::Success) { if (transportConfiguration->transportConfiguration.transportOptions.HasValue()) { - TransportOptionsStruct transportOptionToModify = - transportConfiguration->transportConfiguration.transportOptions.Value(); - transportOptionToModify.streamUsage = transportOptions.streamUsage; - transportOptionToModify.videoStreamID = transportOptions.videoStreamID; - transportOptionToModify.audioStreamID = transportOptions.audioStreamID; - transportOptionToModify.endpointID = transportOptions.endpointID; - transportOptionToModify.url = transportOptions.url; - transportOptionToModify.triggerOptions.triggerType = transportOptions.triggerOptions.triggerType; - // Todo: copy motion zones - transportOptionToModify.triggerOptions.motionSensitivity = transportOptions.triggerOptions.motionSensitivity; - transportOptionToModify.triggerOptions.motionTimeControl = transportOptions.triggerOptions.motionTimeControl; - transportOptionToModify.triggerOptions.maxPreRollLen = transportOptions.triggerOptions.maxPreRollLen; - transportOptionToModify.ingestMethod = transportOptions.ingestMethod; - transportOptionToModify.containerFormat = transportOptions.containerFormat; - transportOptionToModify.containerOptions = transportOptions.containerOptions; - transportOptionToModify.expiryTime = transportOptions.expiryTime; + transportConfiguration->transportConfiguration.transportOptions = + static_cast>(transportOptionArgs); } } @@ -548,16 +576,23 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, const Commands::FindTransport::DecodableType & commandData) { - Status status = Status::Success; Commands::FindTransportResponse::Type response; Optional> connectionID = commandData.connectionID; - static std::vector transportConfigurations{}; + size_t count = 0; + size_t bufferSize = mCurrentConnections.size(); + + Platform::ScopedMemoryBuffer transportConfigurations; + if (!transportConfigurations.Calloc(bufferSize)) + { + ChipLogError(Zcl, "Memory allocation failed for forecast buffer"); + return; + } DataModel::List outTransportConfigurations; - if (connectionID.Value().IsNull()) + if ((connectionID.HasValue() == false) || connectionID.Value().IsNull()) { if (mCurrentConnections.size() == 0) { @@ -570,7 +605,7 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, { if (connection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) { - transportConfigurations.push_back(connection.transportConfiguration); + transportConfigurations[count++] = connection.transportConfiguration; } } } @@ -590,24 +625,13 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } - transportConfigurations.push_back(transportConfiguration->transportConfiguration); + transportConfigurations[count++] = transportConfiguration->transportConfiguration; } - outTransportConfigurations = - DataModel::List(transportConfigurations.data(), transportConfigurations.size()); - - // Call the delegate - status = mDelegate.FindTransport(connectionID); - if (status == Status::Success) - { - response.transportConfigurations = outTransportConfigurations; + response.transportConfigurations = DataModel::List( + Span(transportConfigurations.Get(), count)); - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); - } - else - { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); - } + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } } // namespace PushAvStreamTransport diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index a79fe0a182a41e..d44d88bb1f97d8 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -29,6 +29,9 @@ namespace app { namespace Clusters { namespace PushAvStreamTransport { +static constexpr size_t kMaxUrlLength = 2000u; + +using SupportedFormatStruct = Structs::SupportedFormatStruct::Type; using CMAFContainerOptionsStruct = Structs::CMAFContainerOptionsStruct::Type; using ContainerOptionsStruct = Structs::ContainerOptionsStruct::Type; using TransportZoneOptionsStruct = Structs::TransportZoneOptionsStruct::Type; @@ -37,8 +40,6 @@ using TransportMotionTriggerTimeControlStruct = Structs::TransportMotionTriggerT using TransportOptionsStruct = Structs::TransportOptionsStruct::Type; using TransportConfigurationStruct = Structs::TransportConfigurationStruct::Type; -using TransportOptionsDecodeableStruct = Structs::TransportOptionsStruct::DecodableType; - struct TransportConfigurationStructWithFabricIndex { TransportConfigurationStruct transportConfiguration; @@ -52,6 +53,90 @@ enum class PushAvStreamTransportStatusEnum : uint8_t kUnknown = 0x02 }; +struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct +{ + TransportTriggerOptionsStorage() {}; + + TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType triggerOptions) + { + triggerType = triggerOptions.triggerType; + // motionZones = triggerOptions.motionZones; //Todo: Create Storage for motion zones + motionSensitivity = triggerOptions.motionSensitivity; + motionTimeControl = triggerOptions.motionTimeControl; + maxPreRollLen = triggerOptions.maxPreRollLen; + } +}; + +struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct +{ + CMAFContainerOptionsStorage() {}; + + CMAFContainerOptionsStorage(Optional CMAFContainerOptions) + { + if (CMAFContainerOptions.HasValue() == true) + { + chunkDuration = CMAFContainerOptions.Value().chunkDuration; + + MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); + CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKey.Value(), CENCKeyBuffer); + CENCKey.SetValue(CENCKeyBuffer); + + metadataEnabled = CMAFContainerOptions.Value().metadataEnabled; + + MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); + CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKeyID.Value(), CENCKeyIDBuffer); + CENCKeyID.SetValue(CENCKeyIDBuffer); + } + } + +private: + uint8_t mCENCKeyBuffer[16]; + uint8_t mCENCKeyIDBuffer[16]; +}; + +struct ContainerOptionsStorage : public ContainerOptionsStruct +{ + ContainerOptionsStorage() {}; + + ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType containerOptions) + { + containerType = containerOptions.containerType; + + CMAFContainerOptionsStorage cmafContainerStorage(containerOptions.CMAFContainerOptions); + CMAFContainerOptions.SetValue(cmafContainerStorage); + } +}; + +struct TransportOptionsStorage : public TransportOptionsStruct +{ + TransportOptionsStorage() {}; + + TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions) + { + streamUsage = transportOptions.streamUsage; + videoStreamID = transportOptions.videoStreamID; + audioStreamID = transportOptions.audioStreamID; + endpointID = transportOptions.endpointID; + + MutableCharSpan urlBuffer(mUrlBuffer); + CopyCharSpanToMutableCharSpan(transportOptions.url, urlBuffer); + url = urlBuffer; + + TransportTriggerOptionsStorage triggerOptionsStorage(transportOptions.triggerOptions); + triggerOptions = triggerOptionsStorage; + + ingestMethod = transportOptions.ingestMethod; + + ContainerOptionsStorage containerOptionsStorage(transportOptions.containerOptions); + containerOptions = containerOptionsStorage; + + expiryTime = transportOptions.expiryTime; + } + +private: + char mUrlBuffer[kMaxUrlLength]; +}; + /** @brief * Defines interfaces for implementing application-specific logic for various aspects of the PushAvStreamTransport Delegate. * Specifically, it defines interfaces for the command handling and loading of the allocated streams. @@ -68,14 +153,14 @@ class PushAvStreamTransportDelegate * * @param transportOptions[in] represent the configuration options of the transport to be allocated * - * @param outTransporConfiguration[out] represent the configuration of the transport to be allocated + * @param connectionID[in] Indicates the connectionID to allocate. * - * @return Success if the allocation is successful and a PushTransportConnectionID was - * produced; otherwise, the command SHALL be rejected with an appropriate - * error. + * @return Success if the allocation is successful and a PushTransportConnectionID was produced; otherwise, the command SHALL + * be rejected with an appropriate error. The delegate is expected the process the transport options, allocate the transport and + * map it to the connectionID. On Success TransportConfigurationStruct is sent as response by the server. */ - virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsDecodeableStruct & transportOptions, - TransportConfigurationStruct & outTransporConfiguration) = 0; + virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, + const uint16_t connectionID) = 0; /** * @brief Handle Command Delegate for Stream transport deallocation for the * provided connectionID. @@ -98,7 +183,7 @@ class PushAvStreamTransportDelegate * appropriate error. */ virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, - const TransportOptionsDecodeableStruct & transportOptions) = 0; + const TransportOptionsStruct & transportOptions) = 0; /** * @brief Handle Command Delegate for Stream transport modification. @@ -129,14 +214,12 @@ appropriate const Optional & timeControl) = 0; /** - * @brief Handle Command Delegate to get the Stream Options Configuration for the specified push transport. - * - * @param connectionID [in] Indicates the allocated connectionID to get the Stream Options Configuration of. - * @return Success if the transport is already allocated; otherwise, the command SHALL be rejected with an appropriate - * error. + * @brief Validates the url + * @param[in] url The url to validate * + * @return boolean value true if the url is valid else false. */ - virtual Protocols::InteractionModel::Status FindTransport(const Optional> & connectionID) = 0; + virtual bool validateUrl(std::string url) = 0; /** * @brief Validates the bandwidth requirement against the camera's resource management @@ -174,6 +257,13 @@ appropriate const Optional> & videoStreamId, const Optional> & audioStreamId) = 0; + /** + * @brief Gets the status of the transport + * + * @param[in] connectionID Indicates the connectionID of the stream transport to check status + * + * @return busy if transport is active else idle + */ virtual PushAvStreamTransportStatusEnum GetTransportStatus(const uint16_t connectionID) = 0; /** @@ -183,11 +273,16 @@ appropriate virtual void OnAttributeChanged(AttributeId attributeId) = 0; /** + * @brief * Delegate functions to load the allocated transport connections. * The delegate application is responsible for creating and persisting these connections ( based on the Allocation commands ). * These Load APIs would be used to load the pre-allocated transport connections context information into the cluster server - * list, at initialization. Once loaded, the cluster server would be serving Reads on these attributes. The list is updatable - * via the Add/Remove functions for the respective transport connections. + * list, at initialization. Once loaded, the cluster server would be serving Reads on these attributes. The list is updatable + * via the Add/Remove functions for the respective transport connections. + * + * @note + * The callee is responsible for allocating the buffer that holds the currentConnections. + * The buffer is allocated internally by the function and returned to the caller via an output parameter. */ virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; @@ -204,11 +299,12 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm /** * Creates a Push AV Stream Transport server instance. The Init() function needs to be called for this instance to be registered * and called by the interaction model at the appropriate times. - * @param aDelegate A reference to the delegate to be used by this server. + * @param aDelegate A reference to the delegate to be used by this server. * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. + * @param aFeatures The bitflags value that identifies which features are supported by this instance. * Note: the caller must ensure that the delegate lives throughout the instance's lifetime. */ - PushAvStreamTransportServer(PushAvStreamTransportDelegate & delegate, EndpointId endpointId); + PushAvStreamTransportServer(PushAvStreamTransportDelegate & delegate, EndpointId endpointId, const BitFlags aFeatures); ~PushAvStreamTransportServer() override; @@ -230,8 +326,6 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm bool HasFeature(Feature feature) const; // Attribute Getters - BitMask GetSupportedContainerFormats() const { return mSupportedContainerFormats; } - BitMask GetSupportedIngestMethods() const { return mSupportedIngestMethods; } private: enum class UpsertResultEnum : uint8_t @@ -242,11 +336,10 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm PushAvStreamTransportDelegate & mDelegate; + const BitFlags mFeatures; + // Attributes - // Todo: Add SupportedFormats attribute form https://github.com/CHIP-Specifications/connectedhomeip-spec/pull/11504 - BitMask mSupportedContainerFormats; - BitMask mSupportedIngestMethods; - // lists + std::vector mSupportedFormats; /*Moved from TransportConfigurationStruct to TransportConfigurationStructWithFabricIndex * to perform fabric index checks */ @@ -265,6 +358,7 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm // Helpers to read list items via delegate APIs CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder); + CHIP_ERROR ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder); // Helper functions uint16_t GenerateConnectionID(); diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index 40fa9d6632e182..056b0d2aa3f0d6 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -774,7 +774,6 @@ "FeatureMap" ], "Camera AV Settings User Level Management": ["MPTZPosition"], - "Push AV Stream Transport": ["CurrentConnections"], "Soil Measurement": ["SoilMoistureMeasurementLimits"], "Unit Localization": ["TemperatureUnit"] }, diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index c07d12f8768478..1f5f3df6802a08 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -768,7 +768,6 @@ "FeatureMap" ], "Camera AV Settings User Level Management": ["MPTZPosition"], - "Push AV Stream Transport": ["CurrentConnections"], "Soil Measurement": ["SoilMoistureMeasurementLimits"], "Unit Localization": ["TemperatureUnit"] }, From 69d8bde8da9ba2d989e76b756c8b9b7a97d90b73 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Thu, 15 May 2025 13:31:49 +0000 Subject: [PATCH 17/40] Restyled by clang-format --- .../push-av-stream-transport-delegate-impl.h | 2 +- ...push-av-stream-transport-delegate-impl.cpp | 2 +- .../push-av-stream-transport-server.cpp | 36 ++++++------ .../push-av-stream-transport-server.h | 58 +++++++++++++------ 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index 9a219955f7eac2..f74ae2c24df826 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -62,7 +62,7 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate PushAvStreamTransportStatusEnum GetTransportStatus(const uint16_t connectionID); void OnAttributeChanged(AttributeId attributeId); - CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); + CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); CHIP_ERROR PersistentAttributesLoadedCallback(); void Init(); diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index 0ab561a0f75d59..9100e14f37e00c 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -150,7 +150,7 @@ void PushAvStreamTransportManager::Init() ChipLogProgress(Zcl, "Push AV Stream Transport Initialized"); } CHIP_ERROR -PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) +PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) { ChipLogProgress(Zcl, "Push AV Current Connections loaded"); diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 0d655fd4fe3d1a..34aa419efbfa98 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -97,7 +97,7 @@ CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const At } PushAvStreamTransportServer::UpsertResultEnum PushAvStreamTransportServer::UpsertStreamTransportConnection( - const TransportConfigurationStructWithFabricIndex & transportConfiguration) + const TransportConfigurationStorageWithFabricIndex & transportConfiguration) { UpsertResultEnum result; auto it = std::find_if(mCurrentConnections.begin(), mCurrentConnections.end(), @@ -128,7 +128,7 @@ void PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t // Erase-Remove idiom mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), - [transportConnectionId](const TransportConfigurationStructWithFabricIndex & s) { + [transportConnectionId](const TransportConfigurationStorageWithFabricIndex & s) { return s.transportConfiguration.connectionID == transportConnectionId; }), mCurrentConnections.end()); @@ -246,7 +246,7 @@ void PushAvStreamTransportServer::InvokeCommand(HandlerContext & handlerContext) } } -TransportConfigurationStructWithFabricIndex * +TransportConfigurationStorageWithFabricIndex * PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) { for (auto & transportConnection : mCurrentConnections) @@ -351,19 +351,19 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c return; } - TransportConfigurationStruct outTransportConfiguration; - outTransportConfiguration.connectionID = connectionID; - outTransportConfiguration.transportStatus = TransportStatusEnum::kInactive; + std::shared_ptr transportOptionsPtr = std::make_shared(transportOptions); - TransportOptionsStorage transportOptionArgs(transportOptions); - Status status = mDelegate.AllocatePushTransport(transportOptionArgs, connectionID); + TransportConfigurationStorage outTransportConfiguration(connectionID, transportOptionsPtr); + + Status status = mDelegate.AllocatePushTransport(*transportOptionsPtr, connectionID); if (status == Status::Success) { // add connection to CurrentConnections FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - outTransportConfiguration.transportOptions.SetValue(transportOptionArgs); - TransportConfigurationStructWithFabricIndex transportConfiguration({ outTransportConfiguration, peerFabricIndex }); + + TransportConfigurationStorageWithFabricIndex transportConfiguration({ outTransportConfiguration, peerFabricIndex }); + UpsertStreamTransportConnection(transportConfiguration); response.transportConfiguration = outTransportConfiguration; @@ -378,9 +378,9 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c void PushAvStreamTransportServer::HandleDeallocatePushTransport( HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & commandData) { - Status status = Status::Success; - uint16_t connectionID = commandData.connectionID; - TransportConfigurationStructWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleDeallocatePushTransport: ConnectionID Not Found."); @@ -412,7 +412,7 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx uint16_t connectionID = commandData.connectionID; auto & transportOptions = commandData.transportOptions; - TransportConfigurationStructWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); + TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); if (transportConfiguration == nullptr) { @@ -486,7 +486,7 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, } else { - TransportConfigurationStructWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID.Value()); + TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID.Value()); if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); @@ -516,7 +516,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( auto & activationReason = commandData.activationReason; auto & timeControl = commandData.timeControl; - TransportConfigurationStructWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); + TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); if (transportConfiguration == nullptr) { @@ -590,8 +590,6 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, return; } - DataModel::List outTransportConfigurations; - if ((connectionID.HasValue() == false) || connectionID.Value().IsNull()) { if (mCurrentConnections.size() == 0) @@ -611,7 +609,7 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, } else { - TransportConfigurationStructWithFabricIndex * transportConfiguration = + TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID.Value().Value()); if (transportConfiguration == nullptr) { diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index d44d88bb1f97d8..fab51d66569f43 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -40,12 +40,6 @@ using TransportMotionTriggerTimeControlStruct = Structs::TransportMotionTriggerT using TransportOptionsStruct = Structs::TransportOptionsStruct::Type; using TransportConfigurationStruct = Structs::TransportConfigurationStruct::Type; -struct TransportConfigurationStructWithFabricIndex -{ - TransportConfigurationStruct transportConfiguration; - chip::FabricIndex fabricIndex; -}; - enum class PushAvStreamTransportStatusEnum : uint8_t { kBusy = 0x00, @@ -102,9 +96,12 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct { containerType = containerOptions.containerType; - CMAFContainerOptionsStorage cmafContainerStorage(containerOptions.CMAFContainerOptions); - CMAFContainerOptions.SetValue(cmafContainerStorage); + mCMAFContainerStorage = CMAFContainerOptionsStorage(containerOptions.CMAFContainerOptions); + CMAFContainerOptions.SetValue(mCMAFContainerStorage); } + +private: + CMAFContainerOptionsStorage mCMAFContainerStorage; }; struct TransportOptionsStorage : public TransportOptionsStruct @@ -119,22 +116,49 @@ struct TransportOptionsStorage : public TransportOptionsStruct endpointID = transportOptions.endpointID; MutableCharSpan urlBuffer(mUrlBuffer); - CopyCharSpanToMutableCharSpan(transportOptions.url, urlBuffer); + CopyCharSpanToMutableCharSpanWithTruncation(transportOptions.url, urlBuffer); url = urlBuffer; - TransportTriggerOptionsStorage triggerOptionsStorage(transportOptions.triggerOptions); - triggerOptions = triggerOptionsStorage; + mTriggerOptionsStorage = TransportTriggerOptionsStorage(transportOptions.triggerOptions); + triggerOptions = mTriggerOptionsStorage; ingestMethod = transportOptions.ingestMethod; - ContainerOptionsStorage containerOptionsStorage(transportOptions.containerOptions); - containerOptions = containerOptionsStorage; + mContainerOptionsStorage = ContainerOptionsStorage(transportOptions.containerOptions); + containerOptions = mContainerOptionsStorage; expiryTime = transportOptions.expiryTime; } private: char mUrlBuffer[kMaxUrlLength]; + TransportTriggerOptionsStorage mTriggerOptionsStorage; + ContainerOptionsStorage mContainerOptionsStorage; +}; + +struct TransportConfigurationStorage : public TransportConfigurationStruct +{ + TransportConfigurationStorage() {} + + TransportConfigurationStorage(const uint16_t aConnectionID, std::shared_ptr aTransportOptionsPtr) + { + connectionID = aConnectionID; + transportStatus = TransportStatusEnum::kInactive; + /*Store the pointer to keep buffer alive*/ + mTransportOptionsPtr = aTransportOptionsPtr; + /*Convert Storage type to base type*/ + transportOptions.SetValue(*aTransportOptionsPtr); + } + std::shared_ptr GetTransportOptionsPtr() const { return mTransportOptionsPtr; } + +private: + std::shared_ptr mTransportOptionsPtr; +}; + +struct TransportConfigurationStorageWithFabricIndex +{ + TransportConfigurationStorage transportConfiguration; + chip::FabricIndex fabricIndex; }; /** @brief @@ -284,7 +308,7 @@ appropriate * The callee is responsible for allocating the buffer that holds the currentConnections. * The buffer is allocated internally by the function and returned to the caller via an output parameter. */ - virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; + virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; /** * @brief Callback into the delegate once persistent attributes managed by @@ -343,7 +367,7 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm /*Moved from TransportConfigurationStruct to TransportConfigurationStructWithFabricIndex * to perform fabric index checks */ - std::vector mCurrentConnections; + std::vector mCurrentConnections; /** * IM-level implementation of read @@ -362,9 +386,9 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm // Helper functions uint16_t GenerateConnectionID(); - TransportConfigurationStructWithFabricIndex * FindStreamTransportConnection(const uint16_t connectionID); + TransportConfigurationStorageWithFabricIndex * FindStreamTransportConnection(const uint16_t connectionID); // Add/Remove Management functions for transport - UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStructWithFabricIndex & transportConfiguration); + UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStorageWithFabricIndex & transportConfiguration); void RemoveStreamTransportConnection(const uint16_t connectionID); From 1fdc58660a4de0e49cc88eec481cecf01bfbaa70 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 15 May 2025 21:40:01 +0000 Subject: [PATCH 18/40] Restyled by clang-format --- .../push-av-stream-transport-server.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index fab51d66569f43..3260ca5928ab65 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -49,7 +49,7 @@ enum class PushAvStreamTransportStatusEnum : uint8_t struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct { - TransportTriggerOptionsStorage() {}; + TransportTriggerOptionsStorage(){}; TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType triggerOptions) { @@ -63,7 +63,7 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct { - CMAFContainerOptionsStorage() {}; + CMAFContainerOptionsStorage(){}; CMAFContainerOptionsStorage(Optional CMAFContainerOptions) { @@ -90,7 +90,7 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct struct ContainerOptionsStorage : public ContainerOptionsStruct { - ContainerOptionsStorage() {}; + ContainerOptionsStorage(){}; ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType containerOptions) { @@ -106,7 +106,7 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct struct TransportOptionsStorage : public TransportOptionsStruct { - TransportOptionsStorage() {}; + TransportOptionsStorage(){}; TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions) { From bb9e0531f166bdd667a54ff77079292ee85ae3ef Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Tue, 20 May 2025 13:10:33 +0530 Subject: [PATCH 19/40] Address review comments --- .../push-av-stream-transport-delegate-impl.h | 2 +- ...push-av-stream-transport-delegate-impl.cpp | 2 +- .../push-av-stream-transport-server.cpp | 17 +-- .../push-av-stream-transport-server.h | 106 +++++++++++++----- 4 files changed, 88 insertions(+), 39 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index f74ae2c24df826..574b07a4ef59a4 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -53,7 +53,7 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, const Optional & timeControl); - bool validateUrl(std::string url); + bool ValidateUrl(std::string url); CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, const Optional> & videoStreamId, const Optional> & audioStreamId); diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index 9100e14f37e00c..7687fb24b8727b 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -113,7 +113,7 @@ CHIP_ERROR PushAvStreamTransportManager::ValidateBandwidthLimit(StreamUsageEnum return CHIP_NO_ERROR; } -bool PushAvStreamTransportManager::validateUrl(std::string url) +bool PushAvStreamTransportManager::ValidateUrl(std::string url) { return true; } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 34aa419efbfa98..f61045c1976b57 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -296,17 +296,17 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (isFormatSupported == false) { - auto status = static_cast(StatusCodeEnum::kInvalidCombination); + auto status = to_underlying(StatusCodeEnum::kInvalidCombination); ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Format Combination"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } - bool isValidUrl = mDelegate.validateUrl(std::string(transportOptions.url.data(), transportOptions.url.size())); + bool isValidUrl = mDelegate.ValidateUrl(std::string(transportOptions.url.data(), transportOptions.url.size())); if (isValidUrl == false) { - auto status = static_cast(StatusCodeEnum::kInvalidURL); + auto status = to_underlying(StatusCodeEnum::kInvalidURL); ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Url"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; @@ -314,7 +314,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (transportOptions.triggerOptions.triggerType == TransportTriggerTypeEnum::kUnknownEnumValue) { - auto status = static_cast(StatusCodeEnum::kInvalidTriggerType); + auto status = to_underlying(StatusCodeEnum::kInvalidTriggerType); ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Trigger type"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; @@ -336,7 +336,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c mDelegate.ValidateStreamUsage(transportOptions.streamUsage, transportOptions.videoStreamID, transportOptions.audioStreamID); if (err != CHIP_NO_ERROR) { - auto status = static_cast(StatusCodeEnum::kInvalidStream); + auto status = to_underlying(StatusCodeEnum::kInvalidStream); ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Stream"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; @@ -351,7 +351,8 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c return; } - std::shared_ptr transportOptionsPtr = std::make_shared(transportOptions); + std::shared_ptr transportOptionsPtr = + std::make_shared(transportOptions, mFeatures); TransportConfigurationStorage outTransportConfiguration(connectionID, transportOptionsPtr); @@ -541,7 +542,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( if (transportConfiguration->transportConfiguration.transportStatus == TransportStatusEnum::kInactive) { - auto clusterStatus = static_cast(StatusCodeEnum::kInvalidTransportStatus); + auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTransportStatus); ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Transport status"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; @@ -552,7 +553,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( TransportTriggerTypeEnum::kContinuous) { - auto clusterStatus = static_cast(StatusCodeEnum::kInvalidTriggerType); + auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTriggerType); ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Trigger type"); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 3260ca5928ab65..c66e620cfb22fe 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -49,37 +49,77 @@ enum class PushAvStreamTransportStatusEnum : uint8_t struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct { - TransportTriggerOptionsStorage(){}; + TransportTriggerOptionsStorage() {}; - TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType triggerOptions) + TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType triggerOptions, + const BitFlags features) { triggerType = triggerOptions.triggerType; - // motionZones = triggerOptions.motionZones; //Todo: Create Storage for motion zones + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion && triggerOptions.motionZones.HasValue()) + { + if (triggerOptions.motionZones.Value().IsNull() == false) + { + auto & motionZonesList = triggerOptions.motionZones; + auto iter = motionZonesList.Value().Value().begin(); + + while (iter.Next()) + { + auto & transportZoneOption = iter.GetValue(); + mTransportZoneOptions.push_back(transportZoneOption); + } + + motionZones.SetValue(DataModel::Nullable>( + Span(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); + } + } + else + { + motionZones.Missing(); + } + motionSensitivity = triggerOptions.motionSensitivity; motionTimeControl = triggerOptions.motionTimeControl; maxPreRollLen = triggerOptions.maxPreRollLen; } + +private: + std::vector mTransportZoneOptions; }; struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct { - CMAFContainerOptionsStorage(){}; + CMAFContainerOptionsStorage() {}; - CMAFContainerOptionsStorage(Optional CMAFContainerOptions) + CMAFContainerOptionsStorage(Optional CMAFContainerOptions, + const BitFlags features) { if (CMAFContainerOptions.HasValue() == true) { chunkDuration = CMAFContainerOptions.Value().chunkDuration; - MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); - CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKey.Value(), CENCKeyBuffer); - CENCKey.SetValue(CENCKeyBuffer); - - metadataEnabled = CMAFContainerOptions.Value().metadataEnabled; - - MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); - CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKeyID.Value(), CENCKeyIDBuffer); - CENCKeyID.SetValue(CENCKeyIDBuffer); + if (CMAFContainerOptions.Value().CENCKey.HasValue()) + { + MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); + CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKey.Value(), CENCKeyBuffer); + CENCKey.SetValue(CENCKeyBuffer); + } + + if (features.Has(Feature::kMetadata) && CMAFContainerOptions.HasValue()) + { + metadataEnabled = CMAFContainerOptions.Value().metadataEnabled; + } + else + { + metadataEnabled.Missing(); + } + + if (CMAFContainerOptions.Value().CENCKey.HasValue()) + { + MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); + CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKeyID.Value(), CENCKeyIDBuffer); + CENCKeyID.SetValue(CENCKeyIDBuffer); + } } } @@ -90,14 +130,21 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct struct ContainerOptionsStorage : public ContainerOptionsStruct { - ContainerOptionsStorage(){}; + ContainerOptionsStorage() {}; - ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType containerOptions) + ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType containerOptions, const BitFlags features) { containerType = containerOptions.containerType; - mCMAFContainerStorage = CMAFContainerOptionsStorage(containerOptions.CMAFContainerOptions); - CMAFContainerOptions.SetValue(mCMAFContainerStorage); + if (containerType == ContainerFormatEnum::kCmaf) + { + mCMAFContainerStorage = CMAFContainerOptionsStorage(containerOptions.CMAFContainerOptions, features); + CMAFContainerOptions.SetValue(mCMAFContainerStorage); + } + else + { + CMAFContainerOptions.Missing(); + } } private: @@ -106,9 +153,9 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct struct TransportOptionsStorage : public TransportOptionsStruct { - TransportOptionsStorage(){}; + TransportOptionsStorage() {}; - TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions) + TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions, const BitFlags features) { streamUsage = transportOptions.streamUsage; videoStreamID = transportOptions.videoStreamID; @@ -119,12 +166,12 @@ struct TransportOptionsStorage : public TransportOptionsStruct CopyCharSpanToMutableCharSpanWithTruncation(transportOptions.url, urlBuffer); url = urlBuffer; - mTriggerOptionsStorage = TransportTriggerOptionsStorage(transportOptions.triggerOptions); + mTriggerOptionsStorage = TransportTriggerOptionsStorage(transportOptions.triggerOptions, features); triggerOptions = mTriggerOptionsStorage; ingestMethod = transportOptions.ingestMethod; - mContainerOptionsStorage = ContainerOptionsStorage(transportOptions.containerOptions); + mContainerOptionsStorage = ContainerOptionsStorage(transportOptions.containerOptions, features); containerOptions = mContainerOptionsStorage; expiryTime = transportOptions.expiryTime; @@ -158,7 +205,7 @@ struct TransportConfigurationStorage : public TransportConfigurationStruct struct TransportConfigurationStorageWithFabricIndex { TransportConfigurationStorage transportConfiguration; - chip::FabricIndex fabricIndex; + FabricIndex fabricIndex; }; /** @brief @@ -180,8 +227,8 @@ class PushAvStreamTransportDelegate * @param connectionID[in] Indicates the connectionID to allocate. * * @return Success if the allocation is successful and a PushTransportConnectionID was produced; otherwise, the command SHALL - * be rejected with an appropriate error. The delegate is expected the process the transport options, allocate the transport and - * map it to the connectionID. On Success TransportConfigurationStruct is sent as response by the server. + * be rejected with an appropriate error. The delegate is expected to process the transport options, allocate the transport + * and map it to the connectionID. On Success TransportConfigurationStruct is sent as response by the server. */ virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, const uint16_t connectionID) = 0; @@ -235,7 +282,7 @@ appropriate */ virtual Protocols::InteractionModel::Status ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, - const Optional & timeControl) = 0; + const Optional & timeControl) = 0; /** * @brief Validates the url @@ -243,7 +290,7 @@ appropriate * * @return boolean value true if the url is valid else false. */ - virtual bool validateUrl(std::string url) = 0; + virtual bool ValidateUrl(std::string url) = 0; /** * @brief Validates the bandwidth requirement against the camera's resource management @@ -305,8 +352,9 @@ appropriate * via the Add/Remove functions for the respective transport connections. * * @note - * The callee is responsible for allocating the buffer that holds the currentConnections. - * The buffer is allocated internally by the function and returned to the caller via an output parameter. + * + * The required buffers are managed by TransportConfigurationStorage, the delegate function is expected to populate the vector + * correctly. */ virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; From 281c6ffce4ce20459696daed593cc2fbb5a162b6 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Wed, 28 May 2025 15:14:42 +0530 Subject: [PATCH 20/40] Address review comments --- .../push-av-stream-transport-delegate-impl.h | 2 +- ...push-av-stream-transport-delegate-impl.cpp | 3 +- .../push-av-stream-transport-server.cpp | 590 +++++++++++++++--- .../push-av-stream-transport-server.h | 260 ++++++-- 4 files changed, 746 insertions(+), 109 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index 574b07a4ef59a4..18d799efdf64ca 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -45,7 +45,7 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate const uint16_t connectionID); Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID); Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, - const TransportOptionsStruct & transportOptions); + const Structs::TransportOptionsStruct::DecodableType transportOptions); Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus); diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index 7687fb24b8727b..33a834724489d2 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -57,7 +57,8 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::DeallocatePush } Protocols::InteractionModel::Status -PushAvStreamTransportManager::ModifyPushTransport(const uint16_t connectionID, const TransportOptionsStruct & transportOptions) +PushAvStreamTransportManager::ModifyPushTransport(const uint16_t connectionID, + const Structs::TransportOptionsStruct::DecodableType transportOptions) { for (PushAvStream & stream : pushavStreams) { diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index f61045c1976b57..111cc6d9f6d469 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,10 @@ PushAvStreamTransportServer::PushAvStreamTransportServer(PushAvStreamTransportDe AttributeAccessInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), CommandHandlerInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), mDelegate(aDelegate), mFeatures(aFeatures), mSupportedFormats{ SupportedFormatStruct{ ContainerFormatEnum::kCmaf, IngestMethodsEnum::kCMAFIngest } } -{} +{ + /* set the base class delegates endpointId */ + mDelegate.SetEndpointId(aEndpointId); +} PushAvStreamTransportServer::~PushAvStreamTransportServer() { @@ -274,18 +278,236 @@ uint16_t PushAvStreamTransportServer::GenerateConnectionID() return kMaxConnectionId; // All 0 to 65534 IDs are in use } +void PushAvStreamTransportServer::PushAVStreamTransportDeallocateCallback(System::Layer *, void * callbackContext) +{ + PushAVStreamTransportDeallocateCallbackContext * transportDeallocateContext = + static_cast((callbackContext)); + + uint16_t connectionID = transportDeallocateContext->connectionID; + + // Call the delegate + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + transportDeallocateContext->instance->mDelegate.DeallocatePushTransport(connectionID)); + + if (delegateStatus.IsSuccess() == true) + { + ChipLogProgress(Zcl, "Push AV Stream Transport Deallocate timer expired. %s", "Deallocating"); + + // Remove connection from CurrentConnections + transportDeallocateContext->instance->RemoveStreamTransportConnection(connectionID); + } + else + { + ChipLogError(Zcl, "Push AV Stream Transport Deallocate timer expired. %s", "Deallocation Failed"); + } + + delete transportDeallocateContext; +} + +void PushAvStreamTransportServer::ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec) +{ + uint32_t timeoutMs = timeoutSec * MILLISECOND_TICKS_PER_SECOND; + + PushAVStreamTransportDeallocateCallbackContext * transportDeallocateContext = + new (std::nothrow) PushAVStreamTransportDeallocateCallbackContext{ this, connectionID }; + + if (transportDeallocateContext == nullptr) + { + ChipLogError(Zcl, "Failed to allocate memory for deallocate context"); + return; + } + + CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(timeoutMs), + PushAVStreamTransportDeallocateCallback, + static_cast(transportDeallocateContext)); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Failed to schedule deallocate: timeout=%" PRIu32 ", status=%" CHIP_ERROR_FORMAT, timeoutSec, + err.Format()); + } +} + void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, const Commands::AllocatePushTransport::DecodableType & commandData) { Commands::AllocatePushTransportResponse::Type response; auto & transportOptions = commandData.transportOptions; + // Contraints check on incoming transport Options + + VerifyOrReturn(transportOptions.streamUsage != StreamUsageEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid streamUsage ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(transportOptions.videoStreamID.HasValue() || transportOptions.audioStreamID.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(transportOptions.url.size() <= 2000, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + + auto & triggerOptions = transportOptions.triggerOptions; + + VerifyOrReturn(triggerOptions.triggerType != TransportTriggerTypeEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid triggerType ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion) + { + VerifyOrReturn(triggerOptions.motionZones.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing motion zones ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + if (triggerOptions.motionZones.Value().IsNull() == false) + { + auto & motionZonesList = triggerOptions.motionZones; + auto iter = motionZonesList.Value().Value().begin(); + + while (iter.Next()) + { + auto & transportZoneOption = iter.GetValue(); + + if (mFeatures.Has(Feature::kPerZoneSensitivity)) + { + VerifyOrReturn(transportZoneOption.sensitivity.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Zone Sensitivity ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(transportZoneOption.sensitivity.Value() >= 1 && transportZoneOption.sensitivity.Value() <= 10, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Zone Sensitivity Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } + } + } + + if (mFeatures.Has(Feature::kPerZoneSensitivity) == false) + { + VerifyOrReturn(triggerOptions.motionSensitivity.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Sensitivity ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + VerifyOrReturn( + triggerOptions.motionSensitivity.Value().Value() >= 1 && triggerOptions.motionSensitivity.Value().Value() <= 10, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Sensitivity Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } + + VerifyOrReturn(triggerOptions.motionTimeControl.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Time Control ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(triggerOptions.motionTimeControl.Value().initialDuration >= 1, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + + VerifyOrReturn(triggerOptions.motionTimeControl.Value().maxDuration >= 1, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion || + triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand) + { + VerifyOrReturn(triggerOptions.maxPreRollLen.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Max Pre Roll Len field ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + } + + VerifyOrReturn(transportOptions.ingestMethod != IngestMethodsEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + // Todo: TLSEndpointID Validation - bool isFormatSupported = false; - IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; + IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; + + VerifyOrReturn(transportOptions.ingestMethod != IngestMethodsEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + ContainerOptionsStruct containerOptions = commandData.transportOptions.containerOptions; + VerifyOrReturn(containerOptions.containerType != ContainerFormatEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Container Format ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + if (containerOptions.containerType == ContainerFormatEnum::kCmaf) + { + VerifyOrReturn(containerOptions.CMAFContainerOptions.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) + { + VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size() <= kMaxCENCKeyLength, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } + + if (mFeatures.Has(Feature::kMetadata)) + { + VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().metadataEnabled.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options MetadataEnabled ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + } + + if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) + { + VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKeyID.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options CENC Key ID ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size() <= kMaxCENCKeyIDLength, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key ID constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } + } + + bool isFormatSupported = false; + for (auto & supportsFormat : mSupportedFormats) { if ((supportsFormat.ingestMethod == ingestMethod) && (supportsFormat.containerFormat == containerOptions.containerType)) @@ -297,7 +519,11 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (isFormatSupported == false) { auto status = to_underlying(StatusCodeEnum::kInvalidCombination); - ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Format Combination"); + ChipLogError(Zcl, + "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method and Container Format Combination : (Ingest Method: " + "%02X and Container Format: %02X)", + AttributeAccessInterface::GetEndpointId().Value(), static_cast(ingestMethod), + static_cast(containerOptions.containerType)); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } @@ -307,26 +533,30 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (isValidUrl == false) { auto status = to_underlying(StatusCodeEnum::kInvalidURL); - ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Url"); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Url", AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } + /*Spec issue for invalid Trigger Type: https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/11701*/ if (transportOptions.triggerOptions.triggerType == TransportTriggerTypeEnum::kUnknownEnumValue) { auto status = to_underlying(StatusCodeEnum::kInvalidTriggerType); - ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Trigger type"); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Trigger type", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } - // Todo: Validate MotionZones list in the TransportTriggerOptionsStruct field + // Validate ZoneId + // Validate Bandwidth Requirement CHIP_ERROR err = mDelegate.ValidateBandwidthLimit(transportOptions.streamUsage, transportOptions.videoStreamID, transportOptions.audioStreamID); if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "HandleAllocatePushTransport: Resource Exhausted"); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Resource Exhausted", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); return; } @@ -337,7 +567,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (err != CHIP_NO_ERROR) { auto status = to_underlying(StatusCodeEnum::kInvalidStream); - ChipLogError(Zcl, "HandleAllocatePushTransport: Invalid Stream"); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Stream", AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } @@ -346,13 +576,21 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (connectionID == kMaxConnectionId) { - ChipLogError(Zcl, "HandleAllocatePushTransport: Max Connections Exhausted"); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Max Connections Exhausted", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); return; } - std::shared_ptr transportOptionsPtr = - std::make_shared(transportOptions, mFeatures); + std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + + if (transportOptionsPtr == nullptr) + { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Memory Allocation failed for transportOptions", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); + return; + } TransportConfigurationStorage outTransportConfiguration(connectionID, transportOptionsPtr); @@ -368,6 +606,12 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c UpsertStreamTransportConnection(transportConfiguration); response.transportConfiguration = outTransportConfiguration; + // ExpiryTime Handling + if (transportOptions.expiryTime.HasValue()) + { + ScheduleTransportDeallocate(connectionID, transportOptions.expiryTime.Value()); + } + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } else @@ -379,31 +623,34 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c void PushAvStreamTransportServer::HandleDeallocatePushTransport( HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & commandData) { - Status status = Status::Success; uint16_t connectionID = commandData.connectionID; TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); if (transportConfiguration == nullptr) { - ChipLogError(Zcl, "HandleDeallocatePushTransport: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleDeallocatePushTransport[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) { - ChipLogError(Zcl, "HandleDeallocatePushTransport: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleDeallocatePushTransport[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } // Call the delegate - status = mDelegate.DeallocatePushTransport(connectionID); + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode(mDelegate.DeallocatePushTransport(connectionID)); - if (status == Status::Success) - // Remove connection form CurrentConnections + if (delegateStatus.IsSuccess() == true) + { + // Remove connection from CurrentConnections RemoveStreamTransportConnection(connectionID); + } - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); } void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx, @@ -413,54 +660,159 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx uint16_t connectionID = commandData.connectionID; auto & transportOptions = commandData.transportOptions; + // Contraints check on incoming transport Options + + VerifyOrReturn(transportOptions.streamUsage != StreamUsageEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid streamUsage ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(transportOptions.videoStreamID.HasValue() || transportOptions.audioStreamID.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(transportOptions.url.size() <= 2000, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + + auto & triggerOptions = transportOptions.triggerOptions; + + VerifyOrReturn(triggerOptions.triggerType != TransportTriggerTypeEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid triggerType ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion) + { + VerifyOrReturn(triggerOptions.motionZones.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing motion zones ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + if (triggerOptions.motionZones.Value().IsNull() == false) + { + auto & motionZonesList = triggerOptions.motionZones; + auto iter = motionZonesList.Value().Value().begin(); + + while (iter.Next()) + { + auto & transportZoneOption = iter.GetValue(); + + if (mFeatures.Has(Feature::kPerZoneSensitivity)) + { + VerifyOrReturn(transportZoneOption.sensitivity.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Zone Sensitivity ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(transportZoneOption.sensitivity.Value() >= 1 && transportZoneOption.sensitivity.Value() <= 10, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Zone Sensitivity Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } + } + } + + if (mFeatures.Has(Feature::kPerZoneSensitivity) == false) + { + VerifyOrReturn(triggerOptions.motionSensitivity.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Sensitivity ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + VerifyOrReturn( + triggerOptions.motionSensitivity.Value().Value() >= 1 && triggerOptions.motionSensitivity.Value().Value() <= 10, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Sensitivity Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } + + VerifyOrReturn(triggerOptions.motionTimeControl.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Time Control ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + VerifyOrReturn(triggerOptions.motionTimeControl.Value().initialDuration >= 1, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + + VerifyOrReturn(triggerOptions.motionTimeControl.Value().maxDuration >= 1, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion || + triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand) + { + VerifyOrReturn(triggerOptions.maxPreRollLen.HasValue(), { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Max Pre Roll Len field ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + } + + VerifyOrReturn(transportOptions.ingestMethod != IngestMethodsEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); if (transportConfiguration == nullptr) { - ChipLogError(Zcl, "HandleModifyPushTransport: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) { - ChipLogError(Zcl, "HandleModifyPushTransport: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } if (mDelegate.GetTransportStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) { - ChipLogError(Zcl, "HandleModifyPushTransport: Connection is Busy"); + ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: Connection is Busy", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Busy); return; } - TransportOptionsStruct transportOptionArgs; - transportOptionArgs.streamUsage = transportOptions.streamUsage; - transportOptionArgs.videoStreamID = transportOptions.videoStreamID; - transportOptionArgs.audioStreamID = transportOptions.audioStreamID; - transportOptionArgs.endpointID = transportOptions.endpointID; - transportOptionArgs.url = transportOptions.url; - transportOptionArgs.triggerOptions.triggerType = transportOptions.triggerOptions.triggerType; - // Todo: copy motion zones - transportOptionArgs.triggerOptions.motionSensitivity = transportOptions.triggerOptions.motionSensitivity; - transportOptionArgs.triggerOptions.motionTimeControl = transportOptions.triggerOptions.motionTimeControl; - transportOptionArgs.triggerOptions.maxPreRollLen = transportOptions.triggerOptions.maxPreRollLen; - transportOptionArgs.ingestMethod = transportOptions.ingestMethod; - transportOptionArgs.containerOptions = transportOptions.containerOptions; - transportOptionArgs.expiryTime = transportOptions.expiryTime; + std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + if (transportOptionsPtr == nullptr) + { + ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: Memory Allocation failed for transportOptions", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); + return; + } // Call the delegate - status = mDelegate.ModifyPushTransport(connectionID, transportOptionArgs); + status = mDelegate.ModifyPushTransport(connectionID, transportOptions); if (status == Status::Success) { - if (transportConfiguration->transportConfiguration.transportOptions.HasValue()) - { - transportConfiguration->transportConfiguration.transportOptions = - static_cast>(transportOptionArgs); - } + transportConfiguration->transportConfiguration.SetTransportOptionsPtr(transportOptionsPtr); } ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); @@ -480,7 +832,6 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, { if (transportConnection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) { - transportConnection.transportConfiguration.transportStatus = transportStatus; connectionIDList.push_back(transportConnection.transportConfiguration.connectionID); } } @@ -490,22 +841,35 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID.Value()); if (transportConfiguration == nullptr) { - ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleSetTransportStatus[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) { - ChipLogError(Zcl, "HandleSetTransportStatus: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleSetTransportStatus[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } - - transportConfiguration->transportConfiguration.transportStatus = transportStatus; connectionIDList.push_back(connectionID.Value()); } // Call the delegate status = mDelegate.SetTransportStatus(connectionIDList, transportStatus); + if (status == Status::Success) + { + for (auto & connID : connectionIDList) + { + for (auto & transportConnection : mCurrentConnections) + { + if (transportConnection.transportConfiguration.connectionID == connID) + { + transportConnection.transportConfiguration.transportStatus = transportStatus; + } + } + } + } ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } @@ -515,27 +879,52 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( Status status = Status::Success; uint16_t connectionID = commandData.connectionID; auto & activationReason = commandData.activationReason; - auto & timeControl = commandData.timeControl; + + VerifyOrReturn(activationReason != TriggerActivationReasonEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Activation Reason ", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + }); + + Optional timeControl = commandData.timeControl; + + if (timeControl.HasValue()) + { + VerifyOrReturn(timeControl.Value().initialDuration >= 1, { + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + + VerifyOrReturn(timeControl.Value().maxDuration >= 1, { + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + } TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); if (transportConfiguration == nullptr) { - ChipLogError(Zcl, "HandleManuallyTriggerTransport: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) { - ChipLogError(Zcl, "HandleManuallyTriggerTransport: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } if (mDelegate.GetTransportStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) { - ChipLogError(Zcl, "HandleManuallyTriggerTransport: Connection is Busy"); + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Connection is Busy", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Busy); return; } @@ -543,7 +932,8 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( if (transportConfiguration->transportConfiguration.transportStatus == TransportStatusEnum::kInactive) { auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTransportStatus); - ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Transport status"); + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Transport status", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; } @@ -554,23 +944,37 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( { auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTriggerType); - ChipLogError(Zcl, "HandleManuallyTriggerTransport: Invalid Trigger type"); + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Trigger type", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; } if (transportConfiguration->transportConfiguration.transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand && - !timeControl.HasValue()) + timeControl.HasValue() == false) { - ChipLogError(Zcl, "HandleManuallyTriggerTransport: Time control field not present"); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Time control field not present", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::DynamicConstraintError); return; } } + // When trigger type is motion in the allocated transport but triggering it manually + if (timeControl.HasValue() == false) + { + timeControl = transportConfiguration->transportConfiguration.transportOptions.Value().triggerOptions.motionTimeControl; + } + // Call the delegate status = mDelegate.ManuallyTriggerTransport(connectionID, activationReason, timeControl); + + if (status == Status::Success) + { + mDelegate.GeneratePushTransportBeginEvent(connectionID, TransportTriggerTypeEnum::kCommand, MakeOptional(activationReason)); + } + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); } @@ -581,21 +985,14 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, Optional> connectionID = commandData.connectionID; - size_t count = 0; - size_t bufferSize = mCurrentConnections.size(); - - Platform::ScopedMemoryBuffer transportConfigurations; - if (!transportConfigurations.Calloc(bufferSize)) - { - ChipLogError(Zcl, "Memory allocation failed for forecast buffer"); - return; - } + std::vector transportConfigurations; if ((connectionID.HasValue() == false) || connectionID.Value().IsNull()) { if (mCurrentConnections.size() == 0) { - ChipLogError(Zcl, "HandleFindTransport: ConnectionID not found"); + ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID not found", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } @@ -604,7 +1001,7 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, { if (connection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) { - transportConfigurations[count++] = connection.transportConfiguration; + transportConfigurations.push_back(connection.transportConfiguration); } } } @@ -614,25 +1011,74 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, FindStreamTransportConnection(connectionID.Value().Value()); if (transportConfiguration == nullptr) { - ChipLogError(Zcl, "HandleFindTransport: ConnectionID not found"); + ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID not found", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) { - ChipLogError(Zcl, "HandleFindTransport: ConnectionID Not Found."); + ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID Not Found.", + AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } - transportConfigurations[count++] = transportConfiguration->transportConfiguration; + transportConfigurations.push_back(transportConfiguration->transportConfiguration); + } + + if (transportConfigurations.size() == 0) + { + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, Status::NotFound); } - response.transportConfigurations = DataModel::List( - Span(transportConfigurations.Get(), count)); + response.transportConfigurations = + DataModel::List(transportConfigurations.data(), transportConfigurations.size()); ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } +Status PushAvStreamTransportDelegate::GeneratePushTransportBeginEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason) +{ + Events::PushTransportBegin::Type event; + EventNumber eventNumber; + + event.connectionID = connectionID; + event.triggerType = triggerType; + event.activationReason = activationReason; + + CHIP_ERROR err = LogEvent(event, mEndpointId, eventNumber); + if (CHIP_NO_ERROR != err) + { + ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportBegin event: %" CHIP_ERROR_FORMAT, mEndpointId, + err.Format()); + return Status::Failure; + } + return Status::Success; +} + +Status PushAvStreamTransportDelegate::GeneratePushTransportEndEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason) +{ + Events::PushTransportEnd::Type event; + EventNumber eventNumber; + + event.connectionID = connectionID; + event.triggerType = triggerType; + event.activationReason = activationReason; + + CHIP_ERROR err = LogEvent(event, mEndpointId, eventNumber); + if (CHIP_NO_ERROR != err) + { + ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportEnd event: %" CHIP_ERROR_FORMAT, mEndpointId, + err.Format()); + return Status::Failure; + } + return Status::Success; +} + } // namespace PushAvStreamTransport } // namespace Clusters } // namespace app diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index c66e620cfb22fe..5e422139802564 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -29,7 +29,9 @@ namespace app { namespace Clusters { namespace PushAvStreamTransport { -static constexpr size_t kMaxUrlLength = 2000u; +static constexpr size_t kMaxUrlLength = 2000u; +static constexpr size_t kMaxCENCKeyLength = 16u; +static constexpr size_t kMaxCENCKeyIDLength = 16u; using SupportedFormatStruct = Structs::SupportedFormatStruct::Type; using CMAFContainerOptionsStruct = Structs::CMAFContainerOptionsStruct::Type; @@ -51,8 +53,36 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct { TransportTriggerOptionsStorage() {}; - TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType triggerOptions, - const BitFlags features) + TransportTriggerOptionsStorage(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) + { + *this = aTransportTriggerOptionsStorage; + } + + TransportTriggerOptionsStorage & operator=(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) + { + triggerType = aTransportTriggerOptionsStorage.triggerType; + + mTransportZoneOptions = aTransportTriggerOptionsStorage.mTransportZoneOptions; + + // Rebind motionZones to point to the copied vector if it was set + if (aTransportTriggerOptionsStorage.motionZones.HasValue() && !aTransportTriggerOptionsStorage.motionZones.Value().IsNull()) + { + motionZones.SetValue(DataModel::Nullable>( + Span(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); + } + else + { + motionZones.Missing(); + } + + motionSensitivity = aTransportTriggerOptionsStorage.motionSensitivity; + motionTimeControl = aTransportTriggerOptionsStorage.motionTimeControl; + maxPreRollLen = aTransportTriggerOptionsStorage.maxPreRollLen; + + return *this; + } + + TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType triggerOptions) { triggerType = triggerOptions.triggerType; @@ -91,8 +121,43 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct { CMAFContainerOptionsStorage() {}; - CMAFContainerOptionsStorage(Optional CMAFContainerOptions, - const BitFlags features) + CMAFContainerOptionsStorage(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) + { + *this = aCMAFContainerOptionsStorage; + } + + CMAFContainerOptionsStorage & operator=(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) + { + chunkDuration = aCMAFContainerOptionsStorage.chunkDuration; + + std::memcpy(mCENCKeyBuffer, aCMAFContainerOptionsStorage.mCENCKeyBuffer, sizeof(mCENCKeyBuffer)); + + std::memcpy(mCENCKeyIDBuffer, aCMAFContainerOptionsStorage.mCENCKeyIDBuffer, sizeof(mCENCKeyIDBuffer)); + + if (aCMAFContainerOptionsStorage.CENCKey.HasValue()) + { + CENCKey.SetValue(ByteSpan(mCENCKeyBuffer, aCMAFContainerOptionsStorage.CENCKey.Value().size())); + } + else + { + CENCKey.Missing(); + } + + metadataEnabled = aCMAFContainerOptionsStorage.metadataEnabled; + + if (aCMAFContainerOptionsStorage.CENCKeyID.HasValue()) + { + CENCKeyID.SetValue(ByteSpan(mCENCKeyIDBuffer, aCMAFContainerOptionsStorage.CENCKeyID.Value().size())); + } + else + { + CENCKeyID.Missing(); + } + + return *this; + } + + CMAFContainerOptionsStorage(Optional CMAFContainerOptions) { if (CMAFContainerOptions.HasValue() == true) { @@ -104,41 +169,55 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKey.Value(), CENCKeyBuffer); CENCKey.SetValue(CENCKeyBuffer); } - - if (features.Has(Feature::kMetadata) && CMAFContainerOptions.HasValue()) - { - metadataEnabled = CMAFContainerOptions.Value().metadataEnabled; - } else { - metadataEnabled.Missing(); + CENCKey.Missing(); } + metadataEnabled = CMAFContainerOptions.Value().metadataEnabled; + if (CMAFContainerOptions.Value().CENCKey.HasValue()) { MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKeyID.Value(), CENCKeyIDBuffer); CENCKeyID.SetValue(CENCKeyIDBuffer); } + else + { + CENCKeyID.Missing(); + } } } private: - uint8_t mCENCKeyBuffer[16]; - uint8_t mCENCKeyIDBuffer[16]; + uint8_t mCENCKeyBuffer[kMaxCENCKeyLength]; + uint8_t mCENCKeyIDBuffer[kMaxCENCKeyIDLength]; }; struct ContainerOptionsStorage : public ContainerOptionsStruct { ContainerOptionsStorage() {}; - ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType containerOptions, const BitFlags features) + ContainerOptionsStorage(const ContainerOptionsStorage & aContainerOptionsStorage) { *this = aContainerOptionsStorage; } + + ContainerOptionsStorage & operator=(const ContainerOptionsStorage & aContainerOptionsStorage) + { + containerType = aContainerOptionsStorage.containerType; + + mCMAFContainerStorage = aContainerOptionsStorage.mCMAFContainerStorage; + + CMAFContainerOptions.SetValue(mCMAFContainerStorage); + + return *this; + } + + ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType containerOptions) { containerType = containerOptions.containerType; if (containerType == ContainerFormatEnum::kCmaf) { - mCMAFContainerStorage = CMAFContainerOptionsStorage(containerOptions.CMAFContainerOptions, features); + mCMAFContainerStorage = CMAFContainerOptionsStorage(containerOptions.CMAFContainerOptions); CMAFContainerOptions.SetValue(mCMAFContainerStorage); } else @@ -155,7 +234,34 @@ struct TransportOptionsStorage : public TransportOptionsStruct { TransportOptionsStorage() {}; - TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions, const BitFlags features) + TransportOptionsStorage(const TransportOptionsStorage & aTransportOptionsStorage) { *this = aTransportOptionsStorage; } + + TransportOptionsStorage & operator=(const TransportOptionsStorage & aTransportOptionsStorage) + { + streamUsage = aTransportOptionsStorage.streamUsage; + videoStreamID = aTransportOptionsStorage.videoStreamID; + audioStreamID = aTransportOptionsStorage.audioStreamID; + endpointID = aTransportOptionsStorage.endpointID; + + // Deep copy the URL buffer + std::memcpy(mUrlBuffer, aTransportOptionsStorage.mUrlBuffer, kMaxUrlLength); + url = MutableCharSpan(mUrlBuffer, aTransportOptionsStorage.url.size()); + + // Copy internal storage objects + mTriggerOptionsStorage = aTransportOptionsStorage.mTriggerOptionsStorage; + triggerOptions = mTriggerOptionsStorage; + + ingestMethod = aTransportOptionsStorage.ingestMethod; + + mContainerOptionsStorage = aTransportOptionsStorage.mContainerOptionsStorage; + containerOptions = mContainerOptionsStorage; + + expiryTime = aTransportOptionsStorage.expiryTime; + + return *this; + } + + TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions) { streamUsage = transportOptions.streamUsage; videoStreamID = transportOptions.videoStreamID; @@ -166,12 +272,12 @@ struct TransportOptionsStorage : public TransportOptionsStruct CopyCharSpanToMutableCharSpanWithTruncation(transportOptions.url, urlBuffer); url = urlBuffer; - mTriggerOptionsStorage = TransportTriggerOptionsStorage(transportOptions.triggerOptions, features); + mTriggerOptionsStorage = TransportTriggerOptionsStorage(transportOptions.triggerOptions); triggerOptions = mTriggerOptionsStorage; ingestMethod = transportOptions.ingestMethod; - mContainerOptionsStorage = ContainerOptionsStorage(transportOptions.containerOptions, features); + mContainerOptionsStorage = ContainerOptionsStorage(transportOptions.containerOptions); containerOptions = mContainerOptionsStorage; expiryTime = transportOptions.expiryTime; @@ -187,6 +293,30 @@ struct TransportConfigurationStorage : public TransportConfigurationStruct { TransportConfigurationStorage() {} + TransportConfigurationStorage(const TransportConfigurationStorage & aTransportConfigurationStorage) + { + *this = aTransportConfigurationStorage; + } + + TransportConfigurationStorage & operator=(const TransportConfigurationStorage & aTransportConfigurationStorage) + { + connectionID = aTransportConfigurationStorage.connectionID; + transportStatus = aTransportConfigurationStorage.transportStatus; + + mTransportOptionsPtr = aTransportConfigurationStorage.mTransportOptionsPtr; + + if (mTransportOptionsPtr) + { + transportOptions.SetValue(*mTransportOptionsPtr); + } + else + { + transportOptions.Missing(); + } + + return *this; + } + TransportConfigurationStorage(const uint16_t aConnectionID, std::shared_ptr aTransportOptionsPtr) { connectionID = aConnectionID; @@ -194,9 +324,28 @@ struct TransportConfigurationStorage : public TransportConfigurationStruct /*Store the pointer to keep buffer alive*/ mTransportOptionsPtr = aTransportOptionsPtr; /*Convert Storage type to base type*/ - transportOptions.SetValue(*aTransportOptionsPtr); + if (mTransportOptionsPtr) + { + transportOptions.SetValue(*mTransportOptionsPtr); + } + else + { + transportOptions.Missing(); + } } std::shared_ptr GetTransportOptionsPtr() const { return mTransportOptionsPtr; } + void SetTransportOptionsPtr(std::shared_ptr aTransportOptionsPtr) + { + mTransportOptionsPtr = aTransportOptionsPtr; + if (mTransportOptionsPtr) + { + transportOptions.SetValue(*mTransportOptionsPtr); + } + else + { + transportOptions.Missing(); + } + } private: std::shared_ptr mTransportOptionsPtr; @@ -219,6 +368,8 @@ class PushAvStreamTransportDelegate virtual ~PushAvStreamTransportDelegate() = default; + void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; } + /** * @brief Handle Command Delegate for stream transport allocation with the provided transport configuration option. * @@ -227,8 +378,14 @@ class PushAvStreamTransportDelegate * @param connectionID[in] Indicates the connectionID to allocate. * * @return Success if the allocation is successful and a PushTransportConnectionID was produced; otherwise, the command SHALL - * be rejected with an appropriate error. The delegate is expected to process the transport options, allocate the transport - * and map it to the connectionID. On Success TransportConfigurationStruct is sent as response by the server. + * be rejected with Failure. + * + * The buffers storing URL, Trigger Options, Motion Zones, Container Options is owned by the PushAVStreamTransport Server. + * The buffers are allocated on success of AllocatePushTransport and deallocated on success of DeallocatePushTransport + * command. The delegate is expected to process the transport following transport options: URL : Validate the URL + * StreamUsage,VideoStreamID,AudioStreamID for selection of Stream. + * Allocate the transport and map it to the connectionID. + * On Success TransportConfigurationStruct is sent as response by the server. */ virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, const uint16_t connectionID) = 0; @@ -238,8 +395,8 @@ class PushAvStreamTransportDelegate * * @param connectionID[in] Indicates the connectionID to deallocate. * - * @return Success if the transport deallocation is successful; otherwise, the command SHALL be rejected with an appropriate - * error. + * @return Success if the transport deallocation is successful; otherwise, the delegate is expected to return status code BUSY + * if the transport is currently uploading. * */ virtual Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID) = 0; @@ -248,13 +405,16 @@ class PushAvStreamTransportDelegate * * @param connectionID [in] Indicates the connectionID of the stream transport to modify. * - * @param transportOptions [out] represents the Trigger Options to modify. + * @param transportOptions [out] represents the Transport Options to modify. + * + * The buffers storing URL, Trigger Options, Motion Zones, Container Options is owned by the PushAVStreamTransport Server. + * The allocated buffers are cleared and reassigned on success of ModifyPushTransport and deallocated on success of + * DeallocatePushTransport. * - * @return Success if the stream transport modification is successful; otherwise, the command SHALL be rejected with an - * appropriate error. + * @return Success if the stream transport modification is successful; otherwise, the command SHALL be rejected with Failure. */ - virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, - const TransportOptionsStruct & transportOptions) = 0; + virtual Protocols::InteractionModel::Status + ModifyPushTransport(const uint16_t connectionID, const Structs::TransportOptionsStruct::DecodableType transportOptions) = 0; /** * @brief Handle Command Delegate for Stream transport modification. @@ -262,8 +422,7 @@ class PushAvStreamTransportDelegate * @param connectionIDList [in] represent the list of connectionIDs for which new transport status to apply. * @param transportStatus [in] represents the updated status of the connection(s). * - * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with an - * appropriate error. + * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with Failure. */ virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus) = 0; @@ -276,9 +435,13 @@ class PushAvStreamTransportDelegate * * @param timeControl [in] Configuration to control the life cycle of a triggered transport. * - * @return Success if the stream transport trigger is successful; otherwise, the command SHALL be rejected with an -appropriate - * error. + * The delegate is expected to begin transmission using the timeControl values. + * + * Emitting of PushTransportBegin event is handled by the server when delegate returns success. + * + * The delegate should emit PushTransportEnd Event using GeneratePushTransportEndEvent() API when timeControl values when +transmission ends. + * @return Success if the stream transport trigger is successful; otherwise, the command SHALL be rejected with Failure. */ virtual Protocols::InteractionModel::Status ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, @@ -333,7 +496,7 @@ appropriate * * @param[in] connectionID Indicates the connectionID of the stream transport to check status * - * @return busy if transport is active else idle + * @return busy if transport is uploading else idle */ virtual PushAvStreamTransportStatusEnum GetTransportStatus(const uint16_t connectionID) = 0; @@ -363,6 +526,17 @@ appropriate * the Cluster have been loaded from Storage. */ virtual CHIP_ERROR PersistentAttributesLoadedCallback() = 0; + + // Send Push AV Stream Transport events + Protocols::InteractionModel::Status + GeneratePushTransportBeginEvent(const uint16_t connectionID, const TransportTriggerTypeEnum triggerType, + const Optional activationReason); + Protocols::InteractionModel::Status GeneratePushTransportEndEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason); + +protected: + EndpointId mEndpointId = 0; }; class PushAvStreamTransportServer : public AttributeAccessInterface, public CommandHandlerInterface @@ -397,7 +571,7 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm bool HasFeature(Feature feature) const; - // Attribute Getters + static void PushAVStreamTransportDeallocateCallback(chip::System::Layer *, void * callbackContext); private: enum class UpsertResultEnum : uint8_t @@ -406,6 +580,12 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm kUpdated = 0x01, }; + struct PushAVStreamTransportDeallocateCallbackContext + { + PushAvStreamTransportServer * instance; + uint16_t connectionID; + }; + PushAvStreamTransportDelegate & mDelegate; const BitFlags mFeatures; @@ -434,12 +614,22 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm // Helper functions uint16_t GenerateConnectionID(); + TransportConfigurationStorageWithFabricIndex * FindStreamTransportConnection(const uint16_t connectionID); + // Add/Remove Management functions for transport UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStorageWithFabricIndex & transportConfiguration); void RemoveStreamTransportConnection(const uint16_t connectionID); + /** + * @brief Schedule deallocate with a given timeout + * + * @param endpointId endpoint where DoorLockServer is running + * @param timeoutSec timeout in seconds + */ + void ScheduleTransportDeallocate(chip::EndpointId endpointId, uint32_t timeoutSec); + /** * @brief Inherited from CommandHandlerInterface */ From a459df797198ac3f830683a0346a5dc173feeef0 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Wed, 28 May 2025 09:47:33 +0000 Subject: [PATCH 21/40] Restyled by clang-format --- .../push-av-stream-transport-server.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 5e422139802564..98d2e7f89ea341 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -51,7 +51,7 @@ enum class PushAvStreamTransportStatusEnum : uint8_t struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct { - TransportTriggerOptionsStorage() {}; + TransportTriggerOptionsStorage(){}; TransportTriggerOptionsStorage(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) { @@ -119,7 +119,7 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct { - CMAFContainerOptionsStorage() {}; + CMAFContainerOptionsStorage(){}; CMAFContainerOptionsStorage(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) { @@ -196,7 +196,7 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct struct ContainerOptionsStorage : public ContainerOptionsStruct { - ContainerOptionsStorage() {}; + ContainerOptionsStorage(){}; ContainerOptionsStorage(const ContainerOptionsStorage & aContainerOptionsStorage) { *this = aContainerOptionsStorage; } @@ -232,7 +232,7 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct struct TransportOptionsStorage : public TransportOptionsStruct { - TransportOptionsStorage() {}; + TransportOptionsStorage(){}; TransportOptionsStorage(const TransportOptionsStorage & aTransportOptionsStorage) { *this = aTransportOptionsStorage; } From 890e11fdee1c23287ade98c4497ba275593b3b06 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Wed, 28 May 2025 15:52:11 +0530 Subject: [PATCH 22/40] update all-cluster-app.matter --- .../all-clusters-common/all-clusters-app.matter | 14 -------------- .../push-av-stream-transport-server.cpp | 2 +- .../push-av-stream-transport-server.h | 1 + 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 08ebe8d7bf26b2..9f2a2ca9a68c8e 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -7425,13 +7425,6 @@ provisional cluster PushAvStreamTransport = 1365 { kInvalidTransportStatus = 8; } - shared enum StreamUsageEnum : enum8 { - kInternal = 0; - kRecording = 1; - kAnalysis = 2; - kLiveView = 3; - } - enum TransportStatusEnum : enum8 { kActive = 0; kInactive = 1; @@ -7601,13 +7594,6 @@ provisional cluster PushAvStreamTransport = 1365 { kInvalidTransportStatus = 8; } - shared enum StreamUsageEnum : enum8 { - kInternal = 0; - kRecording = 1; - kAnalysis = 2; - kLiveView = 3; - } - enum TransportStatusEnum : enum8 { kActive = 0; kInactive = 1; diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 111cc6d9f6d469..ce5bac4c3e8014 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -1028,7 +1028,7 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, if (transportConfigurations.size() == 0) { - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, Status::NotFound); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); } response.transportConfigurations = diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 98d2e7f89ea341..6eb4c3074e4a5a 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -41,6 +41,7 @@ using TransportTriggerOptionsStruct = Structs::TransportTriggerOptions using TransportMotionTriggerTimeControlStruct = Structs::TransportMotionTriggerTimeControlStruct::Type; using TransportOptionsStruct = Structs::TransportOptionsStruct::Type; using TransportConfigurationStruct = Structs::TransportConfigurationStruct::Type; +using StreamUsageEnum = chip::app::Clusters::Globals::StreamUsageEnum; enum class PushAvStreamTransportStatusEnum : uint8_t { From b973744264a23d334bb01be2952844f6dd601529 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Thu, 29 May 2025 18:05:58 +0530 Subject: [PATCH 23/40] Address Review Comments --- .../push-av-stream-transport-delegate-impl.h | 17 +- ...push-av-stream-transport-delegate-impl.cpp | 47 +++-- .../push-av-stream-transport-server.cpp | 162 ++++++++++-------- .../push-av-stream-transport-server.h | 70 +++++--- 4 files changed, 191 insertions(+), 105 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index 18d799efdf64ca..6ebc6737d0ffed 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -55,11 +55,18 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate bool ValidateUrl(std::string url); - CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, const Optional> & videoStreamId, - const Optional> & audioStreamId); - CHIP_ERROR ValidateBandwidthLimit(StreamUsageEnum streamUsage, const Optional> & videoStreamId, - const Optional> & audioStreamId); - PushAvStreamTransportStatusEnum GetTransportStatus(const uint16_t connectionID); + Protocols::InteractionModel::Status ValidateBandwidthLimit(StreamUsageEnum streamUsage, + const Optional> & videoStreamId, + const Optional> & audioStreamId); + Protocols::InteractionModel::Status SelectVideoStream(StreamUsageEnum streamUsage, uint16_t & videoStreamId); + + Protocols::InteractionModel::Status SelectAudioStream(StreamUsageEnum streamUsage, uint16_t & audioStreamId); + + Protocols::InteractionModel::Status ValidateVideoStream(uint16_t videoStreamId); + + Protocols::InteractionModel::Status ValidateAudioStream(uint16_t audioStreamId); + + PushAvStreamTransportStatusEnum GetTransportBusyStatus(const uint16_t connectionID); void OnAttributeChanged(AttributeId attributeId); CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index 33a834724489d2..f43875e435fb22 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -105,13 +105,14 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::ManuallyTrigge return Status::Success; } -CHIP_ERROR PushAvStreamTransportManager::ValidateBandwidthLimit(StreamUsageEnum streamUsage, - const Optional> & videoStreamId, - const Optional> & audioStreamId) +Protocols::InteractionModel::Status +PushAvStreamTransportManager::ValidateBandwidthLimit(StreamUsageEnum streamUsage, + const Optional> & videoStreamId, + const Optional> & audioStreamId) { // TODO: Validates the requested stream usage against the camera's resource management. - // Returning CHIP_NO_ERROR to pass through checks in the Server Implementation. - return CHIP_NO_ERROR; + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; } bool PushAvStreamTransportManager::ValidateUrl(std::string url) @@ -119,17 +120,37 @@ bool PushAvStreamTransportManager::ValidateUrl(std::string url) return true; } -CHIP_ERROR -PushAvStreamTransportManager::ValidateStreamUsage(StreamUsageEnum streamUsage, - const Optional> & videoStreamId, - const Optional> & audioStreamId) +Protocols::InteractionModel::Status PushAvStreamTransportManager::SelectVideoStream(StreamUsageEnum streamUsage, + uint16_t & videoStreamId) { - // TODO: Validates the requested stream usage against the camera's resource management and stream priority policies. - // Returning CHIP_NO_ERROR to pass through checks in the Server Implementation. - return CHIP_NO_ERROR; + // TODO: Select and Assign videoStreamID from the allocated videoStreams + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; +} + +Protocols::InteractionModel::Status PushAvStreamTransportManager::SelectAudioStream(StreamUsageEnum streamUsage, + uint16_t & audioStreamId) +{ + // TODO: Select and Assign audioStreamID from the allocated audioStreams + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; +} + +Protocols::InteractionModel::Status PushAvStreamTransportManager::ValidateVideoStream(uint16_t videoStreamId) +{ + // TODO: Validate videoStreamID from the allocated videoStreams + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; +} + +Protocols::InteractionModel::Status PushAvStreamTransportManager::ValidateAudioStream(uint16_t audioStreamId) +{ + // TODO: Validate audioStreamID from the allocated audioStreams + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; } -PushAvStreamTransportStatusEnum PushAvStreamTransportManager::GetTransportStatus(const uint16_t connectionID) +PushAvStreamTransportStatusEnum PushAvStreamTransportManager::GetTransportBusyStatus(const uint16_t connectionID) { for (PushAvStream & stream : pushavStreams) { diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index ce5bac4c3e8014..bde17244d1ed16 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -261,6 +261,18 @@ PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connec return nullptr; } +TransportConfigurationStorageWithFabricIndex * +PushAvStreamTransportServer::FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, FabricIndex fabricIndex) +{ + for (auto & transportConnection : mCurrentConnections) + { + if (transportConnection.transportConfiguration.connectionID == connectionID && + transportConnection.fabricIndex == fabricIndex) + return &transportConnection; + } + return nullptr; +} + uint16_t PushAvStreamTransportServer::GenerateConnectionID() { static uint16_t lastID = 0; @@ -548,12 +560,12 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c return; } - // Validate ZoneId + // Todo:Validate ZoneId // Validate Bandwidth Requirement - CHIP_ERROR err = mDelegate.ValidateBandwidthLimit(transportOptions.streamUsage, transportOptions.videoStreamID, - transportOptions.audioStreamID); - if (err != CHIP_NO_ERROR) + Status status = mDelegate.ValidateBandwidthLimit(transportOptions.streamUsage, transportOptions.videoStreamID, + transportOptions.audioStreamID); + if (status != Status::Success) { ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Resource Exhausted", AttributeAccessInterface::GetEndpointId().Value()); @@ -561,32 +573,75 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c return; } - // Validate the StreamUsageEnum as per resource management and stream priorities. - err = - mDelegate.ValidateStreamUsage(transportOptions.streamUsage, transportOptions.videoStreamID, transportOptions.audioStreamID); - if (err != CHIP_NO_ERROR) + std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + + if (transportOptionsPtr == nullptr) { - auto status = to_underlying(StatusCodeEnum::kInvalidStream); - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Stream", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Memory Allocation failed for transportOptions", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); return; } - uint16_t connectionID = GenerateConnectionID(); + if (transportOptions.videoStreamID.HasValue()) + { + if (transportOptions.videoStreamID.Value().IsNull() == true) + { + uint16_t videoStreamID; - if (connectionID == kMaxConnectionId) + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + mDelegate.SelectVideoStream(transportOptions.streamUsage, videoStreamID)); + + if (delegateStatus.IsSuccess() == false) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); + return; + } + + transportOptionsPtr->videoStreamID.SetValue(videoStreamID); + } + else + { + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + mDelegate.ValidateVideoStream(transportOptions.videoStreamID.Value().Value())); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); + return; + } + } + + if (transportOptions.audioStreamID.HasValue() && transportOptions.audioStreamID.Value().IsNull()) { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Max Connections Exhausted", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); - return; + if (transportOptions.audioStreamID.Value().IsNull() == true) + { + uint16_t audioStreamID; + + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + mDelegate.SelectAudioStream(transportOptions.streamUsage, audioStreamID)); + + if (delegateStatus.IsSuccess() == false) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); + return; + } + + transportOptionsPtr->audioStreamID.SetValue(audioStreamID); + } + else + { + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + mDelegate.ValidateAudioStream(transportOptions.videoStreamID.Value().Value())); + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); + return; + } } - std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + uint16_t connectionID = GenerateConnectionID(); - if (transportOptionsPtr == nullptr) + if (connectionID == kMaxConnectionId) { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Memory Allocation failed for transportOptions", + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Max Connections Exhausted", AttributeAccessInterface::GetEndpointId().Value()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); return; @@ -594,7 +649,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c TransportConfigurationStorage outTransportConfiguration(connectionID, transportOptionsPtr); - Status status = mDelegate.AllocatePushTransport(*transportOptionsPtr, connectionID); + status = mDelegate.AllocatePushTransport(*transportOptionsPtr, connectionID); if (status == Status::Success) { @@ -623,8 +678,10 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c void PushAvStreamTransportServer::HandleDeallocatePushTransport( HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & commandData) { - uint16_t connectionID = commandData.connectionID; - TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); + uint16_t connectionID = commandData.connectionID; + FabricIndex FabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); + TransportConfigurationStorageWithFabricIndex * transportConfiguration = + FindStreamTransportConnectionWithinFabric(connectionID, FabricIndex); if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleDeallocatePushTransport[ep=%d]: ConnectionID Not Found.", @@ -633,14 +690,6 @@ void PushAvStreamTransportServer::HandleDeallocatePushTransport( return; } - if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) - { - ChipLogError(Zcl, "HandleDeallocatePushTransport[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } - // Call the delegate auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode(mDelegate.DeallocatePushTransport(connectionID)); @@ -772,17 +821,12 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); }); - TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); + FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - if (transportConfiguration == nullptr) - { - ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } + TransportConfigurationStorageWithFabricIndex * transportConfiguration = + FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); - if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) + if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: ConnectionID Not Found.", AttributeAccessInterface::GetEndpointId().Value()); @@ -790,7 +834,7 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx return; } - if (mDelegate.GetTransportStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) + if (mDelegate.GetTransportBusyStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) { ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: Connection is Busy", AttributeAccessInterface::GetEndpointId().Value()); @@ -838,7 +882,9 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, } else { - TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID.Value()); + FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); + TransportConfigurationStorageWithFabricIndex * transportConfiguration = + FindStreamTransportConnectionWithinFabric(connectionID.Value(), fabricIndex); if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleSetTransportStatus[ep=%d]: ConnectionID Not Found.", @@ -846,13 +892,6 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } - if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) - { - ChipLogError(Zcl, "HandleSetTransportStatus[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } connectionIDList.push_back(connectionID.Value()); } // Call the delegate @@ -903,7 +942,9 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( }); } - TransportConfigurationStorageWithFabricIndex * transportConfiguration = FindStreamTransportConnection(connectionID); + FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); + TransportConfigurationStorageWithFabricIndex * transportConfiguration = + FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); if (transportConfiguration == nullptr) { @@ -913,15 +954,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( return; } - if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) - { - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } - - if (mDelegate.GetTransportStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) + if (mDelegate.GetTransportBusyStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) { ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Connection is Busy", AttributeAccessInterface::GetEndpointId().Value()); @@ -1007,8 +1040,9 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, } else { + FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); TransportConfigurationStorageWithFabricIndex * transportConfiguration = - FindStreamTransportConnection(connectionID.Value().Value()); + FindStreamTransportConnectionWithinFabric(connectionID.Value().Value(), fabricIndex); if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID not found", @@ -1016,13 +1050,7 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); return; } - if (transportConfiguration->fabricIndex != ctx.mCommandHandler.GetAccessingFabricIndex()) - { - ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } + transportConfigurations.push_back(transportConfiguration->transportConfiguration); } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 6eb4c3074e4a5a..05b20b1d1c49d3 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -52,7 +52,7 @@ enum class PushAvStreamTransportStatusEnum : uint8_t struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct { - TransportTriggerOptionsStorage(){}; + TransportTriggerOptionsStorage() {}; TransportTriggerOptionsStorage(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) { @@ -120,7 +120,7 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct { - CMAFContainerOptionsStorage(){}; + CMAFContainerOptionsStorage() {}; CMAFContainerOptionsStorage(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) { @@ -197,7 +197,7 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct struct ContainerOptionsStorage : public ContainerOptionsStruct { - ContainerOptionsStorage(){}; + ContainerOptionsStorage() {}; ContainerOptionsStorage(const ContainerOptionsStorage & aContainerOptionsStorage) { *this = aContainerOptionsStorage; } @@ -233,7 +233,7 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct struct TransportOptionsStorage : public TransportOptionsStruct { - TransportOptionsStorage(){}; + TransportOptionsStorage() {}; TransportOptionsStorage(const TransportOptionsStorage & aTransportOptionsStorage) { *this = aTransportOptionsStorage; } @@ -467,30 +467,57 @@ transmission ends. * @param[in] videoStreamId Optional identifier for the requested video stream. * @param[in] audioStreamId Optional identifier for the requested audio stream. * - * @return CHIP_ERROR CHIP_NO_ERROR if the stream usage is valid; an appropriate error code otherwise. + * @return Status::Success if the stream usage is valid; and Status::ResourceExhausted otherwise. */ - virtual CHIP_ERROR ValidateBandwidthLimit(StreamUsageEnum streamUsage, - const Optional> & videoStreamId, - const Optional> & audioStreamId) = 0; + virtual Protocols::InteractionModel::Status + ValidateBandwidthLimit(StreamUsageEnum streamUsage, const Optional> & videoStreamId, + const Optional> & audioStreamId) = 0; /** - * @brief Validates the requested stream usage against the camera's resource management - * and stream priority policies. + * @brief Assign an existing Video Stream as per the camera's resource management and stream priority policies using requested + * stream usage. * - * The implementation SHALL ensure: - * - The requested stream usage (streamUsage) is allowed given the current allocation of - * camera resources (e.g. CPU, memory, network bandwidth) and the prioritized stream list. + * @param[in] streamUsage The desired usage type for the stream (e.g. live view, recording, etc.). + * @param[in] videoStreamId Identifier for the requested video stream. + * + * @return Assign the selected videoStreamID and return Status::Success. Return Status::InvalidStream if there are no allocated + * VideoStream. + */ + virtual Protocols::InteractionModel::Status SelectVideoStream(StreamUsageEnum streamUsage, uint16_t & videoStreamId) = 0; + /** + * @brief Assign an existing Audio Stream as per the camera's resource management and stream priority policies using requested + * stream usage. * * @param[in] streamUsage The desired usage type for the stream (e.g. live view, recording, etc.). - * @param[in] videoStreamId Optional identifier for the requested video stream. - * @param[in] audioStreamId Optional identifier for the requested audio stream. + * @param[in] audioStreamId Identifier for the requested audio stream. + * + * @return Assign the selected audioStreamID and return Status::Success. Return Status::InvalidStream if there are no allocated + * audioStream. + */ + + virtual Protocols::InteractionModel::Status SelectAudioStream(StreamUsageEnum streamUsage, uint16_t & audioStreamId) = 0; + /** + * @brief Validate that the videoStream corresponding to the videoStreamID is allocated. + * + * @param[in] videoStreamId Identifier for the requested video stream. * - * @return CHIP_ERROR CHIP_NO_ERROR if the stream usage is valid; an appropriate error code otherwise. + * @return Status::Success if there is an allocated video stream. Return Status::InvalidStream if there are no allocated + * video stream with videoStreamID. */ - virtual CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, - const Optional> & videoStreamId, - const Optional> & audioStreamId) = 0; + + virtual Protocols::InteractionModel::Status ValidateVideoStream(uint16_t videoStreamId) = 0; + + /** + * @brief Validate that the audioStream corresponding to the audioStreamID is allocated. + * + * @param[in] audioStreamId Identifier for the requested audio stream. + * + * @return Status::Success if there is an allocated audio stream. Return Status::InvalidStream if there are no allocated + * audio stream with audioStreamID. + */ + + virtual Protocols::InteractionModel::Status ValidateAudioStream(uint16_t audioStreamId) = 0; /** * @brief Gets the status of the transport @@ -499,7 +526,7 @@ transmission ends. * * @return busy if transport is uploading else idle */ - virtual PushAvStreamTransportStatusEnum GetTransportStatus(const uint16_t connectionID) = 0; + virtual PushAvStreamTransportStatusEnum GetTransportBusyStatus(const uint16_t connectionID) = 0; /** * @brief Delegate callback for notifying change in an attribute. @@ -618,6 +645,9 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm TransportConfigurationStorageWithFabricIndex * FindStreamTransportConnection(const uint16_t connectionID); + TransportConfigurationStorageWithFabricIndex * FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, + FabricIndex fabricIndex); + // Add/Remove Management functions for transport UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStorageWithFabricIndex & transportConfiguration); From f9d4c73baba9894e4f681303a5bc1caa72c7c25f Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 29 May 2025 12:40:23 +0000 Subject: [PATCH 24/40] Restyled by clang-format --- .../push-av-stream-transport-server.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 05b20b1d1c49d3..046d9eda147b7f 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -52,7 +52,7 @@ enum class PushAvStreamTransportStatusEnum : uint8_t struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct { - TransportTriggerOptionsStorage() {}; + TransportTriggerOptionsStorage(){}; TransportTriggerOptionsStorage(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) { @@ -120,7 +120,7 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct { - CMAFContainerOptionsStorage() {}; + CMAFContainerOptionsStorage(){}; CMAFContainerOptionsStorage(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) { @@ -197,7 +197,7 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct struct ContainerOptionsStorage : public ContainerOptionsStruct { - ContainerOptionsStorage() {}; + ContainerOptionsStorage(){}; ContainerOptionsStorage(const ContainerOptionsStorage & aContainerOptionsStorage) { *this = aContainerOptionsStorage; } @@ -233,7 +233,7 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct struct TransportOptionsStorage : public TransportOptionsStruct { - TransportOptionsStorage() {}; + TransportOptionsStorage(){}; TransportOptionsStorage(const TransportOptionsStorage & aTransportOptionsStorage) { *this = aTransportOptionsStorage; } From d4aff2680134a55ce6c2f7364661021724592de8 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 30 May 2025 14:21:56 +0530 Subject: [PATCH 25/40] [ESP32]: Optimize flash usage for all-clusters-app --- examples/all-clusters-app/esp32/sdkconfig.defaults | 3 +++ examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults | 3 +++ examples/all-clusters-app/esp32/sdkconfig_m5stack_rpc.defaults | 3 +++ examples/all-clusters-app/esp32/sdkconfig_rpc.defaults | 3 +++ 4 files changed, 12 insertions(+) diff --git a/examples/all-clusters-app/esp32/sdkconfig.defaults b/examples/all-clusters-app/esp32/sdkconfig.defaults index 4dcff1583e4f87..3f1e85dedb135e 100644 --- a/examples/all-clusters-app/esp32/sdkconfig.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig.defaults @@ -69,3 +69,6 @@ CONFIG_MAX_GROUP_ENDPOINTS_PER_FABRIC=3 # Enable OTA Requestor CONFIG_ENABLE_OTA_REQUESTOR=y + +# Optimize Flash +CONFIG_NEWLIB_NANO_FORMAT=y diff --git a/examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults b/examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults index 03691548ea91db..7a3e15a9f48a4a 100644 --- a/examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig_m5stack.defaults @@ -79,3 +79,6 @@ CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y CONFIG_EVENT_LOGGING_CRIT_BUFFER_SIZE=512 CONFIG_EVENT_LOGGING_INFO_BUFFER_SIZE=512 CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE=512 + +# Optimize Flash +CONFIG_NEWLIB_NANO_FORMAT=y diff --git a/examples/all-clusters-app/esp32/sdkconfig_m5stack_rpc.defaults b/examples/all-clusters-app/esp32/sdkconfig_m5stack_rpc.defaults index 0a426dffd0066e..62efd0b551a07f 100644 --- a/examples/all-clusters-app/esp32/sdkconfig_m5stack_rpc.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig_m5stack_rpc.defaults @@ -91,3 +91,6 @@ CONFIG_BT_NIMBLE_ROLE_OBSERVER=n CONFIG_EVENT_LOGGING_CRIT_BUFFER_SIZE=512 CONFIG_EVENT_LOGGING_INFO_BUFFER_SIZE=512 CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE=512 + +# Optimize Flash +CONFIG_NEWLIB_NANO_FORMAT=y \ No newline at end of file diff --git a/examples/all-clusters-app/esp32/sdkconfig_rpc.defaults b/examples/all-clusters-app/esp32/sdkconfig_rpc.defaults index 3faf5754c82a99..c04c0021a59247 100644 --- a/examples/all-clusters-app/esp32/sdkconfig_rpc.defaults +++ b/examples/all-clusters-app/esp32/sdkconfig_rpc.defaults @@ -90,3 +90,6 @@ CONFIG_BT_NIMBLE_ROLE_OBSERVER=n CONFIG_EVENT_LOGGING_CRIT_BUFFER_SIZE=512 CONFIG_EVENT_LOGGING_INFO_BUFFER_SIZE=512 CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE=512 + +# Optimize Flash +CONFIG_NEWLIB_NANO_FORMAT=y \ No newline at end of file From 8394c3a24d28dfe091c2af97444957bc600fe6ab Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 30 May 2025 16:27:44 +0530 Subject: [PATCH 26/40] zap_generate for updated push_av_stream_transport xml --- .../all-clusters-common/all-clusters-app.matter | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index eed8417808a63e..4c832a77b0ad27 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -7504,10 +7504,11 @@ provisional cluster PushAvStreamTransport = 1365 { optional epoch_s expiryTime = 8; } - struct TransportConfigurationStruct { + fabric_scoped struct TransportConfigurationStruct { int16u connectionID = 0; TransportStatusEnum transportStatus = 1; optional TransportOptionsStruct transportOptions = 2; + fabric_idx fabricIndex = 254; } struct SupportedFormatStruct { @@ -7673,10 +7674,11 @@ provisional cluster PushAvStreamTransport = 1365 { optional epoch_s expiryTime = 8; } - struct TransportConfigurationStruct { + fabric_scoped struct TransportConfigurationStruct { int16u connectionID = 0; TransportStatusEnum transportStatus = 1; optional TransportOptionsStruct transportOptions = 2; + fabric_idx fabricIndex = 254; } struct SupportedFormatStruct { From 763b1ee167f7e8016c6901eef63315abfe27ce57 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 30 May 2025 18:17:12 +0530 Subject: [PATCH 27/40] Update Push AV server implementation for fabric-scoped transportConfigurationStruct --- .../push-av-stream-transport-delegate-impl.h | 2 +- ...push-av-stream-transport-delegate-impl.cpp | 2 +- .../push-av-stream-transport-server.cpp | 111 ++++++++++-------- .../push-av-stream-transport-server.h | 31 ++--- 4 files changed, 81 insertions(+), 65 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index 6ebc6737d0ffed..71d2d6c3628de1 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -69,7 +69,7 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate PushAvStreamTransportStatusEnum GetTransportBusyStatus(const uint16_t connectionID); void OnAttributeChanged(AttributeId attributeId); - CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); + CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections); CHIP_ERROR PersistentAttributesLoadedCallback(); void Init(); diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index f43875e435fb22..03c05ebfe0623a 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -172,7 +172,7 @@ void PushAvStreamTransportManager::Init() ChipLogProgress(Zcl, "Push AV Stream Transport Initialized"); } CHIP_ERROR -PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) +PushAvStreamTransportManager::LoadCurrentConnections(std::vector & currentConnections) { ChipLogProgress(Zcl, "Push AV Current Connections loaded"); diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index bde17244d1ed16..291de16c908e1e 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -94,20 +94,19 @@ CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const At { for (const auto & currentConnections : mCurrentConnections) { - ReturnErrorOnFailure(encoder.Encode(currentConnections.transportConfiguration)); + ReturnErrorOnFailure(encoder.Encode(currentConnections)); } return CHIP_NO_ERROR; } -PushAvStreamTransportServer::UpsertResultEnum PushAvStreamTransportServer::UpsertStreamTransportConnection( - const TransportConfigurationStorageWithFabricIndex & transportConfiguration) +PushAvStreamTransportServer::UpsertResultEnum +PushAvStreamTransportServer::UpsertStreamTransportConnection(const TransportConfigurationStorage & transportConfiguration) { UpsertResultEnum result; - auto it = std::find_if(mCurrentConnections.begin(), mCurrentConnections.end(), - [id = transportConfiguration.transportConfiguration.connectionID](const auto & existing) { - return existing.transportConfiguration.connectionID == id; - }); + auto it = + std::find_if(mCurrentConnections.begin(), mCurrentConnections.end(), + [id = transportConfiguration.connectionID](const auto & existing) { return existing.connectionID == id; }); if (it != mCurrentConnections.end()) { @@ -132,8 +131,8 @@ void PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t // Erase-Remove idiom mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), - [transportConnectionId](const TransportConfigurationStorageWithFabricIndex & s) { - return s.transportConfiguration.connectionID == transportConnectionId; + [transportConnectionId](const TransportConfigurationStorage & s) { + return s.connectionID == transportConnectionId; }), mCurrentConnections.end()); @@ -250,25 +249,30 @@ void PushAvStreamTransportServer::InvokeCommand(HandlerContext & handlerContext) } } -TransportConfigurationStorageWithFabricIndex * -PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) +TransportConfigurationStorage * PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) { for (auto & transportConnection : mCurrentConnections) { - if (transportConnection.transportConfiguration.connectionID == connectionID) + if (transportConnection.connectionID == connectionID) + { return &transportConnection; + } } return nullptr; } -TransportConfigurationStorageWithFabricIndex * -PushAvStreamTransportServer::FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, FabricIndex fabricIndex) +TransportConfigurationStorage * PushAvStreamTransportServer::FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, + FabricIndex fabricIndex) { for (auto & transportConnection : mCurrentConnections) { - if (transportConnection.transportConfiguration.connectionID == connectionID && - transportConnection.fabricIndex == fabricIndex) - return &transportConnection; + if (transportConnection.connectionID == connectionID) + { + if (transportConnection.GetFabricIndex() == fabricIndex) + { + return &transportConnection; + } + } } return nullptr; } @@ -605,8 +609,11 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( mDelegate.ValidateVideoStream(transportOptions.videoStreamID.Value().Value())); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); - return; + if (delegateStatus.IsSuccess() == false) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); + return; + } } } @@ -632,8 +639,11 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( mDelegate.ValidateAudioStream(transportOptions.videoStreamID.Value().Value())); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); - return; + if (delegateStatus.IsSuccess() == false) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); + return; + } } } @@ -647,8 +657,6 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c return; } - TransportConfigurationStorage outTransportConfiguration(connectionID, transportOptionsPtr); - status = mDelegate.AllocatePushTransport(*transportOptionsPtr, connectionID); if (status == Status::Success) @@ -656,9 +664,12 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c // add connection to CurrentConnections FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorageWithFabricIndex transportConfiguration({ outTransportConfiguration, peerFabricIndex }); + TransportConfigurationStorage outTransportConfiguration(connectionID, transportOptionsPtr); + + outTransportConfiguration.SetFabricIndex(peerFabricIndex); + + UpsertStreamTransportConnection(outTransportConfiguration); - UpsertStreamTransportConnection(transportConfiguration); response.transportConfiguration = outTransportConfiguration; // ExpiryTime Handling @@ -678,10 +689,9 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c void PushAvStreamTransportServer::HandleDeallocatePushTransport( HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & commandData) { - uint16_t connectionID = commandData.connectionID; - FabricIndex FabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorageWithFabricIndex * transportConfiguration = - FindStreamTransportConnectionWithinFabric(connectionID, FabricIndex); + uint16_t connectionID = commandData.connectionID; + FabricIndex FabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); + TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, FabricIndex); if (transportConfiguration == nullptr) { ChipLogError(Zcl, "HandleDeallocatePushTransport[ep=%d]: ConnectionID Not Found.", @@ -823,8 +833,7 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorageWithFabricIndex * transportConfiguration = - FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); + TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); if (transportConfiguration == nullptr) { @@ -856,7 +865,7 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx if (status == Status::Success) { - transportConfiguration->transportConfiguration.SetTransportOptionsPtr(transportOptionsPtr); + transportConfiguration->SetTransportOptionsPtr(transportOptionsPtr); } ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); @@ -868,6 +877,13 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, Status status = Status::Success; DataModel::Nullable connectionID = commandData.connectionID; auto & transportStatus = commandData.transportStatus; + + VerifyOrReturn(transportStatus != TransportStatusEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); + }); + std::vector connectionIDList; if (connectionID.IsNull()) @@ -876,14 +892,14 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, { if (transportConnection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) { - connectionIDList.push_back(transportConnection.transportConfiguration.connectionID); + connectionIDList.push_back(transportConnection.connectionID); } } } else { FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorageWithFabricIndex * transportConfiguration = + TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID.Value(), fabricIndex); if (transportConfiguration == nullptr) { @@ -902,9 +918,9 @@ void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, { for (auto & transportConnection : mCurrentConnections) { - if (transportConnection.transportConfiguration.connectionID == connID) + if (transportConnection.connectionID == connID) { - transportConnection.transportConfiguration.transportStatus = transportStatus; + transportConnection.transportStatus = transportStatus; } } } @@ -942,9 +958,8 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( }); } - FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorageWithFabricIndex * transportConfiguration = - FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); + FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); + TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); if (transportConfiguration == nullptr) { @@ -962,7 +977,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( return; } - if (transportConfiguration->transportConfiguration.transportStatus == TransportStatusEnum::kInactive) + if (transportConfiguration->transportStatus == TransportStatusEnum::kInactive) { auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTransportStatus); ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Transport status", @@ -970,10 +985,9 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; } - if (transportConfiguration->transportConfiguration.transportOptions.HasValue()) + if (transportConfiguration->transportOptions.HasValue()) { - if (transportConfiguration->transportConfiguration.transportOptions.Value().triggerOptions.triggerType == - TransportTriggerTypeEnum::kContinuous) + if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kContinuous) { auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTriggerType); @@ -982,8 +996,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); return; } - if (transportConfiguration->transportConfiguration.transportOptions.Value().triggerOptions.triggerType == - TransportTriggerTypeEnum::kCommand && + if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand && timeControl.HasValue() == false) { @@ -997,7 +1010,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( // When trigger type is motion in the allocated transport but triggering it manually if (timeControl.HasValue() == false) { - timeControl = transportConfiguration->transportConfiguration.transportOptions.Value().triggerOptions.motionTimeControl; + timeControl = transportConfiguration->transportOptions.Value().triggerOptions.motionTimeControl; } // Call the delegate @@ -1034,14 +1047,14 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, { if (connection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) { - transportConfigurations.push_back(connection.transportConfiguration); + transportConfigurations.push_back(connection); } } } else { FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorageWithFabricIndex * transportConfiguration = + TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID.Value().Value(), fabricIndex); if (transportConfiguration == nullptr) { @@ -1051,7 +1064,7 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, return; } - transportConfigurations.push_back(transportConfiguration->transportConfiguration); + transportConfigurations.push_back(*transportConfiguration); } if (transportConfigurations.size() == 0) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index 046d9eda147b7f..f0577ba5deaaa5 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -303,6 +303,7 @@ struct TransportConfigurationStorage : public TransportConfigurationStruct { connectionID = aTransportConfigurationStorage.connectionID; transportStatus = aTransportConfigurationStorage.transportStatus; + fabricIndex = aTransportConfigurationStorage.fabricIndex; mTransportOptionsPtr = aTransportConfigurationStorage.mTransportOptionsPtr; @@ -352,12 +353,6 @@ struct TransportConfigurationStorage : public TransportConfigurationStruct std::shared_ptr mTransportOptionsPtr; }; -struct TransportConfigurationStorageWithFabricIndex -{ - TransportConfigurationStorage transportConfiguration; - FabricIndex fabricIndex; -}; - /** @brief * Defines interfaces for implementing application-specific logic for various aspects of the PushAvStreamTransport Delegate. * Specifically, it defines interfaces for the command handling and loading of the allocated streams. @@ -423,7 +418,16 @@ class PushAvStreamTransportDelegate * @param connectionIDList [in] represent the list of connectionIDs for which new transport status to apply. * @param transportStatus [in] represents the updated status of the connection(s). * - * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with Failure. + * If the transportStatus field is set to Inactive(1).Disable transmissions on the transport.Remove any queued items for + * upload.Cancel any uploads currently in progress. + * Emit GeneratePushTransportEndEvent on stop. + * + * Else If the TransportStatus field is set to Active(0).Enable transmissions on the transport. + * If the transports trigger type is Continuous,begin Transmission immediately. + * Else If the transports trigger type is Command or Motion, and the underlying trigger is currently active and still within + * the Time Control Bounds,begin Transmission immediately. + * Emit GeneratePushTransportBeginEvent on start. + * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with Failure. */ virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus) = 0; @@ -441,7 +445,7 @@ class PushAvStreamTransportDelegate * Emitting of PushTransportBegin event is handled by the server when delegate returns success. * * The delegate should emit PushTransportEnd Event using GeneratePushTransportEndEvent() API when timeControl values when -transmission ends. + transmission ends. * @return Success if the stream transport trigger is successful; otherwise, the command SHALL be rejected with Failure. */ virtual Protocols::InteractionModel::Status @@ -547,7 +551,7 @@ transmission ends. * The required buffers are managed by TransportConfigurationStorage, the delegate function is expected to populate the vector * correctly. */ - virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; + virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; /** * @brief Callback into the delegate once persistent attributes managed by @@ -623,7 +627,7 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm /*Moved from TransportConfigurationStruct to TransportConfigurationStructWithFabricIndex * to perform fabric index checks */ - std::vector mCurrentConnections; + std::vector mCurrentConnections; /** * IM-level implementation of read @@ -643,13 +647,12 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm // Helper functions uint16_t GenerateConnectionID(); - TransportConfigurationStorageWithFabricIndex * FindStreamTransportConnection(const uint16_t connectionID); + TransportConfigurationStorage * FindStreamTransportConnection(const uint16_t connectionID); - TransportConfigurationStorageWithFabricIndex * FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, - FabricIndex fabricIndex); + TransportConfigurationStorage * FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, FabricIndex fabricIndex); // Add/Remove Management functions for transport - UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStorageWithFabricIndex & transportConfiguration); + UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStorage & transportConfiguration); void RemoveStreamTransportConnection(const uint16_t connectionID); From 1da03d394c0b9f87155174efe3b498bf4dbbbf0e Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Wed, 4 Jun 2025 03:13:54 +0530 Subject: [PATCH 28/40] Address review comments --- .../push-av-stream-transport-server.cpp | 134 +++++++--- .../push-av-stream-transport-server.h | 233 ++++++++++++------ 2 files changed, 264 insertions(+), 103 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 291de16c908e1e..eb52b010f82fc2 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -57,6 +57,10 @@ PushAvStreamTransportServer::PushAvStreamTransportServer(PushAvStreamTransportDe PushAvStreamTransportServer::~PushAvStreamTransportServer() { + for (const auto & timerContext : mtimerContexts) + { + DeviceLayer::SystemLayer().CancelTimer(PushAVStreamTransportDeallocateCallback, static_cast(timerContext.get())); + } Shutdown(); } @@ -90,11 +94,15 @@ CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeSupportedFormats(const Attr return CHIP_NO_ERROR; } -CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder) +CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, + FabricIndex fabricIndex) { for (const auto & currentConnections : mCurrentConnections) { - ReturnErrorOnFailure(encoder.Encode(currentConnections)); + if (currentConnections.fabricIndex == fabricIndex) + { + ReturnErrorOnFailure(encoder.Encode(currentConnections)); + } } return CHIP_NO_ERROR; @@ -125,6 +133,27 @@ PushAvStreamTransportServer::UpsertStreamTransportConnection(const TransportConf return result; } +PushAvStreamTransportServer::UpsertResultEnum +PushAvStreamTransportServer::UpsertTimerAppState(std::shared_ptr timerAppState) +{ + UpsertResultEnum result; + auto it = std::find_if(mtimerContexts.begin(), mtimerContexts.end(), + [id = timerAppState->connectionID](const auto & existing) { return existing->connectionID == id; }); + + if (it != mtimerContexts.end()) + { + *it = timerAppState; + result = UpsertResultEnum::kUpdated; + } + else + { + mtimerContexts.push_back(timerAppState); + result = UpsertResultEnum::kInserted; + } + + return result; +} + void PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t transportConnectionId) { size_t originalSize = mCurrentConnections.size(); @@ -145,6 +174,22 @@ void PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t } } +void PushAvStreamTransportServer::RemoveTimerAppState(const uint16_t connectionID) +{ + // Erase-Remove idiom + auto it = std::remove_if(mtimerContexts.begin(), mtimerContexts.end(), + [connectionID](const std::shared_ptr & s) { + if (s->connectionID == connectionID) + { + DeviceLayer::SystemLayer().CancelTimer(NULL, static_cast(s.get())); + return true; // Remove from vector + } + return false; // Keep in vector + }); + + mtimerContexts.erase(it, mtimerContexts.end()); +} + CHIP_ERROR PushAvStreamTransportServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { VerifyOrDie(aPath.mClusterId == PushAvStreamTransport::Id); @@ -162,8 +207,9 @@ CHIP_ERROR PushAvStreamTransportServer::Read(const ConcreteReadAttributePath & a break; case CurrentConnections::Id: - ReturnErrorOnFailure(aEncoder.EncodeList( - [this](const auto & encoder) -> CHIP_ERROR { return this->ReadAndEncodeCurrentConnections(encoder); })); + ReturnErrorOnFailure(aEncoder.EncodeList([this, &aEncoder](const auto & encoder) -> CHIP_ERROR { + return this->ReadAndEncodeCurrentConnections(encoder, aEncoder.AccessingFabricIndex()); + })); break; } @@ -311,21 +357,20 @@ void PushAvStreamTransportServer::PushAVStreamTransportDeallocateCallback(System // Remove connection from CurrentConnections transportDeallocateContext->instance->RemoveStreamTransportConnection(connectionID); + transportDeallocateContext->instance->RemoveTimerAppState(connectionID); } else { ChipLogError(Zcl, "Push AV Stream Transport Deallocate timer expired. %s", "Deallocation Failed"); } - - delete transportDeallocateContext; } void PushAvStreamTransportServer::ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec) { uint32_t timeoutMs = timeoutSec * MILLISECOND_TICKS_PER_SECOND; - PushAVStreamTransportDeallocateCallbackContext * transportDeallocateContext = - new (std::nothrow) PushAVStreamTransportDeallocateCallbackContext{ this, connectionID }; + std::shared_ptr transportDeallocateContext{ new ( + std::nothrow) PushAVStreamTransportDeallocateCallbackContext{ this, connectionID } }; if (transportDeallocateContext == nullptr) { @@ -335,13 +380,17 @@ void PushAvStreamTransportServer::ScheduleTransportDeallocate(uint16_t connectio CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(timeoutMs), PushAVStreamTransportDeallocateCallback, - static_cast(transportDeallocateContext)); + static_cast(transportDeallocateContext.get())); if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "Failed to schedule deallocate: timeout=%" PRIu32 ", status=%" CHIP_ERROR_FORMAT, timeoutSec, err.Format()); } + else + { + UpsertTimerAppState(transportDeallocateContext); + } } void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, @@ -355,7 +404,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c VerifyOrReturn(transportOptions.streamUsage != StreamUsageEnum::kUnknownEnumValue, { ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid streamUsage ", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); }); VerifyOrReturn(transportOptions.videoStreamID.HasValue() || transportOptions.audioStreamID.HasValue(), { @@ -364,9 +413,9 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); }); - VerifyOrReturn(transportOptions.url.size() <= 2000, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", - AttributeAccessInterface::GetEndpointId().Value()); + VerifyOrReturn(transportOptions.url.size() <= kMaxUrlLength, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: URL length: %ld exceeding max allowed length of 2000", + AttributeAccessInterface::GetEndpointId().Value(), transportOptions.url.size()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); }); @@ -375,7 +424,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c VerifyOrReturn(triggerOptions.triggerType != TransportTriggerTypeEnum::kUnknownEnumValue, { ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid triggerType ", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); }); if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion) @@ -410,6 +459,12 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c }); } } + + if (iter.GetStatus() != CHIP_NO_ERROR) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + return; + } } if (mFeatures.Has(Feature::kPerZoneSensitivity) == false) @@ -456,20 +511,14 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c }); } - VerifyOrReturn(transportOptions.ingestMethod != IngestMethodsEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - // Todo: TLSEndpointID Validation IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; - VerifyOrReturn(transportOptions.ingestMethod != IngestMethodsEnum::kUnknownEnumValue, { + VerifyOrReturn(ingestMethod != IngestMethodsEnum::kUnknownEnumValue, { ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); }); ContainerOptionsStruct containerOptions = commandData.transportOptions.containerOptions; @@ -477,7 +526,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c VerifyOrReturn(containerOptions.containerType != ContainerFormatEnum::kUnknownEnumValue, { ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Container Format ", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); }); if (containerOptions.containerType == ContainerFormatEnum::kCmaf) @@ -490,9 +539,13 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) { - VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size() <= kMaxCENCKeyLength, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); + VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size() == kMaxCENCKeyLength, { + ChipLogError( + Zcl, + "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key constraint Error, actual length: %ld not " + "equal to expected length of 16", + AttributeAccessInterface::GetEndpointId().Value(), + containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); }); } @@ -514,9 +567,12 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); }); - VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size() <= kMaxCENCKeyIDLength, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key ID constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); + VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size() == kMaxCENCKeyIDLength, { + ChipLogError(Zcl, + "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key ID constraint Error, actual " + "length: %ld not equal to expected length of 16", + AttributeAccessInterface::GetEndpointId().Value(), + containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size()); ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); }); } @@ -538,8 +594,8 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method and Container Format Combination : (Ingest Method: " "%02X and Container Format: %02X)", - AttributeAccessInterface::GetEndpointId().Value(), static_cast(ingestMethod), - static_cast(containerOptions.containerType)); + AttributeAccessInterface::GetEndpointId().Value(), to_underlying(ingestMethod), + to_underlying(containerOptions.containerType)); ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); return; } @@ -573,7 +629,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c { ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Resource Exhausted", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); return; } @@ -617,7 +673,7 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c } } - if (transportOptions.audioStreamID.HasValue() && transportOptions.audioStreamID.Value().IsNull()) + if (transportOptions.audioStreamID.HasValue() == true) { if (transportOptions.audioStreamID.Value().IsNull() == true) { @@ -707,6 +763,7 @@ void PushAvStreamTransportServer::HandleDeallocatePushTransport( { // Remove connection from CurrentConnections RemoveStreamTransportConnection(connectionID); + RemoveTimerAppState(connectionID); } ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); @@ -779,6 +836,11 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx }); } } + if (iter.GetStatus() != CHIP_NO_ERROR) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + return; + } } if (mFeatures.Has(Feature::kPerZoneSensitivity) == false) @@ -938,7 +1000,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( VerifyOrReturn(activationReason != TriggerActivationReasonEnum::kUnknownEnumValue, { ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Activation Reason ", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); }); Optional timeControl = commandData.timeControl; @@ -1131,3 +1193,7 @@ Status PushAvStreamTransportDelegate::GeneratePushTransportEndEvent(const uint16 * */ void MatterPushAvStreamTransportPluginServerInitCallback() {} +void MatterPushAvStreamTransportClusterServerShutdownCallback(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "Shuting Push AV Stream Transport server cluster on endpoint %d", endpoint); +} diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index f0577ba5deaaa5..a396af195ff948 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -65,15 +65,13 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct mTransportZoneOptions = aTransportTriggerOptionsStorage.mTransportZoneOptions; + motionZones = aTransportTriggerOptionsStorage.motionZones; + // Rebind motionZones to point to the copied vector if it was set - if (aTransportTriggerOptionsStorage.motionZones.HasValue() && !aTransportTriggerOptionsStorage.motionZones.Value().IsNull()) - { - motionZones.SetValue(DataModel::Nullable>( - Span(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); - } - else + if (motionZones.HasValue() == true && motionZones.Value().IsNull() == false) { - motionZones.Missing(); + motionZones.SetValue(DataModel::MakeNullable( + DataModel::List(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); } motionSensitivity = aTransportTriggerOptionsStorage.motionSensitivity; @@ -83,16 +81,55 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct return *this; } + TransportTriggerOptionsStorage & operator=(const Structs::TransportTriggerOptionsStruct::DecodableType aTransportTriggerOptions) + { + triggerType = aTransportTriggerOptions.triggerType; + + auto & motionZonesList = aTransportTriggerOptions.motionZones; + + if (triggerType == TransportTriggerTypeEnum::kMotion && motionZonesList.HasValue()) + { + if (motionZonesList.Value().IsNull() == false) + { + auto iter = motionZonesList.Value().Value().begin(); + + while (iter.Next()) + { + auto & transportZoneOption = iter.GetValue(); + mTransportZoneOptions.push_back(transportZoneOption); + } + + motionZones.SetValue(DataModel::MakeNullable( + DataModel::List(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); + } + else + { + motionZones.Value().SetNull(); + } + } + else + { + motionZones.ClearValue(); + } + + motionSensitivity = aTransportTriggerOptions.motionSensitivity; + motionTimeControl = aTransportTriggerOptions.motionTimeControl; + maxPreRollLen = aTransportTriggerOptions.maxPreRollLen; + + return *this; + } + TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType triggerOptions) { triggerType = triggerOptions.triggerType; - if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion && triggerOptions.motionZones.HasValue()) + auto & motionZonesList = triggerOptions.motionZones; + + if (triggerType == TransportTriggerTypeEnum::kMotion && motionZonesList.HasValue()) { - if (triggerOptions.motionZones.Value().IsNull() == false) + if (motionZonesList.Value().IsNull() == false) { - auto & motionZonesList = triggerOptions.motionZones; - auto iter = motionZonesList.Value().Value().begin(); + auto iter = motionZonesList.Value().Value().begin(); while (iter.Next()) { @@ -100,13 +137,17 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct mTransportZoneOptions.push_back(transportZoneOption); } - motionZones.SetValue(DataModel::Nullable>( - Span(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); + motionZones.SetValue(DataModel::MakeNullable( + DataModel::List(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); + } + else + { + motionZones.Value().SetNull(); } } else { - motionZones.Missing(); + motionZones.ClearValue(); } motionSensitivity = triggerOptions.motionSensitivity; @@ -135,58 +176,79 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct std::memcpy(mCENCKeyIDBuffer, aCMAFContainerOptionsStorage.mCENCKeyIDBuffer, sizeof(mCENCKeyIDBuffer)); - if (aCMAFContainerOptionsStorage.CENCKey.HasValue()) + CENCKey = aCMAFContainerOptionsStorage.CENCKey; + + CENCKeyID = aCMAFContainerOptionsStorage.CENCKeyID; + + if (CENCKey.HasValue()) { CENCKey.SetValue(ByteSpan(mCENCKeyBuffer, aCMAFContainerOptionsStorage.CENCKey.Value().size())); } - else - { - CENCKey.Missing(); - } metadataEnabled = aCMAFContainerOptionsStorage.metadataEnabled; - if (aCMAFContainerOptionsStorage.CENCKeyID.HasValue()) + if (CENCKeyID.HasValue()) { CENCKeyID.SetValue(ByteSpan(mCENCKeyIDBuffer, aCMAFContainerOptionsStorage.CENCKeyID.Value().size())); } - else + + return *this; + } + + CMAFContainerOptionsStorage & operator=(const Structs::CMAFContainerOptionsStruct::Type aCMAFContainerOptions) + { + chunkDuration = aCMAFContainerOptions.chunkDuration; + + CENCKey = aCMAFContainerOptions.CENCKey; + + CENCKeyID = aCMAFContainerOptions.CENCKeyID; + + if (CENCKey.HasValue()) { - CENCKeyID.Missing(); + MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); + CopySpanToMutableSpan(aCMAFContainerOptions.CENCKey.Value(), CENCKeyBuffer); + CENCKey.SetValue(CENCKeyBuffer); + } + + metadataEnabled = aCMAFContainerOptions.metadataEnabled; + + if (CENCKeyID.HasValue()) + { + MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); + CopySpanToMutableSpan(aCMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); + CENCKeyID.SetValue(CENCKeyIDBuffer); } return *this; } - CMAFContainerOptionsStorage(Optional CMAFContainerOptions) + CMAFContainerOptionsStorage(Structs::CMAFContainerOptionsStruct::Type CMAFContainerOptions) { - if (CMAFContainerOptions.HasValue() == true) - { - chunkDuration = CMAFContainerOptions.Value().chunkDuration; - if (CMAFContainerOptions.Value().CENCKey.HasValue()) - { - MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); - CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKey.Value(), CENCKeyBuffer); - CENCKey.SetValue(CENCKeyBuffer); - } - else - { - CENCKey.Missing(); - } + chunkDuration = CMAFContainerOptions.chunkDuration; - metadataEnabled = CMAFContainerOptions.Value().metadataEnabled; + if (CMAFContainerOptions.CENCKey.HasValue()) + { + MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); + CopySpanToMutableSpan(CMAFContainerOptions.CENCKey.Value(), CENCKeyBuffer); + CENCKey.SetValue(CENCKeyBuffer); + } + else + { + CENCKey.ClearValue(); + } - if (CMAFContainerOptions.Value().CENCKey.HasValue()) - { - MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); - CopySpanToMutableSpan(CMAFContainerOptions.Value().CENCKeyID.Value(), CENCKeyIDBuffer); - CENCKeyID.SetValue(CENCKeyIDBuffer); - } - else - { - CENCKeyID.Missing(); - } + metadataEnabled = CMAFContainerOptions.metadataEnabled; + + if (CMAFContainerOptions.CENCKey.HasValue()) + { + MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); + CopySpanToMutableSpan(CMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); + CENCKeyID.SetValue(CENCKeyIDBuffer); + } + else + { + CENCKeyID.ClearValue(); } } @@ -203,11 +265,31 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct ContainerOptionsStorage & operator=(const ContainerOptionsStorage & aContainerOptionsStorage) { - containerType = aContainerOptionsStorage.containerType; + containerType = aContainerOptionsStorage.containerType; + CMAFContainerOptions = aContainerOptionsStorage.CMAFContainerOptions; + + if (containerType == ContainerFormatEnum::kCmaf) + { + mCMAFContainerStorage = aContainerOptionsStorage.mCMAFContainerStorage; + CMAFContainerOptions.SetValue(mCMAFContainerStorage); + } + + return *this; + } - mCMAFContainerStorage = aContainerOptionsStorage.mCMAFContainerStorage; + ContainerOptionsStorage & operator=(const Structs::ContainerOptionsStruct::DecodableType aContainerOptions) + { + containerType = aContainerOptions.containerType; - CMAFContainerOptions.SetValue(mCMAFContainerStorage); + if (containerType == ContainerFormatEnum::kCmaf) + { + mCMAFContainerStorage = aContainerOptions.CMAFContainerOptions.Value(); + CMAFContainerOptions.SetValue(mCMAFContainerStorage); + } + else + { + CMAFContainerOptions.ClearValue(); + } return *this; } @@ -218,12 +300,12 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct if (containerType == ContainerFormatEnum::kCmaf) { - mCMAFContainerStorage = CMAFContainerOptionsStorage(containerOptions.CMAFContainerOptions); + mCMAFContainerStorage = containerOptions.CMAFContainerOptions.Value(); CMAFContainerOptions.SetValue(mCMAFContainerStorage); } else { - CMAFContainerOptions.Missing(); + CMAFContainerOptions.ClearValue(); } } @@ -273,12 +355,12 @@ struct TransportOptionsStorage : public TransportOptionsStruct CopyCharSpanToMutableCharSpanWithTruncation(transportOptions.url, urlBuffer); url = urlBuffer; - mTriggerOptionsStorage = TransportTriggerOptionsStorage(transportOptions.triggerOptions); + mTriggerOptionsStorage = transportOptions.triggerOptions; triggerOptions = mTriggerOptionsStorage; ingestMethod = transportOptions.ingestMethod; - mContainerOptionsStorage = ContainerOptionsStorage(transportOptions.containerOptions); + mContainerOptionsStorage = transportOptions.containerOptions; containerOptions = mContainerOptionsStorage; expiryTime = transportOptions.expiryTime; @@ -292,7 +374,7 @@ struct TransportOptionsStorage : public TransportOptionsStruct struct TransportConfigurationStorage : public TransportConfigurationStruct { - TransportConfigurationStorage() {} + TransportConfigurationStorage(){} TransportConfigurationStorage(const TransportConfigurationStorage & aTransportConfigurationStorage) { @@ -313,7 +395,7 @@ struct TransportConfigurationStorage : public TransportConfigurationStruct } else { - transportOptions.Missing(); + transportOptions.ClearValue(); } return *this; @@ -332,7 +414,7 @@ struct TransportConfigurationStorage : public TransportConfigurationStruct } else { - transportOptions.Missing(); + transportOptions.ClearValue(); } } std::shared_ptr GetTransportOptionsPtr() const { return mTransportOptionsPtr; } @@ -345,7 +427,7 @@ struct TransportConfigurationStorage : public TransportConfigurationStruct } else { - transportOptions.Missing(); + transportOptions.ClearValue(); } } @@ -374,13 +456,14 @@ class PushAvStreamTransportDelegate * @param connectionID[in] Indicates the connectionID to allocate. * * @return Success if the allocation is successful and a PushTransportConnectionID was produced; otherwise, the command SHALL - * be rejected with Failure. + * be rejected with Failure. * - * The buffers storing URL, Trigger Options, Motion Zones, Container Options is owned by the PushAVStreamTransport Server. - * The buffers are allocated on success of AllocatePushTransport and deallocated on success of DeallocatePushTransport - * command. The delegate is expected to process the transport following transport options: URL : Validate the URL + + * The delegate is expected to process the transport following transport options: URL : Validate the URL * StreamUsage,VideoStreamID,AudioStreamID for selection of Stream. + * * Allocate the transport and map it to the connectionID. + * * On Success TransportConfigurationStruct is sent as response by the server. */ virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, @@ -404,8 +487,9 @@ class PushAvStreamTransportDelegate * @param transportOptions [out] represents the Transport Options to modify. * * The buffers storing URL, Trigger Options, Motion Zones, Container Options is owned by the PushAVStreamTransport Server. + * * The allocated buffers are cleared and reassigned on success of ModifyPushTransport and deallocated on success of - * DeallocatePushTransport. + * DeallocatePushTransport. * * @return Success if the stream transport modification is successful; otherwise, the command SHALL be rejected with Failure. */ @@ -420,13 +504,17 @@ class PushAvStreamTransportDelegate * * If the transportStatus field is set to Inactive(1).Disable transmissions on the transport.Remove any queued items for * upload.Cancel any uploads currently in progress. + * * Emit GeneratePushTransportEndEvent on stop. * * Else If the TransportStatus field is set to Active(0).Enable transmissions on the transport. * If the transports trigger type is Continuous,begin Transmission immediately. + * * Else If the transports trigger type is Command or Motion, and the underlying trigger is currently active and still within * the Time Control Bounds,begin Transmission immediately. + * * Emit GeneratePushTransportBeginEvent on start. + * * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with Failure. */ virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, @@ -444,8 +532,9 @@ class PushAvStreamTransportDelegate * * Emitting of PushTransportBegin event is handled by the server when delegate returns success. * - * The delegate should emit PushTransportEnd Event using GeneratePushTransportEndEvent() API when timeControl values when - transmission ends. + * The delegate should emit PushTransportEnd Event using GeneratePushTransportEndEvent() API when timeControl values indicates + * end of transmission. + * * @return Success if the stream transport trigger is successful; otherwise, the command SHALL be rejected with Failure. */ virtual Protocols::InteractionModel::Status @@ -603,8 +692,6 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm bool HasFeature(Feature feature) const; - static void PushAVStreamTransportDeallocateCallback(chip::System::Layer *, void * callbackContext); - private: enum class UpsertResultEnum : uint8_t { @@ -618,6 +705,8 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm uint16_t connectionID; }; + std::vector> mtimerContexts; + PushAvStreamTransportDelegate & mDelegate; const BitFlags mFeatures; @@ -641,7 +730,7 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm void LoadPersistentAttributes(); // Helpers to read list items via delegate APIs - CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder); + CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, FabricIndex fabricIndex); CHIP_ERROR ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder); // Helper functions @@ -656,13 +745,19 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm void RemoveStreamTransportConnection(const uint16_t connectionID); + static void PushAVStreamTransportDeallocateCallback(chip::System::Layer *, void * callbackContext); + + UpsertResultEnum UpsertTimerAppState(std::shared_ptr timerAppState); + + void RemoveTimerAppState(const uint16_t connectionID); + /** * @brief Schedule deallocate with a given timeout * * @param endpointId endpoint where DoorLockServer is running * @param timeoutSec timeout in seconds */ - void ScheduleTransportDeallocate(chip::EndpointId endpointId, uint32_t timeoutSec); + void ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec); /** * @brief Inherited from CommandHandlerInterface From dd5a8ef25fdbc751f4dc95341505ba2dc7822dc7 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Tue, 3 Jun 2025 21:46:33 +0000 Subject: [PATCH 29/40] Restyled by clang-format --- .../push-av-stream-transport-server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index a396af195ff948..fca7ed0b43aab8 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -374,7 +374,7 @@ struct TransportOptionsStorage : public TransportOptionsStruct struct TransportConfigurationStorage : public TransportConfigurationStruct { - TransportConfigurationStorage(){} + TransportConfigurationStorage() {} TransportConfigurationStorage(const TransportConfigurationStorage & aTransportConfigurationStorage) { From 2a11c3bdb09d96e11519d4303399242a140365d7 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Thu, 5 Jun 2025 02:50:01 +0530 Subject: [PATCH 30/40] Common method for validation of incoming TransportOpions --- .../push-av-stream-transport-delegate-impl.h | 2 +- ...push-av-stream-transport-delegate-impl.cpp | 3 +- .../push-av-stream-transport-server.cpp | 419 ++++++++---------- .../push-av-stream-transport-server.h | 29 +- 4 files changed, 193 insertions(+), 260 deletions(-) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index 71d2d6c3628de1..c26cdc3096ed2f 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -45,7 +45,7 @@ class PushAvStreamTransportManager : public PushAvStreamTransportDelegate const uint16_t connectionID); Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID); Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, - const Structs::TransportOptionsStruct::DecodableType transportOptions); + const TransportOptionsStorage transportOptions); Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, TransportStatusEnum transportStatus); diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index 03c05ebfe0623a..962df7e45ec8e3 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -57,8 +57,7 @@ Protocols::InteractionModel::Status PushAvStreamTransportManager::DeallocatePush } Protocols::InteractionModel::Status -PushAvStreamTransportManager::ModifyPushTransport(const uint16_t connectionID, - const Structs::TransportOptionsStruct::DecodableType transportOptions) +PushAvStreamTransportManager::ModifyPushTransport(const uint16_t connectionID, const TransportOptionsStorage transportOptions) { for (PushAvStream & stream : pushavStreams) { diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index eb52b010f82fc2..87955b96fda5cc 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -393,47 +393,36 @@ void PushAvStreamTransportServer::ScheduleTransportDeallocate(uint16_t connectio } } -void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, - const Commands::AllocatePushTransport::DecodableType & commandData) +Status PushAvStreamTransportServer::ValidateIncomingTransportOptions( + const Structs::TransportOptionsStruct::DecodableType & transportOptions) { - Commands::AllocatePushTransportResponse::Type response; - auto & transportOptions = commandData.transportOptions; - // Contraints check on incoming transport Options + VerifyOrReturnValue(transportOptions.streamUsage != StreamUsageEnum::kUnknownEnumValue, Status::ConstraintError, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid streamUsage ", + AttributeAccessInterface::GetEndpointId().Value())); - VerifyOrReturn(transportOptions.streamUsage != StreamUsageEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid streamUsage ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - - VerifyOrReturn(transportOptions.videoStreamID.HasValue() || transportOptions.audioStreamID.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); + VerifyOrReturnValue(transportOptions.videoStreamID.HasValue() || transportOptions.audioStreamID.HasValue(), + Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", + AttributeAccessInterface::GetEndpointId().Value())); - VerifyOrReturn(transportOptions.url.size() <= kMaxUrlLength, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: URL length: %ld exceeding max allowed length of 2000", - AttributeAccessInterface::GetEndpointId().Value(), transportOptions.url.size()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + VerifyOrReturnValue( + transportOptions.url.size() >= kMinUrlLength && transportOptions.url.size() <= kMaxUrlLength, Status::ConstraintError, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: URL length: %u not in allowed length range of 13 to 2000", + AttributeAccessInterface::GetEndpointId().Value(), + static_cast(AttributeAccessInterface::GetEndpointId().Value(), transportOptions.url.size()))); auto & triggerOptions = transportOptions.triggerOptions; - VerifyOrReturn(triggerOptions.triggerType != TransportTriggerTypeEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid triggerType ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + VerifyOrReturnValue(triggerOptions.triggerType != TransportTriggerTypeEnum::kUnknownEnumValue, Status::ConstraintError, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid triggerType ", + AttributeAccessInterface::GetEndpointId().Value())); if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion) { - VerifyOrReturn(triggerOptions.motionZones.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing motion zones ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); + VerifyOrReturnValue(triggerOptions.motionZones.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing motion zones ", + AttributeAccessInterface::GetEndpointId().Value())); if (triggerOptions.motionZones.Value().IsNull() == false) { @@ -446,138 +435,188 @@ void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & c if (mFeatures.Has(Feature::kPerZoneSensitivity)) { - VerifyOrReturn(transportZoneOption.sensitivity.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Zone Sensitivity ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - - VerifyOrReturn(transportZoneOption.sensitivity.Value() >= 1 && transportZoneOption.sensitivity.Value() <= 10, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Zone Sensitivity Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + VerifyOrReturnValue(transportZoneOption.sensitivity.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Zone Sensitivity ", + AttributeAccessInterface::GetEndpointId().Value())); + + VerifyOrReturnValue(transportZoneOption.sensitivity.Value() >= 1 && + transportZoneOption.sensitivity.Value() <= 10, + Status::ConstraintError, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Zone Sensitivity Constraint Error", + AttributeAccessInterface::GetEndpointId().Value())); + } + else + { + VerifyOrReturnValue( + transportZoneOption.sensitivity.HasValue() == false, Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Found Zone Sensitivity which is not expected.", + AttributeAccessInterface::GetEndpointId().Value())); } } if (iter.GetStatus() != CHIP_NO_ERROR) { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - return; - } - } - - if (mFeatures.Has(Feature::kPerZoneSensitivity) == false) - { - VerifyOrReturn(triggerOptions.motionSensitivity.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Sensitivity ", + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Zones TLV Validation failed", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - VerifyOrReturn( - triggerOptions.motionSensitivity.Value().Value() >= 1 && triggerOptions.motionSensitivity.Value().Value() <= 10, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Sensitivity Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + return Status::InvalidCommand; + } } - VerifyOrReturn(triggerOptions.motionTimeControl.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Time Control ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); + VerifyOrReturnValue(triggerOptions.motionTimeControl.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Time Control ", + AttributeAccessInterface::GetEndpointId().Value())); - VerifyOrReturn(triggerOptions.motionTimeControl.Value().initialDuration >= 1, { + VerifyOrReturnValue( + triggerOptions.motionTimeControl.Value().initialDuration >= 1, Status::ConstraintError, ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + AttributeAccessInterface::GetEndpointId().Value())); - VerifyOrReturn(triggerOptions.motionTimeControl.Value().maxDuration >= 1, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + VerifyOrReturnValue(triggerOptions.motionTimeControl.Value().maxDuration >= 1, Status::ConstraintError, + ChipLogError(Zcl, + "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", + AttributeAccessInterface::GetEndpointId().Value())); + } + else + { + + VerifyOrReturnValue(triggerOptions.motionZones.HasValue() == false, Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Found motion zones which is not expected", + AttributeAccessInterface::GetEndpointId().Value())); + + VerifyOrReturnValue(triggerOptions.motionTimeControl.HasValue() == false, Status::InvalidCommand, + ChipLogError(Zcl, + "HandleAllocatePushTransport[ep=%d]: Found Motion Time Control which is not expected ", + AttributeAccessInterface::GetEndpointId().Value())); + } + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion && mFeatures.Has(Feature::kPerZoneSensitivity) == false) + { + VerifyOrReturnValue(triggerOptions.motionSensitivity.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Sensitivity ", + AttributeAccessInterface::GetEndpointId().Value())); + + VerifyOrReturnValue(triggerOptions.motionSensitivity.Value().Value() >= 1 && + triggerOptions.motionSensitivity.Value().Value() <= 10, + Status::ConstraintError, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Sensitivity Constraint Error", + AttributeAccessInterface::GetEndpointId().Value())); + } + else + { + VerifyOrReturnValue(triggerOptions.motionSensitivity.HasValue() == false, Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Found Motion Sensitivity which is not expected ", + AttributeAccessInterface::GetEndpointId().Value())); } if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion || triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand) { - VerifyOrReturn(triggerOptions.maxPreRollLen.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Max Pre Roll Len field ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); + VerifyOrReturnValue(triggerOptions.maxPreRollLen.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Max Pre Roll Len field ", + AttributeAccessInterface::GetEndpointId().Value())); + } + else + { + VerifyOrReturnValue(triggerOptions.maxPreRollLen.HasValue() == false, Status::InvalidCommand, + ChipLogError(Zcl, + "HandleAllocatePushTransport[ep=%d]: Found Max Pre Roll Len field which is not expected.", + AttributeAccessInterface::GetEndpointId().Value())); } - // Todo: TLSEndpointID Validation - - IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; + IngestMethodsEnum ingestMethod = transportOptions.ingestMethod; - VerifyOrReturn(ingestMethod != IngestMethodsEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + VerifyOrReturnValue(ingestMethod != IngestMethodsEnum::kUnknownEnumValue, Status::ConstraintError, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", + AttributeAccessInterface::GetEndpointId().Value())); - ContainerOptionsStruct containerOptions = commandData.transportOptions.containerOptions; + ContainerOptionsStruct containerOptions = transportOptions.containerOptions; - VerifyOrReturn(containerOptions.containerType != ContainerFormatEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Container Format ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + VerifyOrReturnValue(containerOptions.containerType != ContainerFormatEnum::kUnknownEnumValue, Status::ConstraintError, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Container Format ", + AttributeAccessInterface::GetEndpointId().Value())); if (containerOptions.containerType == ContainerFormatEnum::kCmaf) { - VerifyOrReturn(containerOptions.CMAFContainerOptions.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); + VerifyOrReturnValue(containerOptions.CMAFContainerOptions.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options ", + AttributeAccessInterface::GetEndpointId().Value())); if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) { - VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size() == kMaxCENCKeyLength, { + VerifyOrReturnValue( + containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size() == kMaxCENCKeyLength, Status::ConstraintError, ChipLogError( Zcl, - "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key constraint Error, actual length: %ld not " + "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key constraint Error, actual length: %u not " "equal to expected length of 16", AttributeAccessInterface::GetEndpointId().Value(), - containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + static_cast(containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size()))); } - if (mFeatures.Has(Feature::kMetadata)) + if (!mFeatures.Has(Feature::kMetadata)) { - VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().metadataEnabled.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options MetadataEnabled ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); + VerifyOrReturnValue( + containerOptions.CMAFContainerOptions.Value().metadataEnabled.HasValue() == false, Status::InvalidCommand, + ChipLogError( + Zcl, "HandleAllocatePushTransport[ep=%d]: Found CMAF Container Options MetadataEnabled which is not expected.", + AttributeAccessInterface::GetEndpointId().Value())); } if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) { - VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKeyID.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options CENC Key ID ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); + VerifyOrReturnValue(containerOptions.CMAFContainerOptions.Value().CENCKeyID.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options CENC Key ID ", + AttributeAccessInterface::GetEndpointId().Value())); - VerifyOrReturn(containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size() == kMaxCENCKeyIDLength, { + VerifyOrReturnValue( + containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size() == kMaxCENCKeyIDLength, + Status::ConstraintError, ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key ID constraint Error, actual " - "length: %ld not equal to expected length of 16", + "length: %u not equal to expected length of 16", AttributeAccessInterface::GetEndpointId().Value(), - containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + static_cast(containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size()))); } + else + { + VerifyOrReturnValue( + containerOptions.CMAFContainerOptions.Value().CENCKeyID.HasValue() == false, Status::InvalidCommand, + ChipLogError(Zcl, + "HandleAllocatePushTransport[ep=%d]: Found CMAF Container Options CENC Key ID which is not expected", + AttributeAccessInterface::GetEndpointId().Value())); + } + } + else + { + VerifyOrReturnValue(containerOptions.CMAFContainerOptions.HasValue() == false, Status::InvalidCommand, + ChipLogError(Zcl, + "HandleAllocatePushTransport[ep=%d]: Found CMAF Container Options which is not expected ", + AttributeAccessInterface::GetEndpointId().Value())); } + return Status::Success; +} + +void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, + const Commands::AllocatePushTransport::DecodableType & commandData) +{ + Commands::AllocatePushTransportResponse::Type response; + auto & transportOptions = commandData.transportOptions; + + Status transportOptionsValidityStatus = ValidateIncomingTransportOptions(transportOptions); + + VerifyOrReturn(transportOptionsValidityStatus == Status::Success, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: TransportOptions of command data is not Valid", + AttributeAccessInterface::GetEndpointId().Value()); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, transportOptionsValidityStatus); + }); + + // Todo: TLSEndpointID Validation + + IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; + + ContainerOptionsStruct containerOptions = commandData.transportOptions.containerOptions; + bool isFormatSupported = false; for (auto & supportsFormat : mSupportedFormats) @@ -777,120 +816,12 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx auto & transportOptions = commandData.transportOptions; // Contraints check on incoming transport Options + Status transportOptionsValidityStatus = ValidateIncomingTransportOptions(transportOptions); - VerifyOrReturn(transportOptions.streamUsage != StreamUsageEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid streamUsage ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - - VerifyOrReturn(transportOptions.videoStreamID.HasValue() || transportOptions.audioStreamID.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - - VerifyOrReturn(transportOptions.url.size() <= 2000, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - - auto & triggerOptions = transportOptions.triggerOptions; - - VerifyOrReturn(triggerOptions.triggerType != TransportTriggerTypeEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid triggerType ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - - if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion) - { - VerifyOrReturn(triggerOptions.motionZones.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing motion zones ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - - if (triggerOptions.motionZones.Value().IsNull() == false) - { - auto & motionZonesList = triggerOptions.motionZones; - auto iter = motionZonesList.Value().Value().begin(); - - while (iter.Next()) - { - auto & transportZoneOption = iter.GetValue(); - - if (mFeatures.Has(Feature::kPerZoneSensitivity)) - { - VerifyOrReturn(transportZoneOption.sensitivity.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Zone Sensitivity ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - - VerifyOrReturn(transportZoneOption.sensitivity.Value() >= 1 && transportZoneOption.sensitivity.Value() <= 10, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Zone Sensitivity Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - } - } - if (iter.GetStatus() != CHIP_NO_ERROR) - { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - return; - } - } - - if (mFeatures.Has(Feature::kPerZoneSensitivity) == false) - { - VerifyOrReturn(triggerOptions.motionSensitivity.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Sensitivity ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - VerifyOrReturn( - triggerOptions.motionSensitivity.Value().Value() >= 1 && triggerOptions.motionSensitivity.Value().Value() <= 10, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Sensitivity Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - } - - VerifyOrReturn(triggerOptions.motionTimeControl.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Time Control ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - - VerifyOrReturn(triggerOptions.motionTimeControl.Value().initialDuration >= 1, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - - VerifyOrReturn(triggerOptions.motionTimeControl.Value().maxDuration >= 1, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - } - - if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion || - triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand) - { - VerifyOrReturn(triggerOptions.maxPreRollLen.HasValue(), { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Max Pre Roll Len field ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - }); - } - - VerifyOrReturn(transportOptions.ingestMethod != IngestMethodsEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", + VerifyOrReturn(transportOptionsValidityStatus == Status::Success, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: TransportOptions of command data is not Valid", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, transportOptionsValidityStatus); }); FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); @@ -923,7 +854,7 @@ void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx return; } // Call the delegate - status = mDelegate.ModifyPushTransport(connectionID, transportOptions); + status = mDelegate.ModifyPushTransport(connectionID, *transportOptionsPtr); if (status == Status::Success) { @@ -1080,7 +1011,7 @@ void PushAvStreamTransportServer::HandleManuallyTriggerTransport( if (status == Status::Success) { - mDelegate.GeneratePushTransportBeginEvent(connectionID, TransportTriggerTypeEnum::kCommand, MakeOptional(activationReason)); + GeneratePushTransportBeginEvent(connectionID, TransportTriggerTypeEnum::kCommand, MakeOptional(activationReason)); } ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); @@ -1140,9 +1071,9 @@ void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } -Status PushAvStreamTransportDelegate::GeneratePushTransportBeginEvent(const uint16_t connectionID, - const TransportTriggerTypeEnum triggerType, - const Optional activationReason) +Status PushAvStreamTransportServer::GeneratePushTransportBeginEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason) { Events::PushTransportBegin::Type event; EventNumber eventNumber; @@ -1151,19 +1082,19 @@ Status PushAvStreamTransportDelegate::GeneratePushTransportBeginEvent(const uint event.triggerType = triggerType; event.activationReason = activationReason; - CHIP_ERROR err = LogEvent(event, mEndpointId, eventNumber); + CHIP_ERROR err = LogEvent(event, AttributeAccessInterface::GetEndpointId().Value(), eventNumber); if (CHIP_NO_ERROR != err) { - ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportBegin event: %" CHIP_ERROR_FORMAT, mEndpointId, - err.Format()); + ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportBegin event: %" CHIP_ERROR_FORMAT, + AttributeAccessInterface::GetEndpointId().Value(), err.Format()); return Status::Failure; } return Status::Success; } -Status PushAvStreamTransportDelegate::GeneratePushTransportEndEvent(const uint16_t connectionID, - const TransportTriggerTypeEnum triggerType, - const Optional activationReason) +Status PushAvStreamTransportServer::GeneratePushTransportEndEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason) { Events::PushTransportEnd::Type event; EventNumber eventNumber; @@ -1172,11 +1103,11 @@ Status PushAvStreamTransportDelegate::GeneratePushTransportEndEvent(const uint16 event.triggerType = triggerType; event.activationReason = activationReason; - CHIP_ERROR err = LogEvent(event, mEndpointId, eventNumber); + CHIP_ERROR err = LogEvent(event, AttributeAccessInterface::GetEndpointId().Value(), eventNumber); if (CHIP_NO_ERROR != err) { - ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportEnd event: %" CHIP_ERROR_FORMAT, mEndpointId, - err.Format()); + ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportEnd event: %" CHIP_ERROR_FORMAT, + AttributeAccessInterface::GetEndpointId().Value(), err.Format()); return Status::Failure; } return Status::Success; diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index fca7ed0b43aab8..e5ae0e79594705 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -29,6 +29,7 @@ namespace app { namespace Clusters { namespace PushAvStreamTransport { +static constexpr size_t kMinUrlLength = 13u; static constexpr size_t kMaxUrlLength = 2000u; static constexpr size_t kMaxCENCKeyLength = 16u; static constexpr size_t kMaxCENCKeyIDLength = 16u; @@ -458,7 +459,6 @@ class PushAvStreamTransportDelegate * @return Success if the allocation is successful and a PushTransportConnectionID was produced; otherwise, the command SHALL * be rejected with Failure. * - * The delegate is expected to process the transport following transport options: URL : Validate the URL * StreamUsage,VideoStreamID,AudioStreamID for selection of Stream. * @@ -488,13 +488,13 @@ class PushAvStreamTransportDelegate * * The buffers storing URL, Trigger Options, Motion Zones, Container Options is owned by the PushAVStreamTransport Server. * - * The allocated buffers are cleared and reassigned on success of ModifyPushTransport and deallocated on success of - * DeallocatePushTransport. + * The allocated buffers are cleared and reassigned to modified transportOptions on success of ModifyPushTransport and + * deallocated on success of DeallocatePushTransport. * * @return Success if the stream transport modification is successful; otherwise, the command SHALL be rejected with Failure. */ - virtual Protocols::InteractionModel::Status - ModifyPushTransport(const uint16_t connectionID, const Structs::TransportOptionsStruct::DecodableType transportOptions) = 0; + virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, + const TransportOptionsStorage transportOptions) = 0; /** * @brief Handle Command Delegate for Stream transport modification. @@ -648,14 +648,6 @@ class PushAvStreamTransportDelegate */ virtual CHIP_ERROR PersistentAttributesLoadedCallback() = 0; - // Send Push AV Stream Transport events - Protocols::InteractionModel::Status - GeneratePushTransportBeginEvent(const uint16_t connectionID, const TransportTriggerTypeEnum triggerType, - const Optional activationReason); - Protocols::InteractionModel::Status GeneratePushTransportEndEvent(const uint16_t connectionID, - const TransportTriggerTypeEnum triggerType, - const Optional activationReason); - protected: EndpointId mEndpointId = 0; }; @@ -692,6 +684,14 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm bool HasFeature(Feature feature) const; + // Send Push AV Stream Transport events + Protocols::InteractionModel::Status + GeneratePushTransportBeginEvent(const uint16_t connectionID, const TransportTriggerTypeEnum triggerType, + const Optional activationReason); + Protocols::InteractionModel::Status GeneratePushTransportEndEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason); + private: enum class UpsertResultEnum : uint8_t { @@ -745,6 +745,9 @@ class PushAvStreamTransportServer : public AttributeAccessInterface, public Comm void RemoveStreamTransportConnection(const uint16_t connectionID); + Protocols::InteractionModel::Status + ValidateIncomingTransportOptions(const Structs::TransportOptionsStruct::DecodableType & transportOptions); + static void PushAVStreamTransportDeallocateCallback(chip::System::Layer *, void * callbackContext); UpsertResultEnum UpsertTimerAppState(std::shared_ptr timerAppState); From 7b93e26113dcc6e9294df15feca835aa851bde82 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Thu, 12 Jun 2025 17:15:48 +0530 Subject: [PATCH 31/40] Fix CI failure --- .../push-av-stream-transport-server.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 87955b96fda5cc..b621aaaf03031c 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -181,7 +181,8 @@ void PushAvStreamTransportServer::RemoveTimerAppState(const uint16_t connectionI [connectionID](const std::shared_ptr & s) { if (s->connectionID == connectionID) { - DeviceLayer::SystemLayer().CancelTimer(NULL, static_cast(s.get())); + DeviceLayer::SystemLayer().CancelTimer(PushAVStreamTransportDeallocateCallback, + static_cast(s.get())); return true; // Remove from vector } return false; // Keep in vector @@ -408,9 +409,8 @@ Status PushAvStreamTransportServer::ValidateIncomingTransportOptions( VerifyOrReturnValue( transportOptions.url.size() >= kMinUrlLength && transportOptions.url.size() <= kMaxUrlLength, Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: URL length: %u not in allowed length range of 13 to 2000", - AttributeAccessInterface::GetEndpointId().Value(), - static_cast(AttributeAccessInterface::GetEndpointId().Value(), transportOptions.url.size()))); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: URL length: %" PRIu32 " not in allowed length range of 13 to 2000", + AttributeAccessInterface::GetEndpointId().Value(), static_cast(transportOptions.url.size()))); auto & triggerOptions = transportOptions.triggerOptions; @@ -547,7 +547,8 @@ Status PushAvStreamTransportServer::ValidateIncomingTransportOptions( containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size() == kMaxCENCKeyLength, Status::ConstraintError, ChipLogError( Zcl, - "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key constraint Error, actual length: %u not " + "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key constraint Error, actual length: %" PRIu32 + " not " "equal to expected length of 16", AttributeAccessInterface::GetEndpointId().Value(), static_cast(containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size()))); @@ -573,7 +574,7 @@ Status PushAvStreamTransportServer::ValidateIncomingTransportOptions( Status::ConstraintError, ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key ID constraint Error, actual " - "length: %u not equal to expected length of 16", + "length: %" PRIu32 " not equal to expected length of 16", AttributeAccessInterface::GetEndpointId().Value(), static_cast(containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size()))); } From 69bf3aca0b25f45b2aa699ceb85de55072802755 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Thu, 26 Jun 2025 17:02:36 +0530 Subject: [PATCH 32/40] Codegen Integration for Push AV Cluster --- .../all-clusters-app.matter | 173 +-- .../all-clusters-common/all-clusters-app.zap | 165 +-- ...push-av-stream-transport-delegate-impl.cpp | 33 +- .../all-clusters-app/linux/main-common.cpp | 6 + scripts/tools/check_includes_config.py | 3 + .../push-av-stream-transport-server/BUILD.gn | 21 +- .../CodegenIntegration.cpp | 120 ++ .../CodegenIntegration.h | 34 + .../app_config_dependent_sources.cmake | 5 + .../app_config_dependent_sources.gni | 4 +- .../constants.h | 52 + .../push-av-stream-transport-delegate.h | 234 ++++ .../push-av-stream-transport-logic.cpp | 1080 ++++++++++++++++ .../push-av-stream-transport-logic.h | 138 ++ .../push-av-stream-transport-server.cpp | 1131 ++--------------- .../push-av-stream-transport-server.h | 769 +---------- .../push-av-stream-transport-storage.h | 417 ++++++ 17 files changed, 2284 insertions(+), 2101 deletions(-) create mode 100644 src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp create mode 100644 src/app/clusters/push-av-stream-transport-server/CodegenIntegration.h create mode 100644 src/app/clusters/push-av-stream-transport-server/constants.h create mode 100644 src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h create mode 100644 src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp create mode 100644 src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h create mode 100644 src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 15fd0a221235b1..0bac7d498f8415 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -7595,176 +7595,6 @@ provisional cluster PushAvStreamTransport = 1365 { fabric command FindTransport(FindTransportRequest): FindTransportResponse = 6; } -/** This cluster implements the upload of Audio and Video streams from the Push AV Stream Transport Cluster using suitable push-based transports. */ -provisional cluster PushAvStreamTransport = 1365 { - revision 1; - - enum ContainerFormatEnum : enum8 { - kCMAF = 0 [spec_name = "CMAF"]; - } - - enum IngestMethodsEnum : enum8 { - kCMAFIngest = 0; - } - - enum StatusCodeEnum : enum8 { - kInvalidTLSEndpoint = 2; - kInvalidStream = 3; - kInvalidURL = 4; - kInvalidZone = 5; - kInvalidCombination = 6; - kInvalidTriggerType = 7; - kInvalidTransportStatus = 8; - } - - enum TransportStatusEnum : enum8 { - kActive = 0; - kInactive = 1; - } - - enum TransportTriggerTypeEnum : enum8 { - kCommand = 0; - kMotion = 1; - kContinuous = 2; - } - - enum TriggerActivationReasonEnum : enum8 { - kUserInitiated = 0; - kAutomation = 1; - kEmergency = 2; - } - - bitmap Feature : bitmap32 { - kPerZoneSensitivity = 0x1; - kMetadata = 0x2; - } - - struct TransportMotionTriggerTimeControlStruct { - int16u initialDuration = 0; - int16u augmentationDuration = 1; - elapsed_s maxDuration = 2; - int16u blindDuration = 3; - } - - struct TransportZoneOptionsStruct { - nullable int16u zone = 0; - optional int8u sensitivity = 1; - } - - struct TransportTriggerOptionsStruct { - TransportTriggerTypeEnum triggerType = 0; - optional nullable TransportZoneOptionsStruct motionZones[] = 1; - optional nullable int8u motionSensitivity = 2; - optional TransportMotionTriggerTimeControlStruct motionTimeControl = 3; - optional int16u maxPreRollLen = 4; - } - - struct CMAFContainerOptionsStruct { - int16u chunkDuration = 0; - optional octet_string<16> CENCKey = 1; - optional boolean metadataEnabled = 2; - optional octet_string<16> CENCKeyID = 3; - } - - struct ContainerOptionsStruct { - ContainerFormatEnum containerType = 0; - optional CMAFContainerOptionsStruct CMAFContainerOptions = 1; - } - - struct TransportOptionsStruct { - StreamUsageEnum streamUsage = 0; - optional nullable int16u videoStreamID = 1; - optional nullable int16u audioStreamID = 2; - int16u endpointID = 3; - long_char_string<2000> url = 4; - TransportTriggerOptionsStruct triggerOptions = 5; - IngestMethodsEnum ingestMethod = 6; - ContainerOptionsStruct containerOptions = 7; - optional epoch_s expiryTime = 8; - } - - fabric_scoped struct TransportConfigurationStruct { - int16u connectionID = 0; - TransportStatusEnum transportStatus = 1; - optional TransportOptionsStruct transportOptions = 2; - fabric_idx fabricIndex = 254; - } - - struct SupportedFormatStruct { - ContainerFormatEnum containerFormat = 0; - IngestMethodsEnum ingestMethod = 1; - } - - info event PushTransportBegin = 0 { - int16u connectionID = 0; - TransportTriggerTypeEnum triggerType = 1; - optional TriggerActivationReasonEnum activationReason = 2; - } - - info event PushTransportEnd = 1 { - int16u connectionID = 0; - TransportTriggerTypeEnum triggerType = 1; - optional TriggerActivationReasonEnum activationReason = 2; - } - - readonly attribute SupportedFormatStruct supportedFormats[] = 0; - readonly attribute TransportConfigurationStruct currentConnections[] = 1; - readonly attribute command_id generatedCommandList[] = 65528; - readonly attribute command_id acceptedCommandList[] = 65529; - readonly attribute attrib_id attributeList[] = 65531; - readonly attribute bitmap32 featureMap = 65532; - readonly attribute int16u clusterRevision = 65533; - - request struct AllocatePushTransportRequest { - TransportOptionsStruct transportOptions = 0; - } - - response struct AllocatePushTransportResponse = 1 { - TransportConfigurationStruct transportConfiguration = 0; - } - - request struct DeallocatePushTransportRequest { - int16u connectionID = 0; - } - - request struct ModifyPushTransportRequest { - int16u connectionID = 0; - TransportOptionsStruct transportOptions = 1; - } - - request struct SetTransportStatusRequest { - nullable int16u connectionID = 0; - TransportStatusEnum transportStatus = 1; - } - - request struct ManuallyTriggerTransportRequest { - int16u connectionID = 0; - TriggerActivationReasonEnum activationReason = 1; - optional TransportMotionTriggerTimeControlStruct timeControl = 2; - } - - request struct FindTransportRequest { - optional nullable int16u connectionID = 0; - } - - response struct FindTransportResponse = 7 { - TransportConfigurationStruct transportConfigurations[] = 0; - } - - /** This command SHALL allocate a transport and return a PushTransportConnectionID. */ - fabric command access(invoke: manage) AllocatePushTransport(AllocatePushTransportRequest): AllocatePushTransportResponse = 0; - /** This command SHALL be generated to request the Node deallocates the specified transport. */ - fabric command access(invoke: manage) DeallocatePushTransport(DeallocatePushTransportRequest): DefaultSuccess = 2; - /** This command is used to request the Node modifies the configuration of the specified push transport. */ - fabric command access(invoke: manage) ModifyPushTransport(ModifyPushTransportRequest): DefaultSuccess = 3; - /** This command SHALL be generated to request the Node modifies the Transport Status of a specified transport or all transports. */ - fabric command access(invoke: manage) SetTransportStatus(SetTransportStatusRequest): DefaultSuccess = 4; - /** This command SHALL be generated to request the Node to manually start the specified push transport. */ - fabric command ManuallyTriggerTransport(ManuallyTriggerTransportRequest): DefaultSuccess = 5; - /** This command SHALL return the Transport Configuration for the specified push transport or all allocated transports for the fabric if null. */ - fabric command FindTransport(FindTransportRequest): FindTransportResponse = 6; -} - /** This cluster provides facilities to configure and play Chime sounds, such as those used in a doorbell. */ provisional cluster Chime = 1366 { revision 1; @@ -8841,7 +8671,6 @@ endpoint 1 { device type ma_onofflight = 256, version 1; binding cluster OnOff; - binding cluster PushAvStreamTransport; server cluster Identify { ram attribute identifyTime default = 0x0000; @@ -10119,6 +9948,8 @@ endpoint 1 { } server cluster PushAvStreamTransport { + emits event PushTransportBegin; + emits event PushTransportEnd; callback attribute supportedFormats; callback attribute currentConnections; callback attribute generatedCommandList; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index 2cf900acc62f35..ae781568ea1a1d 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -1,6 +1,6 @@ { "fileFormat": 2, - "featureLevel": 106, + "featureLevel": 107, "creator": "zap", "keyValuePairs": [ { @@ -2239,10 +2239,10 @@ "side": "server", "type": "int8u", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2271,10 +2271,10 @@ "side": "server", "type": "int8u", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2287,10 +2287,10 @@ "side": "server", "type": "int8u", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2303,10 +2303,10 @@ "side": "server", "type": "boolean", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2319,10 +2319,10 @@ "side": "server", "type": "NetworkCommissioningStatusEnum", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2335,10 +2335,10 @@ "side": "server", "type": "octet_string", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2351,10 +2351,10 @@ "side": "server", "type": "int32s", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2386,7 +2386,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2402,7 +2402,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -2463,10 +2463,10 @@ "side": "server", "type": "bitmap32", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "2", + "defaultValue": null, "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -2479,10 +2479,10 @@ "side": "server", "type": "int16u", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "1", + "defaultValue": null, "reportable": 1, "minInterval": 0, "maxInterval": 65344, @@ -20795,7 +20795,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -20808,7 +20808,7 @@ "side": "server", "type": "percent", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, "defaultValue": null, @@ -20827,7 +20827,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -20843,7 +20843,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -20859,7 +20859,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -20888,10 +20888,10 @@ "side": "server", "type": "int16u", "included": 1, - "storageOption": "RAM", + "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "1", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -21555,81 +21555,6 @@ } ] }, - { - "name": "Push AV Stream Transport", - "code": 1365, - "mfgCode": null, - "define": "PUSH_AV_STREAM_TRANSPORT_CLUSTER", - "side": "client", - "enabled": 1, - "apiMaturity": "provisional", - "commands": [ - { - "name": "AllocatePushTransport", - "code": 0, - "mfgCode": null, - "source": "client", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "AllocatePushTransportResponse", - "code": 1, - "mfgCode": null, - "source": "server", - "isIncoming": 1, - "isEnabled": 1 - }, - { - "name": "DeallocatePushTransport", - "code": 2, - "mfgCode": null, - "source": "client", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "ModifyPushTransport", - "code": 3, - "mfgCode": null, - "source": "client", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "SetTransportStatus", - "code": 4, - "mfgCode": null, - "source": "client", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "ManuallyTriggerTransport", - "code": 5, - "mfgCode": null, - "source": "client", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "FindTransport", - "code": 6, - "mfgCode": null, - "source": "client", - "isIncoming": 0, - "isEnabled": 1 - }, - { - "name": "FindTransportResponse", - "code": 7, - "mfgCode": null, - "source": "server", - "isIncoming": 1, - "isEnabled": 1 - } - ] - }, { "name": "Push AV Stream Transport", "code": 1365, @@ -21817,6 +21742,22 @@ "maxInterval": 65534, "reportableChange": 0 } + ], + "events": [ + { + "name": "PushTransportBegin", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "PushTransportEnd", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + } ] }, { @@ -26218,7 +26159,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -26250,7 +26191,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -26266,7 +26207,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -26282,7 +26223,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -26298,7 +26239,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -26314,7 +26255,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -26330,7 +26271,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -26394,7 +26335,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "0", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -26410,7 +26351,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "1", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp index 962df7e45ec8e3..4ea0a7c103c864 100644 --- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -32,11 +32,6 @@ using namespace chip::app::Clusters; using namespace chip::app::Clusters::PushAvStreamTransport; using chip::Protocols::InteractionModel::Status; -// Global pointer to overall PushAV Stream Transport implementing the Cluster delegate. -std::unique_ptr sPushAvStramTransportInstance; -// Global pointer to PushAV Stream Transport Server SDK cluster; -std::unique_ptr sPushAvStramTransportClusterServerInstance; - Protocols::InteractionModel::Status PushAvStreamTransportManager::AllocatePushTransport(const TransportOptionsStruct & transportOptions, const uint16_t connectionID) { @@ -181,31 +176,7 @@ PushAvStreamTransportManager::LoadCurrentConnections(std::vector(); - sPushAvStramTransportInstance->Init(); - - BitFlags features; - - sPushAvStramTransportClusterServerInstance = - std::make_unique(*sPushAvStramTransportInstance.get(), endpoint, features); - sPushAvStramTransportClusterServerInstance->Init(); -} - -void emberAfPushAvStreamTransportClusterShutdownCallback(EndpointId endpoint) -{ - sPushAvStramTransportClusterServerInstance = nullptr; - sPushAvStramTransportInstance = nullptr; -} diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp index 600e3b026abc0e..5c311def8b2067 100644 --- a/examples/all-clusters-app/linux/main-common.cpp +++ b/examples/all-clusters-app/linux/main-common.cpp @@ -41,6 +41,7 @@ #include "tcc-mode.h" #include "thermostat-delegate-impl.h" #include "water-heater-mode.h" +#include "push-av-stream-transport-delegate-impl.h" #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +89,7 @@ Clusters::TemperatureControl::AppSupportedTemperatureLevelsDelegate sAppSupporte Clusters::ModeSelect::StaticSupportedModesManager sStaticSupportedModesManager; Clusters::ValveConfigurationAndControl::ValveControlDelegate sValveDelegate; Clusters::TimeSynchronization::ExtendedTimeSyncDelegate sTimeSyncDelegate; +Clusters::PushAvStreamTransport::PushAvStreamTransportManager gPushAvStreamTransportManager; // Please refer to https://github.com/CHIP-Specifications/connectedhomeip-spec/blob/master/src/namespaces constexpr const uint8_t kNamespaceCommon = 7; @@ -201,6 +204,9 @@ void ApplicationInit() Clusters::WaterHeaterManagement::WhmApplicationInit(chip::EndpointId(1)); + ChipLogProgress(AppServer, "Setting Push AV Stream Transport delegate"); + Clusters::PushAvStreamTransport::SetDelegate(chip::EndpointId(1), &gPushAvStreamTransportManager); + SetTagList(/* endpoint= */ 0, Span(gEp0TagList)); SetTagList(/* endpoint= */ 1, Span(gEp1TagList)); SetTagList(/* endpoint= */ 2, Span(gEp2TagList)); diff --git a/scripts/tools/check_includes_config.py b/scripts/tools/check_includes_config.py index a0358d93952f7f..aa77eb4b8d188b 100644 --- a/scripts/tools/check_includes_config.py +++ b/scripts/tools/check_includes_config.py @@ -144,6 +144,9 @@ 'src/app/clusters/camera-av-settings-user-level-management-server/camera-av-settings-user-level-management-server.h': {'string', 'vector'}, 'src/app/clusters/webrtc-transport-requestor-server/webrtc-transport-requestor-server.h': {'string', 'vector'}, 'src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h': {'vector'}, + 'src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h': {'vector'}, + 'src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h': {'vector'}, + 'src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h': {'vector'}, 'src/credentials/attestation_verifier/FileAttestationTrustStore.h': {'vector'}, 'src/credentials/attestation_verifier/FileAttestationTrustStore.cpp': {'string'}, 'src/credentials/attestation_verifier/TestDACRevocationDelegateImpl.cpp': {'fstream'}, diff --git a/src/app/clusters/push-av-stream-transport-server/BUILD.gn b/src/app/clusters/push-av-stream-transport-server/BUILD.gn index a309e9b59916ff..120c0e278434ff 100644 --- a/src/app/clusters/push-av-stream-transport-server/BUILD.gn +++ b/src/app/clusters/push-av-stream-transport-server/BUILD.gn @@ -11,5 +11,24 @@ # 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. -group("push-av-stream-transport-server") { +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +source_set("push-av-stream-transport-server") { + sources = [ + "push-av-stream-transport-delegate.h", + "push-av-stream-transport-logic.cpp", + "push-av-stream-transport-logic.h", + "push-av-stream-transport-server.cpp", + "push-av-stream-transport-server.h", + "push-av-stream-transport-storage.h", + ] + + public_deps = [ + "${chip_root}/src/app/", + "${chip_root}/src/app/server-cluster", + "${chip_root}/src/lib/core:types", + "${chip_root}/zzz_generated/app-common/clusters/PushAvStreamTransport", + ] + public_configs = [ "${chip_root}/src:includes" ] } diff --git a/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp new file mode 100644 index 00000000000000..e1f8ad2b6a043e --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::PushAvStreamTransport::Attributes; +using chip::Protocols::InteractionModel::Status; + +namespace { + +static constexpr size_t kPushAvStreamTransportFixedClusterCount = + PushAvStreamTransport::StaticApplicationConfig::kFixedClusterConfig.size(); +static constexpr size_t kPushAvStreamTransportMaxClusterCount = kPushAvStreamTransportFixedClusterCount + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; + +LazyRegisteredServerCluster gServers[kPushAvStreamTransportMaxClusterCount]; + +// Find the 0-based array index corresponding to the given endpoint id. +// Log an error if not found. +bool findEndpointWithLog(EndpointId endpointId, uint16_t & outArrayIndex) +{ + uint16_t arrayIndex = + emberAfGetClusterServerEndpointIndex(endpointId, PushAvStreamTransport::Id, kPushAvStreamTransportFixedClusterCount); + + if (arrayIndex >= kPushAvStreamTransportMaxClusterCount) + { + ChipLogError(AppServer, "Cound not find endpoint index for endpoint %u", endpointId); + return false; + } + return true; +} + +} // namespace +void emberAfPushAvStreamTransportClusterInitCallback(EndpointId endpointId) +{ + + uint16_t arrayIndex = 0; + if (!findEndpointWithLog(endpointId, arrayIndex)) + { + return; + } + uint32_t rawFeatureMap; + if (FeatureMap::Get(endpointId, &rawFeatureMap) != Status::Success) + { + ChipLogError(AppServer, "Failed to get feature map for endpoint %u", endpointId); + rawFeatureMap = 0; + } + ChipLogProgress(AppServer, "Registering Push AV Stream Transport on endpoint %u, %d", endpointId,arrayIndex); + gServers[arrayIndex].Create(endpointId,BitFlags(rawFeatureMap)); + CHIP_ERROR err = CodegenDataModelProvider::Instance().Registry().Register(gServers[arrayIndex].Registration()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to register OTA on endpoint %u: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); + } + +} + +void emberAfPushAvStreamTransportClusterShutdownCallback(EndpointId endpointId) +{ + uint16_t arrayIndex = 0; + if (!findEndpointWithLog(endpointId, arrayIndex)) + { + return; + } + + CHIP_ERROR err = CodegenDataModelProvider::Instance().Registry().Unregister(&gServers[arrayIndex].Cluster()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to unregister OTA on endpoint %u: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); + } + gServers[arrayIndex].Cluster().Deinit(); + gServers[arrayIndex].Destroy(); +} + +void MatterPushAvStreamTransportPluginServerInitCallback() {} + +void MatterPushAvStreamTransportPluginServerShutdownCallback() {} + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +void SetDelegate(EndpointId endpointId, PushAvStreamTransportDelegate * delegate) +{ + ChipLogProgress(AppServer, "Setting Push AV Stream Transport delegate on endpoint %u", endpointId); + uint16_t arrayIndex = 0; + if (!findEndpointWithLog(endpointId, arrayIndex)) + { + return; + } + gServers[arrayIndex].Cluster().SetDelegate(endpointId,delegate); + gServers[arrayIndex].Cluster().Init(); +} + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.h b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.h new file mode 100644 index 00000000000000..3e8ee0554f3f1f --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.h @@ -0,0 +1,34 @@ +/* + * + * Copyright (c) 2021 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 "push-av-stream-transport-delegate.h" + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +/// Sets the given delegate on an endpoint configured via code-generation +void SetDelegate(chip::EndpointId endpointId, PushAvStreamTransportDelegate * delegate); + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake index f7ada4d5188470..c5e8f43b8921e5 100644 --- a/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake +++ b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake @@ -18,4 +18,9 @@ TARGET_SOURCES( PRIVATE "${CLUSTER_DIR}/push-av-stream-transport-server.cpp" "${CLUSTER_DIR}/push-av-stream-transport-server.h" + "${CLUSTER_DIR}/push-av-stream-transport-logic.cpp" + "${CLUSTER_DIR}/push-av-stream-transport-logic.h" + "${CLUSTER_DIR}/push-av-stream-transport-delegate.h" + "${CLUSTER_DIR}/CodegenIntegration.cpp" + "${CLUSTER_DIR}/CodegenIntegration.h" ) \ No newline at end of file diff --git a/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.gni b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.gni index 786feca9b86f84..be7224f0bbcf9e 100644 --- a/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.gni +++ b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.gni @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. app_config_dependent_sources = [ - "push-av-stream-transport-server.cpp", - "push-av-stream-transport-server.h", + "CodegenIntegration.cpp", + "CodegenIntegration.h", ] diff --git a/src/app/clusters/push-av-stream-transport-server/constants.h b/src/app/clusters/push-av-stream-transport-server/constants.h new file mode 100644 index 00000000000000..bd714db04bc444 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/constants.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021-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 +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +using SupportedFormatStruct = Structs::SupportedFormatStruct::Type; +using CMAFContainerOptionsStruct = Structs::CMAFContainerOptionsStruct::Type; +using ContainerOptionsStruct = Structs::ContainerOptionsStruct::Type; +using TransportZoneOptionsStruct = Structs::TransportZoneOptionsStruct::Type; +using TransportTriggerOptionsStruct = Structs::TransportTriggerOptionsStruct::Type; +using TransportMotionTriggerTimeControlStruct = Structs::TransportMotionTriggerTimeControlStruct::Type; +using TransportOptionsStruct = Structs::TransportOptionsStruct::Type; +using TransportConfigurationStruct = Structs::TransportConfigurationStruct::Type; +using StreamUsageEnum = chip::app::Clusters::Globals::StreamUsageEnum; + +static constexpr size_t kMinUrlLength = 13u; +static constexpr size_t kMaxUrlLength = 2000u; +static constexpr size_t kMaxCENCKeyLength = 16u; +static constexpr size_t kMaxCENCKeyIDLength = 16u; + +enum class PushAvStreamTransportStatusEnum : uint8_t +{ + kBusy = 0x00, + kIdle = 0x01, + kUnknown = 0x02 +}; + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h new file mode 100644 index 00000000000000..de94168aef8ea6 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h @@ -0,0 +1,234 @@ +/* + * + * 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 +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +/** + * @brief Defines interfaces for implementing application-specific logic for the PushAvStreamTransport Delegate. + * + * This class provides interfaces for command handling and loading of allocated streams. + */ +class PushAvStreamTransportDelegate +{ +public: + PushAvStreamTransportDelegate() = default; + + virtual ~PushAvStreamTransportDelegate() = default; + + void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; } + + /** + * @brief Handles stream transport allocation with the provided transport configuration option. + * + * @param transportOptions The configuration options of the transport to be allocated + * @param connectionID The connectionID to allocate + * @return Success if allocation is successful and a PushTransportConnectionID was produced; + * otherwise, the command is rejected with Failure + * + * The delegate processes the transport following options: + * - URL: Validates the URL + * - StreamUsage, VideoStreamID, AudioStreamID: For selection of Stream + * + * Allocates the transport and maps it to the connectionID. + * On Success, TransportConfigurationStruct is sent as response by the server. + */ + virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, + const uint16_t connectionID) = 0; + + /** + * @brief Handles stream transport deallocation for the provided connectionID. + * + * @param connectionID The connectionID to deallocate + * @return Success if transport deallocation is successful; + * BUSY if the transport is currently uploading + */ + virtual Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID) = 0; + + /** + * @brief Handles stream transport modification. + * + * @param connectionID The connectionID of the stream transport to modify + * @param transportOptions The Transport Options to modify + * @return Success if stream transport modification is successful; + * Failure if modification fails + * + * @note The buffers storing URL, Trigger Options, Motion Zones, Container Options are owned by + * the PushAVStreamTransport Server. The allocated buffers are cleared and reassigned to modified + * transportOptions on success of ModifyPushTransport and deallocated on success of DeallocatePushTransport. + */ + virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, + const TransportOptionsStorage transportOptions) = 0; + + /** + * @brief Handles stream transport status modification. + * + * @param connectionIDList List of connectionIDs for which new transport status to apply + * @param transportStatus Updated status of the connection(s) + * @return Success if stream transport status is successfully set; + * Failure if status modification fails + * + * Behavior based on transportStatus: + * - Inactive(1): + * - Disables transmissions + * - Removes queued items + * - Cancels active uploads + * - Emits GeneratePushTransportEndEvent + * - Active(0): + * - Enables transmissions + * - For Continuous trigger type: begins transmission immediately + * - For Command/Motion trigger: begins if trigger is active and within Time Control Bounds + * - Emits GeneratePushTransportBeginEvent + */ + virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, + TransportStatusEnum transportStatus) = 0; + + /** + * @brief Requests manual start of the specified push transport. + * + * @param connectionID The connectionID of the stream transport to trigger + * @param activationReason Information about why the transport was started/stopped + * @param timeControl Configuration to control triggered transport lifecycle + * @return Success if stream transport trigger is successful; + * Failure if trigger fails + * + * @note The server handles PushTransportBegin event emission on success. + * The delegate should emit PushTransportEnd Event using GeneratePushTransportEndEvent() + * when timeControl values indicate end of transmission. + */ + virtual Protocols::InteractionModel::Status + ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, + const Optional & timeControl) = 0; + + /** + * @brief Validates the provided URL. + * + * @param url The URL to validate + * @return true if URL is valid, false otherwise + */ + virtual bool ValidateUrl(std::string url) = 0; + + /** + * @brief Validates bandwidth requirements against camera's resource management. + * + * @param streamUsage The desired usage type for the stream (e.g. live view, recording) + * @param videoStreamId Optional identifier for the requested video stream + * @param audioStreamId Optional identifier for the requested audio stream + * @return Status::Success if stream usage is valid; + * Status::ResourceExhausted if resources are insufficient + * + * Ensures the requested stream usage is allowed given current allocation of + * camera resources (CPU, memory, network bandwidth). + */ + virtual Protocols::InteractionModel::Status + ValidateBandwidthLimit(StreamUsageEnum streamUsage, const Optional> & videoStreamId, + const Optional> & audioStreamId) = 0; + + /** + * @brief Assigns existing Video Stream based on camera's resource management and stream priority policies. + * + * @param streamUsage The desired usage type for the stream + * @param videoStreamId Identifier for the requested video stream + * @return Status::Success and selected videoStreamID if successful; + * Status::InvalidStream if no allocated VideoStream exists + */ + virtual Protocols::InteractionModel::Status SelectVideoStream(StreamUsageEnum streamUsage, uint16_t & videoStreamId) = 0; + + /** + * @brief Assigns existing Audio Stream based on camera's resource management and stream priority policies. + * + * @param streamUsage The desired usage type for the stream + * @param audioStreamId Identifier for the requested audio stream + * @return Status::Success and selected audioStreamID if successful; + * Status::InvalidStream if no allocated AudioStream exists + */ + virtual Protocols::InteractionModel::Status SelectAudioStream(StreamUsageEnum streamUsage, uint16_t & audioStreamId) = 0; + + /** + * @brief Validates that the video stream corresponding to videoStreamID is allocated. + * + * @param videoStreamId Identifier for the requested video stream + * @return Status::Success if allocated video stream exists; + * Status::InvalidStream if no allocated video stream with videoStreamID exists + */ + virtual Protocols::InteractionModel::Status ValidateVideoStream(uint16_t videoStreamId) = 0; + + /** + * @brief Validates that the audio stream corresponding to audioStreamID is allocated. + * + * @param audioStreamId Identifier for the requested audio stream + * @return Status::Success if allocated audio stream exists; + * Status::InvalidStream if no allocated audio stream with audioStreamID exists + */ + virtual Protocols::InteractionModel::Status ValidateAudioStream(uint16_t audioStreamId) = 0; + + /** + * @brief Gets the status of the transport. + * + * @param connectionID The connectionID of the stream transport to check status + * @return busy if transport is uploading, idle otherwise + */ + virtual PushAvStreamTransportStatusEnum GetTransportBusyStatus(const uint16_t connectionID) = 0; + + /** + * @brief Delegate callback for notifying change in an attribute. + * + * @param attributeId The ID of the attribute that changed + */ + virtual void OnAttributeChanged(AttributeId attributeId) = 0; + + /** + * @brief Loads pre-allocated transport connections into the cluster server list. + * + * @param currentConnections Vector to store loaded transport configurations + * @return CHIP_ERROR indicating success or failure + * + * The delegate application is responsible for creating and persisting connections + * based on Allocation commands. These connections' context information is loaded + * into the cluster server list at initialization for serving attribute reads. + * The list can be updated via Add/Remove functions for respective transport connections. + * + * @note Required buffers are managed by TransportConfigurationStorage; + * the delegate function must populate the vector correctly. + */ + virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; + + /** + * @brief Callback after persistent attributes managed by the Cluster are loaded from Storage. + * + * @return CHIP_ERROR indicating success or failure + */ + virtual CHIP_ERROR PersistentAttributesLoadedCallback() = 0; + +protected: + EndpointId mEndpointId = kInvalidEndpointId; +}; +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp new file mode 100644 index 00000000000000..a386a7330b884e --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp @@ -0,0 +1,1080 @@ +/** + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 "constants.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static constexpr uint16_t kMaxConnectionId = 65535; // This is also invalid connectionID + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::PushAvStreamTransport; +using namespace chip::app::Clusters::PushAvStreamTransport::Structs; +using namespace chip::app::Clusters::PushAvStreamTransport::Attributes; +using namespace Protocols::InteractionModel; +using chip::Protocols::InteractionModel::Status; + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +PushAvStreamTransportServerLogic::PushAvStreamTransportServerLogic(EndpointId aEndpoint, BitFlags aFeatures) : + mEndpointId(aEndpoint), mFeatures(aFeatures), + mSupportedFormats{ SupportedFormatStruct{ ContainerFormatEnum::kCmaf, IngestMethodsEnum::kCMAFIngest } } +{} + +PushAvStreamTransportServerLogic::~PushAvStreamTransportServerLogic() +{ + for (const auto & timerContext : mTimerContexts) + { + DeviceLayer::SystemLayer().CancelTimer(PushAVStreamTransportDeallocateCallback, static_cast(timerContext.get())); + } + Shutdown(); +} + +CHIP_ERROR PushAvStreamTransportServerLogic::Init() +{ + LoadPersistentAttributes(); + return CHIP_NO_ERROR; +} + +void PushAvStreamTransportServerLogic::Shutdown() {} + +bool PushAvStreamTransportServerLogic::HasFeature(Feature feature) const +{ + return mFeatures.Has(feature); +} + +CHIP_ERROR PushAvStreamTransportServerLogic::ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder) +{ + for (const auto & supportsFormat : mSupportedFormats) + { + ReturnErrorOnFailure(encoder.Encode(supportsFormat)); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +PushAvStreamTransportServerLogic::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, + FabricIndex fabricIndex) +{ + for (const auto & currentConnections : mCurrentConnections) + { + if (currentConnections.fabricIndex == fabricIndex) + { + ReturnErrorOnFailure(encoder.Encode(currentConnections)); + } + } + + return CHIP_NO_ERROR; +} + +bool PushAvStreamTransportServerLogic::IsNullDelegateWithLogging(EndpointId endpointIdForLogging) +{ + + if (mDelegate == nullptr) + { + ChipLogError(Zcl, "No PushAvStreamTransportDelegate set for ep:%u", endpointIdForLogging); + return true; + } + return false; +} + +PushAvStreamTransportServerLogic::UpsertResultEnum +PushAvStreamTransportServerLogic::UpsertStreamTransportConnection(const TransportConfigurationStorage & transportConfiguration) +{ + UpsertResultEnum result; + auto it = + std::find_if(mCurrentConnections.begin(), mCurrentConnections.end(), + [id = transportConfiguration.connectionID](const auto & existing) { return existing.connectionID == id; }); + + if (it != mCurrentConnections.end()) + { + *it = transportConfiguration; + result = UpsertResultEnum::kUpdated; + } + else + { + mCurrentConnections.push_back(transportConfiguration); + result = UpsertResultEnum::kInserted; + } + + MatterReportingAttributeChangeCallback(mEndpointId, PushAvStreamTransport::Id, + PushAvStreamTransport::Attributes::CurrentConnections::Id); + + return result; +} + +PushAvStreamTransportServerLogic::UpsertResultEnum +PushAvStreamTransportServerLogic::UpsertTimerAppState(std::shared_ptr timerAppState) +{ + UpsertResultEnum result; + auto it = std::find_if(mTimerContexts.begin(), mTimerContexts.end(), + [id = timerAppState->connectionID](const auto & existing) { return existing->connectionID == id; }); + + if (it != mTimerContexts.end()) + { + *it = timerAppState; + result = UpsertResultEnum::kUpdated; + } + else + { + mTimerContexts.push_back(timerAppState); + result = UpsertResultEnum::kInserted; + } + + return result; +} + +void PushAvStreamTransportServerLogic::RemoveStreamTransportConnection(const uint16_t transportConnectionId) +{ + size_t originalSize = mCurrentConnections.size(); + + // Erase-Remove idiom + mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), + [transportConnectionId](const TransportConfigurationStorage & s) { + return s.connectionID == transportConnectionId; + }), + mCurrentConnections.end()); + + // If a connection was removed, the size will be smaller. + if (mCurrentConnections.size() < originalSize) + { + // Notify the stack that the CurrentConnections attribute has changed. + MatterReportingAttributeChangeCallback(mEndpointId, PushAvStreamTransport::Id, + PushAvStreamTransport::Attributes::CurrentConnections::Id); + } +} + +void PushAvStreamTransportServerLogic::RemoveTimerAppState(const uint16_t connectionID) +{ + // Erase-Remove idiom + auto it = std::remove_if(mTimerContexts.begin(), mTimerContexts.end(), + [connectionID](const std::shared_ptr & s) { + if (s->connectionID == connectionID) + { + DeviceLayer::SystemLayer().CancelTimer(PushAVStreamTransportDeallocateCallback, + static_cast(s.get())); + return true; // Remove from vector + } + return false; // Keep in vector + }); + + mTimerContexts.erase(it, mTimerContexts.end()); +} + +void PushAvStreamTransportServerLogic::LoadPersistentAttributes() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Load currentConnections + err = mDelegate->LoadCurrentConnections(mCurrentConnections); + if (err != CHIP_NO_ERROR) + { + ChipLogDetail(Zcl, "PushAVStreamTransport: Unable to load allocated connections from the KVS."); + } + + // Signal delegate that all persistent configuration attributes have been loaded. + mDelegate->PersistentAttributesLoadedCallback(); +} + +TransportConfigurationStorage * PushAvStreamTransportServerLogic::FindStreamTransportConnection(const uint16_t connectionID) +{ + for (auto & transportConnection : mCurrentConnections) + { + if (transportConnection.connectionID == connectionID) + { + return &transportConnection; + } + } + return nullptr; +} + +TransportConfigurationStorage * +PushAvStreamTransportServerLogic::FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, FabricIndex fabricIndex) +{ + for (auto & transportConnection : mCurrentConnections) + { + if (transportConnection.connectionID == connectionID) + { + if (transportConnection.GetFabricIndex() == fabricIndex) + { + return &transportConnection; + } + } + } + return nullptr; +} + +uint16_t PushAvStreamTransportServerLogic::GenerateConnectionID() +{ + static uint16_t lastID = 0; + + for (uint16_t i = 0; i < kMaxConnectionId; ++i) + { + uint16_t candidateID = static_cast((lastID + i + 1) % kMaxConnectionId); // Wrap from 0 to 65534 + if (FindStreamTransportConnection(candidateID) == nullptr) + { + lastID = candidateID; + return candidateID; + } + } + + return kMaxConnectionId; // All 0 to 65534 IDs are in use +} + +void PushAvStreamTransportServerLogic::PushAVStreamTransportDeallocateCallback(System::Layer *, void * callbackContext) +{ + PushAVStreamTransportDeallocateCallbackContext * transportDeallocateContext = + static_cast((callbackContext)); + + uint16_t connectionID = transportDeallocateContext->connectionID; + + // Call the delegate + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + transportDeallocateContext->instance->mDelegate->DeallocatePushTransport(connectionID)); + + if (delegateStatus.IsSuccess()) + { + ChipLogProgress(Zcl, "Push AV Stream Transport Deallocate timer expired. %s", "Deallocating"); + + // Remove connection from CurrentConnections + transportDeallocateContext->instance->RemoveStreamTransportConnection(connectionID); + transportDeallocateContext->instance->RemoveTimerAppState(connectionID); + } + else + { + ChipLogError(Zcl, "Push AV Stream Transport Deallocate timer expired. %s", "Deallocation Failed"); + } +} + +void PushAvStreamTransportServerLogic::ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec) +{ + uint32_t timeoutMs = timeoutSec * MILLISECOND_TICKS_PER_SECOND; + + std::shared_ptr transportDeallocateContext{ new ( + std::nothrow) PushAVStreamTransportDeallocateCallbackContext{ this, connectionID } }; + + if (transportDeallocateContext == nullptr) + { + ChipLogError(Zcl, "Failed to allocate memory for deallocate context"); + return; + } + + CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(timeoutMs), + PushAVStreamTransportDeallocateCallback, + static_cast(transportDeallocateContext.get())); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Failed to schedule deallocate: timeout=%" PRIu32 ", status=%" CHIP_ERROR_FORMAT, timeoutSec, + err.Format()); + } + else + { + UpsertTimerAppState(transportDeallocateContext); + } +} + +Status PushAvStreamTransportServerLogic::ValidateIncomingTransportOptions( + const Structs::TransportOptionsStruct::DecodableType & transportOptions) +{ + // Contraints check on incoming transport Options + VerifyOrReturnValue( + transportOptions.streamUsage != StreamUsageEnum::kUnknownEnumValue, Status::ConstraintError, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Invalid streamUsage ", mEndpointId)); + + VerifyOrReturnValue( + transportOptions.videoStreamID.HasValue() || transportOptions.audioStreamID.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Missing videoStreamID and audioStreamID", + mEndpointId)); + + VerifyOrReturnValue(transportOptions.url.size() >= kMinUrlLength && transportOptions.url.size() <= kMaxUrlLength, + Status::ConstraintError, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: URL length: %" PRIu32 + " not in allowed length range of 13 to 2000", + mEndpointId, static_cast(transportOptions.url.size()))); + + auto & triggerOptions = transportOptions.triggerOptions; + + VerifyOrReturnValue( + triggerOptions.triggerType != TransportTriggerTypeEnum::kUnknownEnumValue, Status::ConstraintError, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Invalid triggerType ", mEndpointId)); + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion) + { + VerifyOrReturnValue( + triggerOptions.motionZones.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Missing motion zones ", mEndpointId)); + + if (!triggerOptions.motionZones.Value().IsNull()) + { + auto & motionZonesList = triggerOptions.motionZones; + auto iter = motionZonesList.Value().Value().begin(); + + while (iter.Next()) + { + auto & transportZoneOption = iter.GetValue(); + + if (mFeatures.Has(Feature::kPerZoneSensitivity)) + { + VerifyOrReturnValue( + transportZoneOption.sensitivity.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Missing Zone Sensitivity ", + mEndpointId)); + + VerifyOrReturnValue( + transportZoneOption.sensitivity.Value() >= 1 && transportZoneOption.sensitivity.Value() <= 10, + Status::ConstraintError, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Zone Sensitivity Constraint Error", + mEndpointId)); + } + else + { + VerifyOrReturnValue(!transportZoneOption.sensitivity.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Found Zone " + "Sensitivity which is not expected.", + mEndpointId)); + } + } + + if (iter.GetStatus() != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Motion Zones TLV Validation failed", + mEndpointId); + return Status::InvalidCommand; + } + } + + VerifyOrReturnValue(triggerOptions.motionTimeControl.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Missing Motion Time Control ", + mEndpointId)); + + VerifyOrReturnValue( + triggerOptions.motionTimeControl.Value().initialDuration >= 1, Status::ConstraintError, + ChipLogError( + Zcl, + "Transport Options verification from command data[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", + mEndpointId)); + + VerifyOrReturnValue( + triggerOptions.motionTimeControl.Value().maxDuration >= 1, Status::ConstraintError, + ChipLogError( + Zcl, "Transport Options verification from command data[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", + mEndpointId)); + } + else + { + + VerifyOrReturnValue( + !triggerOptions.motionZones.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Found motion zones which is not expected", + mEndpointId)); + + VerifyOrReturnValue( + !triggerOptions.motionTimeControl.HasValue(), Status::InvalidCommand, + ChipLogError( + Zcl, "Transport Options verification from command data[ep=%d]: Found Motion Time Control which is not expected ", + mEndpointId)); + } + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion && !mFeatures.Has(Feature::kPerZoneSensitivity)) + { + VerifyOrReturnValue( + triggerOptions.motionSensitivity.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Missing Motion Sensitivity ", mEndpointId)); + + VerifyOrReturnValue( + triggerOptions.motionSensitivity.Value().Value() >= 1 && triggerOptions.motionSensitivity.Value().Value() <= 10, + Status::ConstraintError, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Motion Sensitivity Constraint Error", + mEndpointId)); + } + else + { + VerifyOrReturnValue( + !triggerOptions.motionSensitivity.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Found Motion Sensitivity which is not expected ", + mEndpointId)); + } + + if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion || + triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand) + { + VerifyOrReturnValue(triggerOptions.maxPreRollLen.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Missing Max Pre Roll Len field ", + mEndpointId)); + } + else + { + VerifyOrReturnValue( + !triggerOptions.maxPreRollLen.HasValue(), Status::InvalidCommand, + ChipLogError( + Zcl, "Transport Options verification from command data[ep=%d]: Found Max Pre Roll Len field which is not expected.", + mEndpointId)); + } + + IngestMethodsEnum ingestMethod = transportOptions.ingestMethod; + + VerifyOrReturnValue( + ingestMethod != IngestMethodsEnum::kUnknownEnumValue, Status::ConstraintError, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Invalid Ingest Method ", mEndpointId)); + + const ContainerOptionsStruct & containerOptions = transportOptions.containerOptions; + + VerifyOrReturnValue( + containerOptions.containerType != ContainerFormatEnum::kUnknownEnumValue, Status::ConstraintError, + ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Invalid Container Format ", mEndpointId)); + + if (containerOptions.containerType == ContainerFormatEnum::kCmaf) + { + VerifyOrReturnValue(containerOptions.CMAFContainerOptions.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Missing CMAF Container Options ", + mEndpointId)); + + if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) + { + VerifyOrReturnValue( + containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size() == kMaxCENCKeyLength, Status::ConstraintError, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: CMAF Container Options CENC Key constraint " + "Error, actual length: %" PRIu32 " not " + "equal to expected length of 16", + mEndpointId, + static_cast(containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size()))); + } + + if (!mFeatures.Has(Feature::kMetadata)) + { + VerifyOrReturnValue(!containerOptions.CMAFContainerOptions.Value().metadataEnabled.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Found CMAF Container " + "Options MetadataEnabled which is not expected.", + mEndpointId)); + } + + if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) + { + VerifyOrReturnValue( + containerOptions.CMAFContainerOptions.Value().CENCKeyID.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Missing CMAF Container Options CENC Key ID ", + mEndpointId)); + + VerifyOrReturnValue( + containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size() == kMaxCENCKeyIDLength, + Status::ConstraintError, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: CMAF Container Options CENC Key ID " + "constraint Error, actual " + "length: %" PRIu32 " not equal to expected length of 16", + mEndpointId, + static_cast(containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size()))); + } + else + { + VerifyOrReturnValue(!containerOptions.CMAFContainerOptions.Value().CENCKeyID.HasValue(), Status::InvalidCommand, + ChipLogError(Zcl, + "Transport Options verification from command data[ep=%d]: Found CMAF Container " + "Options CENC Key ID which is not expected", + mEndpointId)); + } + } + else + { + VerifyOrReturnValue( + !containerOptions.CMAFContainerOptions.HasValue(), Status::InvalidCommand, + ChipLogError( + Zcl, "Transport Options verification from command data[ep=%d]: Found CMAF Container Options which is not expected ", + mEndpointId)); + } + + return Status::Success; +} + +std::optional +PushAvStreamTransportServerLogic::HandleAllocatePushTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::AllocatePushTransport::DecodableType & commandData) +{ + if (IsNullDelegateWithLogging(commandPath.mEndpointId)) + { + return Status::UnsupportedCommand; + } + + Commands::AllocatePushTransportResponse::Type response; + auto & transportOptions = commandData.transportOptions; + + Status transportOptionsValidityStatus = ValidateIncomingTransportOptions(transportOptions); + + VerifyOrDo(transportOptionsValidityStatus == Status::Success, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: TransportOptions of command data is not Valid", mEndpointId); + handler.AddStatus(commandPath, transportOptionsValidityStatus); + return std::nullopt; + }); + + // Todo: TLSEndpointID Validation + + IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; + + ContainerOptionsStruct containerOptions = commandData.transportOptions.containerOptions; + + bool isFormatSupported = false; + + for (auto & supportsFormat : mSupportedFormats) + { + if ((supportsFormat.ingestMethod == ingestMethod) && (supportsFormat.containerFormat == containerOptions.containerType)) + { + isFormatSupported = true; + } + } + + if (isFormatSupported == false) + { + auto status = to_underlying(StatusCodeEnum::kInvalidCombination); + ChipLogError(Zcl, + "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method and Container Format Combination : (Ingest Method: " + "%02X and Container Format: %02X)", + mEndpointId, to_underlying(ingestMethod), to_underlying(containerOptions.containerType)); + handler.AddClusterSpecificFailure(commandPath, status); + return std::nullopt; + } + + bool isValidUrl = mDelegate->ValidateUrl(std::string(transportOptions.url.data(), transportOptions.url.size())); + + if (isValidUrl == false) + { + auto status = to_underlying(StatusCodeEnum::kInvalidURL); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Url", mEndpointId); + handler.AddClusterSpecificFailure(commandPath, status); + return std::nullopt; + } + + /*Spec issue for invalid Trigger Type: https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/11701*/ + if (transportOptions.triggerOptions.triggerType == TransportTriggerTypeEnum::kUnknownEnumValue) + { + auto status = to_underlying(StatusCodeEnum::kInvalidTriggerType); + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Trigger type", mEndpointId); + handler.AddClusterSpecificFailure(commandPath, status); + return std::nullopt; + } + + // Todo:Validate ZoneId + + // Validate Bandwidth Requirement + Status status = mDelegate->ValidateBandwidthLimit(transportOptions.streamUsage, transportOptions.videoStreamID, + transportOptions.audioStreamID); + if (status != Status::Success) + { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Resource Exhausted", mEndpointId); + handler.AddStatus(commandPath, status); + return std::nullopt; + } + + std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + + if (transportOptionsPtr == nullptr) + { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Memory Allocation failed for transportOptions", mEndpointId); + handler.AddStatus(commandPath, Status::ResourceExhausted); + return std::nullopt; + } + + if (transportOptions.videoStreamID.HasValue()) + { + if (transportOptions.videoStreamID.Value().IsNull()) + { + uint16_t videoStreamID; + + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + mDelegate->SelectVideoStream(transportOptions.streamUsage, videoStreamID)); + + if (!delegateStatus.IsSuccess()) + { + handler.AddStatus(commandPath, delegateStatus); + return std::nullopt; + } + + transportOptionsPtr->videoStreamID.SetValue(videoStreamID); + } + else + { + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + mDelegate->ValidateVideoStream(transportOptions.videoStreamID.Value().Value())); + + if (!delegateStatus.IsSuccess()) + { + handler.AddStatus(commandPath, delegateStatus); + return std::nullopt; + } + } + } + + if (transportOptions.audioStreamID.HasValue()) + { + if (transportOptions.audioStreamID.Value().IsNull()) + { + uint16_t audioStreamID; + + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + mDelegate->SelectAudioStream(transportOptions.streamUsage, audioStreamID)); + + if (!delegateStatus.IsSuccess()) + { + handler.AddStatus(commandPath, delegateStatus); + return std::nullopt; + } + + transportOptionsPtr->audioStreamID.SetValue(audioStreamID); + } + else + { + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( + mDelegate->ValidateAudioStream(transportOptions.videoStreamID.Value().Value())); + + if (!delegateStatus.IsSuccess()) + { + handler.AddStatus(commandPath, delegateStatus); + return std::nullopt; + } + } + } + + uint16_t connectionID = GenerateConnectionID(); + + if (connectionID == kMaxConnectionId) + { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Max Connections Exhausted", mEndpointId); + handler.AddStatus(commandPath, Status::ResourceExhausted); + return std::nullopt; + } + + status = mDelegate->AllocatePushTransport(*transportOptionsPtr, connectionID); + + if (status == Status::Success) + { + // add connection to CurrentConnections + FabricIndex peerFabricIndex = handler.GetAccessingFabricIndex(); + + TransportConfigurationStorage outTransportConfiguration(connectionID, transportOptionsPtr); + + outTransportConfiguration.transportStatus = TransportStatusEnum::kInactive; + + outTransportConfiguration.SetFabricIndex(peerFabricIndex); + + UpsertStreamTransportConnection(outTransportConfiguration); + + response.transportConfiguration = outTransportConfiguration; + + // ExpiryTime Handling + if (transportOptions.expiryTime.HasValue()) + { + ScheduleTransportDeallocate(connectionID, transportOptions.expiryTime.Value()); + } + + handler.AddResponse(commandPath, response); + } + else + { + handler.AddStatus(commandPath, status); + } + + return std::nullopt; +} + +std::optional PushAvStreamTransportServerLogic::HandleDeallocatePushTransport( + CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::DeallocatePushTransport::DecodableType & commandData) +{ + if (IsNullDelegateWithLogging(commandPath.mEndpointId)) + { + return Status::UnsupportedCommand; + } + + uint16_t connectionID = commandData.connectionID; + FabricIndex FabricIndex = handler.GetAccessingFabricIndex(); + TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, FabricIndex); + if (transportConfiguration == nullptr) + { + ChipLogError(Zcl, "HandleDeallocatePushTransport[ep=%d]: ConnectionID Not Found.", mEndpointId); + handler.AddStatus(commandPath, Status::NotFound); + return std::nullopt; + } + + // Call the delegate + auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode(mDelegate->DeallocatePushTransport(connectionID)); + + if (delegateStatus.IsSuccess()) + { + // Remove connection from CurrentConnections + RemoveStreamTransportConnection(connectionID); + RemoveTimerAppState(connectionID); + } + + handler.AddStatus(commandPath, delegateStatus); + + return std::nullopt; +} + +std::optional +PushAvStreamTransportServerLogic::HandleModifyPushTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::ModifyPushTransport::DecodableType & commandData) +{ + if (IsNullDelegateWithLogging(commandPath.mEndpointId)) + { + return Status::UnsupportedCommand; + } + + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + auto & transportOptions = commandData.transportOptions; + + // Contraints check on incoming transport Options + Status transportOptionsValidityStatus = ValidateIncomingTransportOptions(transportOptions); + + VerifyOrDo(transportOptionsValidityStatus == Status::Success, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: TransportOptions of command data is not Valid", mEndpointId); + handler.AddStatus(commandPath, transportOptionsValidityStatus); + }); + + FabricIndex fabricIndex = handler.GetAccessingFabricIndex(); + + TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); + + if (transportConfiguration == nullptr) + { + ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: ConnectionID Not Found.", mEndpointId); + handler.AddStatus(commandPath, Status::NotFound); + return std::nullopt; + } + + if (mDelegate->GetTransportBusyStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) + { + ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: Connection is Busy", mEndpointId); + handler.AddStatus(commandPath, Status::Busy); + return std::nullopt; + } + + std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + + if (transportOptionsPtr == nullptr) + { + ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: Memory Allocation failed for transportOptions", mEndpointId); + handler.AddStatus(commandPath, Status::ResourceExhausted); + return std::nullopt; + } + // Call the delegate + status = mDelegate->ModifyPushTransport(connectionID, *transportOptionsPtr); + + if (status == Status::Success) + { + transportConfiguration->SetTransportOptionsPtr(transportOptionsPtr); + } + + handler.AddStatus(commandPath, status); + + return std::nullopt; +} + +std::optional +PushAvStreamTransportServerLogic::HandleSetTransportStatus(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::SetTransportStatus::DecodableType & commandData) +{ + if (IsNullDelegateWithLogging(commandPath.mEndpointId)) + { + return Status::UnsupportedCommand; + } + + Status status = Status::Success; + DataModel::Nullable connectionID = commandData.connectionID; + auto & transportStatus = commandData.transportStatus; + + VerifyOrDo(transportStatus != TransportStatusEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", mEndpointId); + handler.AddStatus(commandPath, Status::ConstraintError); + }); + + std::vector connectionIDList; + + if (connectionID.IsNull()) + { + for (auto & transportConnection : mCurrentConnections) + { + if (transportConnection.fabricIndex == handler.GetAccessingFabricIndex()) + { + connectionIDList.push_back(transportConnection.connectionID); + } + } + } + else + { + FabricIndex fabricIndex = handler.GetAccessingFabricIndex(); + TransportConfigurationStorage * transportConfiguration = + FindStreamTransportConnectionWithinFabric(connectionID.Value(), fabricIndex); + if (transportConfiguration == nullptr) + { + ChipLogError(Zcl, "HandleSetTransportStatus[ep=%d]: ConnectionID Not Found.", mEndpointId); + handler.AddStatus(commandPath, Status::NotFound); + return std::nullopt; + } + connectionIDList.push_back(connectionID.Value()); + } + // Call the delegate + status = mDelegate->SetTransportStatus(connectionIDList, transportStatus); + if (status == Status::Success) + { + for (auto & connID : connectionIDList) + { + for (auto & transportConnection : mCurrentConnections) + { + if (transportConnection.connectionID == connID) + { + transportConnection.transportStatus = transportStatus; + } + } + } + } + handler.AddStatus(commandPath, status); + + return std::nullopt; +} + +std::optional PushAvStreamTransportServerLogic::HandleManuallyTriggerTransport( + CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::ManuallyTriggerTransport::DecodableType & commandData) +{ + if (IsNullDelegateWithLogging(commandPath.mEndpointId)) + { + return Status::UnsupportedCommand; + } + + Status status = Status::Success; + uint16_t connectionID = commandData.connectionID; + auto & activationReason = commandData.activationReason; + + VerifyOrDo(activationReason != TriggerActivationReasonEnum::kUnknownEnumValue, { + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Activation Reason ", mEndpointId); + handler.AddStatus(commandPath, Status::ConstraintError); + }); + + Optional timeControl = commandData.timeControl; + + if (timeControl.HasValue()) + { + VerifyOrDo(timeControl.Value().initialDuration >= 1, { + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", + mEndpointId); + handler.AddStatus(commandPath, Status::ConstraintError); + }); + + VerifyOrDo(timeControl.Value().maxDuration >= 1, { + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", + mEndpointId); + handler.AddStatus(commandPath, Status::ConstraintError); + }); + } + + FabricIndex fabricIndex = handler.GetAccessingFabricIndex(); + TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); + + if (transportConfiguration == nullptr) + { + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: ConnectionID Not Found.", mEndpointId); + handler.AddStatus(commandPath, Status::NotFound); + return std::nullopt; + } + + if (mDelegate->GetTransportBusyStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) + { + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Connection is Busy", mEndpointId); + handler.AddStatus(commandPath, Status::Busy); + return std::nullopt; + } + + if (transportConfiguration->transportStatus == TransportStatusEnum::kInactive) + { + auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTransportStatus); + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Transport status", mEndpointId); + handler.AddClusterSpecificFailure(commandPath, clusterStatus); + return std::nullopt; + } + if (transportConfiguration->transportOptions.HasValue()) + { + if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kContinuous) + { + + auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTriggerType); + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Trigger type", mEndpointId); + handler.AddClusterSpecificFailure(commandPath, clusterStatus); + return std::nullopt; + } + if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand && + !timeControl.HasValue()) + { + + ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Time control field not present", mEndpointId); + handler.AddStatus(commandPath, Status::DynamicConstraintError); + return std::nullopt; + } + } + + // When trigger type is motion in the allocated transport but triggering it manually + if (!timeControl.HasValue()) + { + timeControl = transportConfiguration->transportOptions.Value().triggerOptions.motionTimeControl; + } + + // Call the delegate + status = mDelegate->ManuallyTriggerTransport(connectionID, activationReason, timeControl); + + if (status == Status::Success) + { + GeneratePushTransportBeginEvent(connectionID, TransportTriggerTypeEnum::kCommand, MakeOptional(activationReason)); + } + + handler.AddStatus(commandPath, status); + + return std::nullopt; +} + +std::optional +PushAvStreamTransportServerLogic::HandleFindTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::FindTransport::DecodableType & commandData) +{ + if (IsNullDelegateWithLogging(commandPath.mEndpointId)) + { + return Status::UnsupportedCommand; + } + + Commands::FindTransportResponse::Type response; + + Optional> connectionID = commandData.connectionID; + + std::vector transportConfigurations; + + if (!connectionID.HasValue() || connectionID.Value().IsNull()) + { + if (mCurrentConnections.size() == 0) + { + ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID not found", mEndpointId); + handler.AddStatus(commandPath, Status::NotFound); + return std::nullopt; + } + + for (auto & connection : mCurrentConnections) + { + if (connection.fabricIndex == handler.GetAccessingFabricIndex()) + { + transportConfigurations.push_back(connection); + } + } + } + else + { + FabricIndex fabricIndex = handler.GetAccessingFabricIndex(); + TransportConfigurationStorage * transportConfiguration = + FindStreamTransportConnectionWithinFabric(connectionID.Value().Value(), fabricIndex); + if (transportConfiguration == nullptr) + { + ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID not found", mEndpointId); + handler.AddStatus(commandPath, Status::NotFound); + return std::nullopt; + } + + transportConfigurations.push_back(*transportConfiguration); + } + + if (transportConfigurations.size() == 0) + { + handler.AddStatus(commandPath, Status::NotFound); + } + + response.transportConfigurations = + DataModel::List(transportConfigurations.data(), transportConfigurations.size()); + + handler.AddResponse(commandPath, response); + + return std::nullopt; +} + +Status +PushAvStreamTransportServerLogic::GeneratePushTransportBeginEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason) +{ + Events::PushTransportBegin::Type event; + EventNumber eventNumber; + + event.connectionID = connectionID; + event.triggerType = triggerType; + event.activationReason = activationReason; + + CHIP_ERROR err = LogEvent(event, mEndpointId, eventNumber); + if (CHIP_NO_ERROR != err) + { + ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportBegin event: %" CHIP_ERROR_FORMAT, mEndpointId, + err.Format()); + return Status::Failure; + } + return Status::Success; +} + +Status PushAvStreamTransportServerLogic::GeneratePushTransportEndEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason) +{ + Events::PushTransportEnd::Type event; + EventNumber eventNumber; + + event.connectionID = connectionID; + event.triggerType = triggerType; + event.activationReason = activationReason; + + CHIP_ERROR err = LogEvent(event, mEndpointId, eventNumber); + if (CHIP_NO_ERROR != err) + { + ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportEnd event: %" CHIP_ERROR_FORMAT, mEndpointId, + err.Format()); + return Status::Failure; + } + return Status::Success; +} + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h new file mode 100644 index 00000000000000..a92f00328d6ac5 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +class PushAvStreamTransportServerLogic +{ +public: + PushAvStreamTransportServerLogic(EndpointId aEndpoint, BitFlags aFeatures); + ~PushAvStreamTransportServerLogic(); + + void SetDelegate(EndpointId aEndpoint, PushAvStreamTransportDelegate * delegate) + { + mDelegate = delegate; + mDelegate->SetEndpointId(aEndpoint); + } + + enum class UpsertResultEnum : uint8_t + { + kInserted = 0x00, + kUpdated = 0x01, + }; + + struct PushAVStreamTransportDeallocateCallbackContext + { + PushAvStreamTransportServerLogic * instance; + uint16_t connectionID; + }; + + EndpointId mEndpointId = kInvalidEndpointId; + + std::vector> mTimerContexts; + + BitFlags mFeatures; + + std::vector mSupportedFormats; + + std::vector mCurrentConnections; + + CHIP_ERROR Init(); + + void Shutdown(); + + bool HasFeature(Feature feature) const; + + // Helpers to read list items via delegate APIs + CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, FabricIndex fabricIndex); + CHIP_ERROR ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder); + + Protocols::InteractionModel::Status + ValidateIncomingTransportOptions(const Structs::TransportOptionsStruct::DecodableType & transportOptions); + + std::optional + HandleAllocatePushTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::AllocatePushTransport::DecodableType & commandData); + + std::optional + HandleDeallocatePushTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::DeallocatePushTransport::DecodableType & commandData); + + std::optional + HandleModifyPushTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::ModifyPushTransport::DecodableType & commandData); + + std::optional + HandleSetTransportStatus(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::SetTransportStatus::DecodableType & commandData); + + std::optional + HandleManuallyTriggerTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const Commands::ManuallyTriggerTransport::DecodableType & commandData); + + std::optional HandleFindTransport(CommandHandler & handler, + const ConcreteCommandPath & commandPath, + const Commands::FindTransport::DecodableType & commandData); + + // Send Push AV Stream Transport events + Protocols::InteractionModel::Status + GeneratePushTransportBeginEvent(const uint16_t connectionID, const TransportTriggerTypeEnum triggerType, + const Optional activationReason); + Protocols::InteractionModel::Status GeneratePushTransportEndEvent(const uint16_t connectionID, + const TransportTriggerTypeEnum triggerType, + const Optional activationReason); + +private: + PushAvStreamTransportDelegate * mDelegate = nullptr; + + /// Convenience method that returns if the internal delegate is null and will log + /// an error if the check returns true + bool IsNullDelegateWithLogging(EndpointId endpointIdForLogging); + + /** + * Helper function that loads all the persistent attributes from the KVS. + */ + void LoadPersistentAttributes(); + + // Helper functions + uint16_t GenerateConnectionID(); + + TransportConfigurationStorage * FindStreamTransportConnection(const uint16_t connectionID); + + TransportConfigurationStorage * FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, FabricIndex fabricIndex); + + // Add/Remove Management functions for transport + UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStorage & transportConfiguration); + + void RemoveStreamTransportConnection(const uint16_t connectionID); + + static void PushAVStreamTransportDeallocateCallback(chip::System::Layer *, void * callbackContext); + + UpsertResultEnum UpsertTimerAppState(std::shared_ptr timerAppState); + + void RemoveTimerAppState(const uint16_t connectionID); + + /** + * @brief Schedule deallocate with a given timeout + * + * @param endpointId endpoint where DoorLockServer is running + * @param timeoutSec timeout in seconds + */ + void ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec); +}; +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index b621aaaf03031c..1da2c63b93cd9e 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -16,6 +16,7 @@ * */ +#include "constants.h" #include #include #include @@ -23,1095 +24,139 @@ #include #include #include -#include #include +#include +#include +#include #include +#include #include #include -static constexpr uint16_t kMaxConnectionId = 65535; // This is also invalid connectionID - -using namespace chip; -using namespace chip::app; -using namespace chip::app::Clusters; -using namespace chip::app::Clusters::PushAvStreamTransport; -using namespace chip::app::Clusters::PushAvStreamTransport::Structs; -using namespace chip::app::Clusters::PushAvStreamTransport::Attributes; -using namespace Protocols::InteractionModel; -using chip::Protocols::InteractionModel::Status; - namespace chip { namespace app { namespace Clusters { namespace PushAvStreamTransport { +namespace { -PushAvStreamTransportServer::PushAvStreamTransportServer(PushAvStreamTransportDelegate & aDelegate, EndpointId aEndpointId, - const BitFlags aFeatures) : - AttributeAccessInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), - CommandHandlerInterface(MakeOptional(aEndpointId), PushAvStreamTransport::Id), mDelegate(aDelegate), mFeatures(aFeatures), - mSupportedFormats{ SupportedFormatStruct{ ContainerFormatEnum::kCmaf, IngestMethodsEnum::kCMAFIngest } } -{ - /* set the base class delegates endpointId */ - mDelegate.SetEndpointId(aEndpointId); -} - -PushAvStreamTransportServer::~PushAvStreamTransportServer() -{ - for (const auto & timerContext : mtimerContexts) - { - DeviceLayer::SystemLayer().CancelTimer(PushAVStreamTransportDeallocateCallback, static_cast(timerContext.get())); - } - Shutdown(); -} - -CHIP_ERROR PushAvStreamTransportServer::Init() -{ - LoadPersistentAttributes(); - - VerifyOrReturnError(AttributeAccessInterfaceRegistry::Instance().Register(this), CHIP_ERROR_INTERNAL); - ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(this)); - return CHIP_NO_ERROR; -} - -void PushAvStreamTransportServer::Shutdown() -{ // Unregister command handler and attribute access interfaces - CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(this); - AttributeAccessInterfaceRegistry::Instance().Unregister(this); -} - -bool PushAvStreamTransportServer::HasFeature(Feature feature) const -{ - return mFeatures.Has(feature); -} - -CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder) -{ - for (const auto & supportsFormat : mSupportedFormats) - { - ReturnErrorOnFailure(encoder.Encode(supportsFormat)); - } - - return CHIP_NO_ERROR; -} - -CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, - FabricIndex fabricIndex) -{ - for (const auto & currentConnections : mCurrentConnections) - { - if (currentConnections.fabricIndex == fabricIndex) - { - ReturnErrorOnFailure(encoder.Encode(currentConnections)); - } - } - - return CHIP_NO_ERROR; -} - -PushAvStreamTransportServer::UpsertResultEnum -PushAvStreamTransportServer::UpsertStreamTransportConnection(const TransportConfigurationStorage & transportConfiguration) -{ - UpsertResultEnum result; - auto it = - std::find_if(mCurrentConnections.begin(), mCurrentConnections.end(), - [id = transportConfiguration.connectionID](const auto & existing) { return existing.connectionID == id; }); - - if (it != mCurrentConnections.end()) - { - *it = transportConfiguration; - result = UpsertResultEnum::kUpdated; - } - else - { - mCurrentConnections.push_back(transportConfiguration); - result = UpsertResultEnum::kInserted; - } - - MatterReportingAttributeChangeCallback(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, - PushAvStreamTransport::Attributes::CurrentConnections::Id); - - return result; -} - -PushAvStreamTransportServer::UpsertResultEnum -PushAvStreamTransportServer::UpsertTimerAppState(std::shared_ptr timerAppState) -{ - UpsertResultEnum result; - auto it = std::find_if(mtimerContexts.begin(), mtimerContexts.end(), - [id = timerAppState->connectionID](const auto & existing) { return existing->connectionID == id; }); - - if (it != mtimerContexts.end()) - { - *it = timerAppState; - result = UpsertResultEnum::kUpdated; - } - else - { - mtimerContexts.push_back(timerAppState); - result = UpsertResultEnum::kInserted; - } - - return result; -} - -void PushAvStreamTransportServer::RemoveStreamTransportConnection(const uint16_t transportConnectionId) -{ - size_t originalSize = mCurrentConnections.size(); - - // Erase-Remove idiom - mCurrentConnections.erase(std::remove_if(mCurrentConnections.begin(), mCurrentConnections.end(), - [transportConnectionId](const TransportConfigurationStorage & s) { - return s.connectionID == transportConnectionId; - }), - mCurrentConnections.end()); - - // If a connection was removed, the size will be smaller. - if (mCurrentConnections.size() < originalSize) - { - // Notify the stack that the CurrentConnections attribute has changed. - MatterReportingAttributeChangeCallback(AttributeAccessInterface::GetEndpointId().Value(), PushAvStreamTransport::Id, - PushAvStreamTransport::Attributes::CurrentConnections::Id); - } -} - -void PushAvStreamTransportServer::RemoveTimerAppState(const uint16_t connectionID) -{ - // Erase-Remove idiom - auto it = std::remove_if(mtimerContexts.begin(), mtimerContexts.end(), - [connectionID](const std::shared_ptr & s) { - if (s->connectionID == connectionID) - { - DeviceLayer::SystemLayer().CancelTimer(PushAVStreamTransportDeallocateCallback, - static_cast(s.get())); - return true; // Remove from vector - } - return false; // Keep in vector - }); - - mtimerContexts.erase(it, mtimerContexts.end()); -} - -CHIP_ERROR PushAvStreamTransportServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) -{ - VerifyOrDie(aPath.mClusterId == PushAvStreamTransport::Id); - ChipLogProgress(Zcl, "Push AV Stream Transport[ep=%d]: Reading", AttributeAccessInterface::GetEndpointId().Value()); - - switch (aPath.mAttributeId) - { - case FeatureMap::Id: - ReturnErrorOnFailure(aEncoder.Encode(mFeatures)); - break; - - case SupportedFormats::Id: - ReturnErrorOnFailure(aEncoder.EncodeList( - [this](const auto & encoder) -> CHIP_ERROR { return this->ReadAndEncodeSupportedFormats(encoder); })); - break; - - case CurrentConnections::Id: - ReturnErrorOnFailure(aEncoder.EncodeList([this, &aEncoder](const auto & encoder) -> CHIP_ERROR { - return this->ReadAndEncodeCurrentConnections(encoder, aEncoder.AccessingFabricIndex()); - })); - break; - } - - return CHIP_NO_ERROR; -} - -void PushAvStreamTransportServer::LoadPersistentAttributes() -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - // Load currentConnections - err = mDelegate.LoadCurrentConnections(mCurrentConnections); - if (err != CHIP_NO_ERROR) - { - ChipLogDetail(Zcl, "PushAVStreamTransport: Unable to load allocated connections from the KVS."); - } - - // Signal delegate that all persistent configuration attributes have been loaded. - mDelegate.PersistentAttributesLoadedCallback(); -} - -// CommandHandlerInterface -void PushAvStreamTransportServer::InvokeCommand(HandlerContext & handlerContext) -{ - ChipLogDetail(Zcl, "PushAV: InvokeCommand"); - switch (handlerContext.mRequestPath.mCommandId) - { - case Commands::AllocatePushTransport::Id: - ChipLogDetail(Zcl, "PushAVStreamTransport: Allocating Push Transport"); - - HandleCommand( - handlerContext, - [this](HandlerContext & ctx, const auto & commandData) { HandleAllocatePushTransport(ctx, commandData); }); - - break; - - case Commands::DeallocatePushTransport::Id: - ChipLogDetail(Zcl, "PushAVStreamTransport: Deallocating Push Transport"); - - HandleCommand( - handlerContext, - [this](HandlerContext & ctx, const auto & commandData) { HandleDeallocatePushTransport(ctx, commandData); }); - - break; +using namespace chip::app::Clusters::PushAvStreamTransport::Commands; - case Commands::ModifyPushTransport::Id: - ChipLogDetail(Zcl, "PushAVStreamTransport: Modifying Push Transport"); +constexpr DataModel::AcceptedCommandEntry kAcceptedCommands[] = { + AllocatePushTransport::kMetadataEntry, DeallocatePushTransport::kMetadataEntry, ModifyPushTransport::kMetadataEntry, + SetTransportStatus::kMetadataEntry, ManuallyTriggerTransport::kMetadataEntry, FindTransport::kMetadataEntry, +}; - HandleCommand( - handlerContext, - [this](HandlerContext & ctx, const auto & commandData) { HandleModifyPushTransport(ctx, commandData); }); +constexpr CommandId kGeneratedCommands[] = { + AllocatePushTransportResponse::Id, + FindTransportResponse::Id, +}; - break; +constexpr DataModel::AttributeEntry kAttributes[] = { + PushAvStreamTransport::Attributes::SupportedFormats::kMetadataEntry, + PushAvStreamTransport::Attributes::CurrentConnections::kMetadataEntry, +}; - case Commands::SetTransportStatus::Id: - ChipLogDetail(Zcl, "PushAVStreamTransport: Setting Push Transport Status"); +} // namespace - HandleCommand( - handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleSetTransportStatus(ctx, commandData); }); +using Protocols::InteractionModel::Status; +using namespace PushAvStreamTransport::Commands; - break; - - case Commands::ManuallyTriggerTransport::Id: - ChipLogDetail(Zcl, "PushAVStreamTransport: Manually Triggered Push Transport"); - - HandleCommand( - handlerContext, - [this](HandlerContext & ctx, const auto & commandData) { HandleManuallyTriggerTransport(ctx, commandData); }); - - break; - - case Commands::FindTransport::Id: - ChipLogDetail(Zcl, "PushAVStreamTransport: Finding Push Transport"); - - HandleCommand( - handlerContext, [this](HandlerContext & ctx, const auto & commandData) { HandleFindTransport(ctx, commandData); }); - - break; - default: - // Mark unrecognized command as UnsupportedCommand - handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, Status::UnsupportedCommand); - break; - } -} - -TransportConfigurationStorage * PushAvStreamTransportServer::FindStreamTransportConnection(const uint16_t connectionID) -{ - for (auto & transportConnection : mCurrentConnections) - { - if (transportConnection.connectionID == connectionID) - { - return &transportConnection; - } - } - return nullptr; -} - -TransportConfigurationStorage * PushAvStreamTransportServer::FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, - FabricIndex fabricIndex) -{ - for (auto & transportConnection : mCurrentConnections) - { - if (transportConnection.connectionID == connectionID) - { - if (transportConnection.GetFabricIndex() == fabricIndex) - { - return &transportConnection; - } - } - } - return nullptr; -} - -uint16_t PushAvStreamTransportServer::GenerateConnectionID() -{ - static uint16_t lastID = 0; - - for (uint16_t i = 0; i < kMaxConnectionId; ++i) - { - uint16_t candidateID = static_cast((lastID + i + 1) % kMaxConnectionId); // Wrap from 0 to 65534 - if (FindStreamTransportConnection(candidateID) == nullptr) - { - lastID = candidateID; - return candidateID; - } - } - - return kMaxConnectionId; // All 0 to 65534 IDs are in use -} - -void PushAvStreamTransportServer::PushAVStreamTransportDeallocateCallback(System::Layer *, void * callbackContext) -{ - PushAVStreamTransportDeallocateCallbackContext * transportDeallocateContext = - static_cast((callbackContext)); - - uint16_t connectionID = transportDeallocateContext->connectionID; - - // Call the delegate - auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( - transportDeallocateContext->instance->mDelegate.DeallocatePushTransport(connectionID)); - - if (delegateStatus.IsSuccess() == true) - { - ChipLogProgress(Zcl, "Push AV Stream Transport Deallocate timer expired. %s", "Deallocating"); - - // Remove connection from CurrentConnections - transportDeallocateContext->instance->RemoveStreamTransportConnection(connectionID); - transportDeallocateContext->instance->RemoveTimerAppState(connectionID); - } - else - { - ChipLogError(Zcl, "Push AV Stream Transport Deallocate timer expired. %s", "Deallocation Failed"); - } -} - -void PushAvStreamTransportServer::ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec) -{ - uint32_t timeoutMs = timeoutSec * MILLISECOND_TICKS_PER_SECOND; - - std::shared_ptr transportDeallocateContext{ new ( - std::nothrow) PushAVStreamTransportDeallocateCallbackContext{ this, connectionID } }; - - if (transportDeallocateContext == nullptr) - { - ChipLogError(Zcl, "Failed to allocate memory for deallocate context"); - return; - } - - CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(timeoutMs), - PushAVStreamTransportDeallocateCallback, - static_cast(transportDeallocateContext.get())); - - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to schedule deallocate: timeout=%" PRIu32 ", status=%" CHIP_ERROR_FORMAT, timeoutSec, - err.Format()); - } - else - { - UpsertTimerAppState(transportDeallocateContext); - } -} - -Status PushAvStreamTransportServer::ValidateIncomingTransportOptions( - const Structs::TransportOptionsStruct::DecodableType & transportOptions) +CHIP_ERROR PushAvStreamTransportServer::Attributes(const ConcreteClusterPath & path, + ReadOnlyBufferBuilder & builder) { - // Contraints check on incoming transport Options - VerifyOrReturnValue(transportOptions.streamUsage != StreamUsageEnum::kUnknownEnumValue, Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid streamUsage ", - AttributeAccessInterface::GetEndpointId().Value())); - - VerifyOrReturnValue(transportOptions.videoStreamID.HasValue() || transportOptions.audioStreamID.HasValue(), - Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing videoStreamID and audioStreamID", - AttributeAccessInterface::GetEndpointId().Value())); - - VerifyOrReturnValue( - transportOptions.url.size() >= kMinUrlLength && transportOptions.url.size() <= kMaxUrlLength, Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: URL length: %" PRIu32 " not in allowed length range of 13 to 2000", - AttributeAccessInterface::GetEndpointId().Value(), static_cast(transportOptions.url.size()))); - - auto & triggerOptions = transportOptions.triggerOptions; - - VerifyOrReturnValue(triggerOptions.triggerType != TransportTriggerTypeEnum::kUnknownEnumValue, Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid triggerType ", - AttributeAccessInterface::GetEndpointId().Value())); - - if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion) - { - VerifyOrReturnValue(triggerOptions.motionZones.HasValue(), Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing motion zones ", - AttributeAccessInterface::GetEndpointId().Value())); - - if (triggerOptions.motionZones.Value().IsNull() == false) - { - auto & motionZonesList = triggerOptions.motionZones; - auto iter = motionZonesList.Value().Value().begin(); - - while (iter.Next()) - { - auto & transportZoneOption = iter.GetValue(); - - if (mFeatures.Has(Feature::kPerZoneSensitivity)) - { - VerifyOrReturnValue(transportZoneOption.sensitivity.HasValue(), Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Zone Sensitivity ", - AttributeAccessInterface::GetEndpointId().Value())); - - VerifyOrReturnValue(transportZoneOption.sensitivity.Value() >= 1 && - transportZoneOption.sensitivity.Value() <= 10, - Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Zone Sensitivity Constraint Error", - AttributeAccessInterface::GetEndpointId().Value())); - } - else - { - VerifyOrReturnValue( - transportZoneOption.sensitivity.HasValue() == false, Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Found Zone Sensitivity which is not expected.", - AttributeAccessInterface::GetEndpointId().Value())); - } - } - - if (iter.GetStatus() != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Zones TLV Validation failed", - AttributeAccessInterface::GetEndpointId().Value()); - return Status::InvalidCommand; - } - } - - VerifyOrReturnValue(triggerOptions.motionTimeControl.HasValue(), Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Time Control ", - AttributeAccessInterface::GetEndpointId().Value())); - - VerifyOrReturnValue( - triggerOptions.motionTimeControl.Value().initialDuration >= 1, Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value())); - - VerifyOrReturnValue(triggerOptions.motionTimeControl.Value().maxDuration >= 1, Status::ConstraintError, - ChipLogError(Zcl, - "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value())); - } - else - { - - VerifyOrReturnValue(triggerOptions.motionZones.HasValue() == false, Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Found motion zones which is not expected", - AttributeAccessInterface::GetEndpointId().Value())); - - VerifyOrReturnValue(triggerOptions.motionTimeControl.HasValue() == false, Status::InvalidCommand, - ChipLogError(Zcl, - "HandleAllocatePushTransport[ep=%d]: Found Motion Time Control which is not expected ", - AttributeAccessInterface::GetEndpointId().Value())); - } - - if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion && mFeatures.Has(Feature::kPerZoneSensitivity) == false) - { - VerifyOrReturnValue(triggerOptions.motionSensitivity.HasValue(), Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Motion Sensitivity ", - AttributeAccessInterface::GetEndpointId().Value())); - - VerifyOrReturnValue(triggerOptions.motionSensitivity.Value().Value() >= 1 && - triggerOptions.motionSensitivity.Value().Value() <= 10, - Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Sensitivity Constraint Error", - AttributeAccessInterface::GetEndpointId().Value())); - } - else - { - VerifyOrReturnValue(triggerOptions.motionSensitivity.HasValue() == false, Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Found Motion Sensitivity which is not expected ", - AttributeAccessInterface::GetEndpointId().Value())); - } - - if (triggerOptions.triggerType == TransportTriggerTypeEnum::kMotion || - triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand) - { - VerifyOrReturnValue(triggerOptions.maxPreRollLen.HasValue(), Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing Max Pre Roll Len field ", - AttributeAccessInterface::GetEndpointId().Value())); - } - else - { - VerifyOrReturnValue(triggerOptions.maxPreRollLen.HasValue() == false, Status::InvalidCommand, - ChipLogError(Zcl, - "HandleAllocatePushTransport[ep=%d]: Found Max Pre Roll Len field which is not expected.", - AttributeAccessInterface::GetEndpointId().Value())); - } - - IngestMethodsEnum ingestMethod = transportOptions.ingestMethod; - - VerifyOrReturnValue(ingestMethod != IngestMethodsEnum::kUnknownEnumValue, Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method ", - AttributeAccessInterface::GetEndpointId().Value())); - - ContainerOptionsStruct containerOptions = transportOptions.containerOptions; - - VerifyOrReturnValue(containerOptions.containerType != ContainerFormatEnum::kUnknownEnumValue, Status::ConstraintError, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Container Format ", - AttributeAccessInterface::GetEndpointId().Value())); - - if (containerOptions.containerType == ContainerFormatEnum::kCmaf) - { - VerifyOrReturnValue(containerOptions.CMAFContainerOptions.HasValue(), Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options ", - AttributeAccessInterface::GetEndpointId().Value())); - - if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) - { - VerifyOrReturnValue( - containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size() == kMaxCENCKeyLength, Status::ConstraintError, - ChipLogError( - Zcl, - "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key constraint Error, actual length: %" PRIu32 - " not " - "equal to expected length of 16", - AttributeAccessInterface::GetEndpointId().Value(), - static_cast(containerOptions.CMAFContainerOptions.Value().CENCKey.Value().size()))); - } - - if (!mFeatures.Has(Feature::kMetadata)) - { - VerifyOrReturnValue( - containerOptions.CMAFContainerOptions.Value().metadataEnabled.HasValue() == false, Status::InvalidCommand, - ChipLogError( - Zcl, "HandleAllocatePushTransport[ep=%d]: Found CMAF Container Options MetadataEnabled which is not expected.", - AttributeAccessInterface::GetEndpointId().Value())); - } - - if (containerOptions.CMAFContainerOptions.Value().CENCKey.HasValue()) - { - VerifyOrReturnValue(containerOptions.CMAFContainerOptions.Value().CENCKeyID.HasValue(), Status::InvalidCommand, - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Missing CMAF Container Options CENC Key ID ", - AttributeAccessInterface::GetEndpointId().Value())); - - VerifyOrReturnValue( - containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size() == kMaxCENCKeyIDLength, - Status::ConstraintError, - ChipLogError(Zcl, - "HandleAllocatePushTransport[ep=%d]: CMAF Container Options CENC Key ID constraint Error, actual " - "length: %" PRIu32 " not equal to expected length of 16", - AttributeAccessInterface::GetEndpointId().Value(), - static_cast(containerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size()))); - } - else - { - VerifyOrReturnValue( - containerOptions.CMAFContainerOptions.Value().CENCKeyID.HasValue() == false, Status::InvalidCommand, - ChipLogError(Zcl, - "HandleAllocatePushTransport[ep=%d]: Found CMAF Container Options CENC Key ID which is not expected", - AttributeAccessInterface::GetEndpointId().Value())); - } - } - else - { - VerifyOrReturnValue(containerOptions.CMAFContainerOptions.HasValue() == false, Status::InvalidCommand, - ChipLogError(Zcl, - "HandleAllocatePushTransport[ep=%d]: Found CMAF Container Options which is not expected ", - AttributeAccessInterface::GetEndpointId().Value())); - } - - return Status::Success; + ReturnErrorOnFailure(builder.ReferenceExisting(kAttributes)); + return builder.AppendElements(DefaultServerCluster::GlobalAttributes()); } -void PushAvStreamTransportServer::HandleAllocatePushTransport(HandlerContext & ctx, - const Commands::AllocatePushTransport::DecodableType & commandData) +DataModel::ActionReturnStatus PushAvStreamTransportServer::ReadAttribute(const DataModel::ReadAttributeRequest & request, + AttributeValueEncoder & aEncoder) { - Commands::AllocatePushTransportResponse::Type response; - auto & transportOptions = commandData.transportOptions; - - Status transportOptionsValidityStatus = ValidateIncomingTransportOptions(transportOptions); - - VerifyOrReturn(transportOptionsValidityStatus == Status::Success, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: TransportOptions of command data is not Valid", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, transportOptionsValidityStatus); - }); - - // Todo: TLSEndpointID Validation - - IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; - - ContainerOptionsStruct containerOptions = commandData.transportOptions.containerOptions; - - bool isFormatSupported = false; - - for (auto & supportsFormat : mSupportedFormats) - { - if ((supportsFormat.ingestMethod == ingestMethod) && (supportsFormat.containerFormat == containerOptions.containerType)) - { - isFormatSupported = true; - } - } - - if (isFormatSupported == false) - { - auto status = to_underlying(StatusCodeEnum::kInvalidCombination); - ChipLogError(Zcl, - "HandleAllocatePushTransport[ep=%d]: Invalid Ingest Method and Container Format Combination : (Ingest Method: " - "%02X and Container Format: %02X)", - AttributeAccessInterface::GetEndpointId().Value(), to_underlying(ingestMethod), - to_underlying(containerOptions.containerType)); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); - return; - } - - bool isValidUrl = mDelegate.ValidateUrl(std::string(transportOptions.url.data(), transportOptions.url.size())); - - if (isValidUrl == false) - { - auto status = to_underlying(StatusCodeEnum::kInvalidURL); - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Url", AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); - return; - } - - /*Spec issue for invalid Trigger Type: https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/11701*/ - if (transportOptions.triggerOptions.triggerType == TransportTriggerTypeEnum::kUnknownEnumValue) - { - auto status = to_underlying(StatusCodeEnum::kInvalidTriggerType); - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Invalid Trigger type", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, status); - return; - } - - // Todo:Validate ZoneId - - // Validate Bandwidth Requirement - Status status = mDelegate.ValidateBandwidthLimit(transportOptions.streamUsage, transportOptions.videoStreamID, - transportOptions.audioStreamID); - if (status != Status::Success) - { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Resource Exhausted", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); - return; - } - - std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; - - if (transportOptionsPtr == nullptr) - { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Memory Allocation failed for transportOptions", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); - return; - } - - if (transportOptions.videoStreamID.HasValue()) - { - if (transportOptions.videoStreamID.Value().IsNull() == true) - { - uint16_t videoStreamID; + VerifyOrDie(request.path.mClusterId == PushAvStreamTransport::Id); - auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( - mDelegate.SelectVideoStream(transportOptions.streamUsage, videoStreamID)); - - if (delegateStatus.IsSuccess() == false) - { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); - return; - } - - transportOptionsPtr->videoStreamID.SetValue(videoStreamID); - } - else - { - auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( - mDelegate.ValidateVideoStream(transportOptions.videoStreamID.Value().Value())); - - if (delegateStatus.IsSuccess() == false) - { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); - return; - } - } - } + ChipLogProgress(Zcl, "Push AV Stream Transport[ep=%d]: Reading Attribute %d", request.path.mEndpointId, + request.path.mAttributeId); - if (transportOptions.audioStreamID.HasValue() == true) + switch (request.path.mAttributeId) { - if (transportOptions.audioStreamID.Value().IsNull() == true) - { - uint16_t audioStreamID; + case PushAvStreamTransport::Attributes::FeatureMap::Id: + return aEncoder.Encode(mLogic.mFeatures.Raw()); - auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( - mDelegate.SelectAudioStream(transportOptions.streamUsage, audioStreamID)); + case PushAvStreamTransport::Attributes::ClusterRevision::Id: + return aEncoder.Encode(PushAvStreamTransport::kRevision); - if (delegateStatus.IsSuccess() == false) - { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); - return; - } - - transportOptionsPtr->audioStreamID.SetValue(audioStreamID); - } - else - { - auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode( - mDelegate.ValidateAudioStream(transportOptions.videoStreamID.Value().Value())); + case PushAvStreamTransport::Attributes::SupportedFormats::Id: + return aEncoder.EncodeList( + [this](const auto & encoder) -> CHIP_ERROR { return mLogic.ReadAndEncodeSupportedFormats(encoder); }); - if (delegateStatus.IsSuccess() == false) + case PushAvStreamTransport::Attributes::CurrentConnections::Id: + return aEncoder.EncodeList([this, &aEncoder](const auto & encoder) -> CHIP_ERROR { + CHIP_ERROR err = mLogic.ReadAndEncodeCurrentConnections(encoder, aEncoder.AccessingFabricIndex()); + if (err != CHIP_NO_ERROR) { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); - return; + ChipLogError(Zcl, "Push AV Stream Transport: Error reading CurrentConnections"); } - } - } - - uint16_t connectionID = GenerateConnectionID(); - - if (connectionID == kMaxConnectionId) - { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Max Connections Exhausted", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); - return; - } - - status = mDelegate.AllocatePushTransport(*transportOptionsPtr, connectionID); - - if (status == Status::Success) - { - // add connection to CurrentConnections - FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - - TransportConfigurationStorage outTransportConfiguration(connectionID, transportOptionsPtr); - - outTransportConfiguration.SetFabricIndex(peerFabricIndex); - - UpsertStreamTransportConnection(outTransportConfiguration); - - response.transportConfiguration = outTransportConfiguration; - - // ExpiryTime Handling - if (transportOptions.expiryTime.HasValue()) - { - ScheduleTransportDeallocate(connectionID, transportOptions.expiryTime.Value()); - } - - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); - } - else - { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); - } -} - -void PushAvStreamTransportServer::HandleDeallocatePushTransport( - HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & commandData) -{ - uint16_t connectionID = commandData.connectionID; - FabricIndex FabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, FabricIndex); - if (transportConfiguration == nullptr) - { - ChipLogError(Zcl, "HandleDeallocatePushTransport[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } - - // Call the delegate - auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode(mDelegate.DeallocatePushTransport(connectionID)); - - if (delegateStatus.IsSuccess() == true) - { - // Remove connection from CurrentConnections - RemoveStreamTransportConnection(connectionID); - RemoveTimerAppState(connectionID); + return err; + }); } - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus); + return Status::UnsupportedAttribute; } -void PushAvStreamTransportServer::HandleModifyPushTransport(HandlerContext & ctx, - const Commands::ModifyPushTransport::DecodableType & commandData) +CHIP_ERROR PushAvStreamTransportServer::AcceptedCommands(const ConcreteClusterPath & path, + ReadOnlyBufferBuilder & builder) { - Status status = Status::Success; - uint16_t connectionID = commandData.connectionID; - auto & transportOptions = commandData.transportOptions; - - // Contraints check on incoming transport Options - Status transportOptionsValidityStatus = ValidateIncomingTransportOptions(transportOptions); - - VerifyOrReturn(transportOptionsValidityStatus == Status::Success, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: TransportOptions of command data is not Valid", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, transportOptionsValidityStatus); - }); - - FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - - TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); - - if (transportConfiguration == nullptr) - { - ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } - - if (mDelegate.GetTransportBusyStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) - { - ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: Connection is Busy", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Busy); - return; - } - - std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; - - if (transportOptionsPtr == nullptr) - { - ChipLogError(Zcl, "HandleModifyPushTransport[ep=%d]: Memory Allocation failed for transportOptions", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted); - return; - } - // Call the delegate - status = mDelegate.ModifyPushTransport(connectionID, *transportOptionsPtr); - - if (status == Status::Success) - { - transportConfiguration->SetTransportOptionsPtr(transportOptionsPtr); - } - - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + return builder.ReferenceExisting(kAcceptedCommands); } -void PushAvStreamTransportServer::HandleSetTransportStatus(HandlerContext & ctx, - const Commands::SetTransportStatus::DecodableType & commandData) +CHIP_ERROR PushAvStreamTransportServer::GeneratedCommands(const ConcreteClusterPath & path, + ReadOnlyBufferBuilder & builder) { - Status status = Status::Success; - DataModel::Nullable connectionID = commandData.connectionID; - auto & transportStatus = commandData.transportStatus; - - VerifyOrReturn(transportStatus != TransportStatusEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - - std::vector connectionIDList; - - if (connectionID.IsNull()) - { - for (auto & transportConnection : mCurrentConnections) - { - if (transportConnection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) - { - connectionIDList.push_back(transportConnection.connectionID); - } - } - } - else - { - FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorage * transportConfiguration = - FindStreamTransportConnectionWithinFabric(connectionID.Value(), fabricIndex); - if (transportConfiguration == nullptr) - { - ChipLogError(Zcl, "HandleSetTransportStatus[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } - connectionIDList.push_back(connectionID.Value()); - } - // Call the delegate - status = mDelegate.SetTransportStatus(connectionIDList, transportStatus); - if (status == Status::Success) - { - for (auto & connID : connectionIDList) - { - for (auto & transportConnection : mCurrentConnections) - { - if (transportConnection.connectionID == connID) - { - transportConnection.transportStatus = transportStatus; - } - } - } - } - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); + return builder.ReferenceExisting(kGeneratedCommands); } -void PushAvStreamTransportServer::HandleManuallyTriggerTransport( - HandlerContext & ctx, const Commands::ManuallyTriggerTransport::DecodableType & commandData) +std::optional PushAvStreamTransportServer::InvokeCommand(const DataModel::InvokeRequest & request, + TLV::TLVReader & input_arguments, + CommandHandler * handler) { - Status status = Status::Success; - uint16_t connectionID = commandData.connectionID; - auto & activationReason = commandData.activationReason; - - VerifyOrReturn(activationReason != TriggerActivationReasonEnum::kUnknownEnumValue, { - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Activation Reason ", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); + using namespace PushAvStreamTransport::Commands; - Optional timeControl = commandData.timeControl; - - if (timeControl.HasValue()) - { - VerifyOrReturn(timeControl.Value().initialDuration >= 1, { - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Motion Time Control (InitialDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - - VerifyOrReturn(timeControl.Value().maxDuration >= 1, { - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Motion Time Control (MaxDuration) Constraint Error", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError); - }); - } - - FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorage * transportConfiguration = FindStreamTransportConnectionWithinFabric(connectionID, fabricIndex); - - if (transportConfiguration == nullptr) - { - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: ConnectionID Not Found.", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } - - if (mDelegate.GetTransportBusyStatus(connectionID) == PushAvStreamTransportStatusEnum::kBusy) - { - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Connection is Busy", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Busy); - return; - } + FabricIndex fabricIndex = request.GetAccessingFabricIndex(); - if (transportConfiguration->transportStatus == TransportStatusEnum::kInactive) + switch (request.path.mCommandId) { - auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTransportStatus); - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Transport status", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); - return; + case AllocatePushTransport::Id: { + AllocatePushTransport::DecodableType data; + ReturnErrorOnFailure(data.Decode(input_arguments, fabricIndex)); + return mLogic.HandleAllocatePushTransport(*handler, request.path, data); } - if (transportConfiguration->transportOptions.HasValue()) - { - if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kContinuous) - { - - auto clusterStatus = to_underlying(StatusCodeEnum::kInvalidTriggerType); - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Invalid Trigger type", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, clusterStatus); - return; - } - if (transportConfiguration->transportOptions.Value().triggerOptions.triggerType == TransportTriggerTypeEnum::kCommand && - timeControl.HasValue() == false) - { - - ChipLogError(Zcl, "HandleManuallyTriggerTransport[ep=%d]: Time control field not present", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::DynamicConstraintError); - return; - } + case DeallocatePushTransport::Id: { + DeallocatePushTransport::DecodableType data; + ReturnErrorOnFailure(data.Decode(input_arguments, fabricIndex)); + return mLogic.HandleDeallocatePushTransport(*handler, request.path, data); } - - // When trigger type is motion in the allocated transport but triggering it manually - if (timeControl.HasValue() == false) - { - timeControl = transportConfiguration->transportOptions.Value().triggerOptions.motionTimeControl; + case ModifyPushTransport::Id: { + ModifyPushTransport::DecodableType data; + ReturnErrorOnFailure(data.Decode(input_arguments, fabricIndex)); + return mLogic.HandleModifyPushTransport(*handler, request.path, data); } - - // Call the delegate - status = mDelegate.ManuallyTriggerTransport(connectionID, activationReason, timeControl); - - if (status == Status::Success) - { - GeneratePushTransportBeginEvent(connectionID, TransportTriggerTypeEnum::kCommand, MakeOptional(activationReason)); - } - - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status); -} - -void PushAvStreamTransportServer::HandleFindTransport(HandlerContext & ctx, - const Commands::FindTransport::DecodableType & commandData) -{ - Commands::FindTransportResponse::Type response; - - Optional> connectionID = commandData.connectionID; - - std::vector transportConfigurations; - - if ((connectionID.HasValue() == false) || connectionID.Value().IsNull()) - { - if (mCurrentConnections.size() == 0) - { - ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID not found", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } - - for (auto & connection : mCurrentConnections) - { - if (connection.fabricIndex == ctx.mCommandHandler.GetAccessingFabricIndex()) - { - transportConfigurations.push_back(connection); - } - } + case SetTransportStatus::Id: { + SetTransportStatus::DecodableType data; + ReturnErrorOnFailure(data.Decode(input_arguments, fabricIndex)); + return mLogic.HandleSetTransportStatus(*handler, request.path, data); } - else - { - FabricIndex fabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex(); - TransportConfigurationStorage * transportConfiguration = - FindStreamTransportConnectionWithinFabric(connectionID.Value().Value(), fabricIndex); - if (transportConfiguration == nullptr) - { - ChipLogError(Zcl, "HandleFindTransport[ep=%d]: ConnectionID not found", - AttributeAccessInterface::GetEndpointId().Value()); - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); - return; - } - - transportConfigurations.push_back(*transportConfiguration); + case ManuallyTriggerTransport::Id: { + ManuallyTriggerTransport::DecodableType data; + ReturnErrorOnFailure(data.Decode(input_arguments, fabricIndex)); + return mLogic.HandleManuallyTriggerTransport(*handler, request.path, data); } - - if (transportConfigurations.size() == 0) - { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound); + case FindTransport::Id: { + FindTransport::DecodableType data; + ReturnErrorOnFailure(data.Decode(input_arguments, fabricIndex)); + return mLogic.HandleFindTransport(*handler, request.path, data); } - - response.transportConfigurations = - DataModel::List(transportConfigurations.data(), transportConfigurations.size()); - - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); -} - -Status PushAvStreamTransportServer::GeneratePushTransportBeginEvent(const uint16_t connectionID, - const TransportTriggerTypeEnum triggerType, - const Optional activationReason) -{ - Events::PushTransportBegin::Type event; - EventNumber eventNumber; - - event.connectionID = connectionID; - event.triggerType = triggerType; - event.activationReason = activationReason; - - CHIP_ERROR err = LogEvent(event, AttributeAccessInterface::GetEndpointId().Value(), eventNumber); - if (CHIP_NO_ERROR != err) - { - ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportBegin event: %" CHIP_ERROR_FORMAT, - AttributeAccessInterface::GetEndpointId().Value(), err.Format()); - return Status::Failure; } - return Status::Success; -} - -Status PushAvStreamTransportServer::GeneratePushTransportEndEvent(const uint16_t connectionID, - const TransportTriggerTypeEnum triggerType, - const Optional activationReason) -{ - Events::PushTransportEnd::Type event; - EventNumber eventNumber; - - event.connectionID = connectionID; - event.triggerType = triggerType; - event.activationReason = activationReason; - CHIP_ERROR err = LogEvent(event, AttributeAccessInterface::GetEndpointId().Value(), eventNumber); - if (CHIP_NO_ERROR != err) - { - ChipLogError(AppServer, "Endpoint %d - Unable to generate PushAVTransportEnd event: %" CHIP_ERROR_FORMAT, - AttributeAccessInterface::GetEndpointId().Value(), err.Format()); - return Status::Failure; - } - return Status::Success; + return Status::UnsupportedCommand; } } // namespace PushAvStreamTransport @@ -1119,13 +164,7 @@ Status PushAvStreamTransportServer::GeneratePushTransportEndEvent(const uint16_t } // namespace app } // namespace chip -/** @brief Push AV Stream Transport Cluster Server Init - * - * Server Init - * - */ -void MatterPushAvStreamTransportPluginServerInitCallback() {} -void MatterPushAvStreamTransportClusterServerShutdownCallback(EndpointId endpoint) +void MatterPushAvStreamTransportClusterServerShutdownCallback(chip::EndpointId endpoint) { ChipLogProgress(Zcl, "Shuting Push AV Stream Transport server cluster on endpoint %d", endpoint); } diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h index e5ae0e79594705..2274f73d255162 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h @@ -18,766 +18,59 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include +#include namespace chip { namespace app { namespace Clusters { namespace PushAvStreamTransport { -static constexpr size_t kMinUrlLength = 13u; -static constexpr size_t kMaxUrlLength = 2000u; -static constexpr size_t kMaxCENCKeyLength = 16u; -static constexpr size_t kMaxCENCKeyIDLength = 16u; - -using SupportedFormatStruct = Structs::SupportedFormatStruct::Type; -using CMAFContainerOptionsStruct = Structs::CMAFContainerOptionsStruct::Type; -using ContainerOptionsStruct = Structs::ContainerOptionsStruct::Type; -using TransportZoneOptionsStruct = Structs::TransportZoneOptionsStruct::Type; -using TransportTriggerOptionsStruct = Structs::TransportTriggerOptionsStruct::Type; -using TransportMotionTriggerTimeControlStruct = Structs::TransportMotionTriggerTimeControlStruct::Type; -using TransportOptionsStruct = Structs::TransportOptionsStruct::Type; -using TransportConfigurationStruct = Structs::TransportConfigurationStruct::Type; -using StreamUsageEnum = chip::app::Clusters::Globals::StreamUsageEnum; - -enum class PushAvStreamTransportStatusEnum : uint8_t -{ - kBusy = 0x00, - kIdle = 0x01, - kUnknown = 0x02 -}; - -struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct -{ - TransportTriggerOptionsStorage(){}; - - TransportTriggerOptionsStorage(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) - { - *this = aTransportTriggerOptionsStorage; - } - - TransportTriggerOptionsStorage & operator=(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) - { - triggerType = aTransportTriggerOptionsStorage.triggerType; - - mTransportZoneOptions = aTransportTriggerOptionsStorage.mTransportZoneOptions; - - motionZones = aTransportTriggerOptionsStorage.motionZones; - - // Rebind motionZones to point to the copied vector if it was set - if (motionZones.HasValue() == true && motionZones.Value().IsNull() == false) - { - motionZones.SetValue(DataModel::MakeNullable( - DataModel::List(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); - } - - motionSensitivity = aTransportTriggerOptionsStorage.motionSensitivity; - motionTimeControl = aTransportTriggerOptionsStorage.motionTimeControl; - maxPreRollLen = aTransportTriggerOptionsStorage.maxPreRollLen; - - return *this; - } - - TransportTriggerOptionsStorage & operator=(const Structs::TransportTriggerOptionsStruct::DecodableType aTransportTriggerOptions) - { - triggerType = aTransportTriggerOptions.triggerType; - - auto & motionZonesList = aTransportTriggerOptions.motionZones; - - if (triggerType == TransportTriggerTypeEnum::kMotion && motionZonesList.HasValue()) - { - if (motionZonesList.Value().IsNull() == false) - { - auto iter = motionZonesList.Value().Value().begin(); - - while (iter.Next()) - { - auto & transportZoneOption = iter.GetValue(); - mTransportZoneOptions.push_back(transportZoneOption); - } - - motionZones.SetValue(DataModel::MakeNullable( - DataModel::List(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); - } - else - { - motionZones.Value().SetNull(); - } - } - else - { - motionZones.ClearValue(); - } - - motionSensitivity = aTransportTriggerOptions.motionSensitivity; - motionTimeControl = aTransportTriggerOptions.motionTimeControl; - maxPreRollLen = aTransportTriggerOptions.maxPreRollLen; - - return *this; - } - - TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType triggerOptions) - { - triggerType = triggerOptions.triggerType; - - auto & motionZonesList = triggerOptions.motionZones; - - if (triggerType == TransportTriggerTypeEnum::kMotion && motionZonesList.HasValue()) - { - if (motionZonesList.Value().IsNull() == false) - { - auto iter = motionZonesList.Value().Value().begin(); - - while (iter.Next()) - { - auto & transportZoneOption = iter.GetValue(); - mTransportZoneOptions.push_back(transportZoneOption); - } - - motionZones.SetValue(DataModel::MakeNullable( - DataModel::List(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); - } - else - { - motionZones.Value().SetNull(); - } - } - else - { - motionZones.ClearValue(); - } - - motionSensitivity = triggerOptions.motionSensitivity; - motionTimeControl = triggerOptions.motionTimeControl; - maxPreRollLen = triggerOptions.maxPreRollLen; - } - -private: - std::vector mTransportZoneOptions; -}; - -struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct -{ - CMAFContainerOptionsStorage(){}; - - CMAFContainerOptionsStorage(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) - { - *this = aCMAFContainerOptionsStorage; - } - - CMAFContainerOptionsStorage & operator=(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) - { - chunkDuration = aCMAFContainerOptionsStorage.chunkDuration; - - std::memcpy(mCENCKeyBuffer, aCMAFContainerOptionsStorage.mCENCKeyBuffer, sizeof(mCENCKeyBuffer)); - - std::memcpy(mCENCKeyIDBuffer, aCMAFContainerOptionsStorage.mCENCKeyIDBuffer, sizeof(mCENCKeyIDBuffer)); - - CENCKey = aCMAFContainerOptionsStorage.CENCKey; - - CENCKeyID = aCMAFContainerOptionsStorage.CENCKeyID; - - if (CENCKey.HasValue()) - { - CENCKey.SetValue(ByteSpan(mCENCKeyBuffer, aCMAFContainerOptionsStorage.CENCKey.Value().size())); - } - - metadataEnabled = aCMAFContainerOptionsStorage.metadataEnabled; - - if (CENCKeyID.HasValue()) - { - CENCKeyID.SetValue(ByteSpan(mCENCKeyIDBuffer, aCMAFContainerOptionsStorage.CENCKeyID.Value().size())); - } - - return *this; - } - - CMAFContainerOptionsStorage & operator=(const Structs::CMAFContainerOptionsStruct::Type aCMAFContainerOptions) - { - chunkDuration = aCMAFContainerOptions.chunkDuration; - - CENCKey = aCMAFContainerOptions.CENCKey; - - CENCKeyID = aCMAFContainerOptions.CENCKeyID; - - if (CENCKey.HasValue()) - { - MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); - CopySpanToMutableSpan(aCMAFContainerOptions.CENCKey.Value(), CENCKeyBuffer); - CENCKey.SetValue(CENCKeyBuffer); - } - - metadataEnabled = aCMAFContainerOptions.metadataEnabled; - - if (CENCKeyID.HasValue()) - { - MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); - CopySpanToMutableSpan(aCMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); - CENCKeyID.SetValue(CENCKeyIDBuffer); - } - - return *this; - } - - CMAFContainerOptionsStorage(Structs::CMAFContainerOptionsStruct::Type CMAFContainerOptions) - { - - chunkDuration = CMAFContainerOptions.chunkDuration; - - if (CMAFContainerOptions.CENCKey.HasValue()) - { - MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); - CopySpanToMutableSpan(CMAFContainerOptions.CENCKey.Value(), CENCKeyBuffer); - CENCKey.SetValue(CENCKeyBuffer); - } - else - { - CENCKey.ClearValue(); - } - - metadataEnabled = CMAFContainerOptions.metadataEnabled; - - if (CMAFContainerOptions.CENCKey.HasValue()) - { - MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); - CopySpanToMutableSpan(CMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); - CENCKeyID.SetValue(CENCKeyIDBuffer); - } - else - { - CENCKeyID.ClearValue(); - } - } - -private: - uint8_t mCENCKeyBuffer[kMaxCENCKeyLength]; - uint8_t mCENCKeyIDBuffer[kMaxCENCKeyIDLength]; -}; - -struct ContainerOptionsStorage : public ContainerOptionsStruct -{ - ContainerOptionsStorage(){}; - - ContainerOptionsStorage(const ContainerOptionsStorage & aContainerOptionsStorage) { *this = aContainerOptionsStorage; } - - ContainerOptionsStorage & operator=(const ContainerOptionsStorage & aContainerOptionsStorage) - { - containerType = aContainerOptionsStorage.containerType; - CMAFContainerOptions = aContainerOptionsStorage.CMAFContainerOptions; - - if (containerType == ContainerFormatEnum::kCmaf) - { - mCMAFContainerStorage = aContainerOptionsStorage.mCMAFContainerStorage; - CMAFContainerOptions.SetValue(mCMAFContainerStorage); - } - - return *this; - } - - ContainerOptionsStorage & operator=(const Structs::ContainerOptionsStruct::DecodableType aContainerOptions) - { - containerType = aContainerOptions.containerType; - - if (containerType == ContainerFormatEnum::kCmaf) - { - mCMAFContainerStorage = aContainerOptions.CMAFContainerOptions.Value(); - CMAFContainerOptions.SetValue(mCMAFContainerStorage); - } - else - { - CMAFContainerOptions.ClearValue(); - } - - return *this; - } - - ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType containerOptions) - { - containerType = containerOptions.containerType; - - if (containerType == ContainerFormatEnum::kCmaf) - { - mCMAFContainerStorage = containerOptions.CMAFContainerOptions.Value(); - CMAFContainerOptions.SetValue(mCMAFContainerStorage); - } - else - { - CMAFContainerOptions.ClearValue(); - } - } - -private: - CMAFContainerOptionsStorage mCMAFContainerStorage; -}; - -struct TransportOptionsStorage : public TransportOptionsStruct -{ - TransportOptionsStorage(){}; - - TransportOptionsStorage(const TransportOptionsStorage & aTransportOptionsStorage) { *this = aTransportOptionsStorage; } - - TransportOptionsStorage & operator=(const TransportOptionsStorage & aTransportOptionsStorage) - { - streamUsage = aTransportOptionsStorage.streamUsage; - videoStreamID = aTransportOptionsStorage.videoStreamID; - audioStreamID = aTransportOptionsStorage.audioStreamID; - endpointID = aTransportOptionsStorage.endpointID; - - // Deep copy the URL buffer - std::memcpy(mUrlBuffer, aTransportOptionsStorage.mUrlBuffer, kMaxUrlLength); - url = MutableCharSpan(mUrlBuffer, aTransportOptionsStorage.url.size()); - - // Copy internal storage objects - mTriggerOptionsStorage = aTransportOptionsStorage.mTriggerOptionsStorage; - triggerOptions = mTriggerOptionsStorage; - - ingestMethod = aTransportOptionsStorage.ingestMethod; - - mContainerOptionsStorage = aTransportOptionsStorage.mContainerOptionsStorage; - containerOptions = mContainerOptionsStorage; - - expiryTime = aTransportOptionsStorage.expiryTime; - - return *this; - } - - TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions) - { - streamUsage = transportOptions.streamUsage; - videoStreamID = transportOptions.videoStreamID; - audioStreamID = transportOptions.audioStreamID; - endpointID = transportOptions.endpointID; - - MutableCharSpan urlBuffer(mUrlBuffer); - CopyCharSpanToMutableCharSpanWithTruncation(transportOptions.url, urlBuffer); - url = urlBuffer; - - mTriggerOptionsStorage = transportOptions.triggerOptions; - triggerOptions = mTriggerOptionsStorage; - - ingestMethod = transportOptions.ingestMethod; - - mContainerOptionsStorage = transportOptions.containerOptions; - containerOptions = mContainerOptionsStorage; - - expiryTime = transportOptions.expiryTime; - } - -private: - char mUrlBuffer[kMaxUrlLength]; - TransportTriggerOptionsStorage mTriggerOptionsStorage; - ContainerOptionsStorage mContainerOptionsStorage; -}; - -struct TransportConfigurationStorage : public TransportConfigurationStruct -{ - TransportConfigurationStorage() {} - - TransportConfigurationStorage(const TransportConfigurationStorage & aTransportConfigurationStorage) - { - *this = aTransportConfigurationStorage; - } - - TransportConfigurationStorage & operator=(const TransportConfigurationStorage & aTransportConfigurationStorage) - { - connectionID = aTransportConfigurationStorage.connectionID; - transportStatus = aTransportConfigurationStorage.transportStatus; - fabricIndex = aTransportConfigurationStorage.fabricIndex; - - mTransportOptionsPtr = aTransportConfigurationStorage.mTransportOptionsPtr; - - if (mTransportOptionsPtr) - { - transportOptions.SetValue(*mTransportOptionsPtr); - } - else - { - transportOptions.ClearValue(); - } - - return *this; - } - - TransportConfigurationStorage(const uint16_t aConnectionID, std::shared_ptr aTransportOptionsPtr) - { - connectionID = aConnectionID; - transportStatus = TransportStatusEnum::kInactive; - /*Store the pointer to keep buffer alive*/ - mTransportOptionsPtr = aTransportOptionsPtr; - /*Convert Storage type to base type*/ - if (mTransportOptionsPtr) - { - transportOptions.SetValue(*mTransportOptionsPtr); - } - else - { - transportOptions.ClearValue(); - } - } - std::shared_ptr GetTransportOptionsPtr() const { return mTransportOptionsPtr; } - void SetTransportOptionsPtr(std::shared_ptr aTransportOptionsPtr) - { - mTransportOptionsPtr = aTransportOptionsPtr; - if (mTransportOptionsPtr) - { - transportOptions.SetValue(*mTransportOptionsPtr); - } - else - { - transportOptions.ClearValue(); - } - } - -private: - std::shared_ptr mTransportOptionsPtr; -}; - -/** @brief - * Defines interfaces for implementing application-specific logic for various aspects of the PushAvStreamTransport Delegate. - * Specifically, it defines interfaces for the command handling and loading of the allocated streams. - */ -class PushAvStreamTransportDelegate +/// Integration of Push AV Stream Transport logic within the matter data model +/// +/// Translates between matter calls and Push AV Stream Transport logic +class PushAvStreamTransportServer : public DefaultServerCluster { public: - PushAvStreamTransportDelegate() = default; - - virtual ~PushAvStreamTransportDelegate() = default; - - void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; } - - /** - * @brief Handle Command Delegate for stream transport allocation with the provided transport configuration option. - * - * @param transportOptions[in] represent the configuration options of the transport to be allocated - * - * @param connectionID[in] Indicates the connectionID to allocate. - * - * @return Success if the allocation is successful and a PushTransportConnectionID was produced; otherwise, the command SHALL - * be rejected with Failure. - * - * The delegate is expected to process the transport following transport options: URL : Validate the URL - * StreamUsage,VideoStreamID,AudioStreamID for selection of Stream. - * - * Allocate the transport and map it to the connectionID. - * - * On Success TransportConfigurationStruct is sent as response by the server. - */ - virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, - const uint16_t connectionID) = 0; - /** - * @brief Handle Command Delegate for Stream transport deallocation for the - * provided connectionID. - * - * @param connectionID[in] Indicates the connectionID to deallocate. - * - * @return Success if the transport deallocation is successful; otherwise, the delegate is expected to return status code BUSY - * if the transport is currently uploading. - * - */ - virtual Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID) = 0; - /** - * @brief Handle Command Delegate for Stream transport modification. - * - * @param connectionID [in] Indicates the connectionID of the stream transport to modify. - * - * @param transportOptions [out] represents the Transport Options to modify. - * - * The buffers storing URL, Trigger Options, Motion Zones, Container Options is owned by the PushAVStreamTransport Server. - * - * The allocated buffers are cleared and reassigned to modified transportOptions on success of ModifyPushTransport and - * deallocated on success of DeallocatePushTransport. - * - * @return Success if the stream transport modification is successful; otherwise, the command SHALL be rejected with Failure. - */ - virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, - const TransportOptionsStorage transportOptions) = 0; - - /** - * @brief Handle Command Delegate for Stream transport modification. - * - * @param connectionIDList [in] represent the list of connectionIDs for which new transport status to apply. - * @param transportStatus [in] represents the updated status of the connection(s). - * - * If the transportStatus field is set to Inactive(1).Disable transmissions on the transport.Remove any queued items for - * upload.Cancel any uploads currently in progress. - * - * Emit GeneratePushTransportEndEvent on stop. - * - * Else If the TransportStatus field is set to Active(0).Enable transmissions on the transport. - * If the transports trigger type is Continuous,begin Transmission immediately. - * - * Else If the transports trigger type is Command or Motion, and the underlying trigger is currently active and still within - * the Time Control Bounds,begin Transmission immediately. - * - * Emit GeneratePushTransportBeginEvent on start. - * - * @return Success if the stream transport status is successfully set; otherwise, the command SHALL be rejected with Failure. - */ - virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, - TransportStatusEnum transportStatus) = 0; - /** - * @brief Handle Command Delegate to request the Node to manually start the specified push transport. - * - * @param connectionID [in] Indicates the connectionID of the stream transport to set trigger for. - * - * @param activationReason [in] Provide information as to why the transport was started or stopped. - * - * @param timeControl [in] Configuration to control the life cycle of a triggered transport. - * - * The delegate is expected to begin transmission using the timeControl values. - * - * Emitting of PushTransportBegin event is handled by the server when delegate returns success. - * - * The delegate should emit PushTransportEnd Event using GeneratePushTransportEndEvent() API when timeControl values indicates - * end of transmission. - * - * @return Success if the stream transport trigger is successful; otherwise, the command SHALL be rejected with Failure. - */ - virtual Protocols::InteractionModel::Status - ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, - const Optional & timeControl) = 0; - - /** - * @brief Validates the url - * @param[in] url The url to validate - * - * @return boolean value true if the url is valid else false. - */ - virtual bool ValidateUrl(std::string url) = 0; - - /** - * @brief Validates the bandwidth requirement against the camera's resource management - * - * The implementation SHALL ensure: - * - The requested stream usage (streamUsage) is allowed given the current allocation of - * camera resources (e.g. CPU, memory, network bandwidth). - * - * @param[in] streamUsage The desired usage type for the stream (e.g. live view, recording, etc.). - * @param[in] videoStreamId Optional identifier for the requested video stream. - * @param[in] audioStreamId Optional identifier for the requested audio stream. - * - * @return Status::Success if the stream usage is valid; and Status::ResourceExhausted otherwise. - */ - - virtual Protocols::InteractionModel::Status - ValidateBandwidthLimit(StreamUsageEnum streamUsage, const Optional> & videoStreamId, - const Optional> & audioStreamId) = 0; - - /** - * @brief Assign an existing Video Stream as per the camera's resource management and stream priority policies using requested - * stream usage. - * - * @param[in] streamUsage The desired usage type for the stream (e.g. live view, recording, etc.). - * @param[in] videoStreamId Identifier for the requested video stream. - * - * @return Assign the selected videoStreamID and return Status::Success. Return Status::InvalidStream if there are no allocated - * VideoStream. - */ - virtual Protocols::InteractionModel::Status SelectVideoStream(StreamUsageEnum streamUsage, uint16_t & videoStreamId) = 0; - /** - * @brief Assign an existing Audio Stream as per the camera's resource management and stream priority policies using requested - * stream usage. - * - * @param[in] streamUsage The desired usage type for the stream (e.g. live view, recording, etc.). - * @param[in] audioStreamId Identifier for the requested audio stream. - * - * @return Assign the selected audioStreamID and return Status::Success. Return Status::InvalidStream if there are no allocated - * audioStream. - */ - - virtual Protocols::InteractionModel::Status SelectAudioStream(StreamUsageEnum streamUsage, uint16_t & audioStreamId) = 0; - /** - * @brief Validate that the videoStream corresponding to the videoStreamID is allocated. - * - * @param[in] videoStreamId Identifier for the requested video stream. - * - * @return Status::Success if there is an allocated video stream. Return Status::InvalidStream if there are no allocated - * video stream with videoStreamID. - */ - - virtual Protocols::InteractionModel::Status ValidateVideoStream(uint16_t videoStreamId) = 0; - - /** - * @brief Validate that the audioStream corresponding to the audioStreamID is allocated. - * - * @param[in] audioStreamId Identifier for the requested audio stream. - * - * @return Status::Success if there is an allocated audio stream. Return Status::InvalidStream if there are no allocated - * audio stream with audioStreamID. - */ - - virtual Protocols::InteractionModel::Status ValidateAudioStream(uint16_t audioStreamId) = 0; - /** - * @brief Gets the status of the transport + * @brief Creates a Push AV Stream Transport server instance * - * @param[in] connectionID Indicates the connectionID of the stream transport to check status + * This instance needs to be initialized by calling Init() before it can be registered + * and used by the interaction model. * - * @return busy if transport is uploading else idle - */ - virtual PushAvStreamTransportStatusEnum GetTransportBusyStatus(const uint16_t connectionID) = 0; - - /** - * @brief Delegate callback for notifying change in an attribute. + * @param aEndpointId The endpoint on which this cluster exists (must match zap configuration) + * @param aFeatures Bitflags indicating which features are supported by this instance * + * @note The caller must ensure the delegate lives throughout the instance's lifetime */ - virtual void OnAttributeChanged(AttributeId attributeId) = 0; + PushAvStreamTransportServer(EndpointId aEndpointId, BitFlags aFeatures) : + DefaultServerCluster({ aEndpointId, PushAvStreamTransport::Id }), mLogic(aEndpointId, aFeatures) + {} - /** - * @brief - * Delegate functions to load the allocated transport connections. - * The delegate application is responsible for creating and persisting these connections ( based on the Allocation commands ). - * These Load APIs would be used to load the pre-allocated transport connections context information into the cluster server - * list, at initialization. Once loaded, the cluster server would be serving Reads on these attributes. The list is updatable - * via the Add/Remove functions for the respective transport connections. - * - * @note - * - * The required buffers are managed by TransportConfigurationStorage, the delegate function is expected to populate the vector - * correctly. - */ - virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; + PushAvStreamTransportServerLogic & GetLogic() { return mLogic; } - /** - * @brief Callback into the delegate once persistent attributes managed by - * the Cluster have been loaded from Storage. - */ - virtual CHIP_ERROR PersistentAttributesLoadedCallback() = 0; + void SetDelegate(EndpointId aEndpoint, PushAvStreamTransportDelegate * delegate) { mLogic.SetDelegate(aEndpoint, delegate); } -protected: - EndpointId mEndpointId = 0; -}; + CHIP_ERROR Init() { return mLogic.Init(); } -class PushAvStreamTransportServer : public AttributeAccessInterface, public CommandHandlerInterface -{ -public: - /** - * Creates a Push AV Stream Transport server instance. The Init() function needs to be called for this instance to be registered - * and called by the interaction model at the appropriate times. - * @param aDelegate A reference to the delegate to be used by this server. - * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. - * @param aFeatures The bitflags value that identifies which features are supported by this instance. - * Note: the caller must ensure that the delegate lives throughout the instance's lifetime. - */ - PushAvStreamTransportServer(PushAvStreamTransportDelegate & delegate, EndpointId endpointId, const BitFlags aFeatures); + void Deinit() { mLogic.Shutdown(); } - ~PushAvStreamTransportServer() override; - - /** - * @brief Initialise the Push AV Stream Transport server instance. - * This function must be called after defining an PushAvStreamTransportServer class object. - * @return Returns an error if the given endpoint and cluster ID have not been enabled in zap or if the - * CommandHandler or AttributeHandler registration fails, else returns CHIP_NO_ERROR. - * This method also checks if the feature setting is valid, if invalid it will return CHIP_ERROR_INVALID_ARGUMENT. - */ - CHIP_ERROR Init(); - - /** - * @brief - * Unregisters the command handler and attribute interface, releasing resources. - */ - void Shutdown(); + DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request, + AttributeValueEncoder & encoder) override; + CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path, + ReadOnlyBufferBuilder & builder) override; + CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder & builder) override; - bool HasFeature(Feature feature) const; + std::optional InvokeCommand(const DataModel::InvokeRequest & request, + chip::TLV::TLVReader & input_arguments, + CommandHandler * handler) override; - // Send Push AV Stream Transport events - Protocols::InteractionModel::Status - GeneratePushTransportBeginEvent(const uint16_t connectionID, const TransportTriggerTypeEnum triggerType, - const Optional activationReason); - Protocols::InteractionModel::Status GeneratePushTransportEndEvent(const uint16_t connectionID, - const TransportTriggerTypeEnum triggerType, - const Optional activationReason); + CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder & builder) override; private: - enum class UpsertResultEnum : uint8_t - { - kInserted = 0x00, - kUpdated = 0x01, - }; - - struct PushAVStreamTransportDeallocateCallbackContext - { - PushAvStreamTransportServer * instance; - uint16_t connectionID; - }; - - std::vector> mtimerContexts; - - PushAvStreamTransportDelegate & mDelegate; - - const BitFlags mFeatures; - - // Attributes - std::vector mSupportedFormats; - /*Moved from TransportConfigurationStruct to TransportConfigurationStructWithFabricIndex - * to perform fabric index checks - */ - std::vector mCurrentConnections; - - /** - * IM-level implementation of read - * @return appropriately mapped CHIP_ERROR if applicable - */ - CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; - - /** - * Helper function that loads all the persistent attributes from the KVS. - */ - void LoadPersistentAttributes(); - - // Helpers to read list items via delegate APIs - CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, FabricIndex fabricIndex); - CHIP_ERROR ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder); - - // Helper functions - uint16_t GenerateConnectionID(); - - TransportConfigurationStorage * FindStreamTransportConnection(const uint16_t connectionID); - - TransportConfigurationStorage * FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, FabricIndex fabricIndex); - - // Add/Remove Management functions for transport - UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStorage & transportConfiguration); - - void RemoveStreamTransportConnection(const uint16_t connectionID); - - Protocols::InteractionModel::Status - ValidateIncomingTransportOptions(const Structs::TransportOptionsStruct::DecodableType & transportOptions); - - static void PushAVStreamTransportDeallocateCallback(chip::System::Layer *, void * callbackContext); - - UpsertResultEnum UpsertTimerAppState(std::shared_ptr timerAppState); - - void RemoveTimerAppState(const uint16_t connectionID); - - /** - * @brief Schedule deallocate with a given timeout - * - * @param endpointId endpoint where DoorLockServer is running - * @param timeoutSec timeout in seconds - */ - void ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec); - - /** - * @brief Inherited from CommandHandlerInterface - */ - void InvokeCommand(HandlerContext & ctx) override; - - void HandleAllocatePushTransport(HandlerContext & ctx, const Commands::AllocatePushTransport::DecodableType & req); - - void HandleDeallocatePushTransport(HandlerContext & ctx, const Commands::DeallocatePushTransport::DecodableType & req); - - void HandleModifyPushTransport(HandlerContext & ctx, const Commands::ModifyPushTransport::DecodableType & req); - - void HandleSetTransportStatus(HandlerContext & ctx, const Commands::SetTransportStatus::DecodableType & req); - - void HandleManuallyTriggerTransport(HandlerContext & ctx, const Commands::ManuallyTriggerTransport::DecodableType & req); - - void HandleFindTransport(HandlerContext & ctx, const Commands::FindTransport::DecodableType & req); + PushAvStreamTransportServerLogic mLogic; }; } // namespace PushAvStreamTransport diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h new file mode 100644 index 00000000000000..6896d424d41ba4 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h @@ -0,0 +1,417 @@ +/* + * + * 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 +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +/** + * @brief Storage implementation for transport trigger options. + * Provides deep copy functionality and internal storage for motion zones. + * Must be used when trigger options need to persist beyond the original data scope. + */ +struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct +{ + TransportTriggerOptionsStorage() {} + + TransportTriggerOptionsStorage(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) + { + *this = aTransportTriggerOptionsStorage; + } + + TransportTriggerOptionsStorage & operator=(const TransportTriggerOptionsStorage & aTransportTriggerOptionsStorage) + { + if (this == &aTransportTriggerOptionsStorage) + { + return *this; + } + + triggerType = aTransportTriggerOptionsStorage.triggerType; + + mTransportZoneOptions = aTransportTriggerOptionsStorage.mTransportZoneOptions; + + motionZones = aTransportTriggerOptionsStorage.motionZones; + + // Rebind motionZones to point to the copied vector if it was set + if (motionZones.HasValue() && !motionZones.Value().IsNull()) + { + motionZones.SetValue(DataModel::MakeNullable( + DataModel::List(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); + } + + motionSensitivity = aTransportTriggerOptionsStorage.motionSensitivity; + motionTimeControl = aTransportTriggerOptionsStorage.motionTimeControl; + maxPreRollLen = aTransportTriggerOptionsStorage.maxPreRollLen; + + return *this; + } + + TransportTriggerOptionsStorage & + operator=(const Structs::TransportTriggerOptionsStruct::DecodableType & aTransportTriggerOptions) + { + + triggerType = aTransportTriggerOptions.triggerType; + + auto & motionZonesList = aTransportTriggerOptions.motionZones; + + if (triggerType == TransportTriggerTypeEnum::kMotion && motionZonesList.HasValue()) + { + if (!motionZonesList.Value().IsNull()) + { + auto iter = motionZonesList.Value().Value().begin(); + + while (iter.Next()) + { + auto & transportZoneOption = iter.GetValue(); + mTransportZoneOptions.push_back(transportZoneOption); + } + // GetStatus() check is not needed here, because the motionZonesList is already checked in the + // ValidateIncomingTransportOptions() function + + motionZones.SetValue(DataModel::MakeNullable( + DataModel::List(mTransportZoneOptions.data(), mTransportZoneOptions.size()))); + } + else + { + motionZones.Value().SetNull(); + } + } + else + { + motionZones.ClearValue(); + } + + motionSensitivity = aTransportTriggerOptions.motionSensitivity; + motionTimeControl = aTransportTriggerOptions.motionTimeControl; + maxPreRollLen = aTransportTriggerOptions.maxPreRollLen; + + return *this; + } + + TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType & triggerOptions) + { + *this = triggerOptions; + } + +private: + std::vector mTransportZoneOptions; +}; + +/** + * @brief Storage implementation for CMAF container options. + * Manages fixed-size buffers for CENC keys and IDs with bounds checking. + * Must be used when CMAF container configurations need persistent key storage. + */ +struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct +{ + CMAFContainerOptionsStorage() {} + + CMAFContainerOptionsStorage(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) + { + *this = aCMAFContainerOptionsStorage; + } + + CMAFContainerOptionsStorage & operator=(const CMAFContainerOptionsStorage & aCMAFContainerOptionsStorage) + { + if (this == &aCMAFContainerOptionsStorage) + { + return *this; + } + + *this = static_cast(aCMAFContainerOptionsStorage); + + return *this; + } + + CMAFContainerOptionsStorage & operator=(const Structs::CMAFContainerOptionsStruct::Type & aCMAFContainerOptions) + { + chunkDuration = aCMAFContainerOptions.chunkDuration; + + CENCKey = aCMAFContainerOptions.CENCKey; + + CENCKeyID = aCMAFContainerOptions.CENCKeyID; + + if (CENCKey.HasValue()) + { + MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); + // ValidateIncomingTransportOptions() function already checked the CENCKey length + CopySpanToMutableSpan(aCMAFContainerOptions.CENCKey.Value(), CENCKeyBuffer); + CENCKey.SetValue(CENCKeyBuffer); + } + + metadataEnabled = aCMAFContainerOptions.metadataEnabled; + + if (CENCKeyID.HasValue()) + { + MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); + // ValidateIncomingTransportOptions() function already checked the CENCKeyID length + CopySpanToMutableSpan(aCMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); + CENCKeyID.SetValue(CENCKeyIDBuffer); + } + + return *this; + } + + CMAFContainerOptionsStorage(Structs::CMAFContainerOptionsStruct::Type & CMAFContainerOptions) + { + + chunkDuration = CMAFContainerOptions.chunkDuration; + + if (CMAFContainerOptions.CENCKey.HasValue()) + { + MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); + // ValidateIncomingTransportOptions() function already checked the CENCKeyID length + CopySpanToMutableSpan(CMAFContainerOptions.CENCKey.Value(), CENCKeyBuffer); + CENCKey.SetValue(CENCKeyBuffer); + } + else + { + CENCKey.ClearValue(); + } + + metadataEnabled = CMAFContainerOptions.metadataEnabled; + + if (CMAFContainerOptions.CENCKey.HasValue()) + { + MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); + // ValidateIncomingTransportOptions() function already checked the CENCKeyID length + CopySpanToMutableSpan(CMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); + CENCKeyID.SetValue(CENCKeyIDBuffer); + } + else + { + CENCKeyID.ClearValue(); + } + } + +private: + uint8_t mCENCKeyBuffer[kMaxCENCKeyLength]; + uint8_t mCENCKeyIDBuffer[kMaxCENCKeyIDLength]; +}; + +/** + * @brief Storage implementation for container format options. + * Manages different container formats with type-specific storage handling. + * Currently supports CMAF containers. + */ +struct ContainerOptionsStorage : public ContainerOptionsStruct +{ + ContainerOptionsStorage() {} + + ContainerOptionsStorage(const ContainerOptionsStorage & aContainerOptionsStorage) { *this = aContainerOptionsStorage; } + + ContainerOptionsStorage & operator=(const ContainerOptionsStorage & aContainerOptionsStorage) + { + if (this == &aContainerOptionsStorage) + { + return *this; + } + + *this = static_cast(aContainerOptionsStorage); + + return *this; + } + + ContainerOptionsStorage & operator=(const Structs::ContainerOptionsStruct::DecodableType & aContainerOptions) + { + containerType = aContainerOptions.containerType; + + if (containerType == ContainerFormatEnum::kCmaf) + { + mCMAFContainerStorage = aContainerOptions.CMAFContainerOptions.Value(); + CMAFContainerOptions.SetValue(mCMAFContainerStorage); + } + else + { + CMAFContainerOptions.ClearValue(); + } + + return *this; + } + + ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType & containerOptions) + { + containerType = containerOptions.containerType; + + if (containerType == ContainerFormatEnum::kCmaf) + { + mCMAFContainerStorage = containerOptions.CMAFContainerOptions.Value(); + CMAFContainerOptions.SetValue(mCMAFContainerStorage); + } + else + { + CMAFContainerOptions.ClearValue(); + } + } + +private: + CMAFContainerOptionsStorage mCMAFContainerStorage; +}; + +/** + * @brief Storage implementation for transport options. + * Manages URL storage, trigger options, container options, and stream configurations. + * Must be used when transport configurations need to persist beyond request scope. + */ +struct TransportOptionsStorage : public TransportOptionsStruct +{ + TransportOptionsStorage() {} + + TransportOptionsStorage(const TransportOptionsStorage & aTransportOptionsStorage) { *this = aTransportOptionsStorage; } + + TransportOptionsStorage & operator=(const TransportOptionsStorage & aTransportOptionsStorage) + { + if (this == &aTransportOptionsStorage) + { + return *this; + } + streamUsage = aTransportOptionsStorage.streamUsage; + videoStreamID = aTransportOptionsStorage.videoStreamID; + audioStreamID = aTransportOptionsStorage.audioStreamID; + endpointID = aTransportOptionsStorage.endpointID; + + // Deep copy the URL buffer + std::memcpy(mUrlBuffer, aTransportOptionsStorage.mUrlBuffer, kMaxUrlLength); + url = MutableCharSpan(mUrlBuffer, aTransportOptionsStorage.url.size()); + + // Copy internal storage objects + mTriggerOptionsStorage = aTransportOptionsStorage.mTriggerOptionsStorage; + triggerOptions = mTriggerOptionsStorage; + + ingestMethod = aTransportOptionsStorage.ingestMethod; + + mContainerOptionsStorage = aTransportOptionsStorage.mContainerOptionsStorage; + containerOptions = mContainerOptionsStorage; + + expiryTime = aTransportOptionsStorage.expiryTime; + + return *this; + } + + TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions) + { + streamUsage = transportOptions.streamUsage; + videoStreamID = transportOptions.videoStreamID; + audioStreamID = transportOptions.audioStreamID; + endpointID = transportOptions.endpointID; + + MutableCharSpan urlBuffer(mUrlBuffer); + // ValidateIncomingTransportOptions() function already checked the url length + CopyCharSpanToMutableCharSpanWithTruncation(transportOptions.url, urlBuffer); + url = urlBuffer; + + mTriggerOptionsStorage = transportOptions.triggerOptions; + triggerOptions = mTriggerOptionsStorage; + + ingestMethod = transportOptions.ingestMethod; + + mContainerOptionsStorage = transportOptions.containerOptions; + containerOptions = mContainerOptionsStorage; + + expiryTime = transportOptions.expiryTime; + } + +private: + char mUrlBuffer[kMaxUrlLength]; + TransportTriggerOptionsStorage mTriggerOptionsStorage; + ContainerOptionsStorage mContainerOptionsStorage; +}; + +/** + * @brief Storage implementation for transport configurations. + * Manages connection ID, status, fabric index and transport options. + * Uses shared_ptr for transport options to ensure proper lifetime management. + */ +struct TransportConfigurationStorage : public TransportConfigurationStruct +{ + TransportConfigurationStorage() {} + + TransportConfigurationStorage(const TransportConfigurationStorage & aTransportConfigurationStorage) + { + *this = aTransportConfigurationStorage; + } + + TransportConfigurationStorage & operator=(const TransportConfigurationStorage & aTransportConfigurationStorage) + { + if (this == &aTransportConfigurationStorage) + { + return *this; + } + connectionID = aTransportConfigurationStorage.connectionID; + transportStatus = aTransportConfigurationStorage.transportStatus; + fabricIndex = aTransportConfigurationStorage.fabricIndex; + + mTransportOptionsPtr = aTransportConfigurationStorage.mTransportOptionsPtr; + + if (mTransportOptionsPtr) + { + transportOptions.SetValue(*mTransportOptionsPtr); + } + else + { + transportOptions.ClearValue(); + } + + return *this; + } + + TransportConfigurationStorage(const uint16_t aConnectionID, std::shared_ptr aTransportOptionsPtr) + { + connectionID = aConnectionID; + transportStatus = TransportStatusEnum::kInactive; + /*Store the pointer to keep buffer alive*/ + mTransportOptionsPtr = aTransportOptionsPtr; + /*Convert Storage type to base type*/ + if (mTransportOptionsPtr) + { + transportOptions.SetValue(*mTransportOptionsPtr); + } + else + { + transportOptions.ClearValue(); + } + } + std::shared_ptr GetTransportOptionsPtr() const { return mTransportOptionsPtr; } + void SetTransportOptionsPtr(std::shared_ptr aTransportOptionsPtr) + { + mTransportOptionsPtr = aTransportOptionsPtr; + if (mTransportOptionsPtr) + { + transportOptions.SetValue(*mTransportOptionsPtr); + } + else + { + transportOptions.ClearValue(); + } + } + +private: + std::shared_ptr mTransportOptionsPtr; +}; + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip From fe0a198b695d22c35d4e11450cbc7ed7ca910f40 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 26 Jun 2025 11:34:23 +0000 Subject: [PATCH 33/40] Restyled by clang-format --- examples/all-clusters-app/linux/main-common.cpp | 4 ++-- .../CodegenIntegration.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp index 5c311def8b2067..c6deb57692874a 100644 --- a/examples/all-clusters-app/linux/main-common.cpp +++ b/examples/all-clusters-app/linux/main-common.cpp @@ -35,13 +35,13 @@ #include "operational-state-delegate-impl.h" #include "oven-modes.h" #include "oven-operational-state-delegate.h" +#include "push-av-stream-transport-delegate-impl.h" #include "resource-monitoring-delegates.h" #include "rvc-modes.h" #include "rvc-operational-state-delegate-impl.h" #include "tcc-mode.h" #include "thermostat-delegate-impl.h" #include "water-heater-mode.h" -#include "push-av-stream-transport-delegate-impl.h" #include #include @@ -51,11 +51,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp index e1f8ad2b6a043e..2c415de40116b7 100644 --- a/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp +++ b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp @@ -14,11 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include #include #include #include #include -#include #include @@ -32,7 +32,8 @@ namespace { static constexpr size_t kPushAvStreamTransportFixedClusterCount = PushAvStreamTransport::StaticApplicationConfig::kFixedClusterConfig.size(); -static constexpr size_t kPushAvStreamTransportMaxClusterCount = kPushAvStreamTransportFixedClusterCount + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; +static constexpr size_t kPushAvStreamTransportMaxClusterCount = + kPushAvStreamTransportFixedClusterCount + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; LazyRegisteredServerCluster gServers[kPushAvStreamTransportMaxClusterCount]; @@ -66,14 +67,13 @@ void emberAfPushAvStreamTransportClusterInitCallback(EndpointId endpointId) ChipLogError(AppServer, "Failed to get feature map for endpoint %u", endpointId); rawFeatureMap = 0; } - ChipLogProgress(AppServer, "Registering Push AV Stream Transport on endpoint %u, %d", endpointId,arrayIndex); - gServers[arrayIndex].Create(endpointId,BitFlags(rawFeatureMap)); + ChipLogProgress(AppServer, "Registering Push AV Stream Transport on endpoint %u, %d", endpointId, arrayIndex); + gServers[arrayIndex].Create(endpointId, BitFlags(rawFeatureMap)); CHIP_ERROR err = CodegenDataModelProvider::Instance().Registry().Register(gServers[arrayIndex].Registration()); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to register OTA on endpoint %u: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); } - } void emberAfPushAvStreamTransportClusterShutdownCallback(EndpointId endpointId) @@ -110,7 +110,7 @@ void SetDelegate(EndpointId endpointId, PushAvStreamTransportDelegate * delegate { return; } - gServers[arrayIndex].Cluster().SetDelegate(endpointId,delegate); + gServers[arrayIndex].Cluster().SetDelegate(endpointId, delegate); gServers[arrayIndex].Cluster().Init(); } From c2c4a0441b5d634e98b4696e1c71511c8ca6c233 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Thu, 26 Jun 2025 21:07:45 +0530 Subject: [PATCH 34/40] Add unit tests for push av --- src/BUILD.gn | 1 + .../push-av-stream-transport-logic.cpp | 16 +- .../push-av-stream-transport-server.cpp | 2 +- .../tests/BUILD.gn | 39 + .../TestPushAVStreamTransportCluster.cpp | 1162 +++++++++++++++++ .../TestPushAVStreamTransportStorage.cpp | 110 ++ 6 files changed, 1317 insertions(+), 13 deletions(-) create mode 100644 src/app/clusters/push-av-stream-transport-server/tests/BUILD.gn create mode 100644 src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp create mode 100644 src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp diff --git a/src/BUILD.gn b/src/BUILD.gn index 1fdc9383d9f668..2533d4438b6e75 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn @@ -77,6 +77,7 @@ if (chip_build_tests) { "${chip_root}/src/transport/retransmit/tests", "${chip_root}/src/transport/tests", "${chip_root}/src/tracing/esp32_diagnostic_trace/tests", + "${chip_root}/src/app/clusters/push-av-stream-transport-server/tests", ] # Skip DNSSD tests for Mbed platform due to flash memory size limitations diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp index a386a7330b884e..9469325aca9c52 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -136,22 +137,13 @@ PushAvStreamTransportServerLogic::UpsertStreamTransportConnection(const Transpor PushAvStreamTransportServerLogic::UpsertResultEnum PushAvStreamTransportServerLogic::UpsertTimerAppState(std::shared_ptr timerAppState) { - UpsertResultEnum result; auto it = std::find_if(mTimerContexts.begin(), mTimerContexts.end(), [id = timerAppState->connectionID](const auto & existing) { return existing->connectionID == id; }); - if (it != mTimerContexts.end()) - { - *it = timerAppState; - result = UpsertResultEnum::kUpdated; - } - else - { - mTimerContexts.push_back(timerAppState); - result = UpsertResultEnum::kInserted; - } + assert(it == mTimerContexts.end()); - return result; + mTimerContexts.push_back(timerAppState); + return UpsertResultEnum::kInserted; } void PushAvStreamTransportServerLogic::RemoveStreamTransportConnection(const uint16_t transportConnectionId) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp index 1da2c63b93cd9e..236bc0dc3c6e1f 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp @@ -73,7 +73,7 @@ DataModel::ActionReturnStatus PushAvStreamTransportServer::ReadAttribute(const D { VerifyOrDie(request.path.mClusterId == PushAvStreamTransport::Id); - ChipLogProgress(Zcl, "Push AV Stream Transport[ep=%d]: Reading Attribute %d", request.path.mEndpointId, + ChipLogProgress(Zcl, "Push AV Stream Transport[ep=%d]: Reading Attribute %" PRIu32, request.path.mEndpointId, request.path.mAttributeId); switch (request.path.mAttributeId) diff --git a/src/app/clusters/push-av-stream-transport-server/tests/BUILD.gn b/src/app/clusters/push-av-stream-transport-server/tests/BUILD.gn new file mode 100644 index 00000000000000..89b38252d4ed5d --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/tests/BUILD.gn @@ -0,0 +1,39 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") +import("//build_overrides/pigweed.gni") + +import("${chip_root}/build/chip/chip_test_suite.gni") + +chip_test_suite("tests") { + output_name = "libTestPushAVStreamTransport" + + test_sources = [ "TestPushAVStreamTransportCluster.cpp", + "TestPushAVStreamTransportStorage.cpp", + ] + + sources = [] + + cflags = [ "-Wconversion" ] + + public_deps = [ + "${chip_root}/src/app/clusters/push-av-stream-transport-server", + "${chip_root}/src/app/clusters/testing", + "${chip_root}/src/app/tests:helpers", + "${chip_root}/src/lib/core:string-builder-adapters", + "${chip_root}/src/lib/support", + ] +} diff --git a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp new file mode 100644 index 00000000000000..dde0c6ff70e6e9 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp @@ -0,0 +1,1162 @@ +/* + * + * 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { + +class MockCommandHandler : public CommandHandler +{ +public: + ~MockCommandHandler() override {} + + struct ResponseRecord + { + ConcreteCommandPath path; + CommandId commandId; + chip::System::PacketBufferHandle encodedData; + }; + + CHIP_ERROR FallibleAddStatus(const ConcreteCommandPath & aRequestCommandPath, + const Protocols::InteractionModel::ClusterStatusCode & aStatus, + const char * context = nullptr) override + { + mStatuses.push_back({ aRequestCommandPath, aStatus }); + return CHIP_NO_ERROR; + } + + void AddStatus(const ConcreteCommandPath & aRequestCommandPath, const Protocols::InteractionModel::ClusterStatusCode & aStatus, + const char * context = nullptr) override + { + CHIP_ERROR err = FallibleAddStatus(aRequestCommandPath, aStatus, context); + VerifyOrDie(err == CHIP_NO_ERROR); + } + + CHIP_ERROR AddClusterSpecificSuccess(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus) override + { + return FallibleAddStatus(aRequestCommandPath, + Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificSuccess(aClusterStatus)); + } + + CHIP_ERROR AddClusterSpecificFailure(const ConcreteCommandPath & aRequestCommandPath, ClusterStatus aClusterStatus) override + { + return FallibleAddStatus(aRequestCommandPath, + Protocols::InteractionModel::ClusterStatusCode::ClusterSpecificFailure(aClusterStatus)); + } + + FabricIndex GetAccessingFabricIndex() const override { return mFabricIndex; } + + const std::vector & GetResponses() const { return mResponses; } + + CHIP_ERROR AddResponseData(const ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId, + const DataModel::EncodableToTLV & aEncodable) override + { + chip::System::PacketBufferHandle handle = chip::MessagePacketBuffer::New(1024); + VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY); + + TLV::TLVWriter baseWriter; + baseWriter.Init(handle->Start(), handle->MaxDataLength()); + + FabricIndex fabricIndex = 1; + + DataModel::FabricAwareTLVWriter writer(baseWriter, fabricIndex); + + TLV::TLVType containerType; + ReturnErrorOnFailure( + static_cast(writer).StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); + ReturnErrorOnFailure(aEncodable.EncodeTo(writer, TLV::ContextTag(chip::app::CommandDataIB::Tag::kFields))); + ReturnErrorOnFailure(static_cast(writer).EndContainer(containerType)); + + handle->SetDataLength(static_cast(writer).GetLengthWritten()); + + mResponses.push_back({ aRequestCommandPath, aResponseCommandId, std::move(handle) }); + return CHIP_NO_ERROR; + } + + void AddResponse(const ConcreteCommandPath & aRequestCommandPath, CommandId aResponseCommandId, + const DataModel::EncodableToTLV & aEncodable) override + { + AddResponseData(aRequestCommandPath, aResponseCommandId, aEncodable); + } + + bool IsTimedInvoke() const override { return mIsTimedInvoke; } + + void FlushAcksRightAwayOnSlowCommand() override { mAcksFlushed = true; } + + Access::SubjectDescriptor GetSubjectDescriptor() const override { return mSubjectDescriptor; } + + Messaging::ExchangeContext * GetExchangeContext() const override { return mExchangeContext; } + + // Optional for test configuration + void SetFabricIndex(FabricIndex index) { mFabricIndex = index; } + void SetTimedInvoke(bool isTimed) { mIsTimedInvoke = isTimed; } + void SetExchangeContext(Messaging::ExchangeContext * context) { mExchangeContext = context; } + +private: + struct StatusRecord + { + ConcreteCommandPath path; + Protocols::InteractionModel::ClusterStatusCode status; + }; + + std::vector mResponses; + std::vector mStatuses; + + FabricIndex mFabricIndex = 0; + bool mIsTimedInvoke = false; + bool mAcksFlushed = false; + Messaging::ExchangeContext * mExchangeContext = nullptr; + Access::SubjectDescriptor mSubjectDescriptor; +}; + +static uint8_t gDebugEventBuffer[120]; +static uint8_t gInfoEventBuffer[120]; +static uint8_t gCritEventBuffer[120]; +static chip::app::CircularEventBuffer gCircularEventBuffer[3]; + +class MockEventLogging : public chip::Test::AppContext +{ +public: + void SetUp() override + { + const chip::app::LogStorageResources logStorageResources[] = { + { &gDebugEventBuffer[0], sizeof(gDebugEventBuffer), chip::app::PriorityLevel::Debug }, + { &gInfoEventBuffer[0], sizeof(gInfoEventBuffer), chip::app::PriorityLevel::Info }, + { &gCritEventBuffer[0], sizeof(gCritEventBuffer), chip::app::PriorityLevel::Critical }, + }; + + AppContext::SetUp(); + + ASSERT_EQ(mEventCounter.Init(0), CHIP_NO_ERROR); + + chip::app::EventManagement::CreateEventManagement(&GetExchangeManager(), std::size(logStorageResources), + gCircularEventBuffer, logStorageResources, &mEventCounter); + } + + void TearDown() override + { + chip::app::EventManagement::DestroyEventManagement(); + AppContext::TearDown(); + } + +private: + chip::MonotonicallyIncreasingCounter mEventCounter; +}; + +static void CheckLogState(chip::app::EventManagement & aLogMgmt, size_t expectedNumEvents, chip::app::PriorityLevel aPriority) +{ + chip::TLV::TLVReader reader; + size_t elementCount; + chip::app::CircularEventBufferWrapper bufWrapper; + EXPECT_EQ(aLogMgmt.GetEventReader(reader, aPriority, &bufWrapper), CHIP_NO_ERROR); + + EXPECT_EQ(chip::TLV::Utilities::Count(reader, elementCount, false), CHIP_NO_ERROR); + + EXPECT_EQ(elementCount, expectedNumEvents); +} + +} // namespace app +} // namespace chip + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +struct PushAvStream +{ + uint16_t id; + TransportOptionsStruct transportOptions; + TransportStatusEnum transportStatus; + PushAvStreamTransportStatusEnum connectionStatus; +}; + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +using TransportZoneOptionsDecodableStruct = Structs::TransportZoneOptionsStruct::DecodableType; +using TransportTriggerOptionsDecodableStruct = Structs::TransportTriggerOptionsStruct::DecodableType; +using TransportMotionTriggerTimeControlDecodableStruct = Structs::TransportMotionTriggerTimeControlStruct::DecodableType; +using TransportOptionsDecodableStruct = Structs::TransportOptionsStruct::DecodableType; + +using namespace chip::Protocols::InteractionModel; + +class TestPushAVStreamTransportDelegateImpl : public PushAvStreamTransportDelegate +{ +public: + Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, + const uint16_t connectionID) + { + PushAvStream stream{ connectionID, transportOptions, TransportStatusEnum::kInactive, + PushAvStreamTransportStatusEnum::kIdle }; + + /*Store the allocated stream persistently*/ + pushavStreams.push_back(stream); + + return Status::Success; + } + + Protocols::InteractionModel::Status DeallocatePushTransport(const uint16_t connectionID) + { + pushavStreams.erase(std::remove_if(pushavStreams.begin(), pushavStreams.end(), + [connectionID](const PushAvStream & stream) { return stream.id == connectionID; }), + pushavStreams.end()); + return Status::Success; + } + + Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, + const TransportOptionsStorage transportOptions) + { + for (PushAvStream & stream : pushavStreams) + { + if (stream.id == connectionID) + { + ChipLogProgress(Zcl, "Modified Push AV Stream with ID: %d", connectionID); + return Status::Success; + } + } + ChipLogError(Zcl, "Allocated Push AV Stream with ID: %d not found", connectionID); + return Status::NotFound; + } + + Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, + TransportStatusEnum transportStatus) + { + for (PushAvStream & stream : pushavStreams) + { + for (uint16_t connectionID : connectionIDList) + { + if (stream.id == connectionID) + { + stream.transportStatus = transportStatus; + ChipLogProgress(Zcl, "Set Transport Status for Push AV Stream with ID: %d", connectionID); + } + } + } + return Status::Success; + } + + Protocols::InteractionModel::Status + ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, + const Optional & timeControl) + { + // TODO: Validates the requested stream usage against the camera's resource management and stream priority policies. + for (PushAvStream & stream : pushavStreams) + { + if (stream.id == connectionID) + { + stream.connectionStatus = PushAvStreamTransportStatusEnum::kBusy; + ChipLogProgress(Zcl, "Transport triggered for Push AV Stream with ID: %d", connectionID); + } + } + return Status::Success; + } + + Protocols::InteractionModel::Status ValidateBandwidthLimit(StreamUsageEnum streamUsage, + const Optional> & videoStreamId, + const Optional> & audioStreamId) + { + // TODO: Validates the requested stream usage against the camera's resource management. + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; + } + + bool ValidateUrl(std::string url) { return true; } + + Protocols::InteractionModel::Status SelectVideoStream(StreamUsageEnum streamUsage, uint16_t & videoStreamId) + { + // TODO: Select and Assign videoStreamID from the allocated videoStreams + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; + } + + Protocols::InteractionModel::Status SelectAudioStream(StreamUsageEnum streamUsage, uint16_t & audioStreamId) + { + // TODO: Select and Assign audioStreamID from the allocated audioStreams + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; + } + + Protocols::InteractionModel::Status ValidateVideoStream(uint16_t videoStreamId) + { + // TODO: Validate videoStreamID from the allocated videoStreams + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; + } + + Protocols::InteractionModel::Status ValidateAudioStream(uint16_t audioStreamId) + { + // TODO: Validate audioStreamID from the allocated audioStreams + // Returning Status::Success to pass through checks in the Server Implementation. + return Status::Success; + } + + PushAvStreamTransportStatusEnum GetTransportBusyStatus(const uint16_t connectionID) + { + for (PushAvStream & stream : pushavStreams) + { + if (stream.id == connectionID) + { + return stream.connectionStatus; + } + } + return PushAvStreamTransportStatusEnum::kUnknown; + } + + void OnAttributeChanged(AttributeId attributeId) + { + ChipLogProgress(Zcl, "Attribute changed for AttributeId = " ChipLogFormatMEI, ChipLogValueMEI(attributeId)); + } + + void Init() { ChipLogProgress(Zcl, "Push AV Stream Transport Initialized"); } + CHIP_ERROR + LoadCurrentConnections(std::vector & currentConnections) + { + ChipLogProgress(Zcl, "Push AV Current Connections loaded"); + + return CHIP_NO_ERROR; + } + + CHIP_ERROR + PersistentAttributesLoadedCallback() + { + ChipLogProgress(Zcl, "Persistent attributes loaded"); + + return CHIP_NO_ERROR; + } + +private: + std::vector pushavStreams; +}; + +class TestPushAVStreamTransportServerLogic : public ::testing::Test +{ +public: + static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } + static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } +}; + +TEST_F(TestPushAVStreamTransportServerLogic, TestTransportOptionsConstraints) +{ + PushAvStreamTransportServerLogic logic(1, BitFlags(0)); + + TestPushAVStreamTransportDelegateImpl mockDelegate; + logic.SetDelegate(1, &mockDelegate); + + // Create CMAFContainerOptionsStruct object + CMAFContainerOptionsStruct cmafContainerOptions; + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.SetValue(true); + std::string cencKey = "1234567890"; + std::string cencKeyID = "1234567890"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); + + // Create ContainerOptionsStruct object + ContainerOptionsStruct containerOptions; + containerOptions.containerType = ContainerFormatEnum::kCmaf; + containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); + + // Create a TransportMotionTriggerTimeControlStruct object + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + + motionTimeControl.initialDuration = 5000; + motionTimeControl.augmentationDuration = 2000; + motionTimeControl.maxDuration = 30000; + motionTimeControl.blindDuration = 1000; + + std::vector mTransportZoneOptions; + + TransportTriggerOptionsDecodableStruct triggerOptions; + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + + triggerOptions.motionZones.SetValue( + DataModel::Nullable>()); + triggerOptions.motionZones.Value().SetNull(); + triggerOptions.motionSensitivity.SetValue(8); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + // Create TransportOptionsStruct object + TransportOptionsDecodableStruct transportOptions; + transportOptions.streamUsage = StreamUsageEnum::kAnalysis; + transportOptions.videoStreamID.SetValue(1); + transportOptions.audioStreamID.SetValue(2); + transportOptions.endpointID = 1; + transportOptions.url = "rtsp://192.168.1.100:554/stream"; + transportOptions.triggerOptions = triggerOptions; + transportOptions.containerOptions = containerOptions; + transportOptions.expiryTime.SetValue(1000); + + EXPECT_EQ(logic.ValidateIncomingTransportOptions(transportOptions), Status::ConstraintError); +} + +void PrintBufHex(const uint8_t * buf, size_t size) +{ + for (size_t i = 0; i < size; ++i) + { + printf("%02X ", buf[i]); // Print each byte as 2-digit hex + + // Optional: print a newline every 16 bytes + if ((i + 1) % 16 == 0) + printf("\n"); + } + + // Add final newline if buffer didn’t end on a 16-byte boundary + if (size % 16 != 0) + printf("\n"); +} + +TEST_F(TestPushAVStreamTransportServerLogic, TestCurrentConnectionsAttributeAccess) +{ + PushAvStreamTransportServer server(1, BitFlags(1)); + + // Create CMAFContainerOptionsStruct object + CMAFContainerOptionsStruct cmafContainerOptions; + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.ClearValue(); + std::string cencKey = "deadbeefdeadbeef"; + std::string cencKeyID = "dadabeefdadabeef"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); + + // Create ContainerOptionsStruct object + ContainerOptionsStruct containerOptions; + containerOptions.containerType = ContainerFormatEnum::kCmaf; + containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); + + // Create a TransportMotionTriggerTimeControlStruct object + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + + motionTimeControl.initialDuration = 5000; + motionTimeControl.augmentationDuration = 2000; + motionTimeControl.maxDuration = 30000; + motionTimeControl.blindDuration = 1000; + + TransportTriggerOptionsDecodableStruct triggerOptions; + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + + // Create transport zone options structs + Structs::TransportZoneOptionsStruct::Type zone1; + zone1.zone.SetNonNull(1); + zone1.sensitivity.SetValue(5); + + Structs::TransportZoneOptionsStruct::Type zone2; + zone2.zone.SetNonNull(2); + zone2.sensitivity.SetValue(10); + + // Encode them into a TLV buffer + uint8_t tlvBuffer[512]; + TLV::TLVWriter writer; + writer.Init(tlvBuffer, sizeof(tlvBuffer)); + + TLV::TLVWriter containerWriter; + CHIP_ERROR err; + + err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = writer.CloseContainer(containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + size_t encodedLen = writer.GetLengthWritten(); + + // Decode the TLV into a DecodableList + TLV::TLVReader motionZonesReader; + motionZonesReader.Init(tlvBuffer, static_cast(encodedLen)); + err = motionZonesReader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + DataModel::DecodableList decodedList; + err = decodedList.Decode(motionZonesReader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.ClearValue(); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + // Create TransportOptionsStruct object + TransportOptionsDecodableStruct transportOptions; + transportOptions.streamUsage = StreamUsageEnum::kAnalysis; + transportOptions.videoStreamID.SetValue(1); + transportOptions.audioStreamID.SetValue(2); + transportOptions.endpointID = 1; + std::string url = "rtsp://192.168.1.100:554/stream"; + transportOptions.url = Span(url.data(), url.size()); + transportOptions.triggerOptions = triggerOptions; + transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; + transportOptions.containerOptions = containerOptions; + transportOptions.expiryTime.SetValue(1000); + + EXPECT_EQ(server.GetLogic().ValidateIncomingTransportOptions(transportOptions), Status::Success); + + std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + + EXPECT_NE(transportOptionsPtr, nullptr); + + uint16_t connectionID = 1; + + TransportConfigurationStorage transportConfiguration(connectionID, transportOptionsPtr); + + FabricIndex peerFabricIndex = 1; + + transportConfiguration.SetFabricIndex(peerFabricIndex); + + transportConfiguration.transportStatus = TransportStatusEnum::kInactive; + + server.GetLogic().mCurrentConnections.push_back(transportConfiguration); + + // Test reading current connections through attribute reader + + uint8_t buf[1024]; + + TLV::TLVWriter tlvWriter; + tlvWriter.Init(buf); + + AttributeReportIBs::Builder builder; + builder.Init(&tlvWriter); + + ConcreteAttributePath path(1, Clusters::PushAvStreamTransport::Id, + Clusters::PushAvStreamTransport::Attributes::CurrentConnections::Id); + + DataModel::ReadAttributeRequest request; + request.path = path; + request.readFlags.Set(DataModel::ReadFlags::kFabricFiltered); + chip::DataVersion dataVersion(0); + Access::SubjectDescriptor subjectDescriptor; + subjectDescriptor.fabricIndex = peerFabricIndex; + AttributeValueEncoder encoder(builder, subjectDescriptor, path, dataVersion, true); + + // Read the CurrentConnections attribute using the Actions cluster's Read function + DataModel::ActionReturnStatus status = server.ReadAttribute(request, encoder); + EXPECT_TRUE(status.IsSuccess()); + + TLV::TLVReader reader; + reader.Init(buf); + + PrintBufHex(buf, tlvWriter.GetLengthWritten()); + + TLV::TLVReader attrReportsReader; + TLV::TLVReader attrReportReader; + TLV::TLVReader attrDataReader; + + reader.Next(); + reader.OpenContainer(attrReportsReader); + + attrReportsReader.Next(); + attrReportsReader.OpenContainer(attrReportReader); + + attrReportReader.Next(); + attrReportReader.OpenContainer(attrDataReader); + + // We're now in the attribute data IB, skip to the desired tag, we want TagNum = 2 + attrDataReader.Next(); + for (int i = 0; i < 3 && !(IsContextTag(attrDataReader.GetTag()) && TagNumFromTag(attrDataReader.GetTag()) == 2); ++i) + { + attrDataReader.Next(); + } + EXPECT_TRUE(IsContextTag(attrDataReader.GetTag())); + EXPECT_EQ(TagNumFromTag(attrDataReader.GetTag()), 2u); + + Clusters::PushAvStreamTransport::Attributes::CurrentConnections::TypeInfo::DecodableType currentConnections; + err = currentConnections.Decode(attrDataReader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + auto iter = currentConnections.begin(); + EXPECT_TRUE(iter.Next()); + Structs::TransportConfigurationStruct::DecodableType readTransportConfiguration = iter.GetValue(); + EXPECT_EQ(readTransportConfiguration.connectionID, 1); + EXPECT_EQ(readTransportConfiguration.transportStatus, TransportStatusEnum::kInactive); + EXPECT_TRUE(readTransportConfiguration.transportOptions.HasValue()); + Structs::TransportOptionsStruct::DecodableType readTransportOptions = readTransportConfiguration.transportOptions.Value(); + EXPECT_EQ(readTransportOptions.streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(readTransportOptions.videoStreamID, 1); + EXPECT_EQ(readTransportOptions.audioStreamID, 2); + EXPECT_EQ(readTransportOptions.endpointID, 1); + + std::string urlStr(readTransportOptions.url.data(), readTransportOptions.url.size()); + EXPECT_EQ(urlStr, "rtsp://192.168.1.100:554/stream"); + + Structs::TransportTriggerOptionsStruct::DecodableType readTriggerOptions = readTransportOptions.triggerOptions; + EXPECT_EQ(readTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion); + EXPECT_TRUE(readTriggerOptions.motionZones.HasValue()); + EXPECT_FALSE(readTriggerOptions.motionZones.Value().IsNull()); + + DataModel::DecodableList readMotionZonesList = + readTriggerOptions.motionZones.Value().Value(); + + auto readMotionZonesIter = readMotionZonesList.begin(); + + EXPECT_TRUE(readMotionZonesIter.Next()); + const auto & decodedZone1 = readMotionZonesIter.GetValue(); + EXPECT_TRUE(!decodedZone1.zone.IsNull()); + EXPECT_EQ(decodedZone1.zone.Value(), 1); + EXPECT_TRUE(decodedZone1.sensitivity.HasValue()); + EXPECT_EQ(decodedZone1.sensitivity.Value(), 5); + + EXPECT_TRUE(readMotionZonesIter.Next()); + const auto & decodedZone2 = readMotionZonesIter.GetValue(); + EXPECT_TRUE(!decodedZone2.zone.IsNull()); + EXPECT_EQ(decodedZone2.zone.Value(), 2); + EXPECT_TRUE(decodedZone2.sensitivity.HasValue()); + EXPECT_EQ(decodedZone2.sensitivity.Value(), 10); + + // Should be no more entries + EXPECT_FALSE(readMotionZonesIter.Next()); + + EXPECT_FALSE(readTriggerOptions.motionSensitivity.HasValue()); + EXPECT_TRUE(readTriggerOptions.maxPreRollLen.HasValue()); + EXPECT_EQ(readTriggerOptions.maxPreRollLen.Value(), 1000); + + EXPECT_TRUE(readTriggerOptions.motionTimeControl.HasValue()); + Structs::TransportMotionTriggerTimeControlStruct::DecodableType readMotionTimeControl = + readTriggerOptions.motionTimeControl.Value(); + EXPECT_EQ(readMotionTimeControl.initialDuration, 5000); + EXPECT_EQ(readMotionTimeControl.augmentationDuration, 2000); + EXPECT_EQ(readMotionTimeControl.maxDuration, (uint32_t) 30000); + EXPECT_EQ(readMotionTimeControl.blindDuration, 1000); + + EXPECT_EQ(readTransportOptions.ingestMethod, IngestMethodsEnum::kCMAFIngest); + + Structs::ContainerOptionsStruct::DecodableType readContainerOptions = readTransportOptions.containerOptions; + EXPECT_EQ(readContainerOptions.containerType, ContainerFormatEnum::kCmaf); + EXPECT_TRUE(readContainerOptions.CMAFContainerOptions.HasValue()); + Structs::CMAFContainerOptionsStruct::Type readCMAFContainerOptions = readContainerOptions.CMAFContainerOptions.Value(); + EXPECT_EQ(readCMAFContainerOptions.chunkDuration, 1000); + EXPECT_FALSE(readCMAFContainerOptions.metadataEnabled.HasValue()); + + std::string cencKeyStr(readCMAFContainerOptions.CENCKey.Value().data(), + readCMAFContainerOptions.CENCKey.Value().data() + readCMAFContainerOptions.CENCKey.Value().size()); + + EXPECT_EQ(cencKeyStr, "deadbeefdeadbeef"); + + std::string cencKeyIDStr(readCMAFContainerOptions.CENCKeyID.Value().data(), + readCMAFContainerOptions.CENCKeyID.Value().data() + readCMAFContainerOptions.CENCKeyID.Value().size()); + + EXPECT_EQ(cencKeyIDStr, "dadabeefdadabeef"); +} + +TEST_F(TestPushAVStreamTransportServerLogic, AllocatePushTransport) +{ + PushAvStreamTransportServer server(1, BitFlags(1)); + TestPushAVStreamTransportDelegateImpl mockDelegate; + server.GetLogic().SetDelegate(1, &mockDelegate); + + // Create CMAFContainerOptionsStruct object + CMAFContainerOptionsStruct cmafContainerOptions; + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.ClearValue(); + std::string cencKey = "deadbeefdeadbeef"; + std::string cencKeyID = "dadabeefdadabeef"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); + + // Create ContainerOptionsStruct object + ContainerOptionsStruct containerOptions; + containerOptions.containerType = ContainerFormatEnum::kCmaf; + containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); + + // Create a TransportMotionTriggerTimeControlStruct object + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + + motionTimeControl.initialDuration = 5000; + motionTimeControl.augmentationDuration = 2000; + motionTimeControl.maxDuration = 30000; + motionTimeControl.blindDuration = 1000; + + TransportTriggerOptionsDecodableStruct triggerOptions; + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + + // Create transport zone options structs + Structs::TransportZoneOptionsStruct::Type zone1; + zone1.zone.SetNonNull(1); + zone1.sensitivity.SetValue(5); + + Structs::TransportZoneOptionsStruct::Type zone2; + zone2.zone.SetNonNull(2); + zone2.sensitivity.SetValue(10); + + // Encode them into a TLV buffer + uint8_t tlvBuffer[512]; + TLV::TLVWriter writer; + writer.Init(tlvBuffer, sizeof(tlvBuffer)); + + TLV::TLVWriter containerWriter; + CHIP_ERROR err; + + err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = writer.CloseContainer(containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + size_t encodedLen = writer.GetLengthWritten(); + + // Decode the TLV into a DecodableList + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(encodedLen)); + err = reader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + DataModel::DecodableList decodedList; + err = decodedList.Decode(reader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.ClearValue(); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + // Create TransportOptionsStruct object + TransportOptionsDecodableStruct transportOptions; + transportOptions.streamUsage = StreamUsageEnum::kAnalysis; + transportOptions.videoStreamID.SetValue(1); + transportOptions.audioStreamID.SetValue(2); + transportOptions.endpointID = 1; + std::string url = "rtsp://192.168.1.100:554/stream"; + transportOptions.url = Span(url.data(), url.size()); + transportOptions.triggerOptions = triggerOptions; + transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; + transportOptions.containerOptions = containerOptions; + transportOptions.expiryTime.ClearValue(); + + MockCommandHandler commandHandler; + commandHandler.SetFabricIndex(1); + ConcreteCommandPath kCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::AllocatePushTransport::Id }; + Commands::AllocatePushTransport::DecodableType commandData; + commandData.transportOptions = transportOptions; + + // Without a delegate, command is unsupported. + EXPECT_EQ(server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath, commandData), std::nullopt); + + EXPECT_EQ(server.GetLogic().mCurrentConnections.size(), (size_t) 1); + + TransportConfigurationStorage transportConfiguration = server.GetLogic().mCurrentConnections[0]; + EXPECT_EQ(transportConfiguration.connectionID, 1); + EXPECT_EQ(transportConfiguration.GetFabricIndex(), 1); + + Structs::TransportOptionsStruct::Type readTransportOptions = transportConfiguration.transportOptions.Value(); + EXPECT_EQ(readTransportOptions.streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(readTransportOptions.videoStreamID, 1); + EXPECT_EQ(readTransportOptions.audioStreamID, 2); + EXPECT_EQ(readTransportOptions.endpointID, 1); + std::string urlStr(readTransportOptions.url.data(), readTransportOptions.url.size()); + EXPECT_EQ(urlStr, "rtsp://192.168.1.100:554/stream"); + + Structs::TransportTriggerOptionsStruct::Type readTriggerOptions = readTransportOptions.triggerOptions; + EXPECT_EQ(readTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion); + EXPECT_TRUE(readTriggerOptions.motionZones.HasValue()); + EXPECT_FALSE(readTriggerOptions.motionZones.Value().IsNull()); + + DataModel::List & motionZonesList = + readTriggerOptions.motionZones.Value().Value(); + + EXPECT_EQ(motionZonesList.size(), (size_t) 2); + + Structs::TransportZoneOptionsStruct::Type motionZone1 = motionZonesList[0]; + Structs::TransportZoneOptionsStruct::Type motionZone2 = motionZonesList[1]; + + EXPECT_FALSE(motionZone1.zone.IsNull()); + EXPECT_EQ(motionZone1.zone.Value(), 1); + EXPECT_TRUE(motionZone1.sensitivity.HasValue()); + EXPECT_EQ(motionZone1.sensitivity.Value(), 5); + + EXPECT_FALSE(motionZone2.zone.IsNull()); + EXPECT_EQ(motionZone2.zone.Value(), 2); + EXPECT_TRUE(motionZone2.sensitivity.HasValue()); + EXPECT_EQ(motionZone2.sensitivity.Value(), 10); + + EXPECT_FALSE(readTriggerOptions.motionSensitivity.HasValue()); + EXPECT_TRUE(readTriggerOptions.maxPreRollLen.HasValue()); + EXPECT_EQ(readTriggerOptions.maxPreRollLen.Value(), (uint32_t) 1000); +} + +TEST_F(TestPushAVStreamTransportServerLogic, AllocatePushTransportResponse) +{ + PushAvStreamTransportServer server(1, BitFlags(1)); + TestPushAVStreamTransportDelegateImpl mockDelegate; + server.GetLogic().SetDelegate(1, &mockDelegate); + + // Create CMAFContainerOptionsStruct object + CMAFContainerOptionsStruct cmafContainerOptions; + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.ClearValue(); + std::string cencKey = "deadbeefdeadbeef"; + std::string cencKeyID = "dadabeefdadabeef"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); + + // Create ContainerOptionsStruct object + ContainerOptionsStruct containerOptions; + containerOptions.containerType = ContainerFormatEnum::kCmaf; + containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); + + // Create a TransportMotionTriggerTimeControlStruct object + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + + motionTimeControl.initialDuration = 5000; + motionTimeControl.augmentationDuration = 2000; + motionTimeControl.maxDuration = 30000; + motionTimeControl.blindDuration = 1000; + + TransportTriggerOptionsDecodableStruct triggerOptions; + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + + // Create transport zone options structs + Structs::TransportZoneOptionsStruct::Type zone1; + zone1.zone.SetNonNull(1); + zone1.sensitivity.SetValue(5); + + Structs::TransportZoneOptionsStruct::Type zone2; + zone2.zone.SetNonNull(2); + zone2.sensitivity.SetValue(10); + + // Encode them into a TLV buffer + uint8_t tlvBuffer[512]; + TLV::TLVWriter writer; + writer.Init(tlvBuffer, sizeof(tlvBuffer)); + + TLV::TLVWriter containerWriter; + CHIP_ERROR err; + + err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = writer.CloseContainer(containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + size_t encodedLen = writer.GetLengthWritten(); + + // Decode the TLV into a DecodableList + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(encodedLen)); + err = reader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + DataModel::DecodableList decodedList; + err = decodedList.Decode(reader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.ClearValue(); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + // Create TransportOptionsStruct object + TransportOptionsDecodableStruct transportOptions; + transportOptions.streamUsage = StreamUsageEnum::kAnalysis; + transportOptions.videoStreamID.SetValue(1); + transportOptions.audioStreamID.SetValue(2); + transportOptions.endpointID = 1; + std::string url = "rtsp://192.168.1.100:554/stream"; + transportOptions.url = Span(url.data(), url.size()); + transportOptions.triggerOptions = triggerOptions; + transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; + transportOptions.containerOptions = containerOptions; + transportOptions.expiryTime.ClearValue(); + + MockCommandHandler commandHandler; + commandHandler.SetFabricIndex(1); + ConcreteCommandPath kCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::AllocatePushTransport::Id }; + Commands::AllocatePushTransport::DecodableType commandData; + commandData.transportOptions = transportOptions; + + // Call the command handler + server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath, commandData); + + // Check the response + const auto & responses = commandHandler.GetResponses(); + EXPECT_EQ(responses.size(), (size_t) 1); + + // Get the encoded buffer + const auto & encodedBuffer = responses[0].encodedData; + + PrintBufHex(encodedBuffer->Start(), encodedBuffer->DataLength()); + + EXPECT_FALSE(encodedBuffer.IsNull()); + + // Set up TLV reader + TLV::TLVReader responseReader; + responseReader.Init(encodedBuffer->Start(), static_cast(encodedBuffer->DataLength())); + + // Enter the top-level anonymous structure (CommandDataIB wrapper) + err = responseReader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + TLV::TLVReader outerContainer; + err = responseReader.OpenContainer(outerContainer); + EXPECT_EQ(err, CHIP_NO_ERROR); + + // Read the next element inside the container: should be kFields + err = outerContainer.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + EXPECT_TRUE(IsContextTag(outerContainer.GetTag())); + EXPECT_EQ(TagNumFromTag(outerContainer.GetTag()), chip::to_underlying(CommandDataIB::Tag::kFields)); + + // Decode into response object + Commands::AllocatePushTransportResponse::DecodableType decodedResponse; + err = decodedResponse.Decode(outerContainer); + EXPECT_EQ(err, CHIP_NO_ERROR); + + // Validate decoded fields + // ConnectionID=2 as connection is being allocated second time. + EXPECT_EQ(decodedResponse.transportConfiguration.connectionID, 2); + EXPECT_EQ(decodedResponse.transportConfiguration.GetFabricIndex(), 1); + EXPECT_EQ(decodedResponse.transportConfiguration.transportStatus, TransportStatusEnum::kInactive); + + EXPECT_TRUE(decodedResponse.transportConfiguration.transportOptions.HasValue()); + + Structs::TransportOptionsStruct::DecodableType readTransportOptions = + decodedResponse.transportConfiguration.transportOptions.Value(); + + EXPECT_EQ(readTransportOptions.streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(readTransportOptions.videoStreamID, 1); + EXPECT_EQ(readTransportOptions.audioStreamID, 2); + EXPECT_EQ(readTransportOptions.endpointID, 1); + std::string urlStr(readTransportOptions.url.data(), readTransportOptions.url.size()); + EXPECT_EQ(urlStr, "rtsp://192.168.1.100:554/stream"); + + Structs::TransportTriggerOptionsStruct::DecodableType readTriggerOptions = readTransportOptions.triggerOptions; + EXPECT_EQ(readTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion); + EXPECT_TRUE(readTriggerOptions.motionZones.HasValue()); + EXPECT_FALSE(readTriggerOptions.motionZones.Value().IsNull()); + + DataModel::DecodableList & readMotionZonesList = + readTriggerOptions.motionZones.Value().Value(); + + auto readMotionZonesIter = readMotionZonesList.begin(); + + EXPECT_TRUE(readMotionZonesIter.Next()); + const auto & decodedZone1 = readMotionZonesIter.GetValue(); + EXPECT_TRUE(!decodedZone1.zone.IsNull()); + EXPECT_EQ(decodedZone1.zone.Value(), 1); + EXPECT_TRUE(decodedZone1.sensitivity.HasValue()); + EXPECT_EQ(decodedZone1.sensitivity.Value(), 5); + + EXPECT_TRUE(readMotionZonesIter.Next()); + const auto & decodedZone2 = readMotionZonesIter.GetValue(); + EXPECT_TRUE(!decodedZone2.zone.IsNull()); + EXPECT_EQ(decodedZone2.zone.Value(), 2); + EXPECT_TRUE(decodedZone2.sensitivity.HasValue()); + EXPECT_EQ(decodedZone2.sensitivity.Value(), 10); + + // Should be no more entries + EXPECT_FALSE(readMotionZonesIter.Next()); + + EXPECT_FALSE(readTriggerOptions.motionSensitivity.HasValue()); + EXPECT_TRUE(readTriggerOptions.maxPreRollLen.HasValue()); + EXPECT_EQ(readTriggerOptions.maxPreRollLen.Value(), 1000); + + EXPECT_TRUE(readTriggerOptions.motionTimeControl.HasValue()); + Structs::TransportMotionTriggerTimeControlStruct::DecodableType readMotionTimeControl = + readTriggerOptions.motionTimeControl.Value(); + EXPECT_EQ(readMotionTimeControl.initialDuration, 5000); + EXPECT_EQ(readMotionTimeControl.augmentationDuration, 2000); + EXPECT_EQ(readMotionTimeControl.maxDuration, (uint32_t) 30000); + EXPECT_EQ(readMotionTimeControl.blindDuration, 1000); + + EXPECT_EQ(readTransportOptions.ingestMethod, IngestMethodsEnum::kCMAFIngest); + + Structs::ContainerOptionsStruct::DecodableType readContainerOptions = readTransportOptions.containerOptions; + EXPECT_EQ(readContainerOptions.containerType, ContainerFormatEnum::kCmaf); + EXPECT_TRUE(readContainerOptions.CMAFContainerOptions.HasValue()); + Structs::CMAFContainerOptionsStruct::Type readCMAFContainerOptions = readContainerOptions.CMAFContainerOptions.Value(); + EXPECT_EQ(readCMAFContainerOptions.chunkDuration, 1000); + EXPECT_FALSE(readCMAFContainerOptions.metadataEnabled.HasValue()); + + std::string cencKeyStr(readCMAFContainerOptions.CENCKey.Value().data(), + readCMAFContainerOptions.CENCKey.Value().data() + readCMAFContainerOptions.CENCKey.Value().size()); + + EXPECT_EQ(cencKeyStr, "deadbeefdeadbeef"); + + std::string cencKeyIDStr(readCMAFContainerOptions.CENCKeyID.Value().data(), + readCMAFContainerOptions.CENCKeyID.Value().data() + readCMAFContainerOptions.CENCKeyID.Value().size()); + + EXPECT_EQ(cencKeyIDStr, "dadabeefdadabeef"); +} + +TEST_F(MockEventLogging, ManuallyTriggerTransport) +{ + PushAvStreamTransportServer server(1, BitFlags(1)); + TestPushAVStreamTransportDelegateImpl mockDelegate; + server.GetLogic().SetDelegate(1, &mockDelegate); + + // Create CMAFContainerOptionsStruct object + CMAFContainerOptionsStruct cmafContainerOptions; + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.ClearValue(); + std::string cencKey = "deadbeefdeadbeef"; + std::string cencKeyID = "dadabeefdadabeef"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); + + // Create ContainerOptionsStruct object + ContainerOptionsStruct containerOptions; + containerOptions.containerType = ContainerFormatEnum::kCmaf; + containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); + + // Create a TransportMotionTriggerTimeControlStruct object + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + + motionTimeControl.initialDuration = 5000; + motionTimeControl.augmentationDuration = 2000; + motionTimeControl.maxDuration = 30000; + motionTimeControl.blindDuration = 1000; + + TransportTriggerOptionsDecodableStruct triggerOptions; + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + + // Create transport zone options structs + Structs::TransportZoneOptionsStruct::Type zone1; + zone1.zone.SetNonNull(1); + zone1.sensitivity.SetValue(5); + + Structs::TransportZoneOptionsStruct::Type zone2; + zone2.zone.SetNonNull(2); + zone2.sensitivity.SetValue(10); + + // Encode them into a TLV buffer + uint8_t tlvBuffer[512]; + TLV::TLVWriter writer; + writer.Init(tlvBuffer, sizeof(tlvBuffer)); + + TLV::TLVWriter containerWriter; + CHIP_ERROR err; + + err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = writer.CloseContainer(containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + size_t encodedLen = writer.GetLengthWritten(); + + // Decode the TLV into a DecodableList + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(encodedLen)); + err = reader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + DataModel::DecodableList decodedList; + err = decodedList.Decode(reader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.ClearValue(); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + // Create TransportOptionsStruct object + TransportOptionsDecodableStruct transportOptions; + transportOptions.streamUsage = StreamUsageEnum::kAnalysis; + transportOptions.videoStreamID.SetValue(1); + transportOptions.audioStreamID.SetValue(2); + transportOptions.endpointID = 1; + std::string url = "rtsp://192.168.1.100:554/stream"; + transportOptions.url = Span(url.data(), url.size()); + transportOptions.triggerOptions = triggerOptions; + transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; + transportOptions.containerOptions = containerOptions; + transportOptions.expiryTime.ClearValue(); + + MockCommandHandler commandHandler; + commandHandler.SetFabricIndex(1); + ConcreteCommandPath kCommandPath1{ 1, Clusters::PushAvStreamTransport::Id, Commands::AllocatePushTransport::Id }; + Commands::AllocatePushTransport::DecodableType commandData1; + commandData1.transportOptions = transportOptions; + + server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath1, commandData1); + + ConcreteCommandPath kCommandPath3{ 1, Clusters::PushAvStreamTransport::Id, Commands::SetTransportStatus::Id }; + Commands::SetTransportStatus::DecodableType commandData3; + commandData3.connectionID.SetNonNull(3); + commandData3.transportStatus = TransportStatusEnum::kActive; + server.GetLogic().HandleSetTransportStatus(commandHandler, kCommandPath3, commandData3); + + // Status status = server.GetLogic().GeneratePushTransportBeginEvent(1, TransportTriggerTypeEnum::kMotion, + // MakeOptional(TriggerActivationReasonEnum::kUserInitiated)); + // EXPECT_EQ(status, Status::Success); + + ConcreteCommandPath kCommandPath2{ 1, Clusters::PushAvStreamTransport::Id, Commands::ManuallyTriggerTransport::Id }; + Commands::ManuallyTriggerTransport::DecodableType commandData2; + commandData2.connectionID = 3; + commandData2.activationReason = TriggerActivationReasonEnum::kUserInitiated; + + server.GetLogic().HandleManuallyTriggerTransport(commandHandler, kCommandPath2, commandData2); + + chip::app::EventManagement & logMgmt = chip::app::EventManagement::GetInstance(); + + CheckLogState(logMgmt, 1, chip::app::PriorityLevel::Info); +} + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp new file mode 100644 index 00000000000000..226daebbd26975 --- /dev/null +++ b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp @@ -0,0 +1,110 @@ +/* + * + * 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 + +#include + +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace PushAvStreamTransport { + +using TransportZoneOptionsDecodableStruct = Structs::TransportZoneOptionsStruct::DecodableType; +using TransportTriggerOptionsDecodableStruct = Structs::TransportTriggerOptionsStruct::DecodableType; +using TransportMotionTriggerTimeControlDecodableStruct = Structs::TransportMotionTriggerTimeControlStruct::DecodableType; +using TransportOptionsDecodableStruct = Structs::TransportOptionsStruct::DecodableType; + +class TestPushAVStreamTransportStorage : public ::testing::Test +{ +public: + static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } + static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } +}; + +uint8_t tlvBuffer[512]; +TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; +TransportTriggerOptionsDecodableStruct triggerOptions; +DataModel::DecodableList decodedList; + +TEST_F(TestPushAVStreamTransportStorage, TestSetUpTriggerOptions) +{ + // Create a TransportMotionTriggerTimeControlStruct object + motionTimeControl.initialDuration = 5000; + motionTimeControl.augmentationDuration = 2000; + motionTimeControl.maxDuration = 30000; + motionTimeControl.blindDuration = 1000; + + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + + // Create transport zone options structs + Structs::TransportZoneOptionsStruct::Type zone1; + zone1.zone.SetNonNull(1); + zone1.sensitivity.SetValue(5); + + Structs::TransportZoneOptionsStruct::Type zone2; + zone2.zone.SetNonNull(2); + zone2.sensitivity.SetValue(10); + + // Encode them into a TLV buffer + TLV::TLVWriter writer; + writer.Init(tlvBuffer, sizeof(tlvBuffer)); + + TLV::TLVWriter containerWriter; + CHIP_ERROR err; + + err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = writer.CloseContainer(containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + size_t encodedLen = writer.GetLengthWritten(); + + // Decode the TLV into a DecodableList + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(encodedLen)); + err = reader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = decodedList.Decode(reader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.ClearValue(); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); +} + +TEST_F(TestPushAVStreamTransportStorage, TestTransportTriggerOptionsStorage) +{ + TransportTriggerOptionsStorage transportTriggerOptionsStorage(triggerOptions); +} + +} // namespace PushAvStreamTransport +} // namespace Clusters +} // namespace app +} // namespace chip From eb4f90db5b95fe197218d77b94413fa22ba738be Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 26 Jun 2025 15:41:29 +0000 Subject: [PATCH 35/40] Restyled by gn --- .../push-av-stream-transport-server/tests/BUILD.gn | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/tests/BUILD.gn b/src/app/clusters/push-av-stream-transport-server/tests/BUILD.gn index 89b38252d4ed5d..0a0dead1e33048 100644 --- a/src/app/clusters/push-av-stream-transport-server/tests/BUILD.gn +++ b/src/app/clusters/push-av-stream-transport-server/tests/BUILD.gn @@ -21,9 +21,10 @@ import("${chip_root}/build/chip/chip_test_suite.gni") chip_test_suite("tests") { output_name = "libTestPushAVStreamTransport" - test_sources = [ "TestPushAVStreamTransportCluster.cpp", - "TestPushAVStreamTransportStorage.cpp", - ] + test_sources = [ + "TestPushAVStreamTransportCluster.cpp", + "TestPushAVStreamTransportStorage.cpp", + ] sources = [] From f25de39479461f557ae2f1d2cc055101e168d082 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 27 Jun 2025 08:13:14 +0530 Subject: [PATCH 36/40] make push av logic type safe Restyled by clang-format Restyled by gn --- .../push-av-stream-transport-delegate-impl.h | 2 +- .../push-av-stream-transport-server/BUILD.gn | 4 +-- .../CodegenIntegration.cpp | 2 +- .../app_config_dependent_sources.cmake | 4 +-- ...p => push-av-stream-transport-cluster.cpp} | 32 ++++++++++++++++--- ...r.h => push-av-stream-transport-cluster.h} | 4 +++ .../push-av-stream-transport-logic.cpp | 25 --------------- .../push-av-stream-transport-logic.h | 4 --- .../TestPushAVStreamTransportCluster.cpp | 4 +-- 9 files changed, 40 insertions(+), 41 deletions(-) rename src/app/clusters/push-av-stream-transport-server/{push-av-stream-transport-server.cpp => push-av-stream-transport-cluster.cpp} (86%) rename src/app/clusters/push-av-stream-transport-server/{push-av-stream-transport-server.h => push-av-stream-transport-cluster.h} (92%) diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h index c26cdc3096ed2f..fbe97b1364873b 100644 --- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h @@ -18,7 +18,7 @@ #pragma once -#include +#include #include #include diff --git a/src/app/clusters/push-av-stream-transport-server/BUILD.gn b/src/app/clusters/push-av-stream-transport-server/BUILD.gn index 120c0e278434ff..630841805ad110 100644 --- a/src/app/clusters/push-av-stream-transport-server/BUILD.gn +++ b/src/app/clusters/push-av-stream-transport-server/BUILD.gn @@ -16,11 +16,11 @@ import("//build_overrides/chip.gni") source_set("push-av-stream-transport-server") { sources = [ + "push-av-stream-transport-cluster.cpp", + "push-av-stream-transport-cluster.h", "push-av-stream-transport-delegate.h", "push-av-stream-transport-logic.cpp", "push-av-stream-transport-logic.h", - "push-av-stream-transport-server.cpp", - "push-av-stream-transport-server.h", "push-av-stream-transport-storage.h", ] diff --git a/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp index 2c415de40116b7..92ff0beece5ccd 100644 --- a/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp +++ b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp @@ -15,7 +15,7 @@ * limitations under the License. */ #include -#include +#include #include #include #include diff --git a/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake index c5e8f43b8921e5..0053a93e48257a 100644 --- a/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake +++ b/src/app/clusters/push-av-stream-transport-server/app_config_dependent_sources.cmake @@ -16,8 +16,8 @@ TARGET_SOURCES( ${APP_TARGET} PRIVATE - "${CLUSTER_DIR}/push-av-stream-transport-server.cpp" - "${CLUSTER_DIR}/push-av-stream-transport-server.h" + "${CLUSTER_DIR}/push-av-stream-transport-cluster.cpp" + "${CLUSTER_DIR}/push-av-stream-transport-cluster.h" "${CLUSTER_DIR}/push-av-stream-transport-logic.cpp" "${CLUSTER_DIR}/push-av-stream-transport-logic.h" "${CLUSTER_DIR}/push-av-stream-transport-delegate.h" diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp similarity index 86% rename from src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp rename to src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp index 236bc0dc3c6e1f..fa99f46a680de0 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -68,6 +68,31 @@ CHIP_ERROR PushAvStreamTransportServer::Attributes(const ConcreteClusterPath & p return builder.AppendElements(DefaultServerCluster::GlobalAttributes()); } +CHIP_ERROR PushAvStreamTransportServer::ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder) +{ + for (const auto & supportsFormat : mLogic.mSupportedFormats) + { + ReturnErrorOnFailure(encoder.Encode(supportsFormat)); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +PushAvStreamTransportServer::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, + FabricIndex fabricIndex) +{ + for (const auto & currentConnections : mLogic.mCurrentConnections) + { + if (currentConnections.fabricIndex == fabricIndex) + { + ReturnErrorOnFailure(encoder.Encode(currentConnections)); + } + } + + return CHIP_NO_ERROR; +} + DataModel::ActionReturnStatus PushAvStreamTransportServer::ReadAttribute(const DataModel::ReadAttributeRequest & request, AttributeValueEncoder & aEncoder) { @@ -85,12 +110,11 @@ DataModel::ActionReturnStatus PushAvStreamTransportServer::ReadAttribute(const D return aEncoder.Encode(PushAvStreamTransport::kRevision); case PushAvStreamTransport::Attributes::SupportedFormats::Id: - return aEncoder.EncodeList( - [this](const auto & encoder) -> CHIP_ERROR { return mLogic.ReadAndEncodeSupportedFormats(encoder); }); + return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR { return ReadAndEncodeSupportedFormats(encoder); }); case PushAvStreamTransport::Attributes::CurrentConnections::Id: return aEncoder.EncodeList([this, &aEncoder](const auto & encoder) -> CHIP_ERROR { - CHIP_ERROR err = mLogic.ReadAndEncodeCurrentConnections(encoder, aEncoder.AccessingFabricIndex()); + CHIP_ERROR err = ReadAndEncodeCurrentConnections(encoder, aEncoder.AccessingFabricIndex()); if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "Push AV Stream Transport: Error reading CurrentConnections"); diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.h similarity index 92% rename from src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h rename to src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.h index 2274f73d255162..8be2ad1abbc4a5 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-server.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.h @@ -71,6 +71,10 @@ class PushAvStreamTransportServer : public DefaultServerCluster private: PushAvStreamTransportServerLogic mLogic; + + // Helpers to read list items + CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, FabricIndex fabricIndex); + CHIP_ERROR ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder); }; } // namespace PushAvStreamTransport diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp index 9469325aca9c52..bf57802fb4050d 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp @@ -73,31 +73,6 @@ bool PushAvStreamTransportServerLogic::HasFeature(Feature feature) const return mFeatures.Has(feature); } -CHIP_ERROR PushAvStreamTransportServerLogic::ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder) -{ - for (const auto & supportsFormat : mSupportedFormats) - { - ReturnErrorOnFailure(encoder.Encode(supportsFormat)); - } - - return CHIP_NO_ERROR; -} - -CHIP_ERROR -PushAvStreamTransportServerLogic::ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, - FabricIndex fabricIndex) -{ - for (const auto & currentConnections : mCurrentConnections) - { - if (currentConnections.fabricIndex == fabricIndex) - { - ReturnErrorOnFailure(encoder.Encode(currentConnections)); - } - } - - return CHIP_NO_ERROR; -} - bool PushAvStreamTransportServerLogic::IsNullDelegateWithLogging(EndpointId endpointIdForLogging) { diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h index a92f00328d6ac5..7520fcf6273d48 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h @@ -55,10 +55,6 @@ class PushAvStreamTransportServerLogic bool HasFeature(Feature feature) const; - // Helpers to read list items via delegate APIs - CHIP_ERROR ReadAndEncodeCurrentConnections(const AttributeValueEncoder::ListEncodeHelper & encoder, FabricIndex fabricIndex); - CHIP_ERROR ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder); - Protocols::InteractionModel::Status ValidateIncomingTransportOptions(const Structs::TransportOptionsStruct::DecodableType & transportOptions); diff --git a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp index dde0c6ff70e6e9..615b0dff42aced 100644 --- a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp +++ b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -575,7 +575,7 @@ TEST_F(TestPushAVStreamTransportServerLogic, TestCurrentConnectionsAttributeAcce subjectDescriptor.fabricIndex = peerFabricIndex; AttributeValueEncoder encoder(builder, subjectDescriptor, path, dataVersion, true); - // Read the CurrentConnections attribute using the Actions cluster's Read function + // Read the CurrentConnections attribute using the cluster's Read function DataModel::ActionReturnStatus status = server.ReadAttribute(request, encoder); EXPECT_TRUE(status.IsSuccess()); From a37853de5d294fbe6aced28aab23ea05e63e598d Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Tue, 1 Jul 2025 01:44:14 +0530 Subject: [PATCH 37/40] update push av cluster unit tests --- .../push-av-stream-transport-cluster.cpp | 2 +- .../push-av-stream-transport-logic.cpp | 8 +- .../push-av-stream-transport-logic.h | 7 +- .../TestPushAVStreamTransportCluster.cpp | 730 +++++++++++------- 4 files changed, 462 insertions(+), 285 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp index fa99f46a680de0..77b968c0b1e9fc 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp @@ -117,7 +117,7 @@ DataModel::ActionReturnStatus PushAvStreamTransportServer::ReadAttribute(const D CHIP_ERROR err = ReadAndEncodeCurrentConnections(encoder, aEncoder.AccessingFabricIndex()); if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "Push AV Stream Transport: Error reading CurrentConnections"); + ChipLogError(Zcl, "Push AV Stream Transport: Error reading CurrentConnections %s", err.AsString()); } return err; }); diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp index bf57802fb4050d..e911fe001c4b93 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp @@ -243,7 +243,7 @@ void PushAvStreamTransportServerLogic::PushAVStreamTransportDeallocateCallback(S } } -void PushAvStreamTransportServerLogic::ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec) +CHIP_ERROR PushAvStreamTransportServerLogic::ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec) { uint32_t timeoutMs = timeoutSec * MILLISECOND_TICKS_PER_SECOND; @@ -253,7 +253,7 @@ void PushAvStreamTransportServerLogic::ScheduleTransportDeallocate(uint16_t conn if (transportDeallocateContext == nullptr) { ChipLogError(Zcl, "Failed to allocate memory for deallocate context"); - return; + return CHIP_ERROR_NO_MEMORY; } CHIP_ERROR err = DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(timeoutMs), @@ -269,6 +269,8 @@ void PushAvStreamTransportServerLogic::ScheduleTransportDeallocate(uint16_t conn { UpsertTimerAppState(transportDeallocateContext); } + + return err; } Status PushAvStreamTransportServerLogic::ValidateIncomingTransportOptions( @@ -511,7 +513,7 @@ PushAvStreamTransportServerLogic::HandleAllocatePushTransport(CommandHandler & h VerifyOrDo(transportOptionsValidityStatus == Status::Success, { ChipLogError(Zcl, "HandleAllocatePushTransport[ep=%d]: TransportOptions of command data is not Valid", mEndpointId); handler.AddStatus(commandPath, transportOptionsValidityStatus); - return std::nullopt; + return transportOptionsValidityStatus; }); // Todo: TLSEndpointID Validation diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h index 7520fcf6273d48..de8fdb18756834 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h @@ -24,6 +24,11 @@ class PushAvStreamTransportServerLogic void SetDelegate(EndpointId aEndpoint, PushAvStreamTransportDelegate * delegate) { mDelegate = delegate; + if (mDelegate == nullptr) + { + ChipLogError(Zcl, "Push AV Stream Transport: Delegate is null"); + return; + } mDelegate->SetEndpointId(aEndpoint); } @@ -126,7 +131,7 @@ class PushAvStreamTransportServerLogic * @param endpointId endpoint where DoorLockServer is running * @param timeoutSec timeout in seconds */ - void ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec); + CHIP_ERROR ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec); }; } // namespace PushAvStreamTransport } // namespace Clusters diff --git a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp index 615b0dff42aced..fb5a334af36f41 100644 --- a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp +++ b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp @@ -188,25 +188,6 @@ static void CheckLogState(chip::app::EventManagement & aLogMgmt, size_t expected EXPECT_EQ(elementCount, expectedNumEvents); } - -} // namespace app -} // namespace chip - -namespace chip { -namespace app { -namespace Clusters { -namespace PushAvStreamTransport { - -struct PushAvStream -{ - uint16_t id; - TransportOptionsStruct transportOptions; - TransportStatusEnum transportStatus; - PushAvStreamTransportStatusEnum connectionStatus; -}; - -} // namespace PushAvStreamTransport -} // namespace Clusters } // namespace app } // namespace chip @@ -222,6 +203,13 @@ using TransportOptionsDecodableStruct = Structs::TransportOptio using namespace chip::Protocols::InteractionModel; +struct PushAvStream +{ + uint16_t id; + TransportOptionsStruct transportOptions; + TransportStatusEnum transportStatus; + PushAvStreamTransportStatusEnum connectionStatus; +}; class TestPushAVStreamTransportDelegateImpl : public PushAvStreamTransportDelegate { public: @@ -379,115 +367,170 @@ class TestPushAVStreamTransportServerLogic : public ::testing::Test TEST_F(TestPushAVStreamTransportServerLogic, TestTransportOptionsConstraints) { - PushAvStreamTransportServerLogic logic(1, BitFlags(0)); + std::string cencKey = "1234567890ABCDEF"; + std::string cencKeyID = "1234567890ABCDEF"; - TestPushAVStreamTransportDelegateImpl mockDelegate; - logic.SetDelegate(1, &mockDelegate); + CMAFContainerOptionsStruct cmafContainerOptions; + ContainerOptionsStruct containerOptions; + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + std::vector mTransportZoneOptions; + TransportTriggerOptionsDecodableStruct triggerOptions; + + std::string url = "rtsp://192.168.1.100:554/stream"; + TransportOptionsDecodableStruct transportOptions; + + uint8_t tlvBuffer[512]; + Structs::TransportZoneOptionsStruct::Type zone1; + Structs::TransportZoneOptionsStruct::Type zone2; + DataModel::DecodableList decodedList; + + PushAvStreamTransportServerLogic logic(1, BitFlags(1)); // Create CMAFContainerOptionsStruct object - CMAFContainerOptionsStruct cmafContainerOptions; cmafContainerOptions.chunkDuration = 1000; - cmafContainerOptions.metadataEnabled.SetValue(true); - std::string cencKey = "1234567890"; - std::string cencKeyID = "1234567890"; + cmafContainerOptions.metadataEnabled.ClearValue(); + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); // Create ContainerOptionsStruct object - ContainerOptionsStruct containerOptions; containerOptions.containerType = ContainerFormatEnum::kCmaf; containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); // Create a TransportMotionTriggerTimeControlStruct object - TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; - motionTimeControl.initialDuration = 5000; motionTimeControl.augmentationDuration = 2000; motionTimeControl.maxDuration = 30000; motionTimeControl.blindDuration = 1000; - std::vector mTransportZoneOptions; - - TransportTriggerOptionsDecodableStruct triggerOptions; triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; - triggerOptions.motionZones.SetValue( - DataModel::Nullable>()); - triggerOptions.motionZones.Value().SetNull(); - triggerOptions.motionSensitivity.SetValue(8); + triggerOptions.motionZones.ClearValue(); + + triggerOptions.motionSensitivity.ClearValue(); triggerOptions.motionTimeControl.SetValue(motionTimeControl); triggerOptions.maxPreRollLen.SetValue(1000); // Create TransportOptionsStruct object - TransportOptionsDecodableStruct transportOptions; transportOptions.streamUsage = StreamUsageEnum::kAnalysis; transportOptions.videoStreamID.SetValue(1); transportOptions.audioStreamID.SetValue(2); transportOptions.endpointID = 1; - transportOptions.url = "rtsp://192.168.1.100:554/stream"; + transportOptions.url = Span(url.data(), url.size()); transportOptions.triggerOptions = triggerOptions; transportOptions.containerOptions = containerOptions; - transportOptions.expiryTime.SetValue(1000); + transportOptions.expiryTime.ClearValue(); + + // Invalid command because the motion zones are missing + EXPECT_EQ(logic.ValidateIncomingTransportOptions(transportOptions), Status::InvalidCommand); + + // Create transport zone options structs + zone1.zone.SetNonNull(1); + zone1.sensitivity.SetValue(5); + + zone2.zone.SetNonNull(2); + zone2.sensitivity.SetValue(10); + + // Encode them into a TLV buffer + TLV::TLVWriter writer; + writer.Init(tlvBuffer, sizeof(tlvBuffer)); + + TLV::TLVWriter containerWriter; + CHIP_ERROR err; + + err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + EXPECT_EQ(err, CHIP_NO_ERROR); - EXPECT_EQ(logic.ValidateIncomingTransportOptions(transportOptions), Status::ConstraintError); + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = writer.CloseContainer(containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + size_t encodedLen = writer.GetLengthWritten(); + + // Decode the TLV into a DecodableList + TLV::TLVReader motionZonesReader; + motionZonesReader.Init(tlvBuffer, static_cast(encodedLen)); + err = motionZonesReader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = decodedList.Decode(motionZonesReader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + + // Upadate the trigger options in the transport options + transportOptions.triggerOptions = triggerOptions; + EXPECT_EQ(logic.ValidateIncomingTransportOptions(transportOptions), Status::Success); } void PrintBufHex(const uint8_t * buf, size_t size) { for (size_t i = 0; i < size; ++i) { - printf("%02X ", buf[i]); // Print each byte as 2-digit hex + printf("%02X ", buf[i]); - // Optional: print a newline every 16 bytes if ((i + 1) % 16 == 0) printf("\n"); } - // Add final newline if buffer didn’t end on a 16-byte boundary if (size % 16 != 0) printf("\n"); } -TEST_F(TestPushAVStreamTransportServerLogic, TestCurrentConnectionsAttributeAccess) +TEST_F(TestPushAVStreamTransportServerLogic, Test_AllocateTransport_AllocateTransportResponse_ReadAttribute_DeallocateTransport) { - PushAvStreamTransportServer server(1, BitFlags(1)); + /* + * Test AllocatePushTransport + */ + std::string cencKey = "1234567890ABCDEF"; + std::string cencKeyID = "1234567890ABCDEF"; - // Create CMAFContainerOptionsStruct object CMAFContainerOptionsStruct cmafContainerOptions; + ContainerOptionsStruct containerOptions; + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + std::vector mTransportZoneOptions; + TransportTriggerOptionsDecodableStruct triggerOptions; + + std::string url = "rtsp://192.168.1.100:554/stream"; + TransportOptionsDecodableStruct transportOptions; + + uint8_t tlvBuffer[512]; + Structs::TransportZoneOptionsStruct::Type zone1; + Structs::TransportZoneOptionsStruct::Type zone2; + DataModel::DecodableList decodedList; + + // Create CMAFContainerOptionsStruct object cmafContainerOptions.chunkDuration = 1000; cmafContainerOptions.metadataEnabled.ClearValue(); - std::string cencKey = "deadbeefdeadbeef"; - std::string cencKeyID = "dadabeefdadabeef"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); // Create ContainerOptionsStruct object - ContainerOptionsStruct containerOptions; containerOptions.containerType = ContainerFormatEnum::kCmaf; containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); // Create a TransportMotionTriggerTimeControlStruct object - TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; - motionTimeControl.initialDuration = 5000; motionTimeControl.augmentationDuration = 2000; motionTimeControl.maxDuration = 30000; motionTimeControl.blindDuration = 1000; - TransportTriggerOptionsDecodableStruct triggerOptions; triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; // Create transport zone options structs - Structs::TransportZoneOptionsStruct::Type zone1; zone1.zone.SetNonNull(1); zone1.sensitivity.SetValue(5); - Structs::TransportZoneOptionsStruct::Type zone2; zone2.zone.SetNonNull(2); zone2.sensitivity.SetValue(10); // Encode them into a TLV buffer - uint8_t tlvBuffer[512]; TLV::TLVWriter writer; writer.Init(tlvBuffer, sizeof(tlvBuffer)); @@ -514,48 +557,163 @@ TEST_F(TestPushAVStreamTransportServerLogic, TestCurrentConnectionsAttributeAcce err = motionZonesReader.Next(); EXPECT_EQ(err, CHIP_NO_ERROR); - DataModel::DecodableList decodedList; err = decodedList.Decode(motionZonesReader); EXPECT_EQ(err, CHIP_NO_ERROR); triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.ClearValue(); triggerOptions.motionTimeControl.SetValue(motionTimeControl); triggerOptions.maxPreRollLen.SetValue(1000); // Create TransportOptionsStruct object - TransportOptionsDecodableStruct transportOptions; transportOptions.streamUsage = StreamUsageEnum::kAnalysis; transportOptions.videoStreamID.SetValue(1); transportOptions.audioStreamID.SetValue(2); transportOptions.endpointID = 1; - std::string url = "rtsp://192.168.1.100:554/stream"; transportOptions.url = Span(url.data(), url.size()); transportOptions.triggerOptions = triggerOptions; - transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; transportOptions.containerOptions = containerOptions; - transportOptions.expiryTime.SetValue(1000); + transportOptions.expiryTime.ClearValue(); + + PushAvStreamTransportServer server(1, BitFlags(1)); + TestPushAVStreamTransportDelegateImpl mockDelegate; + + MockCommandHandler commandHandler; + commandHandler.SetFabricIndex(1); + ConcreteCommandPath kCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::AllocatePushTransport::Id }; + Commands::AllocatePushTransport::DecodableType commandData; + commandData.transportOptions = transportOptions; - EXPECT_EQ(server.GetLogic().ValidateIncomingTransportOptions(transportOptions), Status::Success); + // Without a delegate, command is unsupported. + EXPECT_EQ(server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath, commandData), Status::UnsupportedCommand); - std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + // Set the delegate to the server logic + server.GetLogic().SetDelegate(1, &mockDelegate); + EXPECT_EQ(server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath, commandData), std::nullopt); - EXPECT_NE(transportOptionsPtr, nullptr); + EXPECT_EQ(server.GetLogic().mCurrentConnections.size(), (size_t) 1); + uint16_t allocatedConnectionID = server.GetLogic().mCurrentConnections[0].connectionID; - uint16_t connectionID = 1; + /* + * Test AllocatePushTransportResponse + */ - TransportConfigurationStorage transportConfiguration(connectionID, transportOptionsPtr); + // Check the response + const auto & responses = commandHandler.GetResponses(); + EXPECT_EQ(responses.size(), (size_t) 1); - FabricIndex peerFabricIndex = 1; + // Get the encoded buffer + const auto & encodedBuffer = responses[0].encodedData; + + PrintBufHex(encodedBuffer->Start(), encodedBuffer->DataLength()); - transportConfiguration.SetFabricIndex(peerFabricIndex); + EXPECT_FALSE(encodedBuffer.IsNull()); - transportConfiguration.transportStatus = TransportStatusEnum::kInactive; + // Set up TLV reader + TLV::TLVReader responseReader; + responseReader.Init(encodedBuffer->Start(), static_cast(encodedBuffer->DataLength())); - server.GetLogic().mCurrentConnections.push_back(transportConfiguration); + // Enter the top-level anonymous structure (CommandDataIB wrapper) + err = responseReader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); - // Test reading current connections through attribute reader + TLV::TLVReader outerContainer; + err = responseReader.OpenContainer(outerContainer); + EXPECT_EQ(err, CHIP_NO_ERROR); + + // Read the next element inside the container: should be kFields + err = outerContainer.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + EXPECT_TRUE(IsContextTag(outerContainer.GetTag())); + EXPECT_EQ(TagNumFromTag(outerContainer.GetTag()), chip::to_underlying(CommandDataIB::Tag::kFields)); + + // Decode into response object + Commands::AllocatePushTransportResponse::DecodableType decodedResponse; + err = decodedResponse.Decode(outerContainer); + EXPECT_EQ(err, CHIP_NO_ERROR); + + // Validate decoded fields + EXPECT_EQ(decodedResponse.transportConfiguration.connectionID, allocatedConnectionID); + EXPECT_EQ(decodedResponse.transportConfiguration.GetFabricIndex(), 1); + EXPECT_EQ(decodedResponse.transportConfiguration.transportStatus, TransportStatusEnum::kInactive); + + EXPECT_TRUE(decodedResponse.transportConfiguration.transportOptions.HasValue()); + + Structs::TransportOptionsStruct::DecodableType respTransportOptions = + decodedResponse.transportConfiguration.transportOptions.Value(); + + EXPECT_EQ(respTransportOptions.streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(respTransportOptions.videoStreamID, 1); + EXPECT_EQ(respTransportOptions.audioStreamID, 2); + EXPECT_EQ(respTransportOptions.endpointID, 1); + std::string respUrlStr(respTransportOptions.url.data(), respTransportOptions.url.size()); + EXPECT_EQ(respUrlStr, "rtsp://192.168.1.100:554/stream"); + + Structs::TransportTriggerOptionsStruct::DecodableType respTriggerOptions = respTransportOptions.triggerOptions; + EXPECT_EQ(respTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion); + EXPECT_TRUE(respTriggerOptions.motionZones.HasValue()); + EXPECT_FALSE(respTriggerOptions.motionZones.Value().IsNull()); + + DataModel::DecodableList & respMotionZonesList = + respTriggerOptions.motionZones.Value().Value(); + + auto respMotionZonesIter = respMotionZonesList.begin(); + + EXPECT_TRUE(respMotionZonesIter.Next()); + const auto & respDecodedZone1 = respMotionZonesIter.GetValue(); + EXPECT_TRUE(!respDecodedZone1.zone.IsNull()); + EXPECT_EQ(respDecodedZone1.zone.Value(), 1); + EXPECT_TRUE(respDecodedZone1.sensitivity.HasValue()); + EXPECT_EQ(respDecodedZone1.sensitivity.Value(), 5); + + EXPECT_TRUE(respMotionZonesIter.Next()); + const auto & respDecodedZone2 = respMotionZonesIter.GetValue(); + EXPECT_TRUE(!respDecodedZone2.zone.IsNull()); + EXPECT_EQ(respDecodedZone2.zone.Value(), 2); + EXPECT_TRUE(respDecodedZone2.sensitivity.HasValue()); + EXPECT_EQ(respDecodedZone2.sensitivity.Value(), 10); + + // Should be no more entries + EXPECT_FALSE(respMotionZonesIter.Next()); + EXPECT_FALSE(respTriggerOptions.motionSensitivity.HasValue()); + EXPECT_TRUE(respTriggerOptions.maxPreRollLen.HasValue()); + EXPECT_EQ(respTriggerOptions.maxPreRollLen.Value(), 1000); + + EXPECT_TRUE(respTriggerOptions.motionTimeControl.HasValue()); + Structs::TransportMotionTriggerTimeControlStruct::DecodableType respMotionTimeControl = + respTriggerOptions.motionTimeControl.Value(); + EXPECT_EQ(respMotionTimeControl.initialDuration, 5000); + EXPECT_EQ(respMotionTimeControl.augmentationDuration, 2000); + EXPECT_EQ(respMotionTimeControl.maxDuration, (uint32_t) 30000); + EXPECT_EQ(respMotionTimeControl.blindDuration, 1000); + + EXPECT_EQ(respTransportOptions.ingestMethod, IngestMethodsEnum::kCMAFIngest); + + Structs::ContainerOptionsStruct::DecodableType respContainerOptions = respTransportOptions.containerOptions; + EXPECT_EQ(respContainerOptions.containerType, ContainerFormatEnum::kCmaf); + EXPECT_TRUE(respContainerOptions.CMAFContainerOptions.HasValue()); + Structs::CMAFContainerOptionsStruct::Type respCMAFContainerOptions = respContainerOptions.CMAFContainerOptions.Value(); + EXPECT_EQ(respCMAFContainerOptions.chunkDuration, 1000); + EXPECT_FALSE(respCMAFContainerOptions.metadataEnabled.HasValue()); + + std::string respCENCKeyStr(respCMAFContainerOptions.CENCKey.Value().data(), + respCMAFContainerOptions.CENCKey.Value().data() + respCMAFContainerOptions.CENCKey.Value().size()); + + EXPECT_EQ(respCENCKeyStr, "1234567890ABCDEF"); + + std::string respCENCKeyIDStr(respCMAFContainerOptions.CENCKeyID.Value().data(), + respCMAFContainerOptions.CENCKeyID.Value().data() + + respCMAFContainerOptions.CENCKeyID.Value().size()); + + EXPECT_EQ(respCENCKeyIDStr, "1234567890ABCDEF"); + + /* + * Test ReadAttribute + */ + // Test reading current connections through attribute reader uint8_t buf[1024]; TLV::TLVWriter tlvWriter; @@ -572,6 +730,7 @@ TEST_F(TestPushAVStreamTransportServerLogic, TestCurrentConnectionsAttributeAcce request.readFlags.Set(DataModel::ReadFlags::kFabricFiltered); chip::DataVersion dataVersion(0); Access::SubjectDescriptor subjectDescriptor; + FabricIndex peerFabricIndex = 1; subjectDescriptor.fabricIndex = peerFabricIndex; AttributeValueEncoder encoder(builder, subjectDescriptor, path, dataVersion, true); @@ -613,7 +772,7 @@ TEST_F(TestPushAVStreamTransportServerLogic, TestCurrentConnectionsAttributeAcce auto iter = currentConnections.begin(); EXPECT_TRUE(iter.Next()); Structs::TransportConfigurationStruct::DecodableType readTransportConfiguration = iter.GetValue(); - EXPECT_EQ(readTransportConfiguration.connectionID, 1); + EXPECT_EQ(readTransportConfiguration.connectionID, allocatedConnectionID); EXPECT_EQ(readTransportConfiguration.transportStatus, TransportStatusEnum::kInactive); EXPECT_TRUE(readTransportConfiguration.transportOptions.HasValue()); Structs::TransportOptionsStruct::DecodableType readTransportOptions = readTransportConfiguration.transportOptions.Value(); @@ -676,56 +835,78 @@ TEST_F(TestPushAVStreamTransportServerLogic, TestCurrentConnectionsAttributeAcce std::string cencKeyStr(readCMAFContainerOptions.CENCKey.Value().data(), readCMAFContainerOptions.CENCKey.Value().data() + readCMAFContainerOptions.CENCKey.Value().size()); - EXPECT_EQ(cencKeyStr, "deadbeefdeadbeef"); + EXPECT_EQ(cencKeyStr, "1234567890ABCDEF"); std::string cencKeyIDStr(readCMAFContainerOptions.CENCKeyID.Value().data(), readCMAFContainerOptions.CENCKeyID.Value().data() + readCMAFContainerOptions.CENCKeyID.Value().size()); - EXPECT_EQ(cencKeyIDStr, "dadabeefdadabeef"); + EXPECT_EQ(cencKeyIDStr, "1234567890ABCDEF"); + + /* + * Test DeallocatePushTransport + */ + MockCommandHandler deallocateCommandHandler; + deallocateCommandHandler.SetFabricIndex(1); + ConcreteCommandPath kDeallocateCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::DeallocatePushTransport::Id }; + Commands::DeallocatePushTransport::DecodableType deallocateCommandData; + deallocateCommandData.connectionID = allocatedConnectionID; + + EXPECT_EQ( + server.GetLogic().HandleDeallocatePushTransport(deallocateCommandHandler, kDeallocateCommandPath, deallocateCommandData), + std::nullopt); + + EXPECT_EQ(server.GetLogic().mCurrentConnections.size(), (size_t) 0); } -TEST_F(TestPushAVStreamTransportServerLogic, AllocatePushTransport) +TEST_F(MockEventLogging, Test_AllocateTransport_ModifyTransport_FindTransport_FindTransportResponse) { - PushAvStreamTransportServer server(1, BitFlags(1)); - TestPushAVStreamTransportDelegateImpl mockDelegate; - server.GetLogic().SetDelegate(1, &mockDelegate); + /* + * Test AllocatePushTransport + */ + std::string cencKey = "1234567890ABCDEF"; + std::string cencKeyID = "1234567890ABCDEF"; - // Create CMAFContainerOptionsStruct object CMAFContainerOptionsStruct cmafContainerOptions; + ContainerOptionsStruct containerOptions; + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + std::vector mTransportZoneOptions; + TransportTriggerOptionsDecodableStruct triggerOptions; + + std::string url = "rtsp://192.168.1.100:554/stream"; + TransportOptionsDecodableStruct transportOptions; + + uint8_t tlvBuffer[512]; + Structs::TransportZoneOptionsStruct::Type zone1; + Structs::TransportZoneOptionsStruct::Type zone2; + DataModel::DecodableList decodedList; + + // Create CMAFContainerOptionsStruct object cmafContainerOptions.chunkDuration = 1000; cmafContainerOptions.metadataEnabled.ClearValue(); - std::string cencKey = "deadbeefdeadbeef"; - std::string cencKeyID = "dadabeefdadabeef"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); // Create ContainerOptionsStruct object - ContainerOptionsStruct containerOptions; containerOptions.containerType = ContainerFormatEnum::kCmaf; containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); // Create a TransportMotionTriggerTimeControlStruct object - TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; - motionTimeControl.initialDuration = 5000; motionTimeControl.augmentationDuration = 2000; motionTimeControl.maxDuration = 30000; motionTimeControl.blindDuration = 1000; - TransportTriggerOptionsDecodableStruct triggerOptions; triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; // Create transport zone options structs - Structs::TransportZoneOptionsStruct::Type zone1; zone1.zone.SetNonNull(1); zone1.sensitivity.SetValue(5); - Structs::TransportZoneOptionsStruct::Type zone2; zone2.zone.SetNonNull(2); zone2.sensitivity.SetValue(10); // Encode them into a TLV buffer - uint8_t tlvBuffer[512]; TLV::TLVWriter writer; writer.Init(tlvBuffer, sizeof(tlvBuffer)); @@ -747,185 +928,155 @@ TEST_F(TestPushAVStreamTransportServerLogic, AllocatePushTransport) size_t encodedLen = writer.GetLengthWritten(); // Decode the TLV into a DecodableList - TLV::TLVReader reader; - reader.Init(tlvBuffer, static_cast(encodedLen)); - err = reader.Next(); + TLV::TLVReader motionZonesReader; + motionZonesReader.Init(tlvBuffer, static_cast(encodedLen)); + err = motionZonesReader.Next(); EXPECT_EQ(err, CHIP_NO_ERROR); - DataModel::DecodableList decodedList; - err = decodedList.Decode(reader); + err = decodedList.Decode(motionZonesReader); EXPECT_EQ(err, CHIP_NO_ERROR); triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.ClearValue(); triggerOptions.motionTimeControl.SetValue(motionTimeControl); triggerOptions.maxPreRollLen.SetValue(1000); // Create TransportOptionsStruct object - TransportOptionsDecodableStruct transportOptions; transportOptions.streamUsage = StreamUsageEnum::kAnalysis; transportOptions.videoStreamID.SetValue(1); transportOptions.audioStreamID.SetValue(2); transportOptions.endpointID = 1; - std::string url = "rtsp://192.168.1.100:554/stream"; transportOptions.url = Span(url.data(), url.size()); transportOptions.triggerOptions = triggerOptions; - transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; transportOptions.containerOptions = containerOptions; transportOptions.expiryTime.ClearValue(); + PushAvStreamTransportServer server(1, BitFlags(1)); + TestPushAVStreamTransportDelegateImpl mockDelegate; + MockCommandHandler commandHandler; commandHandler.SetFabricIndex(1); ConcreteCommandPath kCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::AllocatePushTransport::Id }; Commands::AllocatePushTransport::DecodableType commandData; commandData.transportOptions = transportOptions; - // Without a delegate, command is unsupported. + // Set the delegate to the server logic + server.GetLogic().SetDelegate(1, &mockDelegate); EXPECT_EQ(server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath, commandData), std::nullopt); EXPECT_EQ(server.GetLogic().mCurrentConnections.size(), (size_t) 1); + uint16_t allocatedConnectionID = server.GetLogic().mCurrentConnections[0].connectionID; - TransportConfigurationStorage transportConfiguration = server.GetLogic().mCurrentConnections[0]; - EXPECT_EQ(transportConfiguration.connectionID, 1); - EXPECT_EQ(transportConfiguration.GetFabricIndex(), 1); - - Structs::TransportOptionsStruct::Type readTransportOptions = transportConfiguration.transportOptions.Value(); - EXPECT_EQ(readTransportOptions.streamUsage, StreamUsageEnum::kAnalysis); - EXPECT_EQ(readTransportOptions.videoStreamID, 1); - EXPECT_EQ(readTransportOptions.audioStreamID, 2); - EXPECT_EQ(readTransportOptions.endpointID, 1); - std::string urlStr(readTransportOptions.url.data(), readTransportOptions.url.size()); - EXPECT_EQ(urlStr, "rtsp://192.168.1.100:554/stream"); - - Structs::TransportTriggerOptionsStruct::Type readTriggerOptions = readTransportOptions.triggerOptions; - EXPECT_EQ(readTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion); - EXPECT_TRUE(readTriggerOptions.motionZones.HasValue()); - EXPECT_FALSE(readTriggerOptions.motionZones.Value().IsNull()); - - DataModel::List & motionZonesList = - readTriggerOptions.motionZones.Value().Value(); - - EXPECT_EQ(motionZonesList.size(), (size_t) 2); - - Structs::TransportZoneOptionsStruct::Type motionZone1 = motionZonesList[0]; - Structs::TransportZoneOptionsStruct::Type motionZone2 = motionZonesList[1]; - - EXPECT_FALSE(motionZone1.zone.IsNull()); - EXPECT_EQ(motionZone1.zone.Value(), 1); - EXPECT_TRUE(motionZone1.sensitivity.HasValue()); - EXPECT_EQ(motionZone1.sensitivity.Value(), 5); - - EXPECT_FALSE(motionZone2.zone.IsNull()); - EXPECT_EQ(motionZone2.zone.Value(), 2); - EXPECT_TRUE(motionZone2.sensitivity.HasValue()); - EXPECT_EQ(motionZone2.sensitivity.Value(), 10); - - EXPECT_FALSE(readTriggerOptions.motionSensitivity.HasValue()); - EXPECT_TRUE(readTriggerOptions.maxPreRollLen.HasValue()); - EXPECT_EQ(readTriggerOptions.maxPreRollLen.Value(), (uint32_t) 1000); -} - -TEST_F(TestPushAVStreamTransportServerLogic, AllocatePushTransportResponse) -{ - PushAvStreamTransportServer server(1, BitFlags(1)); - TestPushAVStreamTransportDelegateImpl mockDelegate; - server.GetLogic().SetDelegate(1, &mockDelegate); + /* + * Test ModifyPushTransport + */ // Create CMAFContainerOptionsStruct object - CMAFContainerOptionsStruct cmafContainerOptions; - cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.chunkDuration = 500; cmafContainerOptions.metadataEnabled.ClearValue(); - std::string cencKey = "deadbeefdeadbeef"; - std::string cencKeyID = "dadabeefdadabeef"; + + cencKey = "ABCDEF1234567890"; + cencKeyID = "ABCDEF1234567890"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); // Create ContainerOptionsStruct object - ContainerOptionsStruct containerOptions; containerOptions.containerType = ContainerFormatEnum::kCmaf; containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); // Create a TransportMotionTriggerTimeControlStruct object - TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; - - motionTimeControl.initialDuration = 5000; - motionTimeControl.augmentationDuration = 2000; - motionTimeControl.maxDuration = 30000; + motionTimeControl.initialDuration = 1000; + motionTimeControl.augmentationDuration = 1000; + motionTimeControl.maxDuration = 10000; motionTimeControl.blindDuration = 1000; - TransportTriggerOptionsDecodableStruct triggerOptions; triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; // Create transport zone options structs - Structs::TransportZoneOptionsStruct::Type zone1; - zone1.zone.SetNonNull(1); - zone1.sensitivity.SetValue(5); + zone1.zone.SetNonNull(7); + zone1.sensitivity.SetValue(8); - Structs::TransportZoneOptionsStruct::Type zone2; - zone2.zone.SetNonNull(2); - zone2.sensitivity.SetValue(10); + zone2.zone.SetNonNull(9); + zone2.sensitivity.SetValue(6); // Encode them into a TLV buffer - uint8_t tlvBuffer[512]; - TLV::TLVWriter writer; - writer.Init(tlvBuffer, sizeof(tlvBuffer)); + TLV::TLVWriter modifyTLVWriter; + modifyTLVWriter.Init(tlvBuffer, sizeof(tlvBuffer)); - TLV::TLVWriter containerWriter; - CHIP_ERROR err; + TLV::TLVWriter modifyContainerWriter; - err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + err = modifyTLVWriter.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, modifyContainerWriter); EXPECT_EQ(err, CHIP_NO_ERROR); - err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + err = DataModel::Encode(modifyContainerWriter, TLV::AnonymousTag(), zone1); EXPECT_EQ(err, CHIP_NO_ERROR); - err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + err = DataModel::Encode(modifyContainerWriter, TLV::AnonymousTag(), zone2); EXPECT_EQ(err, CHIP_NO_ERROR); - err = writer.CloseContainer(containerWriter); + err = modifyTLVWriter.CloseContainer(modifyContainerWriter); EXPECT_EQ(err, CHIP_NO_ERROR); - size_t encodedLen = writer.GetLengthWritten(); + size_t modifyEncodedLen = modifyTLVWriter.GetLengthWritten(); // Decode the TLV into a DecodableList - TLV::TLVReader reader; - reader.Init(tlvBuffer, static_cast(encodedLen)); - err = reader.Next(); + TLV::TLVReader modifyMotionZonesReader; + modifyMotionZonesReader.Init(tlvBuffer, static_cast(modifyEncodedLen)); + err = modifyMotionZonesReader.Next(); EXPECT_EQ(err, CHIP_NO_ERROR); - DataModel::DecodableList decodedList; - err = decodedList.Decode(reader); + DataModel::DecodableList modifyDecodedList; + + err = modifyDecodedList.Decode(modifyMotionZonesReader); EXPECT_EQ(err, CHIP_NO_ERROR); - triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(modifyDecodedList)); + triggerOptions.motionSensitivity.ClearValue(); triggerOptions.motionTimeControl.SetValue(motionTimeControl); triggerOptions.maxPreRollLen.SetValue(1000); // Create TransportOptionsStruct object - TransportOptionsDecodableStruct transportOptions; transportOptions.streamUsage = StreamUsageEnum::kAnalysis; - transportOptions.videoStreamID.SetValue(1); - transportOptions.audioStreamID.SetValue(2); + transportOptions.videoStreamID.SetValue(11); + transportOptions.audioStreamID.SetValue(22); transportOptions.endpointID = 1; - std::string url = "rtsp://192.168.1.100:554/stream"; + url = "rtsp://192.168.1.100:554/modify-stream"; transportOptions.url = Span(url.data(), url.size()); transportOptions.triggerOptions = triggerOptions; - transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; transportOptions.containerOptions = containerOptions; transportOptions.expiryTime.ClearValue(); - MockCommandHandler commandHandler; - commandHandler.SetFabricIndex(1); - ConcreteCommandPath kCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::AllocatePushTransport::Id }; - Commands::AllocatePushTransport::DecodableType commandData; - commandData.transportOptions = transportOptions; + MockCommandHandler modifyCommandHandler; + modifyCommandHandler.SetFabricIndex(1); + + ConcreteCommandPath kModifyCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::ModifyPushTransport::Id }; + Commands::ModifyPushTransport::DecodableType modifyCommandData; + modifyCommandData.connectionID = allocatedConnectionID; + modifyCommandData.transportOptions = transportOptions; + + server.GetLogic().HandleModifyPushTransport(modifyCommandHandler, kModifyCommandPath, modifyCommandData); + + EXPECT_EQ(server.GetLogic().mCurrentConnections.size(), (size_t) 1); + + /* + * Test FindPushTransport + */ + + MockCommandHandler findCommandHandler; + findCommandHandler.SetFabricIndex(1); - // Call the command handler - server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath, commandData); + ConcreteCommandPath kFindCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::FindTransport::Id }; + Commands::FindTransport::DecodableType findCommandData; + // As connectionID is static, the new allocated connectionID will be 2. + findCommandData.connectionID.SetValue(DataModel::MakeNullable(allocatedConnectionID)); + + server.GetLogic().HandleFindTransport(findCommandHandler, kFindCommandPath, findCommandData); // Check the response - const auto & responses = commandHandler.GetResponses(); + const auto & responses = findCommandHandler.GetResponses(); EXPECT_EQ(responses.size(), (size_t) 1); // Get the encoded buffer @@ -955,129 +1106,135 @@ TEST_F(TestPushAVStreamTransportServerLogic, AllocatePushTransportResponse) EXPECT_EQ(TagNumFromTag(outerContainer.GetTag()), chip::to_underlying(CommandDataIB::Tag::kFields)); // Decode into response object - Commands::AllocatePushTransportResponse::DecodableType decodedResponse; + Commands::FindTransportResponse::DecodableType decodedResponse; err = decodedResponse.Decode(outerContainer); EXPECT_EQ(err, CHIP_NO_ERROR); - // Validate decoded fields - // ConnectionID=2 as connection is being allocated second time. - EXPECT_EQ(decodedResponse.transportConfiguration.connectionID, 2); - EXPECT_EQ(decodedResponse.transportConfiguration.GetFabricIndex(), 1); - EXPECT_EQ(decodedResponse.transportConfiguration.transportStatus, TransportStatusEnum::kInactive); + auto iter = decodedResponse.transportConfigurations.begin(); - EXPECT_TRUE(decodedResponse.transportConfiguration.transportOptions.HasValue()); + EXPECT_TRUE(iter.Next()); - Structs::TransportOptionsStruct::DecodableType readTransportOptions = - decodedResponse.transportConfiguration.transportOptions.Value(); + Structs::TransportConfigurationStruct::DecodableType readTransportConfiguration = iter.GetValue(); + EXPECT_EQ(readTransportConfiguration.connectionID, allocatedConnectionID); + EXPECT_EQ(readTransportConfiguration.transportStatus, TransportStatusEnum::kInactive); - EXPECT_EQ(readTransportOptions.streamUsage, StreamUsageEnum::kAnalysis); - EXPECT_EQ(readTransportOptions.videoStreamID, 1); - EXPECT_EQ(readTransportOptions.audioStreamID, 2); - EXPECT_EQ(readTransportOptions.endpointID, 1); - std::string urlStr(readTransportOptions.url.data(), readTransportOptions.url.size()); - EXPECT_EQ(urlStr, "rtsp://192.168.1.100:554/stream"); + EXPECT_TRUE(readTransportConfiguration.transportOptions.HasValue()); + Structs::TransportOptionsStruct::DecodableType findTransportOptions = readTransportConfiguration.transportOptions.Value(); + EXPECT_EQ(findTransportOptions.streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(findTransportOptions.videoStreamID, 11); + EXPECT_EQ(findTransportOptions.audioStreamID, 22); + EXPECT_EQ(findTransportOptions.endpointID, 1); - Structs::TransportTriggerOptionsStruct::DecodableType readTriggerOptions = readTransportOptions.triggerOptions; - EXPECT_EQ(readTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion); - EXPECT_TRUE(readTriggerOptions.motionZones.HasValue()); - EXPECT_FALSE(readTriggerOptions.motionZones.Value().IsNull()); + std::string findUrlStr(findTransportOptions.url.data(), findTransportOptions.url.size()); + EXPECT_EQ(findUrlStr, "rtsp://192.168.1.100:554/modify-stream"); - DataModel::DecodableList & readMotionZonesList = - readTriggerOptions.motionZones.Value().Value(); + Structs::TransportTriggerOptionsStruct::DecodableType findTriggerOptions = findTransportOptions.triggerOptions; + EXPECT_EQ(findTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion); + EXPECT_TRUE(findTriggerOptions.motionZones.HasValue()); + EXPECT_FALSE(findTriggerOptions.motionZones.Value().IsNull()); - auto readMotionZonesIter = readMotionZonesList.begin(); + DataModel::DecodableList findMotionZonesList = + findTriggerOptions.motionZones.Value().Value(); - EXPECT_TRUE(readMotionZonesIter.Next()); - const auto & decodedZone1 = readMotionZonesIter.GetValue(); + auto findMotionZonesIter = findMotionZonesList.begin(); + + EXPECT_TRUE(findMotionZonesIter.Next()); + const auto & decodedZone1 = findMotionZonesIter.GetValue(); EXPECT_TRUE(!decodedZone1.zone.IsNull()); - EXPECT_EQ(decodedZone1.zone.Value(), 1); + EXPECT_EQ(decodedZone1.zone.Value(), 7); EXPECT_TRUE(decodedZone1.sensitivity.HasValue()); - EXPECT_EQ(decodedZone1.sensitivity.Value(), 5); + EXPECT_EQ(decodedZone1.sensitivity.Value(), 8); - EXPECT_TRUE(readMotionZonesIter.Next()); - const auto & decodedZone2 = readMotionZonesIter.GetValue(); + EXPECT_TRUE(findMotionZonesIter.Next()); + const auto & decodedZone2 = findMotionZonesIter.GetValue(); EXPECT_TRUE(!decodedZone2.zone.IsNull()); - EXPECT_EQ(decodedZone2.zone.Value(), 2); + EXPECT_EQ(decodedZone2.zone.Value(), 9); EXPECT_TRUE(decodedZone2.sensitivity.HasValue()); - EXPECT_EQ(decodedZone2.sensitivity.Value(), 10); + EXPECT_EQ(decodedZone2.sensitivity.Value(), 6); // Should be no more entries - EXPECT_FALSE(readMotionZonesIter.Next()); + EXPECT_FALSE(findMotionZonesIter.Next()); - EXPECT_FALSE(readTriggerOptions.motionSensitivity.HasValue()); - EXPECT_TRUE(readTriggerOptions.maxPreRollLen.HasValue()); - EXPECT_EQ(readTriggerOptions.maxPreRollLen.Value(), 1000); + EXPECT_FALSE(findTriggerOptions.motionSensitivity.HasValue()); + EXPECT_TRUE(findTriggerOptions.maxPreRollLen.HasValue()); + EXPECT_EQ(findTriggerOptions.maxPreRollLen.Value(), 1000); - EXPECT_TRUE(readTriggerOptions.motionTimeControl.HasValue()); - Structs::TransportMotionTriggerTimeControlStruct::DecodableType readMotionTimeControl = - readTriggerOptions.motionTimeControl.Value(); - EXPECT_EQ(readMotionTimeControl.initialDuration, 5000); - EXPECT_EQ(readMotionTimeControl.augmentationDuration, 2000); - EXPECT_EQ(readMotionTimeControl.maxDuration, (uint32_t) 30000); - EXPECT_EQ(readMotionTimeControl.blindDuration, 1000); + EXPECT_TRUE(findTriggerOptions.motionTimeControl.HasValue()); + Structs::TransportMotionTriggerTimeControlStruct::DecodableType findMotionTimeControl = + findTriggerOptions.motionTimeControl.Value(); + EXPECT_EQ(findMotionTimeControl.initialDuration, 1000); + EXPECT_EQ(findMotionTimeControl.augmentationDuration, 1000); + EXPECT_EQ(findMotionTimeControl.maxDuration, (uint32_t) 10000); + EXPECT_EQ(findMotionTimeControl.blindDuration, 1000); - EXPECT_EQ(readTransportOptions.ingestMethod, IngestMethodsEnum::kCMAFIngest); + EXPECT_EQ(findTransportOptions.ingestMethod, IngestMethodsEnum::kCMAFIngest); - Structs::ContainerOptionsStruct::DecodableType readContainerOptions = readTransportOptions.containerOptions; - EXPECT_EQ(readContainerOptions.containerType, ContainerFormatEnum::kCmaf); - EXPECT_TRUE(readContainerOptions.CMAFContainerOptions.HasValue()); - Structs::CMAFContainerOptionsStruct::Type readCMAFContainerOptions = readContainerOptions.CMAFContainerOptions.Value(); - EXPECT_EQ(readCMAFContainerOptions.chunkDuration, 1000); - EXPECT_FALSE(readCMAFContainerOptions.metadataEnabled.HasValue()); + Structs::ContainerOptionsStruct::DecodableType findContainerOptions = findTransportOptions.containerOptions; + EXPECT_EQ(findContainerOptions.containerType, ContainerFormatEnum::kCmaf); + EXPECT_TRUE(findContainerOptions.CMAFContainerOptions.HasValue()); + Structs::CMAFContainerOptionsStruct::Type findCMAFContainerOptions = findContainerOptions.CMAFContainerOptions.Value(); + EXPECT_EQ(findCMAFContainerOptions.chunkDuration, 500); + EXPECT_FALSE(findCMAFContainerOptions.metadataEnabled.HasValue()); - std::string cencKeyStr(readCMAFContainerOptions.CENCKey.Value().data(), - readCMAFContainerOptions.CENCKey.Value().data() + readCMAFContainerOptions.CENCKey.Value().size()); + std::string cencKeyStr(findCMAFContainerOptions.CENCKey.Value().data(), + findCMAFContainerOptions.CENCKey.Value().data() + findCMAFContainerOptions.CENCKey.Value().size()); - EXPECT_EQ(cencKeyStr, "deadbeefdeadbeef"); + EXPECT_EQ(cencKeyStr, "ABCDEF1234567890"); - std::string cencKeyIDStr(readCMAFContainerOptions.CENCKeyID.Value().data(), - readCMAFContainerOptions.CENCKeyID.Value().data() + readCMAFContainerOptions.CENCKeyID.Value().size()); + std::string cencKeyIDStr(findCMAFContainerOptions.CENCKeyID.Value().data(), + findCMAFContainerOptions.CENCKeyID.Value().data() + findCMAFContainerOptions.CENCKeyID.Value().size()); - EXPECT_EQ(cencKeyIDStr, "dadabeefdadabeef"); + EXPECT_EQ(cencKeyIDStr, "ABCDEF1234567890"); + + EXPECT_FALSE(iter.Next()); } -TEST_F(MockEventLogging, ManuallyTriggerTransport) +TEST_F(MockEventLogging, Test_AllocateTransport_SetTransportStatus_ManuallyTriggerTransport) { - PushAvStreamTransportServer server(1, BitFlags(1)); - TestPushAVStreamTransportDelegateImpl mockDelegate; - server.GetLogic().SetDelegate(1, &mockDelegate); + std::string cencKey = "1234567890ABCDEF"; + std::string cencKeyID = "1234567890ABCDEF"; - // Create CMAFContainerOptionsStruct object CMAFContainerOptionsStruct cmafContainerOptions; + ContainerOptionsStruct containerOptions; + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + std::vector mTransportZoneOptions; + TransportTriggerOptionsDecodableStruct triggerOptions; + + std::string url = "rtsp://192.168.1.100:554/stream"; + TransportOptionsDecodableStruct transportOptions; + + uint8_t tlvBuffer[512]; + Structs::TransportZoneOptionsStruct::Type zone1; + Structs::TransportZoneOptionsStruct::Type zone2; + DataModel::DecodableList decodedList; + + // Create CMAFContainerOptionsStruct object cmafContainerOptions.chunkDuration = 1000; cmafContainerOptions.metadataEnabled.ClearValue(); - std::string cencKey = "deadbeefdeadbeef"; - std::string cencKeyID = "dadabeefdadabeef"; + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); // Create ContainerOptionsStruct object - ContainerOptionsStruct containerOptions; containerOptions.containerType = ContainerFormatEnum::kCmaf; containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); // Create a TransportMotionTriggerTimeControlStruct object - TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; - motionTimeControl.initialDuration = 5000; motionTimeControl.augmentationDuration = 2000; motionTimeControl.maxDuration = 30000; motionTimeControl.blindDuration = 1000; - TransportTriggerOptionsDecodableStruct triggerOptions; triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; // Create transport zone options structs - Structs::TransportZoneOptionsStruct::Type zone1; zone1.zone.SetNonNull(1); zone1.sensitivity.SetValue(5); - Structs::TransportZoneOptionsStruct::Type zone2; zone2.zone.SetNonNull(2); zone2.sensitivity.SetValue(10); // Encode them into a TLV buffer - uint8_t tlvBuffer[512]; TLV::TLVWriter writer; writer.Init(tlvBuffer, sizeof(tlvBuffer)); @@ -1099,57 +1256,70 @@ TEST_F(MockEventLogging, ManuallyTriggerTransport) size_t encodedLen = writer.GetLengthWritten(); // Decode the TLV into a DecodableList - TLV::TLVReader reader; - reader.Init(tlvBuffer, static_cast(encodedLen)); - err = reader.Next(); + TLV::TLVReader motionZonesReader; + motionZonesReader.Init(tlvBuffer, static_cast(encodedLen)); + err = motionZonesReader.Next(); EXPECT_EQ(err, CHIP_NO_ERROR); - DataModel::DecodableList decodedList; - err = decodedList.Decode(reader); + err = decodedList.Decode(motionZonesReader); EXPECT_EQ(err, CHIP_NO_ERROR); triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.ClearValue(); triggerOptions.motionTimeControl.SetValue(motionTimeControl); triggerOptions.maxPreRollLen.SetValue(1000); // Create TransportOptionsStruct object - TransportOptionsDecodableStruct transportOptions; transportOptions.streamUsage = StreamUsageEnum::kAnalysis; transportOptions.videoStreamID.SetValue(1); transportOptions.audioStreamID.SetValue(2); transportOptions.endpointID = 1; - std::string url = "rtsp://192.168.1.100:554/stream"; transportOptions.url = Span(url.data(), url.size()); transportOptions.triggerOptions = triggerOptions; - transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; transportOptions.containerOptions = containerOptions; transportOptions.expiryTime.ClearValue(); + PushAvStreamTransportServer server(1, BitFlags(1)); + TestPushAVStreamTransportDelegateImpl mockDelegate; + MockCommandHandler commandHandler; commandHandler.SetFabricIndex(1); - ConcreteCommandPath kCommandPath1{ 1, Clusters::PushAvStreamTransport::Id, Commands::AllocatePushTransport::Id }; - Commands::AllocatePushTransport::DecodableType commandData1; - commandData1.transportOptions = transportOptions; + ConcreteCommandPath kCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::AllocatePushTransport::Id }; + Commands::AllocatePushTransport::DecodableType commandData; + commandData.transportOptions = transportOptions; + + server.GetLogic().SetDelegate(1, &mockDelegate); + EXPECT_EQ(server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath, commandData), std::nullopt); + EXPECT_EQ(server.GetLogic().mCurrentConnections.size(), (size_t) 1); + + uint16_t allocatedConnectionID = server.GetLogic().mCurrentConnections[0].connectionID; + + /* + * Test SetTransportStatus + */ + + MockCommandHandler setCommandHandler; + setCommandHandler.SetFabricIndex(1); + + ConcreteCommandPath kSetCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::SetTransportStatus::Id }; + Commands::SetTransportStatus::DecodableType setCommandData; - server.GetLogic().HandleAllocatePushTransport(commandHandler, kCommandPath1, commandData1); + setCommandData.connectionID.SetNonNull(allocatedConnectionID); + setCommandData.transportStatus = TransportStatusEnum::kActive; + server.GetLogic().HandleSetTransportStatus(setCommandHandler, kSetCommandPath, setCommandData); - ConcreteCommandPath kCommandPath3{ 1, Clusters::PushAvStreamTransport::Id, Commands::SetTransportStatus::Id }; - Commands::SetTransportStatus::DecodableType commandData3; - commandData3.connectionID.SetNonNull(3); - commandData3.transportStatus = TransportStatusEnum::kActive; - server.GetLogic().HandleSetTransportStatus(commandHandler, kCommandPath3, commandData3); + EXPECT_EQ(server.GetLogic().mCurrentConnections[0].transportStatus, TransportStatusEnum::kActive); - // Status status = server.GetLogic().GeneratePushTransportBeginEvent(1, TransportTriggerTypeEnum::kMotion, - // MakeOptional(TriggerActivationReasonEnum::kUserInitiated)); - // EXPECT_EQ(status, Status::Success); + MockCommandHandler triggerCommandHandler; + triggerCommandHandler.SetFabricIndex(1); - ConcreteCommandPath kCommandPath2{ 1, Clusters::PushAvStreamTransport::Id, Commands::ManuallyTriggerTransport::Id }; - Commands::ManuallyTriggerTransport::DecodableType commandData2; - commandData2.connectionID = 3; - commandData2.activationReason = TriggerActivationReasonEnum::kUserInitiated; + ConcreteCommandPath kTriggerCommandPath{ 1, Clusters::PushAvStreamTransport::Id, Commands::ManuallyTriggerTransport::Id }; + Commands::ManuallyTriggerTransport::DecodableType triggerCommandData; + triggerCommandData.connectionID = allocatedConnectionID; + triggerCommandData.activationReason = TriggerActivationReasonEnum::kUserInitiated; - server.GetLogic().HandleManuallyTriggerTransport(commandHandler, kCommandPath2, commandData2); + server.GetLogic().HandleManuallyTriggerTransport(triggerCommandHandler, kTriggerCommandPath, triggerCommandData); chip::app::EventManagement & logMgmt = chip::app::EventManagement::GetInstance(); From 421822e2d731b8dcb2a8711685073e06bc19fee9 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Wed, 2 Jul 2025 23:50:09 +0530 Subject: [PATCH 38/40] Correct namespaces --- .../CodegenIntegration.cpp | 2 +- .../push-av-stream-transport-cluster.cpp | 2 - .../push-av-stream-transport-cluster.h | 4 +- .../push-av-stream-transport-delegate.h | 33 ++++++------ .../push-av-stream-transport-logic.cpp | 15 +++--- .../push-av-stream-transport-logic.h | 51 ++++++++++--------- 6 files changed, 53 insertions(+), 54 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp index 92ff0beece5ccd..4a57d4126c3c5c 100644 --- a/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp +++ b/src/app/clusters/push-av-stream-transport-server/CodegenIntegration.cpp @@ -35,7 +35,7 @@ static constexpr size_t kPushAvStreamTransportFixedClusterCount = static constexpr size_t kPushAvStreamTransportMaxClusterCount = kPushAvStreamTransportFixedClusterCount + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; -LazyRegisteredServerCluster gServers[kPushAvStreamTransportMaxClusterCount]; +LazyRegisteredServerCluster gServers[kPushAvStreamTransportMaxClusterCount]; // Find the 0-based array index corresponding to the given endpoint id. // Log an error if not found. diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp index 77b968c0b1e9fc..32cd102694f241 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.cpp @@ -36,7 +36,6 @@ namespace chip { namespace app { namespace Clusters { -namespace PushAvStreamTransport { namespace { using namespace chip::app::Clusters::PushAvStreamTransport::Commands; @@ -183,7 +182,6 @@ std::optional PushAvStreamTransportServer::Invoke return Status::UnsupportedCommand; } -} // namespace PushAvStreamTransport } // namespace Clusters } // namespace app } // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.h index 8be2ad1abbc4a5..7ed7ceee73bc9f 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-cluster.h @@ -26,7 +26,6 @@ namespace chip { namespace app { namespace Clusters { -namespace PushAvStreamTransport { /// Integration of Push AV Stream Transport logic within the matter data model /// @@ -45,7 +44,7 @@ class PushAvStreamTransportServer : public DefaultServerCluster * * @note The caller must ensure the delegate lives throughout the instance's lifetime */ - PushAvStreamTransportServer(EndpointId aEndpointId, BitFlags aFeatures) : + PushAvStreamTransportServer(EndpointId aEndpointId, BitFlags aFeatures) : DefaultServerCluster({ aEndpointId, PushAvStreamTransport::Id }), mLogic(aEndpointId, aFeatures) {} @@ -77,7 +76,6 @@ class PushAvStreamTransportServer : public DefaultServerCluster CHIP_ERROR ReadAndEncodeSupportedFormats(const AttributeValueEncoder::ListEncodeHelper & encoder); }; -} // namespace PushAvStreamTransport } // namespace Clusters } // namespace app } // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h index de94168aef8ea6..e65804f670a181 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h @@ -27,7 +27,6 @@ namespace chip { namespace app { namespace Clusters { -namespace PushAvStreamTransport { /** * @brief Defines interfaces for implementing application-specific logic for the PushAvStreamTransport Delegate. @@ -58,8 +57,9 @@ class PushAvStreamTransportDelegate * Allocates the transport and maps it to the connectionID. * On Success, TransportConfigurationStruct is sent as response by the server. */ - virtual Protocols::InteractionModel::Status AllocatePushTransport(const TransportOptionsStruct & transportOptions, - const uint16_t connectionID) = 0; + virtual Protocols::InteractionModel::Status + AllocatePushTransport(const PushAvStreamTransport::Structs::TransportOptionsStruct::Type & transportOptions, + const uint16_t connectionID) = 0; /** * @brief Handles stream transport deallocation for the provided connectionID. @@ -82,8 +82,8 @@ class PushAvStreamTransportDelegate * the PushAVStreamTransport Server. The allocated buffers are cleared and reassigned to modified * transportOptions on success of ModifyPushTransport and deallocated on success of DeallocatePushTransport. */ - virtual Protocols::InteractionModel::Status ModifyPushTransport(const uint16_t connectionID, - const TransportOptionsStorage transportOptions) = 0; + virtual Protocols::InteractionModel::Status + ModifyPushTransport(const uint16_t connectionID, const PushAvStreamTransport::TransportOptionsStorage transportOptions) = 0; /** * @brief Handles stream transport status modification. @@ -106,7 +106,7 @@ class PushAvStreamTransportDelegate * - Emits GeneratePushTransportBeginEvent */ virtual Protocols::InteractionModel::Status SetTransportStatus(const std::vector connectionIDList, - TransportStatusEnum transportStatus) = 0; + PushAvStreamTransport::TransportStatusEnum transportStatus) = 0; /** * @brief Requests manual start of the specified push transport. @@ -121,9 +121,9 @@ class PushAvStreamTransportDelegate * The delegate should emit PushTransportEnd Event using GeneratePushTransportEndEvent() * when timeControl values indicate end of transmission. */ - virtual Protocols::InteractionModel::Status - ManuallyTriggerTransport(const uint16_t connectionID, TriggerActivationReasonEnum activationReason, - const Optional & timeControl) = 0; + virtual Protocols::InteractionModel::Status ManuallyTriggerTransport( + const uint16_t connectionID, PushAvStreamTransport::TriggerActivationReasonEnum activationReason, + const Optional & timeControl) = 0; /** * @brief Validates the provided URL. @@ -146,7 +146,8 @@ class PushAvStreamTransportDelegate * camera resources (CPU, memory, network bandwidth). */ virtual Protocols::InteractionModel::Status - ValidateBandwidthLimit(StreamUsageEnum streamUsage, const Optional> & videoStreamId, + ValidateBandwidthLimit(PushAvStreamTransport::StreamUsageEnum streamUsage, + const Optional> & videoStreamId, const Optional> & audioStreamId) = 0; /** @@ -157,7 +158,8 @@ class PushAvStreamTransportDelegate * @return Status::Success and selected videoStreamID if successful; * Status::InvalidStream if no allocated VideoStream exists */ - virtual Protocols::InteractionModel::Status SelectVideoStream(StreamUsageEnum streamUsage, uint16_t & videoStreamId) = 0; + virtual Protocols::InteractionModel::Status SelectVideoStream(PushAvStreamTransport::StreamUsageEnum streamUsage, + uint16_t & videoStreamId) = 0; /** * @brief Assigns existing Audio Stream based on camera's resource management and stream priority policies. @@ -167,7 +169,8 @@ class PushAvStreamTransportDelegate * @return Status::Success and selected audioStreamID if successful; * Status::InvalidStream if no allocated AudioStream exists */ - virtual Protocols::InteractionModel::Status SelectAudioStream(StreamUsageEnum streamUsage, uint16_t & audioStreamId) = 0; + virtual Protocols::InteractionModel::Status SelectAudioStream(PushAvStreamTransport::StreamUsageEnum streamUsage, + uint16_t & audioStreamId) = 0; /** * @brief Validates that the video stream corresponding to videoStreamID is allocated. @@ -193,7 +196,7 @@ class PushAvStreamTransportDelegate * @param connectionID The connectionID of the stream transport to check status * @return busy if transport is uploading, idle otherwise */ - virtual PushAvStreamTransportStatusEnum GetTransportBusyStatus(const uint16_t connectionID) = 0; + virtual PushAvStreamTransport::PushAvStreamTransportStatusEnum GetTransportBusyStatus(const uint16_t connectionID) = 0; /** * @brief Delegate callback for notifying change in an attribute. @@ -216,7 +219,8 @@ class PushAvStreamTransportDelegate * @note Required buffers are managed by TransportConfigurationStorage; * the delegate function must populate the vector correctly. */ - virtual CHIP_ERROR LoadCurrentConnections(std::vector & currentConnections) = 0; + virtual CHIP_ERROR + LoadCurrentConnections(std::vector & currentConnections) = 0; /** * @brief Callback after persistent attributes managed by the Cluster are loaded from Storage. @@ -228,7 +232,6 @@ class PushAvStreamTransportDelegate protected: EndpointId mEndpointId = kInvalidEndpointId; }; -} // namespace PushAvStreamTransport } // namespace Clusters } // namespace app } // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp index e911fe001c4b93..db2562e7a46ac0 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp @@ -44,11 +44,11 @@ using chip::Protocols::InteractionModel::Status; namespace chip { namespace app { namespace Clusters { -namespace PushAvStreamTransport { PushAvStreamTransportServerLogic::PushAvStreamTransportServerLogic(EndpointId aEndpoint, BitFlags aFeatures) : mEndpointId(aEndpoint), mFeatures(aFeatures), - mSupportedFormats{ SupportedFormatStruct{ ContainerFormatEnum::kCmaf, IngestMethodsEnum::kCMAFIngest } } + mSupportedFormats{ PushAvStreamTransport::SupportedFormatStruct{ PushAvStreamTransport::ContainerFormatEnum::kCmaf, + PushAvStreamTransport::IngestMethodsEnum::kCMAFIngest } } {} PushAvStreamTransportServerLogic::~PushAvStreamTransportServerLogic() @@ -423,7 +423,7 @@ Status PushAvStreamTransportServerLogic::ValidateIncomingTransportOptions( ingestMethod != IngestMethodsEnum::kUnknownEnumValue, Status::ConstraintError, ChipLogError(Zcl, "Transport Options verification from command data[ep=%d]: Invalid Ingest Method ", mEndpointId)); - const ContainerOptionsStruct & containerOptions = transportOptions.containerOptions; + const Structs::ContainerOptionsStruct::Type & containerOptions = transportOptions.containerOptions; VerifyOrReturnValue( containerOptions.containerType != ContainerFormatEnum::kUnknownEnumValue, Status::ConstraintError, @@ -520,7 +520,7 @@ PushAvStreamTransportServerLogic::HandleAllocatePushTransport(CommandHandler & h IngestMethodsEnum ingestMethod = commandData.transportOptions.ingestMethod; - ContainerOptionsStruct containerOptions = commandData.transportOptions.containerOptions; + Structs::ContainerOptionsStruct::Type containerOptions = commandData.transportOptions.containerOptions; bool isFormatSupported = false; @@ -953,7 +953,7 @@ PushAvStreamTransportServerLogic::HandleFindTransport(CommandHandler & handler, Optional> connectionID = commandData.connectionID; - std::vector transportConfigurations; + std::vector transportConfigurations; if (!connectionID.HasValue() || connectionID.Value().IsNull()) { @@ -992,8 +992,8 @@ PushAvStreamTransportServerLogic::HandleFindTransport(CommandHandler & handler, handler.AddStatus(commandPath, Status::NotFound); } - response.transportConfigurations = - DataModel::List(transportConfigurations.data(), transportConfigurations.size()); + response.transportConfigurations = DataModel::List( + transportConfigurations.data(), transportConfigurations.size()); handler.AddResponse(commandPath, response); @@ -1043,7 +1043,6 @@ Status PushAvStreamTransportServerLogic::GeneratePushTransportEndEvent(const uin return Status::Success; } -} // namespace PushAvStreamTransport } // namespace Clusters } // namespace app } // namespace chip diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h index de8fdb18756834..88d60638f80bfd 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h @@ -13,12 +13,11 @@ namespace chip { namespace app { namespace Clusters { -namespace PushAvStreamTransport { class PushAvStreamTransportServerLogic { public: - PushAvStreamTransportServerLogic(EndpointId aEndpoint, BitFlags aFeatures); + PushAvStreamTransportServerLogic(EndpointId aEndpoint, BitFlags aFeatures); ~PushAvStreamTransportServerLogic(); void SetDelegate(EndpointId aEndpoint, PushAvStreamTransportDelegate * delegate) @@ -48,52 +47,52 @@ class PushAvStreamTransportServerLogic std::vector> mTimerContexts; - BitFlags mFeatures; + BitFlags mFeatures; - std::vector mSupportedFormats; + std::vector mSupportedFormats; - std::vector mCurrentConnections; + std::vector mCurrentConnections; CHIP_ERROR Init(); void Shutdown(); - bool HasFeature(Feature feature) const; + bool HasFeature(PushAvStreamTransport::Feature feature) const; - Protocols::InteractionModel::Status - ValidateIncomingTransportOptions(const Structs::TransportOptionsStruct::DecodableType & transportOptions); + Protocols::InteractionModel::Status ValidateIncomingTransportOptions( + const PushAvStreamTransport::Structs::TransportOptionsStruct::DecodableType & transportOptions); std::optional HandleAllocatePushTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, - const Commands::AllocatePushTransport::DecodableType & commandData); + const PushAvStreamTransport::Commands::AllocatePushTransport::DecodableType & commandData); std::optional HandleDeallocatePushTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, - const Commands::DeallocatePushTransport::DecodableType & commandData); + const PushAvStreamTransport::Commands::DeallocatePushTransport::DecodableType & commandData); std::optional HandleModifyPushTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, - const Commands::ModifyPushTransport::DecodableType & commandData); + const PushAvStreamTransport::Commands::ModifyPushTransport::DecodableType & commandData); std::optional HandleSetTransportStatus(CommandHandler & handler, const ConcreteCommandPath & commandPath, - const Commands::SetTransportStatus::DecodableType & commandData); + const PushAvStreamTransport::Commands::SetTransportStatus::DecodableType & commandData); std::optional HandleManuallyTriggerTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, - const Commands::ManuallyTriggerTransport::DecodableType & commandData); + const PushAvStreamTransport::Commands::ManuallyTriggerTransport::DecodableType & commandData); - std::optional HandleFindTransport(CommandHandler & handler, - const ConcreteCommandPath & commandPath, - const Commands::FindTransport::DecodableType & commandData); + std::optional + HandleFindTransport(CommandHandler & handler, const ConcreteCommandPath & commandPath, + const PushAvStreamTransport::Commands::FindTransport::DecodableType & commandData); // Send Push AV Stream Transport events Protocols::InteractionModel::Status - GeneratePushTransportBeginEvent(const uint16_t connectionID, const TransportTriggerTypeEnum triggerType, - const Optional activationReason); - Protocols::InteractionModel::Status GeneratePushTransportEndEvent(const uint16_t connectionID, - const TransportTriggerTypeEnum triggerType, - const Optional activationReason); + GeneratePushTransportBeginEvent(const uint16_t connectionID, const PushAvStreamTransport::TransportTriggerTypeEnum triggerType, + const Optional activationReason); + Protocols::InteractionModel::Status + GeneratePushTransportEndEvent(const uint16_t connectionID, const PushAvStreamTransport::TransportTriggerTypeEnum triggerType, + const Optional activationReason); private: PushAvStreamTransportDelegate * mDelegate = nullptr; @@ -110,12 +109,14 @@ class PushAvStreamTransportServerLogic // Helper functions uint16_t GenerateConnectionID(); - TransportConfigurationStorage * FindStreamTransportConnection(const uint16_t connectionID); + PushAvStreamTransport::TransportConfigurationStorage * FindStreamTransportConnection(const uint16_t connectionID); - TransportConfigurationStorage * FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, FabricIndex fabricIndex); + PushAvStreamTransport::TransportConfigurationStorage * FindStreamTransportConnectionWithinFabric(const uint16_t connectionID, + FabricIndex fabricIndex); // Add/Remove Management functions for transport - UpsertResultEnum UpsertStreamTransportConnection(const TransportConfigurationStorage & transportConfiguration); + UpsertResultEnum + UpsertStreamTransportConnection(const PushAvStreamTransport::TransportConfigurationStorage & transportConfiguration); void RemoveStreamTransportConnection(const uint16_t connectionID); @@ -133,7 +134,7 @@ class PushAvStreamTransportServerLogic */ CHIP_ERROR ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec); }; -} // namespace PushAvStreamTransport + } // namespace Clusters } // namespace app } // namespace chip From ee3de07644305ae0dbd2f75e528e90fdcc2d0044 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 4 Jul 2025 05:10:45 +0530 Subject: [PATCH 39/40] Update PushAVStreamTransport Storage tests --- .../push-av-stream-transport-storage.h | 62 +--- .../TestPushAVStreamTransportStorage.cpp | 344 +++++++++++++++++- 2 files changed, 351 insertions(+), 55 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h index 6896d424d41ba4..d2465ee384da06 100644 --- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h +++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-storage.h @@ -110,7 +110,7 @@ struct TransportTriggerOptionsStorage : public TransportTriggerOptionsStruct return *this; } - TransportTriggerOptionsStorage(Structs::TransportTriggerOptionsStruct::DecodableType & triggerOptions) + TransportTriggerOptionsStorage(const Structs::TransportTriggerOptionsStruct::DecodableType & triggerOptions) { *this = triggerOptions; } @@ -160,50 +160,31 @@ struct CMAFContainerOptionsStorage : public CMAFContainerOptionsStruct CopySpanToMutableSpan(aCMAFContainerOptions.CENCKey.Value(), CENCKeyBuffer); CENCKey.SetValue(CENCKeyBuffer); } - - metadataEnabled = aCMAFContainerOptions.metadataEnabled; - - if (CENCKeyID.HasValue()) - { - MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); - // ValidateIncomingTransportOptions() function already checked the CENCKeyID length - CopySpanToMutableSpan(aCMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); - CENCKeyID.SetValue(CENCKeyIDBuffer); - } - - return *this; - } - - CMAFContainerOptionsStorage(Structs::CMAFContainerOptionsStruct::Type & CMAFContainerOptions) - { - - chunkDuration = CMAFContainerOptions.chunkDuration; - - if (CMAFContainerOptions.CENCKey.HasValue()) - { - MutableByteSpan CENCKeyBuffer(mCENCKeyBuffer); - // ValidateIncomingTransportOptions() function already checked the CENCKeyID length - CopySpanToMutableSpan(CMAFContainerOptions.CENCKey.Value(), CENCKeyBuffer); - CENCKey.SetValue(CENCKeyBuffer); - } else { CENCKey.ClearValue(); } - metadataEnabled = CMAFContainerOptions.metadataEnabled; + metadataEnabled = aCMAFContainerOptions.metadataEnabled; - if (CMAFContainerOptions.CENCKey.HasValue()) + if (CENCKeyID.HasValue()) { MutableByteSpan CENCKeyIDBuffer(mCENCKeyIDBuffer); // ValidateIncomingTransportOptions() function already checked the CENCKeyID length - CopySpanToMutableSpan(CMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); + CopySpanToMutableSpan(aCMAFContainerOptions.CENCKeyID.Value(), CENCKeyIDBuffer); CENCKeyID.SetValue(CENCKeyIDBuffer); } else { CENCKeyID.ClearValue(); } + + return *this; + } + + CMAFContainerOptionsStorage(const Structs::CMAFContainerOptionsStruct::Type & CMAFContainerOptions) + { + *this = CMAFContainerOptions; } private: @@ -251,20 +232,7 @@ struct ContainerOptionsStorage : public ContainerOptionsStruct return *this; } - ContainerOptionsStorage(Structs::ContainerOptionsStruct::DecodableType & containerOptions) - { - containerType = containerOptions.containerType; - - if (containerType == ContainerFormatEnum::kCmaf) - { - mCMAFContainerStorage = containerOptions.CMAFContainerOptions.Value(); - CMAFContainerOptions.SetValue(mCMAFContainerStorage); - } - else - { - CMAFContainerOptions.ClearValue(); - } - } + ContainerOptionsStorage(const Structs::ContainerOptionsStruct::DecodableType & containerOptions) { *this = containerOptions; } private: CMAFContainerOptionsStorage mCMAFContainerStorage; @@ -310,7 +278,7 @@ struct TransportOptionsStorage : public TransportOptionsStruct return *this; } - TransportOptionsStorage(Structs::TransportOptionsStruct::DecodableType transportOptions) + TransportOptionsStorage & operator=(const Structs::TransportOptionsStruct::DecodableType & transportOptions) { streamUsage = transportOptions.streamUsage; videoStreamID = transportOptions.videoStreamID; @@ -331,8 +299,12 @@ struct TransportOptionsStorage : public TransportOptionsStruct containerOptions = mContainerOptionsStorage; expiryTime = transportOptions.expiryTime; + + return *this; } + TransportOptionsStorage(const Structs::TransportOptionsStruct::DecodableType & transportOptions) { *this = transportOptions; } + private: char mUrlBuffer[kMaxUrlLength]; TransportTriggerOptionsStorage mTriggerOptionsStorage; diff --git a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp index 226daebbd26975..4fddb309101fd2 100644 --- a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp +++ b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp @@ -39,13 +39,16 @@ class TestPushAVStreamTransportStorage : public ::testing::Test static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } }; -uint8_t tlvBuffer[512]; -TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; -TransportTriggerOptionsDecodableStruct triggerOptions; -DataModel::DecodableList decodedList; - -TEST_F(TestPushAVStreamTransportStorage, TestSetUpTriggerOptions) +TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) { + uint8_t tlvBuffer[512]; + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + TransportTriggerOptionsDecodableStruct triggerOptions; + DataModel::DecodableList decodedList; + TransportOptionsDecodableStruct transportOptions; + + /*Test TransportOptionsStorage*/ + // Create a TransportMotionTriggerTimeControlStruct object motionTimeControl.initialDuration = 5000; motionTimeControl.augmentationDuration = 2000; @@ -94,14 +97,335 @@ TEST_F(TestPushAVStreamTransportStorage, TestSetUpTriggerOptions) EXPECT_EQ(err, CHIP_NO_ERROR); triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); - triggerOptions.motionSensitivity.ClearValue(); + triggerOptions.motionSensitivity.SetValue(DataModel::MakeNullable((uint8_t) 5)); triggerOptions.motionTimeControl.SetValue(motionTimeControl); triggerOptions.maxPreRollLen.SetValue(1000); -} -TEST_F(TestPushAVStreamTransportStorage, TestTransportTriggerOptionsStorage) -{ TransportTriggerOptionsStorage transportTriggerOptionsStorage(triggerOptions); + + EXPECT_EQ(transportTriggerOptionsStorage.triggerType, TransportTriggerTypeEnum::kMotion); + + DataModel::List & motionZonesList = + transportTriggerOptionsStorage.motionZones.Value().Value(); + + EXPECT_EQ(motionZonesList.size(), (size_t) 2); + + Structs::TransportZoneOptionsStruct::Type motionZone1 = motionZonesList[0]; + Structs::TransportZoneOptionsStruct::Type motionZone2 = motionZonesList[1]; + + EXPECT_FALSE(motionZone1.zone.IsNull()); + EXPECT_EQ(motionZone1.zone.Value(), 1); + EXPECT_TRUE(motionZone1.sensitivity.HasValue()); + EXPECT_EQ(motionZone1.sensitivity.Value(), 5); + + EXPECT_FALSE(motionZone2.zone.IsNull()); + EXPECT_EQ(motionZone2.zone.Value(), 2); + EXPECT_TRUE(motionZone2.sensitivity.HasValue()); + EXPECT_EQ(motionZone2.sensitivity.Value(), 10); + + EXPECT_EQ(transportTriggerOptionsStorage.motionSensitivity.Value().Value(), (uint8_t) 5); + + EXPECT_EQ(transportTriggerOptionsStorage.motionTimeControl.Value().initialDuration, 5000); + EXPECT_EQ(transportTriggerOptionsStorage.motionTimeControl.Value().augmentationDuration, 2000); + EXPECT_EQ(transportTriggerOptionsStorage.motionTimeControl.Value().maxDuration, (uint32_t) 30000); + EXPECT_EQ(transportTriggerOptionsStorage.motionTimeControl.Value().blindDuration, 1000); + EXPECT_EQ(transportTriggerOptionsStorage.maxPreRollLen.Value(), 1000); + + TransportTriggerOptionsStorage transportTriggerOptionsStorageCopy(transportTriggerOptionsStorage); + + EXPECT_EQ(transportTriggerOptionsStorageCopy.triggerType, TransportTriggerTypeEnum::kMotion); + + DataModel::List & motionZonesListCopy = + transportTriggerOptionsStorageCopy.motionZones.Value().Value(); + + EXPECT_EQ(motionZonesListCopy.size(), (size_t) 2); + + Structs::TransportZoneOptionsStruct::Type motionZone1Copy = motionZonesListCopy[0]; + Structs::TransportZoneOptionsStruct::Type motionZone2Copy = motionZonesListCopy[1]; + + EXPECT_FALSE(motionZone1Copy.zone.IsNull()); + EXPECT_EQ(motionZone1Copy.zone.Value(), 1); + EXPECT_TRUE(motionZone1Copy.sensitivity.HasValue()); + EXPECT_EQ(motionZone1Copy.sensitivity.Value(), 5); + + EXPECT_FALSE(motionZone2Copy.zone.IsNull()); + EXPECT_EQ(motionZone2Copy.zone.Value(), 2); + EXPECT_TRUE(motionZone2Copy.sensitivity.HasValue()); + EXPECT_EQ(motionZone2Copy.sensitivity.Value(), 10); + + EXPECT_EQ(transportTriggerOptionsStorageCopy.motionSensitivity.Value().Value(), (uint8_t) 5); + + EXPECT_EQ(transportTriggerOptionsStorageCopy.motionTimeControl.Value().initialDuration, 5000); + EXPECT_EQ(transportTriggerOptionsStorageCopy.motionTimeControl.Value().augmentationDuration, 2000); + EXPECT_EQ(transportTriggerOptionsStorageCopy.motionTimeControl.Value().maxDuration, (uint32_t) 30000); + EXPECT_EQ(transportTriggerOptionsStorageCopy.motionTimeControl.Value().blindDuration, 1000); + + // Accessing buffer using base struct + TransportTriggerOptionsStruct BaseTransportTriggerOptions = + static_cast(transportTriggerOptionsStorageCopy); + + DataModel::List & motionZonesListBase = + BaseTransportTriggerOptions.motionZones.Value().Value(); + + EXPECT_EQ(motionZonesListBase.size(), (size_t) 2); + + Structs::TransportZoneOptionsStruct::Type motionZone1Base = motionZonesListBase[0]; + Structs::TransportZoneOptionsStruct::Type motionZone2Base = motionZonesListBase[1]; + + EXPECT_FALSE(motionZone1Base.zone.IsNull()); + EXPECT_EQ(motionZone1Base.zone.Value(), 1); + EXPECT_TRUE(motionZone1Base.sensitivity.HasValue()); + EXPECT_EQ(motionZone1Base.sensitivity.Value(), 5); + + EXPECT_FALSE(motionZone2Base.zone.IsNull()); + EXPECT_EQ(motionZone2Base.zone.Value(), 2); + EXPECT_TRUE(motionZone2Base.sensitivity.HasValue()); + EXPECT_EQ(motionZone2Base.sensitivity.Value(), 10); + + /*Test CMAFContainerOptionsStorage*/ + + CMAFContainerOptionsStruct cmafContainerOptions; + + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.SetValue(true); + + std::string cencKey = "1234567890ABCDEF"; + std::string cencKeyID = "1234567890ABCDEF"; + + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); + + CMAFContainerOptionsStorage cmafContainerOptionsStorage(cmafContainerOptions); + + EXPECT_EQ(cmafContainerOptionsStorage.chunkDuration, 1000); + EXPECT_TRUE(cmafContainerOptionsStorage.metadataEnabled.HasValue()); + EXPECT_TRUE(cmafContainerOptionsStorage.metadataEnabled.Value()); + + std::string cencKeyStr(cmafContainerOptionsStorage.CENCKey.Value().data(), + cmafContainerOptionsStorage.CENCKey.Value().data() + cmafContainerOptionsStorage.CENCKey.Value().size()); + + EXPECT_EQ(cencKeyStr, cencKey); + + std::string cencKeyIDStr(cmafContainerOptionsStorage.CENCKeyID.Value().data(), + cmafContainerOptionsStorage.CENCKeyID.Value().data() + + cmafContainerOptionsStorage.CENCKeyID.Value().size()); + + EXPECT_EQ(cencKeyIDStr, cencKeyID); + + CMAFContainerOptionsStorage cmafContainerOptionsStorageCopy(cmafContainerOptionsStorage); + + EXPECT_EQ(cmafContainerOptionsStorageCopy.chunkDuration, 1000); + EXPECT_TRUE(cmafContainerOptionsStorageCopy.metadataEnabled.HasValue()); + EXPECT_TRUE(cmafContainerOptionsStorageCopy.metadataEnabled.Value()); + + std::string cencKeyStrCopy(cmafContainerOptionsStorageCopy.CENCKey.Value().data(), + cmafContainerOptionsStorageCopy.CENCKey.Value().data() + + cmafContainerOptionsStorageCopy.CENCKey.Value().size()); + + EXPECT_EQ(cencKeyStrCopy, cencKey); + + std::string cencKeyIDStrCopy(cmafContainerOptionsStorageCopy.CENCKeyID.Value().data(), + cmafContainerOptionsStorageCopy.CENCKeyID.Value().data() + + cmafContainerOptionsStorageCopy.CENCKeyID.Value().size()); + + EXPECT_EQ(cencKeyIDStrCopy, cencKeyID); + + // Accessing buffer using base struct + + CMAFContainerOptionsStruct BaseCMAFContainerOptions = static_cast(cmafContainerOptionsStorageCopy); + + EXPECT_EQ(BaseCMAFContainerOptions.chunkDuration, 1000); + EXPECT_TRUE(BaseCMAFContainerOptions.metadataEnabled.HasValue()); + EXPECT_TRUE(BaseCMAFContainerOptions.metadataEnabled.Value()); + + std::string cencKeyStrBase(BaseCMAFContainerOptions.CENCKey.Value().data(), + BaseCMAFContainerOptions.CENCKey.Value().data() + BaseCMAFContainerOptions.CENCKey.Value().size()); + + EXPECT_EQ(cencKeyStrBase, cencKey); + + std::string cencKeyIDStrBase(BaseCMAFContainerOptions.CENCKeyID.Value().data(), + BaseCMAFContainerOptions.CENCKeyID.Value().data() + + BaseCMAFContainerOptions.CENCKeyID.Value().size()); + + EXPECT_EQ(cencKeyIDStrBase, cencKeyID); + + /*Test ContainerOptionsStorage*/ + + // Using Type as ContainerOptionsStruct::DecodableType == ContainerOptionsStruct::Type + + ContainerOptionsStruct containerOptions; + + containerOptions.containerType = ContainerFormatEnum::kCmaf; + containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); + + ContainerOptionsStorage containerOptionsStorage(containerOptions); + + EXPECT_EQ(containerOptionsStorage.containerType, ContainerFormatEnum::kCmaf); + EXPECT_TRUE(containerOptionsStorage.CMAFContainerOptions.HasValue()); + EXPECT_EQ(containerOptionsStorage.CMAFContainerOptions.Value().chunkDuration, 1000); + EXPECT_TRUE(containerOptionsStorage.CMAFContainerOptions.Value().metadataEnabled.HasValue()); + EXPECT_TRUE(containerOptionsStorage.CMAFContainerOptions.Value().metadataEnabled.Value()); + + std::string cencKeyStrContainer(containerOptionsStorage.CMAFContainerOptions.Value().CENCKey.Value().data(), + containerOptionsStorage.CMAFContainerOptions.Value().CENCKey.Value().data() + + containerOptionsStorage.CMAFContainerOptions.Value().CENCKey.Value().size()); + + EXPECT_EQ(cencKeyStrContainer, cencKey); + + std::string cencKeyIDStrContainer(containerOptionsStorage.CMAFContainerOptions.Value().CENCKeyID.Value().data(), + containerOptionsStorage.CMAFContainerOptions.Value().CENCKeyID.Value().data() + + containerOptionsStorage.CMAFContainerOptions.Value().CENCKeyID.Value().size()); + + EXPECT_EQ(cencKeyIDStrContainer, cencKeyID); + + ContainerOptionsStorage containerOptionsStorageCopy(containerOptionsStorage); + + EXPECT_EQ(containerOptionsStorageCopy.containerType, ContainerFormatEnum::kCmaf); + EXPECT_TRUE(containerOptionsStorageCopy.CMAFContainerOptions.HasValue()); + EXPECT_EQ(containerOptionsStorageCopy.CMAFContainerOptions.Value().chunkDuration, 1000); + EXPECT_TRUE(containerOptionsStorageCopy.CMAFContainerOptions.Value().metadataEnabled.HasValue()); + EXPECT_TRUE(containerOptionsStorageCopy.CMAFContainerOptions.Value().metadataEnabled.Value()); + + std::string cencKeyStrContainerCopy(containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKey.Value().data(), + containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKey.Value().data() + + containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKey.Value().size()); + + EXPECT_EQ(cencKeyStrContainerCopy, cencKey); + + std::string cencKeyIDStrContainerCopy(containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKeyID.Value().data(), + containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKeyID.Value().data() + + containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKeyID.Value().size()); + + EXPECT_EQ(cencKeyIDStrContainerCopy, cencKeyID); + + // Accessing buffer using base struct + + ContainerOptionsStruct BaseContainerOptions = static_cast(containerOptionsStorage); + + EXPECT_EQ(BaseContainerOptions.containerType, ContainerFormatEnum::kCmaf); + EXPECT_TRUE(BaseContainerOptions.CMAFContainerOptions.HasValue()); + EXPECT_EQ(BaseContainerOptions.CMAFContainerOptions.Value().chunkDuration, 1000); + EXPECT_TRUE(BaseContainerOptions.CMAFContainerOptions.Value().metadataEnabled.HasValue()); + EXPECT_TRUE(BaseContainerOptions.CMAFContainerOptions.Value().metadataEnabled.Value()); + + std::string containerCENCKeyStrBase(BaseContainerOptions.CMAFContainerOptions.Value().CENCKey.Value().data(), + BaseContainerOptions.CMAFContainerOptions.Value().CENCKey.Value().data() + + BaseContainerOptions.CMAFContainerOptions.Value().CENCKey.Value().size()); + + EXPECT_EQ(containerCENCKeyStrBase, cencKey); + + std::string containerCENCKeyIDStrBase(BaseContainerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().data(), + BaseContainerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().data() + + BaseContainerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size()); + + EXPECT_EQ(containerCENCKeyIDStrBase, cencKeyID); + + /*Test TransportOptionsStorage*/ + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + + triggerOptions.motionSensitivity.SetValue(DataModel::MakeNullable((uint8_t) 5)); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + transportOptions.streamUsage = StreamUsageEnum::kAnalysis; + transportOptions.videoStreamID.SetValue(1); + transportOptions.audioStreamID.SetValue(2); + transportOptions.endpointID = 1; + std::string url = "rtsp://192.168.1.100:554/stream"; + transportOptions.url = Span(url.data(), url.size()); + transportOptions.triggerOptions = triggerOptions; + transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; + transportOptions.containerOptions = containerOptions; + transportOptions.expiryTime.SetValue(1000); + + TransportOptionsStorage transportOptionsStorage(transportOptions); + + EXPECT_EQ(transportOptionsStorage.streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(transportOptionsStorage.videoStreamID.Value(), (uint16_t) 1); + EXPECT_EQ(transportOptionsStorage.audioStreamID.Value(), (uint16_t) 2); + EXPECT_EQ(transportOptionsStorage.endpointID, 1); + EXPECT_EQ(transportOptionsStorage.url.size(), url.size()); + std::string transportOptionsUrlStr(transportOptionsStorage.url.data(), transportOptionsStorage.url.size()); + EXPECT_EQ(transportOptionsUrlStr, url); + + TransportOptionsStorage transportOptionsStorageCopy(transportOptionsStorage); + + EXPECT_EQ(transportOptionsStorageCopy.streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(transportOptionsStorageCopy.videoStreamID.Value(), (uint16_t) 1); + EXPECT_EQ(transportOptionsStorageCopy.audioStreamID.Value(), (uint16_t) 2); + EXPECT_EQ(transportOptionsStorageCopy.endpointID, 1); + EXPECT_EQ(transportOptionsStorageCopy.url.size(), url.size()); + std::string transportOptionsUrlStrCopy(transportOptionsStorageCopy.url.data(), transportOptionsStorageCopy.url.size()); + EXPECT_EQ(transportOptionsUrlStrCopy, url); + + // Accessing buffer using base struct + + TransportOptionsStruct BaseTransportOptions = static_cast(transportOptionsStorage); + + EXPECT_EQ(BaseTransportOptions.streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(BaseTransportOptions.videoStreamID.Value(), (uint16_t) 1); + EXPECT_EQ(BaseTransportOptions.audioStreamID.Value(), (uint16_t) 2); + EXPECT_EQ(BaseTransportOptions.endpointID, 1); + EXPECT_EQ(BaseTransportOptions.url.size(), url.size()); + std::string transportOptionsUrlStrBase(BaseTransportOptions.url.data(), BaseTransportOptions.url.size()); + EXPECT_EQ(transportOptionsUrlStrBase, url); + + /*Test TransportConfigurationStorage*/ + + std::shared_ptr transportOptionsPtr{ new (std::nothrow) TransportOptionsStorage(transportOptions) }; + + TransportConfigurationStorage transportConfigurationStorage(1, transportOptionsPtr); + + EXPECT_EQ(transportConfigurationStorage.connectionID, 1); + EXPECT_EQ(transportConfigurationStorage.transportStatus, TransportStatusEnum::kInactive); + EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().videoStreamID.Value(), (uint16_t) 1); + EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().audioStreamID.Value(), (uint16_t) 2); + EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().endpointID, 1); + EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().url.size(), url.size()); + std::string transportOptionsUrlStrConfiguration(transportConfigurationStorage.transportOptions.Value().url.data(), + transportConfigurationStorage.transportOptions.Value().url.size()); + EXPECT_EQ(transportOptionsUrlStrConfiguration, url); + + TransportConfigurationStorage transportConfigurationStorageCopy(transportConfigurationStorage); + + EXPECT_EQ(transportConfigurationStorageCopy.connectionID, 1); + EXPECT_EQ(transportConfigurationStorageCopy.transportStatus, TransportStatusEnum::kInactive); + EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().videoStreamID.Value(), (uint16_t) 1); + EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().audioStreamID.Value(), (uint16_t) 2); + EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().endpointID, 1); + EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().url.size(), url.size()); + std::string transportOptionsUrlStrConfigurationCopy(transportConfigurationStorageCopy.transportOptions.Value().url.data(), + transportConfigurationStorageCopy.transportOptions.Value().url.size()); + EXPECT_EQ(transportOptionsUrlStrConfigurationCopy, url); + + // Accessing buffer using base struct + + TransportConfigurationStruct BaseTransportConfiguration = + static_cast(transportConfigurationStorage); + + EXPECT_EQ(BaseTransportConfiguration.connectionID, 1); + EXPECT_EQ(BaseTransportConfiguration.transportStatus, TransportStatusEnum::kInactive); + EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().streamUsage, StreamUsageEnum::kAnalysis); + EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().videoStreamID.Value(), (uint16_t) 1); + EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().audioStreamID.Value(), (uint16_t) 2); + EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().endpointID, 1); + EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().url.size(), url.size()); + std::string transportOptionsUrlStrConfigurationBase(BaseTransportConfiguration.transportOptions.Value().url.data(), + BaseTransportConfiguration.transportOptions.Value().url.size()); + EXPECT_EQ(transportOptionsUrlStrConfigurationBase, url); + + /*Test TransportConfigurationStorage with null transport options*/ + + std::shared_ptr nullTransportOptionsPtr; + + TransportConfigurationStorage transportConfigurationStorageNull(1, nullTransportOptionsPtr); + + EXPECT_EQ(transportConfigurationStorageNull.connectionID, 1); + EXPECT_EQ(transportConfigurationStorageNull.transportStatus, TransportStatusEnum::kInactive); + EXPECT_FALSE(transportConfigurationStorageNull.transportOptions.HasValue()); } } // namespace PushAvStreamTransport From 8c4f86e392dfb9e02b02d3098e70b21d403c0247 Mon Sep 17 00:00:00 2001 From: Sayon Deep Date: Fri, 4 Jul 2025 09:38:04 +0530 Subject: [PATCH 40/40] Split Storage tests to reduce stack usage --- .../TestPushAVStreamTransportStorage.cpp | 262 ++++++++++++++++-- 1 file changed, 233 insertions(+), 29 deletions(-) diff --git a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp index 4fddb309101fd2..db384ba0875269 100644 --- a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp +++ b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportStorage.cpp @@ -39,14 +39,12 @@ class TestPushAVStreamTransportStorage : public ::testing::Test static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } }; -TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) +TEST_F(TestPushAVStreamTransportStorage, TestTransportTriggerOptionsStorage) { uint8_t tlvBuffer[512]; TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; TransportTriggerOptionsDecodableStruct triggerOptions; DataModel::DecodableList decodedList; - TransportOptionsDecodableStruct transportOptions; - /*Test TransportOptionsStorage*/ // Create a TransportMotionTriggerTimeControlStruct object @@ -181,10 +179,12 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) EXPECT_EQ(motionZone2Base.zone.Value(), 2); EXPECT_TRUE(motionZone2Base.sensitivity.HasValue()); EXPECT_EQ(motionZone2Base.sensitivity.Value(), 10); +} - /*Test CMAFContainerOptionsStorage*/ - +TEST_F(TestPushAVStreamTransportStorage, TestCMAFContainerOptionsStorage) +{ CMAFContainerOptionsStruct cmafContainerOptions; + /*Test CMAFContainerOptionsStorage*/ cmafContainerOptions.chunkDuration = 1000; cmafContainerOptions.metadataEnabled.SetValue(true); @@ -197,6 +197,10 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) CMAFContainerOptionsStorage cmafContainerOptionsStorage(cmafContainerOptions); + // Clear the cencKey and cencKeyID strings to test deep copy of cencKey and cencKeyID + cencKey.clear(); + cencKeyID.clear(); + EXPECT_EQ(cmafContainerOptionsStorage.chunkDuration, 1000); EXPECT_TRUE(cmafContainerOptionsStorage.metadataEnabled.HasValue()); EXPECT_TRUE(cmafContainerOptionsStorage.metadataEnabled.Value()); @@ -204,13 +208,13 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) std::string cencKeyStr(cmafContainerOptionsStorage.CENCKey.Value().data(), cmafContainerOptionsStorage.CENCKey.Value().data() + cmafContainerOptionsStorage.CENCKey.Value().size()); - EXPECT_EQ(cencKeyStr, cencKey); + EXPECT_EQ(cencKeyStr, "1234567890ABCDEF"); std::string cencKeyIDStr(cmafContainerOptionsStorage.CENCKeyID.Value().data(), cmafContainerOptionsStorage.CENCKeyID.Value().data() + cmafContainerOptionsStorage.CENCKeyID.Value().size()); - EXPECT_EQ(cencKeyIDStr, cencKeyID); + EXPECT_EQ(cencKeyIDStr, "1234567890ABCDEF"); CMAFContainerOptionsStorage cmafContainerOptionsStorageCopy(cmafContainerOptionsStorage); @@ -222,13 +226,13 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) cmafContainerOptionsStorageCopy.CENCKey.Value().data() + cmafContainerOptionsStorageCopy.CENCKey.Value().size()); - EXPECT_EQ(cencKeyStrCopy, cencKey); + EXPECT_EQ(cencKeyStrCopy, "1234567890ABCDEF"); std::string cencKeyIDStrCopy(cmafContainerOptionsStorageCopy.CENCKeyID.Value().data(), cmafContainerOptionsStorageCopy.CENCKeyID.Value().data() + cmafContainerOptionsStorageCopy.CENCKeyID.Value().size()); - EXPECT_EQ(cencKeyIDStrCopy, cencKeyID); + EXPECT_EQ(cencKeyIDStrCopy, "1234567890ABCDEF"); // Accessing buffer using base struct @@ -241,13 +245,28 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) std::string cencKeyStrBase(BaseCMAFContainerOptions.CENCKey.Value().data(), BaseCMAFContainerOptions.CENCKey.Value().data() + BaseCMAFContainerOptions.CENCKey.Value().size()); - EXPECT_EQ(cencKeyStrBase, cencKey); + EXPECT_EQ(cencKeyStrBase, "1234567890ABCDEF"); std::string cencKeyIDStrBase(BaseCMAFContainerOptions.CENCKeyID.Value().data(), BaseCMAFContainerOptions.CENCKeyID.Value().data() + BaseCMAFContainerOptions.CENCKeyID.Value().size()); - EXPECT_EQ(cencKeyIDStrBase, cencKeyID); + EXPECT_EQ(cencKeyIDStrBase, "1234567890ABCDEF"); +} + +TEST_F(TestPushAVStreamTransportStorage, TestContainerOptionsStorage) +{ + CMAFContainerOptionsStruct cmafContainerOptions; + /*Test CMAFContainerOptionsStorage*/ + + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.SetValue(true); + + std::string cencKey = "1234567890ABCDEF"; + std::string cencKeyID = "1234567890ABCDEF"; + + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); /*Test ContainerOptionsStorage*/ @@ -260,6 +279,10 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) ContainerOptionsStorage containerOptionsStorage(containerOptions); + // Clear the cencKey and cencKeyID strings to test deep copy of cencKey and cencKeyID + cencKey.clear(); + cencKeyID.clear(); + EXPECT_EQ(containerOptionsStorage.containerType, ContainerFormatEnum::kCmaf); EXPECT_TRUE(containerOptionsStorage.CMAFContainerOptions.HasValue()); EXPECT_EQ(containerOptionsStorage.CMAFContainerOptions.Value().chunkDuration, 1000); @@ -270,13 +293,13 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) containerOptionsStorage.CMAFContainerOptions.Value().CENCKey.Value().data() + containerOptionsStorage.CMAFContainerOptions.Value().CENCKey.Value().size()); - EXPECT_EQ(cencKeyStrContainer, cencKey); + EXPECT_EQ(cencKeyStrContainer, "1234567890ABCDEF"); std::string cencKeyIDStrContainer(containerOptionsStorage.CMAFContainerOptions.Value().CENCKeyID.Value().data(), containerOptionsStorage.CMAFContainerOptions.Value().CENCKeyID.Value().data() + containerOptionsStorage.CMAFContainerOptions.Value().CENCKeyID.Value().size()); - EXPECT_EQ(cencKeyIDStrContainer, cencKeyID); + EXPECT_EQ(cencKeyIDStrContainer, "1234567890ABCDEF"); ContainerOptionsStorage containerOptionsStorageCopy(containerOptionsStorage); @@ -290,13 +313,13 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKey.Value().data() + containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKey.Value().size()); - EXPECT_EQ(cencKeyStrContainerCopy, cencKey); + EXPECT_EQ(cencKeyStrContainerCopy, "1234567890ABCDEF"); std::string cencKeyIDStrContainerCopy(containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKeyID.Value().data(), containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKeyID.Value().data() + containerOptionsStorageCopy.CMAFContainerOptions.Value().CENCKeyID.Value().size()); - EXPECT_EQ(cencKeyIDStrContainerCopy, cencKeyID); + EXPECT_EQ(cencKeyIDStrContainerCopy, "1234567890ABCDEF"); // Accessing buffer using base struct @@ -312,13 +335,92 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) BaseContainerOptions.CMAFContainerOptions.Value().CENCKey.Value().data() + BaseContainerOptions.CMAFContainerOptions.Value().CENCKey.Value().size()); - EXPECT_EQ(containerCENCKeyStrBase, cencKey); + EXPECT_EQ(containerCENCKeyStrBase, "1234567890ABCDEF"); std::string containerCENCKeyIDStrBase(BaseContainerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().data(), BaseContainerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().data() + BaseContainerOptions.CMAFContainerOptions.Value().CENCKeyID.Value().size()); - EXPECT_EQ(containerCENCKeyIDStrBase, cencKeyID); + EXPECT_EQ(containerCENCKeyIDStrBase, "1234567890ABCDEF"); +} + +TEST_F(TestPushAVStreamTransportStorage, TestTransportOptionsStorage) +{ + uint8_t tlvBuffer[512]; + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + TransportTriggerOptionsDecodableStruct triggerOptions; + DataModel::DecodableList decodedList; + CMAFContainerOptionsStruct cmafContainerOptions; + ContainerOptionsStruct containerOptions; + TransportOptionsDecodableStruct transportOptions; + + /*Test TransportOptionsStorage*/ + + // Create a TransportMotionTriggerTimeControlStruct object + motionTimeControl.initialDuration = 5000; + motionTimeControl.augmentationDuration = 2000; + motionTimeControl.maxDuration = 30000; + motionTimeControl.blindDuration = 1000; + + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + + // Create transport zone options structs + Structs::TransportZoneOptionsStruct::Type zone1; + zone1.zone.SetNonNull(1); + zone1.sensitivity.SetValue(5); + + Structs::TransportZoneOptionsStruct::Type zone2; + zone2.zone.SetNonNull(2); + zone2.sensitivity.SetValue(10); + + // Encode them into a TLV buffer + TLV::TLVWriter writer; + writer.Init(tlvBuffer, sizeof(tlvBuffer)); + + TLV::TLVWriter containerWriter; + CHIP_ERROR err; + + err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = writer.CloseContainer(containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + size_t encodedLen = writer.GetLengthWritten(); + + // Decode the TLV into a DecodableList + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(encodedLen)); + err = reader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = decodedList.Decode(reader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.SetValue(DataModel::MakeNullable((uint8_t) 5)); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + /*Test CMAFContainerOptionsStorage*/ + + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.SetValue(true); + + std::string cencKey = "1234567890ABCDEF"; + std::string cencKeyID = "1234567890ABCDEF"; + + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); + + containerOptions.containerType = ContainerFormatEnum::kCmaf; + containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); /*Test TransportOptionsStorage*/ triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; @@ -341,13 +443,15 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) TransportOptionsStorage transportOptionsStorage(transportOptions); + url.clear(); // Clear the url string to test deep copy of url + EXPECT_EQ(transportOptionsStorage.streamUsage, StreamUsageEnum::kAnalysis); EXPECT_EQ(transportOptionsStorage.videoStreamID.Value(), (uint16_t) 1); EXPECT_EQ(transportOptionsStorage.audioStreamID.Value(), (uint16_t) 2); EXPECT_EQ(transportOptionsStorage.endpointID, 1); - EXPECT_EQ(transportOptionsStorage.url.size(), url.size()); + std::string transportOptionsUrlStr(transportOptionsStorage.url.data(), transportOptionsStorage.url.size()); - EXPECT_EQ(transportOptionsUrlStr, url); + EXPECT_EQ(transportOptionsUrlStr, "rtsp://192.168.1.100:554/stream"); TransportOptionsStorage transportOptionsStorageCopy(transportOptionsStorage); @@ -355,9 +459,9 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) EXPECT_EQ(transportOptionsStorageCopy.videoStreamID.Value(), (uint16_t) 1); EXPECT_EQ(transportOptionsStorageCopy.audioStreamID.Value(), (uint16_t) 2); EXPECT_EQ(transportOptionsStorageCopy.endpointID, 1); - EXPECT_EQ(transportOptionsStorageCopy.url.size(), url.size()); + std::string transportOptionsUrlStrCopy(transportOptionsStorageCopy.url.data(), transportOptionsStorageCopy.url.size()); - EXPECT_EQ(transportOptionsUrlStrCopy, url); + EXPECT_EQ(transportOptionsUrlStrCopy, "rtsp://192.168.1.100:554/stream"); // Accessing buffer using base struct @@ -367,9 +471,107 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) EXPECT_EQ(BaseTransportOptions.videoStreamID.Value(), (uint16_t) 1); EXPECT_EQ(BaseTransportOptions.audioStreamID.Value(), (uint16_t) 2); EXPECT_EQ(BaseTransportOptions.endpointID, 1); - EXPECT_EQ(BaseTransportOptions.url.size(), url.size()); + std::string transportOptionsUrlStrBase(BaseTransportOptions.url.data(), BaseTransportOptions.url.size()); - EXPECT_EQ(transportOptionsUrlStrBase, url); + EXPECT_EQ(transportOptionsUrlStrBase, "rtsp://192.168.1.100:554/stream"); +} + +TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) +{ + uint8_t tlvBuffer[512]; + TransportMotionTriggerTimeControlDecodableStruct motionTimeControl; + TransportTriggerOptionsDecodableStruct triggerOptions; + DataModel::DecodableList decodedList; + CMAFContainerOptionsStruct cmafContainerOptions; + ContainerOptionsStruct containerOptions; + TransportOptionsDecodableStruct transportOptions; + + /*Test TransportOptionsStorage*/ + + // Create a TransportMotionTriggerTimeControlStruct object + motionTimeControl.initialDuration = 5000; + motionTimeControl.augmentationDuration = 2000; + motionTimeControl.maxDuration = 30000; + motionTimeControl.blindDuration = 1000; + + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + + // Create transport zone options structs + Structs::TransportZoneOptionsStruct::Type zone1; + zone1.zone.SetNonNull(1); + zone1.sensitivity.SetValue(5); + + Structs::TransportZoneOptionsStruct::Type zone2; + zone2.zone.SetNonNull(2); + zone2.sensitivity.SetValue(10); + + // Encode them into a TLV buffer + TLV::TLVWriter writer; + writer.Init(tlvBuffer, sizeof(tlvBuffer)); + + TLV::TLVWriter containerWriter; + CHIP_ERROR err; + + err = writer.OpenContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone1); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = DataModel::Encode(containerWriter, TLV::AnonymousTag(), zone2); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = writer.CloseContainer(containerWriter); + EXPECT_EQ(err, CHIP_NO_ERROR); + + size_t encodedLen = writer.GetLengthWritten(); + + // Decode the TLV into a DecodableList + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(encodedLen)); + err = reader.Next(); + EXPECT_EQ(err, CHIP_NO_ERROR); + + err = decodedList.Decode(reader); + EXPECT_EQ(err, CHIP_NO_ERROR); + + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + triggerOptions.motionSensitivity.SetValue(DataModel::MakeNullable((uint8_t) 5)); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + /*Test CMAFContainerOptionsStorage*/ + + cmafContainerOptions.chunkDuration = 1000; + cmafContainerOptions.metadataEnabled.SetValue(true); + + std::string cencKey = "1234567890ABCDEF"; + std::string cencKeyID = "1234567890ABCDEF"; + + cmafContainerOptions.CENCKey.SetValue(ByteSpan(reinterpret_cast(cencKey.c_str()), cencKey.size())); + cmafContainerOptions.CENCKeyID.SetValue(ByteSpan(reinterpret_cast(cencKeyID.c_str()), cencKeyID.size())); + + containerOptions.containerType = ContainerFormatEnum::kCmaf; + containerOptions.CMAFContainerOptions.SetValue(cmafContainerOptions); + + /*Test TransportOptionsStorage*/ + triggerOptions.triggerType = TransportTriggerTypeEnum::kMotion; + triggerOptions.motionZones.SetValue(DataModel::MakeNullable(decodedList)); + + triggerOptions.motionSensitivity.SetValue(DataModel::MakeNullable((uint8_t) 5)); + triggerOptions.motionTimeControl.SetValue(motionTimeControl); + triggerOptions.maxPreRollLen.SetValue(1000); + + transportOptions.streamUsage = StreamUsageEnum::kAnalysis; + transportOptions.videoStreamID.SetValue(1); + transportOptions.audioStreamID.SetValue(2); + transportOptions.endpointID = 1; + std::string url = "rtsp://192.168.1.100:554/stream"; + transportOptions.url = Span(url.data(), url.size()); + transportOptions.triggerOptions = triggerOptions; + transportOptions.ingestMethod = IngestMethodsEnum::kCMAFIngest; + transportOptions.containerOptions = containerOptions; + transportOptions.expiryTime.SetValue(1000); /*Test TransportConfigurationStorage*/ @@ -377,16 +579,18 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) TransportConfigurationStorage transportConfigurationStorage(1, transportOptionsPtr); + url.clear(); // Clear the url string to test deep copy of url + EXPECT_EQ(transportConfigurationStorage.connectionID, 1); EXPECT_EQ(transportConfigurationStorage.transportStatus, TransportStatusEnum::kInactive); EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().streamUsage, StreamUsageEnum::kAnalysis); EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().videoStreamID.Value(), (uint16_t) 1); EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().audioStreamID.Value(), (uint16_t) 2); EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().endpointID, 1); - EXPECT_EQ(transportConfigurationStorage.transportOptions.Value().url.size(), url.size()); + std::string transportOptionsUrlStrConfiguration(transportConfigurationStorage.transportOptions.Value().url.data(), transportConfigurationStorage.transportOptions.Value().url.size()); - EXPECT_EQ(transportOptionsUrlStrConfiguration, url); + EXPECT_EQ(transportOptionsUrlStrConfiguration, "rtsp://192.168.1.100:554/stream"); TransportConfigurationStorage transportConfigurationStorageCopy(transportConfigurationStorage); @@ -396,10 +600,10 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().videoStreamID.Value(), (uint16_t) 1); EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().audioStreamID.Value(), (uint16_t) 2); EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().endpointID, 1); - EXPECT_EQ(transportConfigurationStorageCopy.transportOptions.Value().url.size(), url.size()); + std::string transportOptionsUrlStrConfigurationCopy(transportConfigurationStorageCopy.transportOptions.Value().url.data(), transportConfigurationStorageCopy.transportOptions.Value().url.size()); - EXPECT_EQ(transportOptionsUrlStrConfigurationCopy, url); + EXPECT_EQ(transportOptionsUrlStrConfigurationCopy, "rtsp://192.168.1.100:554/stream"); // Accessing buffer using base struct @@ -412,10 +616,10 @@ TEST_F(TestPushAVStreamTransportStorage, TestTransportConfigurationStorage) EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().videoStreamID.Value(), (uint16_t) 1); EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().audioStreamID.Value(), (uint16_t) 2); EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().endpointID, 1); - EXPECT_EQ(BaseTransportConfiguration.transportOptions.Value().url.size(), url.size()); + std::string transportOptionsUrlStrConfigurationBase(BaseTransportConfiguration.transportOptions.Value().url.data(), BaseTransportConfiguration.transportOptions.Value().url.size()); - EXPECT_EQ(transportOptionsUrlStrConfigurationBase, url); + EXPECT_EQ(transportOptionsUrlStrConfigurationBase, "rtsp://192.168.1.100:554/stream"); /*Test TransportConfigurationStorage with null transport options*/