Skip to content

Commit bf77d63

Browse files
Matter Camera: Include all supported resolutions
This change adds logic for including the resolutions exposed by the VideoSensorParams and MinViewport attributes in supportedResolutions, rather than only looking at the resolution embedded within RateDistortionTradeOffPoints.
1 parent 6ddbbad commit bf77d63

File tree

4 files changed

+106
-57
lines changed

4 files changed

+106
-57
lines changed

drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_handlers/attribute_handlers.lua

Lines changed: 37 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,6 @@ end
113113
function CameraAttributeHandlers.rate_distortion_trade_off_points_handler(driver, device, ib, response)
114114
if not ib.data.elements then return end
115115
local resolutions = {}
116-
local max_encoded_pixel_rate = device:get_field(camera_fields.MAX_ENCODED_PIXEL_RATE)
117-
local max_fps = device:get_field(camera_fields.MAX_FRAMES_PER_SECOND)
118-
local emit_capability = max_encoded_pixel_rate ~= nil and max_fps ~= nil
119116
for _, v in ipairs(ib.data.elements) do
120117
local rate_distortion_trade_off_points = v.elements
121118
local width = rate_distortion_trade_off_points.resolution.elements.width.value
@@ -124,77 +121,63 @@ function CameraAttributeHandlers.rate_distortion_trade_off_points_handler(driver
124121
width = width,
125122
height = height
126123
})
127-
if emit_capability then
128-
local fps = camera_utils.compute_fps(max_encoded_pixel_rate, width, height, max_fps)
129-
if fps > 0 then
130-
resolutions[#resolutions].fps = fps
131-
end
132-
end
133-
end
134-
if emit_capability then
135-
device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(resolutions))
136124
end
137125
device:set_field(camera_fields.SUPPORTED_RESOLUTIONS, resolutions)
126+
local max_encoded_pixel_rate = device:get_field(camera_fields.MAX_ENCODED_PIXEL_RATE)
127+
local max_fps = device:get_field(camera_fields.MAX_FRAMES_PER_SECOND)
128+
if max_encoded_pixel_rate and max_fps and device:get_field(camera_fields.MAX_RESOLUTION) and device:get_field(camera_fields.MIN_RESOLUTION) then
129+
local supported_resolutions = camera_utils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps)
130+
device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(supported_resolutions))
131+
end
138132
end
139133

140134
function CameraAttributeHandlers.max_encoded_pixel_rate_handler(driver, device, ib, response)
141-
local resolutions = device:get_field(camera_fields.SUPPORTED_RESOLUTIONS)
135+
device:set_field(camera_fields.MAX_ENCODED_PIXEL_RATE, ib.data.value)
142136
local max_fps = device:get_field(camera_fields.MAX_FRAMES_PER_SECOND)
143-
local emit_capability = resolutions ~= nil and max_fps ~= nil
144-
if emit_capability then
145-
for _, v in pairs(resolutions or {}) do
146-
local fps = camera_utils.compute_fps(ib.data.value, v.width, v.height, max_fps)
147-
if fps > 0 then
148-
v.fps = fps
149-
end
150-
end
151-
device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(resolutions))
137+
if max_fps and device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) and device:get_field(camera_fields.MAX_RESOLUTION) and device:get_field(camera_fields.MIN_RESOLUTION) then
138+
local supported_resolutions = camera_utils.build_supported_resolutions(device, ib.data.value, max_fps)
139+
device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(supported_resolutions))
152140
end
153-
device:set_field(camera_fields.MAX_ENCODED_PIXEL_RATE, ib.data.value)
154141
end
155142

