1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver_impl.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/memory/scoped_refptr.h"
13 #include "base/optional.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "build/build_config.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
20 #include "third_party/blink/public/platform/web_media_stream_source.h"
21 #include "third_party/blink/public/platform/web_string.h"
22 #include "third_party/blink/public/web/web_heap.h"
23 #include "third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.h"
24 #include "third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.h"
25 #include "third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
26 #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h"
27 #include "third_party/blink/renderer/platform/peerconnection/webrtc_util.h"
28 #include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
29 #include "third_party/webrtc/api/test/mock_rtpreceiver.h"
30 #include "third_party/webrtc/api/test/mock_rtpsender.h"
31
32 namespace blink {
33
34 class RTCRtpTransceiverImplTest : public ::testing::Test {
35 public:
SetUp()36 void SetUp() override {
37 dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
38 main_task_runner_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
39 track_map_ = base::MakeRefCounted<blink::WebRtcMediaStreamTrackAdapterMap>(
40 dependency_factory_.get(), main_task_runner_);
41 peer_connection_ = new rtc::RefCountedObject<blink::MockPeerConnectionImpl>(
42 dependency_factory_.get(), nullptr);
43 }
44
TearDown()45 void TearDown() override {
46 // Syncing up with the signaling thread ensures any pending operations on
47 // that thread are executed. If they post back to the main thread, such as
48 // the sender or receiver destructor traits, this is allowed to execute
49 // before the test shuts down the threads.
50 SyncWithSignalingThread();
51 blink::WebHeap::CollectAllGarbageForTesting();
52 }
53
54 // Wait for the signaling thread to perform any queued tasks, executing tasks
55 // posted to the current thread in the meantime while waiting.
SyncWithSignalingThread() const56 void SyncWithSignalingThread() const {
57 base::RunLoop run_loop;
58 dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
59 FROM_HERE, run_loop.QuitClosure());
60 run_loop.Run();
61 }
62
signaling_task_runner() const63 scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner() const {
64 return dependency_factory_->GetWebRtcSignalingTaskRunner();
65 }
66
67 std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
CreateLocalTrackAndAdapter(const std::string & id)68 CreateLocalTrackAndAdapter(const std::string& id) {
69 return track_map_->GetOrCreateLocalTrackAdapter(CreateBlinkLocalTrack(id));
70 }
71
72 std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
CreateRemoteTrackAndAdapter(const std::string & id)73 CreateRemoteTrackAndAdapter(const std::string& id) {
74 rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track =
75 blink::MockWebRtcAudioTrack::Create(id).get();
76 std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
77 track_ref;
78 base::RunLoop run_loop;
79 signaling_task_runner()->PostTask(
80 FROM_HERE,
81 base::BindOnce(&RTCRtpTransceiverImplTest::
82 CreateRemoteTrackAdapterOnSignalingThread,
83 base::Unretained(this), std::move(webrtc_track),
84 base::Unretained(&track_ref),
85 base::Unretained(&run_loop)));
86 run_loop.Run();
87 DCHECK(track_ref);
88 return track_ref;
89 }
90
CreateWebRtcSender(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,const std::string & stream_id)91 rtc::scoped_refptr<blink::FakeRtpSender> CreateWebRtcSender(
92 rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
93 const std::string& stream_id) {
94 return new rtc::RefCountedObject<blink::FakeRtpSender>(
95 std::move(track), std::vector<std::string>({stream_id}));
96 }
97
CreateWebRtcReceiver(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,const std::string & stream_id)98 rtc::scoped_refptr<blink::FakeRtpReceiver> CreateWebRtcReceiver(
99 rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
100 const std::string& stream_id) {
101 rtc::scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
102 new rtc::RefCountedObject<blink::MockMediaStream>(stream_id));
103 return new rtc::RefCountedObject<blink::FakeRtpReceiver>(
104 track.get(),
105 std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
106 {remote_stream}));
107 }
108
CreateWebRtcTransceiver(rtc::scoped_refptr<blink::FakeRtpSender> sender,rtc::scoped_refptr<blink::FakeRtpReceiver> receiver,base::Optional<std::string> mid,bool stopped,webrtc::RtpTransceiverDirection direction,base::Optional<webrtc::RtpTransceiverDirection> current_direction)109 rtc::scoped_refptr<blink::FakeRtpTransceiver> CreateWebRtcTransceiver(
110 rtc::scoped_refptr<blink::FakeRtpSender> sender,
111 rtc::scoped_refptr<blink::FakeRtpReceiver> receiver,
112 base::Optional<std::string> mid,
113 bool stopped,
114 webrtc::RtpTransceiverDirection direction,
115 base::Optional<webrtc::RtpTransceiverDirection> current_direction) {
116 DCHECK(!sender->track() ||
117 sender->track()->kind() == receiver->track()->kind());
118 return new rtc::RefCountedObject<blink::FakeRtpTransceiver>(
119 receiver->track()->kind() ==
120 webrtc::MediaStreamTrackInterface::kAudioKind
121 ? cricket::MEDIA_TYPE_AUDIO
122 : cricket::MEDIA_TYPE_VIDEO,
123 std::move(sender), std::move(receiver), std::move(mid), stopped,
124 direction, std::move(current_direction));
125 }
126
CreateTransceiverState(rtc::scoped_refptr<webrtc::RtpTransceiverInterface> webrtc_transceiver,std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef> sender_track_ref,std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef> receiver_track_ref)127 RtpTransceiverState CreateTransceiverState(
128 rtc::scoped_refptr<webrtc::RtpTransceiverInterface> webrtc_transceiver,
129 std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
130 sender_track_ref,
131 std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
132 receiver_track_ref) {
133 std::vector<std::string> receiver_stream_ids;
134 for (const auto& stream : webrtc_transceiver->receiver()->streams()) {
135 receiver_stream_ids.push_back(stream->id());
136 }
137 return RtpTransceiverState(
138 main_task_runner_, signaling_task_runner(), webrtc_transceiver.get(),
139 blink::RtpSenderState(main_task_runner_, signaling_task_runner(),
140 webrtc_transceiver->sender().get(),
141 std::move(sender_track_ref),
142 webrtc_transceiver->sender()->stream_ids()),
143 blink::RtpReceiverState(main_task_runner_, signaling_task_runner(),
144 webrtc_transceiver->receiver().get(),
145 std::move(receiver_track_ref),
146 std::move(receiver_stream_ids)),
147 blink::ToBaseOptional(webrtc_transceiver->mid()),
148 webrtc_transceiver->stopped(), webrtc_transceiver->direction(),
149 blink::ToBaseOptional(webrtc_transceiver->current_direction()),
150 blink::ToBaseOptional(webrtc_transceiver->fired_direction()));
151 }
152
153 protected:
CreateBlinkLocalTrack(const std::string & id)154 blink::WebMediaStreamTrack CreateBlinkLocalTrack(const std::string& id) {
155 blink::WebMediaStreamSource web_source;
156 web_source.Initialize(
157 blink::WebString::FromUTF8(id), blink::WebMediaStreamSource::kTypeAudio,
158 blink::WebString::FromUTF8("local_audio_track"), false);
159 blink::MediaStreamAudioSource* audio_source =
160 new blink::MediaStreamAudioSource(
161 blink::scheduler::GetSingleThreadTaskRunnerForTesting(), true);
162 // Takes ownership of |audio_source|.
163 web_source.SetPlatformSource(base::WrapUnique(audio_source));
164
165 blink::WebMediaStreamTrack web_track;
166 web_track.Initialize(web_source.Id(), web_source);
167 audio_source->ConnectToTrack(web_track);
168 return web_track;
169 }
170
CreateRemoteTrackAdapterOnSignalingThread(rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track,std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef> * track_ref,base::RunLoop * run_loop)171 void CreateRemoteTrackAdapterOnSignalingThread(
172 rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track,
173 std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>*
174 track_ref,
175 base::RunLoop* run_loop) {
176 *track_ref = track_map_->GetOrCreateRemoteTrackAdapter(webrtc_track.get());
177 run_loop->Quit();
178 }
179
180 private:
181 ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
182
183 protected:
184 std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
185 dependency_factory_;
186 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
187 scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map_;
188 rtc::scoped_refptr<blink::MockPeerConnectionImpl> peer_connection_;
189 };
190
TEST_F(RTCRtpTransceiverImplTest,InitializeTransceiverState)191 TEST_F(RTCRtpTransceiverImplTest, InitializeTransceiverState) {
192 auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
193 auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
194 auto webrtc_transceiver = CreateWebRtcTransceiver(
195 CreateWebRtcSender(local_track_adapter->webrtc_track(), "local_stream"),
196 CreateWebRtcReceiver(remote_track_adapter->webrtc_track(),
197 "remote_stream"),
198 base::nullopt, false, webrtc::RtpTransceiverDirection::kSendRecv,
199 base::nullopt);
200 RtpTransceiverState transceiver_state =
201 CreateTransceiverState(webrtc_transceiver, std::move(local_track_adapter),
202 std::move(remote_track_adapter));
203 EXPECT_FALSE(transceiver_state.is_initialized());
204 transceiver_state.Initialize();
205
206 EXPECT_TRUE(transceiver_state.is_initialized());
207 // Inspect sender states.
208 const auto& sender_state = transceiver_state.sender_state();
209 EXPECT_TRUE(sender_state);
210 EXPECT_TRUE(sender_state->is_initialized());
211 const auto& webrtc_sender = webrtc_transceiver->sender();
212 EXPECT_EQ(sender_state->webrtc_sender().get(), webrtc_sender.get());
213 EXPECT_TRUE(sender_state->track_ref()->is_initialized());
214 EXPECT_EQ(sender_state->track_ref()->webrtc_track(),
215 webrtc_sender->track().get());
216 EXPECT_EQ(sender_state->stream_ids(), webrtc_sender->stream_ids());
217 // Inspect receiver states.
218 const auto& receiver_state = transceiver_state.receiver_state();
219 EXPECT_TRUE(receiver_state);
220 EXPECT_TRUE(receiver_state->is_initialized());
221 const auto& webrtc_receiver = webrtc_transceiver->receiver();
222 EXPECT_EQ(receiver_state->webrtc_receiver().get(), webrtc_receiver.get());
223 EXPECT_TRUE(receiver_state->track_ref()->is_initialized());
224 EXPECT_EQ(receiver_state->track_ref()->webrtc_track(),
225 webrtc_receiver->track().get());
226 std::vector<std::string> receiver_stream_ids;
227 for (const auto& stream : webrtc_receiver->streams()) {
228 receiver_stream_ids.push_back(stream->id());
229 }
230 EXPECT_EQ(receiver_state->stream_ids(), receiver_stream_ids);
231 // Inspect transceiver states.
232 EXPECT_TRUE(blink::OptionalEquals(transceiver_state.mid(),
233 webrtc_transceiver->mid()));
234 EXPECT_EQ(transceiver_state.stopped(), webrtc_transceiver->stopped());
235 EXPECT_TRUE(transceiver_state.direction() == webrtc_transceiver->direction());
236 EXPECT_TRUE(blink::OptionalEquals(transceiver_state.current_direction(),
237 webrtc_transceiver->current_direction()));
238 EXPECT_TRUE(blink::OptionalEquals(transceiver_state.fired_direction(),
239 webrtc_transceiver->fired_direction()));
240 }
241
TEST_F(RTCRtpTransceiverImplTest,CreateTranceiver)242 TEST_F(RTCRtpTransceiverImplTest, CreateTranceiver) {
243 auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
244 auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
245 auto webrtc_transceiver = CreateWebRtcTransceiver(
246 CreateWebRtcSender(local_track_adapter->webrtc_track(), "local_stream"),
247 CreateWebRtcReceiver(remote_track_adapter->webrtc_track(),
248 "remote_stream"),
249 base::nullopt, false, webrtc::RtpTransceiverDirection::kSendRecv,
250 base::nullopt);
251 RtpTransceiverState transceiver_state =
252 CreateTransceiverState(webrtc_transceiver, std::move(local_track_adapter),
253 std::move(remote_track_adapter));
254 EXPECT_FALSE(transceiver_state.is_initialized());
255 transceiver_state.Initialize();
256
257 RTCRtpTransceiverImpl transceiver(
258 peer_connection_.get(), track_map_, std::move(transceiver_state),
259 /*force_encoded_audio_insertable_streams=*/false,
260 /*force_encoded_video_insertable_streams=*/false);
261 EXPECT_TRUE(transceiver.Mid().IsNull());
262 EXPECT_TRUE(transceiver.Sender());
263 EXPECT_TRUE(transceiver.Receiver());
264 EXPECT_FALSE(transceiver.Stopped());
265 EXPECT_EQ(transceiver.Direction(),
266 webrtc::RtpTransceiverDirection::kSendRecv);
267 EXPECT_FALSE(transceiver.CurrentDirection());
268 EXPECT_FALSE(transceiver.FiredDirection());
269 }
270
TEST_F(RTCRtpTransceiverImplTest,ModifyTransceiver)271 TEST_F(RTCRtpTransceiverImplTest, ModifyTransceiver) {
272 auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
273 auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
274 auto webrtc_sender =
275 CreateWebRtcSender(local_track_adapter->webrtc_track(), "local_stream");
276 auto webrtc_receiver = CreateWebRtcReceiver(
277 remote_track_adapter->webrtc_track(), "remote_stream");
278 auto webrtc_transceiver = CreateWebRtcTransceiver(
279 webrtc_sender, webrtc_receiver, base::nullopt, false,
280 webrtc::RtpTransceiverDirection::kSendRecv, base::nullopt);
281
282 // Create initial state.
283 RtpTransceiverState initial_transceiver_state =
284 CreateTransceiverState(webrtc_transceiver, local_track_adapter->Copy(),
285 remote_track_adapter->Copy());
286 EXPECT_FALSE(initial_transceiver_state.is_initialized());
287 initial_transceiver_state.Initialize();
288
289 // Modify the webrtc transceiver and create a new state object for the
290 // modified state.
291 webrtc_transceiver->ReplaceWith(
292 *CreateWebRtcTransceiver(webrtc_sender, webrtc_receiver, "MidyMacMidface",
293 true, webrtc::RtpTransceiverDirection::kInactive,
294 webrtc::RtpTransceiverDirection::kSendRecv));
295 RtpTransceiverState modified_transceiver_state =
296 CreateTransceiverState(webrtc_transceiver, local_track_adapter->Copy(),
297 remote_track_adapter->Copy());
298 EXPECT_FALSE(modified_transceiver_state.is_initialized());
299 modified_transceiver_state.Initialize();
300
301 // Modifying the webrtc transceiver after the initial state was created should
302 // not have affected the transceiver state.
303 RTCRtpTransceiverImpl transceiver(
304 peer_connection_.get(), track_map_, std::move(initial_transceiver_state),
305 /*force_encoded_audio_insertable_streams=*/false,
306 /*force_encoded_video_insertable_streams=*/false);
307 EXPECT_TRUE(transceiver.Mid().IsNull());
308 EXPECT_TRUE(transceiver.Sender());
309 EXPECT_TRUE(transceiver.Receiver());
310 EXPECT_FALSE(transceiver.Stopped());
311 EXPECT_EQ(transceiver.Direction(),
312 webrtc::RtpTransceiverDirection::kSendRecv);
313 EXPECT_FALSE(transceiver.CurrentDirection());
314 EXPECT_FALSE(transceiver.FiredDirection());
315
316 // Setting the state should make the transceiver state up-to-date.
317 transceiver.set_state(std::move(modified_transceiver_state),
318 TransceiverStateUpdateMode::kAll);
319 EXPECT_EQ(transceiver.Mid(), "MidyMacMidface");
320 EXPECT_TRUE(transceiver.Sender());
321 EXPECT_TRUE(transceiver.Receiver());
322 EXPECT_TRUE(transceiver.Stopped());
323 EXPECT_EQ(transceiver.Direction(),
324 webrtc::RtpTransceiverDirection::kInactive);
325 EXPECT_TRUE(transceiver.CurrentDirection() ==
326 webrtc::RtpTransceiverDirection::kSendRecv);
327 EXPECT_FALSE(transceiver.FiredDirection());
328 }
329
TEST_F(RTCRtpTransceiverImplTest,ShallowCopy)330 TEST_F(RTCRtpTransceiverImplTest, ShallowCopy) {
331 auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
332 auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
333 auto webrtc_sender =
334 CreateWebRtcSender(local_track_adapter->webrtc_track(), "local_stream");
335 auto webrtc_receiver = CreateWebRtcReceiver(
336 remote_track_adapter->webrtc_track(), "remote_stream");
337 auto webrtc_transceiver = CreateWebRtcTransceiver(
338 webrtc_sender, webrtc_receiver, base::nullopt, false /* stopped */,
339 webrtc::RtpTransceiverDirection::kSendRecv, base::nullopt);
340
341 std::unique_ptr<RTCRtpTransceiverImpl> transceiver;
342 // Create transceiver.
343 {
344 RtpTransceiverState transceiver_state =
345 CreateTransceiverState(webrtc_transceiver, local_track_adapter->Copy(),
346 remote_track_adapter->Copy());
347 EXPECT_FALSE(transceiver_state.is_initialized());
348 transceiver_state.Initialize();
349 transceiver.reset(new RTCRtpTransceiverImpl(
350 peer_connection_.get(), track_map_, std::move(transceiver_state),
351 /*force_encoded_audio_insertable_streams=*/false,
352 /*force_encoded_video_insertable_streams=*/false));
353 }
354 DCHECK(transceiver);
355 EXPECT_FALSE(transceiver->Stopped());
356
357 std::unique_ptr<RTCRtpTransceiverImpl> shallow_copy =
358 transceiver->ShallowCopy();
359 // Modifying the shallow copy should modify the original too since they have a
360 // shared internal state.
361 {
362 // Modify webrtc transceiver to be stopped.
363 webrtc_transceiver->ReplaceWith(*CreateWebRtcTransceiver(
364 webrtc_sender, webrtc_receiver, base::nullopt, true /* stopped */,
365 webrtc::RtpTransceiverDirection::kSendRecv, base::nullopt));
366 RtpTransceiverState transceiver_state =
367 CreateTransceiverState(webrtc_transceiver, local_track_adapter->Copy(),
368 remote_track_adapter->Copy());
369 EXPECT_FALSE(transceiver_state.is_initialized());
370 transceiver_state.Initialize();
371 // Set the state of the shallow copy.
372 shallow_copy->set_state(std::move(transceiver_state),
373 TransceiverStateUpdateMode::kAll);
374 }
375 EXPECT_TRUE(shallow_copy->Stopped());
376 EXPECT_TRUE(transceiver->Stopped());
377 }
378
TEST_F(RTCRtpTransceiverImplTest,TransceiverStateUpdateModeSetDescription)379 TEST_F(RTCRtpTransceiverImplTest, TransceiverStateUpdateModeSetDescription) {
380 auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
381 auto remote_track_adapter = CreateRemoteTrackAndAdapter("remote_track");
382 auto webrtc_sender =
383 CreateWebRtcSender(local_track_adapter->webrtc_track(), "local_stream");
384 auto webrtc_receiver = CreateWebRtcReceiver(
385 remote_track_adapter->webrtc_track(), "remote_stream");
386 auto webrtc_transceiver = CreateWebRtcTransceiver(
387 webrtc_sender, webrtc_receiver, base::nullopt, false,
388 webrtc::RtpTransceiverDirection::kSendRecv, base::nullopt);
389
390 // Create initial state.
391 RtpTransceiverState initial_transceiver_state =
392 CreateTransceiverState(webrtc_transceiver, local_track_adapter->Copy(),
393 remote_track_adapter->Copy());
394 EXPECT_FALSE(initial_transceiver_state.is_initialized());
395 initial_transceiver_state.Initialize();
396
397 // Modify the webrtc transceiver and create a new state object for the
398 // modified state.
399 webrtc_sender->SetTrack(nullptr);
400 webrtc_transceiver->ReplaceWith(
401 *CreateWebRtcTransceiver(webrtc_sender, webrtc_receiver, "MidyMacMidface",
402 true, webrtc::RtpTransceiverDirection::kInactive,
403 webrtc::RtpTransceiverDirection::kSendRecv));
404 RtpTransceiverState modified_transceiver_state =
405 CreateTransceiverState(webrtc_transceiver, local_track_adapter->Copy(),
406 remote_track_adapter->Copy());
407 EXPECT_FALSE(modified_transceiver_state.is_initialized());
408 modified_transceiver_state.Initialize();
409
410 // Construct a transceiver from the initial state.
411 RTCRtpTransceiverImpl transceiver(
412 peer_connection_.get(), track_map_, std::move(initial_transceiver_state),
413 /*force_encoded_audio_insertable_streams=*/false,
414 /*force_encoded_video_insertable_streams=*/false);
415 // Setting the state with TransceiverStateUpdateMode::kSetDescription should
416 // make the transceiver state up-to-date, except leaving
417 // "transceiver.direction" and "transceiver.sender.track" unmodified.
418 transceiver.set_state(std::move(modified_transceiver_state),
419 TransceiverStateUpdateMode::kSetDescription);
420 EXPECT_EQ(transceiver.Mid(), "MidyMacMidface");
421 EXPECT_TRUE(transceiver.Sender());
422 EXPECT_TRUE(transceiver.Receiver());
423 EXPECT_TRUE(transceiver.Stopped());
424 EXPECT_TRUE(transceiver.CurrentDirection() ==
425 webrtc::RtpTransceiverDirection::kSendRecv);
426 EXPECT_FALSE(transceiver.FiredDirection());
427 // The sender still has a track, even though the modified state doesn't.
428 EXPECT_FALSE(transceiver.Sender()->Track().IsNull());
429 // The direction still "sendrecv", even though the modified state has
430 // "inactive".
431 EXPECT_EQ(transceiver.Direction(),
432 webrtc::RtpTransceiverDirection::kSendRecv);
433 }
434
435 } // namespace blink
436