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