Line data Source code
1 : /*
2 : * Copyright (C) 2022 Sky UK
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation;
7 : * version 2.1 of the License.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, write to the Free Software
16 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 : */
18 :
19 : #define USE_GLIB 1
20 :
21 : #include <cstring>
22 : #include <limits>
23 :
24 : #include <gst/gst.h>
25 :
26 : #include "ControlBackend.h"
27 : #include "GStreamerUtils.h"
28 : #include "IClientLogControl.h"
29 : #include "IMediaPipeline.h"
30 : #include "LogToGstHandler.h"
31 : #include "RialtoGStreamerMSEBaseSink.h"
32 : #include "RialtoGStreamerMSEBaseSinkPrivate.h"
33 :
34 : GST_DEBUG_CATEGORY_STATIC(RialtoMSEBaseSinkDebug);
35 : #define GST_CAT_DEFAULT RialtoMSEBaseSinkDebug
36 :
37 : #define rialto_mse_base_sink_parent_class parent_class
38 3285 : G_DEFINE_TYPE_WITH_CODE(RialtoMSEBaseSink, rialto_mse_base_sink, GST_TYPE_ELEMENT,
39 : G_ADD_PRIVATE(RialtoMSEBaseSink)
40 : GST_DEBUG_CATEGORY_INIT(RialtoMSEBaseSinkDebug, "rialtomsebasesink", 0,
41 : "rialto mse base sink"));
42 :
43 : enum
44 : {
45 : PROP_0,
46 : PROP_IS_SINGLE_PATH_STREAM,
47 : PROP_N_STREAMS,
48 : PROP_HAS_DRM,
49 : PROP_STATS,
50 : PROP_LAST
51 : };
52 :
53 : enum
54 : {
55 : SIGNAL_UNDERFLOW,
56 : SIGNAL_LAST
57 : };
58 :
59 : static guint g_signals[SIGNAL_LAST] = {0};
60 :
61 268 : void rialto_mse_base_sink_initialise_delegate(RialtoMSEBaseSink *sink, const std::shared_ptr<IPlaybackDelegate> &delegate)
62 : {
63 268 : std::unique_lock lock{sink->priv->m_sinkMutex};
64 268 : sink->priv->m_delegate = delegate;
65 :
66 268 : for (auto &[type, value] : sink->priv->m_queuedProperties)
67 : {
68 0 : delegate->setProperty(type, &value);
69 0 : g_value_unset(&value);
70 : }
71 268 : sink->priv->m_queuedProperties.clear();
72 : }
73 :
74 1545 : static std::shared_ptr<IPlaybackDelegate> rialto_mse_base_sink_get_delegate(RialtoMSEBaseSink *sink)
75 : {
76 1545 : std::unique_lock lock{sink->priv->m_sinkMutex};
77 1545 : if (!sink->priv->m_delegate)
78 : {
79 0 : GST_ERROR_OBJECT(sink, "Sink delegate not initialized");
80 : }
81 3090 : return sink->priv->m_delegate;
82 1545 : }
83 :
84 28 : static gboolean rialto_mse_base_sink_send_event(GstElement *element, GstEvent *event)
85 : {
86 28 : if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(element)))
87 : {
88 28 : return delegate->handleSendEvent(event);
89 : }
90 0 : return FALSE;
91 : }
92 :
93 194 : gboolean rialto_mse_base_sink_event(GstPad *pad, GstObject *parent, GstEvent *event)
94 : {
95 194 : if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(parent)))
96 : {
97 194 : return delegate->handleEvent(pad, parent, event);
98 : }
99 0 : return FALSE;
100 : }
101 :
102 32 : GstFlowReturn rialto_mse_base_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
103 : {
104 32 : if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(parent)))
105 : {
106 32 : return delegate->handleBuffer(buf);
107 : }
108 0 : return GST_FLOW_ERROR;
109 : }
110 :
111 21 : static gboolean rialto_mse_base_sink_query(GstElement *element, GstQuery *query)
112 : {
113 21 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(element);
114 21 : if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
115 : {
116 21 : std::optional<gboolean> result{delegate->handleQuery(query)};
117 21 : if (result.has_value())
118 : {
119 8 : return result.value();
120 : }
121 13 : GstElement *parent = GST_ELEMENT(&sink->parent);
122 13 : return GST_ELEMENT_CLASS(parent_class)->query(parent, query);
123 21 : }
124 0 : return FALSE;
125 : }
126 :
127 841 : static GstStateChangeReturn rialto_mse_base_sink_change_state(GstElement *element, GstStateChange transition)
128 : {
129 841 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(element);
130 841 : if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
131 : {
132 841 : GstStateChangeReturn status = delegate->changeState(transition);
133 841 : if (GST_STATE_CHANGE_FAILURE != status)
134 : {
135 837 : GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
136 837 : if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
137 : {
138 0 : GST_WARNING_OBJECT(sink, "State change failed");
139 0 : return result;
140 : }
141 837 : else if (result == GST_STATE_CHANGE_ASYNC)
142 : {
143 0 : return GST_STATE_CHANGE_ASYNC;
144 : }
145 : }
146 841 : return status;
147 : }
148 0 : return GST_STATE_CHANGE_FAILURE;
149 : }
150 :
151 49 : void rialto_mse_base_sink_handle_get_property(RialtoMSEBaseSink *sink, const IPlaybackDelegate::Property &property,
152 : GValue *value)
153 : {
154 49 : if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
155 : {
156 49 : delegate->getProperty(property, value);
157 : }
158 : else // Copy queued value if present
159 : {
160 0 : std::unique_lock lock{sink->priv->m_sinkMutex};
161 0 : if (sink->priv->m_queuedProperties.find(property) != sink->priv->m_queuedProperties.end())
162 : {
163 0 : g_value_copy(&sink->priv->m_queuedProperties[property], value);
164 : }
165 49 : }
166 : }
167 :
168 380 : void rialto_mse_base_sink_handle_set_property(RialtoMSEBaseSink *sink, const IPlaybackDelegate::Property &property,
169 : const GValue *value)
170 : {
171 380 : if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
172 : {
173 380 : delegate->setProperty(property, value);
174 : }
175 : else
176 : {
177 0 : std::unique_lock lock{sink->priv->m_sinkMutex};
178 0 : sink->priv->m_queuedProperties[property] = G_VALUE_INIT;
179 0 : g_value_init(&(sink->priv->m_queuedProperties[property]), G_VALUE_TYPE(value));
180 0 : g_value_copy(value, &(sink->priv->m_queuedProperties[property]));
181 380 : }
182 : }
183 :
184 5 : static void rialto_mse_base_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
185 : {
186 5 : switch (propId)
187 : {
188 1 : case PROP_IS_SINGLE_PATH_STREAM:
189 : // Set default value if it can't be acquired
190 1 : g_value_set_boolean(value, FALSE);
191 1 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object),
192 1 : IPlaybackDelegate::Property::IsSinglePathStream, value);
193 1 : break;
194 1 : case PROP_N_STREAMS:
195 : // Set default value if it can't be acquired
196 1 : g_value_set_int(value, 1);
197 1 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object),
198 1 : IPlaybackDelegate::Property::NumberOfStreams, value);
199 1 : break;
200 1 : case PROP_HAS_DRM:
201 : // Set default value if it can't be acquired
202 1 : g_value_set_boolean(value, TRUE);
203 1 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::HasDrm,
204 : value);
205 1 : break;
206 2 : case PROP_STATS:
207 2 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::Stats, value);
208 2 : break;
209 0 : default:
210 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
211 0 : break;
212 : }
213 5 : }
214 :
215 309 : static void rialto_mse_base_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
216 : {
217 309 : switch (propId)
218 : {
219 154 : case PROP_IS_SINGLE_PATH_STREAM:
220 154 : rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object),
221 154 : IPlaybackDelegate::Property::IsSinglePathStream, value);
222 154 : break;
223 154 : case PROP_N_STREAMS:
224 154 : rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object),
225 154 : IPlaybackDelegate::Property::NumberOfStreams, value);
226 154 : break;
227 1 : case PROP_HAS_DRM:
228 1 : rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::HasDrm,
229 : value);
230 1 : break;
231 0 : default:
232 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
233 0 : break;
234 : }
235 309 : }
236 :
237 1 : void rialto_mse_base_handle_rialto_server_sent_buffer_underflow(RialtoMSEBaseSink *sink)
238 : {
239 1 : GST_WARNING_OBJECT(sink, "Sending underflow signal");
240 : // send 2 last parameters just to be compatible with RDK's buffer-underflow-callback signal signature
241 1 : g_signal_emit(G_OBJECT(sink), g_signals[SIGNAL_UNDERFLOW], 0, 0, nullptr);
242 : }
243 :
244 268 : bool rialto_mse_base_sink_initialise_sinkpad(RialtoMSEBaseSink *sink)
245 : {
246 : GstPadTemplate *pad_template =
247 268 : gst_element_class_get_pad_template(GST_ELEMENT_CLASS(G_OBJECT_GET_CLASS(sink)), "sink");
248 268 : if (!pad_template)
249 : {
250 0 : GST_ERROR_OBJECT(sink, "Could not find sink pad template");
251 0 : return false;
252 : }
253 :
254 268 : GstPad *sinkPad = gst_pad_new_from_template(pad_template, "sink");
255 268 : if (!sinkPad)
256 : {
257 0 : GST_ERROR_OBJECT(sink, "Could not create sinkpad");
258 0 : return false;
259 : }
260 :
261 268 : gst_element_add_pad(GST_ELEMENT_CAST(sink), sinkPad);
262 268 : sink->priv->m_sinkPad = sinkPad;
263 :
264 268 : return true;
265 : }
266 :
267 268 : static void rialto_mse_base_sink_init(RialtoMSEBaseSink *sink)
268 : {
269 268 : GST_INFO_OBJECT(sink, "Init: %" GST_PTR_FORMAT, sink);
270 268 : sink->priv = static_cast<RialtoMSEBaseSinkPrivate *>(rialto_mse_base_sink_get_instance_private(sink));
271 268 : new (sink->priv) RialtoMSEBaseSinkPrivate();
272 :
273 268 : GST_OBJECT_FLAG_SET(sink, GST_ELEMENT_FLAG_SINK);
274 : }
275 :
276 268 : static void rialto_mse_base_sink_finalize(GObject *object)
277 : {
278 268 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(object);
279 268 : RialtoMSEBaseSinkPrivate *priv = sink->priv;
280 268 : GST_INFO_OBJECT(sink, "Finalize: %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, sink, priv);
281 :
282 268 : priv->~RialtoMSEBaseSinkPrivate();
283 268 : GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
284 : }
285 :
286 1 : static void rialto_mse_base_sink_class_init(RialtoMSEBaseSinkClass *klass)
287 : {
288 : std::shared_ptr<firebolt::rialto::IClientLogHandler> logToGstHandler =
289 1 : std::make_shared<firebolt::rialto::LogToGstHandler>();
290 1 : if (!firebolt::rialto::IClientLogControlFactory::createFactory()->createClientLogControl().registerLogHandler(logToGstHandler,
291 : true))
292 : {
293 0 : GST_ERROR("Unable to preRegister log handler");
294 : }
295 :
296 1 : GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
297 1 : GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
298 :
299 1 : gst_element_class_set_metadata(elementClass, "Rialto MSE base sink", "Generic", "A sink for Rialto", "Sky");
300 :
301 1 : gobjectClass->finalize = rialto_mse_base_sink_finalize;
302 1 : gobjectClass->get_property = rialto_mse_base_sink_get_property;
303 1 : gobjectClass->set_property = rialto_mse_base_sink_set_property;
304 1 : elementClass->query = rialto_mse_base_sink_query;
305 1 : elementClass->send_event = rialto_mse_base_sink_send_event;
306 1 : elementClass->change_state = rialto_mse_base_sink_change_state;
307 :
308 1 : g_signals[SIGNAL_UNDERFLOW] = g_signal_new("buffer-underflow-callback", G_TYPE_FROM_CLASS(klass),
309 : (GSignalFlags)(G_SIGNAL_RUN_LAST), 0, nullptr, nullptr,
310 : g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT,
311 : G_TYPE_POINTER);
312 :
313 1 : g_object_class_install_property(gobjectClass, PROP_IS_SINGLE_PATH_STREAM,
314 : g_param_spec_boolean("single-path-stream", "single path stream",
315 : "is single path stream", FALSE, GParamFlags(G_PARAM_READWRITE)));
316 :
317 1 : g_object_class_install_property(gobjectClass, PROP_N_STREAMS,
318 : g_param_spec_int("streams-number", "streams number", "streams number", 1, G_MAXINT,
319 : 1, GParamFlags(G_PARAM_READWRITE)));
320 :
321 1 : g_object_class_install_property(gobjectClass, PROP_HAS_DRM,
322 : g_param_spec_boolean("has-drm", "has drm", "has drm", TRUE,
323 : GParamFlags(G_PARAM_READWRITE)));
324 1 : g_object_class_install_property(gobjectClass, PROP_STATS,
325 : g_param_spec_pointer("stats", NULL, "pointer to a gst_structure",
326 : GParamFlags(G_PARAM_READABLE)));
327 : }
|