156143
function CameraAttributeHandlers.video_sensor_parameters_handler(driver, device, ib, response)
157144
if not ib.data.elements then return end
158-
local resolutions = device:get_field(camera_fields.SUPPORTED_RESOLUTIONS)
145+
local sensor_width = ib.data.elements.sensor_width.value
146+
local sensor_height = ib.data.elements.sensor_height.value
147+
local max_fps = ib.data.elements.max_fps.value
148+
device:set_field(camera_fields.MAX_RESOLUTION, {
149+
width = sensor_width,
150+
height = sensor_height
151+
})
152+
device:set_field(camera_fields.MAX_FRAMES_PER_SECOND, max_fps)
153+
device:emit_event_for_endpoint(ib, capabilities.cameraViewportSettings.videoSensorParameters({
154+
width = sensor_width,
155+
height = sensor_height,
156+
maxFPS = max_fps
157+
}))
159158
local max_encoded_pixel_rate = device:get_field(camera_fields.MAX_ENCODED_PIXEL_RATE)
160-
local emit_capability = resolutions ~= nil and max_encoded_pixel_rate ~= nil
161-
local sensor_width, sensor_height, max_fps
162-
for _, v in pairs(ib.data.elements) do
163-
if v.field_id == 0 then
164-
sensor_width = v.value
165-
elseif v.field_id == 1 then
166-
sensor_height = v.value
167-
elseif v.field_id == 2 then
168-
max_fps = v.value
169-
end
170-
end
171-
172-
if max_fps then
173-
if sensor_width and sensor_height then
174-
device:emit_event_for_endpoint(ib, capabilities.cameraViewportSettings.videoSensorParameters({
175-
width = sensor_width,
176-
height = sensor_height,
177-
maxFPS = max_fps
178-
}))
179-
end
180-
if emit_capability then
181-
for _, v in pairs(resolutions or {}) do
182-
local fps = camera_utils.compute_fps(max_encoded_pixel_rate, v.width, v.height, max_fps)
183-
if fps > 0 then
184-
v.fps = fps
185-
end
186-
end
187-
device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(resolutions))
188-
end
189-
device:set_field(camera_fields.MAX_FRAMES_PER_SECOND, max_fps)
159+
if max_encoded_pixel_rate and max_fps and device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) and device:get_field(camera_fields.MIN_RESOLUTION) then
160+
local supported_resolutions = camera_utils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps)
161+
device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(supported_resolutions))
190162
end
191163
end
192164

193165
function CameraAttributeHandlers.min_viewport_handler(driver, device, ib, response)
166+
if not ib.data.elements then return end
194167
device:emit_event_for_endpoint(ib, capabilities.cameraViewportSettings.minViewportResolution({
195168
width = ib.data.elements.width.value,
196169
height = ib.data.elements.height.value
197170
}))
171+
device:set_field(camera_fields.MIN_RESOLUTION, {
172+
width = ib.data.elements.width.value,
173+
height = ib.data.elements.height.value
174+
})
175+
local max_encoded_pixel_rate = device:get_field(camera_fields.MAX_ENCODED_PIXEL_RATE)
176+
local max_fps = device:get_field(camera_fields.MAX_FRAMES_PER_SECOND)
177+
if max_encoded_pixel_rate and max_fps and device:get_field(camera_fields.SUPPORTED_RESOLUTIONS) and device:get_field(camera_fields.MAX_RESOLUTION) then
178+
local supported_resolutions = camera_utils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps)
179+
device:emit_event_for_endpoint(ib, capabilities.videoStreamSettings.supportedResolutions(supported_resolutions))
180+
end
198181
end
199182

200183
function CameraAttributeHandlers.allocated_video_streams_handler(driver, device, ib, response)

drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/fields.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ CameraFields.MAX_FRAMES_PER_SECOND = "__max_frames_per_second"
1010
CameraFields.MAX_VOLUME_LEVEL = "__max_volume_level"
1111
CameraFields.MIN_VOLUME_LEVEL = "__min_volume_level"
1212
CameraFields.SUPPORTED_RESOLUTIONS = "__supported_resolutions"
13+
CameraFields.MAX_RESOLUTION = "__max_resolution"
14+
CameraFields.MIN_RESOLUTION = "__min_resolution"
1315
CameraFields.TRIGGERED_ZONES = "__triggered_zones"
1416
CameraFields.VIEWPORT = "__viewport"
1517

drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,38 @@ function CameraUtils.compute_fps(max_encoded_pixel_rate, width, height, max_fps)
102102
return math.tointeger(math.floor(fps / fps_step) * fps_step)
103103
end
104104

105+
function CameraUtils.build_supported_resolutions(device, max_encoded_pixel_rate, max_fps)
106+
local resolutions = {}
107+
local added_resolutions = {}
108+
109+
local function add_resolution(width, height)
110+
local key = width .. "x" .. height
111+
if not added_resolutions[key] then
112+
local resolution = { width = width, height = height }
113+
resolution.fps = CameraUtils.compute_fps(max_encoded_pixel_rate, width, height, max_fps)
114+
table.insert(resolutions, resolution)
115+
added_resolutions[key] = true
116+
end
117+
end
118+
119+
local min_resolution = device:get_field(camera_fields.MIN_RESOLUTION)
120+
if min_resolution then
121+
add_resolution(min_resolution.width, min_resolution.height)
122+
end
123+
124+
local trade_off_resolutions = device:get_field(camera_fields.SUPPORTED_RESOLUTIONS)
125+
for _, v in pairs(trade_off_resolutions or {}) do
126+
add_resolution(v.width, v.height)
127+
end
128+
129+
local max_resolution = device:get_field(camera_fields.MAX_RESOLUTION)
130+
if max_resolution then
131+
add_resolution(max_resolution.width, max_resolution.height)
132+
end
133+
134+
return resolutions
135+
end
136+
105137
function CameraUtils.profile_changed(synced_components, prev_components)
106138
if #synced_components ~= #prev_components then
107139
return true

drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,18 @@ local function receive_max_encoded_pixel_rate()
683683
})
684684
end
685685

686+
local function receive_min_viewport()
687+
test.socket.matter:__queue_receive({
688+
mock_device.id,
689+
clusters.CameraAvStreamManagement.attributes.MinViewportResolution:build_test_report_data(
690+
mock_device, CAMERA_EP, clusters.CameraAvStreamManagement.types.VideoResolutionStruct({
691+
width = 1920,
692+
height = 1080
693+
})
694+
)
695+
})
696+
end
697+
686698
local function receive_video_sensor_params()
687699
test.socket.matter:__queue_receive({
688700
mock_device.id,
@@ -697,6 +709,15 @@ local function receive_video_sensor_params()
697709
})
698710
end
699711

712+
local function emit_min_viewport()
713+
test.socket.capability:__expect_send(
714+
mock_device:generate_test_message("main", capabilities.cameraViewportSettings.minViewportResolution({
715+
width = 1920,
716+
height = 1080,
717+
}))
718+
)
719+
end
720+
700721
local function emit_video_sensor_parameters()
701722
test.socket.capability:__expect_send(
702723
mock_device:generate_test_message("main", capabilities.cameraViewportSettings.videoSensorParameters({
@@ -719,6 +740,11 @@ local function emit_supported_resolutions()
719740
width = 3840,
720741
height = 2160,
721742
fps = 15
743+
},
744+
{
745+
width = 7360,
746+
height = 4912,
747+
fps = 0
722748
}
723749
}))
724750
)
@@ -730,24 +756,28 @@ end
730756
-- videoStreamSettings.supportedResolutions is emitted after all three attributes are received.
731757

732758
test.register_coroutine_test(
733-
"Rate Distortion Trade Off Points, MaxEncodedPixelRate, VideoSensorParams reports should generate appropriate events",
759+
"Rate Distortion Trade Off Points, MaxEncodedPixelRate, MinViewport, VideoSensorParams reports should generate appropriate events",
734760
function()
735761
update_device_profile()
736762
test.wait_for_events()
737763
receive_rate_distortion_trade_off_points()
738764
receive_max_encoded_pixel_rate()
765+
receive_min_viewport()
766+
emit_min_viewport()
739767
receive_video_sensor_params()
740768
emit_video_sensor_parameters()
741769
emit_supported_resolutions()
742770
end
743771
)
744772

745773
test.register_coroutine_test(
746-
"Rate Distortion Trade Off Points, VideoSensorParams, MaxEncodedPixelRate reports should generate appropriate events",
774+
"Rate Distortion Trade Off Points, MinViewport, VideoSensorParams, MaxEncodedPixelRate reports should generate appropriate events",
747775
function()
748776
update_device_profile()
749777
test.wait_for_events()
750778
receive_rate_distortion_trade_off_points()
779+
receive_min_viewport()
780+
emit_min_viewport()
751781
receive_video_sensor_params()
752782
emit_video_sensor_parameters()
753783
receive_max_encoded_pixel_rate()
@@ -756,11 +786,13 @@ test.register_coroutine_test(
756786
)
757787

758788
test.register_coroutine_test(
759-
"MaxEncodedPixelRate, VideoSensorParams, Rate Distortion Trade Off Points reports should generate appropriate events",
789+
"MaxEncodedPixelRate, MinViewport, VideoSensorParams, Rate Distortion Trade Off Points reports should generate appropriate events",
760790
function()
761791
update_device_profile()
762792
test.wait_for_events()
763793
receive_max_encoded_pixel_rate()
794+
receive_min_viewport()
795+
emit_min_viewport()
764796
receive_video_sensor_params()
765797
emit_video_sensor_parameters()
766798
receive_rate_distortion_trade_off_points()

0 commit comments

Comments
 (0)