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
|