1 | /* |
2 | * Copyright (C) 2017 Apple Inc. |
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 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
16 | * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
17 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | #include "MockLibWebRTCPeerConnection.h" |
27 | |
28 | #if USE(LIBWEBRTC) |
29 | |
30 | #include "LibWebRTCProvider.h" |
31 | #include <sstream> |
32 | #include <webrtc/pc/mediastream.h> |
33 | #include <wtf/Function.h> |
34 | #include <wtf/MainThread.h> |
35 | #include <wtf/NeverDestroyed.h> |
36 | #include <wtf/Threading.h> |
37 | |
38 | namespace WebCore { |
39 | |
40 | static inline rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface>& getRealPeerConnectionFactory() |
41 | { |
42 | static NeverDestroyed<rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface>> realPeerConnectionFactory; |
43 | return realPeerConnectionFactory; |
44 | } |
45 | |
46 | static inline webrtc::PeerConnectionFactoryInterface* realPeerConnectionFactory() |
47 | { |
48 | return getRealPeerConnectionFactory().get(); |
49 | } |
50 | |
51 | void useRealRTCPeerConnectionFactory(LibWebRTCProvider& provider) |
52 | { |
53 | auto& factory = getRealPeerConnectionFactory(); |
54 | if (!factory) |
55 | return; |
56 | provider.setPeerConnectionFactory(factory.get()); |
57 | factory = nullptr; |
58 | } |
59 | |
60 | void useMockRTCPeerConnectionFactory(LibWebRTCProvider* provider, const String& testCase) |
61 | { |
62 | if (!provider) |
63 | return; |
64 | |
65 | if (!realPeerConnectionFactory()) { |
66 | auto& factory = getRealPeerConnectionFactory(); |
67 | factory = provider->factory(); |
68 | } |
69 | provider->setPeerConnectionFactory(MockLibWebRTCPeerConnectionFactory::create(testCase)); |
70 | } |
71 | |
72 | MockLibWebRTCPeerConnection::~MockLibWebRTCPeerConnection() |
73 | { |
74 | // Free senders in a different thread like an actual peer connection would probably do. |
75 | Thread::create("MockLibWebRTCPeerConnection thread" , [senders = WTFMove(m_senders)] { }); |
76 | } |
77 | |
78 | class MockLibWebRTCPeerConnectionForIceCandidates : public MockLibWebRTCPeerConnection { |
79 | public: |
80 | explicit MockLibWebRTCPeerConnectionForIceCandidates(webrtc::PeerConnectionObserver& observer) : MockLibWebRTCPeerConnection(observer) { } |
81 | virtual ~MockLibWebRTCPeerConnectionForIceCandidates() = default; |
82 | private: |
83 | void gotLocalDescription() final; |
84 | }; |
85 | |
86 | void MockLibWebRTCPeerConnectionForIceCandidates::gotLocalDescription() |
87 | { |
88 | // Let's gather candidates |
89 | LibWebRTCProvider::callOnWebRTCSignalingThread([this]() { |
90 | MockLibWebRTCIceCandidate candidate("2013266431 1 udp 2013266432 192.168.0.100 38838 typ host generation 0" , "1" ); |
91 | m_observer.OnIceCandidate(&candidate); |
92 | }); |
93 | LibWebRTCProvider::callOnWebRTCSignalingThread([this]() { |
94 | MockLibWebRTCIceCandidate candidate("1019216383 1 tcp 1019216384 192.168.0.100 9 typ host tcptype passive generation 0" , "1" ); |
95 | m_observer.OnIceCandidate(&candidate); |
96 | }); |
97 | LibWebRTCProvider::callOnWebRTCSignalingThread([this]() { |
98 | MockLibWebRTCIceCandidate candidate("1677722111 1 tcp 1677722112 172.18.0.1 47989 typ srflx raddr 192.168.0.100 rport 47989 generation 0" , "1" ); |
99 | m_observer.OnIceCandidate(&candidate); |
100 | }); |
101 | LibWebRTCProvider::callOnWebRTCSignalingThread([this]() { |
102 | m_observer.OnIceGatheringChange(webrtc::PeerConnectionInterface::kIceGatheringComplete); |
103 | }); |
104 | } |
105 | |
106 | class MockLibWebRTCPeerConnectionForIceConnectionState : public MockLibWebRTCPeerConnection { |
107 | public: |
108 | explicit MockLibWebRTCPeerConnectionForIceConnectionState(webrtc::PeerConnectionObserver& observer) : MockLibWebRTCPeerConnection(observer) { } |
109 | virtual ~MockLibWebRTCPeerConnectionForIceConnectionState() = default; |
110 | |
111 | private: |
112 | void gotLocalDescription() final; |
113 | }; |
114 | |
115 | void MockLibWebRTCPeerConnectionForIceConnectionState::gotLocalDescription() |
116 | { |
117 | m_observer.OnIceConnectionChange(kIceConnectionChecking); |
118 | m_observer.OnIceConnectionChange(kIceConnectionConnected); |
119 | m_observer.OnIceConnectionChange(kIceConnectionCompleted); |
120 | m_observer.OnIceConnectionChange(kIceConnectionFailed); |
121 | m_observer.OnIceConnectionChange(kIceConnectionDisconnected); |
122 | m_observer.OnIceConnectionChange(kIceConnectionNew); |
123 | } |
124 | |
125 | template<typename U> static inline void releaseInNetworkThread(MockLibWebRTCPeerConnection& mock, U& observer) |
126 | { |
127 | mock.AddRef(); |
128 | observer.AddRef(); |
129 | callOnMainThread([&mock, &observer] { |
130 | LibWebRTCProvider::callOnWebRTCNetworkThread([&mock, &observer]() { |
131 | observer.Release(); |
132 | mock.Release(); |
133 | }); |
134 | }); |
135 | } |
136 | |
137 | class MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileCreatingOffer : public MockLibWebRTCPeerConnection { |
138 | public: |
139 | explicit MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileCreatingOffer(webrtc::PeerConnectionObserver& observer) : MockLibWebRTCPeerConnection(observer) { } |
140 | virtual ~MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileCreatingOffer() = default; |
141 | |
142 | private: |
143 | void CreateOffer(webrtc::CreateSessionDescriptionObserver* observer, const webrtc::PeerConnectionInterface::RTCOfferAnswerOptions&) final { releaseInNetworkThread(*this, *observer); } |
144 | }; |
145 | |
146 | class MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileGettingStats : public MockLibWebRTCPeerConnection { |
147 | public: |
148 | explicit MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileGettingStats(webrtc::PeerConnectionObserver& observer) : MockLibWebRTCPeerConnection(observer) { } |
149 | virtual ~MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileGettingStats() = default; |
150 | |
151 | private: |
152 | bool GetStats(webrtc::StatsObserver*, webrtc::MediaStreamTrackInterface*, StatsOutputLevel) final; |
153 | }; |
154 | |
155 | bool MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileGettingStats::GetStats(webrtc::StatsObserver* observer, webrtc::MediaStreamTrackInterface*, StatsOutputLevel) |
156 | { |
157 | releaseInNetworkThread(*this, *observer); |
158 | return true; |
159 | } |
160 | |
161 | class MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileSettingDescription : public MockLibWebRTCPeerConnection { |
162 | public: |
163 | explicit MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileSettingDescription(webrtc::PeerConnectionObserver& observer) : MockLibWebRTCPeerConnection(observer) { } |
164 | virtual ~MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileSettingDescription() = default; |
165 | |
166 | private: |
167 | void SetLocalDescription(webrtc::SetSessionDescriptionObserver* observer, webrtc::SessionDescriptionInterface* sessionDescription) final |
168 | { |
169 | std::unique_ptr<webrtc::SessionDescriptionInterface> toBeFreed(sessionDescription); |
170 | releaseInNetworkThread(*this, *observer); |
171 | } |
172 | }; |
173 | |
174 | MockLibWebRTCPeerConnectionFactory::MockLibWebRTCPeerConnectionFactory(const String& testCase) |
175 | : m_testCase(testCase.isolatedCopy()) |
176 | { |
177 | } |
178 | |
179 | rtc::scoped_refptr<webrtc::PeerConnectionInterface> MockLibWebRTCPeerConnectionFactory::CreatePeerConnection(const webrtc::PeerConnectionInterface::RTCConfiguration&, webrtc::PeerConnectionDependencies dependencies) |
180 | { |
181 | if (m_testCase == "ICECandidates" ) |
182 | return new rtc::RefCountedObject<MockLibWebRTCPeerConnectionForIceCandidates>(*dependencies.observer); |
183 | |
184 | if (m_testCase == "ICEConnectionState" ) |
185 | return new rtc::RefCountedObject<MockLibWebRTCPeerConnectionForIceConnectionState>(*dependencies.observer); |
186 | |
187 | if (m_testCase == "LibWebRTCReleasingWhileCreatingOffer" ) |
188 | return new rtc::RefCountedObject<MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileCreatingOffer>(*dependencies.observer); |
189 | |
190 | if (m_testCase == "LibWebRTCReleasingWhileGettingStats" ) |
191 | return new rtc::RefCountedObject<MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileGettingStats>(*dependencies.observer); |
192 | |
193 | if (m_testCase == "LibWebRTCReleasingWhileSettingDescription" ) |
194 | return new rtc::RefCountedObject<MockLibWebRTCPeerConnectionReleasedInNetworkThreadWhileSettingDescription>(*dependencies.observer); |
195 | |
196 | return new rtc::RefCountedObject<MockLibWebRTCPeerConnection>(*dependencies.observer); |
197 | } |
198 | |
199 | rtc::scoped_refptr<webrtc::VideoTrackInterface> MockLibWebRTCPeerConnectionFactory::CreateVideoTrack(const std::string& id, webrtc::VideoTrackSourceInterface* source) |
200 | { |
201 | return new rtc::RefCountedObject<MockLibWebRTCVideoTrack>(id, source); |
202 | } |
203 | |
204 | rtc::scoped_refptr<webrtc::AudioTrackInterface> MockLibWebRTCPeerConnectionFactory::CreateAudioTrack(const std::string& id, webrtc::AudioSourceInterface* source) |
205 | { |
206 | return new rtc::RefCountedObject<MockLibWebRTCAudioTrack>(id, source); |
207 | } |
208 | |
209 | rtc::scoped_refptr<webrtc::MediaStreamInterface> MockLibWebRTCPeerConnectionFactory::CreateLocalMediaStream(const std::string& label) |
210 | { |
211 | return new rtc::RefCountedObject<webrtc::MediaStream>(label); |
212 | } |
213 | |
214 | void MockLibWebRTCPeerConnection::SetLocalDescription(webrtc::SetSessionDescriptionObserver* observer, webrtc::SessionDescriptionInterface* sessionDescription) |
215 | { |
216 | std::unique_ptr<webrtc::SessionDescriptionInterface> toBeFreed(sessionDescription); |
217 | LibWebRTCProvider::callOnWebRTCSignalingThread([this, observer] { |
218 | observer->OnSuccess(); |
219 | gotLocalDescription(); |
220 | }); |
221 | } |
222 | |
223 | void MockLibWebRTCPeerConnection::SetRemoteDescription(webrtc::SetSessionDescriptionObserver* observer, webrtc::SessionDescriptionInterface* sessionDescription) |
224 | { |
225 | std::unique_ptr<webrtc::SessionDescriptionInterface> toBeFreed(sessionDescription); |
226 | LibWebRTCProvider::callOnWebRTCSignalingThread([observer] { |
227 | observer->OnSuccess(); |
228 | }); |
229 | ASSERT(sessionDescription); |
230 | if (sessionDescription->type() == "offer" ) { |
231 | std::string sdp; |
232 | sessionDescription->ToString(&sdp); |
233 | |
234 | m_isInitiator = false; |
235 | m_isReceivingAudio = sdp.find("m=audio" ) != std::string::npos; |
236 | m_isReceivingVideo = sdp.find("m=video" ) != std::string::npos; |
237 | } |
238 | } |
239 | |
240 | rtc::scoped_refptr<webrtc::DataChannelInterface> MockLibWebRTCPeerConnection::CreateDataChannel(const std::string& label, const webrtc::DataChannelInit* init) |
241 | { |
242 | webrtc::DataChannelInit parameters; |
243 | if (init) |
244 | parameters = *init; |
245 | return new rtc::RefCountedObject<MockLibWebRTCDataChannel>(std::string(label), parameters.ordered, parameters.reliable, parameters.id); |
246 | } |
247 | |
248 | webrtc::RTCErrorOr<rtc::scoped_refptr<webrtc::RtpSenderInterface>> MockLibWebRTCPeerConnection::AddTrack(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track, const std::vector<std::string>& streamIds) |
249 | { |
250 | LibWebRTCProvider::callOnWebRTCSignalingThread([observer = &m_observer] { |
251 | observer->OnRenegotiationNeeded(); |
252 | }); |
253 | |
254 | if (!streamIds.empty()) |
255 | m_streamLabel = streamIds.front(); |
256 | |
257 | m_senders.append(new rtc::RefCountedObject<MockRtpSender>(WTFMove(track))); |
258 | return rtc::scoped_refptr<webrtc::RtpSenderInterface>(m_senders.last().get()); |
259 | } |
260 | |
261 | bool MockLibWebRTCPeerConnection::RemoveTrack(webrtc::RtpSenderInterface* sender) |
262 | { |
263 | LibWebRTCProvider::callOnWebRTCSignalingThread([observer = &m_observer] { |
264 | observer->OnRenegotiationNeeded(); |
265 | }); |
266 | bool isRemoved = false; |
267 | return m_senders.removeFirstMatching([&](auto& item) { |
268 | if (item.get() != sender) |
269 | return false; |
270 | isRemoved = true; |
271 | return true; |
272 | }); |
273 | } |
274 | |
275 | void MockLibWebRTCPeerConnection::CreateOffer(webrtc::CreateSessionDescriptionObserver* observer, const webrtc::PeerConnectionInterface::RTCOfferAnswerOptions&) |
276 | { |
277 | LibWebRTCProvider::callOnWebRTCSignalingThread([this, observer] { |
278 | std::ostringstream sdp; |
279 | sdp << |
280 | "v=0\r\n" |
281 | "o=- 5667094644266930845 " << m_counter++ << " IN IP4 127.0.0.1\r\n" |
282 | "s=-\r\n" |
283 | "t=0 0\r\n" ; |
284 | if (m_senders.size()) { |
285 | unsigned partCounter = 1; |
286 | sdp << "a=msid-semantic:WMS " << m_streamLabel << "\r\n" ; |
287 | for (auto& sender : m_senders) { |
288 | auto track = sender->track(); |
289 | if (track->kind() != "audio" ) |
290 | continue; |
291 | sdp << |
292 | "m=audio 9 UDP/TLS/RTP/SAVPF 111 8 0\r\n" |
293 | "c=IN IP4 0.0.0.0\r\n" |
294 | "a=rtcp-mux\r\n" |
295 | "a=sendrecv\r\n" |
296 | "a=mid:part" << partCounter++ << "\r\n" |
297 | "a=rtpmap:111 OPUS/48000/2\r\n" |
298 | "a=rtpmap:8 PCMA/8000\r\n" |
299 | "a=rtpmap:0 PCMU/8000\r\n" |
300 | "a=ssrc:3409173717 cname:/chKzCS9K6KOgL0n\r\n" |
301 | "a=msid:" << m_streamLabel << " " << track->id() << "\r\n" |
302 | "a=ice-ufrag:e/B1\r\n" |
303 | "a=ice-pwd:Yotk3Im3mnyi+1Q38p51MDub\r\n" |
304 | "a=fingerprint:sha-256 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\r\n" |
305 | "a=setup:actpass\r\n" ; |
306 | } |
307 | for (auto& sender : m_senders) { |
308 | auto track = sender->track(); |
309 | if (track->kind() != "video" ) |
310 | continue; |
311 | sdp << |
312 | "m=video 9 UDP/TLS/RTP/SAVPF 103 100 120\r\n" |
313 | "c=IN IP4 0.0.0.0\r\n" |
314 | "a=rtcp-mux\r\n" |
315 | "a=sendrecv\r\n" |
316 | "a=mid:part" << partCounter++ << "\r\n" |
317 | "a=rtpmap:103 H264/90000\r\n" |
318 | "a=rtpmap:100 VP8/90000\r\n" |
319 | "a=rtpmap:120 RTX/90000\r\n" |
320 | "a=fmtp:103 packetization-mode=1\r\n" |
321 | "a=fmtp:120 apt=100;rtx-time=200\r\n" |
322 | "a=rtcp-fb:100 nack\r\n" |
323 | "a=rtcp-fb:103 nack pli\r\n" |
324 | "a=rtcp-fb:100 nack pli\r\n" |
325 | "a=rtcp-fb:103 ccm fir\r\n" |
326 | "a=rtcp-fb:100 ccm fir\r\n" |
327 | "a=ssrc:3409173718 cname:/chKzCS9K6KOgL0n\r\n" |
328 | "a=msid:" << m_streamLabel << " " << track->id() << "\r\n" |
329 | "a=ice-ufrag:e/B1\r\n" |
330 | "a=ice-pwd:Yotk3Im3mnyi+1Q38p51MDub\r\n" |
331 | "a=fingerprint:sha-256 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\r\n" |
332 | "a=setup:actpass\r\n" ; |
333 | } |
334 | } |
335 | observer->OnSuccess(new MockLibWebRTCSessionDescription(sdp.str())); |
336 | }); |
337 | } |
338 | |
339 | void MockLibWebRTCPeerConnection::CreateAnswer(webrtc::CreateSessionDescriptionObserver* observer, const webrtc::PeerConnectionInterface::RTCOfferAnswerOptions&) |
340 | { |
341 | LibWebRTCProvider::callOnWebRTCSignalingThread([this, observer] { |
342 | std::ostringstream sdp; |
343 | sdp << |
344 | "v=0\r\n" |
345 | "o=- 5667094644266930846 " << m_counter++ << " IN IP4 127.0.0.1\r\n" |
346 | "s=-\r\n" |
347 | "t=0 0\r\n" ; |
348 | if (m_senders.size()) { |
349 | for (auto& sender : m_senders) { |
350 | auto track = sender->track(); |
351 | if (track->kind() != "audio" ) |
352 | continue; |
353 | sdp << |
354 | "m=audio 9 UDP/TLS/RTP/SAVPF 111 8 0\r\n" |
355 | "c=IN IP4 0.0.0.0\r\n" |
356 | "a=rtcp-mux\r\n" |
357 | "a=recvonly\r\n" |
358 | "a=mid:part1\r\n" |
359 | "a=rtpmap:111 OPUS/48000/2\r\n" |
360 | "a=rtpmap:8 PCMA/8000\r\n" |
361 | "a=rtpmap:0 PCMU/8000\r\n" |
362 | "a=ssrc:3409173717 cname:/chKzCS9K6KOgL0m\r\n" |
363 | "a=ice-ufrag:e/B1\r\n" |
364 | "a=ice-pwd:Yotk3Im3mnyi+1Q38p51MDub\r\n" |
365 | "a=fingerprint:sha-256 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\r\n" |
366 | "a=setup:active\r\n" ; |
367 | } |
368 | for (auto& sender : m_senders) { |
369 | auto track = sender->track(); |
370 | if (track->kind() != "video" ) |
371 | continue; |
372 | sdp << |
373 | "m=video 9 UDP/TLS/RTP/SAVPF 103 100 120\r\n" |
374 | "c=IN IP4 0.0.0.0\r\n" |
375 | "a=rtcp-mux\r\n" |
376 | "a=recvonly\r\n" |
377 | "a=mid:part2\r\n" |
378 | "a=rtpmap:103 H264/90000\r\n" |
379 | "a=rtpmap:100 VP8/90000\r\n" |
380 | "a=rtpmap:120 RTX/90000\r\n" |
381 | "a=fmtp:103 packetization-mode=1\r\n" |
382 | "a=fmtp:120 apt=100;rtx-time=200\r\n" |
383 | "a=rtcp-fb:100 nack\r\n" |
384 | "a=rtcp-fb:103 nack pli\r\n" |
385 | "a=rtcp-fb:100 nack pli\r\n" |
386 | "a=rtcp-fb:103 ccm fir\r\n" |
387 | "a=rtcp-fb:100 ccm fir\r\n" |
388 | "a=ssrc:3409173718 cname:/chKzCS9K6KOgL0n\r\n" |
389 | "a=ice-ufrag:e/B1\r\n" |
390 | "a=ice-pwd:Yotk3Im3mnyi+1Q38p51MDub\r\n" |
391 | "a=fingerprint:sha-256 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\r\n" |
392 | "a=setup:active\r\n" ; |
393 | } |
394 | } else if (!m_isInitiator) { |
395 | if (m_isReceivingAudio) { |
396 | sdp << |
397 | "m=audio 9 UDP/TLS/RTP/SAVPF 111 8 0\r\n" |
398 | "c=IN IP4 0.0.0.0\r\n" |
399 | "a=rtcp-mux\r\n" |
400 | "a=recvonly\r\n" |
401 | "a=mid:part1\r\n" |
402 | "a=rtpmap:111 OPUS/48000/2\r\n" |
403 | "a=rtpmap:8 PCMA/8000\r\n" |
404 | "a=rtpmap:0 PCMU/8000\r\n" |
405 | "a=ssrc:3409173717 cname:/chKzCS9K6KOgL0m\r\n" |
406 | "a=ice-ufrag:e/B1\r\n" |
407 | "a=ice-pwd:Yotk3Im3mnyi+1Q38p51MDub\r\n" |
408 | "a=fingerprint:sha-256 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\r\n" |
409 | "a=setup:active\r\n" ; |
410 | } |
411 | if (m_isReceivingVideo) { |
412 | sdp << |
413 | "m=video 9 UDP/TLS/RTP/SAVPF 103 100 120\r\n" |
414 | "c=IN IP4 0.0.0.0\r\n" |
415 | "a=rtcp-mux\r\n" |
416 | "a=recvonly\r\n" |
417 | "a=mid:part2\r\n" |
418 | "a=rtpmap:103 H264/90000\r\n" |
419 | "a=rtpmap:100 VP8/90000\r\n" |
420 | "a=rtpmap:120 RTX/90000\r\n" |
421 | "a=fmtp:103 packetization-mode=1\r\n" |
422 | "a=fmtp:120 apt=100;rtx-time=200\r\n" |
423 | "a=rtcp-fb:100 nack\r\n" |
424 | "a=rtcp-fb:103 nack pli\r\n" |
425 | "a=rtcp-fb:100 nack pli\r\n" |
426 | "a=rtcp-fb:103 ccm fir\r\n" |
427 | "a=rtcp-fb:100 ccm fir\r\n" |
428 | "a=ssrc:3409173718 cname:/chKzCS9K6KOgL0n\r\n" |
429 | "a=ice-ufrag:e/B1\r\n" |
430 | "a=ice-pwd:Yotk3Im3mnyi+1Q38p51MDub\r\n" |
431 | "a=fingerprint:sha-256 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\r\n" |
432 | "a=setup:active\r\n" ; |
433 | } |
434 | } |
435 | observer->OnSuccess(new MockLibWebRTCSessionDescription(sdp.str())); |
436 | }); |
437 | } |
438 | |
439 | } // namespace WebCore |
440 | |
441 | #endif // USE(LIBWEBRTC) |
442 | |