Skip to content

Commit 0b91331

Browse files
committed
Merge branch 'mr/avsm_integration' into 'main'
Add Camera AV Stream Management cluster integration See merge request app-frameworks/esp-matter!1541
2 parents eda95c7 + b1654ae commit 0b91331

7 files changed

Lines changed: 255 additions & 3 deletions

File tree

components/esp_matter/data_model/esp_matter_delegate_callbacks.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
#include <clusters/time_synchronization/integration.h>
7171
#include <clusters/temperature_control/integration.h>
7272
#include <clusters/resource_monitor/integration.h>
73+
#include <clusters/camera_av_stream_management/integration.h>
7374
#include <clusters/chime/integration.h>
7475
#include <clusters/tls_client_management/integration.h>
7576
#include <clusters/tls_certificate_management/integration.h>
@@ -910,6 +911,13 @@ void TlsCertificateManagementDelegateInitCB(void *delegate, uint16_t endpoint_id
910911
TLSCertificateManagementDelegate *tls_certificate_management_delegate = static_cast<TLSCertificateManagementDelegate *>(delegate);
911912
TlsCertificateManagement::SetDelegate(endpoint_id, *tls_certificate_management_delegate);
912913
}
914+
void CameraAvStreamManagementDelegateInitCB(void *delegate, uint16_t endpoint_id)
915+
{
916+
VerifyOrReturn(delegate != nullptr);
917+
CameraAvStreamManagement::SetDelegate(
918+
endpoint_id,
919+
static_cast<chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManagementDelegate *>(delegate));
920+
}
913921
} // namespace delegate_cb
914922
} // namespace cluster
915923
} // namespace esp_matter

components/esp_matter/data_model/esp_matter_delegate_callbacks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ void TargetNavigatorDelegateInitCB(void *delegate, uint16_t endpoint_id);
8686
void WakeOnLanDelegateInitCB(void *delegate, uint16_t endpoint_id);
8787
void TlsClientManagementDelegateInitCB(void *delegate, uint16_t endpoint_id);
8888
void TlsCertificateManagementDelegateInitCB(void *delegate, uint16_t endpoint_id);
89+
void CameraAvStreamManagementDelegateInitCB(void *delegate, uint16_t endpoint_id);
8990
} // namespace delegate_cb
9091

9192
} // namespace cluster

components/esp_matter/data_model/legacy/esp_matter_cluster.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3893,9 +3893,10 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags)
38933893
cluster_t *cluster = esp_matter::cluster::create(endpoint, CameraAvStreamManagement::Id, flags);
38943894
VerifyOrReturnValue(cluster, NULL, ESP_LOGE(TAG, "Could not create cluster. cluster_id: 0x%08" PRIX32, CameraAvStreamManagement::Id));
38953895
if (flags & CLUSTER_FLAG_SERVER) {
3896-
// TODO: Add a delegate initialization callback.
3897-
// The current esp_matter initialization flow makes this hard to implement cleanly.
3898-
3896+
if (config && config->delegate != nullptr) {
3897+
static const auto delegate_init_cb = CameraAvStreamManagementDelegateInitCB;
3898+
set_delegate_and_init_callback(cluster, delegate_init_cb, config->delegate);
3899+
}
38993900
add_function_list(cluster, function_list, function_flags);
39003901

39013902
VerifyOrReturnValue(config != NULL, ABORT_CLUSTER_CREATE(cluster));
@@ -3952,6 +3953,9 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags)
39523953
}
39533954
command::create_set_stream_priorities(cluster);
39543955

3956+
cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterCameraAvStreamManagementClusterServerInitCallback,
3957+
ESPMatterCameraAvStreamManagementClusterServerShutdownCallback);
3958+
39553959
}
39563960

