LCOV - code coverage report
Current view: top level - media/server/main/source - MediaKeySession.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 94.0 % 233 219
Test Date: 2026-07-07 06:27:16 Functions: 100.0 % 26 26

            Line data    Source code
       1              : /*
       2              :  * If not stated otherwise in this file or this component's LICENSE file the
       3              :  * following copyright and licenses apply:
       4              :  *
       5              :  * Copyright 2022 Sky UK
       6              :  *
       7              :  * Licensed under the Apache License, Version 2.0 (the "License");
       8              :  * you may not use this file except in compliance with the License.
       9              :  * You may obtain a copy of the License at
      10              :  *
      11              :  * http://www.apache.org/licenses/LICENSE-2.0
      12              :  *
      13              :  * Unless required by applicable law or agreed to in writing, software
      14              :  * distributed under the License is distributed on an "AS IS" BASIS,
      15              :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      16              :  * See the License for the specific language governing permissions and
      17              :  * limitations under the License.
      18              :  */
      19              : 
      20              : #include <stdexcept>
      21              : #include <utility>
      22              : 
      23              : #include "MediaKeySession.h"
      24              : #include "MediaKeysCommon.h"
      25              : #include "RialtoServerLogging.h"
      26              : 
      27              : namespace firebolt::rialto::server
      28              : {
      29            2 : std::shared_ptr<IMediaKeySessionFactory> IMediaKeySessionFactory::createFactory()
      30              : {
      31            2 :     std::shared_ptr<IMediaKeySessionFactory> factory;
      32              : 
      33              :     try
      34              :     {
      35            2 :         factory = std::make_shared<MediaKeySessionFactory>();
      36              :     }
      37            0 :     catch (const std::exception &e)
      38              :     {
      39            0 :         RIALTO_SERVER_LOG_ERROR("Failed to create the media key session factory, reason: %s", e.what());
      40              :     }
      41              : 
      42            2 :     return factory;
      43              : }
      44              : 
      45              : std::unique_ptr<IMediaKeySession>
      46            1 : MediaKeySessionFactory::createMediaKeySession(const std::string &keySystem, int32_t keySessionId,
      47              :                                               const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem,
      48              :                                               KeySessionType sessionType, std::weak_ptr<IMediaKeysClient> client) const
      49              : {
      50            1 :     std::unique_ptr<IMediaKeySession> mediaKeys;
      51              :     try
      52              :     {
      53            2 :         mediaKeys = std::make_unique<server::MediaKeySession>(keySystem, keySessionId, ocdmSystem, sessionType, client,
      54            3 :                                                               server::IMainThreadFactory::createFactory());
      55              :     }
      56            0 :     catch (const std::exception &e)
      57              :     {
      58            0 :         RIALTO_SERVER_LOG_ERROR("Failed to create the media key session, reason: %s", e.what());
      59              :     }
      60              : 
      61            1 :     return mediaKeys;
      62              : }
      63              : 
      64           55 : MediaKeySession::MediaKeySession(const std::string &keySystem, int32_t keySessionId,
      65              :                                  const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, KeySessionType sessionType,
      66              :                                  std::weak_ptr<IMediaKeysClient> client,
      67           55 :                                  const std::shared_ptr<IMainThreadFactory> &mainThreadFactory)
      68           55 :     : m_kKeySystem(keySystem), m_kKeySessionId(keySessionId), m_kSessionType(sessionType), m_mediaKeysClient(client),
      69           55 :       m_isSessionConstructed(false), m_isSessionClosed(false), m_licenseRequested(false), m_ongoingOcdmOperation(false),
      70          165 :       m_ocdmError(false), m_decryptErrorLogged(false)
      71              : {
      72           55 :     RIALTO_SERVER_LOG_DEBUG("entry:");
      73              : 
      74           55 :     m_mainThread = mainThreadFactory->getMainThread();
      75           55 :     if (!m_mainThread)
      76              :     {
      77            1 :         throw std::runtime_error("Failed to get the main thread");
      78              :     }
      79           54 :     m_mainThreadClientId = m_mainThread->registerClient();
      80              : 
      81           54 :     m_ocdmSession = ocdmSystem.createSession(this);
      82           54 :     if (!m_ocdmSession)
      83              :     {
      84            1 :         throw std::runtime_error("Ocdm session could not be created");
      85              :     }
      86           53 :     RIALTO_SERVER_LOG_MIL("New OCDM session created");
      87           71 : }
      88              : 
      89          159 : MediaKeySession::~MediaKeySession()
      90              : {
      91           53 :     RIALTO_SERVER_LOG_DEBUG("entry:");
      92              : 
      93           53 :     if (m_isSessionConstructed)
      94              :     {
      95           16 :         if (!m_isSessionClosed)
      96              :         {
      97           13 :             if (MediaKeyErrorStatus::OK != closeKeySession())
      98              :             {
      99            0 :                 RIALTO_SERVER_LOG_ERROR("Failed to close the key session");
     100              :             }
     101              :         }
     102           16 :         if (MediaKeyErrorStatus::OK != m_ocdmSession->destructSession())
     103              :         {
     104            0 :             RIALTO_SERVER_LOG_ERROR("Failed to destruct the key session");
     105              :         }
     106              :     }
     107              : 
     108           53 :     m_mainThread->unregisterClient(m_mainThreadClientId);
     109          106 : }
     110              : 
     111           19 : MediaKeyErrorStatus MediaKeySession::generateRequest(InitDataType initDataType, const std::vector<uint8_t> &initData,
     112              :                                                      const LimitedDurationLicense &ldlState)
     113              : {
     114           19 :     RIALTO_SERVER_LOG_DEBUG("entry:");
     115           19 :     if (LimitedDurationLicense::NOT_SPECIFIED != ldlState)
     116              :     {
     117           12 :         m_extendedInterfaceInUse = true;
     118              :     }
     119              :     else
     120              :     {
     121              :         // Set the request flag for the onLicenseRequest callback
     122            7 :         m_licenseRequested = true;
     123              :     }
     124              : 
     125           19 :     auto status = MediaKeyErrorStatus::OK;
     126              : 
     127              :     // Only construct session if it hasnt previously been constructed
     128           19 :     if (!m_isSessionConstructed)
     129              :     {
     130           17 :         initOcdmErrorChecking();
     131              : 
     132           17 :         status = m_ocdmSession->constructSession(m_kSessionType, initDataType, &initData[0], initData.size());
     133           17 :         if (MediaKeyErrorStatus::OK != status)
     134              :         {
     135            1 :             RIALTO_SERVER_LOG_ERROR("Failed to construct the key session");
     136            1 :             m_licenseRequested = false;
     137              :         }
     138              :         else
     139              :         {
     140           16 :             m_isSessionConstructed = true;
     141           16 :             if (!m_queuedDrmHeader.empty())
     142              :             {
     143            0 :                 RIALTO_SERVER_LOG_DEBUG("Setting queued drm header after session construction");
     144            0 :                 setDrmHeader(m_queuedDrmHeader);
     145            0 :                 m_queuedDrmHeader.clear();
     146              :             }
     147              :         }
     148              : 
     149           17 :         if ((checkForOcdmErrors("generateRequest")) && (MediaKeyErrorStatus::OK == status))
     150              :         {
     151            1 :             status = MediaKeyErrorStatus::FAIL;
     152              :         }
     153              :     }
     154              : 
     155           19 :     if (m_isSessionConstructed && m_extendedInterfaceInUse)
     156              :     {
     157              :         // Ocdm-playready does not notify onProcessChallenge when complete.
     158              :         // Fetch the challenge manually.
     159           12 :         getChallenge(ldlState);
     160              :     }
     161              : 
     162           19 :     return status;
     163              : }
     164              : 
     165           12 : void MediaKeySession::getChallenge(const LimitedDurationLicense &ldlState)
     166              : {
     167           12 :     RIALTO_SERVER_LOG_DEBUG("entry:");
     168           12 :     auto task = [&]()
     169              :     {
     170           12 :         const bool kIsLdl{LimitedDurationLicense::ENABLED == ldlState};
     171           12 :         uint32_t challengeSize = 0;
     172           12 :         MediaKeyErrorStatus status = m_ocdmSession->getChallengeData(kIsLdl, nullptr, &challengeSize);
     173           12 :         if (challengeSize == 0)
     174              :         {
     175            1 :             RIALTO_SERVER_LOG_ERROR("Failed to get the challenge data size, no onLicenseRequest will be generated");
     176            2 :             return;
     177              :         }
     178           11 :         std::vector<uint8_t> challenge(challengeSize, 0x00);
     179           11 :         status = m_ocdmSession->getChallengeData(kIsLdl, &challenge[0], &challengeSize);
     180           11 :         if (MediaKeyErrorStatus::OK != status)
     181              :         {
     182            1 :             RIALTO_SERVER_LOG_ERROR("Failed to get the challenge data, no onLicenseRequest will be generated");
     183            1 :             return;
     184              :         }
     185              : 
     186           10 :         std::string url;
     187           10 :         m_licenseRequested = true;
     188           10 :         onProcessChallenge(url.c_str(), &challenge[0], challengeSize);
     189           11 :     };
     190           12 :     m_mainThread->enqueueTask(m_mainThreadClientId, task);
     191              : }
     192              : 
     193            3 : MediaKeyErrorStatus MediaKeySession::loadSession()
     194              : {
     195            3 :     initOcdmErrorChecking();
     196              : 
     197            3 :     MediaKeyErrorStatus status = m_ocdmSession->load();
     198            3 :     if (MediaKeyErrorStatus::OK != status)
     199              :     {
     200            1 :         RIALTO_SERVER_LOG_ERROR("Failed to load the key session");
     201              :     }
     202              : 
     203            3 :     if ((checkForOcdmErrors("loadSession")) && (MediaKeyErrorStatus::OK == status))
     204              :     {
     205            1 :         status = MediaKeyErrorStatus::FAIL;
     206              :     }
     207              : 
     208            3 :     return status;
     209              : }
     210              : 
     211            5 : MediaKeyErrorStatus MediaKeySession::updateSession(const std::vector<uint8_t> &responseData)
     212              : {
     213            5 :     initOcdmErrorChecking();
     214              : 
     215              :     MediaKeyErrorStatus status;
     216            5 :     if (m_extendedInterfaceInUse)
     217              :     {
     218            2 :         status = m_ocdmSession->storeLicenseData(&responseData[0], responseData.size());
     219            2 :         if (MediaKeyErrorStatus::OK != status)
     220              :         {
     221            1 :             RIALTO_SERVER_LOG_ERROR("Failed to store the license data for the key session");
     222              :         }
     223              :     }
     224              :     else
     225              :     {
     226            3 :         status = m_ocdmSession->update(&responseData[0], responseData.size());
     227            3 :         if (MediaKeyErrorStatus::OK != status)
     228              :         {
     229            1 :             RIALTO_SERVER_LOG_ERROR("Failed to update the key session");
     230              :         }
     231              :     }
     232              : 
     233            5 :     if ((checkForOcdmErrors("updateSession")) && (MediaKeyErrorStatus::OK == status))
     234              :     {
     235            1 :         status = MediaKeyErrorStatus::FAIL;
     236              :     }
     237              : 
     238            5 :     return status;
     239              : }
     240              : 
     241           10 : MediaKeyErrorStatus MediaKeySession::decrypt(GstBuffer *encrypted, GstCaps *caps)
     242              : {
     243           10 :     constexpr uint32_t kHdcpOutputProtectionFailure{4427};
     244              : 
     245           10 :     initOcdmErrorChecking();
     246              : 
     247           10 :     MediaKeyErrorStatus status = m_ocdmSession->decryptBuffer(encrypted, caps);
     248           10 :     if (MediaKeyErrorStatus::OUTPUT_RESTRICTED == status)
     249              :     {
     250            1 :         RIALTO_SERVER_LOG_WARN("Decrypt failed due to HDCP output protection");
     251              :     }
     252            9 :     else if (MediaKeyErrorStatus::OK != status)
     253              :     {
     254            6 :         uint32_t lastDrmError{0};
     255            6 :         m_ocdmSession->getLastDrmError(lastDrmError);
     256            6 :         if (lastDrmError == kHdcpOutputProtectionFailure)
     257              :         {
     258            1 :             RIALTO_SERVER_LOG_WARN("Decrypt failed due to HDCP output protection (DRM error %u)", lastDrmError);
     259            1 :             status = MediaKeyErrorStatus::OUTPUT_RESTRICTED;
     260              :         }
     261              : 
     262              :         // Log decrypt failures once and re-enable logging only after a successful decrypt.
     263            6 :         if ((MediaKeyErrorStatus::OUTPUT_RESTRICTED != status) && !m_decryptErrorLogged)
     264              :         {
     265            4 :             RIALTO_SERVER_LOG_ERROR("Failed to decrypt buffer");
     266            4 :             m_decryptErrorLogged = true;
     267              :         }
     268              :     }
     269              :     else
     270              :     {
     271            3 :         m_decryptErrorLogged = false;
     272              :     }
     273              : 
     274           10 :     if ((checkForOcdmErrors("decrypt")) && (MediaKeyErrorStatus::OK == status))
     275              :     {
     276            1 :         status = MediaKeyErrorStatus::FAIL;
     277              :     }
     278              : 
     279           10 :     return status;
     280              : }
     281              : 
     282           19 : MediaKeyErrorStatus MediaKeySession::closeKeySession()
     283              : {
     284           19 :     initOcdmErrorChecking();
     285              : 
     286              :     MediaKeyErrorStatus status;
     287           19 :     if (m_extendedInterfaceInUse)
     288              :     {
     289           12 :         if (MediaKeyErrorStatus::OK != m_ocdmSession->cancelChallengeData())
     290              :         {
     291            1 :             RIALTO_SERVER_LOG_WARN("Failed to cancel the challenge data for the key session");
     292              :         }
     293              : 
     294           12 :         if (MediaKeyErrorStatus::OK != m_ocdmSession->cleanDecryptContext())
     295              :         {
     296            1 :             RIALTO_SERVER_LOG_WARN("Failed to clean the decrypt context for the key session");
     297              :         }
     298           12 :         status = MediaKeyErrorStatus::OK;
     299           12 :         RIALTO_SERVER_LOG_MIL("OCDM session closed");
     300              :     }
     301              :     else
     302              :     {
     303            7 :         status = m_ocdmSession->close();
     304            7 :         if (MediaKeyErrorStatus::OK != status)
     305              :         {
     306            1 :             RIALTO_SERVER_LOG_ERROR("Failed to Close the key session");
     307              :         }
     308              :     }
     309           19 :     m_isSessionClosed = (MediaKeyErrorStatus::OK == status);
     310              : 
     311           19 :     if ((checkForOcdmErrors("closeKeySession")) && (MediaKeyErrorStatus::OK == status))
     312              :     {
     313            1 :         status = MediaKeyErrorStatus::FAIL;
     314              :     }
     315              : 
     316           19 :     return status;
     317              : }
     318              : 
     319            3 : MediaKeyErrorStatus MediaKeySession::removeKeySession()
     320              : {
     321            3 :     initOcdmErrorChecking();
     322              : 
     323            3 :     MediaKeyErrorStatus status = m_ocdmSession->remove();
     324            3 :     if (MediaKeyErrorStatus::OK != status)
     325              :     {
     326            1 :         RIALTO_SERVER_LOG_ERROR("Failed to remove the key session");
     327              :     }
     328              : 
     329            3 :     if ((checkForOcdmErrors("removeKeySession")) && (MediaKeyErrorStatus::OK == status))
     330              :     {
     331            1 :         status = MediaKeyErrorStatus::FAIL;
     332              :     }
     333              : 
     334            3 :     return status;
     335              : }
     336              : 
     337            3 : MediaKeyErrorStatus MediaKeySession::getCdmKeySessionId(std::string &cdmKeySessionId)
     338              : {
     339            3 :     initOcdmErrorChecking();
     340              : 
     341            3 :     MediaKeyErrorStatus status = m_ocdmSession->getCdmKeySessionId(cdmKeySessionId);
     342            3 :     if (MediaKeyErrorStatus::OK != status)
     343              :     {
     344            1 :         RIALTO_SERVER_LOG_ERROR("Failed to get cdm key session id");
     345              :     }
     346              : 
     347            3 :     if ((checkForOcdmErrors("getCdmKeySessionId")) && (MediaKeyErrorStatus::OK == status))
     348              :     {
     349            1 :         status = MediaKeyErrorStatus::FAIL;
     350              :     }
     351              : 
     352            3 :     return status;
     353              : }
     354              : 
     355            2 : bool MediaKeySession::containsKey(const std::vector<uint8_t> &keyId)
     356              : {
     357            2 :     uint32_t result = m_ocdmSession->hasKeyId(keyId.data(), keyId.size());
     358              : 
     359            2 :     return static_cast<bool>(result);
     360              : }
     361              : 
     362            3 : MediaKeyErrorStatus MediaKeySession::setDrmHeader(const std::vector<uint8_t> &requestData)
     363              : {
     364            3 :     initOcdmErrorChecking();
     365              : 
     366            3 :     if (!m_isSessionConstructed)
     367              :     {
     368            0 :         RIALTO_SERVER_LOG_INFO("Session not yet constructed, queueing drm header to be set after construction");
     369            0 :         m_extendedInterfaceInUse = true;
     370            0 :         m_queuedDrmHeader = requestData;
     371            0 :         return MediaKeyErrorStatus::OK;
     372              :     }
     373              : 
     374            3 :     MediaKeyErrorStatus status = m_ocdmSession->setDrmHeader(requestData.data(), requestData.size());
     375            3 :     if (MediaKeyErrorStatus::OK != status)
     376              :     {
     377            1 :         RIALTO_SERVER_LOG_ERROR("Failed to set drm header");
     378              :     }
     379              : 
     380            3 :     if ((checkForOcdmErrors("setDrmHeader")) && (MediaKeyErrorStatus::OK == status))
     381              :     {
     382            1 :         status = MediaKeyErrorStatus::FAIL;
     383              :     }
     384              : 
     385            3 :     return status;
     386              : }
     387              : 
     388            3 : MediaKeyErrorStatus MediaKeySession::getLastDrmError(uint32_t &errorCode)
     389              : {
     390            3 :     initOcdmErrorChecking();
     391              : 
     392            3 :     MediaKeyErrorStatus status = m_ocdmSession->getLastDrmError(errorCode);
     393            3 :     if (MediaKeyErrorStatus::OK != status)
     394              :     {
     395            1 :         RIALTO_SERVER_LOG_ERROR("Failed to get last drm error");
     396              :     }
     397              : 
     398            3 :     if ((checkForOcdmErrors("getLastDrmError")) && (MediaKeyErrorStatus::OK == status))
     399              :     {
     400            1 :         status = MediaKeyErrorStatus::FAIL;
     401              :     }
     402              : 
     403            3 :     return status;
     404              : }
     405              : 
     406            7 : MediaKeyErrorStatus MediaKeySession::selectKeyId(const std::vector<uint8_t> &keyId)
     407              : {
     408            7 :     if (m_selectedKeyId == keyId)
     409              :     {
     410            1 :         return MediaKeyErrorStatus::OK;
     411              :     }
     412              : 
     413            6 :     initOcdmErrorChecking();
     414              : 
     415            6 :     m_extendedInterfaceInUse = true;
     416            6 :     MediaKeyErrorStatus status = m_ocdmSession->selectKeyId(keyId.size(), keyId.data());
     417            6 :     if (MediaKeyErrorStatus::OK == status)
     418              :     {
     419            4 :         RIALTO_SERVER_LOG_INFO("New keyId selected successfully");
     420            4 :         m_selectedKeyId = keyId;
     421              :     }
     422              : 
     423            6 :     if ((checkForOcdmErrors("selectKeyId")) && (MediaKeyErrorStatus::OK == status))
     424              :     {
     425            1 :         status = MediaKeyErrorStatus::FAIL;
     426              :     }
     427              : 
     428            6 :     return status;
     429              : }
     430              : 
     431           12 : void MediaKeySession::onProcessChallenge(const char url[], const uint8_t challenge[], const uint16_t challengeLength)
     432              : {
     433           12 :     std::string urlStr = url;
     434           24 :     std::vector<unsigned char> challengeVec = std::vector<unsigned char>{challenge, challenge + challengeLength};
     435           12 :     auto task = [&, urlStr = std::move(urlStr), challengeVec = std::move(challengeVec)]()
     436              :     {
     437           12 :         std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
     438           12 :         if (client)
     439              :         {
     440           12 :             if (m_licenseRequested)
     441              :             {
     442           11 :                 client->onLicenseRequest(m_kKeySessionId, challengeVec, urlStr);
     443           11 :                 m_licenseRequested = false;
     444              :             }
     445              :             else
     446              :             {
     447            1 :                 client->onLicenseRenewal(m_kKeySessionId, challengeVec);
     448              :             }
     449              :         }
     450           24 :     };
     451           12 :     m_mainThread->enqueueTask(m_mainThreadClientId, task);
     452              : }
     453              : 
     454            3 : void MediaKeySession::onKeyUpdated(const uint8_t keyId[], const uint8_t keyIdLength)
     455              : {
     456            6 :     std::vector<unsigned char> keyIdVec = std::vector<unsigned char>{keyId, keyId + keyIdLength};
     457            3 :     auto task = [&, keyIdVec = std::move(keyIdVec)]()
     458              :     {
     459            3 :         std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
     460            3 :         if (client)
     461              :         {
     462            3 :             KeyStatus status = m_ocdmSession->getStatus(&keyIdVec[0], keyIdVec.size());
     463            3 :             m_updatedKeyStatuses.push_back(std::make_pair(keyIdVec, status));
     464              :         }
     465            6 :     };
     466            3 :     m_mainThread->enqueueTask(m_mainThreadClientId, task);
     467              : }
     468              : 
     469            1 : void MediaKeySession::onAllKeysUpdated()
     470              : {
     471            1 :     auto task = [&]()
     472              :     {
     473            1 :         std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
     474            1 :         if (client)
     475              :         {
     476            1 :             client->onKeyStatusesChanged(m_kKeySessionId, m_updatedKeyStatuses);
     477            1 :             m_updatedKeyStatuses.clear();
     478              :         }
     479            1 :     };
     480            1 :     m_mainThread->enqueueTask(m_mainThreadClientId, task);
     481              : }
     482              : 
     483           10 : void MediaKeySession::onError(const char message[])
     484              : {
     485           10 :     RIALTO_SERVER_LOG_ERROR("Ocdm returned error: %s", message);
     486              : 
     487           10 :     std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
     488           10 :     if (!m_ongoingOcdmOperation)
     489              :     {
     490            0 :         RIALTO_SERVER_LOG_WARN("Received an asycronous OCDM error, ignoring");
     491              :     }
     492              :     else
     493              :     {
     494           10 :         m_ocdmError = true;
     495              :     }
     496              : }
     497              : 
     498           72 : void MediaKeySession::initOcdmErrorChecking()
     499              : {
     500           72 :     std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
     501           72 :     m_ongoingOcdmOperation = true;
     502           72 :     m_ocdmError = false;
     503              : }
     504              : 
     505           72 : bool MediaKeySession::checkForOcdmErrors(const char *operationStr)
     506              : {
     507           72 :     bool error = false;
     508              : 
     509           72 :     std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
     510           72 :     if (m_ocdmError)
     511              :     {
     512           10 :         RIALTO_SERVER_LOG_ERROR("MediaKeySession received an onError callback, operation '%s' failed", operationStr);
     513           10 :         error = true;
     514              :     }
     515           72 :     m_ongoingOcdmOperation = false;
     516              : 
     517           72 :     return error;
     518              : }
     519              : 
     520              : }; // namespace firebolt::rialto::server
        

Generated by: LCOV version 2.0-1