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

Generated by: LCOV version 2.0-1