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 233 : 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 155 : static std::shared_ptr<IPlaybackDelegate> rialto_web_audio_sink_get_delegate(RialtoWebAudioSink *sink)
58 : {
59 155 : std::unique_lock lock{sink->priv->m_sinkMutex};
60 155 : if (!sink->priv->m_delegate)
61 : {
62 0 : GST_ERROR_OBJECT(sink, "Sink delegate not initialized");
63 : }
64 310 : return sink->priv->m_delegate;
65 155 : }
66 :
67 7 : static void rialto_web_audio_sink_handle_get_property(RialtoWebAudioSink *sink,
68 : const IPlaybackDelegate::Property &property, GValue *value)
69 : {
70 7 : if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
71 : {
72 7 : 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 7 : }
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 : return FALSE;
108 : }
109 :
110 105 : static GstStateChangeReturn rialto_web_audio_sink_change_state(GstElement *element, GstStateChange transition)
111 : {
112 105 : RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(element);
113 105 : if (GST_STATE_CHANGE_NULL_TO_READY == transition)
114 : {
115 25 : GST_INFO_OBJECT(sink, "RialtoWebAudioSink state change to READY. Initializing delegate");
116 25 : rialto_web_audio_sink_initialise_delegate(sink, std::make_shared<PushModeAudioPlaybackDelegate>(element));
117 : }
118 105 : if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
119 : {
120 105 : GstStateChangeReturn status = delegate->changeState(transition);
121 105 : if (GST_STATE_CHANGE_FAILURE != status)
122 : {
123 102 : GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
124 102 : if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
125 : {
126 0 : GST_WARNING_OBJECT(sink, "State change failed");
127 0 : return result;
128 : }
129 102 : else if (result == GST_STATE_CHANGE_ASYNC)
130 : {
131 0 : return GST_STATE_CHANGE_ASYNC;
132 : }
133 : }
134 105 : return status;
135 : }
136 0 : return GST_STATE_CHANGE_FAILURE;
137 : }
138 :
139 20 : static gboolean rialto_web_audio_sink_event(GstPad *pad, GstObject *parent, GstEvent *event)
140 : {
141 20 : if (auto delegate = rialto_web_audio_sink_get_delegate(RIALTO_WEB_AUDIO_SINK(parent)))
142 : {
143 20 : return delegate->handleEvent(pad, parent, event);
144 : }
145 0 : return FALSE;
146 : }
147 :
148 7 : static void rialto_web_audio_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
149 : {
150 7 : switch (propId)
151 : {
152 1 : case PROP_TS_OFFSET:
153 : {
154 1 : rialto_web_audio_sink_handle_get_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::TsOffset,
155 : value);
156 : }
157 :
158 6 : case PROP_VOLUME:
159 : {
160 6 : g_value_set_double(value, kDefaultVolume);
161 6 : rialto_web_audio_sink_handle_get_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::Volume,
162 : value);
163 6 : break;
164 : }
165 :
166 1 : default:
167 : {
168 1 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
169 1 : break;
170 : }
171 : }
172 7 : }
173 :
174 7 : static void rialto_web_audio_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
175 : {
176 7 : switch (propId)
177 : {
178 1 : case PROP_TS_OFFSET:
179 : {
180 1 : rialto_web_audio_sink_handle_set_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::TsOffset,
181 : value);
182 1 : break;
183 : }
184 :
185 5 : case PROP_VOLUME:
186 : {
187 5 : rialto_web_audio_sink_handle_set_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::Volume,
188 : value);
189 5 : break;
190 : }
191 :
192 1 : default:
193 : {
194 1 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
195 1 : break;
196 : }
197 : }
198 7 : }
199 :
200 1 : static GstFlowReturn rialto_web_audio_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
201 : {
202 1 : if (auto delegate = rialto_web_audio_sink_get_delegate(RIALTO_WEB_AUDIO_SINK(parent)))
203 : {
204 1 : return delegate->handleBuffer(buf);
205 : }
206 0 : return GST_FLOW_ERROR;
207 : }
208 :
209 1 : static void rialto_web_audio_sink_setup_supported_caps(GstElementClass *elementClass)
210 : {
211 1 : GstCaps *caps = gst_caps_from_string("audio/x-raw");
212 1 : GstPadTemplate *sinktempl = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
213 1 : gst_element_class_add_pad_template(elementClass, sinktempl);
214 1 : gst_caps_unref(caps);
215 : }
216 :
217 25 : static bool rialto_web_audio_sink_initialise_sinkpad(RialtoWebAudioSink *sink)
218 : {
219 : GstPadTemplate *pad_template =
220 25 : gst_element_class_get_pad_template(GST_ELEMENT_CLASS(G_OBJECT_GET_CLASS(sink)), "sink");
221 25 : if (!pad_template)
222 : {
223 0 : GST_ERROR_OBJECT(sink, "Could not find sink pad template");
224 0 : return false;
225 : }
226 :
227 25 : GstPad *sinkPad = gst_pad_new_from_template(pad_template, "sink");
228 25 : if (!sinkPad)
229 : {
230 0 : GST_ERROR_OBJECT(sink, "Could not create sinkpad");
231 0 : return false;
232 : }
233 :
234 25 : gst_element_add_pad(GST_ELEMENT_CAST(sink), sinkPad);
235 :
236 25 : gst_pad_set_event_function(sinkPad, rialto_web_audio_sink_event);
237 25 : gst_pad_set_chain_function(sinkPad, rialto_web_audio_sink_chain);
238 :
239 25 : return true;
240 : }
241 :
242 25 : static void rialto_web_audio_sink_init(RialtoWebAudioSink *sink)
243 : {
244 25 : GST_INFO_OBJECT(sink, "Init: %" GST_PTR_FORMAT, sink);
245 25 : sink->priv = static_cast<RialtoWebAudioSinkPrivate *>(rialto_web_audio_sink_get_instance_private(sink));
246 25 : new (sink->priv) RialtoWebAudioSinkPrivate();
247 :
248 25 : GST_OBJECT_FLAG_SET(sink, GST_ELEMENT_FLAG_SINK);
249 25 : if (!rialto_web_audio_sink_initialise_sinkpad(sink))
250 : {
251 0 : GST_ERROR_OBJECT(sink, "Failed to initialise AUDIO sink. Sink pad initialisation failed.");
252 0 : return;
253 : }
254 : }
255 :
256 25 : static void rialto_web_audio_sink_finalize(GObject *object)
257 : {
258 25 : RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(object);
259 25 : RialtoWebAudioSinkPrivate *priv = sink->priv;
260 25 : GST_INFO_OBJECT(sink, "Finalize: %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, sink, priv);
261 :
262 25 : priv->~RialtoWebAudioSinkPrivate();
263 :
264 25 : GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
265 : }
266 :
267 1 : static void rialto_web_audio_sink_class_init(RialtoWebAudioSinkClass *klass)
268 : {
269 1 : GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
270 1 : GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
271 :
272 1 : gst_element_class_set_metadata(elementClass, "Rialto Web Audio sink", "Generic", "A sink for Rialto Web Audio",
273 : "Sky");
274 :
275 1 : gobjectClass->finalize = rialto_web_audio_sink_finalize;
276 1 : gobjectClass->get_property = rialto_web_audio_sink_get_property;
277 1 : gobjectClass->set_property = rialto_web_audio_sink_set_property;
278 :
279 1 : elementClass->change_state = rialto_web_audio_sink_change_state;
280 1 : elementClass->send_event = rialto_web_audio_sink_send_event;
281 :
282 1 : g_object_class_install_property(gobjectClass, PROP_TS_OFFSET,
283 : g_param_spec_int64("ts-offset",
284 : "ts-offset", "Not supported, RialtoWebAudioSink does not require the synchronisation of sources",
285 : G_MININT64, G_MAXINT64, 0,
286 : GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
287 :
288 1 : g_object_class_install_property(gobjectClass, PROP_VOLUME,
289 : g_param_spec_double("volume", "Volume", "Volume of this stream", 0, 1.0,
290 : kDefaultVolume,
291 : GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
292 :
293 1 : rialto_web_audio_sink_setup_supported_caps(elementClass);
294 :
295 1 : gst_element_class_set_details_simple(elementClass, "Rialto Web Audio Sink", "Decoder/Audio/Sink/Audio",
296 : "Communicates with Rialto Server", "Sky");
297 : }
|