WebRTC: Make MediaEndpointPeerConnection handle remotely assigned mids correctly
[WebKit-https.git] / Source / WebCore / platform / mock / MockMediaEndpoint.cpp
1 /*
2  * Copyright (C) 2015 Ericsson AB. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in the documentation and/or other materials provided with the
13  *    distribution.
14  * 3. Neither the name of Ericsson nor the names of its contributors
15  *    may be used to endorse or promote products derived from this
16  *    software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #if ENABLE(WEB_RTC)
34 #include "MockMediaEndpoint.h"
35
36 #include "MediaEndpointSessionConfiguration.h"
37 #include "MediaPayload.h"
38 #include "MockRealtimeAudioSource.h"
39 #include "MockRealtimeVideoSource.h"
40 #include "RealtimeMediaSource.h"
41 #include <wtf/MainThread.h>
42
43 namespace WebCore {
44
45 static const char* fingerprint = "8B:87:09:8A:5D:C2:F3:33:EF:C5:B1:F6:84:3A:3D:D6:A3:E2:9C:17:4C:E7:46:3B:1B:CE:84:98:DD:8E:AF:7B";
46 static const char* fingerprintFunction = "sha-256";
47
48 std::unique_ptr<MediaEndpoint> MockMediaEndpoint::create(MediaEndpointClient& client)
49 {
50     return std::unique_ptr<MediaEndpoint>(new MockMediaEndpoint(client));
51 }
52
53 MockMediaEndpoint::MockMediaEndpoint(MediaEndpointClient& client)
54     : m_client(client)
55     , m_iceCandidateTimer(*this, &MockMediaEndpoint::iceCandidateTimerFired)
56     , m_iceTransportTimer(*this, &MockMediaEndpoint::iceTransportTimerFired)
57     , m_unmuteTimer(*this, &MockMediaEndpoint::unmuteTimerFired)
58 {
59 }
60
61 MockMediaEndpoint::~MockMediaEndpoint()
62 {
63     stop();
64 }
65
66 void MockMediaEndpoint::setConfiguration(RefPtr<MediaEndpointConfiguration>&& configuration)
67 {
68     UNUSED_PARAM(configuration);
69 }
70
71 void MockMediaEndpoint::generateDtlsInfo()
72 {
73     callOnMainThread([this]() {
74         m_client.gotDtlsFingerprint(String(fingerprint), String(fingerprintFunction));
75     });
76 }
77
78 MediaPayloadVector MockMediaEndpoint::getDefaultAudioPayloads()
79 {
80     MediaPayloadVector payloads;
81
82     RefPtr<MediaPayload> payload = MediaPayload::create();
83     payload->setType(111);
84     payload->setEncodingName("OPUS");
85     payload->setClockRate(48000);
86     payload->setChannels(2);
87     payloads.append(payload);
88
89     payload = MediaPayload::create();
90     payload->setType(8);
91     payload->setEncodingName("PCMA");
92     payload->setClockRate(8000);
93     payload->setChannels(1);
94     payloads.append(payload);
95
96     payload = MediaPayload::create();
97     payload->setType(0);
98     payload->setEncodingName("PCMU");
99     payload->setClockRate(8000);
100     payload->setChannels(1);
101     payloads.append(payload);
102
103     return payloads;
104 }
105
106 MediaPayloadVector MockMediaEndpoint::getDefaultVideoPayloads()
107 {
108     MediaPayloadVector payloads;
109
110     RefPtr<MediaPayload> payload = MediaPayload::create();
111     payload->setType(103);
112     payload->setEncodingName("H264");
113     payload->setClockRate(90000);
114     payload->setCcmfir(true);
115     payload->setNackpli(true);
116     payload->addParameter("packetizationMode", 1);
117     payloads.append(payload);
118
119     payload = MediaPayload::create();
120     payload->setType(100);
121     payload->setEncodingName("VP8");
122     payload->setClockRate(90000);
123     payload->setCcmfir(true);
124     payload->setNackpli(true);
125     payload->setNack(true);
126     payloads.append(payload);
127
128     payload = MediaPayload::create();
129     payload->setType(120);
130     payload->setEncodingName("RTX");
131     payload->setClockRate(90000);
132     payload->addParameter("apt", 100);
133     payload->addParameter("rtxTime", 200);
134     payloads.append(payload);
135
136     return payloads;
137 }
138
139 MediaPayloadVector MockMediaEndpoint::filterPayloads(const MediaPayloadVector& remotePayloads, const MediaPayloadVector& defaultPayloads)
140 {
141     MediaPayloadVector filteredPayloads;
142
143     for (auto& remotePayload : remotePayloads) {
144         MediaPayload* defaultPayload = nullptr;
145         for (auto& payload : defaultPayloads) {
146             if (payload->encodingName() == remotePayload->encodingName().convertToASCIIUppercase()) {
147                 defaultPayload = payload.get();
148                 break;
149             }
150         }
151         if (!defaultPayload)
152             continue;
153
154         if (defaultPayload->parameters().contains("packetizationMode") && remotePayload->parameters().contains("packetizationMode")
155             && (defaultPayload->parameters().get("packetizationMode") != defaultPayload->parameters().get("packetizationMode")))
156             continue;
157
158         filteredPayloads.append(remotePayload);
159     }
160
161     return filteredPayloads;
162 }
163
164 MediaEndpoint::UpdateResult MockMediaEndpoint::updateReceiveConfiguration(MediaEndpointSessionConfiguration* configuration, bool isInitiator)
165 {
166     UNUSED_PARAM(isInitiator);
167
168     updateConfigurationMids(*configuration);
169     return UpdateResult::Success;
170 }
171
172 MediaEndpoint::UpdateResult MockMediaEndpoint::updateSendConfiguration(MediaEndpointSessionConfiguration* configuration, const RealtimeMediaSourceMap& sendSourceMap, bool isInitiator)
173 {
174     UNUSED_PARAM(sendSourceMap);
175     UNUSED_PARAM(isInitiator);
176
177     updateConfigurationMids(*configuration);
178     return UpdateResult::Success;
179 }
180
181 void MockMediaEndpoint::addRemoteCandidate(IceCandidate& candidate, const String& mid, const String& ufrag, const String& password)
182 {
183     UNUSED_PARAM(candidate);
184     UNUSED_PARAM(mid);
185     UNUSED_PARAM(ufrag);
186     UNUSED_PARAM(password);
187 }
188
189 Ref<RealtimeMediaSource> MockMediaEndpoint::createMutedRemoteSource(const String& mid, RealtimeMediaSource::Type type)
190 {
191     RefPtr<RealtimeMediaSource> source;
192
193     switch (type) {
194     case RealtimeMediaSource::Audio: source = MockRealtimeAudioSource::createMuted("remote audio"); break;
195     case RealtimeMediaSource::Video: source = MockRealtimeVideoSource::createMuted("remote video"); break;
196     case RealtimeMediaSource::None:
197         ASSERT_NOT_REACHED();
198     }
199
200     m_mutedRemoteSources.set(mid, source);
201     return *source;
202 }
203
204 void MockMediaEndpoint::replaceSendSource(RealtimeMediaSource& newSource, const String& mid)
205 {
206     UNUSED_PARAM(newSource);
207     UNUSED_PARAM(mid);
208 }
209
210 void MockMediaEndpoint::replaceMutedRemoteSourceMid(const String& oldMid, const String& newMid)
211 {
212     RefPtr<RealtimeMediaSource> remoteSource = m_mutedRemoteSources.take(oldMid);
213     m_mutedRemoteSources.set(newMid, WTFMove(remoteSource));
214 }
215
216 void MockMediaEndpoint::stop()
217 {
218 }
219
220 void MockMediaEndpoint::emulatePlatformEvent(const String& action)
221 {
222     if (action == "dispatch-fake-ice-candidates")
223         dispatchFakeIceCandidates();
224     else if (action == "step-ice-transport-states")
225         stepIceTransportStates();
226     else if (action == "unmute-remote-sources-by-mid")
227         unmuteRemoteSourcesByMid();
228 }
229
230 void MockMediaEndpoint::updateConfigurationMids(const MediaEndpointSessionConfiguration& configuration)
231 {
232     Vector<String> mids;
233     for (const RefPtr<PeerMediaDescription>& mediaDescription : configuration.mediaDescriptions())
234         mids.append(mediaDescription->mid());
235     m_mids.swap(mids);
236 }
237
238 void MockMediaEndpoint::dispatchFakeIceCandidates()
239 {
240     RefPtr<IceCandidate> iceCandidate = IceCandidate::create();
241     iceCandidate->setType("host");
242     iceCandidate->setFoundation("1");
243     iceCandidate->setComponentId(1);
244     iceCandidate->setPriority(2013266431);
245     iceCandidate->setAddress("192.168.0.100");
246     iceCandidate->setPort(38838);
247     iceCandidate->setTransport("UDP");
248     m_fakeIceCandidates.append(WTFMove(iceCandidate));
249
250     iceCandidate = IceCandidate::create();
251     iceCandidate->setType("host");
252     iceCandidate->setFoundation("2");
253     iceCandidate->setComponentId(1);
254     iceCandidate->setPriority(1019216383);
255     iceCandidate->setAddress("192.168.0.100");
256     iceCandidate->setPort(9);
257     iceCandidate->setTransport("TCP");
258     iceCandidate->setTcpType("active");
259     m_fakeIceCandidates.append(WTFMove(iceCandidate));
260
261     iceCandidate = IceCandidate::create();
262     iceCandidate->setType("srflx");
263     iceCandidate->setFoundation("3");
264     iceCandidate->setComponentId(1);
265     iceCandidate->setPriority(1677722111);
266     iceCandidate->setAddress("172.18.0.1");
267     iceCandidate->setPort(47989);
268     iceCandidate->setTransport("UDP");
269     iceCandidate->setRelatedAddress("192.168.0.100");
270     iceCandidate->setRelatedPort(47989);
271     m_fakeIceCandidates.append(WTFMove(iceCandidate));
272
273     // Reverse order to use takeLast() while keeping the above order
274     m_fakeIceCandidates.reverse();
275
276     m_iceCandidateTimer.startOneShot(0);
277 }
278
279 void MockMediaEndpoint::iceCandidateTimerFired()
280 {
281     if (m_mids.isEmpty())
282         return;
283
284     if (!m_fakeIceCandidates.isEmpty()) {
285         m_client.gotIceCandidate(m_mids[0], m_fakeIceCandidates.takeLast());
286         m_iceCandidateTimer.startOneShot(0);
287     } else
288         m_client.doneGatheringCandidates(m_mids[0]);
289 }
290
291 void MockMediaEndpoint::stepIceTransportStates()
292 {
293     if (m_mids.size() != 3) {
294         LOG_ERROR("The 'step-ice-transport-states' action requires 3 transceivers");
295         return;
296     }
297
298     // Should go to:
299     // 'checking'
300     m_iceTransportStateChanges.append(std::make_pair(m_mids[0], MediaEndpoint::IceTransportState::Checking));
301     m_iceTransportStateChanges.append(std::make_pair(m_mids[1], MediaEndpoint::IceTransportState::Checking));
302     m_iceTransportStateChanges.append(std::make_pair(m_mids[2], MediaEndpoint::IceTransportState::Checking));
303
304     // 'connected'
305     m_iceTransportStateChanges.append(std::make_pair(m_mids[0], MediaEndpoint::IceTransportState::Connected));
306     m_iceTransportStateChanges.append(std::make_pair(m_mids[1], MediaEndpoint::IceTransportState::Completed));
307     m_iceTransportStateChanges.append(std::make_pair(m_mids[2], MediaEndpoint::IceTransportState::Closed));
308
309     // 'completed'
310     m_iceTransportStateChanges.append(std::make_pair(m_mids[0], MediaEndpoint::IceTransportState::Completed));
311
312     // 'failed'
313     m_iceTransportStateChanges.append(std::make_pair(m_mids[0], MediaEndpoint::IceTransportState::Failed));
314
315     // 'disconnected'
316     m_iceTransportStateChanges.append(std::make_pair(m_mids[1], MediaEndpoint::IceTransportState::Disconnected));
317     m_iceTransportStateChanges.append(std::make_pair(m_mids[0], MediaEndpoint::IceTransportState::Closed));
318
319     // 'new'
320     m_iceTransportStateChanges.append(std::make_pair(m_mids[1], MediaEndpoint::IceTransportState::Closed));
321
322     // Reverse order to use takeLast() while keeping the above order
323     m_iceTransportStateChanges.reverse();
324
325     m_iceTransportTimer.startOneShot(0);
326 }
327
328 void MockMediaEndpoint::iceTransportTimerFired()
329 {
330     if (m_iceTransportStateChanges.isEmpty() || m_mids.size() != 3)
331         return;
332
333     auto stateChange = m_iceTransportStateChanges.takeLast();
334     m_client.iceTransportStateChanged(stateChange.first, stateChange.second);
335
336     m_iceTransportTimer.startOneShot(0);
337 }
338
339 void MockMediaEndpoint::unmuteRemoteSourcesByMid()
340 {
341     if (m_mids.isEmpty())
342         return;
343
344     // Looking up each source by its mid, instead of simply iterating over the list of muted sources,
345     // emulates remote media arriving on a media description with a specific mid (RTCRtpTransceiver).
346
347     // Copy values in reverse order to maintain the original order while using takeLast()
348     for (int i = m_mids.size() - 1; i >= 0; --i)
349         m_midsOfSourcesToUnmute.append(m_mids[i]);
350
351     m_unmuteTimer.startOneShot(0);
352 }
353
354 void MockMediaEndpoint::unmuteTimerFired()
355 {
356     RefPtr<RealtimeMediaSource> source = m_mutedRemoteSources.get(m_midsOfSourcesToUnmute.takeLast());
357     if (source)
358         source->setMuted(false);
359
360     if (!m_midsOfSourcesToUnmute.isEmpty())
361         m_unmuteTimer.startOneShot(0);
362 }
363
364 } // namespace WebCore
365
366 #endif // ENABLE(WEB_RTC)