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