1/*
2 * Copyright (C) 2017 Igalia S.L
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
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 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * aint with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21
22#if USE(LIBWEBRTC) && USE(GSTREAMER)
23#include "RealtimeOutgoingAudioSourceLibWebRTC.h"
24
25#include "LibWebRTCAudioFormat.h"
26#include "LibWebRTCProvider.h"
27#include "NotImplemented.h"
28#include "gstreamer/GStreamerAudioData.h"
29
30namespace WebCore {
31
32RealtimeOutgoingAudioSourceLibWebRTC::RealtimeOutgoingAudioSourceLibWebRTC(Ref<MediaStreamTrackPrivate>&& audioSource)
33 : RealtimeOutgoingAudioSource(WTFMove(audioSource))
34{
35 m_adapter = adoptGRef(gst_adapter_new()),
36 m_sampleConverter = nullptr;
37}
38
39RealtimeOutgoingAudioSourceLibWebRTC::~RealtimeOutgoingAudioSourceLibWebRTC()
40{
41 unobserveSource();
42 m_sampleConverter = nullptr;
43}
44
45Ref<RealtimeOutgoingAudioSource> RealtimeOutgoingAudioSource::create(Ref<MediaStreamTrackPrivate>&& audioSource)
46{
47 return RealtimeOutgoingAudioSourceLibWebRTC::create(WTFMove(audioSource));
48}
49
50static inline std::unique_ptr<GStreamerAudioStreamDescription> libwebrtcAudioFormat(int sampleRate,
51 size_t channelCount)
52{
53 GstAudioFormat format = gst_audio_format_build_integer(
54 LibWebRTCAudioFormat::isSigned,
55 LibWebRTCAudioFormat::isBigEndian ? G_BIG_ENDIAN : G_LITTLE_ENDIAN,
56 LibWebRTCAudioFormat::sampleSize,
57 LibWebRTCAudioFormat::sampleSize);
58
59 GstAudioInfo info;
60
61 size_t libWebRTCChannelCount = channelCount >= 2 ? 2 : channelCount;
62 gst_audio_info_set_format(&info, format, sampleRate, libWebRTCChannelCount, nullptr);
63
64 return std::unique_ptr<GStreamerAudioStreamDescription>(new GStreamerAudioStreamDescription(info));
65}
66
67void RealtimeOutgoingAudioSourceLibWebRTC::audioSamplesAvailable(const MediaTime&,
68 const PlatformAudioData& audioData, const AudioStreamDescription& streamDescription,
69 size_t /* sampleCount */)
70{
71 auto data = static_cast<const GStreamerAudioData&>(audioData);
72 auto desc = static_cast<const GStreamerAudioStreamDescription&>(streamDescription);
73
74 if (m_sampleConverter && !gst_audio_info_is_equal(m_inputStreamDescription->getInfo(), desc.getInfo())) {
75 GST_ERROR_OBJECT(this, "FIXME - Audio format renegotiation is not possible yet!");
76 m_sampleConverter = nullptr;
77 }
78
79 if (!m_sampleConverter) {
80 m_inputStreamDescription = std::unique_ptr<GStreamerAudioStreamDescription>(new GStreamerAudioStreamDescription(desc.getInfo()));
81 m_outputStreamDescription = libwebrtcAudioFormat(LibWebRTCAudioFormat::sampleRate, streamDescription.numberOfChannels());
82 m_sampleConverter.reset(gst_audio_converter_new(GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE,
83 m_inputStreamDescription->getInfo(),
84 m_outputStreamDescription->getInfo(),
85 nullptr));
86 }
87
88 LockHolder locker(m_adapterMutex);
89 auto buffer = gst_sample_get_buffer(data.getSample());
90 gst_adapter_push(m_adapter.get(), gst_buffer_ref(buffer));
91 LibWebRTCProvider::callOnWebRTCSignalingThread([protectedThis = makeRef(*this)] {
92 protectedThis->pullAudioData();
93 });
94}
95
96void RealtimeOutgoingAudioSourceLibWebRTC::pullAudioData()
97{
98 if (!m_inputStreamDescription || !m_outputStreamDescription) {
99 GST_INFO("No stream description set yet.");
100
101 return;
102 }
103
104 size_t outChunkSampleCount = LibWebRTCAudioFormat::chunkSampleCount;
105 size_t outBufferSize = outChunkSampleCount * m_outputStreamDescription->getInfo()->bpf;
106
107 LockHolder locker(m_adapterMutex);
108 size_t inChunkSampleCount = gst_audio_converter_get_in_frames(m_sampleConverter.get(), outChunkSampleCount);
109 size_t inBufferSize = inChunkSampleCount * m_inputStreamDescription->getInfo()->bpf;
110
111 while (gst_adapter_available(m_adapter.get()) > inBufferSize) {
112 auto inBuffer = adoptGRef(gst_adapter_take_buffer(m_adapter.get(), inBufferSize));
113 m_audioBuffer.grow(outBufferSize);
114 if (isSilenced())
115 gst_audio_format_fill_silence(m_outputStreamDescription->getInfo()->finfo, m_audioBuffer.data(), outBufferSize);
116 else {
117 auto inMap = GstMappedBuffer::create(inBuffer.get(), GST_MAP_READ);
118
119 gpointer in[1] = { inMap->data() };
120 gpointer out[1] = { m_audioBuffer.data() };
121 if (!gst_audio_converter_samples(m_sampleConverter.get(), static_cast<GstAudioConverterFlags>(0), in, inChunkSampleCount, out, outChunkSampleCount)) {
122 GST_ERROR("Could not convert samples.");
123
124 return;
125 }
126 }
127
128 sendAudioFrames(m_audioBuffer.data(), LibWebRTCAudioFormat::sampleSize, static_cast<int>(m_outputStreamDescription->sampleRate()),
129 static_cast<int>(m_outputStreamDescription->numberOfChannels()), outChunkSampleCount);
130 }
131}
132
133bool RealtimeOutgoingAudioSourceLibWebRTC::isReachingBufferedAudioDataHighLimit()
134{
135 notImplemented();
136 return false;
137}
138
139bool RealtimeOutgoingAudioSourceLibWebRTC::isReachingBufferedAudioDataLowLimit()
140{
141 notImplemented();
142 return false;
143}
144
145bool RealtimeOutgoingAudioSourceLibWebRTC::hasBufferedEnoughData()
146{
147 notImplemented();
148 return false;
149}
150
151} // namespace WebCore
152
153#endif // USE(LIBWEBRTC)
154