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

Generated by: LCOV version 2.0-1