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 <chrono>
21 : #include <cinttypes>
22 : #include <stdexcept>
23 :
24 : #include "FlushWatcher.h"
25 : #include "GstDispatcherThread.h"
26 : #include "GstGenericPlayer.h"
27 : #include "GstProtectionMetadata.h"
28 : #include "IGstTextTrackSinkFactory.h"
29 : #include "IMediaPipeline.h"
30 : #include "ITimer.h"
31 : #include "RialtoServerLogging.h"
32 : #include "TypeConverters.h"
33 : #include "Utils.h"
34 : #include "WorkerThread.h"
35 : #include "tasks/generic/GenericPlayerTaskFactory.h"
36 :
37 : namespace
38 : {
39 : /**
40 : * @brief Report position interval in ms.
41 : * The position reporting timer should be started whenever the PLAYING state is entered and stopped
42 : * whenever the session moves to another playback state.
43 : */
44 : constexpr std::chrono::milliseconds kPositionReportTimerMs{250};
45 : constexpr std::chrono::seconds kSubtitleClockResyncInterval{10};
46 :
47 1 : bool operator==(const firebolt::rialto::server::SegmentData &lhs, const firebolt::rialto::server::SegmentData &rhs)
48 : {
49 2 : return (lhs.position == rhs.position) && (lhs.resetTime == rhs.resetTime) && (lhs.appliedRate == rhs.appliedRate) &&
50 2 : (lhs.stopPosition == rhs.stopPosition);
51 : }
52 : } // namespace
53 :
54 : namespace firebolt::rialto::server
55 : {
56 : std::weak_ptr<IGstGenericPlayerFactory> GstGenericPlayerFactory::m_factory;
57 :
58 3 : std::shared_ptr<IGstGenericPlayerFactory> IGstGenericPlayerFactory::getFactory()
59 : {
60 3 : std::shared_ptr<IGstGenericPlayerFactory> factory = GstGenericPlayerFactory::m_factory.lock();
61 :
62 3 : if (!factory)
63 : {
64 : try
65 : {
66 3 : factory = std::make_shared<GstGenericPlayerFactory>();
67 : }
68 0 : catch (const std::exception &e)
69 : {
70 0 : RIALTO_SERVER_LOG_ERROR("Failed to create the gstreamer player factory, reason: %s", e.what());
71 : }
72 :
73 3 : GstGenericPlayerFactory::m_factory = factory;
74 : }
75 :
76 3 : return factory;
77 : }
78 :
79 1 : std::unique_ptr<IGstGenericPlayer> GstGenericPlayerFactory::createGstGenericPlayer(
80 : IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type,
81 : const VideoRequirements &videoRequirements,
82 : const std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapperFactory> &rdkGstreamerUtilsWrapperFactory)
83 : {
84 1 : std::unique_ptr<IGstGenericPlayer> gstPlayer;
85 :
86 : try
87 : {
88 1 : auto gstWrapperFactory = firebolt::rialto::wrappers::IGstWrapperFactory::getFactory();
89 1 : auto glibWrapperFactory = firebolt::rialto::wrappers::IGlibWrapperFactory::getFactory();
90 1 : std::shared_ptr<firebolt::rialto::wrappers::IGstWrapper> gstWrapper;
91 1 : std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapper> glibWrapper;
92 1 : std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapper> rdkGstreamerUtilsWrapper;
93 1 : if ((!gstWrapperFactory) || (!(gstWrapper = gstWrapperFactory->getGstWrapper())))
94 : {
95 0 : throw std::runtime_error("Cannot create GstWrapper");
96 : }
97 1 : if ((!glibWrapperFactory) || (!(glibWrapper = glibWrapperFactory->getGlibWrapper())))
98 : {
99 0 : throw std::runtime_error("Cannot create GlibWrapper");
100 : }
101 2 : if ((!rdkGstreamerUtilsWrapperFactory) ||
102 2 : (!(rdkGstreamerUtilsWrapper = rdkGstreamerUtilsWrapperFactory->createRdkGstreamerUtilsWrapper())))
103 : {
104 0 : throw std::runtime_error("Cannot create RdkGstreamerUtilsWrapper");
105 : }
106 : gstPlayer = std::make_unique<
107 2 : GstGenericPlayer>(client, decryptionService, type, videoRequirements, gstWrapper, glibWrapper,
108 2 : rdkGstreamerUtilsWrapper, IGstInitialiser::instance(), std::make_unique<FlushWatcher>(),
109 2 : IGstSrcFactory::getFactory(), common::ITimerFactory::getFactory(),
110 2 : std::make_unique<GenericPlayerTaskFactory>(client, gstWrapper, glibWrapper,
111 : rdkGstreamerUtilsWrapper,
112 2 : IGstTextTrackSinkFactory::createFactory()),
113 2 : std::make_unique<WorkerThreadFactory>(), std::make_unique<GstDispatcherThreadFactory>(),
114 3 : IGstProtectionMetadataHelperFactory::createFactory());
115 1 : }
116 0 : catch (const std::exception &e)
117 : {
118 0 : RIALTO_SERVER_LOG_ERROR("Failed to create the gstreamer player, reason: %s", e.what());
119 : }
120 :
121 1 : return gstPlayer;
122 : }
123 :
124 214 : GstGenericPlayer::GstGenericPlayer(
125 : IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type,
126 : const VideoRequirements &videoRequirements,
127 : const std::shared_ptr<firebolt::rialto::wrappers::IGstWrapper> &gstWrapper,
128 : const std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapper> &glibWrapper,
129 : const std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapper> &rdkGstreamerUtilsWrapper,
130 : const IGstInitialiser &gstInitialiser, std::unique_ptr<IFlushWatcher> &&flushWatcher,
131 : const std::shared_ptr<IGstSrcFactory> &gstSrcFactory, std::shared_ptr<common::ITimerFactory> timerFactory,
132 : std::unique_ptr<IGenericPlayerTaskFactory> taskFactory, std::unique_ptr<IWorkerThreadFactory> workerThreadFactory,
133 : std::unique_ptr<IGstDispatcherThreadFactory> gstDispatcherThreadFactory,
134 214 : std::shared_ptr<IGstProtectionMetadataHelperFactory> gstProtectionMetadataFactory)
135 214 : : m_gstPlayerClient(client), m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper},
136 428 : m_rdkGstreamerUtilsWrapper{rdkGstreamerUtilsWrapper}, m_timerFactory{timerFactory},
137 642 : m_taskFactory{std::move(taskFactory)}, m_flushWatcher{std::move(flushWatcher)}
138 : {
139 214 : RIALTO_SERVER_LOG_DEBUG("GstGenericPlayer is constructed.");
140 :
141 214 : gstInitialiser.waitForInitialisation();
142 :
143 214 : m_context.decryptionService = &decryptionService;
144 :
145 214 : if ((!gstSrcFactory) || (!(m_context.gstSrc = gstSrcFactory->getGstSrc())))
146 : {
147 2 : throw std::runtime_error("Cannot create GstSrc");
148 : }
149 :
150 212 : if (!timerFactory)
151 : {
152 1 : throw std::runtime_error("TimeFactory is invalid");
153 : }
154 :
155 422 : if ((!gstProtectionMetadataFactory) ||
156 422 : (!(m_protectionMetadataWrapper = gstProtectionMetadataFactory->createProtectionMetadataWrapper(m_gstWrapper))))
157 : {
158 0 : throw std::runtime_error("Cannot create protection metadata wrapper");
159 : }
160 :
161 : // Ensure that rialtosrc has been initalised
162 211 : m_context.gstSrc->initSrc();
163 :
164 : // Start task thread
165 211 : if ((!workerThreadFactory) || (!(m_workerThread = workerThreadFactory->createWorkerThread())))
166 : {
167 0 : throw std::runtime_error("Failed to create the worker thread");
168 : }
169 :
170 : // Initialise pipeline
171 211 : switch (type)
172 : {
173 210 : case MediaType::MSE:
174 : {
175 210 : initMsePipeline();
176 210 : break;
177 : }
178 1 : default:
179 : {
180 1 : resetWorkerThread();
181 1 : throw std::runtime_error("Media type not supported");
182 : }
183 : }
184 :
185 : // Check the video requirements for a limited video.
186 : // If the video requirements are set to anything lower than the minimum, this playback is assumed to be a secondary
187 : // video in a dual video scenario.
188 210 : if ((kMinPrimaryVideoWidth > videoRequirements.maxWidth) || (kMinPrimaryVideoHeight > videoRequirements.maxHeight))
189 : {
190 8 : RIALTO_SERVER_LOG_MIL("Secondary video playback selected");
191 8 : bool westerossinkSecondaryVideoResult = setWesterossinkSecondaryVideo();
192 8 : bool ermContextResult = setErmContext();
193 8 : if (!westerossinkSecondaryVideoResult && !ermContextResult)
194 : {
195 1 : resetWorkerThread();
196 1 : termPipeline();
197 1 : throw std::runtime_error("Could not set secondary video");
198 : }
199 7 : }
200 : else
201 : {
202 202 : RIALTO_SERVER_LOG_MIL("Primary video playback selected");
203 : }
204 :
205 : m_gstDispatcherThread =
206 209 : gstDispatcherThreadFactory->createGstDispatcherThread(*this, m_context.pipeline, m_gstWrapper);
207 299 : }
208 :
209 418 : GstGenericPlayer::~GstGenericPlayer()
210 : {
211 209 : RIALTO_SERVER_LOG_DEBUG("GstGenericPlayer is destructed.");
212 209 : m_gstDispatcherThread.reset();
213 :
214 209 : resetWorkerThread();
215 :
216 209 : termPipeline();
217 418 : }
218 :
219 210 : void GstGenericPlayer::initMsePipeline()
220 : {
221 : // Make playbin
222 210 : m_context.pipeline = m_gstWrapper->gstElementFactoryMake("playbin", "media_pipeline");
223 : // Set pipeline flags
224 210 : setPlaybinFlags(true);
225 :
226 : // Set callbacks
227 210 : m_glibWrapper->gSignalConnect(m_context.pipeline, "source-setup", G_CALLBACK(&GstGenericPlayer::setupSource), this);
228 210 : m_glibWrapper->gSignalConnect(m_context.pipeline, "element-setup", G_CALLBACK(&GstGenericPlayer::setupElement), this);
229 210 : m_glibWrapper->gSignalConnect(m_context.pipeline, "deep-element-added",
230 : G_CALLBACK(&GstGenericPlayer::deepElementAdded), this);
231 :
232 : // Set uri
233 210 : m_glibWrapper->gObjectSet(m_context.pipeline, "uri", "rialto://", nullptr);
234 :
235 : // Check playsink
236 210 : GstElement *playsink = (m_gstWrapper->gstBinGetByName(GST_BIN(m_context.pipeline), "playsink"));
237 210 : if (playsink)
238 : {
239 209 : m_glibWrapper->gObjectSet(G_OBJECT(playsink), "send-event-mode", 0, nullptr);
240 209 : m_gstWrapper->gstObjectUnref(playsink);
241 : }
242 : else
243 : {
244 1 : GST_WARNING("No playsink ?!?!?");
245 : }
246 210 : if (GST_STATE_CHANGE_FAILURE == m_gstWrapper->gstElementSetState(m_context.pipeline, GST_STATE_READY))
247 : {
248 1 : GST_WARNING("Failed to set pipeline to READY state");
249 : }
250 210 : RIALTO_SERVER_LOG_MIL("New RialtoServer's pipeline created");
251 : }
252 :
253 211 : void GstGenericPlayer::resetWorkerThread()
254 : {
255 211 : m_postponedFlushes.clear();
256 : // Shutdown task thread
257 211 : m_workerThread->enqueueTask(m_taskFactory->createShutdown(*this));
258 211 : m_workerThread->join();
259 211 : m_workerThread.reset();
260 : }
261 :
262 210 : void GstGenericPlayer::termPipeline()
263 : {
264 210 : if (m_finishSourceSetupTimer && m_finishSourceSetupTimer->isActive())
265 : {
266 0 : m_finishSourceSetupTimer->cancel();
267 : }
268 :
269 210 : m_finishSourceSetupTimer.reset();
270 :
271 259 : for (auto &elem : m_context.streamInfo)
272 : {
273 49 : StreamInfo &streamInfo = elem.second;
274 51 : for (auto &buffer : streamInfo.buffers)
275 : {
276 2 : m_gstWrapper->gstBufferUnref(buffer);
277 : }
278 :
279 49 : streamInfo.buffers.clear();
280 : }
281 :
282 210 : m_taskFactory->createStop(m_context, *this)->execute();
283 210 : GstBus *bus = m_gstWrapper->gstPipelineGetBus(GST_PIPELINE(m_context.pipeline));
284 210 : m_gstWrapper->gstBusSetSyncHandler(bus, nullptr, nullptr, nullptr);
285 210 : m_gstWrapper->gstObjectUnref(bus);
286 :
287 210 : if (m_context.source)
288 : {
289 1 : m_gstWrapper->gstObjectUnref(m_context.source);
290 : }
291 210 : if (m_context.subtitleSink)
292 : {
293 4 : m_gstWrapper->gstObjectUnref(m_context.subtitleSink);
294 4 : m_context.subtitleSink = nullptr;
295 : }
296 :
297 210 : if (m_context.videoSink)
298 : {
299 0 : m_gstWrapper->gstObjectUnref(m_context.videoSink);
300 0 : m_context.videoSink = nullptr;
301 : }
302 :
303 : // Delete the pipeline
304 210 : m_gstWrapper->gstObjectUnref(m_context.pipeline);
305 :
306 210 : RIALTO_SERVER_LOG_MIL("RialtoServer's pipeline terminated");
307 : }
308 :
309 841 : unsigned GstGenericPlayer::getGstPlayFlag(const char *nick)
310 : {
311 : GFlagsClass *flagsClass =
312 841 : static_cast<GFlagsClass *>(m_glibWrapper->gTypeClassRef(m_glibWrapper->gTypeFromName("GstPlayFlags")));
313 841 : GFlagsValue *flag = m_glibWrapper->gFlagsGetValueByNick(flagsClass, nick);
314 841 : return flag ? flag->value : 0;
315 : }
316 :
317 1 : void GstGenericPlayer::setupSource(GstElement *pipeline, GstElement *source, GstGenericPlayer *self)
318 : {
319 1 : self->m_gstWrapper->gstObjectRef(source);
320 1 : if (self->m_workerThread)
321 : {
322 1 : self->m_workerThread->enqueueTask(self->m_taskFactory->createSetupSource(self->m_context, *self, source));
323 : }
324 : }
325 :
326 1 : void GstGenericPlayer::setupElement(GstElement *pipeline, GstElement *element, GstGenericPlayer *self)
327 : {
328 1 : RIALTO_SERVER_LOG_DEBUG("Element %s added to the pipeline", GST_ELEMENT_NAME(element));
329 1 : self->m_gstWrapper->gstObjectRef(element);
330 1 : if (self->m_workerThread)
331 : {
332 1 : self->m_workerThread->enqueueTask(self->m_taskFactory->createSetupElement(self->m_context, *self, element));
333 : }
334 : }
335 :
336 1 : void GstGenericPlayer::deepElementAdded(GstBin *pipeline, GstBin *bin, GstElement *element, GstGenericPlayer *self)
337 : {
338 1 : RIALTO_SERVER_LOG_DEBUG("Deep element %s added to the pipeline", GST_ELEMENT_NAME(element));
339 1 : if (self->m_workerThread)
340 : {
341 2 : self->m_workerThread->enqueueTask(
342 2 : self->m_taskFactory->createDeepElementAdded(self->m_context, *self, pipeline, bin, element));
343 : }
344 1 : }
345 :
346 1 : void GstGenericPlayer::attachSource(const std::unique_ptr<IMediaPipeline::MediaSource> &attachedSource)
347 : {
348 1 : if (m_workerThread)
349 : {
350 1 : m_workerThread->enqueueTask(m_taskFactory->createAttachSource(m_context, *this, attachedSource));
351 : }
352 : }
353 :
354 1 : void GstGenericPlayer::removeSource(const MediaSourceType &mediaSourceType)
355 : {
356 1 : if (m_workerThread)
357 : {
358 1 : m_workerThread->enqueueTask(m_taskFactory->createRemoveSource(m_context, *this, mediaSourceType));
359 : }
360 : }
361 :
362 2 : void GstGenericPlayer::allSourcesAttached()
363 : {
364 2 : if (m_workerThread)
365 : {
366 2 : m_workerThread->enqueueTask(m_taskFactory->createFinishSetupSource(m_context, *this));
367 : }
368 : }
369 :
370 1 : void GstGenericPlayer::attachSamples(const IMediaPipeline::MediaSegmentVector &mediaSegments)
371 : {
372 1 : if (m_workerThread)
373 : {
374 1 : m_workerThread->enqueueTask(m_taskFactory->createAttachSamples(m_context, *this, mediaSegments));
375 : }
376 : }
377 :
378 1 : void GstGenericPlayer::attachSamples(const std::shared_ptr<IDataReader> &dataReader)
379 : {
380 1 : if (m_workerThread)
381 : {
382 1 : m_workerThread->enqueueTask(m_taskFactory->createReadShmDataAndAttachSamples(m_context, *this, dataReader));
383 : }
384 : }
385 :
386 1 : void GstGenericPlayer::setPosition(std::int64_t position)
387 : {
388 1 : if (m_workerThread)
389 : {
390 1 : m_workerThread->enqueueTask(m_taskFactory->createSetPosition(m_context, *this, position));
391 : }
392 : }
393 :
394 1 : void GstGenericPlayer::setPlaybackRate(double rate)
395 : {
396 1 : if (m_workerThread)
397 : {
398 1 : m_workerThread->enqueueTask(m_taskFactory->createSetPlaybackRate(m_context, rate));
399 : }
400 : }
401 :
402 11 : bool GstGenericPlayer::getPosition(std::int64_t &position)
403 : {
404 : // We are on main thread here, but m_context.pipeline can be used, because it's modified only in GstGenericPlayer
405 : // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here.
406 11 : position = getPosition(m_context.pipeline);
407 11 : if (position == -1)
408 : {
409 3 : RIALTO_SERVER_LOG_WARN("Query position failed");
410 3 : return false;
411 : }
412 :
413 8 : return true;
414 : }
415 :
416 44 : GstElement *GstGenericPlayer::getSink(const MediaSourceType &mediaSourceType) const
417 : {
418 44 : const char *kSinkName{nullptr};
419 44 : GstElement *sink{nullptr};
420 44 : switch (mediaSourceType)
421 : {
422 24 : case MediaSourceType::AUDIO:
423 24 : kSinkName = "audio-sink";
424 24 : break;
425 18 : case MediaSourceType::VIDEO:
426 18 : kSinkName = "video-sink";
427 18 : break;
428 2 : default:
429 2 : break;
430 : }
431 44 : if (!kSinkName)
432 : {
433 2 : RIALTO_SERVER_LOG_WARN("mediaSourceType not supported %d", static_cast<int>(mediaSourceType));
434 : }
435 : else
436 : {
437 42 : if (m_context.pipeline == nullptr)
438 : {
439 0 : RIALTO_SERVER_LOG_WARN("Pipeline is NULL!");
440 : }
441 : else
442 : {
443 42 : RIALTO_SERVER_LOG_DEBUG("Pipeline is valid: %p", m_context.pipeline);
444 : }
445 42 : m_glibWrapper->gObjectGet(m_context.pipeline, kSinkName, &sink, nullptr);
446 42 : if (sink)
447 : {
448 25 : GstElement *autoSink{sink};
449 25 : if (firebolt::rialto::MediaSourceType::VIDEO == mediaSourceType)
450 14 : autoSink = getSinkChildIfAutoVideoSink(sink);
451 11 : else if (firebolt::rialto::MediaSourceType::AUDIO == mediaSourceType)
452 11 : autoSink = getSinkChildIfAutoAudioSink(sink);
453 :
454 : // Is this an auto-sink?...
455 25 : if (autoSink != sink)
456 : {
457 2 : m_gstWrapper->gstObjectUnref(GST_OBJECT(sink));
458 :
459 : // increase the reference count of the auto sink
460 2 : sink = GST_ELEMENT(m_gstWrapper->gstObjectRef(GST_OBJECT(autoSink)));
461 : }
462 : }
463 : }
464 44 : return sink;
465 : }
466 :
467 1 : void GstGenericPlayer::setSourceFlushed(const MediaSourceType &mediaSourceType)
468 : {
469 1 : m_flushWatcher->setFlushed(mediaSourceType);
470 : }
471 :
472 1 : void GstGenericPlayer::postponeFlush(const MediaSourceType &mediaSourceType, bool resetTime)
473 : {
474 1 : m_postponedFlushes.emplace_back(std::make_pair(mediaSourceType, resetTime));
475 : }
476 :
477 1 : void GstGenericPlayer::executePostponedFlushes()
478 : {
479 1 : if (m_workerThread)
480 : {
481 2 : for (const auto &[mediaSourceType, resetTime] : m_postponedFlushes)
482 : {
483 1 : m_workerThread->enqueueTask(m_taskFactory->createFlush(m_context, *this, mediaSourceType, resetTime));
484 : }
485 : }
486 1 : m_postponedFlushes.clear();
487 : }
488 :
489 6 : void GstGenericPlayer::notifyPlaybackInfo()
490 : {
491 6 : PlaybackInfo info;
492 6 : getPosition(info.currentPosition);
493 6 : getVolume(info.volume);
494 6 : m_gstPlayerClient->notifyPlaybackInfo(info);
495 : }
496 :
497 19 : GstElement *GstGenericPlayer::getDecoder(const MediaSourceType &mediaSourceType)
498 : {
499 19 : GstIterator *it = m_gstWrapper->gstBinIterateRecurse(GST_BIN(m_context.pipeline));
500 19 : GValue item = G_VALUE_INIT;
501 19 : gboolean done = FALSE;
502 :
503 28 : while (!done)
504 : {
505 21 : switch (m_gstWrapper->gstIteratorNext(it, &item))
506 : {
507 12 : case GST_ITERATOR_OK:
508 : {
509 12 : GstElement *element = GST_ELEMENT(m_glibWrapper->gValueGetObject(&item));
510 12 : GstElementFactory *factory = m_gstWrapper->gstElementGetFactory(element);
511 :
512 12 : if (factory)
513 : {
514 12 : GstElementFactoryListType type = GST_ELEMENT_FACTORY_TYPE_DECODER;
515 12 : if (mediaSourceType == MediaSourceType::AUDIO)
516 : {
517 12 : type |= GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
518 : }
519 0 : else if (mediaSourceType == MediaSourceType::VIDEO)
520 : {
521 0 : type |= GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO;
522 : }
523 :
524 12 : if (m_gstWrapper->gstElementFactoryListIsType(factory, type))
525 : {
526 12 : m_glibWrapper->gValueUnset(&item);
527 12 : m_gstWrapper->gstIteratorFree(it);
528 12 : return GST_ELEMENT(m_gstWrapper->gstObjectRef(element));
529 : }
530 : }
531 :
532 0 : m_glibWrapper->gValueUnset(&item);
533 0 : break;
534 : }
535 2 : case GST_ITERATOR_RESYNC:
536 2 : m_gstWrapper->gstIteratorResync(it);
537 2 : break;
538 7 : case GST_ITERATOR_ERROR:
539 : case GST_ITERATOR_DONE:
540 7 : done = TRUE;
541 7 : break;
542 : }
543 : }
544 :
545 7 : RIALTO_SERVER_LOG_WARN("Could not find decoder");
546 :
547 7 : m_glibWrapper->gValueUnset(&item);
548 7 : m_gstWrapper->gstIteratorFree(it);
549 :
550 7 : return nullptr;
551 : }
552 :
553 3 : GstElement *GstGenericPlayer::getParser(const MediaSourceType &mediaSourceType)
554 : {
555 3 : GstIterator *it = m_gstWrapper->gstBinIterateRecurse(GST_BIN(m_context.pipeline));
556 3 : GValue item = G_VALUE_INIT;
557 3 : gboolean done = FALSE;
558 :
559 4 : while (!done)
560 : {
561 3 : switch (m_gstWrapper->gstIteratorNext(it, &item))
562 : {
563 2 : case GST_ITERATOR_OK:
564 : {
565 2 : GstElement *element = GST_ELEMENT(m_glibWrapper->gValueGetObject(&item));
566 2 : GstElementFactory *factory = m_gstWrapper->gstElementGetFactory(element);
567 :
568 2 : if (factory)
569 : {
570 2 : GstElementFactoryListType type = GST_ELEMENT_FACTORY_TYPE_PARSER;
571 2 : if (mediaSourceType == MediaSourceType::AUDIO)
572 : {
573 0 : type |= GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
574 : }
575 2 : else if (mediaSourceType == MediaSourceType::VIDEO)
576 : {
577 2 : type |= GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO;
578 : }
579 :
580 2 : if (m_gstWrapper->gstElementFactoryListIsType(factory, type))
581 : {
582 2 : m_glibWrapper->gValueUnset(&item);
583 2 : m_gstWrapper->gstIteratorFree(it);
584 2 : return GST_ELEMENT(m_gstWrapper->gstObjectRef(element));
585 : }
586 : }
587 :
588 0 : m_glibWrapper->gValueUnset(&item);
589 0 : break;
590 : }
591 0 : case GST_ITERATOR_RESYNC:
592 0 : m_gstWrapper->gstIteratorResync(it);
593 0 : break;
594 1 : case GST_ITERATOR_ERROR:
595 : case GST_ITERATOR_DONE:
596 1 : done = TRUE;
597 1 : break;
598 : }
599 : }
600 :
601 1 : RIALTO_SERVER_LOG_WARN("Could not find parser");
602 :
603 1 : m_glibWrapper->gValueUnset(&item);
604 1 : m_gstWrapper->gstIteratorFree(it);
605 :
606 1 : return nullptr;
607 : }
608 :
609 : std::optional<firebolt::rialto::wrappers::AudioAttributesPrivate>
610 5 : GstGenericPlayer::createAudioAttributes(const std::unique_ptr<IMediaPipeline::MediaSource> &source) const
611 : {
612 5 : std::optional<firebolt::rialto::wrappers::AudioAttributesPrivate> audioAttributes;
613 5 : const IMediaPipeline::MediaSourceAudio *kSource = dynamic_cast<IMediaPipeline::MediaSourceAudio *>(source.get());
614 5 : if (kSource)
615 : {
616 4 : firebolt::rialto::AudioConfig audioConfig = kSource->getAudioConfig();
617 : audioAttributes =
618 12 : firebolt::rialto::wrappers::AudioAttributesPrivate{"", // param set below.
619 4 : audioConfig.numberOfChannels, audioConfig.sampleRate,
620 : 0, // used only in one of logs in rdk_gstreamer_utils, no
621 : // need to set this param.
622 : 0, // used only in one of logs in rdk_gstreamer_utils, no
623 : // need to set this param.
624 4 : audioConfig.codecSpecificConfig.data(),
625 : static_cast<std::uint32_t>(
626 4 : audioConfig.codecSpecificConfig.size())};
627 4 : if (source->getMimeType() == "audio/mp4" || source->getMimeType() == "audio/aac")
628 : {
629 2 : audioAttributes->m_codecParam = "mp4a";
630 : }
631 2 : else if (source->getMimeType() == "audio/x-eac3")
632 : {
633 1 : audioAttributes->m_codecParam = "ec-3";
634 : }
635 1 : else if (source->getMimeType() == "audio/b-wav" || source->getMimeType() == "audio/x-raw")
636 : {
637 1 : audioAttributes->m_codecParam = "lpcm";
638 : }
639 4 : }
640 : else
641 : {
642 1 : RIALTO_SERVER_LOG_ERROR("Failed to cast source");
643 : }
644 :
645 5 : return audioAttributes;
646 : }
647 :
648 1 : bool GstGenericPlayer::setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutputParam)
649 : {
650 1 : if (!m_workerThread)
651 0 : return false;
652 :
653 2 : m_workerThread->enqueueTask(
654 2 : m_taskFactory->createSetImmediateOutput(m_context, *this, mediaSourceType, immediateOutputParam));
655 1 : return true;
656 : }
657 :
658 5 : bool GstGenericPlayer::getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutputRef)
659 : {
660 5 : bool returnValue{false};
661 5 : GstElement *sink{getSink(mediaSourceType)};
662 5 : if (sink)
663 : {
664 3 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "immediate-output"))
665 : {
666 2 : m_glibWrapper->gObjectGet(sink, "immediate-output", &immediateOutputRef, nullptr);
667 2 : returnValue = true;
668 : }
669 : else
670 : {
671 1 : RIALTO_SERVER_LOG_ERROR("immediate-output not supported in element %s", GST_ELEMENT_NAME(sink));
672 : }
673 3 : m_gstWrapper->gstObjectUnref(sink);
674 : }
675 : else
676 : {
677 2 : RIALTO_SERVER_LOG_ERROR("Failed to set immediate-output property, sink is NULL");
678 : }
679 :
680 5 : return returnValue;
681 : }
682 :
683 5 : bool GstGenericPlayer::getStats(const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames)
684 : {
685 5 : bool returnValue{false};
686 5 : GstElement *sink{getSink(mediaSourceType)};
687 5 : if (sink)
688 : {
689 3 : GstStructure *stats{nullptr};
690 3 : m_glibWrapper->gObjectGet(sink, "stats", &stats, nullptr);
691 3 : if (!stats)
692 : {
693 1 : RIALTO_SERVER_LOG_ERROR("failed to get stats from '%s'", GST_ELEMENT_NAME(sink));
694 : }
695 : else
696 : {
697 : guint64 renderedFramesTmp;
698 : guint64 droppedFramesTmp;
699 3 : if (m_gstWrapper->gstStructureGetUint64(stats, "rendered", &renderedFramesTmp) &&
700 1 : m_gstWrapper->gstStructureGetUint64(stats, "dropped", &droppedFramesTmp))
701 : {
702 1 : renderedFrames = renderedFramesTmp;
703 1 : droppedFrames = droppedFramesTmp;
704 1 : returnValue = true;
705 : }
706 : else
707 : {
708 1 : RIALTO_SERVER_LOG_ERROR("failed to get 'rendered' or 'dropped' from structure (%s)",
709 : GST_ELEMENT_NAME(sink));
710 : }
711 2 : m_gstWrapper->gstStructureFree(stats);
712 : }
713 3 : m_gstWrapper->gstObjectUnref(sink);
714 : }
715 : else
716 : {
717 2 : RIALTO_SERVER_LOG_ERROR("Failed to get stats, sink is NULL");
718 : }
719 :
720 5 : return returnValue;
721 : }
722 :
723 4 : GstBuffer *GstGenericPlayer::createBuffer(const IMediaPipeline::MediaSegment &mediaSegment) const
724 : {
725 4 : GstBuffer *gstBuffer = m_gstWrapper->gstBufferNewAllocate(nullptr, mediaSegment.getDataLength(), nullptr);
726 4 : m_gstWrapper->gstBufferFill(gstBuffer, 0, mediaSegment.getData(), mediaSegment.getDataLength());
727 :
728 4 : if (mediaSegment.isEncrypted())
729 : {
730 3 : GstBuffer *keyId = m_gstWrapper->gstBufferNewAllocate(nullptr, mediaSegment.getKeyId().size(), nullptr);
731 3 : m_gstWrapper->gstBufferFill(keyId, 0, mediaSegment.getKeyId().data(), mediaSegment.getKeyId().size());
732 :
733 3 : GstBuffer *initVector = m_gstWrapper->gstBufferNewAllocate(nullptr, mediaSegment.getInitVector().size(), nullptr);
734 6 : m_gstWrapper->gstBufferFill(initVector, 0, mediaSegment.getInitVector().data(),
735 3 : mediaSegment.getInitVector().size());
736 3 : GstBuffer *subsamples{nullptr};
737 3 : if (!mediaSegment.getSubSamples().empty())
738 : {
739 3 : auto subsamplesRawSize = mediaSegment.getSubSamples().size() * (sizeof(guint16) + sizeof(guint32));
740 3 : guint8 *subsamplesRaw = static_cast<guint8 *>(m_glibWrapper->gMalloc(subsamplesRawSize));
741 : GstByteWriter writer;
742 3 : m_gstWrapper->gstByteWriterInitWithData(&writer, subsamplesRaw, subsamplesRawSize, FALSE);
743 :
744 6 : for (const auto &subSample : mediaSegment.getSubSamples())
745 : {
746 3 : m_gstWrapper->gstByteWriterPutUint16Be(&writer, subSample.numClearBytes);
747 3 : m_gstWrapper->gstByteWriterPutUint32Be(&writer, subSample.numEncryptedBytes);
748 : }
749 3 : subsamples = m_gstWrapper->gstBufferNewWrapped(subsamplesRaw, subsamplesRawSize);
750 : }
751 :
752 3 : uint32_t crypt = 0;
753 3 : uint32_t skip = 0;
754 3 : bool encryptionPatternSet = mediaSegment.getEncryptionPattern(crypt, skip);
755 :
756 3 : GstRialtoProtectionData data = {mediaSegment.getMediaKeySessionId(),
757 3 : static_cast<uint32_t>(mediaSegment.getSubSamples().size()),
758 3 : mediaSegment.getInitWithLast15(),
759 : keyId,
760 : initVector,
761 : subsamples,
762 6 : mediaSegment.getCipherMode(),
763 : crypt,
764 : skip,
765 : encryptionPatternSet,
766 6 : m_context.decryptionService};
767 :
768 3 : if (!m_protectionMetadataWrapper->addProtectionMetadata(gstBuffer, data))
769 : {
770 1 : RIALTO_SERVER_LOG_ERROR("Failed to add protection metadata");
771 1 : if (keyId)
772 : {
773 1 : m_gstWrapper->gstBufferUnref(keyId);
774 : }
775 1 : if (initVector)
776 : {
777 1 : m_gstWrapper->gstBufferUnref(initVector);
778 : }
779 1 : if (subsamples)
780 : {
781 1 : m_gstWrapper->gstBufferUnref(subsamples);
782 : }
783 : }
784 : }
785 :
786 4 : GST_BUFFER_TIMESTAMP(gstBuffer) = mediaSegment.getTimeStamp();
787 4 : GST_BUFFER_DURATION(gstBuffer) = mediaSegment.getDuration();
788 4 : return gstBuffer;
789 : }
790 :
791 4 : void GstGenericPlayer::notifyNeedMediaData(const MediaSourceType mediaSource)
792 : {
793 4 : auto elem = m_context.streamInfo.find(mediaSource);
794 4 : if (elem != m_context.streamInfo.end())
795 : {
796 2 : StreamInfo &streamInfo = elem->second;
797 2 : streamInfo.isNeedDataPending = false;
798 :
799 : // Send new NeedMediaData if we still need it
800 2 : if (m_gstPlayerClient && streamInfo.isDataNeeded)
801 : {
802 2 : streamInfo.isNeedDataPending = m_gstPlayerClient->notifyNeedMediaData(mediaSource);
803 : }
804 : }
805 : else
806 : {
807 2 : RIALTO_SERVER_LOG_WARN("Media type %s could not be found", common::convertMediaSourceType(mediaSource));
808 : }
809 4 : }
810 :
811 19 : void GstGenericPlayer::attachData(const firebolt::rialto::MediaSourceType mediaType)
812 : {
813 19 : auto elem = m_context.streamInfo.find(mediaType);
814 19 : if (elem != m_context.streamInfo.end())
815 : {
816 16 : StreamInfo &streamInfo = elem->second;
817 16 : if (streamInfo.buffers.empty() || !streamInfo.isDataNeeded)
818 : {
819 2 : return;
820 : }
821 :
822 14 : if (firebolt::rialto::MediaSourceType::SUBTITLE == mediaType)
823 : {
824 2 : setTextTrackPositionIfRequired(streamInfo.appSrc);
825 : }
826 : else
827 : {
828 36 : pushSampleIfRequired(streamInfo.appSrc, common::convertMediaSourceType(mediaType));
829 : }
830 14 : if (mediaType == firebolt::rialto::MediaSourceType::AUDIO)
831 : {
832 : // This needs to be done before gstAppSrcPushBuffer() is
833 : // called because it can free the memory
834 7 : m_context.lastAudioSampleTimestamps = static_cast<int64_t>(GST_BUFFER_PTS(streamInfo.buffers.back()));
835 : }
836 :
837 28 : for (GstBuffer *buffer : streamInfo.buffers)
838 : {
839 14 : m_gstWrapper->gstAppSrcPushBuffer(GST_APP_SRC(streamInfo.appSrc), buffer);
840 : }
841 14 : streamInfo.buffers.clear();
842 14 : streamInfo.isDataPushed = true;
843 :
844 14 : const bool kIsSingle = m_context.streamInfo.size() == 1;
845 14 : bool allOtherStreamsPushed = std::all_of(m_context.streamInfo.begin(), m_context.streamInfo.end(),
846 15 : [](const auto &entry) { return entry.second.isDataPushed; });
847 :
848 14 : if (!m_context.bufferedNotificationSent && (allOtherStreamsPushed || kIsSingle) && m_gstPlayerClient)
849 : {
850 1 : m_context.bufferedNotificationSent = true;
851 1 : m_gstPlayerClient->notifyNetworkState(NetworkState::BUFFERED);
852 1 : RIALTO_SERVER_LOG_MIL("Buffered NetworkState reached");
853 : }
854 14 : cancelUnderflow(mediaType);
855 :
856 14 : const auto eosInfoIt = m_context.endOfStreamInfo.find(mediaType);
857 14 : if (eosInfoIt != m_context.endOfStreamInfo.end() && eosInfoIt->second == EosState::PENDING)
858 : {
859 0 : setEos(mediaType);
860 : }
861 : }
862 : }
863 :
864 7 : void GstGenericPlayer::updateAudioCaps(int32_t rate, int32_t channels, const std::shared_ptr<CodecData> &codecData)
865 : {
866 7 : auto elem = m_context.streamInfo.find(firebolt::rialto::MediaSourceType::AUDIO);
867 7 : if (elem != m_context.streamInfo.end())
868 : {
869 6 : StreamInfo &streamInfo = elem->second;
870 :
871 6 : constexpr int kInvalidRate{0}, kInvalidChannels{0};
872 6 : GstCaps *currentCaps = m_gstWrapper->gstAppSrcGetCaps(GST_APP_SRC(streamInfo.appSrc));
873 6 : GstCaps *newCaps = m_gstWrapper->gstCapsCopy(currentCaps);
874 :
875 6 : if (rate != kInvalidRate)
876 : {
877 3 : m_gstWrapper->gstCapsSetSimple(newCaps, "rate", G_TYPE_INT, rate, NULL);
878 : }
879 :
880 6 : if (channels != kInvalidChannels)
881 : {
882 3 : m_gstWrapper->gstCapsSetSimple(newCaps, "channels", G_TYPE_INT, channels, NULL);
883 : }
884 :
885 6 : setCodecData(newCaps, codecData);
886 :
887 6 : if (!m_gstWrapper->gstCapsIsEqual(currentCaps, newCaps))
888 : {
889 5 : m_gstWrapper->gstAppSrcSetCaps(GST_APP_SRC(streamInfo.appSrc), newCaps);
890 : }
891 :
892 6 : m_gstWrapper->gstCapsUnref(newCaps);
893 6 : m_gstWrapper->gstCapsUnref(currentCaps);
894 : }
895 7 : }
896 :
897 8 : void GstGenericPlayer::updateVideoCaps(int32_t width, int32_t height, Fraction frameRate,
898 : const std::shared_ptr<CodecData> &codecData)
899 : {
900 8 : auto elem = m_context.streamInfo.find(firebolt::rialto::MediaSourceType::VIDEO);
901 8 : if (elem != m_context.streamInfo.end())
902 : {
903 7 : StreamInfo &streamInfo = elem->second;
904 :
905 7 : GstCaps *currentCaps = m_gstWrapper->gstAppSrcGetCaps(GST_APP_SRC(streamInfo.appSrc));
906 7 : GstCaps *newCaps = m_gstWrapper->gstCapsCopy(currentCaps);
907 :
908 7 : if (width > 0)
909 : {
910 6 : m_gstWrapper->gstCapsSetSimple(newCaps, "width", G_TYPE_INT, width, NULL);
911 : }
912 :
913 7 : if (height > 0)
914 : {
915 6 : m_gstWrapper->gstCapsSetSimple(newCaps, "height", G_TYPE_INT, height, NULL);
916 : }
917 :
918 7 : if ((kUndefinedSize != frameRate.numerator) && (kUndefinedSize != frameRate.denominator))
919 : {
920 6 : m_gstWrapper->gstCapsSetSimple(newCaps, "framerate", GST_TYPE_FRACTION, frameRate.numerator,
921 : frameRate.denominator, NULL);
922 : }
923 :
924 7 : setCodecData(newCaps, codecData);
925 :
926 7 : if (!m_gstWrapper->gstCapsIsEqual(currentCaps, newCaps))
927 : {
928 6 : m_gstWrapper->gstAppSrcSetCaps(GST_APP_SRC(streamInfo.appSrc), newCaps);
929 : }
930 :
931 7 : m_gstWrapper->gstCapsUnref(currentCaps);
932 7 : m_gstWrapper->gstCapsUnref(newCaps);
933 : }
934 8 : }
935 :
936 5 : void GstGenericPlayer::addAudioClippingToBuffer(GstBuffer *buffer, uint64_t clippingStart, uint64_t clippingEnd) const
937 : {
938 5 : if (clippingStart || clippingEnd)
939 : {
940 4 : if (m_gstWrapper->gstBufferAddAudioClippingMeta(buffer, GST_FORMAT_TIME, clippingStart, clippingEnd))
941 : {
942 3 : RIALTO_SERVER_LOG_DEBUG("Added audio clipping to buffer %p, start: %" PRIu64 ", end %" PRIu64, buffer,
943 : clippingStart, clippingEnd);
944 : }
945 : else
946 : {
947 1 : RIALTO_SERVER_LOG_WARN("Failed to add audio clipping to buffer %p, start: %" PRIu64 ", end %" PRIu64,
948 : buffer, clippingStart, clippingEnd);
949 : }
950 : }
951 5 : }
952 :
953 13 : bool GstGenericPlayer::setCodecData(GstCaps *caps, const std::shared_ptr<CodecData> &codecData) const
954 : {
955 13 : if (codecData && CodecDataType::BUFFER == codecData->type)
956 : {
957 7 : gpointer memory = m_glibWrapper->gMemdup(codecData->data.data(), codecData->data.size());
958 7 : GstBuffer *buf = m_gstWrapper->gstBufferNewWrapped(memory, codecData->data.size());
959 7 : m_gstWrapper->gstCapsSetSimple(caps, "codec_data", GST_TYPE_BUFFER, buf, nullptr);
960 7 : m_gstWrapper->gstBufferUnref(buf);
961 7 : return true;
962 : }
963 6 : if (codecData && CodecDataType::STRING == codecData->type)
964 : {
965 2 : std::string codecDataStr(codecData->data.begin(), codecData->data.end());
966 2 : m_gstWrapper->gstCapsSetSimple(caps, "codec_data", G_TYPE_STRING, codecDataStr.c_str(), nullptr);
967 2 : return true;
968 : }
969 4 : return false;
970 : }
971 :
972 12 : void GstGenericPlayer::pushSampleIfRequired(GstElement *source, const std::string &typeStr)
973 : {
974 12 : auto initialPosition = m_context.initialPositions.find(source);
975 12 : if (m_context.initialPositions.end() == initialPosition)
976 : {
977 : // Sending initial sample not needed
978 7 : return;
979 : }
980 : // GstAppSrc does not replace segment, if it's the same as previous one.
981 : // It causes problems with position reporing in amlogic devices, so we need to push
982 : // two segments with different reset time value.
983 5 : pushAdditionalSegmentIfRequired(source);
984 :
985 10 : for (const auto &[position, resetTime, appliedRate, stopPosition] : initialPosition->second)
986 : {
987 6 : GstSeekFlags seekFlag = resetTime ? GST_SEEK_FLAG_FLUSH : GST_SEEK_FLAG_NONE;
988 6 : RIALTO_SERVER_LOG_DEBUG("Pushing new %s sample...", typeStr.c_str());
989 6 : GstSegment *segment{m_gstWrapper->gstSegmentNew()};
990 6 : m_gstWrapper->gstSegmentInit(segment, GST_FORMAT_TIME);
991 6 : if (!m_gstWrapper->gstSegmentDoSeek(segment, m_context.playbackRate, GST_FORMAT_TIME, seekFlag,
992 : GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, stopPosition, nullptr))
993 : {
994 1 : RIALTO_SERVER_LOG_WARN("Segment seek failed.");
995 1 : m_gstWrapper->gstSegmentFree(segment);
996 1 : m_context.initialPositions.erase(initialPosition);
997 1 : return;
998 : }
999 5 : segment->applied_rate = appliedRate;
1000 5 : RIALTO_SERVER_LOG_MIL("New %s segment: [%" GST_TIME_FORMAT ", %" GST_TIME_FORMAT
1001 : "], rate: %f, appliedRate %f, reset_time: %d\n",
1002 : typeStr.c_str(), GST_TIME_ARGS(segment->start), GST_TIME_ARGS(segment->stop),
1003 : segment->rate, segment->applied_rate, resetTime);
1004 :
1005 5 : GstCaps *currentCaps = m_gstWrapper->gstAppSrcGetCaps(GST_APP_SRC(source));
1006 : // We can't pass buffer in GstSample, because implementation of gst_app_src_push_sample
1007 : // uses gst_buffer_copy, which loses RialtoProtectionMeta (that causes problems with EME
1008 : // for first frame).
1009 5 : GstSample *sample = m_gstWrapper->gstSampleNew(nullptr, currentCaps, segment, nullptr);
1010 5 : m_gstWrapper->gstAppSrcPushSample(GST_APP_SRC(source), sample);
1011 5 : m_gstWrapper->gstSampleUnref(sample);
1012 5 : m_gstWrapper->gstCapsUnref(currentCaps);
1013 :
1014 5 : m_gstWrapper->gstSegmentFree(segment);
1015 : }
1016 4 : m_context.currentPosition[source] = initialPosition->second.back();
1017 4 : m_context.initialPositions.erase(initialPosition);
1018 4 : return;
1019 : }
1020 :
1021 5 : void GstGenericPlayer::pushAdditionalSegmentIfRequired(GstElement *source)
1022 : {
1023 5 : auto currentPosition = m_context.currentPosition.find(source);
1024 5 : if (m_context.currentPosition.end() == currentPosition)
1025 : {
1026 4 : return;
1027 : }
1028 1 : auto initialPosition = m_context.initialPositions.find(source);
1029 1 : if (m_context.initialPositions.end() == initialPosition)
1030 : {
1031 0 : return;
1032 : }
1033 2 : if (initialPosition->second.size() == 1 && initialPosition->second.back().resetTime &&
1034 1 : currentPosition->second == initialPosition->second.back())
1035 : {
1036 1 : RIALTO_SERVER_LOG_INFO("Adding additional segment with reset_time = false");
1037 1 : SegmentData additionalSegment = initialPosition->second.back();
1038 1 : additionalSegment.resetTime = false;
1039 1 : initialPosition->second.push_back(additionalSegment);
1040 : }
1041 : }
1042 :
1043 2 : void GstGenericPlayer::setTextTrackPositionIfRequired(GstElement *source)
1044 : {
1045 2 : auto initialPosition = m_context.initialPositions.find(source);
1046 2 : if (m_context.initialPositions.end() == initialPosition)
1047 : {
1048 : // Sending initial sample not needed
1049 1 : return;
1050 : }
1051 :
1052 1 : RIALTO_SERVER_LOG_MIL("New subtitle position set %" GST_TIME_FORMAT,
1053 : GST_TIME_ARGS(initialPosition->second.back().position));
1054 1 : m_glibWrapper->gObjectSet(m_context.subtitleSink, "position",
1055 1 : static_cast<guint64>(initialPosition->second.back().position), nullptr);
1056 :
1057 1 : m_context.initialPositions.erase(initialPosition);
1058 : }
1059 :
1060 7 : bool GstGenericPlayer::reattachSource(const std::unique_ptr<IMediaPipeline::MediaSource> &source)
1061 : {
1062 7 : if (m_context.streamInfo.find(source->getType()) == m_context.streamInfo.end())
1063 : {
1064 1 : RIALTO_SERVER_LOG_ERROR("Unable to switch source, type does not exist");
1065 1 : return false;
1066 : }
1067 6 : if (source->getMimeType().empty())
1068 : {
1069 1 : RIALTO_SERVER_LOG_WARN("Skip switch audio source. Unknown mime type");
1070 1 : return false;
1071 : }
1072 5 : std::optional<firebolt::rialto::wrappers::AudioAttributesPrivate> audioAttributes{createAudioAttributes(source)};
1073 5 : if (!audioAttributes)
1074 : {
1075 1 : RIALTO_SERVER_LOG_ERROR("Failed to create audio attributes");
1076 1 : return false;
1077 : }
1078 :
1079 4 : long long currentDispPts = getPosition(m_context.pipeline); // NOLINT(runtime/int)
1080 4 : GstCaps *caps{createCapsFromMediaSource(m_gstWrapper, m_glibWrapper, source)};
1081 4 : GstAppSrc *appSrc{GST_APP_SRC(m_context.streamInfo[source->getType()].appSrc)};
1082 4 : GstCaps *oldCaps = m_gstWrapper->gstAppSrcGetCaps(appSrc);
1083 4 : if ((!oldCaps) || (!m_gstWrapper->gstCapsIsEqual(caps, oldCaps)))
1084 : {
1085 3 : RIALTO_SERVER_LOG_DEBUG("Caps not equal. Perform audio track codec channel switch.");
1086 3 : int sampleAttributes{
1087 : 0}; // rdk_gstreamer_utils::performAudioTrackCodecChannelSwitch checks if this param != NULL only.
1088 3 : std::uint32_t status{0}; // must be 0 to make rdk_gstreamer_utils::performAudioTrackCodecChannelSwitch work
1089 3 : unsigned int ui32Delay{0}; // output param
1090 3 : long long audioChangeTargetPts{-1}; // NOLINT(runtime/int) output param. Set audioChangeTargetPts =
1091 : // currentDispPts in rdk_gstreamer_utils function stub
1092 3 : unsigned int audioChangeStage{0}; // Output param. Set to AUDCHG_ALIGN in rdk_gstreamer_utils function stub
1093 3 : gchar *oldCapsCStr = m_gstWrapper->gstCapsToString(oldCaps);
1094 3 : std::string oldCapsStr = std::string(oldCapsCStr);
1095 3 : m_glibWrapper->gFree(oldCapsCStr);
1096 3 : bool audioAac{oldCapsStr.find("audio/mpeg") != std::string::npos};
1097 3 : bool svpEnabled{true}; // assume always true
1098 3 : bool retVal{false}; // Output param. Set to TRUE in rdk_gstreamer_utils function stub
1099 : bool result =
1100 3 : m_rdkGstreamerUtilsWrapper
1101 6 : ->performAudioTrackCodecChannelSwitch(&m_context.playbackGroup, &sampleAttributes, &(*audioAttributes),
1102 : &status, &ui32Delay, &audioChangeTargetPts, ¤tDispPts,
1103 : &audioChangeStage,
1104 : &caps, // may fail for amlogic - that implementation changes
1105 : // this parameter, it's probably used by Netflix later
1106 3 : &audioAac, svpEnabled, GST_ELEMENT(appSrc), &retVal);
1107 :
1108 3 : if (!result || !retVal)
1109 : {
1110 3 : RIALTO_SERVER_LOG_WARN("performAudioTrackCodecChannelSwitch failed! Result: %d, retval %d", result, retVal);
1111 : }
1112 : }
1113 : else
1114 : {
1115 1 : RIALTO_SERVER_LOG_DEBUG("Skip switching audio source - caps are the same.");
1116 : }
1117 :
1118 4 : m_context.lastAudioSampleTimestamps = currentDispPts;
1119 4 : if (caps)
1120 4 : m_gstWrapper->gstCapsUnref(caps);
1121 4 : if (oldCaps)
1122 4 : m_gstWrapper->gstCapsUnref(oldCaps);
1123 :
1124 4 : return true;
1125 5 : }
1126 :
1127 0 : bool GstGenericPlayer::hasSourceType(const MediaSourceType &mediaSourceType) const
1128 : {
1129 0 : return m_context.streamInfo.find(mediaSourceType) != m_context.streamInfo.end();
1130 : }
1131 :
1132 88 : void GstGenericPlayer::scheduleNeedMediaData(GstAppSrc *src)
1133 : {
1134 88 : if (m_workerThread)
1135 : {
1136 88 : m_workerThread->enqueueTask(m_taskFactory->createNeedData(m_context, *this, src));
1137 : }
1138 : }
1139 :
1140 1 : void GstGenericPlayer::scheduleEnoughData(GstAppSrc *src)
1141 : {
1142 1 : if (m_workerThread)
1143 : {
1144 1 : m_workerThread->enqueueTask(m_taskFactory->createEnoughData(m_context, src));
1145 : }
1146 : }
1147 :
1148 3 : void GstGenericPlayer::scheduleAudioUnderflow()
1149 : {
1150 3 : if (m_workerThread)
1151 : {
1152 3 : bool underflowEnabled = m_context.isPlaying && !m_context.audioSourceRemoved;
1153 6 : m_workerThread->enqueueTask(
1154 6 : m_taskFactory->createUnderflow(m_context, *this, underflowEnabled, MediaSourceType::AUDIO));
1155 : }
1156 3 : }
1157 :
1158 2 : void GstGenericPlayer::scheduleVideoUnderflow()
1159 : {
1160 2 : if (m_workerThread)
1161 : {
1162 2 : bool underflowEnabled = m_context.isPlaying;
1163 4 : m_workerThread->enqueueTask(
1164 4 : m_taskFactory->createUnderflow(m_context, *this, underflowEnabled, MediaSourceType::VIDEO));
1165 : }
1166 2 : }
1167 :
1168 1 : void GstGenericPlayer::scheduleAllSourcesAttached()
1169 : {
1170 1 : allSourcesAttached();
1171 : }
1172 :
1173 14 : void GstGenericPlayer::cancelUnderflow(firebolt::rialto::MediaSourceType mediaSource)
1174 : {
1175 14 : auto elem = m_context.streamInfo.find(mediaSource);
1176 14 : if (elem != m_context.streamInfo.end())
1177 : {
1178 14 : StreamInfo &streamInfo = elem->second;
1179 14 : if (!streamInfo.underflowOccured)
1180 : {
1181 11 : return;
1182 : }
1183 :
1184 3 : RIALTO_SERVER_LOG_DEBUG("Cancelling %s underflow", common::convertMediaSourceType(mediaSource));
1185 3 : streamInfo.underflowOccured = false;
1186 : }
1187 : }
1188 :
1189 3 : void GstGenericPlayer::play(bool &async)
1190 : {
1191 3 : if (0 == m_ongoingStateChangesNumber)
1192 : {
1193 : // Operation called on main thread, because PAUSED->PLAYING change is synchronous and needs to be done fast.
1194 : //
1195 : // m_context.pipeline can be used, because it's modified only in GstGenericPlayer
1196 : // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here.
1197 2 : ++m_ongoingStateChangesNumber;
1198 2 : async = (changePipelineState(GST_STATE_PLAYING) == GST_STATE_CHANGE_ASYNC);
1199 2 : RIALTO_SERVER_LOG_MIL("State change to PLAYING requested");
1200 : }
1201 : else
1202 : {
1203 1 : ++m_ongoingStateChangesNumber;
1204 1 : async = true;
1205 1 : if (m_workerThread)
1206 : {
1207 1 : m_workerThread->enqueueTask(m_taskFactory->createPlay(*this));
1208 : }
1209 : }
1210 3 : }
1211 :
1212 2 : void GstGenericPlayer::pause()
1213 : {
1214 2 : ++m_ongoingStateChangesNumber;
1215 2 : if (m_workerThread)
1216 : {
1217 2 : m_workerThread->enqueueTask(m_taskFactory->createPause(m_context, *this));
1218 : }
1219 : }
1220 :
1221 1 : void GstGenericPlayer::stop()
1222 : {
1223 1 : ++m_ongoingStateChangesNumber;
1224 1 : if (m_workerThread)
1225 : {
1226 1 : m_workerThread->enqueueTask(m_taskFactory->createStop(m_context, *this));
1227 : }
1228 : }
1229 :
1230 6 : GstStateChangeReturn GstGenericPlayer::changePipelineState(GstState newState)
1231 : {
1232 6 : if (!m_context.pipeline)
1233 : {
1234 1 : RIALTO_SERVER_LOG_ERROR("Change state failed - pipeline is nullptr");
1235 1 : if (m_gstPlayerClient)
1236 1 : m_gstPlayerClient->notifyPlaybackState(PlaybackState::FAILURE);
1237 1 : --m_ongoingStateChangesNumber;
1238 1 : return GST_STATE_CHANGE_FAILURE;
1239 : }
1240 5 : const GstStateChangeReturn result{m_gstWrapper->gstElementSetState(m_context.pipeline, newState)};
1241 5 : if (result == GST_STATE_CHANGE_FAILURE)
1242 : {
1243 1 : RIALTO_SERVER_LOG_ERROR("Change state failed - Gstreamer returned an error");
1244 1 : if (m_gstPlayerClient)
1245 1 : m_gstPlayerClient->notifyPlaybackState(PlaybackState::FAILURE);
1246 : }
1247 5 : --m_ongoingStateChangesNumber;
1248 5 : return result;
1249 : }
1250 :
1251 15 : int64_t GstGenericPlayer::getPosition(GstElement *element)
1252 : {
1253 15 : if (!element)
1254 : {
1255 1 : RIALTO_SERVER_LOG_WARN("Element is null");
1256 1 : return -1;
1257 : }
1258 :
1259 14 : m_gstWrapper->gstStateLock(element);
1260 :
1261 28 : if (m_gstWrapper->gstElementGetState(element) < GST_STATE_PAUSED ||
1262 14 : (m_gstWrapper->gstElementGetStateReturn(element) == GST_STATE_CHANGE_ASYNC &&
1263 1 : m_gstWrapper->gstElementGetStateNext(element) == GST_STATE_PAUSED))
1264 : {
1265 1 : RIALTO_SERVER_LOG_WARN("Element is prerolling or in invalid state - state: %s, return: %s, next: %s",
1266 : m_gstWrapper->gstElementStateGetName(m_gstWrapper->gstElementGetState(element)),
1267 : m_gstWrapper->gstElementStateChangeReturnGetName(
1268 : m_gstWrapper->gstElementGetStateReturn(element)),
1269 : m_gstWrapper->gstElementStateGetName(m_gstWrapper->gstElementGetStateNext(element)));
1270 :
1271 1 : m_gstWrapper->gstStateUnlock(element);
1272 1 : return -1;
1273 : }
1274 13 : m_gstWrapper->gstStateUnlock(element);
1275 :
1276 13 : gint64 position = -1;
1277 13 : if (!m_gstWrapper->gstElementQueryPosition(m_context.pipeline, GST_FORMAT_TIME, &position))
1278 : {
1279 1 : RIALTO_SERVER_LOG_WARN("Failed to query position");
1280 1 : return -1;
1281 : }
1282 :
1283 12 : return position;
1284 : }
1285 :
1286 1 : void GstGenericPlayer::setVideoGeometry(int x, int y, int width, int height)
1287 : {
1288 1 : if (m_workerThread)
1289 : {
1290 2 : m_workerThread->enqueueTask(
1291 2 : m_taskFactory->createSetVideoGeometry(m_context, *this, Rectangle{x, y, width, height}));
1292 : }
1293 1 : }
1294 :
1295 1 : void GstGenericPlayer::setEos(const firebolt::rialto::MediaSourceType &type)
1296 : {
1297 1 : if (m_workerThread)
1298 : {
1299 1 : m_workerThread->enqueueTask(m_taskFactory->createEos(m_context, *this, type));
1300 : }
1301 : }
1302 :
1303 4 : bool GstGenericPlayer::setVideoSinkRectangle()
1304 : {
1305 4 : bool result = false;
1306 4 : GstElement *videoSink{getSink(MediaSourceType::VIDEO)};
1307 4 : if (videoSink)
1308 : {
1309 3 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(videoSink), "rectangle"))
1310 : {
1311 : std::string rect =
1312 4 : std::to_string(m_context.pendingGeometry.x) + ',' + std::to_string(m_context.pendingGeometry.y) + ',' +
1313 6 : std::to_string(m_context.pendingGeometry.width) + ',' + std::to_string(m_context.pendingGeometry.height);
1314 2 : m_glibWrapper->gObjectSet(videoSink, "rectangle", rect.c_str(), nullptr);
1315 2 : m_context.pendingGeometry.clear();
1316 2 : result = true;
1317 : }
1318 : else
1319 : {
1320 1 : RIALTO_SERVER_LOG_ERROR("Failed to set the video rectangle");
1321 : }
1322 3 : m_gstWrapper->gstObjectUnref(videoSink);
1323 : }
1324 : else
1325 : {
1326 1 : RIALTO_SERVER_LOG_ERROR("Failed to set video rectangle, sink is NULL");
1327 : }
1328 :
1329 4 : return result;
1330 : }
1331 :
1332 3 : bool GstGenericPlayer::setImmediateOutput()
1333 : {
1334 3 : bool result{false};
1335 3 : if (m_context.pendingImmediateOutputForVideo.has_value())
1336 : {
1337 3 : GstElement *sink{getSink(MediaSourceType::VIDEO)};
1338 3 : if (sink)
1339 : {
1340 2 : bool immediateOutput{m_context.pendingImmediateOutputForVideo.value()};
1341 2 : RIALTO_SERVER_LOG_DEBUG("Set immediate-output to %s", immediateOutput ? "TRUE" : "FALSE");
1342 :
1343 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "immediate-output"))
1344 : {
1345 1 : gboolean immediateOutputGboolean{immediateOutput ? TRUE : FALSE};
1346 1 : m_glibWrapper->gObjectSet(sink, "immediate-output", immediateOutputGboolean, nullptr);
1347 1 : result = true;
1348 : }
1349 : else
1350 : {
1351 1 : RIALTO_SERVER_LOG_ERROR("Failed to set immediate-output property on sink '%s'", GST_ELEMENT_NAME(sink));
1352 : }
1353 2 : m_context.pendingImmediateOutputForVideo.reset();
1354 2 : m_gstWrapper->gstObjectUnref(sink);
1355 : }
1356 : else
1357 : {
1358 1 : RIALTO_SERVER_LOG_DEBUG("Pending an immediate-output, sink is NULL");
1359 : }
1360 : }
1361 3 : return result;
1362 : }
1363 :
1364 4 : bool GstGenericPlayer::setShowVideoWindow()
1365 : {
1366 4 : if (!m_context.pendingShowVideoWindow.has_value())
1367 : {
1368 1 : RIALTO_SERVER_LOG_WARN("No show video window value to be set. Aborting...");
1369 1 : return false;
1370 : }
1371 :
1372 3 : GstElement *videoSink{getSink(MediaSourceType::VIDEO)};
1373 3 : if (!videoSink)
1374 : {
1375 1 : RIALTO_SERVER_LOG_DEBUG("Setting show video window queued. Video sink is NULL");
1376 1 : return false;
1377 : }
1378 2 : bool result{false};
1379 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(videoSink), "show-video-window"))
1380 : {
1381 1 : m_glibWrapper->gObjectSet(videoSink, "show-video-window", m_context.pendingShowVideoWindow.value(), nullptr);
1382 1 : result = true;
1383 : }
1384 : else
1385 : {
1386 1 : RIALTO_SERVER_LOG_ERROR("Setting show video window failed. Property does not exist");
1387 : }
1388 2 : m_context.pendingShowVideoWindow.reset();
1389 2 : m_gstWrapper->gstObjectUnref(GST_OBJECT(videoSink));
1390 2 : return result;
1391 : }
1392 :
1393 4 : bool GstGenericPlayer::setLowLatency()
1394 : {
1395 4 : bool result{false};
1396 4 : if (m_context.pendingLowLatency.has_value())
1397 : {
1398 4 : GstElement *sink{getSink(MediaSourceType::AUDIO)};
1399 4 : if (sink)
1400 : {
1401 3 : bool lowLatency{m_context.pendingLowLatency.value()};
1402 3 : RIALTO_SERVER_LOG_DEBUG("Set low-latency to %s", lowLatency ? "TRUE" : "FALSE");
1403 :
1404 3 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "low-latency"))
1405 : {
1406 2 : gboolean lowLatencyGboolean{lowLatency ? TRUE : FALSE};
1407 2 : m_glibWrapper->gObjectSet(sink, "low-latency", lowLatencyGboolean, nullptr);
1408 2 : result = true;
1409 : }
1410 : else
1411 : {
1412 1 : RIALTO_SERVER_LOG_ERROR("Failed to set low-latency property on sink '%s'", GST_ELEMENT_NAME(sink));
1413 : }
1414 3 : m_context.pendingLowLatency.reset();
1415 3 : m_gstWrapper->gstObjectUnref(sink);
1416 : }
1417 : else
1418 : {
1419 1 : RIALTO_SERVER_LOG_DEBUG("Pending low-latency, sink is NULL");
1420 : }
1421 : }
1422 4 : return result;
1423 : }
1424 :
1425 3 : bool GstGenericPlayer::setSync()
1426 : {
1427 3 : bool result{false};
1428 3 : if (m_context.pendingSync.has_value())
1429 : {
1430 3 : GstElement *sink{getSink(MediaSourceType::AUDIO)};
1431 3 : if (sink)
1432 : {
1433 2 : bool sync{m_context.pendingSync.value()};
1434 2 : RIALTO_SERVER_LOG_DEBUG("Set sync to %s", sync ? "TRUE" : "FALSE");
1435 :
1436 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "sync"))
1437 : {
1438 1 : gboolean syncGboolean{sync ? TRUE : FALSE};
1439 1 : m_glibWrapper->gObjectSet(sink, "sync", syncGboolean, nullptr);
1440 1 : result = true;
1441 : }
1442 : else
1443 : {
1444 1 : RIALTO_SERVER_LOG_ERROR("Failed to set sync property on sink '%s'", GST_ELEMENT_NAME(sink));
1445 : }
1446 2 : m_context.pendingSync.reset();
1447 2 : m_gstWrapper->gstObjectUnref(sink);
1448 : }
1449 : else
1450 : {
1451 1 : RIALTO_SERVER_LOG_DEBUG("Pending sync, sink is NULL");
1452 : }
1453 : }
1454 3 : return result;
1455 : }
1456 :
1457 3 : bool GstGenericPlayer::setSyncOff()
1458 : {
1459 3 : bool result{false};
1460 3 : if (m_context.pendingSyncOff.has_value())
1461 : {
1462 3 : GstElement *decoder = getDecoder(MediaSourceType::AUDIO);
1463 3 : if (decoder)
1464 : {
1465 2 : bool syncOff{m_context.pendingSyncOff.value()};
1466 2 : RIALTO_SERVER_LOG_DEBUG("Set sync-off to %s", syncOff ? "TRUE" : "FALSE");
1467 :
1468 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "sync-off"))
1469 : {
1470 1 : gboolean syncOffGboolean{decoder ? TRUE : FALSE};
1471 1 : m_glibWrapper->gObjectSet(decoder, "sync-off", syncOffGboolean, nullptr);
1472 1 : result = true;
1473 : }
1474 : else
1475 : {
1476 1 : RIALTO_SERVER_LOG_ERROR("Failed to set sync-off property on decoder '%s'", GST_ELEMENT_NAME(decoder));
1477 : }
1478 2 : m_context.pendingSyncOff.reset();
1479 2 : m_gstWrapper->gstObjectUnref(decoder);
1480 : }
1481 : else
1482 : {
1483 1 : RIALTO_SERVER_LOG_DEBUG("Pending sync-off, decoder is NULL");
1484 : }
1485 : }
1486 3 : return result;
1487 : }
1488 :
1489 6 : bool GstGenericPlayer::setStreamSyncMode(const MediaSourceType &type)
1490 : {
1491 6 : bool result{false};
1492 6 : int32_t streamSyncMode{0};
1493 : {
1494 6 : std::unique_lock lock{m_context.propertyMutex};
1495 6 : if (m_context.pendingStreamSyncMode.find(type) == m_context.pendingStreamSyncMode.end())
1496 : {
1497 0 : return false;
1498 : }
1499 6 : streamSyncMode = m_context.pendingStreamSyncMode[type];
1500 : }
1501 6 : if (MediaSourceType::AUDIO == type)
1502 : {
1503 3 : GstElement *decoder = getDecoder(MediaSourceType::AUDIO);
1504 3 : if (!decoder)
1505 : {
1506 1 : RIALTO_SERVER_LOG_DEBUG("Pending stream-sync-mode, decoder is NULL");
1507 1 : return false;
1508 : }
1509 :
1510 2 : RIALTO_SERVER_LOG_DEBUG("Set stream-sync-mode to %d", streamSyncMode);
1511 :
1512 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "stream-sync-mode"))
1513 : {
1514 1 : gint streamSyncModeGint{static_cast<gint>(streamSyncMode)};
1515 1 : m_glibWrapper->gObjectSet(decoder, "stream-sync-mode", streamSyncModeGint, nullptr);
1516 1 : result = true;
1517 : }
1518 : else
1519 : {
1520 1 : RIALTO_SERVER_LOG_ERROR("Failed to set stream-sync-mode property on decoder '%s'", GST_ELEMENT_NAME(decoder));
1521 : }
1522 2 : m_gstWrapper->gstObjectUnref(decoder);
1523 2 : std::unique_lock lock{m_context.propertyMutex};
1524 2 : m_context.pendingStreamSyncMode.erase(type);
1525 : }
1526 3 : else if (MediaSourceType::VIDEO == type)
1527 : {
1528 3 : GstElement *parser = getParser(MediaSourceType::VIDEO);
1529 3 : if (!parser)
1530 : {
1531 1 : RIALTO_SERVER_LOG_DEBUG("Pending syncmode-streaming, parser is NULL");
1532 1 : return false;
1533 : }
1534 :
1535 2 : gboolean streamSyncModeBoolean{static_cast<gboolean>(streamSyncMode)};
1536 2 : RIALTO_SERVER_LOG_DEBUG("Set syncmode-streaming to %d", streamSyncMode);
1537 :
1538 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(parser), "syncmode-streaming"))
1539 : {
1540 1 : m_glibWrapper->gObjectSet(parser, "syncmode-streaming", streamSyncModeBoolean, nullptr);
1541 1 : result = true;
1542 : }
1543 : else
1544 : {
1545 1 : RIALTO_SERVER_LOG_ERROR("Failed to set syncmode-streaming property on parser '%s'", GST_ELEMENT_NAME(parser));
1546 : }
1547 2 : m_gstWrapper->gstObjectUnref(parser);
1548 2 : std::unique_lock lock{m_context.propertyMutex};
1549 2 : m_context.pendingStreamSyncMode.erase(type);
1550 : }
1551 4 : return result;
1552 : }
1553 :
1554 3 : bool GstGenericPlayer::setRenderFrame()
1555 : {
1556 3 : bool result{false};
1557 3 : if (m_context.pendingRenderFrame)
1558 : {
1559 5 : static const std::string kStepOnPrerollPropertyName = "frame-step-on-preroll";
1560 3 : GstElement *sink{getSink(MediaSourceType::VIDEO)};
1561 3 : if (sink)
1562 : {
1563 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), kStepOnPrerollPropertyName.c_str()))
1564 : {
1565 1 : RIALTO_SERVER_LOG_INFO("Rendering preroll");
1566 :
1567 1 : m_glibWrapper->gObjectSet(sink, kStepOnPrerollPropertyName.c_str(), 1, nullptr);
1568 1 : m_gstWrapper->gstElementSendEvent(sink, m_gstWrapper->gstEventNewStep(GST_FORMAT_BUFFERS, 1, 1.0, true,
1569 : false));
1570 1 : m_glibWrapper->gObjectSet(sink, kStepOnPrerollPropertyName.c_str(), 0, nullptr);
1571 1 : result = true;
1572 : }
1573 : else
1574 : {
1575 1 : RIALTO_SERVER_LOG_ERROR("Video sink doesn't have property `%s`", kStepOnPrerollPropertyName.c_str());
1576 : }
1577 2 : m_gstWrapper->gstObjectUnref(sink);
1578 2 : m_context.pendingRenderFrame = false;
1579 : }
1580 : else
1581 : {
1582 1 : RIALTO_SERVER_LOG_DEBUG("Pending render frame, sink is NULL");
1583 : }
1584 : }
1585 3 : return result;
1586 : }
1587 :
1588 3 : bool GstGenericPlayer::setBufferingLimit()
1589 : {
1590 3 : bool result{false};
1591 3 : guint bufferingLimit{0};
1592 : {
1593 3 : std::unique_lock lock{m_context.propertyMutex};
1594 3 : if (!m_context.pendingBufferingLimit.has_value())
1595 : {
1596 0 : return false;
1597 : }
1598 3 : bufferingLimit = static_cast<guint>(m_context.pendingBufferingLimit.value());
1599 : }
1600 :
1601 3 : GstElement *decoder{getDecoder(MediaSourceType::AUDIO)};
1602 3 : if (decoder)
1603 : {
1604 2 : RIALTO_SERVER_LOG_DEBUG("Set limit-buffering-ms to %u", bufferingLimit);
1605 :
1606 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "limit-buffering-ms"))
1607 : {
1608 1 : m_glibWrapper->gObjectSet(decoder, "limit-buffering-ms", bufferingLimit, nullptr);
1609 1 : result = true;
1610 : }
1611 : else
1612 : {
1613 1 : RIALTO_SERVER_LOG_ERROR("Failed to set limit-buffering-ms property on decoder '%s'",
1614 : GST_ELEMENT_NAME(decoder));
1615 : }
1616 2 : m_gstWrapper->gstObjectUnref(decoder);
1617 2 : std::unique_lock lock{m_context.propertyMutex};
1618 2 : m_context.pendingBufferingLimit.reset();
1619 : }
1620 : else
1621 : {
1622 1 : RIALTO_SERVER_LOG_DEBUG("Pending limit-buffering-ms, decoder is NULL");
1623 : }
1624 3 : return result;
1625 : }
1626 :
1627 2 : bool GstGenericPlayer::setUseBuffering()
1628 : {
1629 2 : std::unique_lock lock{m_context.propertyMutex};
1630 2 : if (m_context.pendingUseBuffering.has_value())
1631 : {
1632 2 : if (m_context.playbackGroup.m_curAudioDecodeBin)
1633 : {
1634 1 : gboolean useBufferingGboolean{m_context.pendingUseBuffering.value() ? TRUE : FALSE};
1635 1 : RIALTO_SERVER_LOG_DEBUG("Set use-buffering to %d", useBufferingGboolean);
1636 1 : m_glibWrapper->gObjectSet(m_context.playbackGroup.m_curAudioDecodeBin, "use-buffering",
1637 : useBufferingGboolean, nullptr);
1638 1 : m_context.pendingUseBuffering.reset();
1639 1 : return true;
1640 : }
1641 : else
1642 : {
1643 1 : RIALTO_SERVER_LOG_DEBUG("Pending use-buffering, decodebin is NULL");
1644 : }
1645 : }
1646 1 : return false;
1647 2 : }
1648 :
1649 8 : bool GstGenericPlayer::setWesterossinkSecondaryVideo()
1650 : {
1651 8 : bool result = false;
1652 8 : GstElementFactory *factory = m_gstWrapper->gstElementFactoryFind("westerossink");
1653 8 : if (factory)
1654 : {
1655 7 : GstElement *videoSink = m_gstWrapper->gstElementFactoryCreate(factory, nullptr);
1656 7 : if (videoSink)
1657 : {
1658 5 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(videoSink), "res-usage"))
1659 : {
1660 4 : m_glibWrapper->gObjectSet(videoSink, "res-usage", 0x0u, nullptr);
1661 4 : m_glibWrapper->gObjectSet(m_context.pipeline, "video-sink", videoSink, nullptr);
1662 4 : result = true;
1663 : }
1664 : else
1665 : {
1666 1 : RIALTO_SERVER_LOG_ERROR("Failed to set the westerossink res-usage");
1667 1 : m_gstWrapper->gstObjectUnref(GST_OBJECT(videoSink));
1668 : }
1669 : }
1670 : else
1671 : {
1672 2 : RIALTO_SERVER_LOG_ERROR("Failed to create the westerossink");
1673 : }
1674 :
1675 7 : m_gstWrapper->gstObjectUnref(GST_OBJECT(factory));
1676 : }
1677 : else
1678 : {
1679 : // No westeros sink
1680 1 : result = true;
1681 : }
1682 :
1683 8 : return result;
1684 : }
1685 :
1686 8 : bool GstGenericPlayer::setErmContext()
1687 : {
1688 8 : bool result = false;
1689 8 : GstContext *context = m_gstWrapper->gstContextNew("erm", false);
1690 8 : if (context)
1691 : {
1692 6 : GstStructure *contextStructure = m_gstWrapper->gstContextWritableStructure(context);
1693 6 : if (contextStructure)
1694 : {
1695 5 : m_gstWrapper->gstStructureSet(contextStructure, "res-usage", G_TYPE_UINT, 0x0u, nullptr);
1696 5 : m_gstWrapper->gstElementSetContext(GST_ELEMENT(m_context.pipeline), context);
1697 5 : result = true;
1698 : }
1699 : else
1700 : {
1701 1 : RIALTO_SERVER_LOG_ERROR("Failed to create the erm structure");
1702 : }
1703 6 : m_gstWrapper->gstContextUnref(context);
1704 : }
1705 : else
1706 : {
1707 2 : RIALTO_SERVER_LOG_ERROR("Failed to create the erm context");
1708 : }
1709 :
1710 8 : return result;
1711 : }
1712 :
1713 6 : void GstGenericPlayer::startPositionReportingAndCheckAudioUnderflowTimer()
1714 : {
1715 : static constexpr std::chrono::milliseconds kPlaybackInfoTimerMs{32};
1716 6 : if (m_positionReportingAndCheckAudioUnderflowTimer && m_positionReportingAndCheckAudioUnderflowTimer->isActive())
1717 : {
1718 1 : return;
1719 : }
1720 :
1721 15 : m_positionReportingAndCheckAudioUnderflowTimer = m_timerFactory->createTimer(
1722 : kPositionReportTimerMs,
1723 5 : [this]()
1724 : {
1725 1 : if (m_workerThread)
1726 : {
1727 1 : m_workerThread->enqueueTask(m_taskFactory->createReportPosition(m_context, *this));
1728 1 : m_workerThread->enqueueTask(m_taskFactory->createCheckAudioUnderflow(m_context, *this));
1729 : }
1730 1 : },
1731 5 : firebolt::rialto::common::TimerType::PERIODIC);
1732 :
1733 5 : notifyPlaybackInfo();
1734 :
1735 : m_playbackInfoTimer =
1736 5 : m_timerFactory
1737 6 : ->createTimer(kPlaybackInfoTimerMs, [this]() { notifyPlaybackInfo(); }, firebolt::rialto::common::TimerType::PERIODIC);
1738 : }
1739 :
1740 4 : void GstGenericPlayer::stopPositionReportingAndCheckAudioUnderflowTimer()
1741 : {
1742 4 : if (m_positionReportingAndCheckAudioUnderflowTimer && m_positionReportingAndCheckAudioUnderflowTimer->isActive())
1743 : {
1744 1 : m_positionReportingAndCheckAudioUnderflowTimer->cancel();
1745 1 : m_positionReportingAndCheckAudioUnderflowTimer.reset();
1746 : }
1747 :
1748 4 : if (m_playbackInfoTimer && m_playbackInfoTimer->isActive())
1749 : {
1750 1 : m_playbackInfoTimer->cancel();
1751 1 : m_playbackInfoTimer.reset();
1752 : }
1753 4 : }
1754 :
1755 0 : void GstGenericPlayer::startSubtitleClockResyncTimer()
1756 : {
1757 0 : if (m_subtitleClockResyncTimer && m_subtitleClockResyncTimer->isActive())
1758 : {
1759 0 : return;
1760 : }
1761 :
1762 0 : m_subtitleClockResyncTimer = m_timerFactory->createTimer(
1763 : kSubtitleClockResyncInterval,
1764 0 : [this]()
1765 : {
1766 0 : if (m_workerThread)
1767 : {
1768 0 : m_workerThread->enqueueTask(m_taskFactory->createSynchroniseSubtitleClock(m_context, *this));
1769 : }
1770 0 : },
1771 0 : firebolt::rialto::common::TimerType::PERIODIC);
1772 : }
1773 :
1774 0 : void GstGenericPlayer::stopSubtitleClockResyncTimer()
1775 : {
1776 0 : if (m_subtitleClockResyncTimer && m_subtitleClockResyncTimer->isActive())
1777 : {
1778 0 : m_subtitleClockResyncTimer->cancel();
1779 0 : m_subtitleClockResyncTimer.reset();
1780 : }
1781 : }
1782 :
1783 2 : void GstGenericPlayer::stopWorkerThread()
1784 : {
1785 2 : if (m_workerThread)
1786 : {
1787 2 : m_workerThread->stop();
1788 : }
1789 : }
1790 :
1791 0 : void GstGenericPlayer::setPendingPlaybackRate()
1792 : {
1793 0 : RIALTO_SERVER_LOG_INFO("Setting pending playback rate");
1794 0 : setPlaybackRate(m_context.pendingPlaybackRate);
1795 : }
1796 :
1797 1 : void GstGenericPlayer::renderFrame()
1798 : {
1799 1 : if (m_workerThread)
1800 : {
1801 1 : m_workerThread->enqueueTask(m_taskFactory->createRenderFrame(m_context, *this));
1802 : }
1803 : }
1804 :
1805 18 : void GstGenericPlayer::setVolume(double targetVolume, uint32_t volumeDuration, firebolt::rialto::EaseType easeType)
1806 : {
1807 18 : if (m_workerThread)
1808 : {
1809 36 : m_workerThread->enqueueTask(
1810 36 : m_taskFactory->createSetVolume(m_context, *this, targetVolume, volumeDuration, easeType));
1811 : }
1812 18 : }
1813 :
1814 9 : bool GstGenericPlayer::getVolume(double ¤tVolume)
1815 : {
1816 : // We are on main thread here, but m_context.pipeline can be used, because it's modified only in GstGenericPlayer
1817 : // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here.
1818 9 : if (!m_context.pipeline)
1819 : {
1820 0 : return false;
1821 : }
1822 :
1823 : // NOTE: No gstreamer documentation for "fade-volume" could be found at the time this code was written.
1824 : // Therefore the author performed several tests on a supported platform (Flex2) to determine the behaviour of this property.
1825 : // The code has been written to be backwardly compatible on platforms that don't have this property.
1826 : // The observed behaviour was:
1827 : // - if the returned fade volume is negative then audio-fade is not active. In this case the usual technique
1828 : // to find volume in the pipeline works and is used.
1829 : // - if the returned fade volume is positive then audio-fade is active. In this case the returned fade volume
1830 : // directly returns the current volume level 0=min to 100=max (and the pipeline's current volume level is
1831 : // meaningless and doesn't contribute in this case).
1832 9 : GstElement *sink{getSink(MediaSourceType::AUDIO)};
1833 11 : if (m_context.audioFadeEnabled && sink &&
1834 2 : m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "fade-volume"))
1835 : {
1836 2 : gint fadeVolume{-100};
1837 2 : m_glibWrapper->gObjectGet(sink, "fade-volume", &fadeVolume, NULL);
1838 2 : if (fadeVolume < 0)
1839 : {
1840 1 : currentVolume = m_gstWrapper->gstStreamVolumeGetVolume(GST_STREAM_VOLUME(m_context.pipeline),
1841 : GST_STREAM_VOLUME_FORMAT_LINEAR);
1842 1 : RIALTO_SERVER_LOG_INFO("Fade volume is negative, using volume from pipeline: %f", currentVolume);
1843 : }
1844 : else
1845 : {
1846 1 : currentVolume = static_cast<double>(fadeVolume) / 100.0;
1847 1 : RIALTO_SERVER_LOG_INFO("Fade volume is supported: %f", currentVolume);
1848 : }
1849 : }
1850 : else
1851 : {
1852 7 : currentVolume = m_gstWrapper->gstStreamVolumeGetVolume(GST_STREAM_VOLUME(m_context.pipeline),
1853 : GST_STREAM_VOLUME_FORMAT_LINEAR);
1854 7 : RIALTO_SERVER_LOG_INFO("Fade volume is not supported, using volume from pipeline: %f", currentVolume);
1855 : }
1856 :
1857 9 : if (sink)
1858 2 : m_gstWrapper->gstObjectUnref(sink);
1859 :
1860 9 : return true;
1861 : }
1862 :
1863 1 : void GstGenericPlayer::setMute(const MediaSourceType &mediaSourceType, bool mute)
1864 : {
1865 1 : if (m_workerThread)
1866 : {
1867 1 : m_workerThread->enqueueTask(m_taskFactory->createSetMute(m_context, *this, mediaSourceType, mute));
1868 : }
1869 : }
1870 :
1871 5 : bool GstGenericPlayer::getMute(const MediaSourceType &mediaSourceType, bool &mute)
1872 : {
1873 : // We are on main thread here, but m_context.pipeline can be used, because it's modified only in GstGenericPlayer
1874 : // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here.
1875 5 : if (mediaSourceType == MediaSourceType::SUBTITLE)
1876 : {
1877 2 : if (!m_context.subtitleSink)
1878 : {
1879 1 : RIALTO_SERVER_LOG_ERROR("There is no subtitle sink");
1880 1 : return false;
1881 : }
1882 1 : gboolean muteValue{FALSE};
1883 1 : m_glibWrapper->gObjectGet(m_context.subtitleSink, "mute", &muteValue, nullptr);
1884 1 : mute = muteValue;
1885 : }
1886 3 : else if (mediaSourceType == MediaSourceType::AUDIO)
1887 : {
1888 2 : if (!m_context.pipeline)
1889 : {
1890 1 : return false;
1891 : }
1892 1 : mute = m_gstWrapper->gstStreamVolumeGetMute(GST_STREAM_VOLUME(m_context.pipeline));
1893 : }
1894 : else
1895 : {
1896 1 : RIALTO_SERVER_LOG_ERROR("Getting mute for type %s unsupported", common::convertMediaSourceType(mediaSourceType));
1897 1 : return false;
1898 : }
1899 :
1900 2 : return true;
1901 : }
1902 :
1903 1 : bool GstGenericPlayer::isAsync(const MediaSourceType &mediaSourceType) const
1904 : {
1905 1 : GstElement *sink = getSink(mediaSourceType);
1906 1 : if (!sink)
1907 : {
1908 0 : RIALTO_SERVER_LOG_WARN("Sink not found for %s", common::convertMediaSourceType(mediaSourceType));
1909 0 : return true; // Our sinks are async by default
1910 : }
1911 1 : gboolean returnValue{TRUE};
1912 1 : m_glibWrapper->gObjectGet(sink, "async", &returnValue, nullptr);
1913 1 : m_gstWrapper->gstObjectUnref(sink);
1914 1 : return returnValue == TRUE;
1915 : }
1916 :
1917 1 : void GstGenericPlayer::setTextTrackIdentifier(const std::string &textTrackIdentifier)
1918 : {
1919 1 : if (m_workerThread)
1920 : {
1921 1 : m_workerThread->enqueueTask(m_taskFactory->createSetTextTrackIdentifier(m_context, textTrackIdentifier));
1922 : }
1923 : }
1924 :
1925 3 : bool GstGenericPlayer::getTextTrackIdentifier(std::string &textTrackIdentifier)
1926 : {
1927 3 : if (!m_context.subtitleSink)
1928 : {
1929 1 : RIALTO_SERVER_LOG_ERROR("There is no subtitle sink");
1930 1 : return false;
1931 : }
1932 :
1933 2 : gchar *identifier = nullptr;
1934 2 : m_glibWrapper->gObjectGet(m_context.subtitleSink, "text-track-identifier", &identifier, nullptr);
1935 :
1936 2 : if (identifier)
1937 : {
1938 1 : textTrackIdentifier = identifier;
1939 1 : m_glibWrapper->gFree(identifier);
1940 1 : return true;
1941 : }
1942 : else
1943 : {
1944 1 : RIALTO_SERVER_LOG_ERROR("Failed to get text track identifier");
1945 1 : return false;
1946 : }
1947 : }
1948 :
1949 1 : bool GstGenericPlayer::setLowLatency(bool lowLatency)
1950 : {
1951 1 : if (m_workerThread)
1952 : {
1953 1 : m_workerThread->enqueueTask(m_taskFactory->createSetLowLatency(m_context, *this, lowLatency));
1954 : }
1955 1 : return true;
1956 : }
1957 :
1958 1 : bool GstGenericPlayer::setSync(bool sync)
1959 : {
1960 1 : if (m_workerThread)
1961 : {
1962 1 : m_workerThread->enqueueTask(m_taskFactory->createSetSync(m_context, *this, sync));
1963 : }
1964 1 : return true;
1965 : }
1966 :
1967 4 : bool GstGenericPlayer::getSync(bool &sync)
1968 : {
1969 4 : bool returnValue{false};
1970 4 : GstElement *sink{getSink(MediaSourceType::AUDIO)};
1971 4 : if (sink)
1972 : {
1973 2 : if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "sync"))
1974 : {
1975 1 : m_glibWrapper->gObjectGet(sink, "sync", &sync, nullptr);
1976 1 : returnValue = true;
1977 : }
1978 : else
1979 : {
1980 1 : RIALTO_SERVER_LOG_ERROR("Sync not supported in sink '%s'", GST_ELEMENT_NAME(sink));
1981 : }
1982 2 : m_gstWrapper->gstObjectUnref(sink);
1983 : }
1984 2 : else if (m_context.pendingSync.has_value())
1985 : {
1986 1 : RIALTO_SERVER_LOG_DEBUG("Returning queued value");
1987 1 : sync = m_context.pendingSync.value();
1988 1 : returnValue = true;
1989 : }
1990 : else
1991 : {
1992 : // We dont know the default setting on the sync, so return failure here
1993 1 : RIALTO_SERVER_LOG_WARN("No audio sink attached or queued value");
1994 : }
1995 :
1996 4 : return returnValue;
1997 : }
1998 :
1999 1 : bool GstGenericPlayer::setSyncOff(bool syncOff)
2000 : {
2001 1 : if (m_workerThread)
2002 : {
2003 1 : m_workerThread->enqueueTask(m_taskFactory->createSetSyncOff(m_context, *this, syncOff));
2004 : }
2005 1 : return true;
2006 : }
2007 :
2008 1 : bool GstGenericPlayer::setStreamSyncMode(const MediaSourceType &mediaSourceType, int32_t streamSyncMode)
2009 : {
2010 1 : if (m_workerThread)
2011 : {
2012 2 : m_workerThread->enqueueTask(
2013 2 : m_taskFactory->createSetStreamSyncMode(m_context, *this, mediaSourceType, streamSyncMode));
2014 : }
2015 1 : return true;
2016 : }
2017 :
2018 5 : bool GstGenericPlayer::getStreamSyncMode(int32_t &streamSyncMode)
2019 : {
2020 5 : bool returnValue{false};
2021 5 : GstElement *decoder = getDecoder(MediaSourceType::AUDIO);
2022 5 : if (decoder && m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "stream-sync-mode"))
2023 : {
2024 2 : m_glibWrapper->gObjectGet(decoder, "stream-sync-mode", &streamSyncMode, nullptr);
2025 2 : returnValue = true;
2026 : }
2027 : else
2028 : {
2029 3 : std::unique_lock lock{m_context.propertyMutex};
2030 3 : if (m_context.pendingStreamSyncMode.find(MediaSourceType::AUDIO) != m_context.pendingStreamSyncMode.end())
2031 : {
2032 1 : RIALTO_SERVER_LOG_DEBUG("Returning queued value");
2033 1 : streamSyncMode = m_context.pendingStreamSyncMode[MediaSourceType::AUDIO];
2034 1 : returnValue = true;
2035 : }
2036 : else
2037 : {
2038 2 : RIALTO_SERVER_LOG_ERROR("Stream sync mode not supported in decoder '%s'",
2039 : (decoder ? GST_ELEMENT_NAME(decoder) : "null"));
2040 : }
2041 3 : }
2042 :
2043 5 : if (decoder)
2044 3 : m_gstWrapper->gstObjectUnref(GST_OBJECT(decoder));
2045 :
2046 5 : return returnValue;
2047 : }
2048 :
2049 1 : void GstGenericPlayer::ping(std::unique_ptr<IHeartbeatHandler> &&heartbeatHandler)
2050 : {
2051 1 : if (m_workerThread)
2052 : {
2053 1 : m_workerThread->enqueueTask(m_taskFactory->createPing(std::move(heartbeatHandler)));
2054 : }
2055 : }
2056 :
2057 1 : void GstGenericPlayer::flush(const MediaSourceType &mediaSourceType, bool resetTime, bool &async)
2058 : {
2059 1 : if (m_workerThread)
2060 : {
2061 1 : async = isAsync(mediaSourceType);
2062 1 : m_flushWatcher->setFlushing(mediaSourceType, async);
2063 1 : m_workerThread->enqueueTask(m_taskFactory->createFlush(m_context, *this, mediaSourceType, resetTime));
2064 : }
2065 : }
2066 :
2067 1 : void GstGenericPlayer::setSourcePosition(const MediaSourceType &mediaSourceType, int64_t position, bool resetTime,
2068 : double appliedRate, uint64_t stopPosition)
2069 : {
2070 1 : if (m_workerThread)
2071 : {
2072 1 : m_workerThread->enqueueTask(m_taskFactory->createSetSourcePosition(m_context, mediaSourceType, position,
2073 : resetTime, appliedRate, stopPosition));
2074 : }
2075 : }
2076 :
2077 0 : void GstGenericPlayer::setSubtitleOffset(int64_t position)
2078 : {
2079 0 : if (m_workerThread)
2080 : {
2081 0 : m_workerThread->enqueueTask(m_taskFactory->createSetSubtitleOffset(m_context, position));
2082 : }
2083 : }
2084 :
2085 1 : void GstGenericPlayer::processAudioGap(int64_t position, uint32_t duration, int64_t discontinuityGap, bool audioAac)
2086 : {
2087 1 : if (m_workerThread)
2088 : {
2089 2 : m_workerThread->enqueueTask(
2090 2 : m_taskFactory->createProcessAudioGap(m_context, position, duration, discontinuityGap, audioAac));
2091 : }
2092 1 : }
2093 :
2094 1 : void GstGenericPlayer::setBufferingLimit(uint32_t limitBufferingMs)
2095 : {
2096 1 : if (m_workerThread)
2097 : {
2098 1 : m_workerThread->enqueueTask(m_taskFactory->createSetBufferingLimit(m_context, *this, limitBufferingMs));
2099 : }
2100 : }
2101 :
2102 5 : bool GstGenericPlayer::getBufferingLimit(uint32_t &limitBufferingMs)
2103 : {
2104 5 : bool returnValue{false};
2105 5 : GstElement *decoder = getDecoder(MediaSourceType::AUDIO);
2106 5 : if (decoder && m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "limit-buffering-ms"))
2107 : {
2108 2 : m_glibWrapper->gObjectGet(decoder, "limit-buffering-ms", &limitBufferingMs, nullptr);
2109 2 : returnValue = true;
2110 : }
2111 : else
2112 : {
2113 3 : std::unique_lock lock{m_context.propertyMutex};
2114 3 : if (m_context.pendingBufferingLimit.has_value())
2115 : {
2116 1 : RIALTO_SERVER_LOG_DEBUG("Returning queued value");
2117 1 : limitBufferingMs = m_context.pendingBufferingLimit.value();
2118 1 : returnValue = true;
2119 : }
2120 : else
2121 : {
2122 2 : RIALTO_SERVER_LOG_ERROR("buffering limit not supported in decoder '%s'",
2123 : (decoder ? GST_ELEMENT_NAME(decoder) : "null"));
2124 : }
2125 3 : }
2126 :
2127 5 : if (decoder)
2128 3 : m_gstWrapper->gstObjectUnref(GST_OBJECT(decoder));
2129 :
2130 5 : return returnValue;
2131 : }
2132 :
2133 1 : void GstGenericPlayer::setUseBuffering(bool useBuffering)
2134 : {
2135 1 : if (m_workerThread)
2136 : {
2137 1 : m_workerThread->enqueueTask(m_taskFactory->createSetUseBuffering(m_context, *this, useBuffering));
2138 : }
2139 : }
2140 :
2141 3 : bool GstGenericPlayer::getUseBuffering(bool &useBuffering)
2142 : {
2143 3 : if (m_context.playbackGroup.m_curAudioDecodeBin)
2144 : {
2145 1 : m_glibWrapper->gObjectGet(m_context.playbackGroup.m_curAudioDecodeBin, "use-buffering", &useBuffering, nullptr);
2146 1 : return true;
2147 : }
2148 : else
2149 : {
2150 2 : std::unique_lock lock{m_context.propertyMutex};
2151 2 : if (m_context.pendingUseBuffering.has_value())
2152 : {
2153 1 : RIALTO_SERVER_LOG_DEBUG("Returning queued value");
2154 1 : useBuffering = m_context.pendingUseBuffering.value();
2155 1 : return true;
2156 : }
2157 2 : }
2158 1 : return false;
2159 : }
2160 :
2161 1 : void GstGenericPlayer::switchSource(const std::unique_ptr<IMediaPipeline::MediaSource> &mediaSource)
2162 : {
2163 1 : if (m_workerThread)
2164 : {
2165 1 : m_workerThread->enqueueTask(m_taskFactory->createSwitchSource(*this, mediaSource));
2166 : }
2167 : }
2168 :
2169 1 : void GstGenericPlayer::handleBusMessage(GstMessage *message)
2170 : {
2171 1 : m_workerThread->enqueueTask(m_taskFactory->createHandleBusMessage(m_context, *this, message, *m_flushWatcher));
2172 : }
2173 :
2174 1 : void GstGenericPlayer::updatePlaybackGroup(GstElement *typefind, const GstCaps *caps)
2175 : {
2176 1 : m_workerThread->enqueueTask(m_taskFactory->createUpdatePlaybackGroup(m_context, *this, typefind, caps));
2177 : }
2178 :
2179 3 : void GstGenericPlayer::addAutoVideoSinkChild(GObject *object)
2180 : {
2181 : // Only add children that are sinks
2182 3 : if (GST_OBJECT_FLAG_IS_SET(GST_ELEMENT(object), GST_ELEMENT_FLAG_SINK))
2183 : {
2184 2 : RIALTO_SERVER_LOG_DEBUG("Store AutoVideoSink child sink");
2185 :
2186 2 : if (m_context.autoVideoChildSink && m_context.autoVideoChildSink != GST_ELEMENT(object))
2187 : {
2188 1 : RIALTO_SERVER_LOG_MIL("AutoVideoSink child is been overwritten");
2189 : }
2190 2 : m_context.autoVideoChildSink = GST_ELEMENT(object);
2191 : }
2192 3 : }
2193 :
2194 3 : void GstGenericPlayer::addAutoAudioSinkChild(GObject *object)
2195 : {
2196 : // Only add children that are sinks
2197 3 : if (GST_OBJECT_FLAG_IS_SET(GST_ELEMENT(object), GST_ELEMENT_FLAG_SINK))
2198 : {
2199 2 : RIALTO_SERVER_LOG_DEBUG("Store AutoAudioSink child sink");
2200 :
2201 2 : if (m_context.autoAudioChildSink && m_context.autoAudioChildSink != GST_ELEMENT(object))
2202 : {
2203 1 : RIALTO_SERVER_LOG_MIL("AutoAudioSink child is been overwritten");
2204 : }
2205 2 : m_context.autoAudioChildSink = GST_ELEMENT(object);
2206 : }
2207 3 : }
2208 :
2209 3 : void GstGenericPlayer::removeAutoVideoSinkChild(GObject *object)
2210 : {
2211 3 : if (GST_OBJECT_FLAG_IS_SET(GST_ELEMENT(object), GST_ELEMENT_FLAG_SINK))
2212 : {
2213 3 : RIALTO_SERVER_LOG_DEBUG("Remove AutoVideoSink child sink");
2214 :
2215 3 : if (m_context.autoVideoChildSink && m_context.autoVideoChildSink != GST_ELEMENT(object))
2216 : {
2217 1 : RIALTO_SERVER_LOG_MIL("AutoVideoSink child sink is not the same as the one stored");
2218 1 : return;
2219 : }
2220 :
2221 2 : m_context.autoVideoChildSink = nullptr;
2222 : }
2223 : }
2224 :
2225 3 : void GstGenericPlayer::removeAutoAudioSinkChild(GObject *object)
2226 : {
2227 3 : if (GST_OBJECT_FLAG_IS_SET(GST_ELEMENT(object), GST_ELEMENT_FLAG_SINK))
2228 : {
2229 3 : RIALTO_SERVER_LOG_DEBUG("Remove AutoAudioSink child sink");
2230 :
2231 3 : if (m_context.autoAudioChildSink && m_context.autoAudioChildSink != GST_ELEMENT(object))
2232 : {
2233 1 : RIALTO_SERVER_LOG_MIL("AutoAudioSink child sink is not the same as the one stored");
2234 1 : return;
2235 : }
2236 :
2237 2 : m_context.autoAudioChildSink = nullptr;
2238 : }
2239 : }
2240 :
2241 14 : GstElement *GstGenericPlayer::getSinkChildIfAutoVideoSink(GstElement *sink) const
2242 : {
2243 14 : const gchar *kTmpName = m_glibWrapper->gTypeName(G_OBJECT_TYPE(sink));
2244 14 : if (!kTmpName)
2245 0 : return sink;
2246 :
2247 28 : const std::string kElementTypeName{kTmpName};
2248 14 : if (kElementTypeName == "GstAutoVideoSink")
2249 : {
2250 1 : if (!m_context.autoVideoChildSink)
2251 : {
2252 0 : RIALTO_SERVER_LOG_WARN("No child sink has been added to the autovideosink");
2253 : }
2254 : else
2255 : {
2256 1 : return m_context.autoVideoChildSink;
2257 : }
2258 : }
2259 13 : return sink;
2260 14 : }
2261 :
2262 11 : GstElement *GstGenericPlayer::getSinkChildIfAutoAudioSink(GstElement *sink) const
2263 : {
2264 11 : const gchar *kTmpName = m_glibWrapper->gTypeName(G_OBJECT_TYPE(sink));
2265 11 : if (!kTmpName)
2266 0 : return sink;
2267 :
2268 22 : const std::string kElementTypeName{kTmpName};
2269 11 : if (kElementTypeName == "GstAutoAudioSink")
2270 : {
2271 1 : if (!m_context.autoAudioChildSink)
2272 : {
2273 0 : RIALTO_SERVER_LOG_WARN("No child sink has been added to the autoaudiosink");
2274 : }
2275 : else
2276 : {
2277 1 : return m_context.autoAudioChildSink;
2278 : }
2279 : }
2280 10 : return sink;
2281 11 : }
2282 :
2283 210 : void GstGenericPlayer::setPlaybinFlags(bool enableAudio)
2284 : {
2285 210 : unsigned flags = getGstPlayFlag("video") | getGstPlayFlag("native-video") | getGstPlayFlag("text");
2286 :
2287 210 : if (enableAudio)
2288 : {
2289 210 : flags |= getGstPlayFlag("audio");
2290 210 : flags |= shouldEnableNativeAudio() ? getGstPlayFlag("native-audio") : 0;
2291 : }
2292 :
2293 210 : m_glibWrapper->gObjectSet(m_context.pipeline, "flags", flags, nullptr);
2294 : }
2295 :
2296 210 : bool GstGenericPlayer::shouldEnableNativeAudio()
2297 : {
2298 210 : GstElementFactory *factory = m_gstWrapper->gstElementFactoryFind("brcmaudiosink");
2299 210 : if (factory)
2300 : {
2301 1 : m_gstWrapper->gstObjectUnref(GST_OBJECT(factory));
2302 1 : return true;
2303 : }
2304 209 : return false;
2305 : }
2306 :
2307 : }; // namespace firebolt::rialto::server
|