Line data Source code
1 : /*
2 : * Copyright (C) 2023 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 : #include <gst/gst.h>
20 :
21 : #include "Constants.h"
22 : #include "PushModeAudioPlaybackDelegate.h"
23 : #include "RialtoGStreamerWebAudioSink.h"
24 :
25 : using namespace firebolt::rialto::client;
26 :
27 : GST_DEBUG_CATEGORY_STATIC(RialtoWebAudioSinkDebug);
28 : #define GST_CAT_DEFAULT RialtoWebAudioSinkDebug
29 :
30 : #define rialto_web_audio_sink_parent_class parent_class
31 232 : G_DEFINE_TYPE_WITH_CODE(RialtoWebAudioSink, rialto_web_audio_sink, GST_TYPE_ELEMENT,
32 : G_ADD_PRIVATE(RialtoWebAudioSink)
33 : GST_DEBUG_CATEGORY_INIT(RialtoWebAudioSinkDebug, "rialtowebaudiosink", 0,
34 : "rialto web audio sink"));
35 : enum
36 : {
37 : PROP_0,
38 : PROP_TS_OFFSET,
39 : PROP_VOLUME,
40 : PROP_LAST
41 : };
42 :
43 25 : void rialto_web_audio_sink_initialise_delegate(RialtoWebAudioSink *sink,
44 : const std::shared_ptr<IPlaybackDelegate> &delegate)
45 : {
46 25 : std::unique_lock lock{sink->priv->m_sinkMutex};
47 25 : sink->priv->m_delegate = delegate;
48 :
49 25 : for (auto &[type, value] : sink->priv->m_queuedProperties)
50 : {
51 0 : delegate->setProperty(type, &value);
52 0 : g_value_unset(&value);
53 : }
54 25 : sink->priv->m_queuedProperties.clear();
55 : }
56 :
57 154 : static std::shared_ptr<IPlaybackDelegate> rialto_web_audio_sink_get_delegate(RialtoWebAudioSink *sink)
58 : {
59 154 : std::unique_lock lock{sink->priv->m_sinkMutex};
60 154 : if (!sink->priv->m_delegate)
61 : {
62 0 : GST_ERROR_OBJECT(sink, "Sink delegate not initialized");
63 : }
64 308 : return sink->priv->m_delegate;
65 154 : }
66 :
67 6 : static void rialto_web_audio_sink_handle_get_property(RialtoWebAudioSink *sink,
68 : const IPlaybackDelegate::Property &property, GValue *value)
69 : {
70 6 : if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
71 : {
72 6 : delegate->getProperty(property, value);
73 : }
74 : else // Copy queued value if present
75 : {
76 0 : std::unique_lock lock{sink->priv->m_sinkMutex};
77 0 : if (sink->priv->m_queuedProperties.find(property) != sink->priv->m_queuedProperties.end())
78 : {
79 0 : g_value_copy(&sink->priv->m_queuedProperties[property], value);
80 : }
81 6 : }
82 : }
83 :
84 6 : static void rialto_web_audio_sink_handle_set_property(RialtoWebAudioSink *sink,
85 : const IPlaybackDelegate::Property &property, const GValue *value)
86 : {
87 6 : if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
88 : {
89 6 : delegate->setProperty(property, value);
90 : }
91 : else
92 : {
93 0 : std::unique_lock lock{sink->priv->m_sinkMutex};
94 0 : sink->priv->m_queuedProperties[property] = G_VALUE_INIT;
95 0 : g_value_init(&(sink->priv->m_queuedProperties[property]), G_VALUE_TYPE(value));
96 0 : g_value_copy(value, &(sink->priv->m_queuedProperties[property]));
97 6 : }
98 : }
99 :
100 16 : static gboolean rialto_web_audio_sink_send_event(GstElement *element, GstEvent *event)
101 : {
102 16 : RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(element);
103 16 : if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
104 : {
105 16 : return delegate->handleSendEvent(event);
106 : }
107 0 : gst_event_unref(event);
108 0 : return FALSE;
109 : }
110 :
111 105 : static GstStateChangeReturn rialto_web_audio_sink_change_state(GstElement *element, GstStateChange transition)
112 : {
113 105 : RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(element);
114 105 : if (GST_STATE_CHANGE_NULL_TO_READY == transition)
115 : {
116 25 : GST_INFO_OBJECT(sink, "RialtoWebAudioSink state change to READY. Initializing delegate");
117 25 : rialto_web_audio_sink_initialise_delegate(sink, std::make_shared<PushModeAudioPlaybackDelegate>(element));
118 : }
119 105 : if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
120 : {
121 105 : GstStateChangeReturn status = delegate->changeState(transition);
122 105 : if (GST_STATE_CHANGE_FAILURE != status)
123 : {
124 102 : GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
125 102 : if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
126 : {
127 0 : GST_WARNING_OBJECT(sink, "State change failed");
128 0 : return result;
129 : }
130 102 : else if (result == GST_STATE_CHANGE_ASYNC)
131 : {
132 0 : return GST_STATE_CHANGE_ASYNC;
133 : }
134 : }
135 105 : return status;
136 : }
137 0 : return GST_STATE_CHANGE_FAILURE;
138 : }
139 :
140 20 : static gboolean rialto_web_audio_sink_event(GstPad *pad, GstObject *parent, GstEvent *event)
141 : {
142 20 : if (auto delegate = rialto_web_audio_sink_get_delegate(RIALTO_WEB_AUDIO_SINK(parent)))
143 : {
144 20 : return delegate->handleEvent(pad, parent, event);
145 : }
146 0 : gst_event_unref(event);
147 0 : return FALSE;
148 : }
149 :
150 7 : static void rialto_web_audio_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
151 : {
152 7 : switch (propId)
153 : {
154 1 : case PROP_TS_OFFSET:
155 : {
156 1 : rialto_web_audio_sink_handle_get_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::TsOffset,
157 : value);
158 1 : break;
159 : }
160 :
161 5 : case PROP_VOLUME:
162 : {
163 5 : g_value_set_double(value, kDefaultVolume);
164 5 : rialto_web_audio_sink_handle_get_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::Volume,
165 : value);
166 5 : break;
167 : }
168 :
169 1 : default:
170 : {
171 1 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
172 1 : break;
173 : }
174 : }
175 7 : }
176 :
177 7 : static void rialto_web_audio_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
178 : {
179 7 : switch (propId)
180 : {
181 1 : case PROP_TS_OFFSET:
182 : {
183 1 : rialto_web_audio_sink_handle_set_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::TsOffset,
184 : value);
185 1 : break;
186 : }
187 :
188 5 : case PROP_VOLUME:
189 : {
190 5 : rialto_web_audio_sink_handle_set_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::Volume,
191 : value);
192 5 : break;
193 : }
194 :
195 1 : default:
196 : {
197 1 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
198 1 : break;
199 : }
200 : }
201 7 : }
202 :
203 1 : static GstFlowReturn rialto_web_audio_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
204 : {
205 1 : if (auto delegate = rialto_web_audio_sink_get_delegate(RIALTO_WEB_AUDIO_SINK(parent)))
206 : {
207 1 : return delegate->handleBuffer(buf);
208 : }
209 0 : gst_buffer_unref(buf);
210 0 : return GST_FLOW_ERROR;
211 : }
212 :
213 1 : static void rialto_web_audio_sink_setup_supported_caps(GstElementClass *elementClass)
214 : {
215 1 : GstCaps *caps = gst_caps_from_string("audio/x-raw");
216 1 : GstPadTemplate *sinktempl = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
217 1 : gst_element_class_add_pad_template(elementClass, sinktempl);
218 1 : gst_caps_unref(caps);
219 : }
220 :
221 25 : static bool rialto_web_audio_sink_initialise_sinkpad(RialtoWebAudioSink *sink)
222 : {
223 : GstPadTemplate *pad_template =
224 25 : gst_element_class_get_pad_template(GST_ELEMENT_CLASS(G_OBJECT_GET_CLASS(sink)), "sink");
225 25 : if (!pad_template)
226 : {
227 0 : GST_ERROR_OBJECT(sink, "Could not find sink pad template");
228 0 : return false;
229 : }
230 :
231 25 : GstPad *sinkPad = gst_pad_new_from_template(pad_template, "sink");
232 25 : if (!sinkPad)
233 : {
234 0 : GST_ERROR_OBJECT(sink, "Could not create sinkpad");
235 0 : return false;
236 : }
237 :
238 25 : gst_element_add_pad(GST_ELEMENT_CAST(sink), sinkPad);
239 :
240 25 : gst_pad_set_event_function(sinkPad, rialto_web_audio_sink_event);
241 25 : gst_pad_set_chain_function(sinkPad, rialto_web_audio_sink_chain);
242 :
243 25 : return true;
244 : }
245 :
246 25 : static void rialto_web_audio_sink_init(RialtoWebAudioSink *sink)
247 : {
248 25 : GST_INFO_OBJECT(sink, "Init: %" GST_PTR_FORMAT, sink);
249 25 : sink->priv = static_cast<RialtoWebAudioSinkPrivate *>(rialto_web_audio_sink_get_instance_private(sink));
250 25 : new (sink->priv) RialtoWebAudioSinkPrivate();
251 :
252 25 : GST_OBJECT_FLAG_SET(sink, GST_ELEMENT_FLAG_SINK);
253 25 : if (!rialto_web_audio_sink_initialise_sinkpad(sink))
254 : {
255 0 : GST_ERROR_OBJECT(sink, "Failed to initialise AUDIO sink. Sink pad initialisation failed.");
256 0 : return;
257 : }
258 : }
259 :
260 25 : static void rialto_web_audio_sink_finalize(GObject *object)
261 : {
262 25 : RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(object);
263 25 : RialtoWebAudioSinkPrivate *priv = sink->priv;
264 25 : GST_INFO_OBJECT(sink, "Finalize: %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, sink, priv);
265 :
266 25 : priv->~RialtoWebAudioSinkPrivate();
267 :
268 25 : GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
269 : }
270 :
271 1 : static void rialto_web_audio_sink_class_init(RialtoWebAudioSinkClass *klass)
272 : {
273 1 : GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
274 1 : GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
275 :
276 1 : gst_element_class_set_metadata(elementClass, "Rialto Web Audio sink", "Generic", "A sink for Rialto Web Audio",
277 : "Sky");
278 :
279 1 : gobjectClass->finalize = rialto_web_audio_sink_finalize;
280 1 : gobjectClass->get_property = rialto_web_audio_sink_get_property;
281 1 : gobjectClass->set_property = rialto_web_audio_sink_set_property;
282 :
283 1 : elementClass->change_state = rialto_web_audio_sink_change_state;
284 1 : elementClass->send_event = rialto_web_audio_sink_send_event;
285 :
286 1 : g_object_class_install_property(gobjectClass, PROP_TS_OFFSET,
287 : g_param_spec_int64("ts-offset",
288 : "ts-offset", "Not supported, RialtoWebAudioSink does not require the synchronisation of sources",
289 : G_MININT64, G_MAXINT64, 0,
290 : GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
291 :
292 1 : g_object_class_install_property(gobjectClass, PROP_VOLUME,
293 : g_param_spec_double("volume", "Volume", "Volume of this stream", 0, 1.0,
294 : kDefaultVolume,
295 : GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
296 :
297 1 : rialto_web_audio_sink_setup_supported_caps(elementClass);
298 :
299 1 : gst_element_class_set_details_simple(elementClass, "Rialto Web Audio Sink", "Decoder/Audio/Sink/Audio",
300 : "Communicates with Rialto Server", "Sky");
301 : }
|