OLD | NEW |
1 /* | 1 /* |
2 * libjingle | 2 * libjingle |
3 * Copyright 2012 Google Inc. | 3 * Copyright 2012 Google Inc. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 const PeerConnectionFactory::Options* options) { | 164 const PeerConnectionFactory::Options* options) { |
165 rtc::scoped_ptr<FakeDtlsIdentityStore> dtls_identity_store( | 165 rtc::scoped_ptr<FakeDtlsIdentityStore> dtls_identity_store( |
166 rtc::SSLStreamAdapter::HaveDtlsSrtp() ? new FakeDtlsIdentityStore() | 166 rtc::SSLStreamAdapter::HaveDtlsSrtp() ? new FakeDtlsIdentityStore() |
167 : nullptr); | 167 : nullptr); |
168 | 168 |
169 return CreateClientWithDtlsIdentityStore(id, constraints, options, | 169 return CreateClientWithDtlsIdentityStore(id, constraints, options, |
170 dtls_identity_store.Pass()); | 170 dtls_identity_store.Pass()); |
171 } | 171 } |
172 | 172 |
173 ~PeerConnectionTestClient() { | 173 ~PeerConnectionTestClient() { |
174 while (!fake_video_renderers_.empty()) { | |
175 RenderMap::iterator it = fake_video_renderers_.begin(); | |
176 delete it->second; | |
177 fake_video_renderers_.erase(it); | |
178 } | |
179 } | 174 } |
180 | 175 |
181 void Negotiate() { Negotiate(true, true); } | 176 void Negotiate() { Negotiate(true, true); } |
182 | 177 |
183 void Negotiate(bool audio, bool video) { | 178 void Negotiate(bool audio, bool video) { |
184 rtc::scoped_ptr<SessionDescriptionInterface> offer; | 179 rtc::scoped_ptr<SessionDescriptionInterface> offer; |
185 ASSERT_TRUE(DoCreateOffer(offer.use())); | 180 ASSERT_TRUE(DoCreateOffer(offer.use())); |
186 | 181 |
187 if (offer->description()->GetContentByName("audio")) { | 182 if (offer->description()->GetContentByName("audio")) { |
188 offer->description()->GetContentByName("audio")->rejected = !audio; | 183 offer->description()->GetContentByName("audio")->rejected = !audio; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
222 void OnSignalingChange( | 217 void OnSignalingChange( |
223 webrtc::PeerConnectionInterface::SignalingState new_state) override { | 218 webrtc::PeerConnectionInterface::SignalingState new_state) override { |
224 EXPECT_EQ(pc()->signaling_state(), new_state); | 219 EXPECT_EQ(pc()->signaling_state(), new_state); |
225 } | 220 } |
226 void OnAddStream(MediaStreamInterface* media_stream) override { | 221 void OnAddStream(MediaStreamInterface* media_stream) override { |
227 media_stream->RegisterObserver(this); | 222 media_stream->RegisterObserver(this); |
228 for (size_t i = 0; i < media_stream->GetVideoTracks().size(); ++i) { | 223 for (size_t i = 0; i < media_stream->GetVideoTracks().size(); ++i) { |
229 const std::string id = media_stream->GetVideoTracks()[i]->id(); | 224 const std::string id = media_stream->GetVideoTracks()[i]->id(); |
230 ASSERT_TRUE(fake_video_renderers_.find(id) == | 225 ASSERT_TRUE(fake_video_renderers_.find(id) == |
231 fake_video_renderers_.end()); | 226 fake_video_renderers_.end()); |
232 fake_video_renderers_[id] = | 227 fake_video_renderers_[id].reset(new webrtc::FakeVideoTrackRenderer( |
233 new webrtc::FakeVideoTrackRenderer(media_stream->GetVideoTracks()[i]); | 228 media_stream->GetVideoTracks()[i])); |
234 } | 229 } |
235 } | 230 } |
236 void OnRemoveStream(MediaStreamInterface* media_stream) override {} | 231 void OnRemoveStream(MediaStreamInterface* media_stream) override {} |
237 void OnRenegotiationNeeded() override {} | 232 void OnRenegotiationNeeded() override {} |
238 void OnIceConnectionChange( | 233 void OnIceConnectionChange( |
239 webrtc::PeerConnectionInterface::IceConnectionState new_state) override { | 234 webrtc::PeerConnectionInterface::IceConnectionState new_state) override { |
240 EXPECT_EQ(pc()->ice_connection_state(), new_state); | 235 EXPECT_EQ(pc()->ice_connection_state(), new_state); |
241 } | 236 } |
242 void OnIceGatheringChange( | 237 void OnIceGatheringChange( |
243 webrtc::PeerConnectionInterface::IceGatheringState new_state) override { | 238 webrtc::PeerConnectionInterface::IceGatheringState new_state) override { |
(...skipping 14 matching lines...) Expand all Loading... |
258 | 253 |
259 // MediaStreamInterface callback | 254 // MediaStreamInterface callback |
260 void OnChanged() override { | 255 void OnChanged() override { |
261 // Track added or removed from MediaStream, so update our renderers. | 256 // Track added or removed from MediaStream, so update our renderers. |
262 rtc::scoped_refptr<StreamCollectionInterface> remote_streams = | 257 rtc::scoped_refptr<StreamCollectionInterface> remote_streams = |
263 pc()->remote_streams(); | 258 pc()->remote_streams(); |
264 // Remove renderers for tracks that were removed. | 259 // Remove renderers for tracks that were removed. |
265 for (auto it = fake_video_renderers_.begin(); | 260 for (auto it = fake_video_renderers_.begin(); |
266 it != fake_video_renderers_.end();) { | 261 it != fake_video_renderers_.end();) { |
267 if (remote_streams->FindVideoTrack(it->first) == nullptr) { | 262 if (remote_streams->FindVideoTrack(it->first) == nullptr) { |
268 auto to_delete = it++; | 263 auto to_remove = it++; |
269 delete to_delete->second; | 264 removed_fake_video_renderers_.push_back(std::move(to_remove->second)); |
270 fake_video_renderers_.erase(to_delete); | 265 fake_video_renderers_.erase(to_remove); |
271 } else { | 266 } else { |
272 ++it; | 267 ++it; |
273 } | 268 } |
274 } | 269 } |
275 // Create renderers for new video tracks. | 270 // Create renderers for new video tracks. |
276 for (size_t stream_index = 0; stream_index < remote_streams->count(); | 271 for (size_t stream_index = 0; stream_index < remote_streams->count(); |
277 ++stream_index) { | 272 ++stream_index) { |
278 MediaStreamInterface* remote_stream = remote_streams->at(stream_index); | 273 MediaStreamInterface* remote_stream = remote_streams->at(stream_index); |
279 for (size_t track_index = 0; | 274 for (size_t track_index = 0; |
280 track_index < remote_stream->GetVideoTracks().size(); | 275 track_index < remote_stream->GetVideoTracks().size(); |
281 ++track_index) { | 276 ++track_index) { |
282 const std::string id = | 277 const std::string id = |
283 remote_stream->GetVideoTracks()[track_index]->id(); | 278 remote_stream->GetVideoTracks()[track_index]->id(); |
284 if (fake_video_renderers_.find(id) != fake_video_renderers_.end()) { | 279 if (fake_video_renderers_.find(id) != fake_video_renderers_.end()) { |
285 continue; | 280 continue; |
286 } | 281 } |
287 fake_video_renderers_[id] = new webrtc::FakeVideoTrackRenderer( | 282 fake_video_renderers_[id].reset(new webrtc::FakeVideoTrackRenderer( |
288 remote_stream->GetVideoTracks()[track_index]); | 283 remote_stream->GetVideoTracks()[track_index])); |
289 } | 284 } |
290 } | 285 } |
291 } | 286 } |
292 | 287 |
293 void SetVideoConstraints(const webrtc::FakeConstraints& video_constraint) { | 288 void SetVideoConstraints(const webrtc::FakeConstraints& video_constraint) { |
294 video_constraints_ = video_constraint; | 289 video_constraints_ = video_constraint; |
295 } | 290 } |
296 | 291 |
297 void AddMediaStream(bool audio, bool video) { | 292 void AddMediaStream(bool audio, bool video) { |
298 std::string stream_label = | 293 std::string stream_label = |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 video_capturers_.begin(); | 440 video_capturers_.begin(); |
446 it != video_capturers_.end(); ++it) { | 441 it != video_capturers_.end(); ++it) { |
447 (*it)->Stop(); | 442 (*it)->Stop(); |
448 } | 443 } |
449 } | 444 } |
450 | 445 |
451 bool AudioFramesReceivedCheck(int number_of_frames) const { | 446 bool AudioFramesReceivedCheck(int number_of_frames) const { |
452 return number_of_frames <= fake_audio_capture_module_->frames_received(); | 447 return number_of_frames <= fake_audio_capture_module_->frames_received(); |
453 } | 448 } |
454 | 449 |
| 450 int audio_frames_received() const { |
| 451 return fake_audio_capture_module_->frames_received(); |
| 452 } |
| 453 |
455 bool VideoFramesReceivedCheck(int number_of_frames) { | 454 bool VideoFramesReceivedCheck(int number_of_frames) { |
456 if (video_decoder_factory_enabled_) { | 455 if (video_decoder_factory_enabled_) { |
457 const std::vector<FakeWebRtcVideoDecoder*>& decoders | 456 const std::vector<FakeWebRtcVideoDecoder*>& decoders |
458 = fake_video_decoder_factory_->decoders(); | 457 = fake_video_decoder_factory_->decoders(); |
459 if (decoders.empty()) { | 458 if (decoders.empty()) { |
460 return number_of_frames <= 0; | 459 return number_of_frames <= 0; |
461 } | 460 } |
462 | 461 |
463 for (std::vector<FakeWebRtcVideoDecoder*>::const_iterator | 462 for (FakeWebRtcVideoDecoder* decoder : decoders) { |
464 it = decoders.begin(); it != decoders.end(); ++it) { | 463 if (number_of_frames > decoder->GetNumFramesReceived()) { |
465 if (number_of_frames > (*it)->GetNumFramesReceived()) { | |
466 return false; | 464 return false; |
467 } | 465 } |
468 } | 466 } |
469 return true; | 467 return true; |
470 } else { | 468 } else { |
471 if (fake_video_renderers_.empty()) { | 469 if (fake_video_renderers_.empty()) { |
472 return number_of_frames <= 0; | 470 return number_of_frames <= 0; |
473 } | 471 } |
474 | 472 |
475 for (RenderMap::const_iterator it = fake_video_renderers_.begin(); | 473 for (const auto& pair : fake_video_renderers_) { |
476 it != fake_video_renderers_.end(); ++it) { | 474 if (number_of_frames > pair.second->num_rendered_frames()) { |
477 if (number_of_frames > it->second->num_rendered_frames()) { | |
478 return false; | 475 return false; |
479 } | 476 } |
480 } | 477 } |
481 return true; | 478 return true; |
482 } | 479 } |
483 } | 480 } |
484 | 481 |
| 482 int video_frames_received() const { |
| 483 int total = 0; |
| 484 if (video_decoder_factory_enabled_) { |
| 485 const std::vector<FakeWebRtcVideoDecoder*>& decoders = |
| 486 fake_video_decoder_factory_->decoders(); |
| 487 for (const FakeWebRtcVideoDecoder* decoder : decoders) { |
| 488 total += decoder->GetNumFramesReceived(); |
| 489 } |
| 490 } else { |
| 491 for (const auto& pair : fake_video_renderers_) { |
| 492 total += pair.second->num_rendered_frames(); |
| 493 } |
| 494 for (const auto& renderer : removed_fake_video_renderers_) { |
| 495 total += renderer->num_rendered_frames(); |
| 496 } |
| 497 } |
| 498 return total; |
| 499 } |
| 500 |
485 // Verify the CreateDtmfSender interface | 501 // Verify the CreateDtmfSender interface |
486 void VerifyDtmf() { | 502 void VerifyDtmf() { |
487 rtc::scoped_ptr<DummyDtmfObserver> observer(new DummyDtmfObserver()); | 503 rtc::scoped_ptr<DummyDtmfObserver> observer(new DummyDtmfObserver()); |
488 rtc::scoped_refptr<DtmfSenderInterface> dtmf_sender; | 504 rtc::scoped_refptr<DtmfSenderInterface> dtmf_sender; |
489 | 505 |
490 // We can't create a DTMF sender with an invalid audio track or a non local | 506 // We can't create a DTMF sender with an invalid audio track or a non local |
491 // track. | 507 // track. |
492 EXPECT_TRUE(peer_connection_->CreateDtmfSender(nullptr) == nullptr); | 508 EXPECT_TRUE(peer_connection_->CreateDtmfSender(nullptr) == nullptr); |
493 rtc::scoped_refptr<webrtc::AudioTrackInterface> non_localtrack( | 509 rtc::scoped_refptr<webrtc::AudioTrackInterface> non_localtrack( |
494 peer_connection_factory_->CreateAudioTrack("dummy_track", nullptr)); | 510 peer_connection_factory_->CreateAudioTrack("dummy_track", nullptr)); |
(...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
871 rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_; | 887 rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_; |
872 rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> | 888 rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> |
873 peer_connection_factory_; | 889 peer_connection_factory_; |
874 | 890 |
875 bool auto_add_stream_ = true; | 891 bool auto_add_stream_ = true; |
876 | 892 |
877 typedef std::pair<std::string, std::string> IceUfragPwdPair; | 893 typedef std::pair<std::string, std::string> IceUfragPwdPair; |
878 std::map<int, IceUfragPwdPair> ice_ufrag_pwd_; | 894 std::map<int, IceUfragPwdPair> ice_ufrag_pwd_; |
879 bool expect_ice_restart_ = false; | 895 bool expect_ice_restart_ = false; |
880 | 896 |
881 // Needed to keep track of number of frames send. | 897 // Needed to keep track of number of frames sent. |
882 rtc::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module_; | 898 rtc::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module_; |
883 // Needed to keep track of number of frames received. | 899 // Needed to keep track of number of frames received. |
884 typedef std::map<std::string, webrtc::FakeVideoTrackRenderer*> RenderMap; | 900 std::map<std::string, rtc::scoped_ptr<webrtc::FakeVideoTrackRenderer>> |
885 RenderMap fake_video_renderers_; | 901 fake_video_renderers_; |
| 902 // Needed to ensure frames aren't received for removed tracks. |
| 903 std::vector<rtc::scoped_ptr<webrtc::FakeVideoTrackRenderer>> |
| 904 removed_fake_video_renderers_; |
886 // Needed to keep track of number of frames received when external decoder | 905 // Needed to keep track of number of frames received when external decoder |
887 // used. | 906 // used. |
888 FakeWebRtcVideoDecoderFactory* fake_video_decoder_factory_ = nullptr; | 907 FakeWebRtcVideoDecoderFactory* fake_video_decoder_factory_ = nullptr; |
889 FakeWebRtcVideoEncoderFactory* fake_video_encoder_factory_ = nullptr; | 908 FakeWebRtcVideoEncoderFactory* fake_video_encoder_factory_ = nullptr; |
890 bool video_decoder_factory_enabled_ = false; | 909 bool video_decoder_factory_enabled_ = false; |
891 webrtc::FakeConstraints video_constraints_; | 910 webrtc::FakeConstraints video_constraints_; |
892 | 911 |
893 // For remote peer communication. | 912 // For remote peer communication. |
894 SignalingMessageReceiver* signaling_message_receiver_ = nullptr; | 913 SignalingMessageReceiver* signaling_message_receiver_ = nullptr; |
895 | 914 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
935 bool VideoFramesReceivedCheck(int frames_received) { | 954 bool VideoFramesReceivedCheck(int frames_received) { |
936 return initiating_client_->VideoFramesReceivedCheck(frames_received) && | 955 return initiating_client_->VideoFramesReceivedCheck(frames_received) && |
937 receiving_client_->VideoFramesReceivedCheck(frames_received); | 956 receiving_client_->VideoFramesReceivedCheck(frames_received); |
938 } | 957 } |
939 void VerifyDtmf() { | 958 void VerifyDtmf() { |
940 initiating_client_->VerifyDtmf(); | 959 initiating_client_->VerifyDtmf(); |
941 receiving_client_->VerifyDtmf(); | 960 receiving_client_->VerifyDtmf(); |
942 } | 961 } |
943 | 962 |
944 void TestUpdateOfferWithRejectedContent() { | 963 void TestUpdateOfferWithRejectedContent() { |
| 964 // Renegotiate, rejecting the video m-line. |
945 initiating_client_->Negotiate(true, false); | 965 initiating_client_->Negotiate(true, false); |
946 EXPECT_TRUE_WAIT( | 966 ASSERT_TRUE_WAIT(SessionActive(), kMaxWaitForActivationMs); |
947 FramesNotPending(kEndAudioFrameCount * 2, kEndVideoFrameCount), | 967 |
948 kMaxWaitForFramesMs); | 968 int pc1_audio_received = initiating_client_->audio_frames_received(); |
949 // There shouldn't be any more video frame after the new offer is | 969 int pc1_video_received = initiating_client_->video_frames_received(); |
950 // negotiated. | 970 int pc2_audio_received = receiving_client_->audio_frames_received(); |
951 EXPECT_FALSE(VideoFramesReceivedCheck(kEndVideoFrameCount + 1)); | 971 int pc2_video_received = receiving_client_->video_frames_received(); |
| 972 |
| 973 // Wait for some additional audio frames to be received. |
| 974 EXPECT_TRUE_WAIT(initiating_client_->AudioFramesReceivedCheck( |
| 975 pc1_audio_received + kEndAudioFrameCount) && |
| 976 receiving_client_->AudioFramesReceivedCheck( |
| 977 pc2_audio_received + kEndAudioFrameCount), |
| 978 kMaxWaitForFramesMs); |
| 979 |
| 980 // During this time, we shouldn't have received any additional video frames |
| 981 // for the rejected video tracks. |
| 982 EXPECT_EQ(pc1_video_received, initiating_client_->video_frames_received()); |
| 983 EXPECT_EQ(pc2_video_received, receiving_client_->video_frames_received()); |
952 } | 984 } |
953 | 985 |
954 void VerifyRenderedSize(int width, int height) { | 986 void VerifyRenderedSize(int width, int height) { |
955 EXPECT_EQ(width, receiving_client()->rendered_width()); | 987 EXPECT_EQ(width, receiving_client()->rendered_width()); |
956 EXPECT_EQ(height, receiving_client()->rendered_height()); | 988 EXPECT_EQ(height, receiving_client()->rendered_height()); |
957 EXPECT_EQ(width, initializing_client()->rendered_width()); | 989 EXPECT_EQ(width, initializing_client()->rendered_width()); |
958 EXPECT_EQ(height, initializing_client()->rendered_height()); | 990 EXPECT_EQ(height, initializing_client()->rendered_height()); |
959 } | 991 } |
960 | 992 |
961 void VerifySessionDescriptions() { | 993 void VerifySessionDescriptions() { |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1285 TEST_F(P2PTestConductor, LocalP2PTestAnswerNone) { | 1317 TEST_F(P2PTestConductor, LocalP2PTestAnswerNone) { |
1286 ASSERT_TRUE(CreateTestClients()); | 1318 ASSERT_TRUE(CreateTestClients()); |
1287 receiving_client()->SetReceiveAudioVideo(false, false); | 1319 receiving_client()->SetReceiveAudioVideo(false, false); |
1288 LocalP2PTest(); | 1320 LocalP2PTest(); |
1289 } | 1321 } |
1290 | 1322 |
1291 // This test sets up an audio and video call between two parties. After the call | 1323 // This test sets up an audio and video call between two parties. After the call |
1292 // runs for a while (10 frames), the caller sends an update offer with video | 1324 // runs for a while (10 frames), the caller sends an update offer with video |
1293 // being rejected. Once the re-negotiation is done, the video flow should stop | 1325 // being rejected. Once the re-negotiation is done, the video flow should stop |
1294 // and the audio flow should continue. | 1326 // and the audio flow should continue. |
1295 // Disabled due to b/14955157. | 1327 TEST_F(P2PTestConductor, UpdateOfferWithRejectedContent) { |
1296 TEST_F(P2PTestConductor, DISABLED_UpdateOfferWithRejectedContent) { | |
1297 ASSERT_TRUE(CreateTestClients()); | 1328 ASSERT_TRUE(CreateTestClients()); |
1298 LocalP2PTest(); | 1329 LocalP2PTest(); |
1299 TestUpdateOfferWithRejectedContent(); | 1330 TestUpdateOfferWithRejectedContent(); |
1300 } | 1331 } |
1301 | 1332 |
1302 // This test sets up a Jsep call between two parties. The MSID is removed from | 1333 // This test sets up a Jsep call between two parties. The MSID is removed from |
1303 // the SDP strings from the caller. | 1334 // the SDP strings from the caller. |
1304 // Disabled due to b/14955157. | 1335 TEST_F(P2PTestConductor, LocalP2PTestWithoutMsid) { |
1305 TEST_F(P2PTestConductor, DISABLED_LocalP2PTestWithoutMsid) { | |
1306 ASSERT_TRUE(CreateTestClients()); | 1336 ASSERT_TRUE(CreateTestClients()); |
1307 receiving_client()->RemoveMsidFromReceivedSdp(true); | 1337 receiving_client()->RemoveMsidFromReceivedSdp(true); |
1308 // TODO(perkj): Currently there is a bug that cause audio to stop playing if | 1338 // TODO(perkj): Currently there is a bug that cause audio to stop playing if |
1309 // audio and video is muxed when MSID is disabled. Remove | 1339 // audio and video is muxed when MSID is disabled. Remove |
1310 // SetRemoveBundleFromSdp once | 1340 // SetRemoveBundleFromSdp once |
1311 // https://code.google.com/p/webrtc/issues/detail?id=1193 is fixed. | 1341 // https://code.google.com/p/webrtc/issues/detail?id=1193 is fixed. |
1312 receiving_client()->RemoveBundleFromReceivedSdp(true); | 1342 receiving_client()->RemoveBundleFromReceivedSdp(true); |
1313 LocalP2PTest(); | 1343 LocalP2PTest(); |
1314 } | 1344 } |
1315 | 1345 |
(...skipping 642 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1958 server.urls.push_back("stun:hostname"); | 1988 server.urls.push_back("stun:hostname"); |
1959 server.urls.push_back("turn:hostname"); | 1989 server.urls.push_back("turn:hostname"); |
1960 servers.push_back(server); | 1990 servers.push_back(server); |
1961 EXPECT_TRUE(webrtc::ParseIceServers(servers, &stun_configurations_, | 1991 EXPECT_TRUE(webrtc::ParseIceServers(servers, &stun_configurations_, |
1962 &turn_configurations_)); | 1992 &turn_configurations_)); |
1963 EXPECT_EQ(1U, stun_configurations_.size()); | 1993 EXPECT_EQ(1U, stun_configurations_.size()); |
1964 EXPECT_EQ(1U, turn_configurations_.size()); | 1994 EXPECT_EQ(1U, turn_configurations_.size()); |
1965 } | 1995 } |
1966 | 1996 |
1967 #endif // if !defined(THREAD_SANITIZER) | 1997 #endif // if !defined(THREAD_SANITIZER) |
OLD | NEW |