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