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