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 "tasks/generic/SetupElement.h"
21 : #include "GenericPlayerContext.h"
22 : #include "IGlibWrapper.h"
23 : #include "IGstGenericPlayerPrivate.h"
24 : #include "IGstWrapper.h"
25 : #include "RialtoServerLogging.h"
26 : #include "Utils.h"
27 :
28 : namespace
29 : {
30 : /**
31 : * @brief Callback for audio underflow event from sink. Called by the Gstreamer thread.
32 : *
33 : * @param[in] object : the object that emitted the signal
34 : * @param[in] fifoDepth : the fifo depth (may be 0)
35 : * @param[in] queueDepth : the queue depth (may be NULL)
36 : * @param[in] self : The pointer to IGstGenericPlayerPrivate
37 : *
38 : * @retval true if the handling of the message is successful, false otherwise.
39 : */
40 1 : void audioUnderflowCallback(GstElement *object, guint fifoDepth, gpointer queueDepth, gpointer self)
41 : {
42 1 : firebolt::rialto::server::IGstGenericPlayerPrivate *player =
43 : static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
44 1 : player->scheduleAudioUnderflow();
45 : }
46 :
47 : /**
48 : * @brief Callback for video underflow event from sink. Called by the Gstreamer thread.
49 : *
50 : * @param[in] object : the object that emitted the signal
51 : * @param[in] fifoDepth : the fifo depth (may be 0)
52 : * @param[in] queueDepth : the queue depth (may be NULL)
53 : * @param[in] self : The pointer to IGstGenericPlayerPrivate
54 : *
55 : * @retval true if the handling of the message is successful, false otherwise.
56 : */
57 1 : void videoUnderflowCallback(GstElement *object, guint fifoDepth, gpointer queueDepth, gpointer self)
58 : {
59 1 : firebolt::rialto::server::IGstGenericPlayerPrivate *player =
60 : static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
61 1 : player->scheduleVideoUnderflow();
62 : }
63 :
64 : /**
65 : * @brief Callback for first video frame event from the emitting video element. Called by the Gstreamer thread.
66 : *
67 : * @param[in] object : the object that emitted the signal
68 : * @param[in] fifoDepth : the fifo depth (may be 0)
69 : * @param[in] queueDepth : the queue depth (may be NULL)
70 : * @param[in] self : The pointer to IGstGenericPlayerPrivate
71 : */
72 1 : void firstVideoFrameCallback(GstElement *object, guint fifoDepth, gpointer queueDepth, gpointer self)
73 : {
74 1 : firebolt::rialto::server::IGstGenericPlayerPrivate *player =
75 : static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
76 1 : player->scheduleFirstVideoFrameReceived();
77 : }
78 :
79 : /**
80 : * @brief Callback for a autovideosink when a child has been added to the sink.
81 : *
82 : * @param[in] obj : the parent element (autovideosink)
83 : * @param[in] object : the child element
84 : * @param[in] name : the name of the child element
85 : * @param[in] self : The pointer to IGstGenericPlayerPrivate
86 : */
87 1 : void autoVideoSinkChildAddedCallback(GstChildProxy *obj, GObject *object, gchar *name, gpointer self)
88 : {
89 1 : RIALTO_SERVER_LOG_DEBUG("AutoVideoSink added element %s", name);
90 1 : firebolt::rialto::server::IGstGenericPlayerPrivate *player =
91 : static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
92 1 : player->addAutoVideoSinkChild(object);
93 : }
94 :
95 : /**
96 : * @brief Callback for a autoaudiosink when a child has been added to the sink.
97 : *
98 : * @param[in] obj : the parent element (autoaudiosink)
99 : * @param[in] object : the child element
100 : * @param[in] name : the name of the child element
101 : * @param[in] self : The pointer to IGstGenericPlayerPrivate
102 : */
103 1 : void autoAudioSinkChildAddedCallback(GstChildProxy *obj, GObject *object, gchar *name, gpointer self)
104 : {
105 1 : RIALTO_SERVER_LOG_DEBUG("AutoAudioSink added element %s", name);
106 1 : firebolt::rialto::server::IGstGenericPlayerPrivate *player =
107 : static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
108 1 : player->addAutoAudioSinkChild(object);
109 : }
110 :
111 : /**
112 : * @brief Callback for a autovideosink when a child has been removed from the sink.
113 : *
114 : * @param[in] obj : the parent element (autovideosink)
115 : * @param[in] object : the child element
116 : * @param[in] name : the name of the child element
117 : * @param[in] self : The pointer to IGstGenericPlayerPrivate
118 : */
119 1 : void autoVideoSinkChildRemovedCallback(GstChildProxy *obj, GObject *object, gchar *name, gpointer self)
120 : {
121 1 : RIALTO_SERVER_LOG_DEBUG("AutoVideoSink removed element %s", name);
122 1 : firebolt::rialto::server::IGstGenericPlayerPrivate *player =
123 : static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
124 1 : player->removeAutoVideoSinkChild(object);
125 : }
126 :
127 : /**
128 : * @brief Callback for a autoaudiosink when a child has been removed from the sink.
129 : *
130 : * @param[in] obj : the parent element (autoaudiosink)
131 : * @param[in] object : the child element
132 : * @param[in] name : the name of the child element
133 : * @param[in] self : The pointer to IGstGenericPlayerPrivate
134 : */
135 1 : void autoAudioSinkChildRemovedCallback(GstChildProxy *obj, GObject *object, gchar *name, gpointer self)
136 : {
137 1 : RIALTO_SERVER_LOG_DEBUG("AutoAudioSink removed element %s", name);
138 1 : firebolt::rialto::server::IGstGenericPlayerPrivate *player =
139 : static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
140 1 : player->removeAutoAudioSinkChild(object);
141 : }
142 : } // namespace
143 :
144 : namespace firebolt::rialto::server::tasks::generic
145 : {
146 35 : SetupElement::SetupElement(GenericPlayerContext &context,
147 : const std::shared_ptr<firebolt::rialto::wrappers::IGstWrapper> &gstWrapper,
148 : const std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapper> &glibWrapper,
149 35 : IGstGenericPlayerPrivate &player, GstElement *element)
150 35 : : m_context{context}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_player{player}, m_element{element}
151 : {
152 35 : RIALTO_SERVER_LOG_DEBUG("Constructing SetupElement");
153 : }
154 :
155 36 : SetupElement::~SetupElement()
156 : {
157 35 : RIALTO_SERVER_LOG_DEBUG("SetupElement finished");
158 36 : }
159 :
160 34 : void SetupElement::execute() const
161 : {
162 34 : RIALTO_SERVER_LOG_DEBUG("Executing SetupElement");
163 :
164 34 : const std::string kElementTypeName = m_glibWrapper->gTypeName(G_OBJECT_TYPE(m_element));
165 34 : if (kElementTypeName == "GstAutoVideoSink")
166 : {
167 : // Check and store child sink so we can set underlying properties
168 5 : m_glibWrapper->gSignalConnect(m_element, "child-added", G_CALLBACK(autoVideoSinkChildAddedCallback), &m_player);
169 5 : m_glibWrapper->gSignalConnect(m_element, "child-removed", G_CALLBACK(autoVideoSinkChildRemovedCallback),
170 5 : &m_player);
171 :
172 : // AutoVideoSink sets child before it is setup on the pipeline, so check for children here
173 5 : GstIterator *sinks = m_gstWrapper->gstBinIterateSinks(GST_BIN(m_element));
174 5 : if (sinks && sinks->size > 1)
175 : {
176 1 : RIALTO_SERVER_LOG_WARN("More than one child sink attached");
177 : }
178 :
179 5 : GValue elem = G_VALUE_INIT;
180 5 : if (m_gstWrapper->gstIteratorNext(sinks, &elem) == GST_ITERATOR_OK)
181 : {
182 2 : m_player.addAutoVideoSinkChild(G_OBJECT(m_glibWrapper->gValueGetObject(&elem)));
183 : }
184 5 : m_glibWrapper->gValueUnset(&elem);
185 :
186 5 : if (sinks)
187 5 : m_gstWrapper->gstIteratorFree(sinks);
188 : }
189 29 : else if (kElementTypeName == "GstAutoAudioSink")
190 : {
191 : // Check and store child sink so we can set underlying properties
192 5 : m_glibWrapper->gSignalConnect(m_element, "child-added", G_CALLBACK(autoAudioSinkChildAddedCallback), &m_player);
193 5 : m_glibWrapper->gSignalConnect(m_element, "child-removed", G_CALLBACK(autoAudioSinkChildRemovedCallback),
194 5 : &m_player);
195 :
196 : // AutoAudioSink sets child before it is setup on the pipeline, so check for children here
197 5 : GstIterator *sinks = m_gstWrapper->gstBinIterateSinks(GST_BIN(m_element));
198 5 : if (sinks && sinks->size > 1)
199 : {
200 1 : RIALTO_SERVER_LOG_WARN("More than one child sink attached");
201 : }
202 :
203 5 : GValue elem = G_VALUE_INIT;
204 5 : if (m_gstWrapper->gstIteratorNext(sinks, &elem) == GST_ITERATOR_OK)
205 : {
206 2 : m_player.addAutoAudioSinkChild(G_OBJECT(m_glibWrapper->gValueGetObject(&elem)));
207 : }
208 5 : m_glibWrapper->gValueUnset(&elem);
209 :
210 5 : if (sinks)
211 5 : m_gstWrapper->gstIteratorFree(sinks);
212 : }
213 :
214 34 : if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "amlhalasink"))
215 : {
216 2 : if (m_context.streamInfo.find(MediaSourceType::VIDEO) != m_context.streamInfo.end())
217 : {
218 : // Wait for video so that the audio aligns at the starting point with timeout of 4000ms.
219 1 : m_glibWrapper->gObjectSet(m_element, "wait-video", TRUE, "a-wait-timeout", 4000, nullptr);
220 : }
221 :
222 : // Xrun occasionally pauses the underlying sink due to unstable playback, but the rest of the pipeline
223 : // remains in the playing state. This causes problems with the synchronization of gst element and rialto
224 : // ultimately hangs waiting for pipeline termination.
225 2 : m_glibWrapper->gObjectSet(m_element, "disable-xrun", TRUE, nullptr);
226 : }
227 32 : else if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "brcmaudiosink"))
228 : {
229 1 : m_glibWrapper->gObjectSet(m_element, "async", TRUE, nullptr);
230 : }
231 31 : else if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "rialtotexttracksink"))
232 : {
233 : // in cannot be set during construction, because playsink overwrites "sync" value of text-sink during setup
234 1 : m_glibWrapper->gObjectSet(m_element, "sync", FALSE, nullptr);
235 : }
236 :
237 34 : if (m_context.subtitleSink)
238 : {
239 3 : if (!m_context.isVideoHandleSet && isVideoDecoder(*m_gstWrapper, m_element))
240 : {
241 3 : if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "westerossink"))
242 : {
243 2 : gpointer decoder{nullptr};
244 2 : m_glibWrapper->gObjectGet(m_element, "videodecoder", &decoder, nullptr);
245 2 : if (decoder)
246 : {
247 1 : m_glibWrapper->gObjectSet(m_context.subtitleSink, "video-decoder", decoder, nullptr);
248 1 : RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", decoder);
249 1 : m_context.isVideoHandleSet = true;
250 : }
251 : else
252 : {
253 1 : m_glibWrapper->gObjectSet(m_context.subtitleSink, "video-decoder", m_element, nullptr);
254 1 : RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", m_element);
255 1 : m_context.isVideoHandleSet = true;
256 : }
257 : }
258 1 : else if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "omx"))
259 : {
260 1 : m_glibWrapper->gObjectSet(m_context.subtitleSink, "video-decoder", m_element, nullptr);
261 1 : RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", m_element);
262 1 : m_context.isVideoHandleSet = true;
263 : }
264 : }
265 : }
266 :
267 34 : if (isDecoder(*m_gstWrapper, m_element) || isSink(*m_gstWrapper, m_element))
268 : {
269 28 : std::optional<std::string> underflowSignalName = getUnderflowSignalName(*m_glibWrapper, m_element);
270 28 : if (underflowSignalName)
271 : {
272 28 : if (isAudio(*m_gstWrapper, m_element))
273 : {
274 16 : RIALTO_SERVER_LOG_INFO("Connecting audio underflow callback for signal: %s",
275 : underflowSignalName.value().c_str());
276 32 : m_glibWrapper->gSignalConnect(m_element, underflowSignalName.value().c_str(),
277 16 : G_CALLBACK(audioUnderflowCallback), &m_player);
278 : }
279 12 : else if (isVideo(*m_gstWrapper, m_element))
280 : {
281 12 : RIALTO_SERVER_LOG_INFO("Connecting video underflow callback for signal: %s",
282 : underflowSignalName.value().c_str());
283 24 : m_glibWrapper->gSignalConnect(m_element, underflowSignalName.value().c_str(),
284 12 : G_CALLBACK(videoUnderflowCallback), &m_player);
285 : }
286 : }
287 :
288 28 : std::optional<std::string> firstFrameSignalName = getFirstFrameSignalName(*m_glibWrapper, m_element);
289 28 : if (firstFrameSignalName)
290 : {
291 1 : if (isVideo(*m_gstWrapper, m_element))
292 : {
293 1 : RIALTO_SERVER_LOG_INFO("Connecting first video frame callback for signal: %s",
294 : firstFrameSignalName.value().c_str());
295 2 : m_glibWrapper->gSignalConnect(m_element, firstFrameSignalName.value().c_str(),
296 1 : G_CALLBACK(firstVideoFrameCallback), &m_player);
297 : }
298 : }
299 28 : }
300 :
301 34 : if (isVideoSink(*m_gstWrapper, m_element))
302 : {
303 10 : if (!m_context.videoSink)
304 : {
305 10 : m_gstWrapper->gstObjectRef(m_element);
306 10 : m_context.videoSink = m_element;
307 : }
308 10 : if (!m_context.pendingGeometry.empty())
309 : {
310 1 : m_player.setVideoSinkRectangle();
311 : }
312 10 : if (m_context.pendingImmediateOutputForVideo.has_value())
313 : {
314 1 : m_player.setImmediateOutput();
315 : }
316 10 : if (m_context.pendingRenderFrame)
317 : {
318 1 : m_player.setRenderFrame();
319 : }
320 10 : if (m_context.pendingShowVideoWindow.has_value())
321 : {
322 1 : m_player.setShowVideoWindow();
323 : }
324 : }
325 24 : else if (isAudioDecoder(*m_gstWrapper, m_element))
326 : {
327 6 : if (m_context.pendingSyncOff.has_value())
328 : {
329 1 : m_player.setSyncOff();
330 : }
331 6 : if (m_context.pendingStreamSyncMode.find(MediaSourceType::AUDIO) != m_context.pendingStreamSyncMode.end())
332 : {
333 1 : m_player.setStreamSyncMode(MediaSourceType::AUDIO);
334 : }
335 6 : if (m_context.pendingBufferingLimit.has_value())
336 : {
337 1 : m_player.setBufferingLimit();
338 : }
339 7 : if (m_context.isLive &&
340 1 : m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(m_element), "enable-rate-correction"))
341 : {
342 1 : RIALTO_SERVER_LOG_INFO("Enabling rate correction for broadcom decoder.");
343 1 : m_glibWrapper->gObjectSet(m_element, "enable-rate-correction", TRUE, nullptr);
344 : }
345 : }
346 18 : else if (isAudioSink(*m_gstWrapper, m_element))
347 : {
348 10 : if (m_context.pendingLowLatency.has_value())
349 : {
350 1 : m_player.setLowLatency();
351 : }
352 10 : if (m_context.pendingSync.has_value())
353 : {
354 1 : m_player.setSync();
355 : }
356 : }
357 8 : else if (isVideoParser(*m_gstWrapper, m_element))
358 : {
359 1 : if (m_context.pendingStreamSyncMode.find(MediaSourceType::VIDEO) != m_context.pendingStreamSyncMode.end())
360 : {
361 1 : m_player.setStreamSyncMode(MediaSourceType::VIDEO);
362 : }
363 : }
364 :
365 34 : if (m_gstWrapper->gstIsBaseParse(m_element))
366 : {
367 1 : m_gstWrapper->gstBaseParseSetPtsInterpolation(GST_BASE_PARSE(m_element), FALSE);
368 : }
369 :
370 34 : m_gstWrapper->gstObjectUnref(m_element);
371 : }
372 : } // namespace firebolt::rialto::server::tasks::generic
|