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