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 4373 : 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_SAMPLE,
51 : PROP_ENABLE_LAST_SAMPLE,
52 : PROP_LAST
53 : };
54 :
55 : enum
56 : {
57 : SIGNAL_UNDERFLOW,
58 : SIGNAL_FIRST_VIDEO_FRAME_RECEIVED,
59 : SIGNAL_LAST
60 : };
61 :
62 : static guint g_signals[SIGNAL_LAST] = {0};
63 :
64 311 : void rialto_mse_base_sink_initialise_delegate(RialtoMSEBaseSink *sink, const std::shared_ptr<IPlaybackDelegate> &delegate)
65 : {
66 311 : std::unique_lock lock{sink->priv->m_sinkMutex};
67 311 : sink->priv->m_delegate = delegate;
68 :
69 313 : for (auto &[type, value] : sink->priv->m_queuedProperties)
70 : {
71 2 : delegate->setProperty(type, &value);
72 2 : g_value_unset(&value);
73 : }
74 311 : sink->priv->m_queuedProperties.clear();
75 : }
76 :
77 1844 : static std::shared_ptr<IPlaybackDelegate> rialto_mse_base_sink_get_delegate(RialtoMSEBaseSink *sink)
78 : {
79 1844 : std::unique_lock lock{sink->priv->m_sinkMutex};
80 1844 : if (!sink->priv->m_delegate)
81 : {
82 3 : GST_ERROR_OBJECT(sink, "Sink delegate not initialized");
83 : }
84 3688 : return sink->priv->m_delegate;
85 1844 : }
86 :
87 45 : static gboolean rialto_mse_base_sink_send_event(GstElement *element, GstEvent *event)
88 : {
89 45 : if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(element)))
90 : {
91 45 : return delegate->handleSendEvent(event);
92 : }
93 0 : return FALSE;
94 : }
95 :
96 225 : gboolean rialto_mse_base_sink_event(GstPad *pad, GstObject *parent, GstEvent *event)
97 : {
98 225 : if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(parent)))
99 : {
100 225 : return delegate->handleEvent(pad, parent, event);
101 : }
102 0 : return FALSE;
103 : }
104 :
105 36 : GstFlowReturn rialto_mse_base_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
106 : {
107 36 : if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(parent)))
108 : {
109 36 : return delegate->handleBuffer(buf);
110 : }
111 0 : return GST_FLOW_ERROR;
112 : }
113 :
114 42 : static gboolean rialto_mse_base_sink_query(GstElement *element, GstQuery *query)
115 : {
116 42 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(element);
117 42 : if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
118 : {
119 42 : std::optional<gboolean> result{delegate->handleQuery(query)};
120 42 : if (result.has_value())
121 : {
122 12 : return result.value();
123 : }
124 30 : GstElement *parent = GST_ELEMENT(&sink->parent);
125 30 : return GST_ELEMENT_CLASS(parent_class)->query(parent, query);
126 42 : }
127 0 : return FALSE;
128 : }
129 :
130 994 : static GstStateChangeReturn rialto_mse_base_sink_change_state(GstElement *element, GstStateChange transition)
131 : {
132 994 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(element);
133 994 : if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
134 : {
135 993 : GstStateChangeReturn status = delegate->changeState(transition);
136 993 : if (GST_STATE_CHANGE_FAILURE != status)
137 : {
138 986 : if (GST_STATE_CHANGE_READY_TO_NULL == transition)
139 : {
140 304 : sink->priv->m_delegate.reset();
141 : }
142 986 : GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
143 986 : if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
144 : {
145 0 : GST_WARNING_OBJECT(sink, "State change failed");
146 0 : return result;
147 : }
148 986 : else if (result == GST_STATE_CHANGE_ASYNC)
149 : {
150 0 : return GST_STATE_CHANGE_ASYNC;
151 : }
152 : }
153 993 : return status;
154 994 : }
155 1 : return GST_STATE_CHANGE_FAILURE;
156 : }
157 :
158 60 : void rialto_mse_base_sink_handle_get_property(RialtoMSEBaseSink *sink, const IPlaybackDelegate::Property &property,
159 : GValue *value)
160 : {
161 60 : if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
162 : {
163 60 : delegate->getProperty(property, value);
164 : }
165 : else // Copy queued value if present
166 : {
167 0 : std::unique_lock lock{sink->priv->m_sinkMutex};
168 0 : if (sink->priv->m_queuedProperties.find(property) != sink->priv->m_queuedProperties.end())
169 : {
170 0 : g_value_copy(&sink->priv->m_queuedProperties[property], value);
171 : }
172 60 : }
173 : }
174 :
175 442 : void rialto_mse_base_sink_handle_set_property(RialtoMSEBaseSink *sink, const IPlaybackDelegate::Property &property,
176 : const GValue *value)
177 : {
178 442 : if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
179 : {
180 440 : delegate->setProperty(property, value);
181 : }
182 : else
183 : {
184 2 : std::unique_lock lock{sink->priv->m_sinkMutex};
185 2 : sink->priv->m_queuedProperties[property] = G_VALUE_INIT;
186 2 : g_value_init(&(sink->priv->m_queuedProperties[property]), G_VALUE_TYPE(value));
187 2 : g_value_copy(value, &(sink->priv->m_queuedProperties[property]));
188 444 : }
189 442 : }
190 :
191 11 : static void rialto_mse_base_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
192 : {
193 11 : switch (propId)
194 : {
195 1 : case PROP_IS_SINGLE_PATH_STREAM:
196 : // Set default value if it can't be acquired
197 1 : g_value_set_boolean(value, FALSE);
198 1 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object),
199 1 : IPlaybackDelegate::Property::IsSinglePathStream, value);
200 1 : break;
201 1 : case PROP_N_STREAMS:
202 : // Set default value if it can't be acquired
203 1 : g_value_set_int(value, 1);
204 1 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object),
205 1 : IPlaybackDelegate::Property::NumberOfStreams, value);
206 1 : break;
207 1 : case PROP_HAS_DRM:
208 : // Set default value if it can't be acquired
209 1 : g_value_set_boolean(value, TRUE);
210 1 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::HasDrm,
211 : value);
212 1 : break;
213 2 : case PROP_STATS:
214 2 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::Stats, value);
215 2 : break;
216 2 : case PROP_ENABLE_LAST_SAMPLE:
217 2 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object),
218 2 : IPlaybackDelegate::Property::EnableLastSample, value);
219 2 : break;
220 4 : case PROP_LAST_SAMPLE:
221 4 : rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::LastSample,
222 : value);
223 4 : break;
224 0 : default:
225 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
226 0 : break;
227 : }
228 11 : }
229 :
230 366 : static void rialto_mse_base_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
231 : {
232 366 : switch (propId)
233 : {
234 182 : case PROP_IS_SINGLE_PATH_STREAM:
235 182 : rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object),
236 182 : IPlaybackDelegate::Property::IsSinglePathStream, value);
237 182 : break;
238 182 : case PROP_N_STREAMS:
239 182 : rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object),
240 182 : IPlaybackDelegate::Property::NumberOfStreams, value);
241 182 : break;
242 1 : case PROP_HAS_DRM:
243 1 : rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::HasDrm,
244 : value);
245 1 : break;
246 1 : case PROP_ENABLE_LAST_SAMPLE:
247 1 : rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object),
248 1 : IPlaybackDelegate::Property::EnableLastSample, value);
249 1 : break;
250 0 : default:
251 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
252 0 : break;
253 : }
254 366 : }
255 :
256 1 : void rialto_mse_base_handle_rialto_server_sent_buffer_underflow(RialtoMSEBaseSink *sink)
257 : {
258 1 : GST_WARNING_OBJECT(sink, "Sending underflow signal");
259 : // send 2 last parameters just to be compatible with RDK's buffer-underflow-callback signal signature
260 1 : g_signal_emit(G_OBJECT(sink), g_signals[SIGNAL_UNDERFLOW], 0, 0, nullptr);
261 : }
262 :
263 1 : void rialto_mse_base_handle_rialto_server_sent_first_video_frame_received(RialtoMSEBaseSink *sink)
264 : {
265 1 : GST_INFO_OBJECT(sink, "Sending first frame received signal");
266 1 : g_signal_emit(G_OBJECT(sink), g_signals[SIGNAL_FIRST_VIDEO_FRAME_RECEIVED], 0, 0, nullptr);
267 : }
268 :
269 310 : bool rialto_mse_base_sink_initialise_sinkpad(RialtoMSEBaseSink *sink)
270 : {
271 : GstPadTemplate *pad_template =
272 310 : gst_element_class_get_pad_template(GST_ELEMENT_CLASS(G_OBJECT_GET_CLASS(sink)), "sink");
273 310 : if (!pad_template)
274 : {
275 0 : GST_ERROR_OBJECT(sink, "Could not find sink pad template");
276 0 : return false;
277 : }
278 :
279 310 : GstPad *sinkPad = gst_pad_new_from_template(pad_template, "sink");
280 310 : if (!sinkPad)
281 : {
282 0 : GST_ERROR_OBJECT(sink, "Could not create sinkpad");
283 0 : return false;
284 : }
285 :
286 310 : gst_element_add_pad(GST_ELEMENT_CAST(sink), sinkPad);
287 310 : sink->priv->m_sinkPad = sinkPad;
288 :
289 310 : return true;
290 : }
291 :
292 310 : static void rialto_mse_base_sink_init(RialtoMSEBaseSink *sink)
293 : {
294 310 : GST_INFO_OBJECT(sink, "Init: %" GST_PTR_FORMAT, sink);
295 310 : sink->priv = static_cast<RialtoMSEBaseSinkPrivate *>(rialto_mse_base_sink_get_instance_private(sink));
296 310 : new (sink->priv) RialtoMSEBaseSinkPrivate();
297 :
298 310 : GST_OBJECT_FLAG_SET(sink, GST_ELEMENT_FLAG_SINK);
299 : }
300 :
301 310 : static void rialto_mse_base_sink_finalize(GObject *object)
302 : {
303 310 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(object);
304 310 : RialtoMSEBaseSinkPrivate *priv = sink->priv;
305 310 : GST_INFO_OBJECT(sink, "Finalize: %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, sink, priv);
306 :
307 310 : priv->~RialtoMSEBaseSinkPrivate();
308 310 : GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
309 : }
310 :
311 1 : static void rialto_mse_base_sink_class_init(RialtoMSEBaseSinkClass *klass)
312 : {
313 : std::shared_ptr<firebolt::rialto::IClientLogHandler> logToGstHandler =
314 1 : std::make_shared<firebolt::rialto::LogToGstHandler>();
315 1 : if (!firebolt::rialto::IClientLogControlFactory::createFactory()->createClientLogControl().registerLogHandler(logToGstHandler,
316 : true))
317 : {
318 0 : GST_ERROR("Unable to preRegister log handler");
319 : }
320 :
321 1 : GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
322 1 : GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
323 :
324 1 : gst_element_class_set_metadata(elementClass, "Rialto MSE base sink", "Generic", "A sink for Rialto", "Sky");
325 :
326 1 : gobjectClass->finalize = rialto_mse_base_sink_finalize;
327 1 : gobjectClass->get_property = rialto_mse_base_sink_get_property;
328 1 : gobjectClass->set_property = rialto_mse_base_sink_set_property;
329 1 : elementClass->query = rialto_mse_base_sink_query;
330 1 : elementClass->send_event = rialto_mse_base_sink_send_event;
331 1 : elementClass->change_state = rialto_mse_base_sink_change_state;
332 :
333 1 : g_signals[SIGNAL_UNDERFLOW] = g_signal_new("buffer-underflow-callback", G_TYPE_FROM_CLASS(klass),
334 : (GSignalFlags)(G_SIGNAL_RUN_LAST), 0, nullptr, nullptr,
335 : g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT,
336 : G_TYPE_POINTER);
337 1 : g_signals[SIGNAL_FIRST_VIDEO_FRAME_RECEIVED] = g_signal_new("first-video-frame-callback", G_TYPE_FROM_CLASS(klass),
338 : (GSignalFlags)(G_SIGNAL_RUN_LAST), 0, nullptr, nullptr,
339 : g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2,
340 : G_TYPE_UINT, G_TYPE_POINTER);
341 1 : g_object_class_install_property(gobjectClass, PROP_IS_SINGLE_PATH_STREAM,
342 : g_param_spec_boolean("single-path-stream", "single path stream",
343 : "is single path stream", FALSE, GParamFlags(G_PARAM_READWRITE)));
344 :
345 1 : g_object_class_install_property(gobjectClass, PROP_N_STREAMS,
346 : g_param_spec_int("streams-number", "streams number", "streams number", 1, G_MAXINT,
347 : 1, GParamFlags(G_PARAM_READWRITE)));
348 :
349 1 : g_object_class_install_property(gobjectClass, PROP_HAS_DRM,
350 : g_param_spec_boolean("has-drm", "has drm", "has drm", TRUE,
351 : GParamFlags(G_PARAM_READWRITE)));
352 1 : g_object_class_install_property(gobjectClass, PROP_STATS,
353 : g_param_spec_pointer("stats", NULL, "pointer to a gst_structure",
354 : GParamFlags(G_PARAM_READABLE)));
355 :
356 1 : g_object_class_install_property(gobjectClass, PROP_ENABLE_LAST_SAMPLE,
357 : g_param_spec_boolean("enable-last-sample", "Enable Last Buffer",
358 : "Enable the last-sample property", FALSE,
359 : GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
360 :
361 1 : g_object_class_install_property(gobjectClass, PROP_LAST_SAMPLE,
362 : g_param_spec_boxed("last-sample", "Last Sample",
363 : "The last sample received in the sink", GST_TYPE_SAMPLE,
364 : GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
365 : }
|