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 : #include <mutex>
19 :
20 : #include <gst/audio/audio.h>
21 : #include <gst/gst.h>
22 :
23 : #include "GStreamerMSEUtils.h"
24 : #include "IMediaPipelineCapabilities.h"
25 : #include "PullModeAudioPlaybackDelegate.h"
26 : #include "PushModeAudioPlaybackDelegate.h"
27 : #include "RialtoGStreamerMSEAudioSink.h"
28 : #include "RialtoGStreamerMSEBaseSinkPrivate.h"
29 :
30 : using namespace firebolt::rialto::client;
31 :
32 : GST_DEBUG_CATEGORY_STATIC(RialtoMSEAudioSinkDebug);
33 : #define GST_CAT_DEFAULT RialtoMSEAudioSinkDebug
34 :
35 : #define rialto_mse_audio_sink_parent_class parent_class
36 3 : G_DEFINE_TYPE_WITH_CODE(RialtoMSEAudioSink, rialto_mse_audio_sink, RIALTO_TYPE_MSE_BASE_SINK,
37 : G_IMPLEMENT_INTERFACE(GST_TYPE_STREAM_VOLUME, NULL)
38 : GST_DEBUG_CATEGORY_INIT(RialtoMSEAudioSinkDebug, "rialtomseaudiosink", 0,
39 : "rialto mse audio sink"));
40 :
41 : enum
42 : {
43 : PROP_0,
44 : PROP_VOLUME,
45 : PROP_MUTE,
46 : PROP_GAP,
47 : PROP_LOW_LATENCY,
48 : PROP_SYNC,
49 : PROP_SYNC_OFF,
50 : PROP_STREAM_SYNC_MODE,
51 : PROP_AUDIO_FADE,
52 : PROP_FADE_VOLUME,
53 : PROP_LIMIT_BUFFERING_MS,
54 : PROP_USE_BUFFERING,
55 : PROP_ASYNC,
56 : PROP_WEBAUDIO,
57 : PROP_LAST
58 : };
59 :
60 752 : static GstStateChangeReturn rialto_mse_audio_sink_change_state(GstElement *element, GstStateChange transition)
61 : {
62 752 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(element);
63 752 : if (GST_STATE_CHANGE_NULL_TO_READY == transition)
64 : {
65 225 : if (PlaybackMode::Pull == sink->priv->m_playbackMode)
66 : {
67 203 : GST_INFO_OBJECT(sink, "RialtoMSEAudioSink state change to READY. Initializing Pull Mode delegate");
68 203 : auto delegate = std::make_shared<PullModeAudioPlaybackDelegate>(element);
69 203 : delegate->createControlBackend();
70 203 : rialto_mse_base_sink_initialise_delegate(sink, delegate);
71 : }
72 : else // Push playback mode
73 : {
74 22 : GST_INFO_OBJECT(sink, "RialtoMSEAudioSink state change to READY. Initializing Push Mode delegate");
75 22 : rialto_mse_base_sink_initialise_delegate(sink, std::make_shared<PushModeAudioPlaybackDelegate>(element));
76 : }
77 : }
78 :
79 752 : GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
80 752 : if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
81 : {
82 6 : GST_WARNING_OBJECT(sink, "State change failed");
83 6 : return result;
84 : }
85 :
86 746 : return result;
87 : }
88 :
89 30 : static void rialto_mse_audio_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
90 : {
91 30 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(object);
92 30 : switch (propId)
93 : {
94 8 : case PROP_VOLUME:
95 : {
96 8 : g_value_set_double(value, kDefaultVolume);
97 8 : rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::Volume, value);
98 8 : break;
99 : }
100 3 : case PROP_MUTE:
101 : {
102 3 : g_value_set_boolean(value, kDefaultMute);
103 3 : rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::Mute, value);
104 3 : break;
105 : }
106 4 : case PROP_SYNC:
107 : {
108 4 : g_value_set_boolean(value, kDefaultSync);
109 4 : rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::Sync, value);
110 4 : break;
111 : }
112 4 : case PROP_STREAM_SYNC_MODE:
113 : {
114 4 : g_value_set_int(value, kDefaultStreamSyncMode);
115 4 : rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::StreamSyncMode, value);
116 4 : break;
117 : }
118 2 : case PROP_FADE_VOLUME:
119 : {
120 2 : g_value_set_uint(value, kDefaultFadeVolume);
121 2 : rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::FadeVolume, value);
122 2 : break;
123 : }
124 3 : case PROP_LIMIT_BUFFERING_MS:
125 : {
126 3 : g_value_set_uint(value, kDefaultBufferingLimit);
127 3 : rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::LimitBufferingMs, value);
128 3 : break;
129 : }
130 3 : case PROP_USE_BUFFERING:
131 : {
132 3 : g_value_set_boolean(value, kDefaultUseBuffering);
133 3 : rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::UseBuffering, value);
134 3 : break;
135 : }
136 1 : case PROP_ASYNC:
137 : {
138 1 : g_value_set_boolean(value, TRUE);
139 1 : rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::Async, value);
140 1 : break;
141 : }
142 1 : case PROP_WEBAUDIO:
143 : {
144 1 : g_value_set_boolean(value, (sink->priv->m_playbackMode == PlaybackMode::Push));
145 1 : break;
146 : }
147 1 : default:
148 : {
149 1 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
150 1 : break;
151 : }
152 : }
153 30 : }
154 :
155 68 : static void rialto_mse_audio_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
156 : {
157 68 : RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(object);
158 68 : switch (propId)
159 : {
160 8 : case PROP_VOLUME:
161 : {
162 8 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Volume, value);
163 8 : break;
164 : }
165 3 : case PROP_MUTE:
166 : {
167 3 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Mute, value);
168 3 : break;
169 : }
170 2 : case PROP_GAP:
171 : {
172 2 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Gap, value);
173 2 : break;
174 : }
175 5 : case PROP_LOW_LATENCY:
176 : {
177 5 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::LowLatency, value);
178 5 : break;
179 : }
180 5 : case PROP_SYNC:
181 : {
182 5 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Sync, value);
183 5 : break;
184 : }
185 5 : case PROP_SYNC_OFF:
186 : {
187 5 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::SyncOff, value);
188 5 : break;
189 : }
190 5 : case PROP_STREAM_SYNC_MODE:
191 : {
192 5 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::StreamSyncMode, value);
193 5 : break;
194 : }
195 4 : case PROP_AUDIO_FADE:
196 : {
197 4 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::AudioFade, value);
198 4 : break;
199 : }
200 3 : case PROP_LIMIT_BUFFERING_MS:
201 : {
202 3 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::LimitBufferingMs, value);
203 3 : break;
204 : }
205 3 : case PROP_USE_BUFFERING:
206 : {
207 3 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::UseBuffering, value);
208 3 : break;
209 : }
210 1 : case PROP_ASYNC:
211 : {
212 1 : rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Async, value);
213 1 : break;
214 : }
215 23 : case PROP_WEBAUDIO:
216 : {
217 23 : if (GST_STATE(sink) > GST_STATE_NULL)
218 : {
219 1 : GST_ERROR_OBJECT(object, "Playback mode set too late - sink is not in NULL state");
220 1 : break;
221 : }
222 22 : if (TRUE == g_value_get_boolean(value))
223 : {
224 22 : sink->priv->m_playbackMode = PlaybackMode::Push;
225 : }
226 : else
227 : {
228 0 : sink->priv->m_playbackMode = PlaybackMode::Pull;
229 : }
230 22 : break;
231 : }
232 1 : default:
233 : {
234 1 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
235 1 : break;
236 : }
237 : }
238 68 : }
239 :
240 230 : static void rialto_mse_audio_sink_init(RialtoMSEAudioSink *sink)
241 : {
242 230 : RialtoMSEBaseSinkPrivate *priv = sink->parent.priv;
243 :
244 230 : if (!rialto_mse_base_sink_initialise_sinkpad(RIALTO_MSE_BASE_SINK(sink)))
245 : {
246 0 : GST_ERROR_OBJECT(sink, "Failed to initialise AUDIO sink. Sink pad initialisation failed.");
247 0 : return;
248 : }
249 :
250 230 : gst_pad_set_chain_function(priv->m_sinkPad, rialto_mse_base_sink_chain);
251 230 : gst_pad_set_event_function(priv->m_sinkPad, rialto_mse_base_sink_event);
252 : }
253 :
254 1 : static void rialto_mse_audio_sink_class_init(RialtoMSEAudioSinkClass *klass)
255 : {
256 1 : GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
257 1 : GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
258 1 : gobjectClass->get_property = rialto_mse_audio_sink_get_property;
259 1 : gobjectClass->set_property = rialto_mse_audio_sink_set_property;
260 1 : elementClass->change_state = rialto_mse_audio_sink_change_state;
261 :
262 1 : g_object_class_install_property(gobjectClass, PROP_VOLUME,
263 : g_param_spec_double("volume", "Volume", "Volume of this stream", 0, 1.0,
264 : kDefaultVolume,
265 : GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
266 :
267 1 : g_object_class_install_property(gobjectClass, PROP_MUTE,
268 : g_param_spec_boolean("mute", "Mute", "Mute status of this stream", kDefaultMute,
269 : GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
270 :
271 1 : g_object_class_install_property(gobjectClass, PROP_GAP,
272 : g_param_spec_boxed("gap", "Gap", "Audio Gap", GST_TYPE_STRUCTURE,
273 : (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
274 :
275 1 : g_object_class_install_property(gobjectClass, PROP_USE_BUFFERING,
276 : g_param_spec_boolean("use-buffering",
277 : "Use buffering", "Emit GST_MESSAGE_BUFFERING based on low-/high-percent thresholds",
278 : kDefaultUseBuffering, G_PARAM_READWRITE));
279 1 : g_object_class_install_property(gobjectClass, PROP_ASYNC,
280 : g_param_spec_boolean("async", "Async", "Asynchronous mode", FALSE, G_PARAM_READWRITE));
281 1 : g_object_class_install_property(gobjectClass, PROP_WEBAUDIO,
282 : g_param_spec_boolean("web-audio",
283 : "Webaudio mode", "Enable webaudio mode. Property should be set before NULL->READY transition",
284 : FALSE, G_PARAM_READWRITE));
285 :
286 : std::unique_ptr<firebolt::rialto::IMediaPipelineCapabilities> mediaPlayerCapabilities =
287 1 : firebolt::rialto::IMediaPipelineCapabilitiesFactory::createFactory()->createMediaPipelineCapabilities();
288 1 : if (mediaPlayerCapabilities)
289 : {
290 : std::vector<std::string> supportedMimeTypes =
291 1 : mediaPlayerCapabilities->getSupportedMimeTypes(firebolt::rialto::MediaSourceType::AUDIO);
292 :
293 1 : rialto_mse_sink_setup_supported_caps(elementClass, supportedMimeTypes);
294 :
295 2 : const std::string kLowLatencyPropertyName{"low-latency"};
296 2 : const std::string kSyncPropertyName{"sync"};
297 2 : const std::string kSyncOffPropertyName{"sync-off"};
298 2 : const std::string kStreamSyncModePropertyName{"stream-sync-mode"};
299 2 : const std::string kAudioFadePropertyName{"audio-fade"};
300 2 : const std::string kFadeVolumePropertyName{"fade-volume"};
301 1 : const std::string kBufferingLimitPropertyName{"limit-buffering-ms"};
302 : const std::vector<std::string> kPropertyNamesToSearch{kLowLatencyPropertyName, kSyncPropertyName,
303 : kSyncOffPropertyName, kStreamSyncModePropertyName,
304 : kBufferingLimitPropertyName, kAudioFadePropertyName,
305 9 : kFadeVolumePropertyName};
306 : std::vector<std::string> supportedProperties{
307 1 : mediaPlayerCapabilities->getSupportedProperties(firebolt::rialto::MediaSourceType::AUDIO,
308 1 : kPropertyNamesToSearch)};
309 :
310 8 : for (auto it = supportedProperties.begin(); it != supportedProperties.end(); ++it)
311 : {
312 7 : if (kLowLatencyPropertyName == *it)
313 : {
314 1 : g_object_class_install_property(gobjectClass, PROP_LOW_LATENCY,
315 : g_param_spec_boolean(kLowLatencyPropertyName.c_str(),
316 : "low latency", "Turn on low latency mode, for use with gaming (no audio decoding, no a/v sync)",
317 : kDefaultLowLatency, GParamFlags(G_PARAM_WRITABLE)));
318 : }
319 6 : else if (kSyncPropertyName == *it)
320 : {
321 1 : g_object_class_install_property(gobjectClass, PROP_SYNC,
322 : g_param_spec_boolean(kSyncPropertyName.c_str(), "sync", "Clock sync",
323 : kDefaultSync, GParamFlags(G_PARAM_READWRITE)));
324 : }
325 5 : else if (kSyncOffPropertyName == *it)
326 : {
327 1 : g_object_class_install_property(gobjectClass, PROP_SYNC_OFF,
328 : g_param_spec_boolean(kSyncOffPropertyName.c_str(),
329 : "sync off", "Turn on free running audio. Must be set before pipeline is PLAYING state.",
330 : kDefaultSyncOff, GParamFlags(G_PARAM_WRITABLE)));
331 : }
332 4 : else if (kStreamSyncModePropertyName == *it)
333 : {
334 1 : g_object_class_install_property(gobjectClass, PROP_STREAM_SYNC_MODE,
335 : g_param_spec_int(kStreamSyncModePropertyName.c_str(),
336 : "stream sync mode", "1 - Frame to decode frame will immediately proceed next frame sync, 0 - Frame decoded with no frame sync",
337 : 0, G_MAXINT, kDefaultStreamSyncMode,
338 : GParamFlags(G_PARAM_READWRITE)));
339 : }
340 3 : else if (kAudioFadePropertyName == *it)
341 : {
342 1 : g_object_class_install_property(gobjectClass, PROP_AUDIO_FADE,
343 : g_param_spec_string(kAudioFadePropertyName.c_str(),
344 : "audio fade", "Start audio fade (vol[0-100],duration ms,easetype[(L)inear,Cubic(I)n,Cubic(O)ut])",
345 : kDefaultAudioFade, GParamFlags(G_PARAM_WRITABLE)));
346 : }
347 2 : else if (kFadeVolumePropertyName == *it)
348 : {
349 1 : g_object_class_install_property(gobjectClass, PROP_FADE_VOLUME,
350 : g_param_spec_uint(kFadeVolumePropertyName.c_str(), "fade volume",
351 : "Get current fade volume", 0, 100, kDefaultFadeVolume,
352 : G_PARAM_READABLE));
353 : }
354 1 : else if (kBufferingLimitPropertyName == *it)
355 : {
356 1 : constexpr uint32_t kMaxValue{20000};
357 1 : g_object_class_install_property(gobjectClass, PROP_LIMIT_BUFFERING_MS,
358 : g_param_spec_uint("limit-buffering-ms",
359 : "limit buffering ms", "Set millisecond threshold used if limit_buffering is set. Changing this value does not enable/disable limit_buffering",
360 : 0, kMaxValue, kDefaultBufferingLimit,
361 : G_PARAM_READWRITE));
362 : }
363 : else
364 : {
365 0 : GST_ERROR("Unexpected property %s returned from rialto", it->c_str());
366 : }
367 : }
368 1 : }
369 : else
370 : {
371 0 : GST_ERROR("Failed to get supported mime types for AUDIO");
372 : }
373 :
374 1 : gst_element_class_set_details_simple(elementClass, "Rialto Audio Sink", "Decoder/Audio/Sink/Audio",
375 : "Communicates with Rialto Server", "Sky");
376 2 : }
|