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