Line data Source code
1 : /*
2 : * Copyright (C) 2025 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 "PullModeAudioPlaybackDelegate.h"
20 : #include "GStreamerMSEUtils.h"
21 : #include "GstreamerCatLog.h"
22 : #include <gst/audio/audio.h>
23 : #include <gst/gst.h>
24 : #include <gst/pbutils/pbutils.h>
25 :
26 : #define GST_CAT_DEFAULT rialtoGStreamerCat
27 :
28 : namespace
29 : {
30 3 : firebolt::rialto::EaseType convertCharToEaseType(char easeTypeChar)
31 : {
32 3 : switch (easeTypeChar)
33 : {
34 1 : case 'L':
35 1 : return firebolt::rialto::EaseType::EASE_LINEAR;
36 1 : case 'I':
37 1 : return firebolt::rialto::EaseType::EASE_IN_CUBIC;
38 1 : case 'O':
39 1 : return firebolt::rialto::EaseType::EASE_OUT_CUBIC;
40 0 : default:
41 0 : return firebolt::rialto::EaseType::EASE_LINEAR;
42 : }
43 : }
44 : } // namespace
45 :
46 185 : PullModeAudioPlaybackDelegate::PullModeAudioPlaybackDelegate(GstElement *sink) : PullModePlaybackDelegate(sink)
47 : {
48 185 : m_mediaSourceType = firebolt::rialto::MediaSourceType::AUDIO;
49 : }
50 :
51 589 : GstStateChangeReturn PullModeAudioPlaybackDelegate::changeState(GstStateChange transition)
52 : {
53 589 : switch (transition)
54 : {
55 103 : case GST_STATE_CHANGE_READY_TO_PAUSED:
56 : {
57 103 : if (!attachToMediaClientAndSetStreamsNumber())
58 : {
59 2 : return GST_STATE_CHANGE_FAILURE;
60 : }
61 :
62 101 : std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
63 101 : if (!client)
64 : {
65 0 : GST_ERROR_OBJECT(m_sink, "MediaPlayerClient is nullptr");
66 0 : return GST_STATE_CHANGE_FAILURE;
67 : }
68 101 : if (m_isVolumeQueued)
69 : {
70 1 : client->setVolume(m_targetVolume, kDefaultVolumeDuration, kDefaultEaseType);
71 1 : m_isVolumeQueued = false;
72 : }
73 101 : if (m_isAudioFadeQueued)
74 : {
75 : AudioFadeConfig audioFadeConfig;
76 : {
77 1 : std::lock_guard<std::mutex> lock(m_audioFadeConfigMutex);
78 1 : audioFadeConfig = m_audioFadeConfig;
79 : }
80 1 : client->setVolume(audioFadeConfig.volume, audioFadeConfig.duration, audioFadeConfig.easeType);
81 1 : m_isAudioFadeQueued = false;
82 : }
83 101 : break;
84 : }
85 486 : default:
86 486 : break;
87 : }
88 587 : return PullModePlaybackDelegate::changeState(transition);
89 : }
90 :
91 137 : gboolean PullModeAudioPlaybackDelegate::handleEvent(GstEvent *event)
92 : {
93 137 : switch (GST_EVENT_TYPE(event))
94 : {
95 98 : case GST_EVENT_CAPS:
96 : {
97 98 : GstCaps *caps{nullptr};
98 98 : gst_event_parse_caps(event, &caps);
99 98 : if (m_sourceAttached)
100 : {
101 2 : GST_INFO_OBJECT(m_sink, "Source already attached. Skip calling attachSource");
102 2 : break;
103 : }
104 :
105 96 : GST_INFO_OBJECT(m_sink, "Attaching AUDIO source with caps %" GST_PTR_FORMAT, caps);
106 :
107 96 : std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource> asource = createMediaSource(caps);
108 96 : if (asource)
109 : {
110 95 : std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
111 95 : if ((!client) || (!client->attachSource(asource, RIALTO_MSE_BASE_SINK(m_sink))))
112 : {
113 1 : GST_ERROR_OBJECT(m_sink, "Failed to attach AUDIO source");
114 : }
115 : else
116 : {
117 94 : m_sourceAttached = true;
118 :
119 94 : if (m_isMuteQueued)
120 : {
121 1 : client->setMute(m_mute, m_sourceId);
122 1 : m_isMuteQueued = false;
123 : }
124 94 : if (m_isLowLatencyQueued)
125 : {
126 2 : if (!client->setLowLatency(m_lowLatency))
127 : {
128 1 : GST_ERROR_OBJECT(m_sink, "Could not set queued low-latency");
129 : }
130 2 : m_isLowLatencyQueued = false;
131 : }
132 94 : if (m_isSyncQueued)
133 : {
134 2 : if (!client->setSync(m_sync))
135 : {
136 1 : GST_ERROR_OBJECT(m_sink, "Could not set queued sync");
137 : }
138 2 : m_isSyncQueued = false;
139 : }
140 94 : if (m_isSyncOffQueued)
141 : {
142 2 : if (!client->setSyncOff(m_syncOff))
143 : {
144 1 : GST_ERROR_OBJECT(m_sink, "Could not set queued sync-off");
145 : }
146 2 : m_isSyncOffQueued = false;
147 : }
148 94 : if (m_isStreamSyncModeQueued)
149 : {
150 2 : if (!client->setStreamSyncMode(m_sourceId, m_streamSyncMode))
151 : {
152 1 : GST_ERROR_OBJECT(m_sink, "Could not set queued stream-sync-mode");
153 : }
154 2 : m_isStreamSyncModeQueued = false;
155 : }
156 94 : if (m_isBufferingLimitQueued)
157 : {
158 1 : client->setBufferingLimit(m_bufferingLimit);
159 1 : m_isBufferingLimitQueued = false;
160 : }
161 94 : if (m_isUseBufferingQueued)
162 : {
163 1 : client->setUseBuffering(m_useBuffering);
164 1 : m_isUseBufferingQueued = false;
165 : }
166 : // check if READY -> PAUSED was requested before source was attached
167 94 : if (GST_STATE_NEXT(m_sink) == GST_STATE_PAUSED)
168 : {
169 94 : client->pause(m_sourceId);
170 : }
171 : }
172 95 : }
173 : else
174 : {
175 1 : GST_ERROR_OBJECT(m_sink, "Failed to create AUDIO source");
176 : }
177 96 : break;
178 : }
179 6 : case GST_EVENT_CUSTOM_DOWNSTREAM:
180 : case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
181 : {
182 6 : if (gst_event_has_name(event, "switch-source"))
183 : {
184 4 : GST_DEBUG_OBJECT(m_sink, "Switch source event received");
185 4 : const GstStructure *structure{gst_event_get_structure(event)};
186 4 : const GValue *value = gst_structure_get_value(structure, "caps");
187 4 : if (!value)
188 : {
189 1 : GST_ERROR_OBJECT(m_sink, "Caps not available in switch-source event");
190 2 : break;
191 : }
192 3 : const GstCaps *caps = gst_value_get_caps(value);
193 3 : GstCaps *mutableCaps = gst_caps_copy(caps);
194 3 : std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource> asource = createMediaSource(mutableCaps);
195 3 : gst_caps_unref(mutableCaps);
196 3 : if (!asource)
197 : {
198 1 : GST_ERROR_OBJECT(m_sink, "Not able to parse caps");
199 1 : break;
200 : }
201 2 : std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
202 2 : if ((!client) || (!client->switchSource(asource)))
203 : {
204 1 : GST_ERROR_OBJECT(m_sink, "Failed to switch AUDIO source");
205 : }
206 3 : }
207 4 : break;
208 : }
209 33 : default:
210 33 : break;
211 : }
212 137 : return PullModePlaybackDelegate::handleEvent(event);
213 : }
214 :
215 30 : void PullModeAudioPlaybackDelegate::getProperty(const Property &type, GValue *value)
216 : {
217 30 : std::shared_ptr<GStreamerMSEMediaPlayerClient> client{m_mediaPlayerManager.getMediaPlayerClient()};
218 :
219 30 : switch (type)
220 : {
221 5 : case IPlaybackDelegate::Property::Volume:
222 : {
223 : double volume;
224 5 : if (client)
225 : {
226 3 : if (client->getVolume(volume))
227 2 : m_targetVolume = volume;
228 : else
229 1 : volume = m_targetVolume; // Use last known volume
230 : }
231 : else
232 : {
233 2 : volume = m_targetVolume;
234 : }
235 5 : g_value_set_double(value, volume);
236 5 : break;
237 : }
238 3 : case IPlaybackDelegate::Property::Mute:
239 : {
240 3 : if (!client)
241 : {
242 2 : g_value_set_boolean(value, m_mute);
243 2 : return;
244 : }
245 1 : g_value_set_boolean(value, client->getMute(m_sourceId));
246 1 : break;
247 : }
248 4 : case IPlaybackDelegate::Property::Sync:
249 : {
250 4 : if (!client)
251 : {
252 2 : g_value_set_boolean(value, m_sync);
253 2 : return;
254 : }
255 :
256 2 : bool sync{kDefaultSync};
257 2 : if (!client->getSync(sync))
258 : {
259 1 : GST_ERROR_OBJECT(m_sink, "Could not get sync");
260 : }
261 2 : g_value_set_boolean(value, sync);
262 2 : break;
263 : }
264 4 : case IPlaybackDelegate::Property::StreamSyncMode:
265 : {
266 4 : if (!client)
267 : {
268 2 : g_value_set_int(value, m_streamSyncMode);
269 2 : return;
270 : }
271 :
272 2 : int32_t streamSyncMode{kDefaultStreamSyncMode};
273 2 : if (!client->getStreamSyncMode(streamSyncMode))
274 : {
275 1 : GST_ERROR_OBJECT(m_sink, "Could not get stream-sync-mode");
276 : }
277 2 : g_value_set_int(value, streamSyncMode);
278 2 : break;
279 : }
280 2 : case IPlaybackDelegate::Property::FadeVolume:
281 : {
282 2 : double volume{};
283 2 : if (!client || !client->getVolume(volume))
284 : {
285 1 : g_value_set_uint(value, kDefaultFadeVolume);
286 1 : return;
287 : }
288 1 : g_value_set_uint(value, static_cast<uint32_t>(volume * 100.0));
289 1 : break;
290 : }
291 3 : case IPlaybackDelegate::Property::LimitBufferingMs:
292 : {
293 3 : if (!client)
294 : {
295 2 : g_value_set_uint(value, m_bufferingLimit);
296 2 : return;
297 : }
298 1 : g_value_set_uint(value, client->getBufferingLimit());
299 1 : break;
300 : }
301 3 : case IPlaybackDelegate::Property::UseBuffering:
302 : {
303 3 : if (!client)
304 : {
305 2 : g_value_set_boolean(value, m_useBuffering);
306 2 : return;
307 : }
308 1 : g_value_set_boolean(value, client->getUseBuffering());
309 1 : break;
310 : }
311 1 : case IPlaybackDelegate::Property::Async:
312 : {
313 : // Rialto MSE Audio sink is always async
314 1 : g_value_set_boolean(value, TRUE);
315 1 : break;
316 : }
317 5 : default:
318 : {
319 5 : PullModePlaybackDelegate::getProperty(type, value);
320 5 : break;
321 : }
322 : }
323 30 : }
324 :
325 256 : void PullModeAudioPlaybackDelegate::setProperty(const Property &type, const GValue *value)
326 : {
327 256 : std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
328 :
329 256 : switch (type)
330 : {
331 3 : case IPlaybackDelegate::Property::Volume:
332 : {
333 3 : m_targetVolume = g_value_get_double(value);
334 3 : if (!client || !m_sourceAttached)
335 : {
336 2 : GST_DEBUG_OBJECT(m_sink, "Enqueue volume setting");
337 2 : m_isVolumeQueued = true;
338 2 : return;
339 : }
340 1 : client->setVolume(m_targetVolume, kDefaultVolumeDuration, kDefaultEaseType);
341 1 : break;
342 : }
343 3 : case IPlaybackDelegate::Property::Mute:
344 : {
345 3 : m_mute = g_value_get_boolean(value);
346 3 : if (!client || !m_sourceAttached)
347 : {
348 2 : GST_DEBUG_OBJECT(m_sink, "Enqueue mute setting");
349 2 : m_isMuteQueued = true;
350 2 : return;
351 : }
352 1 : client->setMute(m_mute, m_sourceId);
353 1 : break;
354 : }
355 2 : case IPlaybackDelegate::Property::Gap:
356 : {
357 2 : gint64 position{0}, discontinuityGap{0};
358 2 : guint duration{0};
359 2 : gboolean audioAac{FALSE};
360 :
361 2 : GstStructure *gapData = GST_STRUCTURE_CAST(g_value_get_boxed(value));
362 2 : if (!gst_structure_get_int64(gapData, "position", &position))
363 : {
364 1 : GST_WARNING_OBJECT(m_sink, "Set gap: position is missing!");
365 : }
366 2 : if (!gst_structure_get_uint(gapData, "duration", &duration))
367 : {
368 1 : GST_WARNING_OBJECT(m_sink, "Set gap: duration is missing!");
369 : }
370 2 : if (!gst_structure_get_int64(gapData, "discontinuity-gap", &discontinuityGap))
371 : {
372 1 : GST_WARNING_OBJECT(m_sink, "Set gap: discontinuity gap is missing!");
373 : }
374 2 : if (!gst_structure_get_boolean(gapData, "audio-aac", &audioAac))
375 : {
376 1 : GST_WARNING_OBJECT(m_sink, "Set gap: audio aac is missing!");
377 : }
378 :
379 2 : GST_DEBUG_OBJECT(m_sink, "Processing audio gap.");
380 2 : client->processAudioGap(position, duration, discontinuityGap, audioAac);
381 2 : break;
382 : }
383 5 : case IPlaybackDelegate::Property::LowLatency:
384 : {
385 5 : m_lowLatency = g_value_get_boolean(value);
386 5 : if (!client)
387 : {
388 3 : GST_DEBUG_OBJECT(m_sink, "Enqueue low latency setting");
389 3 : m_isLowLatencyQueued = true;
390 3 : return;
391 : }
392 :
393 2 : if (!client->setLowLatency(m_lowLatency))
394 : {
395 1 : GST_ERROR_OBJECT(m_sink, "Could not set low-latency");
396 : }
397 2 : break;
398 : }
399 5 : case IPlaybackDelegate::Property::Sync:
400 : {
401 5 : m_sync = g_value_get_boolean(value);
402 5 : if (!client)
403 : {
404 3 : GST_DEBUG_OBJECT(m_sink, "Enqueue sync setting");
405 3 : m_isSyncQueued = true;
406 3 : return;
407 : }
408 :
409 2 : if (!client->setSync(m_sync))
410 : {
411 1 : GST_ERROR_OBJECT(m_sink, "Could not set sync");
412 : }
413 2 : break;
414 : }
415 5 : case IPlaybackDelegate::Property::SyncOff:
416 : {
417 5 : m_syncOff = g_value_get_boolean(value);
418 5 : if (!client)
419 : {
420 3 : GST_DEBUG_OBJECT(m_sink, "Enqueue sync off setting");
421 3 : m_isSyncOffQueued = true;
422 3 : return;
423 : }
424 :
425 2 : if (!client->setSyncOff(m_syncOff))
426 : {
427 1 : GST_ERROR_OBJECT(m_sink, "Could not set sync-off");
428 : }
429 2 : break;
430 : }
431 5 : case IPlaybackDelegate::Property::StreamSyncMode:
432 : {
433 5 : m_streamSyncMode = g_value_get_int(value);
434 5 : if (!client || !m_sourceAttached)
435 : {
436 3 : GST_DEBUG_OBJECT(m_sink, "Enqueue stream sync mode setting");
437 3 : m_isStreamSyncModeQueued = true;
438 3 : return;
439 : }
440 :
441 2 : if (!client->setStreamSyncMode(m_sourceId, m_streamSyncMode))
442 : {
443 1 : GST_ERROR_OBJECT(m_sink, "Could not set stream-sync-mode");
444 : }
445 2 : break;
446 : }
447 4 : case IPlaybackDelegate::Property::AudioFade:
448 : {
449 4 : const gchar *audioFadeStr = g_value_get_string(value);
450 :
451 4 : uint32_t fadeVolume = static_cast<uint32_t>(kDefaultVolume * 100);
452 4 : uint32_t duration = kDefaultVolumeDuration;
453 4 : char easeTypeChar = 'L';
454 :
455 4 : int parsedItems = sscanf(audioFadeStr, "%u,%u,%c", &fadeVolume, &duration, &easeTypeChar);
456 :
457 4 : if (parsedItems == 0)
458 : {
459 1 : GST_ERROR_OBJECT(m_sink, "Failed to parse any values from audio fade string: %s.", audioFadeStr);
460 2 : return;
461 : }
462 3 : else if (parsedItems == 1 || parsedItems == 2)
463 : {
464 1 : GST_WARNING_OBJECT(m_sink, "Partially parsed audio fade string: %s. Continuing with values: fadeVolume=%u, duration=%u, easeTypeChar=%c",
465 : audioFadeStr, fadeVolume, duration, easeTypeChar);
466 : }
467 :
468 3 : if (fadeVolume > 100)
469 : {
470 0 : GST_WARNING_OBJECT(m_sink, "Fade volume is greater than 100. Setting it to 100.");
471 0 : fadeVolume = 100;
472 : }
473 3 : double volume = fadeVolume / 100.0;
474 :
475 3 : firebolt::rialto::EaseType easeType = convertCharToEaseType(easeTypeChar);
476 :
477 : {
478 3 : std::lock_guard<std::mutex> lock(m_audioFadeConfigMutex);
479 3 : m_audioFadeConfig.volume = volume;
480 3 : m_audioFadeConfig.duration = duration;
481 3 : m_audioFadeConfig.easeType = easeType;
482 : }
483 :
484 3 : if (!client)
485 : {
486 1 : GST_DEBUG_OBJECT(m_sink, "Enqueue audio fade setting");
487 1 : m_isAudioFadeQueued = true;
488 1 : return;
489 : }
490 :
491 2 : client->setVolume(volume, duration, easeType);
492 2 : break;
493 : }
494 3 : case IPlaybackDelegate::Property::LimitBufferingMs:
495 : {
496 3 : m_bufferingLimit = g_value_get_uint(value);
497 3 : if (!client)
498 : {
499 2 : GST_DEBUG_OBJECT(m_sink, "Enqueue buffering limit setting");
500 2 : m_isBufferingLimitQueued = true;
501 2 : return;
502 : }
503 :
504 1 : client->setBufferingLimit(m_bufferingLimit);
505 1 : break;
506 : }
507 3 : case IPlaybackDelegate::Property::UseBuffering:
508 : {
509 3 : m_useBuffering = g_value_get_boolean(value);
510 3 : if (!client)
511 : {
512 2 : GST_DEBUG_OBJECT(m_sink, "Enqueue use buffering setting");
513 2 : m_isUseBufferingQueued = true;
514 2 : return;
515 : }
516 :
517 1 : client->setUseBuffering(m_useBuffering);
518 1 : break;
519 : }
520 1 : case IPlaybackDelegate::Property::Async:
521 : {
522 1 : if (FALSE == g_value_get_boolean(value))
523 : {
524 1 : GST_WARNING_OBJECT(m_sink, "Cannot set ASYNC to false - not supported");
525 : }
526 1 : break;
527 : }
528 217 : default:
529 : {
530 217 : PullModePlaybackDelegate::setProperty(type, value);
531 217 : break;
532 : }
533 : }
534 256 : }
535 :
536 1 : void PullModeAudioPlaybackDelegate::handleQos(uint64_t processed, uint64_t dropped) const
537 : {
538 1 : GstBus *bus = gst_element_get_bus(m_sink);
539 : /* Hardcode isLive to FALSE and set invalid timestamps */
540 1 : GstMessage *message = gst_message_new_qos(GST_OBJECT(m_sink), FALSE, GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE,
541 : GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE);
542 1 : gst_message_set_qos_stats(message, GST_FORMAT_DEFAULT, processed, dropped);
543 1 : gst_bus_post(bus, message);
544 1 : gst_object_unref(bus);
545 : }
546 :
547 : std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource>
548 99 : PullModeAudioPlaybackDelegate::createMediaSource(GstCaps *caps) const
549 : {
550 99 : GstStructure *structure = gst_caps_get_structure(caps, 0);
551 99 : const gchar *structName = gst_structure_get_name(structure);
552 :
553 99 : firebolt::rialto::AudioConfig audioConfig{};
554 99 : firebolt::rialto::SegmentAlignment alignment{get_segment_alignment(structure)};
555 99 : std::shared_ptr<firebolt::rialto::CodecData> codecData{get_codec_data(structure)};
556 99 : firebolt::rialto::StreamFormat format{get_stream_format(structure)};
557 :
558 99 : if (structName)
559 : {
560 98 : std::string mimeType{};
561 104 : if (g_str_has_prefix(structName, "audio/mpeg") || g_str_has_prefix(structName, "audio/x-eac3") ||
562 6 : g_str_has_prefix(structName, "audio/x-ac3"))
563 : {
564 93 : gint sample_rate{0};
565 93 : gint number_of_channels{0};
566 93 : gst_structure_get_int(structure, "rate", &sample_rate);
567 93 : gst_structure_get_int(structure, "channels", &number_of_channels);
568 :
569 186 : audioConfig = firebolt::rialto::AudioConfig{static_cast<uint32_t>(number_of_channels),
570 93 : static_cast<uint32_t>(sample_rate),
571 93 : {}};
572 :
573 93 : if (g_str_has_prefix(structName, "audio/mpeg"))
574 : {
575 91 : gint mpegversion{0};
576 91 : gint layer{0};
577 91 : gst_structure_get_int(structure, "mpegversion", &mpegversion);
578 91 : gst_structure_get_int(structure, "layer", &layer);
579 91 : if (1 == mpegversion && 3 == layer)
580 : {
581 1 : mimeType = "audio/mp3";
582 : }
583 : else
584 : {
585 90 : mimeType = "audio/mp4";
586 : }
587 : }
588 : else
589 : {
590 2 : mimeType = "audio/x-eac3";
591 : }
592 : }
593 5 : else if (g_str_has_prefix(structName, "audio/x-opus"))
594 : {
595 2 : mimeType = "audio/x-opus";
596 2 : guint32 sampleRate{48000};
597 2 : guint8 numberOfChannels{}, streams{}, stereoStreams{}, channelMappingFamily{};
598 2 : guint8 channelMapping[256]{};
599 2 : guint16 preSkip{0};
600 2 : gint16 gain{0};
601 2 : if (gst_codec_utils_opus_parse_caps(caps, &sampleRate, &numberOfChannels, &channelMappingFamily, &streams,
602 : &stereoStreams, channelMapping))
603 : {
604 1 : GstBuffer *idHeader{};
605 1 : idHeader = gst_codec_utils_opus_create_header(sampleRate, numberOfChannels, channelMappingFamily,
606 : streams, stereoStreams, channelMapping, preSkip, gain);
607 1 : std::vector<uint8_t> codecSpecificConfig{};
608 1 : GstMapInfo lsMap{};
609 1 : if (gst_buffer_map(idHeader, &lsMap, GST_MAP_READ))
610 : {
611 1 : codecSpecificConfig.assign(lsMap.data, lsMap.data + lsMap.size);
612 1 : gst_buffer_unmap(idHeader, &lsMap);
613 : }
614 : else
615 : {
616 0 : GST_ERROR_OBJECT(m_sink, "Failed to read opus header details from a GstBuffer!");
617 : }
618 1 : gst_buffer_unref(idHeader);
619 :
620 1 : audioConfig = firebolt::rialto::AudioConfig{numberOfChannels, sampleRate, codecSpecificConfig};
621 : }
622 : else
623 : {
624 1 : GST_ERROR("Failed to parse opus caps!");
625 1 : return nullptr;
626 : }
627 : }
628 3 : else if (g_str_has_prefix(structName, "audio/b-wav") || g_str_has_prefix(structName, "audio/x-raw"))
629 : {
630 2 : gint sampleRate{0};
631 2 : gint numberOfChannels{0};
632 2 : std::optional<uint64_t> channelMask;
633 2 : gst_structure_get_int(structure, "rate", &sampleRate);
634 2 : gst_structure_get_int(structure, "channels", &numberOfChannels);
635 : std::optional<firebolt::rialto::Layout> layout =
636 2 : rialto_mse_sink_convert_layout(gst_structure_get_string(structure, "layout"));
637 : std::optional<firebolt::rialto::Format> rialtoFormat =
638 2 : rialto_mse_sink_convert_format(gst_structure_get_string(structure, "format"));
639 2 : const GValue *channelMaskValue = gst_structure_get_value(structure, "channel-mask");
640 2 : if (channelMaskValue)
641 : {
642 2 : channelMask = gst_value_get_bitmask(channelMaskValue);
643 : }
644 :
645 2 : if (g_str_has_prefix(structName, "audio/b-wav"))
646 : {
647 1 : mimeType = "audio/b-wav";
648 : }
649 : else
650 : {
651 1 : mimeType = "audio/x-raw";
652 : }
653 :
654 4 : audioConfig = firebolt::rialto::AudioConfig{static_cast<uint32_t>(numberOfChannels),
655 2 : static_cast<uint32_t>(sampleRate),
656 : {},
657 : rialtoFormat,
658 : layout,
659 2 : channelMask};
660 : }
661 1 : else if (g_str_has_prefix(structName, "audio/x-flac"))
662 : {
663 1 : mimeType = "audio/x-flac";
664 1 : gint sampleRate{0};
665 1 : gint numberOfChannels{0};
666 1 : gst_structure_get_int(structure, "rate", &sampleRate);
667 1 : gst_structure_get_int(structure, "channels", &numberOfChannels);
668 1 : std::vector<std::vector<uint8_t>> streamHeaderVec;
669 1 : const GValue *streamheader = gst_structure_get_value(structure, "streamheader");
670 1 : if (streamheader)
671 : {
672 2 : for (guint i = 0; i < gst_value_array_get_size(streamheader); ++i)
673 : {
674 1 : const GValue *headerValue = gst_value_array_get_value(streamheader, i);
675 1 : GstBuffer *headerBuffer = gst_value_get_buffer(headerValue);
676 1 : if (headerBuffer)
677 : {
678 1 : GstMappedBuffer mappedBuf(headerBuffer, GST_MAP_READ);
679 1 : if (mappedBuf)
680 : {
681 1 : streamHeaderVec.push_back(
682 3 : std::vector<std::uint8_t>(mappedBuf.data(), mappedBuf.data() + mappedBuf.size()));
683 : }
684 1 : }
685 : }
686 : }
687 1 : std::optional<bool> framed;
688 1 : gboolean framedValue{FALSE};
689 1 : if (gst_structure_get_boolean(structure, "framed", &framedValue))
690 : {
691 1 : framed = framedValue;
692 : }
693 :
694 2 : audioConfig = firebolt::rialto::AudioConfig{static_cast<uint32_t>(numberOfChannels),
695 1 : static_cast<uint32_t>(sampleRate),
696 : {},
697 : std::nullopt,
698 : std::nullopt,
699 : std::nullopt,
700 : streamHeaderVec,
701 1 : framed};
702 : }
703 : else
704 : {
705 0 : GST_INFO_OBJECT(m_sink, "%s audio media source created", structName);
706 0 : mimeType = structName;
707 : }
708 :
709 194 : return std::make_unique<firebolt::rialto::IMediaPipeline::MediaSourceAudio>(mimeType, m_hasDrm, audioConfig,
710 97 : alignment, format, codecData);
711 98 : }
712 :
713 1 : GST_ERROR_OBJECT(m_sink, "Empty caps' structure name! Failed to set mime type for audio media source.");
714 1 : return nullptr;
715 99 : }
|