LCOV - code coverage report
Current view: top level - media/server/main/source - SharedMemoryBuffer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 83.2 % 185 154
Test Date: 2025-02-18 13:13:53 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 <algorithm>
      21              : #include <cstring>
      22              : #include <numeric>
      23              : #include <stdexcept>
      24              : 
      25              : #include <fcntl.h>
      26              : #include <sys/mman.h>
      27              : #include <syscall.h>
      28              : #include <unistd.h>
      29              : 
      30              : #include "RialtoServerLogging.h"
      31              : #include "SharedMemoryBuffer.h"
      32              : #include "TypeConverters.h"
      33              : 
      34              : #if !defined(SYS_memfd_create)
      35              : #if defined(__NR_memfd_create)
      36              : #define SYS_memfd_create __NR_memfd_create
      37              : #elif defined(__arm__)
      38              : #define SYS_memfd_create 385
      39              : #endif
      40              : #endif
      41              : 
      42              : #if !defined(MFD_CLOEXEC)
      43              : #define MFD_CLOEXEC 0x0001U
      44              : #endif
      45              : 
      46              : #if !defined(MFD_ALLOW_SEALING)
      47              : #define MFD_ALLOW_SEALING 0x0002U
      48              : #endif
      49              : 
      50              : #if !defined(F_ADD_SEALS)
      51              : #if !defined(F_LINUX_SPECIFIC_BASE)
      52              : #define F_LINUX_SPECIFIC_BASE 1024
      53              : #endif
      54              : #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
      55              : #define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
      56              : 
      57              : #define F_SEAL_SEAL 0x0001
      58              : #define F_SEAL_SHRINK 0x0002
      59              : #define F_SEAL_GROW 0x0004
      60              : #define F_SEAL_WRITE 0x0008
      61              : #endif
      62              : 
      63              : namespace
      64              : {
      65              : const char *kMemoryBufferName{"rialto_avbuf"};
      66              : constexpr int kNoIdAssigned{-1};
      67              : constexpr uint32_t kVideoRegionSize = 7 * 1024 * 1024; // 7MB
      68              : constexpr uint32_t kAudioRegionSize = 1 * 1024 * 1024; // 1MB
      69              : constexpr uint32_t kSubtitleRegionSize = 256 * 1024;   // 256kB
      70              : constexpr uint32_t kWebAudioRegionSize = 10 * 1024;    // 10KB
      71              : 
      72              : std::vector<firebolt::rialto::server::SharedMemoryBuffer::Partition>
      73           96 : calculatePartitionSize(firebolt::rialto::server::ISharedMemoryBuffer::MediaPlaybackType playbackType, int num)
      74              : {
      75           96 :     if (firebolt::rialto::server::ISharedMemoryBuffer::MediaPlaybackType::GENERIC == playbackType)
      76              :     {
      77              :         // As (for now) resolution of playback (for example HD or UHD) is not known, partitions have the same size.
      78           48 :         firebolt::rialto::server::SharedMemoryBuffer::Partition singlePlaybackDataBuffer{kNoIdAssigned, kAudioRegionSize,
      79              :                                                                                          kVideoRegionSize,
      80              :                                                                                          kSubtitleRegionSize};
      81           48 :         return std::vector<firebolt::rialto::server::SharedMemoryBuffer::Partition>(num, singlePlaybackDataBuffer);
      82              :     }
      83           48 :     else if (firebolt::rialto::server::ISharedMemoryBuffer::MediaPlaybackType::WEB_AUDIO == playbackType)
      84              :     {
      85           48 :         firebolt::rialto::server::SharedMemoryBuffer::Partition webAudioDataBuffer{kNoIdAssigned, kWebAudioRegionSize, 0};
      86           48 :         return std::vector<firebolt::rialto::server::SharedMemoryBuffer::Partition>(num, webAudioDataBuffer);
      87              :     }
      88              :     else
      89              :     {
      90            0 :         return std::vector<firebolt::rialto::server::SharedMemoryBuffer::Partition>();
      91              :     }
      92              : }
      93              : 
      94           18 : const char *toString(const firebolt::rialto::server::ISharedMemoryBuffer::MediaPlaybackType &type)
      95              : {
      96           18 :     switch (type)
      97              :     {
      98           13 :     case firebolt::rialto::server::ISharedMemoryBuffer::MediaPlaybackType::GENERIC:
      99              :     {
     100           13 :         return "GENERIC";
     101              :     }
     102            5 :     case firebolt::rialto::server::ISharedMemoryBuffer::MediaPlaybackType::WEB_AUDIO:
     103              :     {
     104            5 :         return "WEB_AUDIO";
     105              :     }
     106              :     }
     107            0 :     return "UNKNOWN";
     108              : }
     109              : } // namespace
     110              : 
     111              : namespace firebolt::rialto::server
     112              : {
     113            2 : std::unique_ptr<ISharedMemoryBufferFactory> ISharedMemoryBufferFactory::createFactory()
     114              : {
     115            2 :     return std::make_unique<SharedMemoryBufferFactory>();
     116              : }
     117              : 
     118              : std::shared_ptr<ISharedMemoryBuffer>
     119           48 : SharedMemoryBufferFactory::createSharedMemoryBuffer(unsigned numOfPlaybacks, unsigned numOfWebAudioPlayers) const
     120              : {
     121           48 :     return std::make_shared<SharedMemoryBuffer>(numOfPlaybacks, numOfWebAudioPlayers);
     122              : }
     123              : 
     124           48 : SharedMemoryBuffer::SharedMemoryBuffer(unsigned numOfPlaybacks, unsigned numOfWebAudioPlayers)
     125           48 :     : m_genericPartitions{calculatePartitionSize(MediaPlaybackType::GENERIC, numOfPlaybacks)},
     126           48 :       m_webAudioPartitions{calculatePartitionSize(MediaPlaybackType::WEB_AUDIO, numOfWebAudioPlayers)},
     127           48 :       m_dataBufferLen{0}, m_dataBufferFd{-1}, m_dataBuffer{nullptr}
     128              : {
     129           48 :     int fd = syscall(SYS_memfd_create, kMemoryBufferName, MFD_CLOEXEC | MFD_ALLOW_SEALING);
     130           48 :     if (fd < 0)
     131              :     {
     132            0 :         RIALTO_SERVER_LOG_SYS_ERROR(errno, "failed to create memory buffer");
     133              :     }
     134              :     else
     135              :     {
     136           48 :         const size_t kBufferSize{calculateBufferSize()};
     137           48 :         if (ftruncate(fd, static_cast<off_t>(kBufferSize)) == -1)
     138              :         {
     139            0 :             RIALTO_SERVER_LOG_SYS_ERROR(errno, "failed to resize memfd");
     140              :         }
     141           48 :         else if (fcntl(fd, F_ADD_SEALS, (F_SEAL_SEAL | F_SEAL_GROW | F_SEAL_SHRINK)) == -1)
     142              :         {
     143            0 :             RIALTO_SERVER_LOG_SYS_ERROR(errno, "failed to seal memfd");
     144              :         }
     145              :         else
     146              :         {
     147           48 :             void *addr = mmap(nullptr, kBufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     148           48 :             if (addr != MAP_FAILED)
     149              :             {
     150           48 :                 m_dataBufferLen = kBufferSize;
     151           48 :                 m_dataBufferFd = fd;
     152           48 :                 m_dataBuffer = reinterpret_cast<uint8_t *>(addr);
     153           48 :                 RIALTO_SERVER_LOG_INFO("Shared Memory Buffer size: %d, ptr: %p", m_dataBufferLen, m_dataBuffer);
     154              :             }
     155              :         }
     156              : 
     157           48 :         if (m_dataBufferFd == -1)
     158              :         {
     159            0 :             if (close(fd) != 0)
     160            0 :                 RIALTO_SERVER_LOG_SYS_ERROR(errno, "failed to close fd");
     161              :         }
     162              :     }
     163              : 
     164           48 :     if (m_dataBufferFd == -1)
     165              :     {
     166            0 :         RIALTO_SERVER_LOG_ERROR("Shared Memory Buffer initialization failed");
     167            0 :         throw std::runtime_error("Shared Memory Buffer initialization failed");
     168              :     }
     169           48 : }
     170              : 
     171           48 : SharedMemoryBuffer::~SharedMemoryBuffer()
     172              : {
     173           48 :     RIALTO_SERVER_LOG_INFO("Destroying Shared Memory Buffer");
     174           48 :     if (m_dataBufferFd != -1)
     175              :     {
     176           48 :         if (munmap(m_dataBuffer, m_dataBufferLen) != 0)
     177            0 :             RIALTO_SERVER_LOG_SYS_ERROR(errno, "failed to unmap buffer");
     178           48 :         if (close(m_dataBufferFd) != 0)
     179            0 :             RIALTO_SERVER_LOG_SYS_ERROR(errno, "failed to close data buffer fd");
     180              : 
     181           48 :         m_dataBufferLen = 0;
     182           48 :         m_dataBufferFd = -1;
     183           48 :         m_dataBuffer = nullptr;
     184              :     }
     185              : }
     186              : 
     187           43 : bool SharedMemoryBuffer::mapPartition(MediaPlaybackType playbackType, int id)
     188              : {
     189           43 :     std::vector<Partition> *partitions = getPlaybackTypePartition(playbackType);
     190           43 :     if (!partitions)
     191              :     {
     192            0 :         RIALTO_SERVER_LOG_ERROR("Cannot map the partition for playback type %s with id: %d", toString(playbackType), id);
     193            0 :         return false;
     194              :     }
     195              : 
     196           96 :     auto partition = std::find_if(partitions->begin(), partitions->end(), [id](const auto &p) { return p.id == id; });
     197           43 :     if (partition != partitions->end())
     198              :     {
     199            2 :         RIALTO_SERVER_LOG_DEBUG("Skip to map shm partition for id: %d. - partition already assigned", id);
     200            2 :         return true;
     201              :     }
     202              :     auto freePartition =
     203           87 :         std::find_if(partitions->begin(), partitions->end(), [](const auto &p) { return p.id == kNoIdAssigned; });
     204           41 :     if (freePartition == partitions->end())
     205              :     {
     206            2 :         RIALTO_SERVER_LOG_ERROR("Failed to map Shm partition for id: %d. No free partition available.", id);
     207            2 :         return false;
     208              :     }
     209           39 :     freePartition->id = id;
     210           39 :     return true;
     211              : }
     212              : 
     213            6 : bool SharedMemoryBuffer::unmapPartition(MediaPlaybackType playbackType, int id)
     214              : {
     215            6 :     std::vector<Partition> *partitions = getPlaybackTypePartition(playbackType);
     216            6 :     if (!partitions)
     217              :     {
     218            0 :         RIALTO_SERVER_LOG_ERROR("Cannot unmap the partition for playback type %s with id: %d", toString(playbackType),
     219              :                                 id);
     220            0 :         return false;
     221              :     }
     222              : 
     223           12 :     auto partition = std::find_if(partitions->begin(), partitions->end(), [id](const auto &p) { return p.id == id; });
     224            6 :     if (partition == partitions->end())
     225              :     {
     226            2 :         RIALTO_SERVER_LOG_WARN("Failed to unmap Shm partition for id: %d. - partition could not be found", id);
     227            2 :         return false;
     228              :     }
     229            4 :     partition->id = kNoIdAssigned;
     230            4 :     return true;
     231              : }
     232              : 
     233            8 : bool SharedMemoryBuffer::clearData(MediaPlaybackType playbackType, int id, const MediaSourceType &mediaSourceType) const
     234              : {
     235            8 :     const std::vector<Partition> *kPartitions = getPlaybackTypePartition(playbackType);
     236            8 :     if (!kPartitions)
     237              :     {
     238            0 :         RIALTO_SERVER_LOG_ERROR("Cannot clear the %s data for playback type %s with id: %d",
     239              :                                 common::convertMediaSourceType(mediaSourceType), toString(playbackType), id);
     240            0 :         return false;
     241              :     }
     242              : 
     243           16 :     auto partition = std::find_if(kPartitions->begin(), kPartitions->end(), [id](const auto &p) { return p.id == id; });
     244            8 :     if (partition == kPartitions->end())
     245              :     {
     246            4 :         RIALTO_SERVER_LOG_WARN("Failed to clear %s data for playback type %s with id: %d. - partition could not be "
     247              :                                "found",
     248              :                                common::convertMediaSourceType(mediaSourceType), toString(playbackType), id);
     249            4 :         return false;
     250              :     }
     251            4 :     std::uint8_t *partitionDataPtr = nullptr;
     252            4 :     if (!getDataPtrForPartition(playbackType, id, &partitionDataPtr))
     253              :     {
     254            0 :         RIALTO_SERVER_LOG_ERROR("Failed to clear %s data for playback type %s with id: %d. - could not get partition "
     255              :                                 "data "
     256              :                                 "ptr",
     257              :                                 common::convertMediaSourceType(mediaSourceType), toString(playbackType), id);
     258            0 :         return false;
     259              :     }
     260              : 
     261            4 :     if (MediaSourceType::VIDEO == mediaSourceType)
     262              :     {
     263            1 :         std::uint8_t *videoData = partitionDataPtr;
     264            1 :         memset(videoData, 0x00, partition->dataBufferVideoLen);
     265            1 :         return true;
     266              :     }
     267            3 :     if (MediaSourceType::AUDIO == mediaSourceType)
     268              :     {
     269            2 :         std::uint8_t *audioData = partitionDataPtr + partition->dataBufferVideoLen;
     270            2 :         memset(audioData, 0x00, partition->dataBufferAudioLen);
     271            2 :         return true;
     272              :     }
     273            1 :     if (MediaSourceType::SUBTITLE == mediaSourceType)
     274              :     {
     275            1 :         std::uint8_t *subtitleoData = partitionDataPtr + partition->dataBufferVideoLen + partition->dataBufferAudioLen;
     276            1 :         memset(subtitleoData, 0x00, partition->dataBufferSubtitleLen);
     277            1 :         return true;
     278              :     }
     279              : 
     280            0 :     return false;
     281              : }
     282              : 
     283           13 : std::uint32_t SharedMemoryBuffer::getDataOffset(MediaPlaybackType playbackType, int id,
     284              :                                                 const MediaSourceType &mediaSourceType) const
     285              : {
     286           13 :     std::uint8_t *buffer = getDataPtr(playbackType, id, mediaSourceType);
     287           13 :     if (!buffer)
     288              :     {
     289           12 :         throw std::runtime_error("Buffer not found for playback type " + std::string(toString(playbackType)) +
     290           16 :                                  " with id: " + std::to_string(id));
     291              :     }
     292            9 :     return buffer - m_dataBuffer;
     293              : }
     294              : 
     295           10 : std::uint32_t SharedMemoryBuffer::getMaxDataLen(MediaPlaybackType playbackType, int id,
     296              :                                                 const MediaSourceType &mediaSourceType) const
     297              : {
     298           10 :     const std::vector<Partition> *kPartitions = getPlaybackTypePartition(playbackType);
     299           10 :     if (!kPartitions)
     300              :     {
     301            0 :         RIALTO_SERVER_LOG_ERROR("Cannot get the max data length for playback type %s with id: %d type: %s",
     302              :                                 toString(playbackType), id, common::convertMediaSourceType(mediaSourceType));
     303            0 :         return false;
     304              :     }
     305              : 
     306           20 :     auto partition = std::find_if(kPartitions->begin(), kPartitions->end(), [id](const auto &p) { return p.id == id; });
     307           10 :     if (partition == kPartitions->end())
     308              :     {
     309            3 :         RIALTO_SERVER_LOG_WARN("Failed to get buffer length for playback type %s with id: %d. type: %s- partition "
     310              :                                "could not be "
     311              :                                "found",
     312              :                                toString(playbackType), id, common::convertMediaSourceType(mediaSourceType));
     313            3 :         return 0;
     314              :     }
     315              : 
     316            7 :     if (MediaSourceType::VIDEO == mediaSourceType)
     317              :     {
     318            3 :         return partition->dataBufferVideoLen;
     319              :     }
     320            4 :     if (MediaSourceType::AUDIO == mediaSourceType)
     321              :     {
     322            3 :         return partition->dataBufferAudioLen;
     323              :     }
     324            1 :     if (MediaSourceType::SUBTITLE == mediaSourceType)
     325              :     {
     326            1 :         return partition->dataBufferSubtitleLen;
     327              :     }
     328              : 
     329            0 :     return 0;
     330              : }
     331              : 
     332           28 : std::uint8_t *SharedMemoryBuffer::getDataPtr(MediaPlaybackType playbackType, int id,
     333              :                                              const MediaSourceType &mediaSourceType) const
     334              : {
     335           28 :     const std::vector<Partition> *kPartitions = getPlaybackTypePartition(playbackType);
     336           28 :     if (!kPartitions)
     337              :     {
     338            0 :         RIALTO_SERVER_LOG_ERROR("Cannot get the buffer offset for playback type %s with id: %d, type: %s",
     339              :                                 toString(playbackType), id, common::convertMediaSourceType(mediaSourceType));
     340            0 :         return nullptr;
     341              :     }
     342              : 
     343           63 :     auto partition = std::find_if(kPartitions->begin(), kPartitions->end(), [id](const auto &p) { return p.id == id; });
     344           28 :     if (partition == kPartitions->end())
     345              :     {
     346            7 :         RIALTO_SERVER_LOG_WARN("Failed to get buffer offset for playback type %s with id: %d, type: %s. - partition "
     347              :                                "could not be "
     348              :                                "found",
     349              :                                toString(playbackType), id, common::convertMediaSourceType(mediaSourceType));
     350            7 :         return nullptr;
     351              :     }
     352           21 :     std::uint8_t *partitionDataPtr = nullptr;
     353           21 :     if (!getDataPtrForPartition(playbackType, id, &partitionDataPtr))
     354              :     {
     355            0 :         RIALTO_SERVER_LOG_ERROR("Failed to get buffer offset for playback type %s with id: %d. - could not get "
     356              :                                 "partition data ptr",
     357              :                                 toString(playbackType), id);
     358            0 :         return nullptr;
     359              :     }
     360              : 
     361           21 :     if ((MediaSourceType::VIDEO == mediaSourceType) && (0 != partition->dataBufferVideoLen))
     362              :     {
     363            7 :         return partitionDataPtr;
     364              :     }
     365           14 :     if ((MediaSourceType::AUDIO == mediaSourceType) && (0 != partition->dataBufferAudioLen))
     366              :     {
     367           10 :         return partitionDataPtr + partition->dataBufferVideoLen;
     368              :     }
     369            4 :     if ((MediaSourceType::SUBTITLE == mediaSourceType) && (0 != partition->dataBufferSubtitleLen))
     370              :     {
     371            2 :         return partitionDataPtr + partition->dataBufferVideoLen + partition->dataBufferAudioLen;
     372              :     }
     373              : 
     374            2 :     return nullptr;
     375              : }
     376              : 
     377            1 : int SharedMemoryBuffer::getFd() const
     378              : {
     379            1 :     return m_dataBufferFd;
     380              : }
     381              : 
     382            1 : std::uint32_t SharedMemoryBuffer::getSize() const
     383              : {
     384            1 :     return m_dataBufferLen;
     385              : }
     386              : 
     387            3 : std::uint8_t *SharedMemoryBuffer::getBuffer() const
     388              : {
     389            3 :     return m_dataBuffer;
     390              : }
     391              : 
     392           48 : size_t SharedMemoryBuffer::calculateBufferSize() const
     393              : {
     394              :     size_t genericSum =
     395           48 :         std::accumulate(m_genericPartitions.begin(), m_genericPartitions.end(), 0,
     396           52 :                         [](size_t sum, const Partition &p)
     397           52 :                         { return sum + p.dataBufferAudioLen + p.dataBufferVideoLen + p.dataBufferSubtitleLen; });
     398           48 :     size_t webAudioSum = std::accumulate(m_webAudioPartitions.begin(), m_webAudioPartitions.end(), 0,
     399           52 :                                          [](size_t sum, const Partition &p)
     400           52 :                                          { return sum + p.dataBufferAudioLen + p.dataBufferVideoLen; });
     401           48 :     return genericSum + webAudioSum;
     402              : }
     403              : 
     404           25 : bool SharedMemoryBuffer::getDataPtrForPartition(MediaPlaybackType playbackType, int id, std::uint8_t **ptr) const
     405              : {
     406           25 :     std::uint8_t *result = m_dataBuffer;
     407              : 
     408           39 :     for (const auto &partition : m_genericPartitions)
     409              :     {
     410           33 :         if ((MediaPlaybackType::GENERIC == playbackType) && (partition.id == id))
     411              :         {
     412           19 :             *ptr = result;
     413           19 :             return true;
     414              :         }
     415           14 :         result += (partition.dataBufferVideoLen + partition.dataBufferAudioLen + partition.dataBufferSubtitleLen);
     416              :     }
     417              : 
     418            8 :     for (const auto &partition : m_webAudioPartitions)
     419              :     {
     420            8 :         if ((MediaPlaybackType::WEB_AUDIO == playbackType) && (partition.id == id))
     421              :         {
     422            6 :             *ptr = result;
     423            6 :             return true;
     424              :         }
     425            2 :         result += (partition.dataBufferVideoLen + partition.dataBufferAudioLen + partition.dataBufferSubtitleLen);
     426              :     }
     427              : 
     428            0 :     RIALTO_SERVER_LOG_ERROR("Could not find the data ptr for playback type %s with id: %d", toString(playbackType), id);
     429            0 :     return false;
     430              : }
     431              : const std::vector<SharedMemoryBuffer::Partition> *
     432           95 : SharedMemoryBuffer::getPlaybackTypePartition(MediaPlaybackType playbackType) const
     433              : {
     434           95 :     if (firebolt::rialto::server::ISharedMemoryBuffer::MediaPlaybackType::GENERIC == playbackType)
     435              :     {
     436           62 :         return &m_genericPartitions;
     437              :     }
     438           33 :     else if (firebolt::rialto::server::ISharedMemoryBuffer::MediaPlaybackType::WEB_AUDIO == playbackType)
     439              :     {
     440           33 :         return &m_webAudioPartitions;
     441              :     }
     442              :     else
     443              :     {
     444            0 :         RIALTO_SERVER_LOG_ERROR("Invalid playback type");
     445            0 :         return nullptr;
     446              :     }
     447              : }
     448              : 
     449           49 : std::vector<SharedMemoryBuffer::Partition> *SharedMemoryBuffer::getPlaybackTypePartition(MediaPlaybackType playbackType)
     450              : {
     451              :     return const_cast<std::vector<SharedMemoryBuffer::Partition> *>(
     452           49 :         const_cast<const SharedMemoryBuffer *>(this)->getPlaybackTypePartition(playbackType));
     453              : }
     454              : } // namespace firebolt::rialto::server
        

Generated by: LCOV version 2.0-1