mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 19:13:13 +00:00
890 lines
34 KiB
C++
890 lines
34 KiB
C++
/*
|
|
*
|
|
* Copyright (c) 2025 Project CHIP Authors
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include <app-common/zap-generated/attributes/Accessors.h>
|
|
#include <app-common/zap-generated/ids/Attributes.h>
|
|
#include <app-common/zap-generated/ids/Clusters.h>
|
|
#include <camera-av-stream-manager.h>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <lib/support/logging/CHIPLogging.h>
|
|
#include <set>
|
|
|
|
using namespace chip;
|
|
using namespace chip::app;
|
|
using namespace chip::app::DataModel;
|
|
using namespace chip::app::Clusters;
|
|
using namespace chip::app::Clusters::CameraAvStreamManagement;
|
|
using namespace chip::app::Clusters::CameraAvStreamManagement::Attributes;
|
|
using chip::Protocols::InteractionModel::Status;
|
|
|
|
namespace {
|
|
|
|
// Constants
|
|
constexpr uint16_t kInvalidStreamID = 65500;
|
|
|
|
} // namespace
|
|
|
|
void CameraAVStreamManager::SetCameraDeviceHAL(CameraDeviceInterface * aCameraDeviceHAL)
|
|
{
|
|
mCameraDeviceHAL = aCameraDeviceHAL;
|
|
}
|
|
|
|
CHIP_ERROR CameraAVStreamManager::ValidateStreamUsage(StreamUsageEnum streamUsage,
|
|
Optional<DataModel::Nullable<uint16_t>> & videoStreamId,
|
|
Optional<DataModel::Nullable<uint16_t>> & audioStreamId)
|
|
{
|
|
// The server ensures that at least one stream Id has a value, and that there
|
|
// are streams allocated If a stream id(s) are provided, it's sufficient to
|
|
// have verified that the provide usage is supported by the camera. If they're
|
|
// Null, look for a stream ID that matches the usage. A match does not need to
|
|
// be exact.
|
|
bool exactlyMatchedVideoStream = false;
|
|
bool looselyMatchedVideoStream = false;
|
|
uint16_t looseVideoStreamID;
|
|
bool exactlyMatchedAudioStream = false;
|
|
bool looselyMatchedAudioStream = false;
|
|
uint16_t looseAudioStreamID;
|
|
|
|
// Is the requested stream usage supported by the camera?
|
|
auto myStreamUsages = GetCameraAVStreamManagementCluster()->GetSupportedStreamUsages();
|
|
auto it = std::find(myStreamUsages.begin(), myStreamUsages.end(), streamUsage);
|
|
if (it == myStreamUsages.end())
|
|
{
|
|
ChipLogError(Camera, "Requested stream usage not found in supported stream usages");
|
|
return CHIP_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
if (videoStreamId.HasValue())
|
|
{
|
|
const std::vector<VideoStreamStruct> & allocatedVideoStreams =
|
|
GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams();
|
|
|
|
// If no Video ID is provided, match to an allocated ID. Exact is preferred
|
|
// if found. We know the stream requested is in supported streams.
|
|
if (videoStreamId.Value().IsNull())
|
|
{
|
|
for (const auto & stream : allocatedVideoStreams)
|
|
{
|
|
if (stream.streamUsage == streamUsage)
|
|
{
|
|
videoStreamId.Emplace(stream.videoStreamID);
|
|
exactlyMatchedVideoStream = true;
|
|
break;
|
|
}
|
|
|
|
looselyMatchedVideoStream = true;
|
|
looseVideoStreamID = stream.videoStreamID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We've been provided with a stream ID, and we know the stream usage is
|
|
// supported by the camera, classify as an exact match
|
|
exactlyMatchedVideoStream = true;
|
|
}
|
|
}
|
|
|
|
if (audioStreamId.HasValue())
|
|
{
|
|
const std::vector<AudioStreamStruct> & allocatedAudioStreams =
|
|
GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams();
|
|
|
|
// If no Audio ID is provided, match to an allocated ID. Exact is preferred
|
|
// if found. We know the stream requested is in supported streams.
|
|
if (audioStreamId.Value().IsNull())
|
|
{
|
|
for (const auto & stream : allocatedAudioStreams)
|
|
{
|
|
if (stream.streamUsage == streamUsage)
|
|
{
|
|
audioStreamId.Emplace(stream.audioStreamID);
|
|
exactlyMatchedAudioStream = true;
|
|
break;
|
|
}
|
|
|
|
looselyMatchedAudioStream = true;
|
|
looseAudioStreamID = stream.audioStreamID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We've been provided with a stream ID, and we know the stream usage is
|
|
// supported by the camera, classify as an exact match
|
|
exactlyMatchedAudioStream = true;
|
|
}
|
|
}
|
|
|
|
// If we have a loose match and no exact match, update the provided stream IDs
|
|
// with the loose match values
|
|
//
|
|
if (looselyMatchedAudioStream && !exactlyMatchedAudioStream)
|
|
{
|
|
audioStreamId.Emplace(looseAudioStreamID);
|
|
}
|
|
|
|
if (looselyMatchedVideoStream && !exactlyMatchedVideoStream)
|
|
{
|
|
videoStreamId.Emplace(looseVideoStreamID);
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
const std::vector<chip::app::Clusters::CameraAvStreamManagement::VideoStreamStruct> &
|
|
CameraAVStreamManager::GetAllocatedVideoStreams() const
|
|
{
|
|
return GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams();
|
|
}
|
|
|
|
const std::vector<chip::app::Clusters::CameraAvStreamManagement::AudioStreamStruct> &
|
|
CameraAVStreamManager::GetAllocatedAudioStreams() const
|
|
{
|
|
return GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams();
|
|
}
|
|
|
|
void CameraAVStreamManager::GetBandwidthForStreams(const Optional<DataModel::Nullable<uint16_t>> & videoStreamId,
|
|
const Optional<DataModel::Nullable<uint16_t>> & audioStreamId,
|
|
uint32_t & outBandwidthbps)
|
|
{
|
|
|
|
outBandwidthbps = 0;
|
|
if (videoStreamId.HasValue() && !videoStreamId.Value().IsNull())
|
|
{
|
|
uint16_t vStreamId = videoStreamId.Value().Value();
|
|
auto & allocatedVideoStreams = GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams();
|
|
for (const chip::app::Clusters::CameraAvStreamManagement::Structs::VideoStreamStruct::Type & stream : allocatedVideoStreams)
|
|
{
|
|
if (stream.videoStreamID == vStreamId)
|
|
{
|
|
outBandwidthbps += stream.maxBitRate;
|
|
ChipLogProgress(Camera, "GetBandwidthForStreams: VideoStream %u maxBitRate: %lu bps", vStreamId, stream.maxBitRate);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (audioStreamId.HasValue() && !audioStreamId.Value().IsNull())
|
|
{
|
|
uint16_t aStreamId = audioStreamId.Value().Value();
|
|
auto & allocatedAudioStreams = GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams();
|
|
for (const chip::app::Clusters::CameraAvStreamManagement::Structs::AudioStreamStruct::Type & stream : allocatedAudioStreams)
|
|
{
|
|
if (stream.audioStreamID == aStreamId)
|
|
{
|
|
outBandwidthbps += stream.bitRate;
|
|
ChipLogProgress(Camera, "GetBandwidthForStreams: AudioStream %u bitRate: %lu bps", aStreamId, stream.bitRate);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::ValidateVideoStreamID(uint16_t videoStreamId)
|
|
{
|
|
const std::vector<VideoStreamStruct> & allocatedVideoStreams = GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams();
|
|
|
|
// Check if the videoStreamId exists in allocated streams
|
|
for (const auto & stream : allocatedVideoStreams)
|
|
{
|
|
if (stream.videoStreamID == videoStreamId)
|
|
{
|
|
ChipLogProgress(Camera, "Video stream ID %u is valid and allocated", videoStreamId);
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
ChipLogError(Camera, "Video stream ID %u not found in allocated video streams", videoStreamId);
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::ValidateAudioStreamID(uint16_t audioStreamId)
|
|
{
|
|
const std::vector<AudioStreamStruct> & allocatedAudioStreams = GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams();
|
|
|
|
// Check if the audioStreamId exists in allocated streams
|
|
for (const auto & stream : allocatedAudioStreams)
|
|
{
|
|
if (stream.audioStreamID == audioStreamId)
|
|
{
|
|
ChipLogProgress(Camera, "Audio stream ID %u is valid and allocated", audioStreamId);
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
ChipLogError(Camera, "Audio stream ID %u not found in allocated audio streams", audioStreamId);
|
|
return CHIP_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
CHIP_ERROR CameraAVStreamManager::IsHardPrivacyModeActive(bool & isActive)
|
|
{
|
|
// Check privacy mode attributes
|
|
isActive = GetCameraAVStreamManagementCluster()->GetHardPrivacyModeOn();
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::IsSoftRecordingPrivacyModeActive(bool & isActive)
|
|
{
|
|
// Check privacy mode attributes
|
|
isActive = GetCameraAVStreamManagementCluster()->GetSoftRecordingPrivacyModeEnabled();
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::IsSoftLivestreamPrivacyModeActive(bool & isActive)
|
|
{
|
|
// Check privacy mode attributes
|
|
isActive = GetCameraAVStreamManagementCluster()->GetSoftLivestreamPrivacyModeEnabled();
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR CameraAVStreamManager::SetHardPrivacyModeOn(bool hardPrivacyMode)
|
|
{
|
|
return GetCameraAVStreamManagementCluster()->SetHardPrivacyModeOn(hardPrivacyMode);
|
|
}
|
|
|
|
bool CameraAVStreamManager::HasAllocatedVideoStreams()
|
|
{
|
|
const std::vector<VideoStreamStruct> & allocatedVideoStreams = GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams();
|
|
return !allocatedVideoStreams.empty();
|
|
}
|
|
|
|
bool CameraAVStreamManager::HasAllocatedAudioStreams()
|
|
{
|
|
const std::vector<AudioStreamStruct> & allocatedAudioStreams = GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams();
|
|
return !allocatedAudioStreams.empty();
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::VideoStreamAllocate(const VideoStreamStruct & allocateArgs,
|
|
uint16_t & outStreamID)
|
|
{
|
|
outStreamID = kInvalidStreamID;
|
|
bool isRequestSupportedByAnyAvailableStream = false;
|
|
|
|
// Check if allocation request can be supported
|
|
for (const auto & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams())
|
|
{
|
|
if (stream.IsCompatible(allocateArgs))
|
|
{
|
|
isRequestSupportedByAnyAvailableStream = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isRequestSupportedByAnyAvailableStream)
|
|
{
|
|
return Status::DynamicConstraintError;
|
|
}
|
|
|
|
// Try to reuse an allocated stream
|
|
std::optional<uint16_t> reusableStreamId = GetCameraAVStreamManagementCluster()->GetReusableVideoStreamId(allocateArgs);
|
|
|
|
if (reusableStreamId.has_value())
|
|
{
|
|
// Found a stream that can be reused
|
|
outStreamID = reusableStreamId.value();
|
|
ChipLogProgress(Camera, "Matching pre-allocated stream with ID: %d exists", outStreamID);
|
|
return Status::Success;
|
|
}
|
|
|
|
// Try to find an unused compatible available stream
|
|
for (auto & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams())
|
|
{
|
|
if (!stream.isAllocated && stream.IsCompatible(allocateArgs))
|
|
{
|
|
uint32_t candidateEncodedPixelRate =
|
|
allocateArgs.maxFrameRate * allocateArgs.maxResolution.height * allocateArgs.maxResolution.width;
|
|
bool encoderRequired = true;
|
|
if (!GetCameraAVStreamManagementCluster()->IsResourceAvailableForStreamAllocation(candidateEncodedPixelRate,
|
|
encoderRequired))
|
|
{
|
|
return Status::ResourceExhausted;
|
|
}
|
|
stream.isAllocated = true;
|
|
outStreamID = stream.videoStreamParams.videoStreamID;
|
|
|
|
// Set the default viewport on the newly allocated stream
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetViewport(stream, mCameraDeviceHAL->GetCameraHALInterface().GetViewport());
|
|
|
|
// Set the current frame rate attribute from HAL
|
|
GetCameraAVStreamManagementCluster()->SetCurrentFrameRate(
|
|
mCameraDeviceHAL->GetCameraHALInterface().GetCurrentFrameRate());
|
|
|
|
return Status::Success;
|
|
}
|
|
}
|
|
|
|
// No compatible stream available for use.
|
|
return Status::ResourceExhausted;
|
|
}
|
|
|
|
void CameraAVStreamManager::OnVideoStreamAllocated(const VideoStreamStruct & allocatedStream, StreamAllocationAction action)
|
|
{
|
|
switch (action)
|
|
{
|
|
case StreamAllocationAction::kNewAllocation:
|
|
ChipLogProgress(Camera, "Starting new video stream with ID: %u", allocatedStream.videoStreamID);
|
|
// mCameraDeviceHAL->GetCameraHALInterface().StartVideoStream(allocatedStream);
|
|
|
|
// Set the current frame rate attribute from HAL once stream has started
|
|
GetCameraAVStreamManagementCluster()->SetCurrentFrameRate(mCameraDeviceHAL->GetCameraHALInterface().GetCurrentFrameRate());
|
|
break;
|
|
|
|
case StreamAllocationAction::kModification:
|
|
// Find the stream and restart it with new parameters
|
|
for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams())
|
|
{
|
|
if (stream.videoStreamParams.videoStreamID == allocatedStream.videoStreamID && stream.isAllocated)
|
|
{
|
|
// For modifications, we always stop and restart the stream to ensure
|
|
// new parameters are applied
|
|
ChipLogProgress(Camera, "Restarting video stream with ID: %u due to modifications", allocatedStream.videoStreamID);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case StreamAllocationAction::kReuse:
|
|
ChipLogProgress(Camera, "Reusing existing video stream with ID: %u without changes", allocatedStream.videoStreamID);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::VideoStreamModify(const uint16_t streamID,
|
|
const chip::Optional<bool> waterMarkEnabled,
|
|
const chip::Optional<bool> osdEnabled)
|
|
{
|
|
for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams())
|
|
{
|
|
if (stream.videoStreamParams.videoStreamID == streamID && stream.isAllocated)
|
|
{
|
|
// TODO: Link with HAL APIs to return error
|
|
if (waterMarkEnabled.HasValue())
|
|
{
|
|
stream.videoStreamParams.watermarkEnabled = waterMarkEnabled;
|
|
}
|
|
if (osdEnabled.HasValue())
|
|
{
|
|
stream.videoStreamParams.OSDEnabled = osdEnabled;
|
|
}
|
|
ChipLogError(Camera, "Modified video stream with ID: %d", streamID);
|
|
return Status::Success;
|
|
}
|
|
}
|
|
|
|
ChipLogError(Camera, "Allocated video stream with ID: %d not found", streamID);
|
|
|
|
return Status::NotFound;
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::VideoStreamDeallocate(const uint16_t streamID)
|
|
{
|
|
for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams())
|
|
{
|
|
if (stream.videoStreamParams.videoStreamID == streamID && stream.isAllocated)
|
|
{
|
|
stream.isAllocated = false;
|
|
return Status::Success;
|
|
}
|
|
}
|
|
|
|
ChipLogError(Camera, "Allocated video stream with ID: %d not found internally", streamID);
|
|
|
|
return Status::NotFound;
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::AudioStreamAllocate(const AudioStreamStruct & allocateArgs,
|
|
uint16_t & outStreamID)
|
|
{
|
|
outStreamID = kInvalidStreamID;
|
|
|
|
for (AudioStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams())
|
|
{
|
|
if (stream.IsCompatible(allocateArgs))
|
|
{
|
|
outStreamID = stream.audioStreamParams.audioStreamID;
|
|
if (!stream.isAllocated)
|
|
{
|
|
stream.isAllocated = true;
|
|
return Status::Success;
|
|
}
|
|
else
|
|
{
|
|
ChipLogProgress(Camera, "Matching pre-allocated stream with ID: %d exists", outStreamID);
|
|
}
|
|
return Status::Success;
|
|
}
|
|
}
|
|
|
|
return Status::DynamicConstraintError;
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::AudioStreamDeallocate(const uint16_t streamID)
|
|
{
|
|
for (AudioStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams())
|
|
{
|
|
if (stream.audioStreamParams.audioStreamID == streamID && stream.isAllocated)
|
|
{
|
|
stream.isAllocated = false;
|
|
return Status::Success;
|
|
}
|
|
}
|
|
|
|
ChipLogError(Camera, "Allocated audio stream with ID: %d not found internally", streamID);
|
|
|
|
return Status::NotFound;
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::SnapshotStreamAllocate(const SnapshotStreamAllocateArgs & allocateArgs,
|
|
uint16_t & outStreamID)
|
|
{
|
|
outStreamID = kInvalidStreamID;
|
|
bool isRequestSupportedByAnyAvailableStream = false;
|
|
|
|
// Check if allocation request can be supported
|
|
for (const auto & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableSnapshotStreams())
|
|
{
|
|
if (stream.IsCompatible(allocateArgs))
|
|
{
|
|
isRequestSupportedByAnyAvailableStream = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isRequestSupportedByAnyAvailableStream)
|
|
{
|
|
return Status::DynamicConstraintError;
|
|
}
|
|
|
|
// Try to reuse an allocated stream.
|
|
std::optional<uint16_t> reusableStreamId = GetCameraAVStreamManagementCluster()->GetReusableSnapshotStreamId(allocateArgs);
|
|
|
|
if (reusableStreamId.has_value())
|
|
{
|
|
// Found a stream that can be reused
|
|
outStreamID = reusableStreamId.value();
|
|
ChipLogProgress(Camera, "Matching pre-allocated stream with ID: %d exists", outStreamID);
|
|
return Status::Success;
|
|
}
|
|
|
|
uint32_t candidateEncodedPixelRate = 0;
|
|
bool encoderRequired = false;
|
|
if (allocateArgs.encodedPixels)
|
|
{
|
|
candidateEncodedPixelRate +=
|
|
allocateArgs.maxFrameRate * allocateArgs.maxResolution.height * allocateArgs.maxResolution.width;
|
|
if (allocateArgs.hardwareEncoder)
|
|
{
|
|
encoderRequired = true;
|
|
}
|
|
}
|
|
|
|
if (!GetCameraAVStreamManagementCluster()->IsResourceAvailableForStreamAllocation(candidateEncodedPixelRate, encoderRequired))
|
|
{
|
|
return Status::ResourceExhausted;
|
|
}
|
|
|
|
// If no pre-allocated stream matches, try allocating a new one.
|
|
if (mCameraDeviceHAL->GetCameraHALInterface().AllocateSnapshotStream(allocateArgs, outStreamID) == CameraError::SUCCESS)
|
|
{
|
|
return Status::Success;
|
|
}
|
|
|
|
// Try to find an unused compatible available stream
|
|
for (auto & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableSnapshotStreams())
|
|
{
|
|
if (!stream.isAllocated && stream.IsCompatible(allocateArgs))
|
|
{
|
|
stream.isAllocated = true;
|
|
outStreamID = stream.snapshotStreamParams.snapshotStreamID;
|
|
|
|
// Set the optional Watermark and OSD values that may have been provided.
|
|
// This is the initial setting of these values, they may be subsequently
|
|
// modified. If the values have no value that is ok, the allocated stream
|
|
// will store as such and ignore.
|
|
stream.snapshotStreamParams.watermarkEnabled = allocateArgs.watermarkEnabled;
|
|
stream.snapshotStreamParams.OSDEnabled = allocateArgs.OSDEnabled;
|
|
|
|
return Status::Success;
|
|
}
|
|
}
|
|
|
|
// No compatible stream available for use.
|
|
return Status::ResourceExhausted;
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::SnapshotStreamModify(const uint16_t streamID,
|
|
const chip::Optional<bool> waterMarkEnabled,
|
|
const chip::Optional<bool> osdEnabled)
|
|
{
|
|
for (SnapshotStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableSnapshotStreams())
|
|
{
|
|
if (stream.snapshotStreamParams.snapshotStreamID == streamID && stream.isAllocated)
|
|
{
|
|
// TODO: Link with HAL APIs to return error
|
|
if (waterMarkEnabled.HasValue())
|
|
{
|
|
stream.snapshotStreamParams.watermarkEnabled = waterMarkEnabled;
|
|
}
|
|
if (osdEnabled.HasValue())
|
|
{
|
|
stream.snapshotStreamParams.OSDEnabled = osdEnabled;
|
|
}
|
|
ChipLogError(Camera, "Modified snapshot stream with ID: %d", streamID);
|
|
return Status::Success;
|
|
}
|
|
}
|
|
|
|
ChipLogError(Camera, "Allocated snapshot stream with ID: %d not found", streamID);
|
|
|
|
return Status::NotFound;
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::SnapshotStreamDeallocate(const uint16_t streamID)
|
|
{
|
|
for (SnapshotStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableSnapshotStreams())
|
|
{
|
|
if (stream.snapshotStreamParams.snapshotStreamID == streamID && stream.isAllocated)
|
|
{
|
|
if (stream.snapshotStreamParams.referenceCount > 0)
|
|
{
|
|
ChipLogError(Camera, "Snapshot stream with ID: %d still in use", streamID);
|
|
return Status::InvalidInState;
|
|
}
|
|
stream.isAllocated = false;
|
|
|
|
return Status::Success;
|
|
}
|
|
}
|
|
|
|
ChipLogError(Camera, "Allocated snapshot stream with ID: %d not found", streamID);
|
|
|
|
return Status::NotFound;
|
|
}
|
|
|
|
void CameraAVStreamManager::OnStreamUsagePrioritiesChanged()
|
|
{
|
|
ChipLogProgress(Camera, "Stream usage priorities changed");
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetStreamUsagePriorities(
|
|
GetCameraAVStreamManagementCluster()->GetStreamUsagePriorities());
|
|
}
|
|
|
|
void CameraAVStreamManager::OnAttributeChanged(AttributeId attributeId)
|
|
{
|
|
ChipLogProgress(Camera, "Attribute changed for AttributeId = " ChipLogFormatMEI, ChipLogValueMEI(attributeId));
|
|
|
|
switch (attributeId)
|
|
{
|
|
case HDRModeEnabled::Id: {
|
|
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetHDRMode(GetCameraAVStreamManagementCluster()->GetHDRModeEnabled());
|
|
break;
|
|
}
|
|
case SoftRecordingPrivacyModeEnabled::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetSoftRecordingPrivacyModeEnabled(
|
|
GetCameraAVStreamManagementCluster()->GetSoftRecordingPrivacyModeEnabled());
|
|
break;
|
|
}
|
|
case SoftLivestreamPrivacyModeEnabled::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetSoftLivestreamPrivacyModeEnabled(
|
|
GetCameraAVStreamManagementCluster()->GetSoftLivestreamPrivacyModeEnabled());
|
|
break;
|
|
}
|
|
case NightVision::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetNightVision(GetCameraAVStreamManagementCluster()->GetNightVision());
|
|
break;
|
|
}
|
|
case NightVisionIllum::Id: {
|
|
break;
|
|
}
|
|
case Viewport::Id: {
|
|
// Update the device default
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetViewport(GetCameraAVStreamManagementCluster()->GetViewport());
|
|
|
|
// Update the per stream viewports on the camera
|
|
for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams())
|
|
{
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetViewport(stream, GetCameraAVStreamManagementCluster()->GetViewport());
|
|
}
|
|
break;
|
|
}
|
|
case SpeakerMuted::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetSpeakerMuted(GetCameraAVStreamManagementCluster()->GetSpeakerMuted());
|
|
break;
|
|
}
|
|
case SpeakerVolumeLevel::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetSpeakerVolume(GetCameraAVStreamManagementCluster()->GetSpeakerVolumeLevel());
|
|
break;
|
|
}
|
|
case MicrophoneMuted::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetMicrophoneMuted(GetCameraAVStreamManagementCluster()->GetMicrophoneMuted());
|
|
break;
|
|
}
|
|
case MicrophoneVolumeLevel::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetMicrophoneVolume(
|
|
GetCameraAVStreamManagementCluster()->GetMicrophoneVolumeLevel());
|
|
break;
|
|
}
|
|
case LocalVideoRecordingEnabled::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetLocalVideoRecordingEnabled(
|
|
GetCameraAVStreamManagementCluster()->GetLocalVideoRecordingEnabled());
|
|
break;
|
|
}
|
|
case LocalSnapshotRecordingEnabled::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetLocalSnapshotRecordingEnabled(
|
|
GetCameraAVStreamManagementCluster()->GetLocalSnapshotRecordingEnabled());
|
|
break;
|
|
}
|
|
case StatusLightEnabled::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetStatusLightEnabled(
|
|
GetCameraAVStreamManagementCluster()->GetStatusLightEnabled());
|
|
break;
|
|
}
|
|
case ImageRotation::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetImageRotation(GetCameraAVStreamManagementCluster()->GetImageRotation());
|
|
break;
|
|
}
|
|
case ImageFlipHorizontal::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetImageFlipHorizontal(
|
|
GetCameraAVStreamManagementCluster()->GetImageFlipHorizontal());
|
|
break;
|
|
}
|
|
case ImageFlipVertical::Id: {
|
|
mCameraDeviceHAL->GetCameraHALInterface().SetImageFlipVertical(
|
|
GetCameraAVStreamManagementCluster()->GetImageFlipVertical());
|
|
break;
|
|
}
|
|
default:
|
|
ChipLogProgress(Camera, "Unknown Attribute with AttributeId = " ChipLogFormatMEI, ChipLogValueMEI(attributeId));
|
|
}
|
|
}
|
|
|
|
Protocols::InteractionModel::Status CameraAVStreamManager::CaptureSnapshot(const Nullable<uint16_t> streamID,
|
|
const VideoResolutionStruct & resolution,
|
|
ImageSnapshot & outImageSnapshot)
|
|
{
|
|
if (mCameraDeviceHAL->GetCameraHALInterface().CaptureSnapshot(streamID, resolution, outImageSnapshot) == CameraError::SUCCESS)
|
|
{
|
|
return Status::Success;
|
|
}
|
|
else
|
|
{
|
|
return Status::Failure;
|
|
}
|
|
|
|
return Status::Failure;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::AllocatedVideoStreamsLoaded()
|
|
{
|
|
const std::vector<VideoStreamStruct> & persistedStreams = GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams();
|
|
auto & halStreams = mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams();
|
|
|
|
for (auto & halStream : halStreams)
|
|
{
|
|
auto it = std::find_if(persistedStreams.begin(), persistedStreams.end(), [&](const VideoStreamStruct & persistedStream) {
|
|
return persistedStream.videoStreamID == halStream.videoStreamParams.videoStreamID;
|
|
});
|
|
|
|
if (it != persistedStreams.end())
|
|
{
|
|
// Found in persisted streams, mark as allocated in HAL
|
|
halStream.isAllocated = true;
|
|
ChipLogProgress(Camera, "HAL Video Stream ID %u marked as allocated from persisted state.",
|
|
halStream.videoStreamParams.videoStreamID);
|
|
|
|
// Signal for starting the video stream
|
|
OnVideoStreamAllocated(*it, StreamAllocationAction::kNewAllocation);
|
|
}
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::AllocatedAudioStreamsLoaded()
|
|
{
|
|
const std::vector<AudioStreamStruct> & persistedStreams = GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams();
|
|
auto & halStreams = mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams();
|
|
|
|
for (auto & halStream : halStreams)
|
|
{
|
|
auto it = std::find_if(persistedStreams.begin(), persistedStreams.end(), [&](const AudioStreamStruct & persistedStream) {
|
|
return persistedStream.audioStreamID == halStream.audioStreamParams.audioStreamID;
|
|
});
|
|
|
|
if (it != persistedStreams.end())
|
|
{
|
|
// Found in persisted streams, mark as allocated in HAL
|
|
halStream.isAllocated = true;
|
|
ChipLogProgress(Camera, "HAL Audio Stream ID %u marked as allocated from persisted state.",
|
|
halStream.audioStreamParams.audioStreamID);
|
|
}
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::AllocatedSnapshotStreamsLoaded()
|
|
{
|
|
ChipLogError(Camera, "Allocated snapshot streams could not be loaded: %" CHIP_ERROR_FORMAT,
|
|
CHIP_ERROR_NOT_IMPLEMENTED.Format());
|
|
return CHIP_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::PersistentAttributesLoadedCallback()
|
|
{
|
|
ChipLogDetail(Camera, "Successfully loaded persistent attributes");
|
|
|
|
CHIP_ERROR err = AllocatedVideoStreamsLoaded();
|
|
if (err != CHIP_NO_ERROR)
|
|
{
|
|
ChipLogError(Camera, "Allocated video streams could not be loaded: %" CHIP_ERROR_FORMAT, err.Format());
|
|
return err;
|
|
}
|
|
|
|
err = AllocatedAudioStreamsLoaded();
|
|
if (err != CHIP_NO_ERROR)
|
|
{
|
|
ChipLogError(Camera, "Allocated audio streams could not be loaded: %" CHIP_ERROR_FORMAT, err.Format());
|
|
return err;
|
|
}
|
|
|
|
err = AllocatedSnapshotStreamsLoaded();
|
|
if (err != CHIP_NO_ERROR)
|
|
{
|
|
ChipLogError(Camera, "Allocated snapshot streams could not be loaded: %" CHIP_ERROR_FORMAT, err.Format());
|
|
return err;
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::OnTransportAcquireAudioVideoStreams(uint16_t audioStreamID, uint16_t videoStreamID)
|
|
{
|
|
// Update the available audio stream in the HAL
|
|
for (AudioStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams())
|
|
{
|
|
if (stream.audioStreamParams.audioStreamID == audioStreamID && stream.isAllocated)
|
|
{
|
|
if (stream.audioStreamParams.referenceCount < UINT8_MAX)
|
|
{
|
|
stream.audioStreamParams.referenceCount++;
|
|
}
|
|
else
|
|
{
|
|
ChipLogError(Camera, "Attempted to increment audio stream %u ref count beyond max limit", audioStreamID);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the available video stream in the HAL
|
|
for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams())
|
|
{
|
|
if (stream.videoStreamParams.videoStreamID == videoStreamID && stream.isAllocated)
|
|
{
|
|
if (stream.videoStreamParams.referenceCount < UINT8_MAX)
|
|
{
|
|
stream.videoStreamParams.referenceCount++;
|
|
}
|
|
else
|
|
{
|
|
ChipLogError(Camera, "Attempted to increment video stream %u ref count beyond max limit", videoStreamID);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the counts in the SDK allocated stream attributes
|
|
if (GetCameraAVStreamManagementCluster()->UpdateAudioStreamRefCount(audioStreamID, /* shouldIncrement = */ true) !=
|
|
CHIP_NO_ERROR)
|
|
{
|
|
ChipLogError(Camera, "Failed to increment audio stream %u ref count in SDK", audioStreamID);
|
|
}
|
|
|
|
if (GetCameraAVStreamManagementCluster()->UpdateVideoStreamRefCount(videoStreamID, /* shouldIncrement = */ true) !=
|
|
CHIP_NO_ERROR)
|
|
{
|
|
ChipLogError(Camera, "Failed to increment video stream %u ref count in SDK", videoStreamID);
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|
|
|
|
CHIP_ERROR
|
|
CameraAVStreamManager::OnTransportReleaseAudioVideoStreams(uint16_t audioStreamID, uint16_t videoStreamID)
|
|
{
|
|
// Update the available audio stream in the HAL
|
|
for (AudioStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams())
|
|
{
|
|
if (stream.audioStreamParams.audioStreamID == audioStreamID && stream.isAllocated)
|
|
{
|
|
if (stream.audioStreamParams.referenceCount > 0)
|
|
{
|
|
stream.audioStreamParams.referenceCount--;
|
|
}
|
|
else
|
|
{
|
|
ChipLogError(Camera,
|
|
"Attempted to decrement audio stream %u ref count when it "
|
|
"was already 0",
|
|
audioStreamID);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the available video stream in the HAL
|
|
for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams())
|
|
{
|
|
if (stream.videoStreamParams.videoStreamID == videoStreamID && stream.isAllocated)
|
|
{
|
|
if (stream.videoStreamParams.referenceCount > 0)
|
|
{
|
|
stream.videoStreamParams.referenceCount--;
|
|
}
|
|
else
|
|
{
|
|
ChipLogError(Camera,
|
|
"Attempted to decrement video stream %u ref count when it "
|
|
"was already 0",
|
|
videoStreamID);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the counts in the SDK allocated stream attributes
|
|
if (GetCameraAVStreamManagementCluster()->UpdateAudioStreamRefCount(audioStreamID, /* shouldIncrement = */ false) !=
|
|
CHIP_NO_ERROR)
|
|
{
|
|
ChipLogError(Camera, "Failed to decrement audio stream %u ref count in SDK", audioStreamID);
|
|
}
|
|
|
|
if (GetCameraAVStreamManagementCluster()->UpdateVideoStreamRefCount(videoStreamID, /* shouldIncrement = */ false) !=
|
|
CHIP_NO_ERROR)
|
|
{
|
|
ChipLogError(Camera, "Failed to decrement video stream %u ref count in SDK", videoStreamID);
|
|
}
|
|
|
|
return CHIP_NO_ERROR;
|
|
}
|