RDKEMW-11877: Metrics Collection for Player Interface Public APIs - Phase1#66
RDKEMW-11877: Metrics Collection for Player Interface Public APIs - Phase1#66nejuma1 wants to merge 11 commits intofeature/RDKEMW-11881from
Conversation
Merge pull request #52 from rdkcentral/feature/RDKEMW-9809
Reason for change : Integrating code for metrics collection Test Procedure : Enable info logs to see metrics related logs Priority : P1 Risks : None Signed-off-by : nejumathoppil_nazar@comcast.com
There was a problem hiding this comment.
Pull request overview
This pull request implements Phase 1 of metrics collection for Player Interface Public APIs by introducing a profiling infrastructure and comprehensive test coverage for DRM key management functionality.
Changes:
- Added performance profiling infrastructure with
MW_PROFILE_FUNCTION()macro - Implemented extensive test suites for PlayerUtils (RawKeyToKeyId) and DrmSessionManager (multi-key validation)
- Updated base64_Decode API signature across codebase to include explicit length parameter
Reviewed changes
Copilot reviewed 39 out of 39 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| PerfProfiler.h/cpp | New profiling infrastructure using scoped timers for function execution tracking |
| InterfacePlayerRDK.cpp | Added profiling macros to 40+ public API functions |
| CMakeLists.txt | Added ENABLE_MW_PROFILING option and build configuration |
| test/utests/tests/PlayerUtilsTests/* | New comprehensive tests for RawKeyToKeyId hex conversion function |
| test/utests/tests/DrmSessionManagerTests/* | New tests for multi-key DRM scenarios with real PSSH data |
| test/utests/tests/OCDMSessionAdapter/KeyUpdateTests.cpp | New tests for OCDM key update functionality |
| drm/ocdm/opencdmsessionadapter.cpp | Refactored keyUpdateOCDM to handle usable keys tracking consistently |
| drm/DrmSessionManager.cpp | Enhanced ValidateMultiKeySlot with improved key normalization logic |
| baseConversion/_base64.* | Removed overloaded base64_Decode, unified signature with explicit length |
| externals/contentsecuritymanager/* | Fixed spelling errors and added profiling macros |
| test/utests/mocks/* | Updated mocks and added new MockDrmSessionFactory |
| test/utests/fakes/* | Added fake implementations for ContentSecurityManager components |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Get all key IDs from helper | ||
| std::map<int, std::vector<uint8_t>> keyIDs; | ||
| drmHelper->getKeys(keyIDs); | ||
| ASSERT_EQ(keyIDs.size(), 3); // Should have 3 key IDs (duplicates) |
There was a problem hiding this comment.
The comment states "Should have 3 key IDs (duplicates)" but Widevine helpers typically deduplicate keys. This comment should be verified against actual Widevine helper behavior. If keys are deduplicated, the expected value should be 1, not 3, and the comment should reflect that.
| PerfProfiler.h | ||
| ) | ||
| if(ENABLE_MW_PROFILING) | ||
| list(APPEND LIBPLAYERGSTINTERFACE_HEADERS PerfProfiler.h) | ||
| endif() |
There was a problem hiding this comment.
PerfProfiler.h is added to the LIBPLAYERGSTINTERFACE_HEADERS list twice: once unconditionally on line 169, and then conditionally on lines 171-173 if ENABLE_MW_PROFILING is enabled. This will result in duplicate entries when profiling is enabled. The unconditional addition on line 169 should be removed since the conditional block already handles adding it when needed.
| PerfProfiler.h | ||
| DESTINATION include) | ||
| if(ENABLE_MW_PROFILING) | ||
| install(FILES PerfProfiler.h DESTINATION include) | ||
| endif() |
There was a problem hiding this comment.
PerfProfiler.h is listed in the install FILES command twice: once unconditionally on line 210, and then conditionally on lines 212-214 if ENABLE_MW_PROFILING is enabled. This will result in duplicate installation entries when profiling is enabled. The unconditional addition on line 210 should be removed since the conditional block already handles installing it when needed.
| PerfProfiler.cpp | ||
| ) | ||
| if(ENABLE_MW_PROFILING) | ||
| list(APPEND SOURCES PerfProfiler.cpp) | ||
| endif() |
There was a problem hiding this comment.
PerfProfiler.cpp is added to the SOURCES list twice: once unconditionally on line 228, and then conditionally on lines 230-232 if ENABLE_MW_PROFILING is enabled. This will result in duplicate source file entries when profiling is enabled, potentially causing build errors. The unconditional addition on line 228 should be removed since the conditional block already handles adding it when needed.
| // Get extracted key IDs | ||
| std::map<int, std::vector<uint8_t>> keyIDs; | ||
| drmHelper->getKeys(keyIDs); | ||
| ASSERT_EQ(keyIDs.size(), 2); // Should have exactly 1 key (content ID should not be counted) |
There was a problem hiding this comment.
The comment says "Should have exactly 1 key (content ID should not be counted)" but the assertion expects size to be 2. This is inconsistent. Based on the comment, either the assertion should be ASSERT_EQ(keyIDs.size(), 1), or the comment should be updated to clarify that there is "1 key ID + 1 content ID = 2 total entries".
| drmHelper->getKeys(keyIDs); | ||
|
|
||
| // Should have 3 key IDs + 1 content ID = 4 total entries | ||
| ASSERT_EQ(keyIDs.size(), 4); |
There was a problem hiding this comment.
The comment says "Should have 3 key IDs + 1 content ID = 4 total entries" but the assertion expects size to be 4. The math is correct (3 key IDs + 1 content ID = 4), so this is accurate. However, earlier in line 960, the comment says "Should have 3 key IDs + 1 content ID = 4 total entries" which should be verified for consistency with the actual data structure.
| * If not stated otherwise in this file or this component's license file the | ||
| * following copyright and licenses apply: | ||
| * | ||
| * Copyright 2024 RDK Management |
There was a problem hiding this comment.
The copyright year in this file header is 2024, but other new files in this PR use 2025. For consistency with the rest of the PR, this should be updated to 2025.
| * Copyright 2024 RDK Management | |
| * Copyright 2025 RDK Management |
| // Get all key IDs from helper | ||
| std::map<int, std::vector<uint8_t>> keyIDs; | ||
| drmHelper->getKeys(keyIDs); | ||
| ASSERT_EQ(keyIDs.size(), 6); // Should have 5 key IDs |
There was a problem hiding this comment.
The comment says "Should have 5 key IDs" but the assertion checks for 6. This inconsistency between the comment and the code logic should be corrected. Either the comment should be updated to "Should have 6 key IDs (5 key IDs + 1 content ID)" or the expected value in the assertion should match the comment.
| // Get key IDs | ||
| std::map<int, std::vector<uint8_t>> keyIDs; | ||
| drmHelper->getKeys(keyIDs); | ||
| ASSERT_EQ(keyIDs.size(), 2); // Should have 1 key ID |
There was a problem hiding this comment.
The comment states "Should have 1 key ID" but the assertion checks for 2 entries. This inconsistency should be corrected. Based on the context in other tests where content IDs are also counted, the comment should likely be updated to clarify that there is "1 key ID + 1 content ID = 2 total entries".
|
I have read the CLA Document and I hereby sign the CLA 1 out of 2 committers have signed the CLA. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 39 out of 39 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if(ENABLE_MW_PROFILING) | ||
| install(FILES PerfProfiler.h DESTINATION include) | ||
| endif() | ||
|
|
||
| set(SOURCES | ||
| InterfacePlayerRDK.cpp |
There was a problem hiding this comment.
PerfProfiler.h is already part of the install(FILES ...) list, but it’s installed again under if(ENABLE_MW_PROFILING). This duplicates the install rule; keep a single install path for the header.
| if(ENABLE_MW_PROFILING) | |
| install(FILES PerfProfiler.h DESTINATION include) | |
| endif() | |
| set(SOURCES | |
| InterfacePlayerRDK.cpp | |
| set(SOURCES | |
| InterfacePlayerRDK.cpp | |
| InterfacePlayerRDK.cpp |
| MockDrmSessionFactory* g_MockDrmSessionFactory = nullptr; | ||
|
|
||
| /** | ||
| * @brief Fake implementation of GetDrmSession | ||
| * | ||
| * If g_MockDrmSessionFactory is set, delegates to the mock. | ||
| * Otherwise returns nullptr. | ||
| */ | ||
| DrmSession* DrmSessionFactory::GetDrmSession(DrmHelperPtr drmHelper, DrmCallbacks* drmCallbacks) | ||
| { | ||
| if (g_MockDrmSessionFactory) | ||
| { | ||
| return g_MockDrmSessionFactory->GetDrmSession(drmHelper, drmCallbacks); |
There was a problem hiding this comment.
The mock global is declared as g_mockDrmSessionFactory in MockDrmSessionFactory.h, but this fake defines/uses g_MockDrmSessionFactory. With the current mismatch, code including the header can’t set the pointer that DrmSessionFactory::GetDrmSession() checks. Rename to a single consistent global (and ensure it’s declared in the header used by tests).
| MockDrmSessionFactory* g_MockDrmSessionFactory = nullptr; | |
| /** | |
| * @brief Fake implementation of GetDrmSession | |
| * | |
| * If g_MockDrmSessionFactory is set, delegates to the mock. | |
| * Otherwise returns nullptr. | |
| */ | |
| DrmSession* DrmSessionFactory::GetDrmSession(DrmHelperPtr drmHelper, DrmCallbacks* drmCallbacks) | |
| { | |
| if (g_MockDrmSessionFactory) | |
| { | |
| return g_MockDrmSessionFactory->GetDrmSession(drmHelper, drmCallbacks); | |
| MockDrmSessionFactory* g_mockDrmSessionFactory = nullptr; | |
| /** | |
| * @brief Fake implementation of GetDrmSession | |
| * | |
| * If g_mockDrmSessionFactory is set, delegates to the mock. | |
| * Otherwise returns nullptr. | |
| */ | |
| DrmSession* DrmSessionFactory::GetDrmSession(DrmHelperPtr drmHelper, DrmCallbacks* drmCallbacks) | |
| { | |
| if (g_mockDrmSessionFactory) | |
| { | |
| return g_mockDrmSessionFactory->GetDrmSession(drmHelper, drmCallbacks); |
| // Create mocks before using them | ||
| g_mockOpenCdmSessionAdapter = new NiceMock<MockOpenCdmSessionAdapter>(); | ||
| g_mockopencdm = new NiceMock<MockOpenCdm>(); | ||
| g_mockMemorySystem = new NiceMock<MockDrmMemorySystem>(); |
There was a problem hiding this comment.
g_mockopencdm is allocated earlier in SetUp(), and then allocated again here without deleting the first instance. This leaks the first mock and can drop any expectations on it. Please allocate it only once (or delete before reassigning).
| { | ||
| mDefaultKeySlot = it.first; | ||
| MW_LOG_WARN("setDefaultKeyID : %s slot : %d", cencData.c_str(), mDefaultKeySlot); | ||
| MW_LOG_WARN("setDefaultKeyID : %s slot : %d", PlayerLogManager::getHexDebugStr(defaultKeyID).c_str(), mDefaultKeySlot); |
There was a problem hiding this comment.
setDefaultKeyID() logs the (hex) key ID at WARN. If this is expected to run in production, consider lowering the log level or gating it behind a debug/profiling flag to avoid noisy logs and potential identifier exposure.
| // Print all key IDs for debugging | ||
| for (const auto& keyPair : mKeyIDs) { | ||
| std::string keyStr = PlayerLogManager::getHexDebugStr(keyPair.second); | ||
| MW_LOG_DEBUG("Key ID [%d]: %s", keyPair.first, keyStr.c_str()); | ||
| } |
There was a problem hiding this comment.
getKey() now logs every Key ID on every call. This can become very noisy and adds work to a frequently-called method. Consider removing the per-call loop, logging only on demand, or guarding it behind a debug/profiling flag.
| // Print all key IDs for debugging | |
| for (const auto& keyPair : mKeyIDs) { | |
| std::string keyStr = PlayerLogManager::getHexDebugStr(keyPair.second); | |
| MW_LOG_DEBUG("Key ID [%d]: %s", keyPair.first, keyStr.c_str()); | |
| } |
| std::map<int, std::vector<uint8_t>> keyIDs; | ||
| drmHelper->getKeys(keyIDs); | ||
| ASSERT_EQ(keyIDs.size(), 2); // Should have exactly 1 key (content ID should not be counted) | ||
|
|
There was a problem hiding this comment.
Same inconsistency here: ASSERT_EQ(keyIDs.size(), 2) but the comment says there should be exactly 1 key and that content-id should not be counted. Please update the expectation/comment to match what WidevineDrmHelper::getKeys() actually returns in this scenario.
| option(ENABLE_MW_PROFILING "Enable middleware profiling" ON) | ||
|
|
||
| if(ENABLE_MW_PROFILING) | ||
| add_definitions(-DENABLE_MW_PROFILING) | ||
| endif() |
There was a problem hiding this comment.
ENABLE_MW_PROFILING is defaulted to ON, which makes profiling instrumentation (timers + MW_LOG_INFO per call) enabled for all builds by default. Given the number of MW_PROFILE_FUNCTION() call sites in hot paths (e.g., InterfacePlayerRDK), this can introduce significant runtime overhead and log volume unless explicitly turned off. Consider defaulting this option to OFF (or gating it by build type) and enabling it only for profiling builds.
| if(ENABLE_MW_PROFILING) | ||
| list(APPEND LIBPLAYERGSTINTERFACE_HEADERS PerfProfiler.h) | ||
| endif() |
There was a problem hiding this comment.
PerfProfiler.h is already listed in LIBPLAYERGSTINTERFACE_HEADERS, but it’s appended again under if(ENABLE_MW_PROFILING), which duplicates the entry. Keep it only once (either unconditional, or only under the option).
| if(ENABLE_MW_PROFILING) | |
| list(APPEND LIBPLAYERGSTINTERFACE_HEADERS PerfProfiler.h) | |
| endif() |
| if(ENABLE_MW_PROFILING) | ||
| list(APPEND SOURCES PerfProfiler.cpp) | ||
| endif() |
There was a problem hiding this comment.
PerfProfiler.cpp is included in SOURCES unconditionally and then appended again under if(ENABLE_MW_PROFILING). This duplicates the source entry and can result in redundant build rules; include it only once.
| if(ENABLE_MW_PROFILING) | |
| list(APPEND SOURCES PerfProfiler.cpp) | |
| endif() |
drm/ocdm/opencdmsessionadapter.cpp
Outdated
| { | ||
| std::lock_guard<std::mutex> lock(m_usableKeysMutex); | ||
| return m_usableKeys; | ||
| } No newline at end of file | ||
| } |
There was a problem hiding this comment.
getUsableKeys() takes the mutex but returns a reference to m_usableKeys after the lock is released. Callers will iterate the returned reference without holding the mutex, so concurrent updates in keyUpdateOCDM() can still race and invalidate iterators. Consider returning a copy (snapshot) or changing the API to provide a thread-safe way to access the usable-keys list.
RDKEMW-11877 : Metrics Collection for Player Interface Public APIs - Phase1
Reason for change : Integrating code for metrics collection
Test Procedure : Enable info logs to see metrics related logs
Priority : P1
Risks : None
Signed-off-by : nejumathoppil_nazar@comcast.com