LCOV - code coverage report
Current view: top level - media/server/main/source - MediaKeySession.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 93.7 % 222 208
Test Date: 2026-03-18 08:38:49 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           51 : 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           51 :                                  const std::shared_ptr<IMainThreadFactory> &mainThreadFactory)
      68           51 :     : m_kKeySystem(keySystem), m_kKeySessionId(keySessionId), m_kSessionType(sessionType), m_mediaKeysClient(client),
      69           51 :       m_isSessionConstructed(false), m_isSessionClosed(false), m_licenseRequested(false), m_ongoingOcdmOperation(false),
      70          153 :       m_ocdmError(false)
      71              : {
      72           51 :     RIALTO_SERVER_LOG_DEBUG("entry:");
      73              : 
      74           51 :     m_mainThread = mainThreadFactory->getMainThread();
      75           51 :     if (!m_mainThread)
      76              :     {
      77            1 :         throw std::runtime_error("Failed to get the main thread");
      78              :     }
      79           50 :     m_mainThreadClientId = m_mainThread->registerClient();
      80              : 
      81           50 :     m_ocdmSession = ocdmSystem.createSession(this);
      82           50 :     if (!m_ocdmSession)
      83              :     {
      84            1 :         throw std::runtime_error("Ocdm session could not be created");
      85              :     }
      86           49 :     RIALTO_SERVER_LOG_MIL("New OCDM session created");
      87           67 : }
      88              : 
      89          147 : MediaKeySession::~MediaKeySession()
      90              : {
      91           49 :     RIALTO_SERVER_LOG_DEBUG("entry:");
      92              : 
      93           49 :     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           49 :     m_mainThread->unregisterClient(m_mainThreadClientId);
     109           98 : }
     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            3 : MediaKeyErrorStatus MediaKeySession::decrypt(GstBuffer *encrypted, GstCaps *caps)
     242              : {
     243            3 :     initOcdmErrorChecking();
     244              : 
     245            3 :     MediaKeyErrorStatus status = m_ocdmSession->decryptBuffer(encrypted, caps);
     246            3 :     if (MediaKeyErrorStatus::OK != status)
     247              :     {
     248            1 :         RIALTO_SERVER_LOG_ERROR("Failed to decrypt buffer");
     249              :     }
     250              : 
     251            3 :     if ((checkForOcdmErrors("decrypt")) && (MediaKeyErrorStatus::OK == status))
     252              :     {
     253            1 :         status = MediaKeyErrorStatus::FAIL;
     254              :     }
     255              : 
     256            3 :     return status;
     257              : }
     258              : 
     259           19 : MediaKeyErrorStatus MediaKeySession::closeKeySession()
     260              : {
     261           19 :     initOcdmErrorChecking();
     262              : 
     263              :     MediaKeyErrorStatus status;
     264           19 :     if (m_extendedInterfaceInUse)
     265              :     {
     266           12 :         if (MediaKeyErrorStatus::OK != m_ocdmSession->cancelChallengeData())
     267              :         {
     268            1 :             RIALTO_SERVER_LOG_WARN("Failed to cancel the challenge data for the key session");
     269              :         }
     270              : 
     271           12 :         if (MediaKeyErrorStatus::OK != m_ocdmSession->cleanDecryptContext())
     272              :         {
     273            1 :             RIALTO_SERVER_LOG_WARN("Failed to clean the decrypt context for the key session");
     274              :         }
     275           12 :         status = MediaKeyErrorStatus::OK;
     276           12 :         RIALTO_SERVER_LOG_MIL("OCDM session closed");
     277              :     }
     278              :     else
     279              :     {
     280            7 :         status = m_ocdmSession->close();
     281            7 :         if (MediaKeyErrorStatus::OK != status)
     282              :         {
     283            1 :             RIALTO_SERVER_LOG_ERROR("Failed to Close the key session");
     284              :         }
     285              :     }
     286           19 :     m_isSessionClosed = (MediaKeyErrorStatus::OK == status);
     287              : 
     288           19 :     if ((checkForOcdmErrors("closeKeySession")) && (MediaKeyErrorStatus::OK == status))
     289              :     {
     290            1 :         status = MediaKeyErrorStatus::FAIL;
     291              :     }
     292              : 
     293           19 :     return status;
     294              : }
     295              : 
     296            3 : MediaKeyErrorStatus MediaKeySession::removeKeySession()
     297              : {
     298            3 :     initOcdmErrorChecking();
     299              : 
     300            3 :     MediaKeyErrorStatus status = m_ocdmSession->remove();
     301            3 :     if (MediaKeyErrorStatus::OK != status)
     302              :     {
     303            1 :         RIALTO_SERVER_LOG_ERROR("Failed to remove the key session");
     304              :     }
     305              : 
     306            3 :     if ((checkForOcdmErrors("removeKeySession")) && (MediaKeyErrorStatus::OK == status))
     307              :     {
     308            1 :         status = MediaKeyErrorStatus::FAIL;
     309              :     }
     310              : 
     311            3 :     return status;
     312              : }
     313              : 
     314            3 : MediaKeyErrorStatus MediaKeySession::getCdmKeySessionId(std::string &cdmKeySessionId)
     315              : {
     316            3 :     initOcdmErrorChecking();
     317              : 
     318            3 :     MediaKeyErrorStatus status = m_ocdmSession->getCdmKeySessionId(cdmKeySessionId);
     319            3 :     if (MediaKeyErrorStatus::OK != status)
     320              :     {
     321            1 :         RIALTO_SERVER_LOG_ERROR("Failed to get cdm key session id");
     322              :     }
     323              : 
     324            3 :     if ((checkForOcdmErrors("getCdmKeySessionId")) && (MediaKeyErrorStatus::OK == status))
     325              :     {
     326            1 :         status = MediaKeyErrorStatus::FAIL;
     327              :     }
     328              : 
     329            3 :     return status;
     330              : }
     331              : 
     332            2 : bool MediaKeySession::containsKey(const std::vector<uint8_t> &keyId)
     333              : {
     334            2 :     uint32_t result = m_ocdmSession->hasKeyId(keyId.data(), keyId.size());
     335              : 
     336            2 :     return static_cast<bool>(result);
     337              : }
     338              : 
     339            3 : MediaKeyErrorStatus MediaKeySession::setDrmHeader(const std::vector<uint8_t> &requestData)
     340              : {
     341            3 :     initOcdmErrorChecking();
     342              : 
     343            3 :     if (!m_isSessionConstructed)
     344              :     {
     345            0 :         RIALTO_SERVER_LOG_INFO("Session not yet constructed, queueing drm header to be set after construction");
     346            0 :         m_extendedInterfaceInUse = true;
     347            0 :         m_queuedDrmHeader = requestData;
     348            0 :         return MediaKeyErrorStatus::OK;
     349              :     }
     350              : 
     351            3 :     MediaKeyErrorStatus status = m_ocdmSession->setDrmHeader(requestData.data(), requestData.size());
     352            3 :     if (MediaKeyErrorStatus::OK != status)
     353              :     {
     354            1 :         RIALTO_SERVER_LOG_ERROR("Failed to set drm header");
     355              :     }
     356              : 
     357            3 :     if ((checkForOcdmErrors("setDrmHeader")) && (MediaKeyErrorStatus::OK == status))
     358              :     {
     359            1 :         status = MediaKeyErrorStatus::FAIL;
     360              :     }
     361              : 
     362            3 :     return status;
     363              : }
     364              : 
     365            3 : MediaKeyErrorStatus MediaKeySession::getLastDrmError(uint32_t &errorCode)
     366              : {
     367            3 :     initOcdmErrorChecking();
     368              : 
     369            3 :     MediaKeyErrorStatus status = m_ocdmSession->getLastDrmError(errorCode);
     370            3 :     if (MediaKeyErrorStatus::OK != status)
     371              :     {
     372            1 :         RIALTO_SERVER_LOG_ERROR("Failed to get last drm error");
     373              :     }
     374              : 
     375            3 :     if ((checkForOcdmErrors("getLastDrmError")) && (MediaKeyErrorStatus::OK == status))
     376              :     {
     377            1 :         status = MediaKeyErrorStatus::FAIL;
     378              :     }
     379              : 
     380            3 :     return status;
     381              : }
     382              : 
     383            7 : MediaKeyErrorStatus MediaKeySession::selectKeyId(const std::vector<uint8_t> &keyId)
     384              : {
     385            7 :     if (m_selectedKeyId == keyId)
     386              :     {
     387            1 :         return MediaKeyErrorStatus::OK;
     388              :     }
     389              : 
     390            6 :     initOcdmErrorChecking();
     391              : 
     392            6 :     m_extendedInterfaceInUse = true;
     393            6 :     MediaKeyErrorStatus status = m_ocdmSession->selectKeyId(keyId.size(), keyId.data());
     394            6 :     if (MediaKeyErrorStatus::OK == status)
     395              :     {
     396            4 :         RIALTO_SERVER_LOG_INFO("New keyId selected successfully");
     397            4 :         m_selectedKeyId = keyId;
     398              :     }
     399              : 
     400            6 :     if ((checkForOcdmErrors("selectKeyId")) && (MediaKeyErrorStatus::OK == status))
     401              :     {
     402            1 :         status = MediaKeyErrorStatus::FAIL;
     403              :     }
     404              : 
     405            6 :     return status;
     406              : }
     407              : 
     408           12 : void MediaKeySession::onProcessChallenge(const char url[], const uint8_t challenge[], const uint16_t challengeLength)
     409              : {
     410           12 :     std::string urlStr = url;
     411           24 :     std::vector<unsigned char> challengeVec = std::vector<unsigned char>{challenge, challenge + challengeLength};
     412           12 :     auto task = [&, urlStr = std::move(urlStr), challengeVec = std::move(challengeVec)]()
     413              :     {
     414           12 :         std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
     415           12 :         if (client)
     416              :         {
     417           12 :             if (m_licenseRequested)
     418              :             {
     419           11 :                 client->onLicenseRequest(m_kKeySessionId, challengeVec, urlStr);
     420           11 :                 m_licenseRequested = false;
     421              :             }
     422              :             else
     423              :             {
     424            1 :                 client->onLicenseRenewal(m_kKeySessionId, challengeVec);
     425              :             }
     426              :         }
     427           24 :     };
     428           12 :     m_mainThread->enqueueTask(m_mainThreadClientId, task);
     429              : }
     430              : 
     431            3 : void MediaKeySession::onKeyUpdated(const uint8_t keyId[], const uint8_t keyIdLength)
     432              : {
     433            6 :     std::vector<unsigned char> keyIdVec = std::vector<unsigned char>{keyId, keyId + keyIdLength};
     434            3 :     auto task = [&, keyIdVec = std::move(keyIdVec)]()
     435              :     {
     436            3 :         std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
     437            3 :         if (client)
     438              :         {
     439            3 :             KeyStatus status = m_ocdmSession->getStatus(&keyIdVec[0], keyIdVec.size());
     440            3 :             m_updatedKeyStatuses.push_back(std::make_pair(keyIdVec, status));
     441              :         }
     442            6 :     };
     443            3 :     m_mainThread->enqueueTask(m_mainThreadClientId, task);
     444              : }
     445              : 
     446            1 : void MediaKeySession::onAllKeysUpdated()
     447              : {
     448            1 :     auto task = [&]()
     449              :     {
     450            1 :         std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
     451            1 :         if (client)
     452              :         {
     453            1 :             client->onKeyStatusesChanged(m_kKeySessionId, m_updatedKeyStatuses);
     454            1 :             m_updatedKeyStatuses.clear();
     455              :         }
     456            1 :     };
     457            1 :     m_mainThread->enqueueTask(m_mainThreadClientId, task);
     458              : }
     459              : 
     460           10 : void MediaKeySession::onError(const char message[])
     461              : {
     462           10 :     RIALTO_SERVER_LOG_ERROR("Ocdm returned error: %s", message);
     463              : 
     464           10 :     std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
     465           10 :     if (!m_ongoingOcdmOperation)
     466              :     {
     467            0 :         RIALTO_SERVER_LOG_WARN("Received an asycronous OCDM error, ignoring");
     468              :     }
     469              :     else
     470              :     {
     471           10 :         m_ocdmError = true;
     472              :     }
     473              : }
     474              : 
     475           65 : void MediaKeySession::initOcdmErrorChecking()
     476              : {
     477           65 :     std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
     478           65 :     m_ongoingOcdmOperation = true;
     479           65 :     m_ocdmError = false;
     480              : }
     481              : 
     482           65 : bool MediaKeySession::checkForOcdmErrors(const char *operationStr)
     483              : {
     484           65 :     bool error = false;
     485              : 
     486           65 :     std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
     487           65 :     if (m_ocdmError)
     488              :     {
     489           10 :         RIALTO_SERVER_LOG_ERROR("MediaKeySession received an onError callback, operation '%s' failed", operationStr);
     490           10 :         error = true;
     491              :     }
     492           65 :     m_ongoingOcdmOperation = false;
     493              : 
     494           65 :     return error;
     495              : }
     496              : 
     497              : }; // namespace firebolt::rialto::server
        

Generated by: LCOV version 2.0-1