[GTK][WPE] Start implementing MediaStream API
[WebKit-https.git] / Source / WebCore / platform / mediastream / gstreamer / GStreamerAudioCaptureSource.cpp
1 /*
2  * Copyright (C) 2018 Metrological Group B.V.
3  * Author: Thibault Saunier <tsaunier@igalia.com>
4  * Author: Alejandro G. Castro  <alex@igalia.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * aint with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23
24 #if ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)
25 #include "GStreamerAudioCaptureSource.h"
26
27 #include "GStreamerAudioData.h"
28 #include "GStreamerAudioStreamDescription.h"
29 #include "GStreamerCaptureDeviceManager.h"
30
31 #include <gst/app/gstappsink.h>
32 #include <gst/gst.h>
33 #include <wtf/NeverDestroyed.h>
34
35 namespace WebCore {
36
37 const static CapabilityValueOrRange defaultVolumeCapability = CapabilityValueOrRange(0.0, 1.0);
38 const static RealtimeMediaSourceCapabilities::EchoCancellation defaultEchoCancellationCapability = RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
39
40 GST_DEBUG_CATEGORY(webkit_audio_capture_source_debug);
41 #define GST_CAT_DEFAULT webkit_audio_capture_source_debug
42
43 static void initializeGStreamerDebug()
44 {
45     static std::once_flag debugRegisteredFlag;
46     std::call_once(debugRegisteredFlag, [] {
47         GST_DEBUG_CATEGORY_INIT(webkit_audio_capture_source_debug, "webkitaudiocapturesource", 0, "WebKit Audio Capture Source.");
48     });
49 }
50
51 class GStreamerAudioCaptureSourceFactory : public RealtimeMediaSource::AudioCaptureFactory {
52 public:
53     CaptureSourceOrError createAudioCaptureSource(const CaptureDevice& device, const MediaConstraints* constraints) final
54     {
55         return GStreamerAudioCaptureSource::create(device.persistentId(), constraints);
56     }
57 };
58
59 static GStreamerAudioCaptureSourceFactory& libWebRTCAudioCaptureSourceFactory()
60 {
61     static NeverDestroyed<GStreamerAudioCaptureSourceFactory> factory;
62     return factory.get();
63 }
64
65 CaptureSourceOrError GStreamerAudioCaptureSource::create(const String& deviceID, const MediaConstraints* constraints)
66 {
67     auto device = GStreamerAudioCaptureDeviceManager::singleton().gstreamerDeviceWithUID(deviceID);
68     if (!device) {
69         auto errorMessage = String::format("GStreamerAudioCaptureSource::create(): GStreamer did not find the device: %s.", deviceID.utf8().data());
70         return CaptureSourceOrError(WTFMove(errorMessage));
71     }
72
73     auto source = adoptRef(*new GStreamerAudioCaptureSource(device.value()));
74
75     if (constraints) {
76         auto result = source->applyConstraints(*constraints);
77         if (result)
78             return WTFMove(result.value().first);
79     }
80     return CaptureSourceOrError(WTFMove(source));
81 }
82
83 RealtimeMediaSource::AudioCaptureFactory& GStreamerAudioCaptureSource::factory()
84 {
85     return libWebRTCAudioCaptureSourceFactory();
86 }
87
88 GStreamerAudioCaptureSource::GStreamerAudioCaptureSource(GStreamerCaptureDevice device)
89     : RealtimeMediaSource(device.persistentId(), RealtimeMediaSource::Type::Audio, device.label())
90     , m_capturer(std::make_unique<GStreamerAudioCapturer>(device))
91 {
92     initializeGStreamerDebug();
93 }
94
95 GStreamerAudioCaptureSource::GStreamerAudioCaptureSource(const String& deviceID, const String& name)
96     : RealtimeMediaSource(deviceID, RealtimeMediaSource::Type::Audio, name)
97     , m_capturer(std::make_unique<GStreamerAudioCapturer>())
98 {
99     initializeGStreamerDebug();
100 }
101
102 GStreamerAudioCaptureSource::~GStreamerAudioCaptureSource()
103 {
104 }
105
106 void GStreamerAudioCaptureSource::startProducingData()
107 {
108     m_capturer->setupPipeline();
109     m_capturer->setSampleRate(sampleRate());
110     g_signal_connect(m_capturer->sink(), "new-sample", G_CALLBACK(newSampleCallback), this);
111     m_capturer->play();
112 }
113
114 GstFlowReturn GStreamerAudioCaptureSource::newSampleCallback(GstElement* sink, GStreamerAudioCaptureSource* source)
115 {
116     auto sample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(sink)));
117
118     // FIXME - figure out a way to avoid copying (on write) the data.
119     GstBuffer* buf = gst_sample_get_buffer(sample.get());
120     auto frames(std::unique_ptr<GStreamerAudioData>(new GStreamerAudioData(WTFMove(sample))));
121     auto streamDesc(std::unique_ptr<GStreamerAudioStreamDescription>(new GStreamerAudioStreamDescription(frames->getAudioInfo())));
122
123     source->audioSamplesAvailable(
124         MediaTime(GST_TIME_AS_USECONDS(GST_BUFFER_PTS(buf)), G_USEC_PER_SEC),
125         *frames, *streamDesc, gst_buffer_get_size(buf) / frames->getAudioInfo().bpf);
126
127     return GST_FLOW_OK;
128 }
129
130 void GStreamerAudioCaptureSource::stopProducingData()
131 {
132     g_signal_handlers_disconnect_by_func(m_capturer->sink(), reinterpret_cast<gpointer>(newSampleCallback), this);
133     m_capturer->stop();
134 }
135
136 const RealtimeMediaSourceCapabilities& GStreamerAudioCaptureSource::capabilities() const
137 {
138     if (m_capabilities)
139         return m_capabilities.value();
140
141     uint i;
142     GRefPtr<GstCaps> caps = m_capturer->caps();
143     int minSampleRate = 0, maxSampleRate = 0;
144     for (i = 0; i < gst_caps_get_size(caps.get()); i++) {
145         int capabilityMinSampleRate = 0, capabilityMaxSampleRate = 0;
146         GstStructure* str = gst_caps_get_structure(caps.get(), i);
147
148         // Only accept raw audio for now.
149         if (!gst_structure_has_name(str, "audio/x-raw"))
150             continue;
151
152         gst_structure_get(str, "rate", GST_TYPE_INT_RANGE, &capabilityMinSampleRate, &capabilityMaxSampleRate, nullptr);
153         if (i > 0) {
154             minSampleRate = std::min(minSampleRate, capabilityMinSampleRate);
155             maxSampleRate = std::max(maxSampleRate, capabilityMaxSampleRate);
156         } else {
157             minSampleRate = capabilityMinSampleRate;
158             maxSampleRate = capabilityMaxSampleRate;
159         }
160     }
161
162     RealtimeMediaSourceCapabilities capabilities(settings().supportedConstraints());
163     capabilities.setDeviceId(id());
164     capabilities.setEchoCancellation(defaultEchoCancellationCapability);
165     capabilities.setVolume(defaultVolumeCapability);
166     capabilities.setSampleRate(CapabilityValueOrRange(minSampleRate, maxSampleRate));
167     m_capabilities = WTFMove(capabilities);
168
169     return m_capabilities.value();
170 }
171
172 bool GStreamerAudioCaptureSource::applySampleRate(int sampleRate)
173 {
174     return m_capturer->setSampleRate(sampleRate);
175 }
176
177 const RealtimeMediaSourceSettings& GStreamerAudioCaptureSource::settings() const
178 {
179     if (!m_currentSettings) {
180         RealtimeMediaSourceSettings settings;
181         settings.setDeviceId(id());
182
183         RealtimeMediaSourceSupportedConstraints supportedConstraints;
184         supportedConstraints.setSupportsDeviceId(true);
185         supportedConstraints.setSupportsEchoCancellation(true);
186         supportedConstraints.setSupportsVolume(true);
187         supportedConstraints.setSupportsSampleRate(true);
188         settings.setSupportedConstraints(supportedConstraints);
189
190         m_currentSettings = WTFMove(settings);
191     }
192
193     m_currentSettings->setVolume(volume());
194     m_currentSettings->setSampleRate(sampleRate());
195     m_currentSettings->setEchoCancellation(echoCancellation());
196
197     return m_currentSettings.value();
198 }
199
200 } // namespace WebCore
201
202 #endif // ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)