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