39573961
if (flags & CLUSTER_FLAG_CLIENT) {

components/esp_matter/data_model/legacy/esp_matter_cluster_impl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ typedef struct config {
973973
uint32_t max_content_buffer_size;
974974
uint32_t max_network_bandwidth;
975975
uint32_t feature_flags;
976+
void *delegate;
976977
config() : max_content_buffer_size(0), max_network_bandwidth(0), feature_flags(0) {}
977978
} config_t;
978979

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2026 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <esp_matter_data_model_priv.h>
16+
#include <app/ClusterCallbacks.h>
17+
#include <app/SafeAttributePersistenceProvider.h>
18+
#include <data_model/esp_matter_data_model.h>
19+
#include <data_model/esp_matter_endpoint.h>
20+
#include <data_model_provider/esp_matter_data_model_provider.h>
21+
#include <unordered_map>
22+
#include "integration.h"
23+
24+
using namespace chip;
25+
using namespace chip::app;
26+
using namespace chip::app::Clusters;
27+
using namespace chip::app::Clusters::CameraAvStreamManagement;
28+
using namespace esp_matter;
29+
30+
namespace {
31+
std::unordered_map<EndpointId, LazyRegisteredServerCluster<CameraAVStreamManagementCluster>> gServers;
32+
std::unordered_map<EndpointId, CameraAvStreamManagementConfig> gConfigs;
33+
34+
bool IsClusterEnabled(EndpointId endpointId)
35+
{
36+
cluster_t *cluster = cluster::get(endpointId, CameraAvStreamManagement::Id);
37+
return cluster != nullptr;
38+
}
39+
40+
uint32_t GetFeatureMap(EndpointId endpointId)
41+
{
42+
attribute_t *attribute = attribute::get(endpointId, CameraAvStreamManagement::Id, Globals::Attributes::FeatureMap::Id);
43+
VerifyOrReturnValue(attribute, 0);
44+
45+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
46+
VerifyOrReturnValue(attribute::get_val_internal(attribute, &val) == ESP_OK, 0);
47+
return val.val.u32;
48+
}
49+
50+
BitFlags<OptionalAttribute> GetOptionalAttributes(EndpointId endpointId)
51+
{
52+
BitFlags<OptionalAttribute> optionalAttrs;
53+
54+
if (endpoint::is_attribute_enabled(endpointId, CameraAvStreamManagement::Id,
55+
CameraAvStreamManagement::Attributes::HardPrivacyModeOn::Id)) {
56+
optionalAttrs.Set(OptionalAttribute::kHardPrivacyModeOn);
57+
}
58+
if (endpoint::is_attribute_enabled(endpointId, CameraAvStreamManagement::Id,
59+
CameraAvStreamManagement::Attributes::NightVisionIllum::Id)) {
60+
optionalAttrs.Set(OptionalAttribute::kNightVisionIllum);
61+
}
62+
if (endpoint::is_attribute_enabled(endpointId, CameraAvStreamManagement::Id,
63+
CameraAvStreamManagement::Attributes::MicrophoneAGCEnabled::Id)) {
64+
optionalAttrs.Set(OptionalAttribute::kMicrophoneAGCEnabled);
65+
}
66+
if (endpoint::is_attribute_enabled(endpointId, CameraAvStreamManagement::Id,
67+
CameraAvStreamManagement::Attributes::ImageRotation::Id)) {
68+
optionalAttrs.Set(OptionalAttribute::kImageRotation);
69+
}
70+
if (endpoint::is_attribute_enabled(endpointId, CameraAvStreamManagement::Id,
71+
CameraAvStreamManagement::Attributes::ImageFlipHorizontal::Id)) {
72+
optionalAttrs.Set(OptionalAttribute::kImageFlipHorizontal);
73+
}
74+
if (endpoint::is_attribute_enabled(endpointId, CameraAvStreamManagement::Id,
75+
CameraAvStreamManagement::Attributes::ImageFlipVertical::Id)) {
76+
optionalAttrs.Set(OptionalAttribute::kImageFlipVertical);
77+
}
78+
if (endpoint::is_attribute_enabled(endpointId, CameraAvStreamManagement::Id,
79+
CameraAvStreamManagement::Attributes::StatusLightEnabled::Id)) {
80+
optionalAttrs.Set(OptionalAttribute::kStatusLightEnabled);
81+
}
82+
if (endpoint::is_attribute_enabled(endpointId, CameraAvStreamManagement::Id,
83+
CameraAvStreamManagement::Attributes::StatusLightBrightness::Id)) {
84+
optionalAttrs.Set(OptionalAttribute::kStatusLightBrightness);
85+
}
86+
return optionalAttrs;
87+
}
88+
} // namespace
89+
90+
namespace chip::app::Clusters::CameraAvStreamManagement {
91+
92+
void SetConfig(EndpointId endpointId, const CameraAvStreamManagementConfig &config)
93+
{
94+
gConfigs[endpointId] = config;
95+
}
96+
97+
const CameraAvStreamManagementConfig * GetConfig(EndpointId endpointId)
98+
{
99+
auto it = gConfigs.find(endpointId);
100+
return (it == gConfigs.end()) ? nullptr : &it->second;
101+
}
102+
103+
void SetDelegate(EndpointId endpointId, CameraAVStreamManagementDelegate * delegate)
104+
{
105+
if (gConfigs.find(endpointId) == gConfigs.end()) {
106+
ChipLogError(AppServer, "Camera AV Stream Management config not found for endpoint %u", endpointId);
107+
return;
108+
}
109+
gConfigs[endpointId].delegate = delegate;
110+
}
111+
112+
CameraAVStreamManagementCluster * GetServer(EndpointId endpointId)
113+
{
114+
if (!gServers[endpointId].IsConstructed()) {
115+
return nullptr;
116+
}
117+
118+
return &gServers[endpointId].Cluster();
119+
}
120+
121+
} // namespace chip::app::Clusters::CameraAvStreamManagement
122+
123+
void ESPMatterCameraAvStreamManagementClusterServerInitCallback(EndpointId endpointId)
124+
{
125+
if (!IsClusterEnabled(endpointId)) {
126+
return;
127+
}
128+
129+
if (!gServers[endpointId].IsConstructed()) {
130+
const CameraAvStreamManagementConfig *config = CameraAvStreamManagement::GetConfig(endpointId);
131+
if (config == nullptr || config->delegate == nullptr) {
132+
ChipLogError(AppServer, "Camera AV Stream Management config/delegate missing for endpoint %u", endpointId);
133+
return;
134+
}
135+
136+
SafeAttributePersistenceProvider * provider = GetSafeAttributePersistenceProvider();
137+
if (provider == nullptr) {
138+
ChipLogError(AppServer, "SafeAttributePersistenceProvider not available for endpoint %u", endpointId);
139+
return;
140+
}
141+
142+
ChipLogProgress(AppServer, "Registering Camera AV Stream Management on endpoint %u", endpointId);
143+
BitFlags<Feature> features(GetFeatureMap(endpointId));
144+
BitFlags<OptionalAttribute> optionalAttrs = GetOptionalAttributes(endpointId);
145+
146+
gServers[endpointId].Create(CameraAVStreamManagementCluster::Context{ *provider }, *config->delegate, endpointId, features,
147+
optionalAttrs,
148+
config->maxConcurrentEncoders, config->maxEncodedPixelRate, config->videoSensorParams,
149+
config->nightVisionUsesInfrared, config->minViewPortRes, config->rateDistortionTradeOffPoints,
150+
config->maxContentBufferSize, config->microphoneCapabilities, config->speakerCapabilities,
151+
config->twoWayTalkSupport, config->snapshotCapabilities, config->maxNetworkBandwidth,
152+
config->supportedStreamUsages, config->streamUsagePriorities);
153+
}
154+
155+
CHIP_ERROR err = data_model::provider::get_instance().registry().Register(gServers[endpointId].Registration());
156+
if (err != CHIP_NO_ERROR) {
157+
ChipLogError(AppServer,
158+
"Failed to register Camera AV Stream Management on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId,
159+
err.Format());
160+
}
161+
}
162+
163+
void ESPMatterCameraAvStreamManagementClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType)
164+
{
165+
auto serverIt = gServers.find(endpointId);
166+
VerifyOrReturn(serverIt != gServers.end());
167+
VerifyOrReturn(serverIt->second.IsConstructed());
168+
169+
CHIP_ERROR err = data_model::provider::get_instance().registry().Unregister(&serverIt->second.Cluster(), shutdownType);
170+
if (err != CHIP_NO_ERROR) {
171+
ChipLogError(AppServer,
172+
"Failed to unregister Camera AV Stream Management on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId,
173+
err.Format());
174+
}
175+
if (shutdownType == ClusterShutdownType::kPermanentRemove) {
176+
serverIt->second.Destroy();
177+
gServers.erase(serverIt);
178+
gConfigs.erase(endpointId);
179+
}
180+
}
181+
182+
void MatterCameraAvStreamManagementPluginServerInitCallback() {}
183+
184+
void MatterCameraAvStreamManagementPluginServerShutdownCallback() {}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2026 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <app/clusters/camera-av-stream-management-server/CameraAVStreamManagementCluster.h>
18+
#include <lib/core/DataModelTypes.h>
19+
#include <lib/support/Span.h>
20+
#include <vector>
21+
22+
namespace chip {
23+
namespace app {
24+
namespace Clusters {
25+
namespace CameraAvStreamManagement {
26+
27+
struct CameraAvStreamManagementConfig {
28+
CameraAVStreamManagementDelegate * delegate = nullptr;
29+
uint8_t maxConcurrentEncoders = 0;
30+
uint32_t maxEncodedPixelRate = 0;
31+
VideoSensorParamsStruct videoSensorParams{};
32+
bool nightVisionUsesInfrared = false;
33+
VideoResolutionStruct minViewPortRes{};
34+
std::vector<Structs::RateDistortionTradeOffPointsStruct::Type> rateDistortionTradeOffPoints;
35+
uint32_t maxContentBufferSize = 0;
36+
AudioCapabilitiesStruct microphoneCapabilities{};
37+
AudioCapabilitiesStruct speakerCapabilities{};
38+
TwoWayTalkSupportTypeEnum twoWayTalkSupport = TwoWayTalkSupportTypeEnum::kNotSupported;
39+
std::vector<Structs::SnapshotCapabilitiesStruct::Type> snapshotCapabilities;
40+
uint32_t maxNetworkBandwidth = 0;
41+
std::vector<Globals::StreamUsageEnum> supportedStreamUsages;
42+
std::vector<Globals::StreamUsageEnum> streamUsagePriorities;
43+
};
44+
45+
void SetConfig(EndpointId endpointId, const CameraAvStreamManagementConfig &config);
46+
const CameraAvStreamManagementConfig * GetConfig(EndpointId endpointId);
47+
void SetDelegate(EndpointId endpointId, CameraAVStreamManagementDelegate * delegate);
48+
CameraAVStreamManagementCluster * GetServer(EndpointId endpointId);
49+
50+
} // namespace CameraAvStreamManagement
51+
} // namespace Clusters
52+
} // namespace app
53+
} // namespace chip

components/esp_matter/test/cluster_lifecycle_basic.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ static void verify_cluster_lifecycle_no_heap_leak(chip::ClusterId cluster_id,
133133

134134
// Requires an application delegate.
135135
// - ClosureControl
136+
// - CameraAvStreamManagement
136137
// - TlsClientManagement
137138
// - TlsCertificateManagement
138139

0 commit comments

Comments
 (0)