[MediaStream] RealtimeMediaSource should be able to vend hashed IDs
[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 static CapabilityValueOrRange defaultVolumeCapability()
38 {
39     return CapabilityValueOrRange(0.0, 1.0);
40 }
41 const static RealtimeMediaSourceCapabilities::EchoCancellation defaultEchoCancellationCapability = RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
42
43 GST_DEBUG_CATEGORY(webkit_audio_capture_source_debug);
44 #define GST_CAT_DEFAULT webkit_audio_capture_source_debug
45
46 static void initializeGStreamerDebug()
47 {
48     static std::once_flag debugRegisteredFlag;
49     std::call_once(debugRegisteredFlag, [] {
50         GST_DEBUG_CATEGORY_INIT(webkit_audio_capture_source_debug, "webkitaudiocapturesource", 0, "WebKit Audio Capture Source.");
51     });
52 }
53
54 class GStreamerAudioCaptureSourceFactory : public AudioCaptureFactory {
55 public:
56     CaptureSourceOrError createAudioCaptureSource(const CaptureDevice& device, String&& hashSalt, const MediaConstraints* constraints) final
57     {
58         return GStreamerAudioCaptureSource::create(String { device.persistentId() }, WTFMove(hashSalt), constraints);
59     }
60 };
61
62 static GStreamerAudioCaptureSourceFactory& libWebRTCAudioCaptureSourceFactory()
63 {
64     static NeverDestroyed<GStreamerAudioCaptureSourceFactory> factory;
65     return factory.get();
66 }
67
68 CaptureSourceOrError GStreamerAudioCaptureSource::create(String&& deviceID, String&& hashSalt, const MediaConstraints* constraints)
69 {
70     auto device = GStreamerAudioCaptureDeviceManager::singleton().gstreamerDeviceWithUID(deviceID);
71     if (!device) {
72         auto errorMessage = String::format("GStreamerAudioCaptureSource::create(): GStreamer did not find the device: %s.", deviceID.utf8().data());
73         return CaptureSourceOrError(WTFMove(errorMessage));
74     }
75
76     auto source = adoptRef(*new GStreamerAudioCaptureSource(device.value(), WTFMove(hashSalt)));
77
78     if (constraints) {
79         auto result = source->applyConstraints(*constraints);
80         if (result)
81             return WTFMove(result.value().first);
82     }
83     return CaptureSourceOrError(WTFMove(source));
84 }
85
86 AudioCaptureFactory& GStreamerAudioCaptureSource::factory()
87 {
88     return libWebRTCAudioCaptureSourceFactory();
89 }
90
91 GStreamerAudioCaptureSource::GStreamerAudioCaptureSource(GStreamerCaptureDevice device, String&& hashSalt)
92     : RealtimeMediaSource(RealtimeMediaSource::Type::Audio, String { device.persistentId() }, String { device.label() }, WTFMove(hashSalt))
93     , m_capturer(std::make_unique<GStreamerAudioCapturer>(device))
94 {
95     initializeGStreamerDebug();
96 }
97
98 GStreamerAudioCaptureSource::GStreamerAudioCaptureSource(String&& deviceID, String&& name, String&& hashSalt)
99     : RealtimeMediaSource(RealtimeMediaSource::Type::Audio, WTFMove(deviceID), WTFMove(name), WTFMove(hashSalt))
100     , m_capturer(std::make_unique<GStreamerAudioCapturer>())
101 {
102     initializeGStreamerDebug();
103 }
104
105 GStreamerAudioCaptureSource::~GStreamerAudioCaptureSource()
106 {
107 }
108
109 void GStreamerAudioCaptureSource::startProducingData()
110 {
111     m_capturer->setupPipeline();
112     m_capturer->setSampleRate(sampleRate());
113     g_signal_connect(m_capturer->sink(), "new-sample", G_CALLBACK(newSampleCallback), this);
114     m_capturer->play();
115 }
116
117 GstFlowReturn GStreamerAudioCaptureSource::newSampleCallback(GstElement* sink, GStreamerAudioCaptureSource* source)
118 {
119     auto sample = adoptGRef(gst_app_sink_pull_sample(GST_APP_SINK(sink)));
120
121     // FIXME - figure out a way to avoid copying (on write) the data.
122     GstBuffer* buf = gst_sample_get_buffer(sample.get());
123     auto frames(std::unique_ptr<GStreamerAudioData>(new GStreamerAudioData(WTFMove(sample))));
124     auto streamDesc(std::unique_ptr<GStreamerAudioStreamDescription>(new GStreamerAudioStreamDescription(frames->getAudioInfo())));
125
126     source->audioSamplesAvailable(
127         MediaTime(GST_TIME_AS_USECONDS(GST_BUFFER_PTS(buf)), G_USEC_PER_SEC),
128         *frames, *streamDesc, gst_buffer_get_size(buf) / frames->getAudioInfo().bpf);
129
130     return GST_FLOW_OK;
131 }
132
133 void GStreamerAudioCaptureSource::stopProducingData()
134 {
135     g_signal_handlers_disconnect_by_func(m_capturer->sink(), reinterpret_cast<gpointer>(newSampleCallback), this);
136     m_capturer->stop();
137 }
138
139 const RealtimeMediaSourceCapabilities& GStreamerAudioCaptureSource::capabilities()
140 {
141     if (m_capabilities)
142         return m_capabilities.value();
143
144     uint i;
145     GRefPtr<GstCaps> caps = m_capturer->caps();
146     int minSampleRate = 0, maxSampleRate = 0;
147     for (i = 0; i < gst_caps_get_size(caps.get()); i++) {
148         int capabilityMinSampleRate = 0, capabilityMaxSampleRate = 0;
149         GstStructure* str = gst_caps_get_structure(caps.get(), i);
150
151         // Only accept raw audio for now.
152         if (!gst_structure_has_name(str, "audio/x-raw"))
153             continue;
154
155         gst_structure_get(str, "rate", GST_TYPE_INT_RANGE, &capabilityMinSampleRate, &capabilityMaxSampleRate, nullptr);
156         if (i > 0) {
157             minSampleRate = std::min(minSampleRate, capabilityMinSampleRate);
158             maxSampleRate = std::max(maxSampleRate, capabilityMaxSampleRate);
159         } else {
160             minSampleRate = capabilityMinSampleRate;
161             maxSampleRate = capabilityMaxSampleRate;
162         }
163     }
164
165     RealtimeMediaSourceCapabilities capabilities(settings().supportedConstraints());
166     capabilities.setDeviceId(hashedId());
167     capabilities.setEchoCancellation(defaultEchoCancellationCapability);
168     capabilities.setVolume(defaultVolumeCapability());
169     capabilities.setSampleRate(CapabilityValueOrRange(minSampleRate, maxSampleRate));
170     m_capabilities = WTFMove(capabilities);
171
172     return m_capabilities.value();
173 }
174
175 void GStreamerAudioCaptureSource::settingsDidChange(OptionSet<RealtimeMediaSourceSettings::Flag> settings)
176 {
177     if (settings.contains(RealtimeMediaSourceSettings::Flag::SampleRate))
178         m_capturer->setSampleRate(sampleRate());
179 }
180
181 const RealtimeMediaSourceSettings& GStreamerAudioCaptureSource::settings()
182 {
183     if (!m_currentSettings) {
184         RealtimeMediaSourceSettings settings;
185         settings.setDeviceId(hashedId());
186
187         RealtimeMediaSourceSupportedConstraints supportedConstraints;
188         supportedConstraints.setSupportsDeviceId(true);
189         supportedConstraints.setSupportsEchoCancellation(true);
190         supportedConstraints.setSupportsVolume(true);
191         supportedConstraints.setSupportsSampleRate(true);
192         settings.setSupportedConstraints(supportedConstraints);
193
194         m_currentSettings = WTFMove(settings);
195     }
196
197     m_currentSettings->setVolume(volume());
198     m_currentSettings->setSampleRate(sampleRate());
199     m_currentSettings->setEchoCancellation(echoCancellation());
200
201     return m_currentSettings.value();
202 }
203
204 } // namespace WebCore
205
206 #endif // ENABLE(MEDIA_STREAM) && USE(LIBWEBRTC) && USE(GSTREAMER)