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