/** * * Copyright (c) 2021 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 "MediaPlaybackManager.h" #include "TvApp-JNI.h" #include #include #include #include #include #include #include #include #include "MediaPlaybackManager.h" #include using namespace chip; using namespace chip::app; using namespace chip::app::DataModel; using namespace chip::app::Clusters::MediaPlayback; using namespace chip::Uint8; using chip::CharSpan; /** @brief Media PlayBack Cluster Init * * This function is called when a specific cluster is initialized. It gives the * application an opportunity to take care of cluster initialization procedures. * It is called exactly once for each endpoint where cluster is present. * * @param endpoint Ver.: always * */ void emberAfMediaPlaybackClusterInitCallback(chip::EndpointId endpoint) { ChipLogProgress(Zcl, "TV Android App: MediaPlayback::PostClusterInit"); TvAppJNIMgr().PostClusterInit(chip::app::Clusters::MediaPlayback::Id, endpoint); } void MediaPlaybackManager::NewManager(jint endpoint, jobject manager) { ChipLogProgress(Zcl, "TV Android App: MediaPlayback::SetDefaultDelegate"); MediaPlaybackManager * mgr = new MediaPlaybackManager(); mgr->InitializeWithObjects(manager); chip::app::Clusters::MediaPlayback::SetDefaultDelegate(static_cast(endpoint), mgr); } PlaybackStateEnum MediaPlaybackManager::HandleGetCurrentState() { uint64_t ret = HandleMediaRequestGetAttribute(MEDIA_PLAYBACK_ATTRIBUTE_PLAYBACK_STATE); return static_cast(ret); } uint64_t MediaPlaybackManager::HandleGetStartTime() { return HandleMediaRequestGetAttribute(MEDIA_PLAYBACK_ATTRIBUTE_START_TIME); } uint64_t MediaPlaybackManager::HandleGetDuration() { return HandleMediaRequestGetAttribute(MEDIA_PLAYBACK_ATTRIBUTE_DURATION); } float MediaPlaybackManager::HandleGetPlaybackSpeed() { long ret = HandleMediaRequestGetLongAttribute(MEDIA_PLAYBACK_ATTRIBUTE_SPEED); return static_cast(ret); } uint64_t MediaPlaybackManager::HandleGetSeekRangeStart() { return HandleMediaRequestGetAttribute(MEDIA_PLAYBACK_ATTRIBUTE_SEEK_RANGE_START); } uint64_t MediaPlaybackManager::HandleGetSeekRangeEnd() { return HandleMediaRequestGetAttribute(MEDIA_PLAYBACK_ATTRIBUTE_SEEK_RANGE_END); } CHIP_ERROR MediaPlaybackManager::HandleGetActiveAudioTrack(AttributeValueEncoder & aEncoder) { return HandleGetActiveTrack(true, aEncoder); } CHIP_ERROR MediaPlaybackManager::HandleGetActiveTrack(bool audio, AttributeValueEncoder & aEncoder) { DeviceLayer::StackUnlock unlock; Structs::TrackStruct::Type response; Structs::TrackAttributesStruct::Type trackAttributes; response.trackAttributes = Nullable(trackAttributes); jobject trackObj; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturnError(env != nullptr, CHIP_JNI_ERROR_NULL_OBJECT, ChipLogError(Zcl, "Could not get JNIEnv for current thread")); JniLocalReferenceScope scope(env); ChipLogProgress(Zcl, "MediaPlaybackManager::HandleGetActiveAudioTrack"); VerifyOrExit(mMediaPlaybackManagerObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mGetActiveTrackMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); env->ExceptionClear(); trackObj = env->CallObjectMethod(mMediaPlaybackManagerObject.ObjectRef(), mGetActiveTrackMethod, static_cast(audio)); if (env->ExceptionCheck()) { ChipLogError(AppServer, "Java exception in MediaPlaybackManager::HandleGetActiveAudioTrack"); env->ExceptionDescribe(); env->ExceptionClear(); goto exit; } if (trackObj != nullptr) { jclass trackClass = env->GetObjectClass(trackObj); jfieldID getIdField = env->GetFieldID(trackClass, "id", "Ljava/lang/String;"); jstring jid = static_cast(env->GetObjectField(trackObj, getIdField)); JniUtfString id(env, jid); if (jid != nullptr) { response.id = id.charSpan(); } jfieldID getLanguageCodeField = env->GetFieldID(trackClass, "languageCode", "Ljava/lang/String;"); jstring jlanguagecode = static_cast(env->GetObjectField(trackObj, getLanguageCodeField)); JniUtfString languageCode(env, jlanguagecode); if (jlanguagecode != nullptr) { trackAttributes.languageCode = languageCode.charSpan(); } jfieldID getDisplayNameField = env->GetFieldID(trackClass, "displayName", "Ljava/lang/String;"); jstring jdisplayname = static_cast(env->GetObjectField(trackObj, getDisplayNameField)); JniUtfString displayName(env, jdisplayname); if (jdisplayname != nullptr) { trackAttributes.displayName = Optional>>( Nullable>(displayName.charSpan())); } } else { return aEncoder.EncodeNull(); } exit: if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "MediaPlaybackManager::HandleGetActiveAudioTrack status error: %s", err.AsString()); } return aEncoder.Encode(response); } CHIP_ERROR MediaPlaybackManager::HandleGetAvailableAudioTracks(AttributeValueEncoder & aEncoder) { return HandleGetAvailableTracks(true, aEncoder); } CHIP_ERROR MediaPlaybackManager::HandleGetAvailableTracks(bool audio, AttributeValueEncoder & aEncoder) { DeviceLayer::StackUnlock unlock; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturnError(env != nullptr, CHIP_JNI_ERROR_NULL_OBJECT, ChipLogError(Zcl, "Could not get JNIEnv for current thread")); JniLocalReferenceScope scope(env); ChipLogProgress(Zcl, "MediaPlaybackManager::HandleGetAvailableAudioTracks"); VerifyOrExit(mMediaPlaybackManagerObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mGetAvailableTracksMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); env->ExceptionClear(); return aEncoder.EncodeList([this, env, audio](const auto & encoder) -> CHIP_ERROR { jobjectArray trackList = (jobjectArray) env->CallObjectMethod(mMediaPlaybackManagerObject.ObjectRef(), mGetAvailableTracksMethod, static_cast(audio)); if (env->ExceptionCheck()) { ChipLogError(Zcl, "Java exception in MediaPlaybackManager::HandleGetAvailableAudioTracks"); env->ExceptionDescribe(); env->ExceptionClear(); return CHIP_ERROR_INCORRECT_STATE; } jint length = env->GetArrayLength(trackList); for (jint i = 0; i < length; i++) { Structs::TrackStruct::Type track; Structs::TrackAttributesStruct::Type trackAttributes; track.trackAttributes = Nullable(trackAttributes); jobject trackObject = env->GetObjectArrayElement(trackList, i); jclass trackClass = env->GetObjectClass(trackObject); jfieldID getIdField = env->GetFieldID(trackClass, "id", "Ljava/lang/String;"); jstring jid = static_cast(env->GetObjectField(trackObject, getIdField)); JniUtfString id(env, jid); if (jid != nullptr) { track.id = id.charSpan(); } jfieldID getLanguageCodeField = env->GetFieldID(trackClass, "languageCode", "Ljava/lang/String;"); jstring jlanguagecode = static_cast(env->GetObjectField(trackObject, getLanguageCodeField)); JniUtfString languageCode(env, jlanguagecode); if (jlanguagecode != nullptr) { trackAttributes.languageCode = languageCode.charSpan(); } jfieldID getDisplayNameField = env->GetFieldID(trackClass, "displayName", "Ljava/lang/String;"); jstring jdisplayname = static_cast(env->GetObjectField(trackObject, getDisplayNameField)); JniUtfString displayName(env, jdisplayname); if (jdisplayname != nullptr) { trackAttributes.displayName = Optional>>( Nullable>(displayName.charSpan())); } ReturnErrorOnFailure(encoder.Encode(track)); } return CHIP_NO_ERROR; }); exit: if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "MediaPlaybackManager::HandleGetAvailableAudioTracks status error: %s", err.AsString()); } return err; } CHIP_ERROR MediaPlaybackManager::HandleGetActiveTextTrack(AttributeValueEncoder & aEncoder) { return HandleGetActiveTrack(false, aEncoder); } CHIP_ERROR MediaPlaybackManager::HandleGetAvailableTextTracks(AttributeValueEncoder & aEncoder) { return HandleGetAvailableTracks(false, aEncoder); } void MediaPlaybackManager::HandlePlay(CommandResponseHelper & helper) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_PLAY, 0)); } void MediaPlaybackManager::HandlePause(CommandResponseHelper & helper) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_PAUSE, 0)); } void MediaPlaybackManager::HandleStop(CommandResponseHelper & helper) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_STOP, 0)); } void MediaPlaybackManager::HandleFastForward(CommandResponseHelper & helper, const chip::Optional & audioAdvanceUnmuted) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_FAST_FORWARD, 0)); } void MediaPlaybackManager::HandlePrevious(CommandResponseHelper & helper) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_PREVIOUS, 0)); } void MediaPlaybackManager::HandleRewind(CommandResponseHelper & helper, const chip::Optional & audioAdvanceUnmuted) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_REWIND, 0)); } void MediaPlaybackManager::HandleSkipBackward(CommandResponseHelper & helper, const uint64_t & deltaPositionMilliseconds) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_SKIP_BACKWARD, deltaPositionMilliseconds)); } void MediaPlaybackManager::HandleSkipForward(CommandResponseHelper & helper, const uint64_t & deltaPositionMilliseconds) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_SKIP_FORWARD, deltaPositionMilliseconds)); } void MediaPlaybackManager::HandleSeek(CommandResponseHelper & helper, const uint64_t & positionMilliseconds) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_SEEK, positionMilliseconds)); } void MediaPlaybackManager::HandleNext(CommandResponseHelper & helper) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_NEXT, 0)); } void MediaPlaybackManager::HandleStartOver(CommandResponseHelper & helper) { helper.Success(HandleMediaRequest(MEDIA_PLAYBACK_REQUEST_START_OVER, 0)); } bool MediaPlaybackManager::HandleActivateAudioTrack(const chip::CharSpan & trackId, const uint8_t & audioOutputIndex) { return HandleActivateTrack(true, trackId); } bool MediaPlaybackManager::HandleActivateTrack(bool audio, const chip::CharSpan & trackId) { DeviceLayer::StackUnlock unlock; std::string id(trackId.data(), trackId.size()); jint ret = -1; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturnValue(env != nullptr, false, ChipLogError(Zcl, "Could not get JNIEnv for current thread")); JniLocalReferenceScope scope(env); ChipLogProgress(Zcl, "MediaPlaybackManager::HandleActivateAudioTrack"); VerifyOrExit(mMediaPlaybackManagerObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mActivateTrackMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); env->ExceptionClear(); { UtfString jniid(env, id.c_str()); ret = env->CallIntMethod(mMediaPlaybackManagerObject.ObjectRef(), mActivateTrackMethod, static_cast(audio), jniid.jniValue()); if (env->ExceptionCheck()) { ChipLogError(AppServer, "Java exception in MediaPlaybackManager::HandleActivateTrack %s", id.c_str()); env->ExceptionDescribe(); env->ExceptionClear(); } } exit: return static_cast(ret) == 0; } bool MediaPlaybackManager::HandleActivateTextTrack(const chip::CharSpan & trackId) { return HandleActivateTrack(false, trackId); } bool MediaPlaybackManager::HandleDeactivateTextTrack() { DeviceLayer::StackUnlock unlock; jint ret = -1; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturnValue(env != nullptr, false, ChipLogError(Zcl, "Could not get JNIEnv for current thread")); JniLocalReferenceScope scope(env); ChipLogProgress(Zcl, "MediaPlaybackManager::HandleDeactivateTextTrack"); VerifyOrExit(mMediaPlaybackManagerObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mDeactivateTextTrackMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); env->ExceptionClear(); ret = env->CallIntMethod(mMediaPlaybackManagerObject.ObjectRef(), mDeactivateTextTrackMethod); if (env->ExceptionCheck()) { ChipLogError(AppServer, "Java exception in MediaPlaybackManager::HandleDeactivateTextTrack"); env->ExceptionDescribe(); env->ExceptionClear(); } exit: return static_cast(ret) == 0; } void MediaPlaybackManager::InitializeWithObjects(jobject managerObject) { JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for MediaPlaybackManager")); VerifyOrReturn(mMediaPlaybackManagerObject.Init(managerObject) == CHIP_NO_ERROR, ChipLogError(Zcl, "Failed to init mMediaPlaybackManagerObject")); jclass mMediaPlaybackManagerClass = env->GetObjectClass(managerObject); VerifyOrReturn(mMediaPlaybackManagerClass != nullptr, ChipLogError(Zcl, "Failed to get MediaPlaybackManager Java class")); mGetAttributeMethod = env->GetMethodID(mMediaPlaybackManagerClass, "getAttributes", "(I)J"); if (mGetAttributeMethod == nullptr) { ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'getAttributes' method"); env->ExceptionClear(); } mRequestMethod = env->GetMethodID(mMediaPlaybackManagerClass, "request", "(IJ)I"); if (mRequestMethod == nullptr) { ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'request' method"); env->ExceptionClear(); } mGetPositionMethod = env->GetMethodID(mMediaPlaybackManagerClass, "getPosition", "()Lcom/matter/tv/server/tvapp/MediaPlaybackPosition;"); if (mGetPositionMethod == nullptr) { ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'getPosition' method"); env->ExceptionClear(); } mGetAvailableTracksMethod = env->GetMethodID(mMediaPlaybackManagerClass, "getAvailableTracks", "(Z)[Lcom/matter/tv/server/tvapp/MediaTrack;"); // TODO if (mGetAvailableTracksMethod == nullptr) { ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'getAvailableTracks' method"); env->ExceptionClear(); } mActivateTrackMethod = env->GetMethodID(mMediaPlaybackManagerClass, "activateTrack", "(ZLjava/lang/String;)I"); if (mActivateTrackMethod == nullptr) { ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'activateTrack' method"); env->ExceptionClear(); } mDeactivateTextTrackMethod = env->GetMethodID(mMediaPlaybackManagerClass, "deactivateTextTrack", "()I"); if (mDeactivateTextTrackMethod == nullptr) { ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'deactivateTextTrack' method"); env->ExceptionClear(); } mGetActiveTrackMethod = env->GetMethodID(mMediaPlaybackManagerClass, "getActiveTrack", "(Z)Lcom/matter/tv/server/tvapp/MediaTrack;"); if (mGetActiveTrackMethod == nullptr) { ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'getActiveTrack' method"); env->ExceptionClear(); } } uint64_t MediaPlaybackManager::HandleMediaRequestGetAttribute(MediaPlaybackRequestAttribute attribute) { DeviceLayer::StackUnlock unlock; uint64_t ret = std::numeric_limits::max(); jlong jAttributeValue = -1; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturnValue(env != nullptr, ret, ChipLogError(Zcl, "Could not get JNIEnv for current thread")); JniLocalReferenceScope scope(env); ChipLogProgress(Zcl, "Received MediaPlaybackManager::HandleMediaRequestGetAttribute:%d", attribute); VerifyOrExit(mMediaPlaybackManagerObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mGetAttributeMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); env->ExceptionClear(); jAttributeValue = env->CallLongMethod(mMediaPlaybackManagerObject.ObjectRef(), mGetAttributeMethod, static_cast(attribute)); if (env->ExceptionCheck()) { ChipLogError(AppServer, "Java exception in MediaPlaybackManager::GetAttribute"); env->ExceptionDescribe(); env->ExceptionClear(); goto exit; } if (jAttributeValue >= 0) { ret = static_cast(jAttributeValue); } else { err = CHIP_ERROR_INCORRECT_STATE; } exit: if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "MediaPlaybackManager::GetAttribute status error: %s", err.AsString()); } return ret; } long MediaPlaybackManager::HandleMediaRequestGetLongAttribute(MediaPlaybackRequestAttribute attribute) { DeviceLayer::StackUnlock unlock; long ret = 0; jlong jAttributeValue = -1; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturnValue(env != nullptr, false, ChipLogError(Zcl, "Could not get JNIEnv for current thread")); JniLocalReferenceScope scope(env); ChipLogProgress(Zcl, "Received MediaPlaybackManager::HandleMediaRequestGetLongAttribute:%d", attribute); VerifyOrExit(mMediaPlaybackManagerObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mGetAttributeMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); env->ExceptionClear(); jAttributeValue = env->CallLongMethod(mMediaPlaybackManagerObject.ObjectRef(), mGetAttributeMethod, static_cast(attribute)); if (env->ExceptionCheck()) { ChipLogError(AppServer, "Java exception in MediaPlaybackManager::GetAttribute"); env->ExceptionDescribe(); env->ExceptionClear(); goto exit; } ret = static_cast(jAttributeValue); exit: if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "MediaPlaybackManager::GetAttribute status error: %s", err.AsString()); } return ret; } Commands::PlaybackResponse::Type MediaPlaybackManager::HandleMediaRequest(MediaPlaybackRequest mediaPlaybackRequest, uint64_t deltaPositionMilliseconds) { DeviceLayer::StackUnlock unlock; Commands::PlaybackResponse::Type response; jint ret = -1; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturnValue(env != nullptr, response, ChipLogError(Zcl, "Could not get JNIEnv for current thread")); JniLocalReferenceScope scope(env); ChipLogProgress(Zcl, "MediaPlaybackManager::Request %d-%ld", mediaPlaybackRequest, static_cast(deltaPositionMilliseconds)); VerifyOrExit(mMediaPlaybackManagerObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mRequestMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); env->ExceptionClear(); ret = env->CallIntMethod(mMediaPlaybackManagerObject.ObjectRef(), mRequestMethod, static_cast(mediaPlaybackRequest), static_cast(deltaPositionMilliseconds)); if (env->ExceptionCheck()) { ChipLogError(AppServer, "Java exception in MediaPlaybackManager::Request %d", mediaPlaybackRequest); env->ExceptionDescribe(); env->ExceptionClear(); response.status = StatusEnum::kInvalidStateForCommand; } response.status = static_cast(ret); exit: if (err != CHIP_NO_ERROR) { response.status = StatusEnum::kInvalidStateForCommand; ChipLogError(Zcl, "MediaPlaybackManager::HandleMediaRequest status error: %s", err.AsString()); } return response; } CHIP_ERROR MediaPlaybackManager::HandleGetSampledPosition(AttributeValueEncoder & aEncoder) { DeviceLayer::StackUnlock unlock; Structs::PlaybackPositionStruct::Type response; response.updatedAt = 0; response.position = Nullable(0); jobject positionObj; CHIP_ERROR err = CHIP_NO_ERROR; JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); VerifyOrReturnError(env != nullptr, CHIP_JNI_ERROR_NULL_OBJECT, ChipLogError(Zcl, "Could not get JNIEnv for current thread")); JniLocalReferenceScope scope(env); ChipLogProgress(Zcl, "MediaPlaybackManager::HandleGetSampledPosition"); VerifyOrExit(mMediaPlaybackManagerObject.HasValidObjectRef(), err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mGetPositionMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); env->ExceptionClear(); positionObj = env->CallObjectMethod(mMediaPlaybackManagerObject.ObjectRef(), mGetPositionMethod); if (env->ExceptionCheck()) { ChipLogError(AppServer, "Java exception in MediaPlaybackManager::HandleGetSampledPosition"); env->ExceptionDescribe(); env->ExceptionClear(); goto exit; } { jclass inputClass = env->GetObjectClass(positionObj); jfieldID positionId = env->GetFieldID(inputClass, "position", "J"); jfieldID updatedAtId = env->GetFieldID(inputClass, "updatedAt", "J"); response.position = Nullable(static_cast(env->GetLongField(positionObj, positionId))); response.updatedAt = static_cast(env->GetLongField(positionObj, updatedAtId)); } exit: if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "MediaPlaybackManager::GetAttribute status error: %" CHIP_ERROR_FORMAT, err.Format()); } return aEncoder.Encode(response); } uint32_t MediaPlaybackManager::GetFeatureMap(chip::EndpointId endpoint) { if (endpoint >= MATTER_DM_CONTENT_LAUNCHER_CLUSTER_SERVER_ENDPOINT_COUNT) { return kEndpointFeatureMap; } uint32_t featureMap = 0; Attributes::FeatureMap::Get(endpoint, &featureMap); return featureMap; } uint16_t MediaPlaybackManager::GetClusterRevision(chip::EndpointId endpoint) { if (endpoint >= MATTER_DM_CONTENT_LAUNCHER_CLUSTER_SERVER_ENDPOINT_COUNT) { return kClusterRevision; } uint16_t clusterRevision = 0; bool success = (Attributes::ClusterRevision::Get(endpoint, &clusterRevision) == chip::Protocols::InteractionModel::Status::Success); if (!success) { ChipLogError(Zcl, "MediaPlaybackManager::GetClusterRevision error reading cluster revision"); } return clusterRevision; }