From 625e416408b5745f57390ce9b2153413428024e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 17:49:26 +0000 Subject: [PATCH 1/5] Initial plan From a8d13e48bb87a85e252f1dcd04877bad4073c6cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:07:50 +0000 Subject: [PATCH 2/5] Implement runtime telemetry event emission throughout the player interface - Add TelemetryMarkers.h with macro definitions for all 17 telemetry event names - Add PlayerTelemetry.h with sendEvent() overloads backed by MW_LOG_MIL - Instrument InterfacePlayerRDK.cpp with 19 PlayerTelemetry::sendEvent() calls: lifecycle (init/shutdown), playback (started/paused/resumed/stopped/completed), buffering (started/ended), seek (started/completed), error (generic/network/decode), pipeline state change failure, EOS detected, and track switched Co-authored-by: dp0000 <53818367+dp0000@users.noreply.github.com> Agent-Logs-Url: https://github.com/rdkcentral/middleware-player-interface/sessions/568b6714-9c63-4663-9966-49cffd3749de --- InterfacePlayerRDK.cpp | 61 +++++++++++++++++++++++++++++++++ PlayerTelemetry.h | 78 ++++++++++++++++++++++++++++++++++++++++++ TelemetryMarkers.h | 58 +++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 PlayerTelemetry.h create mode 100644 TelemetryMarkers.h diff --git a/InterfacePlayerRDK.cpp b/InterfacePlayerRDK.cpp index a7d138b2..8607b118 100644 --- a/InterfacePlayerRDK.cpp +++ b/InterfacePlayerRDK.cpp @@ -35,6 +35,8 @@ #include "player-xternal-stats.h" #endif #include "PlayerUtils.h" +#include "TelemetryMarkers.h" +#include "PlayerTelemetry.h" #define DEFAULT_BUFFERING_TO_MS 10 /**< TimeOut interval to check buffer fullness */ #define DEFAULT_BUFFERING_MAX_MS (1000) /**< max buffering time */ @@ -99,11 +101,13 @@ mSourceSetupCV(), mScheduler(), callbackMap(), setupStreamCallbackMap(), mDrmSys pthread_mutex_init(&interfacePlayerPriv->gstPrivateContext->stream[i].sourceLock, NULL); // start Scheduler Worker for task handling mScheduler.StartScheduler(); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_INITIALIZED); } /* InterfacePlayerRDK destructor*/ InterfacePlayerRDK::~InterfacePlayerRDK() { + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_SHUTDOWN); DestroyPipeline(); if (mDrmSystem) { @@ -409,6 +413,8 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF if (!configureStream[i] && bESChangeStatus && (eGST_MEDIATYPE_AUDIO == i)) { MW_LOG_MIL("AudioType Changed. Force configure pipeline"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_TRACK_SWITCHED, + {{"trackType", "audio"}, {"trackId", std::to_string(trackId)}}); configureStream[i] = true; } @@ -478,6 +484,8 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { MW_LOG_ERR("InterfacePlayerRDK: GST_STATE_PAUSED failed"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, + {{"fromState", "NULL"}, {"toState", "PAUSED"}, {"context", "ConfigurePipeline_pauseOnStart"}}); } } /* If buffering is enabled, set the pipeline in Paused state, once sufficient content has been buffered the pipeline will be set to GST_STATE_PLAYING */ @@ -491,6 +499,12 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { MW_LOG_ERR("InterfacePlayerRDK_Configure GST_STATE_PAUSED failed"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, + {{"fromState", "NULL"}, {"toState", "PAUSED"}, {"context", "ConfigurePipeline_buffering"}}); + } + else + { + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_BUFFERING_STARTED); } interfacePlayerPriv->gstPrivateContext->pendingPlayState = false; interfacePlayerPriv->gstPrivateContext->paused = false; @@ -501,6 +515,12 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { MW_LOG_ERR("InterfacePlayerRDK: GST_STATE_PLAYING failed"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, + {{"fromState", "PAUSED"}, {"toState", "PLAYING"}, {"context", "ConfigurePipeline"}}); + } + else + { + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PLAYBACK_STARTED); } interfacePlayerPriv->gstPrivateContext->pendingPlayState = false; interfacePlayerPriv->gstPrivateContext->paused = false; @@ -1390,6 +1410,7 @@ void InterfacePlayerRDK::TearDownStream(int type) void InterfacePlayerRDK::Stop(bool keepLastFrame) { std::lock_guard lock(mMutex); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PLAYBACK_STOPPED); /* make the execution of this function more deterministic and * reduce scope for potential pipeline lockups*/ @@ -1656,6 +1677,8 @@ bool InterfacePlayerRDK::Flush(double position, int rate, bool shouldTearDown, b */ ResetGstEvents(); MW_LOG_INFO("InterfacePlayerRDK: Pipeline flush seek - start = %f rate = %d", position, rate); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_SEEK_STARTED, + {{"position", std::to_string(position)}, {"rate", std::to_string(rate)}}); double playRate = 1.0; if (eGST_MEDIAFORMAT_PROGRESSIVE == static_cast(m_gstConfigParam->media)) { @@ -1678,6 +1701,11 @@ bool InterfacePlayerRDK::Flush(double position, int rate, bool shouldTearDown, b //Save the updated seek position SetSeekPosition(position); } + else + { + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_SEEK_COMPLETED, + {{"position", std::to_string(position)}, {"rate", std::to_string(rate)}}); + } if ((interfacePlayerPriv->gstPrivateContext->usingRialtoSink) && (interfacePlayerPriv->gstPrivateContext->audio_sink) && @@ -2891,6 +2919,7 @@ bool InterfacePlayerRDK::StopBuffering(bool forceStop, bool &isPlaying) if (current == GST_STATE_PLAYING) { sendEndEvent = true; + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_BUFFERING_ENDED); } } } @@ -3342,11 +3371,23 @@ bool InterfacePlayerRDK::Pause(bool pause , bool forceStopGstreamerPreBuffering) if (nextState != validateStateWithMsTimeout(this,nextState, 100)) { MW_LOG_ERR("InterfacePlayerRDK_Pause - validateStateWithMsTimeout - FAILED GstState %d", nextState); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, + {{"toState", pause ? "PAUSED" : "PLAYING"}, {"context", "Pause_timeout"}}); + } + else + { + PlayerTelemetry::sendEvent(pause ? TELEMETRY_EVENT_PLAYBACK_PAUSED : TELEMETRY_EVENT_PLAYBACK_RESUMED); } } else if (GST_STATE_CHANGE_SUCCESS != rc) { MW_LOG_ERR("InterfacePlayerRDK_Pause - gst_element_set_state - FAILED rc %d", rc); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, + {{"toState", pause ? "PAUSED" : "PLAYING"}, {"context", "Pause_failure"}}); + } + else + { + PlayerTelemetry::sendEvent(pause ? TELEMETRY_EVENT_PLAYBACK_PAUSED : TELEMETRY_EVENT_PLAYBACK_RESUMED); } interfacePlayerPriv->gstPrivateContext->buffering_target_state = nextState; @@ -4107,6 +4148,9 @@ static void GstPlayer_OnGstDecodeErrorCb(GstElement* object, guint arg0, gpointe pInterfacePlayerRDK->OnGstDecodeErrorCb(privatePlayer->gstPrivateContext->decodeErrorCBCount); privatePlayer->gstPrivateContext->decodeErrorMsgTimeMS = NOW_STEADY_TS_MS; MW_LOG_ERR("Got Decode Error message from %s total_cb=%d timeMs=%d", GST_ELEMENT_NAME(object), privatePlayer->gstPrivateContext->decodeErrorCBCount, GST_MIN_DECODE_ERROR_INTERVAL); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DECODE_ERROR, + {{"element", GST_ELEMENT_NAME(object) ? GST_ELEMENT_NAME(object) : "unknown"}, + {"count", std::to_string(privatePlayer->gstPrivateContext->decodeErrorCBCount)}}); privatePlayer->gstPrivateContext->decodeErrorCBCount = 0; #ifdef USE_EXTERNAL_STATS INC_DECODE_ERROR(); // Increment the decoder error for low level AV metric @@ -4151,6 +4195,20 @@ static gboolean bus_message(GstBus * bus, GstMessage * msg, InterfacePlayerRDK * } pInterfacePlayerRDK->busMessageCallback(std::move(busEvent)); MW_LOG_ERR("Debug Info: %s\n", (dbg_info) ? dbg_info : "none"); + if (error->domain == GST_RESOURCE_ERROR) + { + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_NETWORK_ERROR, + {{"element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"}, + {"errorCode", std::to_string(error->code)}, + {"message", error->message ? error->message : ""}}); + } + else + { + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_ERROR, + {{"element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"}, + {"message", error->message ? error->message : ""}, + {"debugInfo", dbg_info ? dbg_info : "none"}}); + } g_clear_error(&error); g_free(dbg_info); break; @@ -4354,6 +4412,8 @@ static gboolean bus_message(GstBus * bus, GstMessage * msg, InterfacePlayerRDK * busEvent.dbg_info = "N/A"; pInterfacePlayerRDK->busMessageCallback(std::move(busEvent)); MW_LOG_MIL("GST_MESSAGE_EOS"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_EOS_DETECTED, + {{"element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"}}); pInterfacePlayerRDK->NotifyEOS(); break; @@ -4936,6 +4996,7 @@ void InterfacePlayerRDK::NotifyEOS() interfacePlayerPriv->gstPrivateContext->eosCallbackIdleTaskPending = true; // eosSignalled is reset once the async task is completed either in Configure/Flush/ResetEOSSignalled, so set the flag before scheduling the task interfacePlayerPriv->gstPrivateContext->eosSignalled = true; + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PLAYBACK_COMPLETED); interfacePlayerPriv->gstPrivateContext->eosCallbackIdleTaskId = mScheduler.ScheduleTask(PlayerAsyncTaskObj(IdleCallbackOnEOS, (void *)this, "IdleCallbackOnEOS")); if (interfacePlayerPriv->gstPrivateContext->eosCallbackIdleTaskId == PLAYER_TASK_ID_INVALID && true == interfacePlayerPriv->gstPrivateContext->eosCallbackIdleTaskPending) { diff --git a/PlayerTelemetry.h b/PlayerTelemetry.h new file mode 100644 index 00000000..15f2ba19 --- /dev/null +++ b/PlayerTelemetry.h @@ -0,0 +1,78 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * 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. + */ + +#pragma once + +/** + * @file PlayerTelemetry.h + * @brief Lightweight telemetry emission utility for the middleware player interface. + * + * Provides PlayerTelemetry::sendEvent() overloads that accept an event name and + * an optional key/value payload map. Events are emitted to the platform logging + * sink so that they can be forwarded by the surrounding RDK telemetry framework. + * + * The implementation deliberately avoids a hard dependency on any particular + * telemetry back-end. Integration with T2 or another platform service can be + * added by extending or replacing the body of sendEvent() without changing any + * call sites. + */ + +#include +#include +#include "PlayerLogManager.h" + +/** + * @class PlayerTelemetry + * @brief Static helper for emitting named telemetry events with optional payload data. + */ +class PlayerTelemetry +{ +public: + /** + * @brief Emit a telemetry event with no additional payload. + * @param[in] eventName One of the TELEMETRY_EVENT_* markers from TelemetryMarkers.h. + */ + static void sendEvent(const std::string& eventName) + { + MW_LOG_MIL("[TELEMETRY] event=%s", eventName.c_str()); + } + + /** + * @brief Emit a telemetry event with a structured key/value payload. + * @param[in] eventName One of the TELEMETRY_EVENT_* markers from TelemetryMarkers.h. + * @param[in] payload Additional context data to attach to the event. + */ + static void sendEvent(const std::string& eventName, + const std::map& payload) + { + std::string fields; + for (const auto& kv : payload) + { + if (!fields.empty()) + { + fields += ' '; + } + fields += kv.first + '=' + kv.second; + } + MW_LOG_MIL("[TELEMETRY] event=%s %s", eventName.c_str(), fields.c_str()); + } + +private: + PlayerTelemetry() = delete; +}; diff --git a/TelemetryMarkers.h b/TelemetryMarkers.h new file mode 100644 index 00000000..5febed06 --- /dev/null +++ b/TelemetryMarkers.h @@ -0,0 +1,58 @@ +/* + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * 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. + */ + +#pragma once + +/** + * @file TelemetryMarkers.h + * @brief Telemetry event marker definitions for the middleware player interface. + * + * Each macro defines the string key used when emitting a telemetry event via + * PlayerTelemetry::sendEvent(). Markers are grouped by category to match the + * lifecycle of a GStreamer-based playback session. + */ + +/* ── Playback events ──────────────────────────────────────────────────────── */ +#define TELEMETRY_EVENT_PLAYBACK_STARTED "PLAYBACK_STARTED" /**< Pipeline transitions to PLAYING */ +#define TELEMETRY_EVENT_PLAYBACK_PAUSED "PLAYBACK_PAUSED" /**< Pipeline transitions to PAUSED on user request */ +#define TELEMETRY_EVENT_PLAYBACK_RESUMED "PLAYBACK_RESUMED" /**< Pipeline transitions back to PLAYING from PAUSED */ +#define TELEMETRY_EVENT_PLAYBACK_STOPPED "PLAYBACK_STOPPED" /**< Stop() tears down the pipeline */ +#define TELEMETRY_EVENT_PLAYBACK_COMPLETED "PLAYBACK_COMPLETED" /**< End-of-stream reached and EOS callback scheduled */ + +/* ── Media / buffering events ─────────────────────────────────────────────── */ +#define TELEMETRY_EVENT_BUFFERING_STARTED "BUFFERING_STARTED" /**< Pre-roll buffering begins */ +#define TELEMETRY_EVENT_BUFFERING_ENDED "BUFFERING_ENDED" /**< Sufficient frames buffered; pipeline unpaused */ +#define TELEMETRY_EVENT_SEEK_STARTED "SEEK_STARTED" /**< Flush seek requested */ +#define TELEMETRY_EVENT_SEEK_COMPLETED "SEEK_COMPLETED" /**< gst_element_seek() succeeded */ + +/* ── Error events ─────────────────────────────────────────────────────────── */ +#define TELEMETRY_EVENT_ERROR "ERROR" /**< Generic GStreamer pipeline error (GST_MESSAGE_ERROR) */ +#define TELEMETRY_EVENT_DECODE_ERROR "DECODE_ERROR" /**< Decoder reported a decode-error-callback */ +#define TELEMETRY_EVENT_NETWORK_ERROR "NETWORK_ERROR" /**< Resource/stream error that indicates a network fault */ + +/* ── Pipeline state change failure ───────────────────────────────────────── */ +#define TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE "MW_PIPELINE_STATE_CHANGE_FAILURE" /**< gst_element_set_state() returned GST_STATE_CHANGE_FAILURE */ + +/* ── Lifecycle events ─────────────────────────────────────────────────────── */ +#define TELEMETRY_EVENT_INITIALIZED "INTERFACE_INITIALIZED" /**< InterfacePlayerRDK constructor completed */ +#define TELEMETRY_EVENT_SHUTDOWN "INTERFACE_SHUTDOWN" /**< InterfacePlayerRDK destructor entered */ + +/* ── Miscellaneous events ─────────────────────────────────────────────────── */ +#define TELEMETRY_EVENT_EOS_DETECTED "END_OF_STREAM_DETECTED" /**< GST_MESSAGE_EOS received on the pipeline bus */ +#define TELEMETRY_EVENT_TRACK_SWITCHED "TRACK_SWITCHED" /**< Mid-stream audio/video track change */ From 173cddd3aa038cc0ff4621e20aa390135dc6d1f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:31:14 +0000 Subject: [PATCH 3/5] Extend critical-error telemetry to DRM, OCDM, HDCP and handler-control subsystems - TelemetryMarkers.h: add 10 new markers for DRM/OCDM/HDCP/handler failures - GstHandlerControl.cpp: emit HANDLER_TIMEOUT when waitForDone() times out - drm/DrmSessionManager.cpp: emit DRM_HELPER_NOT_FOUND, DRM_PSSH_PARSE_FAILED, DRM_SESSION_CREATE_FAILED, and DRM_SESSION_INIT_FAILED (3 variants) - drm/ocdm/opencdmsessionadapter.cpp: emit OCDM_SYSTEM_CREATE_FAILED and OCDM_SESSION_CREATE_FAILED with key system and error code payload - gst-plugins/drm/gst/gstcdmidecryptor.cpp: emit HDCP_PROTECTION_FAILURE, HDCP_COMPLIANCE_FAILURE, and DECRYPT_FAILURE at threshold-breach points Co-authored-by: dp0000 <53818367+dp0000@users.noreply.github.com> Agent-Logs-Url: https://github.com/rdkcentral/middleware-player-interface/sessions/00222964-3d85-4630-8c0d-e2154ccb0f98 --- GstHandlerControl.cpp | 4 ++++ TelemetryMarkers.h | 14 ++++++++++++++ drm/DrmSessionManager.cpp | 14 ++++++++++++++ drm/ocdm/opencdmsessionadapter.cpp | 6 ++++++ gst-plugins/drm/gst/gstcdmidecryptor.cpp | 9 +++++++++ 5 files changed, 47 insertions(+) diff --git a/GstHandlerControl.cpp b/GstHandlerControl.cpp index 11f15198..d4f7e5cb 100644 --- a/GstHandlerControl.cpp +++ b/GstHandlerControl.cpp @@ -19,6 +19,8 @@ #include "GstHandlerControl.h" #include "PlayerLogManager.h" +#include "TelemetryMarkers.h" +#include "PlayerTelemetry.h" #include #include @@ -75,6 +77,8 @@ bool GstHandlerControl::waitForDone(int MaximumDelayMilliseconds, std::string na { MW_LOG_ERR("GstPlayer: %d instance%s of %s running", mInstanceCount, mInstanceCount?"s":"", name.c_str()); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HANDLER_TIMEOUT, + {{"handler", name}, {"count", std::to_string(mInstanceCount)}}); return false; } else diff --git a/TelemetryMarkers.h b/TelemetryMarkers.h index 5febed06..7f893d1a 100644 --- a/TelemetryMarkers.h +++ b/TelemetryMarkers.h @@ -56,3 +56,17 @@ /* ── Miscellaneous events ─────────────────────────────────────────────────── */ #define TELEMETRY_EVENT_EOS_DETECTED "END_OF_STREAM_DETECTED" /**< GST_MESSAGE_EOS received on the pipeline bus */ #define TELEMETRY_EVENT_TRACK_SWITCHED "TRACK_SWITCHED" /**< Mid-stream audio/video track change */ + +/* ── DRM / Content protection events ─────────────────────────────────────── */ +#define TELEMETRY_EVENT_DRM_HELPER_NOT_FOUND "DRM_HELPER_NOT_FOUND" /**< No DRM helper found for the content protection system */ +#define TELEMETRY_EVENT_DRM_PSSH_PARSE_FAILED "DRM_PSSH_PARSE_FAILED" /**< Failed to parse PSSH data from DRM init data */ +#define TELEMETRY_EVENT_DRM_SESSION_CREATE_FAILED "DRM_SESSION_CREATE_FAILED" /**< DRM session creation returned null / invalid params */ +#define TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED "DRM_SESSION_INIT_FAILED" /**< DRM session OCDM initialisation failed */ +#define TELEMETRY_EVENT_OCDM_SYSTEM_CREATE_FAILED "OCDM_SYSTEM_CREATE_FAILED" /**< opencdm_create_system() returned null */ +#define TELEMETRY_EVENT_OCDM_SESSION_CREATE_FAILED "OCDM_SESSION_CREATE_FAILED" /**< opencdm_construct_session() returned an error */ +#define TELEMETRY_EVENT_HDCP_PROTECTION_FAILURE "HDCP_PROTECTION_FAILURE" /**< HDCP output protection failure detected */ +#define TELEMETRY_EVENT_HDCP_COMPLIANCE_FAILURE "HDCP_COMPLIANCE_FAILURE" /**< HDCP compliance check failure (2.2 vs 1.4) */ +#define TELEMETRY_EVENT_DECRYPT_FAILURE "DECRYPT_FAILURE" /**< Decryption failure threshold exceeded */ + +/* ── Handler / concurrency events ────────────────────────────────────────── */ +#define TELEMETRY_EVENT_HANDLER_TIMEOUT "HANDLER_TIMEOUT" /**< A GStreamer bus/callback handler did not complete before timeout */ diff --git a/drm/DrmSessionManager.cpp b/drm/DrmSessionManager.cpp index 8b9a197e..1832c91f 100755 --- a/drm/DrmSessionManager.cpp +++ b/drm/DrmSessionManager.cpp @@ -30,6 +30,8 @@ #include #include "PlayerUtils.h" #include "ContentSecurityManager.h" +#include "TelemetryMarkers.h" +#include "PlayerTelemetry.h" #define DRM_METADATA_TAG_START "" #define DRM_METADATA_TAG_END "" #define SESSION_TOKEN_URL "http://localhost:50050/authService/getSessionToken" @@ -412,6 +414,8 @@ DrmSession * DrmSessionManager::createDrmSession( int& responseCode, if (!DrmHelperEngine::getInstance().hasDRM(drmInfo)) { MW_LOG_ERR(" Failed to locate DRM helper"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_HELPER_NOT_FOUND, + {{"systemId", systemId}}); } else { @@ -426,6 +430,8 @@ DrmSession * DrmSessionManager::createDrmSession( int& responseCode, if (!drmHelper->parsePssh(initDataPtr, initDataLen)) { MW_LOG_ERR(" Failed to Parse PSSH from the DRM InitData"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_PSSH_PARSE_FAILED, + {{"systemId", systemId}, {"initDataLen", std::to_string(initDataLen)}}); err = MW_CORRUPT_DRM_METADATA; } else @@ -446,6 +452,8 @@ DrmSession* DrmSessionManager::createDrmSession(int &responseCode, int &err, std /* This should never happen, since the caller should have already ensure the provided DRMInfo is supported using hasDRM */ MW_LOG_ERR(" Failed to create DRM Session invalid parameters "); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_CREATE_FAILED, + {{"reason", "invalid_parameters"}}); return nullptr; } @@ -928,15 +936,21 @@ KeyState DrmSessionManager::initializeDrmSession(std::shared_ptr drmH { MW_LOG_ERR("DRM session ID is empty: Key State %d ", code); err = MW_DRM_SESSIONID_EMPTY; + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, + {{"reason", "empty_session_id"}, {"keyState", std::to_string(static_cast(code))}}); } else if (code == KEY_ERROR_SESSION_CREATE_FAILED) { MW_LOG_ERR("OCDM session construction failed: Key State %d ", code); err = MW_DRM_SESSION_CREATE_FAILED; + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, + {{"reason", "ocdm_session_create_failed"}, {"keyState", std::to_string(static_cast(code))}}); } else { err= MW_DRM_DATA_BIND_FAILED; + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, + {{"reason", "data_bind_failed"}, {"keyState", std::to_string(static_cast(code))}}); } } diff --git a/drm/ocdm/opencdmsessionadapter.cpp b/drm/ocdm/opencdmsessionadapter.cpp index e45ca119..952a6c26 100644 --- a/drm/ocdm/opencdmsessionadapter.cpp +++ b/drm/ocdm/opencdmsessionadapter.cpp @@ -25,6 +25,8 @@ #include "DrmHelper.h" #include "PlayerUtils.h" +#include "TelemetryMarkers.h" +#include "PlayerTelemetry.h" #include "ProcessHandler.h" #include "PlayerExternalsInterface.h" @@ -96,6 +98,8 @@ void OCDMSessionAdapter::initDRMSystem() #endif if (m_pOpenCDMSystem == nullptr) { MW_LOG_ERR("opencdm_create_system() FAILED"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_OCDM_SYSTEM_CREATE_FAILED, + {{"keySystem", m_keySystem}}); } } MW_LOG_WARN("initDRMSystem :: exit "); @@ -168,6 +172,8 @@ void OCDMSessionAdapter::generateDRMSession(const uint8_t *f_pbInitData, { MW_LOG_ERR("Error constructing OCDM session. OCDM err=0x%x", ocdmRet); m_eKeyState = KEY_ERROR_SESSION_CREATE_FAILED; + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_OCDM_SESSION_CREATE_FAILED, + {{"keySystem", m_keySystem}, {"errorCode", std::to_string(static_cast(ocdmRet))}}); } } } diff --git a/gst-plugins/drm/gst/gstcdmidecryptor.cpp b/gst-plugins/drm/gst/gstcdmidecryptor.cpp index 1666b529..06970a56 100755 --- a/gst-plugins/drm/gst/gstcdmidecryptor.cpp +++ b/gst-plugins/drm/gst/gstcdmidecryptor.cpp @@ -26,6 +26,8 @@ #include #include "DrmConstants.h" #include "SocInterface.h" +#include "TelemetryMarkers.h" +#include "PlayerTelemetry.h" GST_DEBUG_CATEGORY_STATIC ( gst_cdmidecryptor_debug_category); #define GST_CAT_DEFAULT gst_cdmidecryptor_debug_category @@ -650,6 +652,8 @@ static GstFlowReturn gst_cdmidecryptor_transform_ip( if(cdmidecryptor->hdcpOpProtectionFailCount >= DECRYPT_FAILURE_THRESHOLD) { GstStructure *newmsg = gst_structure_new("HDCPProtectionFailure", "message", G_TYPE_STRING,"HDCP Output Protection Error", NULL); gst_element_post_message(reinterpret_cast(cdmidecryptor),gst_message_new_application (GST_OBJECT (cdmidecryptor), newmsg)); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HDCP_PROTECTION_FAILURE, + {{"failCount", std::to_string(cdmidecryptor->hdcpOpProtectionFailCount)}}); } cdmidecryptor->hdcpOpProtectionFailCount = 0; } @@ -665,10 +669,15 @@ static GstFlowReturn gst_cdmidecryptor_transform_ip( { // Failure - 2.2 vs 1.4 HDCP error = g_error_new(GST_STREAM_ERROR , GST_STREAM_ERROR_FAILED, "HDCP Compliance Check Failure"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HDCP_COMPLIANCE_FAILURE, + {{"failCount", std::to_string(cdmidecryptor->decryptFailCount)}}); } else { error = g_error_new(GST_STREAM_ERROR , GST_STREAM_ERROR_FAILED, "Decrypt Error: code %d", errorCode); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DECRYPT_FAILURE, + {{"errorCode", std::to_string(errorCode)}, + {"failCount", std::to_string(cdmidecryptor->decryptFailCount)}}); } gst_element_post_message(reinterpret_cast(cdmidecryptor), gst_message_new_error (GST_OBJECT (cdmidecryptor), error, "Decrypt Failed")); g_error_free(error); From b3b4c7aba8deea5504d4fe30485935e9c8edbf01 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:03:21 +0000 Subject: [PATCH 4/5] Gate telemetry behind PLAYER_TELEMETRY_SUPPORT compile-time flag PlayerTelemetry.h: split into two #ifdef PLAYER_TELEMETRY_SUPPORT branches - Active branch: includes , MW_LOG_MIL emission (compiled when flag set) - No-op stub branch: empty inline sendEvent() overloads (zero overhead when flag absent); call sites need no per-call #ifdef guards in either case CMakeLists.txt: add CMAKE_PLAYER_TELEMETRY_SUPPORT option that appends -DPLAYER_TELEMETRY_SUPPORT to LIBPLAYERGSTINTERFACE_DEFINES, following the same pattern used by CMAKE_GST_SUBTEC_ENABLED and similar feature flags Co-authored-by: dp0000 <53818367+dp0000@users.noreply.github.com> Agent-Logs-Url: https://github.com/rdkcentral/middleware-player-interface/sessions/22158a4c-70d9-474e-9d28-e435b129b1de --- CMakeLists.txt | 5 +++++ PlayerTelemetry.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86e8ed66..4258c9d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -367,6 +367,11 @@ if (CMAKE_GST_SUBTEC_ENABLED) set(LIBPLAYERGSTINTERFACE_DEFINES "${LIBPLAYERGSTINTERFACE_DEFINES} -DGST_SUBTEC_ENABLED") endif() +if (CMAKE_PLAYER_TELEMETRY_SUPPORT) + message("CMAKE_PLAYER_TELEMETRY_SUPPORT set") + set(LIBPLAYERGSTINTERFACE_DEFINES "${LIBPLAYERGSTINTERFACE_DEFINES} -DPLAYER_TELEMETRY_SUPPORT") +endif() + if (CMAKE_SUBTITLE_SUPPORT) message("CMAKE_SUBTITLE_SUPPORT set") set(LIBPLAYERGSTINTERFACE_DEFINES "${LIBPLAYERGSTINTERFACE_DEFINES} -DSUBTITLE_SUPPORTED") diff --git a/PlayerTelemetry.h b/PlayerTelemetry.h index 15f2ba19..c4b2a550 100644 --- a/PlayerTelemetry.h +++ b/PlayerTelemetry.h @@ -31,8 +31,16 @@ * telemetry back-end. Integration with T2 or another platform service can be * added by extending or replacing the body of sendEvent() without changing any * call sites. + * + * When PLAYER_TELEMETRY_SUPPORT is NOT defined at compile time every call to + * sendEvent() is compiled away to a no-op so there is zero overhead and no + * dependency on in non-telemetry builds. Enable telemetry by passing + * -DPLAYER_TELEMETRY_SUPPORT (or setting CMAKE_PLAYER_TELEMETRY_SUPPORT) at + * build time. */ +#ifdef PLAYER_TELEMETRY_SUPPORT + #include #include #include "PlayerLogManager.h" @@ -40,6 +48,8 @@ /** * @class PlayerTelemetry * @brief Static helper for emitting named telemetry events with optional payload data. + * + * Active implementation compiled when PLAYER_TELEMETRY_SUPPORT is defined. */ class PlayerTelemetry { @@ -76,3 +86,28 @@ class PlayerTelemetry private: PlayerTelemetry() = delete; }; + +#else /* PLAYER_TELEMETRY_SUPPORT not defined */ + +#include +#include + +/** + * @class PlayerTelemetry + * @brief No-op stub compiled when PLAYER_TELEMETRY_SUPPORT is not defined. + * + * All methods are empty inline functions so the compiler eliminates them + * entirely. Call sites require no #ifdef guards. + */ +class PlayerTelemetry +{ +public: + static void sendEvent(const std::string& /*eventName*/) {} + static void sendEvent(const std::string& /*eventName*/, + const std::map& /*payload*/) {} + +private: + PlayerTelemetry() = delete; +}; + +#endif /* PLAYER_TELEMETRY_SUPPORT */ From 4ecd38ed34dd76998426f0b677d69a146f343f45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:58:30 +0000 Subject: [PATCH 5/5] Refactor telemetry: named TelemetryPayload objects with int/float/double support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PlayerTelemetry.h: - Add TelemetryPayload builder class with add() overloads for std::string, int, long, float, double — numeric values are auto-converted to string so call sites need no std::to_string() - Replace sendEvent(name, map) with sendEvent(name, TelemetryPayload) - Add matching no-op TelemetryPayload stub in #else branch (non-telemetry builds compile and link without change) All inline {{"key", value}} map arguments replaced with named TelemetryPayload objects in: InterfacePlayerRDK.cpp (10 call sites) GstHandlerControl.cpp (1 call site) drm/DrmSessionManager.cpp (6 call sites) drm/ocdm/opencdmsessionadapter.cpp (2 call sites) gst-plugins/drm/gst/gstcdmidecryptor.cpp (3 call sites) Redundant std::to_string() and unnecessary static_cast removed at all affected call sites. Co-authored-by: dp0000 <53818367+dp0000@users.noreply.github.com> Agent-Logs-Url: https://github.com/rdkcentral/middleware-player-interface/sessions/0277cd52-920d-4e51-915b-f4020683330e --- GstHandlerControl.cpp | 8 +- InterfacePlayerRDK.cpp | 99 ++++++++++++++------- PlayerTelemetry.h | 104 +++++++++++++++++++---- drm/DrmSessionManager.cpp | 46 +++++++--- drm/ocdm/opencdmsessionadapter.cpp | 15 +++- gst-plugins/drm/gst/gstcdmidecryptor.cpp | 23 +++-- 6 files changed, 224 insertions(+), 71 deletions(-) diff --git a/GstHandlerControl.cpp b/GstHandlerControl.cpp index d4f7e5cb..163c81f1 100644 --- a/GstHandlerControl.cpp +++ b/GstHandlerControl.cpp @@ -77,8 +77,12 @@ bool GstHandlerControl::waitForDone(int MaximumDelayMilliseconds, std::string na { MW_LOG_ERR("GstPlayer: %d instance%s of %s running", mInstanceCount, mInstanceCount?"s":"", name.c_str()); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HANDLER_TIMEOUT, - {{"handler", name}, {"count", std::to_string(mInstanceCount)}}); + { + TelemetryPayload handlerPayload; + handlerPayload.add("handler", name); + handlerPayload.add("count", mInstanceCount); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HANDLER_TIMEOUT, handlerPayload); + } return false; } else diff --git a/InterfacePlayerRDK.cpp b/InterfacePlayerRDK.cpp index 8607b118..911e0561 100644 --- a/InterfacePlayerRDK.cpp +++ b/InterfacePlayerRDK.cpp @@ -413,8 +413,12 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF if (!configureStream[i] && bESChangeStatus && (eGST_MEDIATYPE_AUDIO == i)) { MW_LOG_MIL("AudioType Changed. Force configure pipeline"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_TRACK_SWITCHED, - {{"trackType", "audio"}, {"trackId", std::to_string(trackId)}}); + { + TelemetryPayload trackSwitchedPayload; + trackSwitchedPayload.add("trackType", "audio"); + trackSwitchedPayload.add("trackId", trackId); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_TRACK_SWITCHED, trackSwitchedPayload); + } configureStream[i] = true; } @@ -484,8 +488,13 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { MW_LOG_ERR("InterfacePlayerRDK: GST_STATE_PAUSED failed"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, - {{"fromState", "NULL"}, {"toState", "PAUSED"}, {"context", "ConfigurePipeline_pauseOnStart"}}); + { + TelemetryPayload pauseOnStartPayload; + pauseOnStartPayload.add("fromState", "NULL"); + pauseOnStartPayload.add("toState", "PAUSED"); + pauseOnStartPayload.add("context", "ConfigurePipeline_pauseOnStart"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, pauseOnStartPayload); + } } } /* If buffering is enabled, set the pipeline in Paused state, once sufficient content has been buffered the pipeline will be set to GST_STATE_PLAYING */ @@ -499,8 +508,13 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { MW_LOG_ERR("InterfacePlayerRDK_Configure GST_STATE_PAUSED failed"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, - {{"fromState", "NULL"}, {"toState", "PAUSED"}, {"context", "ConfigurePipeline_buffering"}}); + { + TelemetryPayload bufferingPausePayload; + bufferingPausePayload.add("fromState", "NULL"); + bufferingPausePayload.add("toState", "PAUSED"); + bufferingPausePayload.add("context", "ConfigurePipeline_buffering"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, bufferingPausePayload); + } } else { @@ -515,8 +529,13 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { MW_LOG_ERR("InterfacePlayerRDK: GST_STATE_PLAYING failed"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, - {{"fromState", "PAUSED"}, {"toState", "PLAYING"}, {"context", "ConfigurePipeline"}}); + { + TelemetryPayload playingFailPayload; + playingFailPayload.add("fromState", "PAUSED"); + playingFailPayload.add("toState", "PLAYING"); + playingFailPayload.add("context", "ConfigurePipeline"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, playingFailPayload); + } } else { @@ -1677,8 +1696,12 @@ bool InterfacePlayerRDK::Flush(double position, int rate, bool shouldTearDown, b */ ResetGstEvents(); MW_LOG_INFO("InterfacePlayerRDK: Pipeline flush seek - start = %f rate = %d", position, rate); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_SEEK_STARTED, - {{"position", std::to_string(position)}, {"rate", std::to_string(rate)}}); + { + TelemetryPayload seekStartedPayload; + seekStartedPayload.add("position", position); + seekStartedPayload.add("rate", rate); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_SEEK_STARTED, seekStartedPayload); + } double playRate = 1.0; if (eGST_MEDIAFORMAT_PROGRESSIVE == static_cast(m_gstConfigParam->media)) { @@ -1703,8 +1726,10 @@ bool InterfacePlayerRDK::Flush(double position, int rate, bool shouldTearDown, b } else { - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_SEEK_COMPLETED, - {{"position", std::to_string(position)}, {"rate", std::to_string(rate)}}); + TelemetryPayload seekCompletedPayload; + seekCompletedPayload.add("position", position); + seekCompletedPayload.add("rate", rate); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_SEEK_COMPLETED, seekCompletedPayload); } if ((interfacePlayerPriv->gstPrivateContext->usingRialtoSink) && @@ -3371,8 +3396,12 @@ bool InterfacePlayerRDK::Pause(bool pause , bool forceStopGstreamerPreBuffering) if (nextState != validateStateWithMsTimeout(this,nextState, 100)) { MW_LOG_ERR("InterfacePlayerRDK_Pause - validateStateWithMsTimeout - FAILED GstState %d", nextState); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, - {{"toState", pause ? "PAUSED" : "PLAYING"}, {"context", "Pause_timeout"}}); + { + TelemetryPayload pauseTimeoutPayload; + pauseTimeoutPayload.add("toState", pause ? "PAUSED" : "PLAYING"); + pauseTimeoutPayload.add("context", "Pause_timeout"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, pauseTimeoutPayload); + } } else { @@ -3382,8 +3411,12 @@ bool InterfacePlayerRDK::Pause(bool pause , bool forceStopGstreamerPreBuffering) else if (GST_STATE_CHANGE_SUCCESS != rc) { MW_LOG_ERR("InterfacePlayerRDK_Pause - gst_element_set_state - FAILED rc %d", rc); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, - {{"toState", pause ? "PAUSED" : "PLAYING"}, {"context", "Pause_failure"}}); + { + TelemetryPayload pauseFailPayload; + pauseFailPayload.add("toState", pause ? "PAUSED" : "PLAYING"); + pauseFailPayload.add("context", "Pause_failure"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_PIPELINE_STATE_CHANGE_FAILURE, pauseFailPayload); + } } else { @@ -4148,9 +4181,12 @@ static void GstPlayer_OnGstDecodeErrorCb(GstElement* object, guint arg0, gpointe pInterfacePlayerRDK->OnGstDecodeErrorCb(privatePlayer->gstPrivateContext->decodeErrorCBCount); privatePlayer->gstPrivateContext->decodeErrorMsgTimeMS = NOW_STEADY_TS_MS; MW_LOG_ERR("Got Decode Error message from %s total_cb=%d timeMs=%d", GST_ELEMENT_NAME(object), privatePlayer->gstPrivateContext->decodeErrorCBCount, GST_MIN_DECODE_ERROR_INTERVAL); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DECODE_ERROR, - {{"element", GST_ELEMENT_NAME(object) ? GST_ELEMENT_NAME(object) : "unknown"}, - {"count", std::to_string(privatePlayer->gstPrivateContext->decodeErrorCBCount)}}); + { + TelemetryPayload decodeErrPayload; + decodeErrPayload.add("element", GST_ELEMENT_NAME(object) ? GST_ELEMENT_NAME(object) : "unknown"); + decodeErrPayload.add("count", privatePlayer->gstPrivateContext->decodeErrorCBCount); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DECODE_ERROR, decodeErrPayload); + } privatePlayer->gstPrivateContext->decodeErrorCBCount = 0; #ifdef USE_EXTERNAL_STATS INC_DECODE_ERROR(); // Increment the decoder error for low level AV metric @@ -4197,17 +4233,19 @@ static gboolean bus_message(GstBus * bus, GstMessage * msg, InterfacePlayerRDK * MW_LOG_ERR("Debug Info: %s\n", (dbg_info) ? dbg_info : "none"); if (error->domain == GST_RESOURCE_ERROR) { - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_NETWORK_ERROR, - {{"element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"}, - {"errorCode", std::to_string(error->code)}, - {"message", error->message ? error->message : ""}}); + TelemetryPayload networkErrPayload; + networkErrPayload.add("element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"); + networkErrPayload.add("errorCode", error->code); + networkErrPayload.add("message", error->message ? error->message : ""); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_NETWORK_ERROR, networkErrPayload); } else { - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_ERROR, - {{"element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"}, - {"message", error->message ? error->message : ""}, - {"debugInfo", dbg_info ? dbg_info : "none"}}); + TelemetryPayload errPayload; + errPayload.add("element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"); + errPayload.add("message", error->message ? error->message : ""); + errPayload.add("debugInfo", dbg_info ? dbg_info : "none"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_ERROR, errPayload); } g_clear_error(&error); g_free(dbg_info); @@ -4412,8 +4450,11 @@ static gboolean bus_message(GstBus * bus, GstMessage * msg, InterfacePlayerRDK * busEvent.dbg_info = "N/A"; pInterfacePlayerRDK->busMessageCallback(std::move(busEvent)); MW_LOG_MIL("GST_MESSAGE_EOS"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_EOS_DETECTED, - {{"element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"}}); + { + TelemetryPayload eosPayload; + eosPayload.add("element", GST_OBJECT_NAME(msg->src) ? GST_OBJECT_NAME(msg->src) : "unknown"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_EOS_DETECTED, eosPayload); + } pInterfacePlayerRDK->NotifyEOS(); break; diff --git a/PlayerTelemetry.h b/PlayerTelemetry.h index c4b2a550..b8eef291 100644 --- a/PlayerTelemetry.h +++ b/PlayerTelemetry.h @@ -23,18 +23,21 @@ * @file PlayerTelemetry.h * @brief Lightweight telemetry emission utility for the middleware player interface. * - * Provides PlayerTelemetry::sendEvent() overloads that accept an event name and - * an optional key/value payload map. Events are emitted to the platform logging - * sink so that they can be forwarded by the surrounding RDK telemetry framework. + * Provides a TelemetryPayload builder and PlayerTelemetry::sendEvent() overloads. + * TelemetryPayload::add() accepts string, integer, and floating-point values so + * call sites do not need manual std::to_string() conversions. * - * The implementation deliberately avoids a hard dependency on any particular - * telemetry back-end. Integration with T2 or another platform service can be - * added by extending or replacing the body of sendEvent() without changing any - * call sites. + * Usage: + * @code + * TelemetryPayload payload; + * payload.add("systemId", systemId); + * payload.add("retryCount", retryCount); // int — no std::to_string needed + * PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_HELPER_NOT_FOUND, payload); + * @endcode * - * When PLAYER_TELEMETRY_SUPPORT is NOT defined at compile time every call to - * sendEvent() is compiled away to a no-op so there is zero overhead and no - * dependency on in non-telemetry builds. Enable telemetry by passing + * When PLAYER_TELEMETRY_SUPPORT is NOT defined at compile time every call is + * compiled away to a no-op — zero overhead, no dependency on or logging + * headers in non-telemetry builds. Enable telemetry by passing * -DPLAYER_TELEMETRY_SUPPORT (or setting CMAKE_PLAYER_TELEMETRY_SUPPORT) at * build time. */ @@ -45,6 +48,59 @@ #include #include "PlayerLogManager.h" +/** + * @class TelemetryPayload + * @brief Key/value container for telemetry event context data. + * + * Accepts string, integer, and floating-point values via the add() method, + * converting numeric types to their string representation automatically so + * that call sites do not need explicit std::to_string() calls. + */ +class TelemetryPayload +{ +public: + /** @brief Add a string field. */ + TelemetryPayload& add(const std::string& key, const std::string& value) + { + m_fields[key] = value; + return *this; + } + + /** @brief Add an integer field (converted to decimal string). */ + TelemetryPayload& add(const std::string& key, int value) + { + m_fields[key] = std::to_string(value); + return *this; + } + + /** @brief Add a long integer field (converted to decimal string). */ + TelemetryPayload& add(const std::string& key, long value) + { + m_fields[key] = std::to_string(value); + return *this; + } + + /** @brief Add a float field (converted to decimal string). */ + TelemetryPayload& add(const std::string& key, float value) + { + m_fields[key] = std::to_string(value); + return *this; + } + + /** @brief Add a double field (converted to decimal string). */ + TelemetryPayload& add(const std::string& key, double value) + { + m_fields[key] = std::to_string(value); + return *this; + } + + /** @brief Read-only access to the internal map for sendEvent(). */ + const std::map& fields() const { return m_fields; } + +private: + std::map m_fields; +}; + /** * @class PlayerTelemetry * @brief Static helper for emitting named telemetry events with optional payload data. @@ -66,13 +122,12 @@ class PlayerTelemetry /** * @brief Emit a telemetry event with a structured key/value payload. * @param[in] eventName One of the TELEMETRY_EVENT_* markers from TelemetryMarkers.h. - * @param[in] payload Additional context data to attach to the event. + * @param[in] payload Additional context data built with TelemetryPayload::add(). */ - static void sendEvent(const std::string& eventName, - const std::map& payload) + static void sendEvent(const std::string& eventName, const TelemetryPayload& payload) { std::string fields; - for (const auto& kv : payload) + for (const auto& kv : payload.fields()) { if (!fields.empty()) { @@ -90,7 +145,23 @@ class PlayerTelemetry #else /* PLAYER_TELEMETRY_SUPPORT not defined */ #include -#include + +/** + * @class TelemetryPayload + * @brief No-op stub compiled when PLAYER_TELEMETRY_SUPPORT is not defined. + * + * All add() methods are empty inline functions eliminated by the compiler. + * Call sites compile without change and require no #ifdef guards. + */ +class TelemetryPayload +{ +public: + TelemetryPayload& add(const std::string& /*key*/, const std::string& /*value*/) { return *this; } + TelemetryPayload& add(const std::string& /*key*/, int /*value*/) { return *this; } + TelemetryPayload& add(const std::string& /*key*/, long /*value*/) { return *this; } + TelemetryPayload& add(const std::string& /*key*/, float /*value*/) { return *this; } + TelemetryPayload& add(const std::string& /*key*/, double /*value*/) { return *this; } +}; /** * @class PlayerTelemetry @@ -103,8 +174,7 @@ class PlayerTelemetry { public: static void sendEvent(const std::string& /*eventName*/) {} - static void sendEvent(const std::string& /*eventName*/, - const std::map& /*payload*/) {} + static void sendEvent(const std::string& /*eventName*/, const TelemetryPayload& /*payload*/) {} private: PlayerTelemetry() = delete; diff --git a/drm/DrmSessionManager.cpp b/drm/DrmSessionManager.cpp index 1832c91f..6773fcc5 100755 --- a/drm/DrmSessionManager.cpp +++ b/drm/DrmSessionManager.cpp @@ -414,8 +414,11 @@ DrmSession * DrmSessionManager::createDrmSession( int& responseCode, if (!DrmHelperEngine::getInstance().hasDRM(drmInfo)) { MW_LOG_ERR(" Failed to locate DRM helper"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_HELPER_NOT_FOUND, - {{"systemId", systemId}}); + { + TelemetryPayload drmHelperPayload; + drmHelperPayload.add("systemId", systemId); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_HELPER_NOT_FOUND, drmHelperPayload); + } } else { @@ -430,8 +433,12 @@ DrmSession * DrmSessionManager::createDrmSession( int& responseCode, if (!drmHelper->parsePssh(initDataPtr, initDataLen)) { MW_LOG_ERR(" Failed to Parse PSSH from the DRM InitData"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_PSSH_PARSE_FAILED, - {{"systemId", systemId}, {"initDataLen", std::to_string(initDataLen)}}); + { + TelemetryPayload psshPayload; + psshPayload.add("systemId", systemId); + psshPayload.add("initDataLen", static_cast(initDataLen)); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_PSSH_PARSE_FAILED, psshPayload); + } err = MW_CORRUPT_DRM_METADATA; } else @@ -452,8 +459,11 @@ DrmSession* DrmSessionManager::createDrmSession(int &responseCode, int &err, std /* This should never happen, since the caller should have already ensure the provided DRMInfo is supported using hasDRM */ MW_LOG_ERR(" Failed to create DRM Session invalid parameters "); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_CREATE_FAILED, - {{"reason", "invalid_parameters"}}); + { + TelemetryPayload drmSessionPayload; + drmSessionPayload.add("reason", "invalid_parameters"); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_CREATE_FAILED, drmSessionPayload); + } return nullptr; } @@ -936,21 +946,33 @@ KeyState DrmSessionManager::initializeDrmSession(std::shared_ptr drmH { MW_LOG_ERR("DRM session ID is empty: Key State %d ", code); err = MW_DRM_SESSIONID_EMPTY; - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, - {{"reason", "empty_session_id"}, {"keyState", std::to_string(static_cast(code))}}); + { + TelemetryPayload emptySessionPayload; + emptySessionPayload.add("reason", "empty_session_id"); + emptySessionPayload.add("keyState", static_cast(code)); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, emptySessionPayload); + } } else if (code == KEY_ERROR_SESSION_CREATE_FAILED) { MW_LOG_ERR("OCDM session construction failed: Key State %d ", code); err = MW_DRM_SESSION_CREATE_FAILED; - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, - {{"reason", "ocdm_session_create_failed"}, {"keyState", std::to_string(static_cast(code))}}); + { + TelemetryPayload ocdmCreateFailedPayload; + ocdmCreateFailedPayload.add("reason", "ocdm_session_create_failed"); + ocdmCreateFailedPayload.add("keyState", static_cast(code)); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, ocdmCreateFailedPayload); + } } else { err= MW_DRM_DATA_BIND_FAILED; - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, - {{"reason", "data_bind_failed"}, {"keyState", std::to_string(static_cast(code))}}); + { + TelemetryPayload dataBindPayload; + dataBindPayload.add("reason", "data_bind_failed"); + dataBindPayload.add("keyState", static_cast(code)); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DRM_SESSION_INIT_FAILED, dataBindPayload); + } } } diff --git a/drm/ocdm/opencdmsessionadapter.cpp b/drm/ocdm/opencdmsessionadapter.cpp index 952a6c26..d129e455 100644 --- a/drm/ocdm/opencdmsessionadapter.cpp +++ b/drm/ocdm/opencdmsessionadapter.cpp @@ -98,8 +98,11 @@ void OCDMSessionAdapter::initDRMSystem() #endif if (m_pOpenCDMSystem == nullptr) { MW_LOG_ERR("opencdm_create_system() FAILED"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_OCDM_SYSTEM_CREATE_FAILED, - {{"keySystem", m_keySystem}}); + { + TelemetryPayload ocdmSystemPayload; + ocdmSystemPayload.add("keySystem", m_keySystem); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_OCDM_SYSTEM_CREATE_FAILED, ocdmSystemPayload); + } } } MW_LOG_WARN("initDRMSystem :: exit "); @@ -172,8 +175,12 @@ void OCDMSessionAdapter::generateDRMSession(const uint8_t *f_pbInitData, { MW_LOG_ERR("Error constructing OCDM session. OCDM err=0x%x", ocdmRet); m_eKeyState = KEY_ERROR_SESSION_CREATE_FAILED; - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_OCDM_SESSION_CREATE_FAILED, - {{"keySystem", m_keySystem}, {"errorCode", std::to_string(static_cast(ocdmRet))}}); + { + TelemetryPayload ocdmSessionPayload; + ocdmSessionPayload.add("keySystem", m_keySystem); + ocdmSessionPayload.add("errorCode", static_cast(ocdmRet)); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_OCDM_SESSION_CREATE_FAILED, ocdmSessionPayload); + } } } } diff --git a/gst-plugins/drm/gst/gstcdmidecryptor.cpp b/gst-plugins/drm/gst/gstcdmidecryptor.cpp index 06970a56..da82d349 100755 --- a/gst-plugins/drm/gst/gstcdmidecryptor.cpp +++ b/gst-plugins/drm/gst/gstcdmidecryptor.cpp @@ -652,8 +652,11 @@ static GstFlowReturn gst_cdmidecryptor_transform_ip( if(cdmidecryptor->hdcpOpProtectionFailCount >= DECRYPT_FAILURE_THRESHOLD) { GstStructure *newmsg = gst_structure_new("HDCPProtectionFailure", "message", G_TYPE_STRING,"HDCP Output Protection Error", NULL); gst_element_post_message(reinterpret_cast(cdmidecryptor),gst_message_new_application (GST_OBJECT (cdmidecryptor), newmsg)); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HDCP_PROTECTION_FAILURE, - {{"failCount", std::to_string(cdmidecryptor->hdcpOpProtectionFailCount)}}); + { + TelemetryPayload hdcpProtPayload; + hdcpProtPayload.add("failCount", cdmidecryptor->hdcpOpProtectionFailCount); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HDCP_PROTECTION_FAILURE, hdcpProtPayload); + } } cdmidecryptor->hdcpOpProtectionFailCount = 0; } @@ -669,15 +672,21 @@ static GstFlowReturn gst_cdmidecryptor_transform_ip( { // Failure - 2.2 vs 1.4 HDCP error = g_error_new(GST_STREAM_ERROR , GST_STREAM_ERROR_FAILED, "HDCP Compliance Check Failure"); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HDCP_COMPLIANCE_FAILURE, - {{"failCount", std::to_string(cdmidecryptor->decryptFailCount)}}); + { + TelemetryPayload hdcpCompPayload; + hdcpCompPayload.add("failCount", cdmidecryptor->decryptFailCount); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_HDCP_COMPLIANCE_FAILURE, hdcpCompPayload); + } } else { error = g_error_new(GST_STREAM_ERROR , GST_STREAM_ERROR_FAILED, "Decrypt Error: code %d", errorCode); - PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DECRYPT_FAILURE, - {{"errorCode", std::to_string(errorCode)}, - {"failCount", std::to_string(cdmidecryptor->decryptFailCount)}}); + { + TelemetryPayload decryptFailPayload; + decryptFailPayload.add("errorCode", errorCode); + decryptFailPayload.add("failCount", cdmidecryptor->decryptFailCount); + PlayerTelemetry::sendEvent(TELEMETRY_EVENT_DECRYPT_FAILURE, decryptFailPayload); + } } gst_element_post_message(reinterpret_cast(cdmidecryptor), gst_message_new_error (GST_OBJECT (cdmidecryptor), error, "Decrypt Failed")); g_error_free(error);