loss_notification_controller_unittest.cc (22251B)
1 /* 2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/video_coding/loss_notification_controller.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <limits> 16 #include <optional> 17 #include <string> 18 #include <tuple> 19 #include <utility> 20 #include <vector> 21 22 #include "modules/include/module_common_types.h" 23 #include "rtc_base/checks.h" 24 #include "test/gtest.h" 25 26 namespace webrtc { 27 namespace { 28 29 // The information about an RTP packet that is relevant in these tests. 30 struct Packet { 31 uint16_t seq_num; 32 bool first_in_frame; 33 bool is_keyframe; 34 int64_t frame_id; 35 std::vector<int64_t> frame_dependencies; 36 }; 37 38 Packet CreatePacket( 39 bool first_in_frame, 40 bool /* last_in_frame */, 41 uint16_t seq_num, 42 uint16_t frame_id, 43 bool is_key_frame, 44 std::vector<int64_t> ref_frame_ids = std::vector<int64_t>()) { 45 Packet packet; 46 packet.seq_num = seq_num; 47 packet.first_in_frame = first_in_frame; 48 if (first_in_frame) { 49 packet.is_keyframe = is_key_frame; 50 packet.frame_id = frame_id; 51 RTC_DCHECK(!is_key_frame || ref_frame_ids.empty()); 52 packet.frame_dependencies = std::move(ref_frame_ids); 53 } 54 return packet; 55 } 56 57 class PacketStreamCreator final { 58 public: 59 PacketStreamCreator() : seq_num_(0), frame_id_(0), next_is_key_frame_(true) {} 60 61 Packet NextPacket() { 62 std::vector<int64_t> ref_frame_ids; 63 if (!next_is_key_frame_) { 64 ref_frame_ids.push_back(frame_id_ - 1); 65 } 66 67 Packet packet = CreatePacket(true, true, seq_num_++, frame_id_++, 68 next_is_key_frame_, ref_frame_ids); 69 70 next_is_key_frame_ = false; 71 72 return packet; 73 } 74 75 private: 76 uint16_t seq_num_; 77 int64_t frame_id_; 78 bool next_is_key_frame_; 79 }; 80 } // namespace 81 82 // Most of the logic for the tests is here. Subclasses allow parameterizing 83 // the test, or adding some more specific logic. 84 class LossNotificationControllerBaseTest : public ::testing::Test, 85 public KeyFrameRequestSender, 86 public LossNotificationSender { 87 protected: 88 LossNotificationControllerBaseTest() 89 : uut_(this, this), key_frame_requested_(false) {} 90 91 ~LossNotificationControllerBaseTest() override { 92 EXPECT_FALSE(LastKeyFrameRequest()); 93 EXPECT_FALSE(LastLossNotification()); 94 } 95 96 // KeyFrameRequestSender implementation. 97 void RequestKeyFrame() override { 98 EXPECT_FALSE(LastKeyFrameRequest()); 99 EXPECT_FALSE(LastLossNotification()); 100 key_frame_requested_ = true; 101 } 102 103 // LossNotificationSender implementation. 104 void SendLossNotification(uint16_t last_decoded_seq_num, 105 uint16_t last_received_seq_num, 106 bool decodability_flag, 107 bool buffering_allowed) override { 108 EXPECT_TRUE(buffering_allowed); // (Flag useful elsewhere.) 109 EXPECT_FALSE(LastKeyFrameRequest()); 110 EXPECT_FALSE(LastLossNotification()); 111 last_loss_notification_.emplace(last_decoded_seq_num, last_received_seq_num, 112 decodability_flag); 113 } 114 115 void OnReceivedPacket(const Packet& packet) { 116 EXPECT_FALSE(LastKeyFrameRequest()); 117 EXPECT_FALSE(LastLossNotification()); 118 119 if (packet.first_in_frame) { 120 previous_first_packet_in_frame_ = packet; 121 LossNotificationController::FrameDetails frame; 122 frame.is_keyframe = packet.is_keyframe; 123 frame.frame_id = packet.frame_id; 124 frame.frame_dependencies = packet.frame_dependencies; 125 uut_.OnReceivedPacket(packet.seq_num, &frame); 126 } else { 127 uut_.OnReceivedPacket(packet.seq_num, nullptr); 128 } 129 } 130 131 void OnAssembledFrame(uint16_t first_seq_num, 132 int64_t frame_id, 133 bool discardable) { 134 EXPECT_FALSE(LastKeyFrameRequest()); 135 EXPECT_FALSE(LastLossNotification()); 136 137 ASSERT_TRUE(previous_first_packet_in_frame_); 138 uut_.OnAssembledFrame(first_seq_num, frame_id, discardable, 139 previous_first_packet_in_frame_->frame_dependencies); 140 } 141 142 void ExpectKeyFrameRequest() { 143 EXPECT_EQ(LastLossNotification(), std::nullopt); 144 EXPECT_TRUE(LastKeyFrameRequest()); 145 } 146 147 void ExpectLossNotification(uint16_t last_decoded_seq_num, 148 uint16_t last_received_seq_num, 149 bool decodability_flag) { 150 EXPECT_FALSE(LastKeyFrameRequest()); 151 const auto last_ln = LastLossNotification(); 152 ASSERT_TRUE(last_ln); 153 const LossNotification expected_ln( 154 last_decoded_seq_num, last_received_seq_num, decodability_flag); 155 EXPECT_EQ(expected_ln, *last_ln) 156 << "Expected loss notification (" << expected_ln.ToString() 157 << ") != received loss notification (" << last_ln->ToString() + ")"; 158 } 159 160 struct LossNotification { 161 LossNotification(uint16_t last_decoded_seq_num, 162 uint16_t last_received_seq_num, 163 bool decodability_flag) 164 : last_decoded_seq_num(last_decoded_seq_num), 165 last_received_seq_num(last_received_seq_num), 166 decodability_flag(decodability_flag) {} 167 168 LossNotification& operator=(const LossNotification& other) = default; 169 170 bool operator==(const LossNotification& other) const { 171 return last_decoded_seq_num == other.last_decoded_seq_num && 172 last_received_seq_num == other.last_received_seq_num && 173 decodability_flag == other.decodability_flag; 174 } 175 176 std::string ToString() const { 177 return std::to_string(last_decoded_seq_num) + ", " + 178 std::to_string(last_received_seq_num) + ", " + 179 std::to_string(decodability_flag); 180 } 181 182 uint16_t last_decoded_seq_num; 183 uint16_t last_received_seq_num; 184 bool decodability_flag; 185 }; 186 187 bool LastKeyFrameRequest() { 188 const bool result = key_frame_requested_; 189 key_frame_requested_ = false; 190 return result; 191 } 192 193 std::optional<LossNotification> LastLossNotification() { 194 const std::optional<LossNotification> result = last_loss_notification_; 195 last_loss_notification_ = std::nullopt; 196 return result; 197 } 198 199 LossNotificationController uut_; // Unit under test. 200 201 bool key_frame_requested_; 202 203 std::optional<LossNotification> last_loss_notification_; 204 205 // First packet of last frame. (Note that if a test skips the first packet 206 // of a subsequent frame, OnAssembledFrame is not called, and so this is 207 // note read. Therefore, it's not a problem if it is not cleared when 208 // the frame changes.) 209 std::optional<Packet> previous_first_packet_in_frame_; 210 }; 211 212 class LossNotificationControllerTest 213 : public LossNotificationControllerBaseTest, 214 public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> { 215 protected: 216 // Arbitrary parameterized values, to be used by the tests whenever they 217 // wish to either check some combinations, or wish to demonstrate that 218 // a particular arbitrary value is unimportant. 219 template <size_t N> 220 bool Bool() const { 221 return std::get<N>(GetParam()); 222 } 223 }; 224 225 INSTANTIATE_TEST_SUITE_P(_, 226 LossNotificationControllerTest, 227 ::testing::Combine(::testing::Bool(), 228 ::testing::Bool(), 229 ::testing::Bool())); 230 231 // If the first frame, which is a key frame, is lost, then a new key frame 232 // is requested. 233 TEST_P(LossNotificationControllerTest, 234 PacketLossBeforeFirstFrameAssembledTriggersKeyFrameRequest) { 235 OnReceivedPacket(CreatePacket(true, false, 100, 0, true)); 236 OnReceivedPacket(CreatePacket(Bool<0>(), Bool<1>(), 103, 1, false, {0})); 237 ExpectKeyFrameRequest(); 238 } 239 240 // If packet loss occurs (but not of the first packet), then a loss notification 241 // is issued. 242 TEST_P(LossNotificationControllerTest, 243 PacketLossAfterFirstFrameAssembledTriggersLossNotification) { 244 OnReceivedPacket(CreatePacket(true, true, 100, 0, true)); 245 OnAssembledFrame(100, 0, false); 246 const bool first = Bool<0>(); 247 const bool last = Bool<1>(); 248 OnReceivedPacket(CreatePacket(first, last, 103, 1, false, {0})); 249 const bool expected_decodability_flag = first; 250 ExpectLossNotification(100, 103, expected_decodability_flag); 251 } 252 253 // No key frame or loss notifications issued due to an innocuous wrap-around 254 // of the sequence number. 255 TEST_P(LossNotificationControllerTest, SeqNumWrapAround) { 256 uint16_t seq_num = std::numeric_limits<uint16_t>::max(); 257 OnReceivedPacket(CreatePacket(true, true, seq_num, 0, true)); 258 OnAssembledFrame(seq_num, 0, false); 259 const bool first = Bool<0>(); 260 const bool last = Bool<1>(); 261 OnReceivedPacket(CreatePacket(first, last, ++seq_num, 1, false, {0})); 262 } 263 264 TEST_F(LossNotificationControllerTest, 265 KeyFrameAfterPacketLossProducesNoLossNotifications) { 266 OnReceivedPacket(CreatePacket(true, true, 100, 1, true)); 267 OnAssembledFrame(100, 1, false); 268 OnReceivedPacket(CreatePacket(true, true, 108, 8, true)); 269 } 270 271 TEST_P(LossNotificationControllerTest, LostReferenceProducesLossNotification) { 272 OnReceivedPacket(CreatePacket(true, true, 100, 0, true)); 273 OnAssembledFrame(100, 0, false); 274 uint16_t last_decodable_non_discardable_seq_num = 100; 275 276 // RTP gap produces loss notification - not the focus of this test. 277 const bool first = Bool<0>(); 278 const bool last = Bool<1>(); 279 const bool discardable = Bool<2>(); 280 const bool decodable = first; // Depends on assemblability. 281 OnReceivedPacket(CreatePacket(first, last, 107, 3, false, {0})); 282 ExpectLossNotification(100, 107, decodable); 283 OnAssembledFrame(107, 3, discardable); 284 if (!discardable) { 285 last_decodable_non_discardable_seq_num = 107; 286 } 287 288 // Test focus - a loss notification is produced because of the missing 289 // dependency (frame ID 2), despite the RTP sequence number being the 290 // next expected one. 291 OnReceivedPacket(CreatePacket(true, true, 108, 4, false, {2, 0})); 292 ExpectLossNotification(last_decodable_non_discardable_seq_num, 108, false); 293 } 294 295 // The difference between this test and the previous one, is that in this test, 296 // although the reference frame was received, it was not decodable. 297 TEST_P(LossNotificationControllerTest, 298 UndecodableReferenceProducesLossNotification) { 299 OnReceivedPacket(CreatePacket(true, true, 100, 0, true)); 300 OnAssembledFrame(100, 0, false); 301 uint16_t last_decodable_non_discardable_seq_num = 100; 302 303 // RTP gap produces loss notification - not the focus of this test. 304 // Also, not decodable; this is important for later in the test. 305 OnReceivedPacket(CreatePacket(true, true, 107, 3, false, {2})); 306 ExpectLossNotification(100, 107, false); 307 const bool discardable = Bool<0>(); 308 OnAssembledFrame(107, 3, discardable); 309 310 // Test focus - a loss notification is produced because of the undecodable 311 // dependency (frame ID 3, which depended on the missing frame ID 2). 312 OnReceivedPacket(CreatePacket(true, true, 108, 4, false, {3, 0})); 313 ExpectLossNotification(last_decodable_non_discardable_seq_num, 108, false); 314 } 315 316 TEST_P(LossNotificationControllerTest, RobustnessAgainstHighInitialRefFrameId) { 317 constexpr uint16_t max_uint16_t = std::numeric_limits<uint16_t>::max(); 318 OnReceivedPacket(CreatePacket(true, true, 100, 0, true)); 319 OnAssembledFrame(100, 0, false); 320 OnReceivedPacket(CreatePacket(true, true, 101, 1, false, {max_uint16_t})); 321 ExpectLossNotification(100, 101, false); 322 OnAssembledFrame(101, max_uint16_t, Bool<0>()); 323 } 324 325 TEST_P(LossNotificationControllerTest, RepeatedPacketsAreIgnored) { 326 PacketStreamCreator packet_stream; 327 328 const auto key_frame_packet = packet_stream.NextPacket(); 329 OnReceivedPacket(key_frame_packet); 330 OnAssembledFrame(key_frame_packet.seq_num, key_frame_packet.frame_id, false); 331 332 const bool gap = Bool<0>(); 333 334 if (gap) { 335 // Lose one packet. 336 packet_stream.NextPacket(); 337 } 338 339 auto repeated_packet = packet_stream.NextPacket(); 340 OnReceivedPacket(repeated_packet); 341 if (gap) { 342 // Loss notification issued because of the gap. This is not the focus of 343 // the test. 344 ExpectLossNotification(key_frame_packet.seq_num, repeated_packet.seq_num, 345 false); 346 } 347 OnReceivedPacket(repeated_packet); 348 } 349 350 TEST_F(LossNotificationControllerTest, 351 RecognizesDependencyAcrossIntraFrameThatIsNotAKeyframe) { 352 int last_seq_num = 1; 353 auto receive = [&](bool is_key_frame, int64_t frame_id, 354 std::vector<int64_t> ref_frame_ids) { 355 ++last_seq_num; 356 OnReceivedPacket(CreatePacket( 357 /*first_in_frame=*/true, /*last_in_frame=*/true, last_seq_num, frame_id, 358 is_key_frame, std::move(ref_frame_ids))); 359 OnAssembledFrame(last_seq_num, frame_id, /*discardable=*/false); 360 }; 361 // 11 -- 13 362 // | | 363 // 10 12 364 receive(/*is_key_frame=*/true, /*frame_id=*/10, /*ref_frame_ids=*/{}); 365 receive(/*is_key_frame=*/false, /*frame_id=*/11, /*ref_frame_ids=*/{10}); 366 receive(/*is_key_frame=*/false, /*frame_id=*/12, /*ref_frame_ids=*/{}); 367 receive(/*is_key_frame=*/false, /*frame_id=*/13, /*ref_frame_ids=*/{11, 12}); 368 EXPECT_FALSE(LastLossNotification()); 369 } 370 371 class LossNotificationControllerTestDecodabilityFlag 372 : public LossNotificationControllerBaseTest { 373 protected: 374 LossNotificationControllerTestDecodabilityFlag() 375 : key_frame_seq_num_(100), 376 key_frame_frame_id_(0), 377 never_received_frame_id_(key_frame_frame_id_ + 1), 378 seq_num_(0), 379 frame_id_(0) {} 380 381 void ReceiveKeyFrame() { 382 RTC_DCHECK_NE(key_frame_frame_id_, never_received_frame_id_); 383 OnReceivedPacket(CreatePacket(true, true, key_frame_seq_num_, 384 key_frame_frame_id_, true)); 385 OnAssembledFrame(key_frame_seq_num_, key_frame_frame_id_, false); 386 seq_num_ = key_frame_seq_num_; 387 frame_id_ = key_frame_frame_id_; 388 } 389 390 void ReceivePacket(bool first_packet_in_frame, 391 bool last_packet_in_frame, 392 const std::vector<int64_t>& ref_frame_ids) { 393 if (first_packet_in_frame) { 394 frame_id_ += 1; 395 } 396 RTC_DCHECK_NE(frame_id_, never_received_frame_id_); 397 constexpr bool is_key_frame = false; 398 OnReceivedPacket(CreatePacket(first_packet_in_frame, last_packet_in_frame, 399 ++seq_num_, frame_id_, is_key_frame, 400 ref_frame_ids)); 401 } 402 403 void CreateGap() { 404 seq_num_ += 50; 405 frame_id_ += 10; 406 } 407 408 const uint16_t key_frame_seq_num_; 409 const uint16_t key_frame_frame_id_; 410 411 // The tests intentionally never receive this, and can therefore always 412 // use this as an unsatisfied dependency. 413 const int64_t never_received_frame_id_ = 123; 414 415 uint16_t seq_num_; 416 int64_t frame_id_; 417 }; 418 419 TEST_F(LossNotificationControllerTestDecodabilityFlag, 420 SinglePacketFrameWithDecodableDependencies) { 421 ReceiveKeyFrame(); 422 CreateGap(); 423 424 const std::vector<int64_t> ref_frame_ids = {key_frame_frame_id_}; 425 ReceivePacket(true, true, ref_frame_ids); 426 427 const bool expected_decodability_flag = true; 428 ExpectLossNotification(key_frame_seq_num_, seq_num_, 429 expected_decodability_flag); 430 } 431 432 TEST_F(LossNotificationControllerTestDecodabilityFlag, 433 SinglePacketFrameWithUndecodableDependencies) { 434 ReceiveKeyFrame(); 435 CreateGap(); 436 437 const std::vector<int64_t> ref_frame_ids = {never_received_frame_id_}; 438 ReceivePacket(true, true, ref_frame_ids); 439 440 const bool expected_decodability_flag = false; 441 ExpectLossNotification(key_frame_seq_num_, seq_num_, 442 expected_decodability_flag); 443 } 444 445 TEST_F(LossNotificationControllerTestDecodabilityFlag, 446 FirstPacketOfMultiPacketFrameWithDecodableDependencies) { 447 ReceiveKeyFrame(); 448 CreateGap(); 449 450 const std::vector<int64_t> ref_frame_ids = {key_frame_frame_id_}; 451 ReceivePacket(true, false, ref_frame_ids); 452 453 const bool expected_decodability_flag = true; 454 ExpectLossNotification(key_frame_seq_num_, seq_num_, 455 expected_decodability_flag); 456 } 457 458 TEST_F(LossNotificationControllerTestDecodabilityFlag, 459 FirstPacketOfMultiPacketFrameWithUndecodableDependencies) { 460 ReceiveKeyFrame(); 461 CreateGap(); 462 463 const std::vector<int64_t> ref_frame_ids = {never_received_frame_id_}; 464 ReceivePacket(true, false, ref_frame_ids); 465 466 const bool expected_decodability_flag = false; 467 ExpectLossNotification(key_frame_seq_num_, seq_num_, 468 expected_decodability_flag); 469 } 470 471 TEST_F(LossNotificationControllerTestDecodabilityFlag, 472 MiddlePacketOfMultiPacketFrameWithDecodableDependenciesIfFirstMissed) { 473 ReceiveKeyFrame(); 474 CreateGap(); 475 476 const std::vector<int64_t> ref_frame_ids = {key_frame_frame_id_}; 477 ReceivePacket(false, false, ref_frame_ids); 478 479 const bool expected_decodability_flag = false; 480 ExpectLossNotification(key_frame_seq_num_, seq_num_, 481 expected_decodability_flag); 482 } 483 484 TEST_F(LossNotificationControllerTestDecodabilityFlag, 485 MiddlePacketOfMultiPacketFrameWithUndecodableDependenciesIfFirstMissed) { 486 ReceiveKeyFrame(); 487 CreateGap(); 488 489 const std::vector<int64_t> ref_frame_ids = {never_received_frame_id_}; 490 ReceivePacket(false, false, ref_frame_ids); 491 492 const bool expected_decodability_flag = false; 493 ExpectLossNotification(key_frame_seq_num_, seq_num_, 494 expected_decodability_flag); 495 } 496 497 TEST_F(LossNotificationControllerTestDecodabilityFlag, 498 MiddlePacketOfMultiPacketFrameWithDecodableDependenciesIfFirstReceived) { 499 ReceiveKeyFrame(); 500 CreateGap(); 501 502 // First packet in multi-packet frame. A loss notification is produced 503 // because of the gap in RTP sequence numbers. 504 const std::vector<int64_t> ref_frame_ids = {key_frame_frame_id_}; 505 ReceivePacket(true, false, ref_frame_ids); 506 const bool expected_decodability_flag_first = true; 507 ExpectLossNotification(key_frame_seq_num_, seq_num_, 508 expected_decodability_flag_first); 509 510 // Middle packet in multi-packet frame. No additional gap and the frame is 511 // still potentially decodable, so no additional loss indication. 512 ReceivePacket(false, false, ref_frame_ids); 513 EXPECT_FALSE(LastKeyFrameRequest()); 514 EXPECT_FALSE(LastLossNotification()); 515 } 516 517 TEST_F( 518 LossNotificationControllerTestDecodabilityFlag, 519 MiddlePacketOfMultiPacketFrameWithUndecodableDependenciesIfFirstReceived) { 520 ReceiveKeyFrame(); 521 CreateGap(); 522 523 // First packet in multi-packet frame. A loss notification is produced 524 // because of the gap in RTP sequence numbers. The frame is also recognized 525 // as having non-decodable dependencies. 526 const std::vector<int64_t> ref_frame_ids = {never_received_frame_id_}; 527 ReceivePacket(true, false, ref_frame_ids); 528 const bool expected_decodability_flag_first = false; 529 ExpectLossNotification(key_frame_seq_num_, seq_num_, 530 expected_decodability_flag_first); 531 532 // Middle packet in multi-packet frame. No additional gap, but the frame is 533 // known to be non-decodable, so we keep issuing loss indications. 534 ReceivePacket(false, false, ref_frame_ids); 535 const bool expected_decodability_flag_middle = false; 536 ExpectLossNotification(key_frame_seq_num_, seq_num_, 537 expected_decodability_flag_middle); 538 } 539 540 TEST_F(LossNotificationControllerTestDecodabilityFlag, 541 LastPacketOfMultiPacketFrameWithDecodableDependenciesIfAllPrevMissed) { 542 ReceiveKeyFrame(); 543 CreateGap(); 544 545 const std::vector<int64_t> ref_frame_ids = {key_frame_frame_id_}; 546 ReceivePacket(false, true, ref_frame_ids); 547 548 const bool expected_decodability_flag = false; 549 ExpectLossNotification(key_frame_seq_num_, seq_num_, 550 expected_decodability_flag); 551 } 552 553 TEST_F(LossNotificationControllerTestDecodabilityFlag, 554 LastPacketOfMultiPacketFrameWithUndecodableDependenciesIfAllPrevMissed) { 555 ReceiveKeyFrame(); 556 CreateGap(); 557 558 const std::vector<int64_t> ref_frame_ids = {never_received_frame_id_}; 559 ReceivePacket(false, true, ref_frame_ids); 560 561 const bool expected_decodability_flag = false; 562 ExpectLossNotification(key_frame_seq_num_, seq_num_, 563 expected_decodability_flag); 564 } 565 566 TEST_F(LossNotificationControllerTestDecodabilityFlag, 567 LastPacketOfMultiPacketFrameWithDecodableDependenciesIfAllPrevReceived) { 568 ReceiveKeyFrame(); 569 CreateGap(); 570 571 // First packet in multi-packet frame. A loss notification is produced 572 // because of the gap in RTP sequence numbers. 573 const std::vector<int64_t> ref_frame_ids = {key_frame_frame_id_}; 574 ReceivePacket(true, false, ref_frame_ids); 575 const bool expected_decodability_flag_first = true; 576 ExpectLossNotification(key_frame_seq_num_, seq_num_, 577 expected_decodability_flag_first); 578 579 // Last packet in multi-packet frame. No additional gap and the frame is 580 // still potentially decodable, so no additional loss indication. 581 ReceivePacket(false, true, ref_frame_ids); 582 EXPECT_FALSE(LastKeyFrameRequest()); 583 EXPECT_FALSE(LastLossNotification()); 584 } 585 586 TEST_F( 587 LossNotificationControllerTestDecodabilityFlag, 588 LastPacketOfMultiPacketFrameWithUndecodableDependenciesIfAllPrevReceived) { 589 ReceiveKeyFrame(); 590 CreateGap(); 591 592 // First packet in multi-packet frame. A loss notification is produced 593 // because of the gap in RTP sequence numbers. The frame is also recognized 594 // as having non-decodable dependencies. 595 const std::vector<int64_t> ref_frame_ids = {never_received_frame_id_}; 596 ReceivePacket(true, false, ref_frame_ids); 597 const bool expected_decodability_flag_first = false; 598 ExpectLossNotification(key_frame_seq_num_, seq_num_, 599 expected_decodability_flag_first); 600 601 // Last packet in multi-packet frame. No additional gap, but the frame is 602 // known to be non-decodable, so we keep issuing loss indications. 603 ReceivePacket(false, true, ref_frame_ids); 604 const bool expected_decodability_flag_last = false; 605 ExpectLossNotification(key_frame_seq_num_, seq_num_, 606 expected_decodability_flag_last); 607 } 608 609 } // namespace webrtc