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