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