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