LCOV - code coverage report
Current view: top level - media/server/main/source - MediaKeySession.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.7 % 210 203
Test Date: 2025-08-12 08:56:06 Functions: 100.0 % 27 27

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

Generated by: LCOV version 2.0-1