xref: /aosp_15_r20/external/webrtc/modules/video_coding/receiver_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
2  *
3  *  Use of this source code is governed by a BSD-style license
4  *  that can be found in the LICENSE file in the root of the source
5  *  tree. An additional intellectual property rights grant can be found
6  *  in the file PATENTS.  All contributing project authors may
7  *  be found in the AUTHORS file in the root of the source tree.
8  */
9 
10 #include "modules/video_coding/receiver.h"
11 
12 #include <string.h>
13 
14 #include <cstdint>
15 #include <memory>
16 #include <queue>
17 #include <vector>
18 
19 #include "modules/video_coding/encoded_frame.h"
20 #include "modules/video_coding/jitter_buffer_common.h"
21 #include "modules/video_coding/packet.h"
22 #include "modules/video_coding/test/stream_generator.h"
23 #include "modules/video_coding/timing/timing.h"
24 #include "rtc_base/checks.h"
25 #include "system_wrappers/include/clock.h"
26 #include "test/gtest.h"
27 #include "test/scoped_key_value_config.h"
28 
29 namespace webrtc {
30 
31 class TestVCMReceiver : public ::testing::Test {
32  protected:
TestVCMReceiver()33   TestVCMReceiver()
34       : clock_(0),
35         timing_(&clock_, field_trials_),
36         receiver_(&timing_, &clock_, field_trials_),
37         stream_generator_(0, clock_.TimeInMilliseconds()) {}
38 
InsertPacket(int index)39   int32_t InsertPacket(int index) {
40     VCMPacket packet;
41     bool packet_available = stream_generator_.GetPacket(&packet, index);
42     EXPECT_TRUE(packet_available);
43     if (!packet_available)
44       return kGeneralError;  // Return here to avoid crashes below.
45     return receiver_.InsertPacket(packet);
46   }
47 
InsertPacketAndPop(int index)48   int32_t InsertPacketAndPop(int index) {
49     VCMPacket packet;
50     bool packet_available = stream_generator_.PopPacket(&packet, index);
51     EXPECT_TRUE(packet_available);
52     if (!packet_available)
53       return kGeneralError;  // Return here to avoid crashes below.
54     return receiver_.InsertPacket(packet);
55   }
56 
InsertFrame(VideoFrameType frame_type,bool complete)57   int32_t InsertFrame(VideoFrameType frame_type, bool complete) {
58     int num_of_packets = complete ? 1 : 2;
59     stream_generator_.GenerateFrame(
60         frame_type,
61         (frame_type != VideoFrameType::kEmptyFrame) ? num_of_packets : 0,
62         (frame_type == VideoFrameType::kEmptyFrame) ? 1 : 0,
63         clock_.TimeInMilliseconds());
64     int32_t ret = InsertPacketAndPop(0);
65     if (!complete) {
66       // Drop the second packet.
67       VCMPacket packet;
68       stream_generator_.PopPacket(&packet, 0);
69     }
70     clock_.AdvanceTimeMilliseconds(kDefaultFramePeriodMs);
71     return ret;
72   }
73 
DecodeNextFrame()74   bool DecodeNextFrame() {
75     VCMEncodedFrame* frame = receiver_.FrameForDecoding(0, false);
76     if (!frame)
77       return false;
78     receiver_.ReleaseFrame(frame);
79     return true;
80   }
81 
82   test::ScopedKeyValueConfig field_trials_;
83   SimulatedClock clock_;
84   VCMTiming timing_;
85   VCMReceiver receiver_;
86   StreamGenerator stream_generator_;
87 };
88 
TEST_F(TestVCMReceiver,NonDecodableDuration_Empty)89 TEST_F(TestVCMReceiver, NonDecodableDuration_Empty) {
90   const size_t kMaxNackListSize = 1000;
91   const int kMaxPacketAgeToNack = 1000;
92   const int kMaxNonDecodableDuration = 500;
93   const int kMinDelayMs = 500;
94   receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
95                             kMaxNonDecodableDuration);
96   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
97   // Advance time until it's time to decode the key frame.
98   clock_.AdvanceTimeMilliseconds(kMinDelayMs);
99   EXPECT_TRUE(DecodeNextFrame());
100   bool request_key_frame = false;
101   std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
102   EXPECT_FALSE(request_key_frame);
103 }
104 
TEST_F(TestVCMReceiver,NonDecodableDuration_NoKeyFrame)105 TEST_F(TestVCMReceiver, NonDecodableDuration_NoKeyFrame) {
106   const size_t kMaxNackListSize = 1000;
107   const int kMaxPacketAgeToNack = 1000;
108   const int kMaxNonDecodableDuration = 500;
109   receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
110                             kMaxNonDecodableDuration);
111   const int kNumFrames = kDefaultFrameRate * kMaxNonDecodableDuration / 1000;
112   for (int i = 0; i < kNumFrames; ++i) {
113     EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
114   }
115   bool request_key_frame = false;
116   std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
117   EXPECT_TRUE(request_key_frame);
118 }
119 
TEST_F(TestVCMReceiver,NonDecodableDuration_OneIncomplete)120 TEST_F(TestVCMReceiver, NonDecodableDuration_OneIncomplete) {
121   const size_t kMaxNackListSize = 1000;
122   const int kMaxPacketAgeToNack = 1000;
123   const int kMaxNonDecodableDuration = 500;
124   const int kMaxNonDecodableDurationFrames =
125       (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000;
126   const int kMinDelayMs = 500;
127   receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
128                             kMaxNonDecodableDuration);
129   timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs));
130   int64_t key_frame_inserted = clock_.TimeInMilliseconds();
131   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
132   // Insert an incomplete frame.
133   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError);
134   // Insert enough frames to have too long non-decodable sequence.
135   for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) {
136     EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
137   }
138   // Advance time until it's time to decode the key frame.
139   clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() -
140                                  key_frame_inserted);
141   EXPECT_TRUE(DecodeNextFrame());
142   // Make sure we get a key frame request.
143   bool request_key_frame = false;
144   std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
145   EXPECT_TRUE(request_key_frame);
146 }
147 
TEST_F(TestVCMReceiver,NonDecodableDuration_NoTrigger)148 TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger) {
149   const size_t kMaxNackListSize = 1000;
150   const int kMaxPacketAgeToNack = 1000;
151   const int kMaxNonDecodableDuration = 500;
152   const int kMaxNonDecodableDurationFrames =
153       (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000;
154   const int kMinDelayMs = 500;
155   receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
156                             kMaxNonDecodableDuration);
157   timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs));
158   int64_t key_frame_inserted = clock_.TimeInMilliseconds();
159   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
160   // Insert an incomplete frame.
161   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError);
162   // Insert all but one frame to not trigger a key frame request due to
163   // too long duration of non-decodable frames.
164   for (int i = 0; i < kMaxNonDecodableDurationFrames - 1; ++i) {
165     EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
166   }
167   // Advance time until it's time to decode the key frame.
168   clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() -
169                                  key_frame_inserted);
170   EXPECT_TRUE(DecodeNextFrame());
171   // Make sure we don't get a key frame request since we haven't generated
172   // enough frames.
173   bool request_key_frame = false;
174   std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
175   EXPECT_FALSE(request_key_frame);
176 }
177 
TEST_F(TestVCMReceiver,NonDecodableDuration_NoTrigger2)178 TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger2) {
179   const size_t kMaxNackListSize = 1000;
180   const int kMaxPacketAgeToNack = 1000;
181   const int kMaxNonDecodableDuration = 500;
182   const int kMaxNonDecodableDurationFrames =
183       (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000;
184   const int kMinDelayMs = 500;
185   receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
186                             kMaxNonDecodableDuration);
187   timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs));
188   int64_t key_frame_inserted = clock_.TimeInMilliseconds();
189   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
190   // Insert enough frames to have too long non-decodable sequence, except that
191   // we don't have any losses.
192   for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) {
193     EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
194   }
195   // Insert an incomplete frame.
196   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError);
197   // Advance time until it's time to decode the key frame.
198   clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() -
199                                  key_frame_inserted);
200   EXPECT_TRUE(DecodeNextFrame());
201   // Make sure we don't get a key frame request since the non-decodable duration
202   // is only one frame.
203   bool request_key_frame = false;
204   std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
205   EXPECT_FALSE(request_key_frame);
206 }
207 
TEST_F(TestVCMReceiver,NonDecodableDuration_KeyFrameAfterIncompleteFrames)208 TEST_F(TestVCMReceiver, NonDecodableDuration_KeyFrameAfterIncompleteFrames) {
209   const size_t kMaxNackListSize = 1000;
210   const int kMaxPacketAgeToNack = 1000;
211   const int kMaxNonDecodableDuration = 500;
212   const int kMaxNonDecodableDurationFrames =
213       (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000;
214   const int kMinDelayMs = 500;
215   receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack,
216                             kMaxNonDecodableDuration);
217   timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs));
218   int64_t key_frame_inserted = clock_.TimeInMilliseconds();
219   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
220   // Insert an incomplete frame.
221   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError);
222   // Insert enough frames to have too long non-decodable sequence.
223   for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) {
224     EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError);
225   }
226   EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError);
227   // Advance time until it's time to decode the key frame.
228   clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() -
229                                  key_frame_inserted);
230   EXPECT_TRUE(DecodeNextFrame());
231   // Make sure we don't get a key frame request since we have a key frame
232   // in the list.
233   bool request_key_frame = false;
234   std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame);
235   EXPECT_FALSE(request_key_frame);
236 }
237 
238 // A simulated clock, when time elapses, will insert frames into the jitter
239 // buffer, based on initial settings.
240 class SimulatedClockWithFrames : public SimulatedClock {
241  public:
SimulatedClockWithFrames(StreamGenerator * stream_generator,VCMReceiver * receiver)242   SimulatedClockWithFrames(StreamGenerator* stream_generator,
243                            VCMReceiver* receiver)
244       : SimulatedClock(0),
245         stream_generator_(stream_generator),
246         receiver_(receiver) {}
~SimulatedClockWithFrames()247   virtual ~SimulatedClockWithFrames() {}
248 
249   // If `stop_on_frame` is true and next frame arrives between now and
250   // now+`milliseconds`, the clock will be advanced to the arrival time of next
251   // frame.
252   // Otherwise, the clock will be advanced by `milliseconds`.
253   //
254   // For both cases, a frame will be inserted into the jitter buffer at the
255   // instant when the clock time is timestamps_.front().arrive_time.
256   //
257   // Return true if some frame arrives between now and now+`milliseconds`.
AdvanceTimeMilliseconds(int64_t milliseconds,bool stop_on_frame)258   bool AdvanceTimeMilliseconds(int64_t milliseconds, bool stop_on_frame) {
259     return AdvanceTimeMicroseconds(milliseconds * 1000, stop_on_frame);
260   }
261 
AdvanceTimeMicroseconds(int64_t microseconds,bool stop_on_frame)262   bool AdvanceTimeMicroseconds(int64_t microseconds, bool stop_on_frame) {
263     int64_t start_time = TimeInMicroseconds();
264     int64_t end_time = start_time + microseconds;
265     bool frame_injected = false;
266     while (!timestamps_.empty() &&
267            timestamps_.front().arrive_time <= end_time) {
268       RTC_DCHECK_GE(timestamps_.front().arrive_time, start_time);
269 
270       SimulatedClock::AdvanceTimeMicroseconds(timestamps_.front().arrive_time -
271                                               TimeInMicroseconds());
272       GenerateAndInsertFrame((timestamps_.front().render_time + 500) / 1000);
273       timestamps_.pop();
274       frame_injected = true;
275 
276       if (stop_on_frame)
277         return frame_injected;
278     }
279 
280     if (TimeInMicroseconds() < end_time) {
281       SimulatedClock::AdvanceTimeMicroseconds(end_time - TimeInMicroseconds());
282     }
283     return frame_injected;
284   }
285 
286   // Input timestamps are in unit Milliseconds.
287   // And `arrive_timestamps` must be positive and in increasing order.
288   // `arrive_timestamps` determine when we are going to insert frames into the
289   // jitter buffer.
290   // `render_timestamps` are the timestamps on the frame.
SetFrames(const int64_t * arrive_timestamps,const int64_t * render_timestamps,size_t size)291   void SetFrames(const int64_t* arrive_timestamps,
292                  const int64_t* render_timestamps,
293                  size_t size) {
294     int64_t previous_arrive_timestamp = 0;
295     for (size_t i = 0; i < size; i++) {
296       RTC_CHECK_GE(arrive_timestamps[i], previous_arrive_timestamp);
297       timestamps_.push(TimestampPair(arrive_timestamps[i] * 1000,
298                                      render_timestamps[i] * 1000));
299       previous_arrive_timestamp = arrive_timestamps[i];
300     }
301   }
302 
303  private:
304   struct TimestampPair {
TimestampPairwebrtc::SimulatedClockWithFrames::TimestampPair305     TimestampPair(int64_t arrive_timestamp, int64_t render_timestamp)
306         : arrive_time(arrive_timestamp), render_time(render_timestamp) {}
307 
308     int64_t arrive_time;
309     int64_t render_time;
310   };
311 
GenerateAndInsertFrame(int64_t render_timestamp_ms)312   void GenerateAndInsertFrame(int64_t render_timestamp_ms) {
313     VCMPacket packet;
314     stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameKey,
315                                      1,  // media packets
316                                      0,  // empty packets
317                                      render_timestamp_ms);
318 
319     bool packet_available = stream_generator_->PopPacket(&packet, 0);
320     EXPECT_TRUE(packet_available);
321     if (!packet_available)
322       return;  // Return here to avoid crashes below.
323     receiver_->InsertPacket(packet);
324   }
325 
326   std::queue<TimestampPair> timestamps_;
327   StreamGenerator* stream_generator_;
328   VCMReceiver* receiver_;
329 };
330 
331 // Use a SimulatedClockWithFrames
332 // Wait call will do either of these:
333 // 1. If `stop_on_frame` is true, the clock will be turned to the exact instant
334 // that the first frame comes and the frame will be inserted into the jitter
335 // buffer, or the clock will be turned to now + `max_time` if no frame comes in
336 // the window.
337 // 2. If `stop_on_frame` is false, the clock will be turn to now + `max_time`,
338 // and all the frames arriving between now and now + `max_time` will be
339 // inserted into the jitter buffer.
340 //
341 // This is used to simulate the JitterBuffer getting packets from internet as
342 // time elapses.
343 
344 class FrameInjectEvent : public EventWrapper {
345  public:
FrameInjectEvent(SimulatedClockWithFrames * clock,bool stop_on_frame)346   FrameInjectEvent(SimulatedClockWithFrames* clock, bool stop_on_frame)
347       : clock_(clock), stop_on_frame_(stop_on_frame) {}
348 
Set()349   bool Set() override { return true; }
350 
Wait(int max_time_ms)351   EventTypeWrapper Wait(int max_time_ms) override {
352     if (clock_->AdvanceTimeMilliseconds(max_time_ms, stop_on_frame_) &&
353         stop_on_frame_) {
354       return EventTypeWrapper::kEventSignaled;
355     } else {
356       return EventTypeWrapper::kEventTimeout;
357     }
358   }
359 
360  private:
361   SimulatedClockWithFrames* clock_;
362   bool stop_on_frame_;
363 };
364 
365 class VCMReceiverTimingTest : public ::testing::Test {
366  protected:
VCMReceiverTimingTest()367   VCMReceiverTimingTest()
368       : clock_(&stream_generator_, &receiver_),
369         stream_generator_(0, clock_.TimeInMilliseconds()),
370         timing_(&clock_, field_trials_),
371         receiver_(
372             &timing_,
373             &clock_,
374             std::unique_ptr<EventWrapper>(new FrameInjectEvent(&clock_, false)),
375             std::unique_ptr<EventWrapper>(new FrameInjectEvent(&clock_, true)),
376             field_trials_) {}
377 
SetUp()378   virtual void SetUp() {}
379 
380   test::ScopedKeyValueConfig field_trials_;
381   SimulatedClockWithFrames clock_;
382   StreamGenerator stream_generator_;
383   VCMTiming timing_;
384   VCMReceiver receiver_;
385 };
386 
387 // Test whether VCMReceiver::FrameForDecoding handles parameter
388 // `max_wait_time_ms` correctly:
389 // 1. The function execution should never take more than `max_wait_time_ms`.
390 // 2. If the function exit before now + `max_wait_time_ms`, a frame must be
391 //    returned.
TEST_F(VCMReceiverTimingTest,FrameForDecoding)392 TEST_F(VCMReceiverTimingTest, FrameForDecoding) {
393   const size_t kNumFrames = 100;
394   const int kFramePeriod = 40;
395   int64_t arrive_timestamps[kNumFrames];
396   int64_t render_timestamps[kNumFrames];
397 
398   // Construct test samples.
399   // render_timestamps are the timestamps stored in the Frame;
400   // arrive_timestamps controls when the Frame packet got received.
401   for (size_t i = 0; i < kNumFrames; i++) {
402     // Preset frame rate to 25Hz.
403     // But we add a reasonable deviation to arrive_timestamps to mimic Internet
404     // fluctuation.
405     arrive_timestamps[i] =
406         (i + 1) * kFramePeriod + (i % 10) * ((i % 2) ? 1 : -1);
407     render_timestamps[i] = (i + 1) * kFramePeriod;
408   }
409 
410   clock_.SetFrames(arrive_timestamps, render_timestamps, kNumFrames);
411 
412   // Record how many frames we finally get out of the receiver.
413   size_t num_frames_return = 0;
414 
415   const int64_t kMaxWaitTime = 30;
416 
417   // Ideally, we should get all frames that we input in InitializeFrames.
418   // In the case that FrameForDecoding kills frames by error, we rely on the
419   // build bot to kill the test.
420   while (num_frames_return < kNumFrames) {
421     int64_t start_time = clock_.TimeInMilliseconds();
422     VCMEncodedFrame* frame = receiver_.FrameForDecoding(kMaxWaitTime, false);
423     int64_t end_time = clock_.TimeInMilliseconds();
424 
425     // In any case the FrameForDecoding should not wait longer than
426     // max_wait_time.
427     // In the case that we did not get a frame, it should have been waiting for
428     // exactly max_wait_time. (By the testing samples we constructed above, we
429     // are sure there is no timing error, so the only case it returns with NULL
430     // is that it runs out of time.)
431     if (frame) {
432       receiver_.ReleaseFrame(frame);
433       ++num_frames_return;
434       EXPECT_GE(kMaxWaitTime, end_time - start_time);
435     } else {
436       EXPECT_EQ(kMaxWaitTime, end_time - start_time);
437     }
438   }
439 }
440 
441 // Test whether VCMReceiver::FrameForDecoding handles parameter
442 // `prefer_late_decoding` and `max_wait_time_ms` correctly:
443 // 1. The function execution should never take more than `max_wait_time_ms`.
444 // 2. If the function exit before now + `max_wait_time_ms`, a frame must be
445 //    returned and the end time must be equal to the render timestamp - delay
446 //    for decoding and rendering.
TEST_F(VCMReceiverTimingTest,FrameForDecodingPreferLateDecoding)447 TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) {
448   const size_t kNumFrames = 100;
449   const int kFramePeriod = 40;
450 
451   int64_t arrive_timestamps[kNumFrames];
452   int64_t render_timestamps[kNumFrames];
453 
454   auto timings = timing_.GetTimings();
455   TimeDelta render_delay = timings.render_delay;
456   TimeDelta max_decode = timings.max_decode_duration;
457 
458   // Construct test samples.
459   // render_timestamps are the timestamps stored in the Frame;
460   // arrive_timestamps controls when the Frame packet got received.
461   for (size_t i = 0; i < kNumFrames; i++) {
462     // Preset frame rate to 25Hz.
463     // But we add a reasonable deviation to arrive_timestamps to mimic Internet
464     // fluctuation.
465     arrive_timestamps[i] =
466         (i + 1) * kFramePeriod + (i % 10) * ((i % 2) ? 1 : -1);
467     render_timestamps[i] = (i + 1) * kFramePeriod;
468   }
469 
470   clock_.SetFrames(arrive_timestamps, render_timestamps, kNumFrames);
471 
472   // Record how many frames we finally get out of the receiver.
473   size_t num_frames_return = 0;
474   const int64_t kMaxWaitTime = 30;
475   bool prefer_late_decoding = true;
476   while (num_frames_return < kNumFrames) {
477     int64_t start_time = clock_.TimeInMilliseconds();
478 
479     VCMEncodedFrame* frame =
480         receiver_.FrameForDecoding(kMaxWaitTime, prefer_late_decoding);
481     int64_t end_time = clock_.TimeInMilliseconds();
482     if (frame) {
483       EXPECT_EQ(frame->RenderTimeMs() - max_decode.ms() - render_delay.ms(),
484                 end_time);
485       receiver_.ReleaseFrame(frame);
486       ++num_frames_return;
487     } else {
488       EXPECT_EQ(kMaxWaitTime, end_time - start_time);
489     }
490   }
491 }
492 
493 }  // namespace webrtc
494