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