blob: c53a68796593cb491de89fb8e0328eee58a98d57 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
philipel5908c712015-12-21 08:23:20 -080011#include "webrtc/modules/video_coding/video_coding_impl.h"
12
13#include <algorithm>
Erik Språng08127a92016-11-16 16:41:30 +010014#include <utility>
philipel5908c712015-12-21 08:23:20 -080015
philipel721d4022016-12-15 07:10:57 -080016#include "webrtc/base/criticalsection.h"
pbos@webrtc.orga4407322013-07-16 12:32:05 +000017#include "webrtc/common_types.h"
Erik Språng08127a92016-11-16 16:41:30 +010018#include "webrtc/common_video/include/video_bitrate_allocator.h"
pbos@webrtc.orga4407322013-07-16 12:32:05 +000019#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
Erik Språng08127a92016-11-16 16:41:30 +010020#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
philipel721d4022016-12-15 07:10:57 -080021#include "webrtc/modules/video_coding/encoded_frame.h"
Erik Språng08127a92016-11-16 16:41:30 +010022#include "webrtc/modules/video_coding/include/video_codec_initializer.h"
Henrik Kjellander2557b862015-11-18 22:00:21 +010023#include "webrtc/modules/video_coding/include/video_codec_interface.h"
Henrik Kjellander2557b862015-11-18 22:00:21 +010024#include "webrtc/modules/video_coding/jitter_buffer.h"
25#include "webrtc/modules/video_coding/packet.h"
philipel721d4022016-12-15 07:10:57 -080026#include "webrtc/modules/video_coding/timing.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010027#include "webrtc/system_wrappers/include/clock.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000028
agalusza@google.coma7e360e2013-08-01 03:15:08 +000029namespace webrtc {
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +000030namespace vcm {
niklase@google.com470e71d2011-07-07 08:21:25 +000031
philipel5908c712015-12-21 08:23:20 -080032int64_t VCMProcessTimer::Period() const {
33 return _periodMs;
niklase@google.com470e71d2011-07-07 08:21:25 +000034}
35
philipel5908c712015-12-21 08:23:20 -080036int64_t VCMProcessTimer::TimeUntilProcess() const {
37 const int64_t time_since_process = _clock->TimeInMilliseconds() - _latestMs;
38 const int64_t time_until_process = _periodMs - time_since_process;
39 return std::max<int64_t>(time_until_process, 0);
niklase@google.com470e71d2011-07-07 08:21:25 +000040}
41
philipel5908c712015-12-21 08:23:20 -080042void VCMProcessTimer::Processed() {
43 _latestMs = _clock->TimeInMilliseconds();
niklase@google.com470e71d2011-07-07 08:21:25 +000044}
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +000045} // namespace vcm
niklase@google.com470e71d2011-07-07 08:21:25 +000046
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +000047namespace {
andresp@webrtc.org1df9dc32014-01-09 08:01:57 +000048// This wrapper provides a way to modify the callback without the need to expose
49// a register method all the way down to the function calling it.
50class EncodedImageCallbackWrapper : public EncodedImageCallback {
51 public:
52 EncodedImageCallbackWrapper()
Sergey Ulanov525df3f2016-08-02 17:46:41 -070053 : cs_(CriticalSectionWrapper::CreateCriticalSection()),
54 callback_(nullptr) {}
andresp@webrtc.org1df9dc32014-01-09 08:01:57 +000055
56 virtual ~EncodedImageCallbackWrapper() {}
57
58 void Register(EncodedImageCallback* callback) {
59 CriticalSectionScoped cs(cs_.get());
60 callback_ = callback;
61 }
62
Sergey Ulanov525df3f2016-08-02 17:46:41 -070063 virtual Result OnEncodedImage(const EncodedImage& encoded_image,
64 const CodecSpecificInfo* codec_specific_info,
65 const RTPFragmentationHeader* fragmentation) {
andresp@webrtc.org1df9dc32014-01-09 08:01:57 +000066 CriticalSectionScoped cs(cs_.get());
Sergey Ulanov525df3f2016-08-02 17:46:41 -070067 if (callback_) {
68 return callback_->OnEncodedImage(encoded_image, codec_specific_info,
69 fragmentation);
70 }
71 return Result(Result::ERROR_SEND_FAILED);
andresp@webrtc.org1df9dc32014-01-09 08:01:57 +000072 }
73
74 private:
kwiberg3f55dea2016-02-29 05:51:59 -080075 std::unique_ptr<CriticalSectionWrapper> cs_;
andresp@webrtc.org1df9dc32014-01-09 08:01:57 +000076 EncodedImageCallback* callback_ GUARDED_BY(cs_);
77};
78
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +000079class VideoCodingModuleImpl : public VideoCodingModule {
80 public:
stefan@webrtc.org34c5da62014-04-11 14:08:35 +000081 VideoCodingModuleImpl(Clock* clock,
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +000082 EventFactory* event_factory,
philipel83f831a2016-03-12 03:30:23 -080083 NackSender* nack_sender,
sprang3911c262016-04-15 01:24:14 -070084 KeyFrameRequestSender* keyframe_request_sender,
85 EncodedImageCallback* pre_decode_image_callback)
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +000086 : VideoCodingModule(),
perkjf5b2e512016-07-05 08:34:04 -070087 sender_(clock, &post_encode_callback_, nullptr),
philipel721d4022016-12-15 07:10:57 -080088 timing_(new VCMTiming(clock)),
philipel83f831a2016-03-12 03:30:23 -080089 receiver_(clock,
90 event_factory,
sprang3911c262016-04-15 01:24:14 -070091 pre_decode_image_callback,
philipel721d4022016-12-15 07:10:57 -080092 timing_.get(),
philipel83f831a2016-03-12 03:30:23 -080093 nack_sender,
Peter Boström0b250722016-04-22 18:23:15 +020094 keyframe_request_sender) {}
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +000095
Peter Boström0b250722016-04-22 18:23:15 +020096 virtual ~VideoCodingModuleImpl() {}
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +000097
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000098 int64_t TimeUntilNextProcess() override {
pbos0fa9b222015-11-13 05:59:57 -080099 int64_t sender_time = sender_.TimeUntilNextProcess();
100 int64_t receiver_time = receiver_.TimeUntilNextProcess();
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000101 assert(sender_time >= 0);
102 assert(receiver_time >= 0);
103 return VCM_MIN(sender_time, receiver_time);
104 }
105
pbosa26ac922016-02-25 04:50:01 -0800106 void Process() override {
107 sender_.Process();
108 receiver_.Process();
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000109 }
110
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000111 int32_t RegisterSendCodec(const VideoCodec* sendCodec,
112 uint32_t numberOfCores,
113 uint32_t maxPayloadSize) override {
Erik Språng08127a92016-11-16 16:41:30 +0100114 if (sendCodec != nullptr && sendCodec->codecType == kVideoCodecVP8) {
115 // Set up a rate allocator and temporal layers factory for this vp8
116 // instance. The codec impl will have a raw pointer to the TL factory,
117 // and will call it when initializing. Since this can happen
118 // asynchronously keep the instance alive until destruction or until a
119 // new send codec is registered.
120 VideoCodec vp8_codec = *sendCodec;
121 std::unique_ptr<TemporalLayersFactory> tl_factory(
122 new TemporalLayersFactory());
123 vp8_codec.VP8()->tl_factory = tl_factory.get();
124 rate_allocator_ = VideoCodecInitializer::CreateBitrateAllocator(
125 vp8_codec, std::move(tl_factory));
126 return sender_.RegisterSendCodec(&vp8_codec, numberOfCores,
127 maxPayloadSize);
128 }
pbos0fa9b222015-11-13 05:59:57 -0800129 return sender_.RegisterSendCodec(sendCodec, numberOfCores, maxPayloadSize);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000130 }
131
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000132 int32_t RegisterExternalEncoder(VideoEncoder* externalEncoder,
133 uint8_t payloadType,
134 bool internalSource) override {
Peter Boström795dbe42015-11-27 14:09:07 +0100135 sender_.RegisterExternalEncoder(externalEncoder, payloadType,
136 internalSource);
137 return 0;
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000138 }
139
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000140 int Bitrate(unsigned int* bitrate) const override {
pbos0fa9b222015-11-13 05:59:57 -0800141 return sender_.Bitrate(bitrate);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000142 }
143
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000144 int FrameRate(unsigned int* framerate) const override {
pbos0fa9b222015-11-13 05:59:57 -0800145 return sender_.FrameRate(framerate);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000146 }
147
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000148 int32_t SetChannelParameters(uint32_t target_bitrate, // bits/s.
149 uint8_t lossRate,
150 int64_t rtt) override {
Erik Språng08127a92016-11-16 16:41:30 +0100151 return sender_.SetChannelParameters(target_bitrate, lossRate, rtt,
sprang1a646ee2016-12-01 06:34:11 -0800152 rate_allocator_.get(), nullptr);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000153 }
154
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000155 int32_t RegisterProtectionCallback(
156 VCMProtectionCallback* protection) override {
pbos0fa9b222015-11-13 05:59:57 -0800157 return sender_.RegisterProtectionCallback(protection);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000158 }
159
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000160 int32_t SetVideoProtection(VCMVideoProtection videoProtection,
161 bool enable) override {
pbosba8c15b2015-07-14 09:36:34 -0700162 // TODO(pbos): Remove enable from receive-side protection modes as well.
pbos0fa9b222015-11-13 05:59:57 -0800163 return receiver_.SetVideoProtection(videoProtection, enable);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000164 }
165
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700166 int32_t AddVideoFrame(const VideoFrame& videoFrame,
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000167 const CodecSpecificInfo* codecSpecificInfo) override {
Peter Boströmad6fc5a2016-05-12 03:01:31 +0200168 return sender_.AddVideoFrame(videoFrame, codecSpecificInfo);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000169 }
170
perkj600246e2016-05-04 11:26:51 -0700171 int32_t IntraFrameRequest(size_t stream_index) override {
pbos0fa9b222015-11-13 05:59:57 -0800172 return sender_.IntraFrameRequest(stream_index);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000173 }
174
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000175 int32_t EnableFrameDropper(bool enable) override {
pbos0fa9b222015-11-13 05:59:57 -0800176 return sender_.EnableFrameDropper(enable);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000177 }
178
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000179 int32_t RegisterReceiveCodec(const VideoCodec* receiveCodec,
180 int32_t numberOfCores,
181 bool requireKeyFrame) override {
pbos0fa9b222015-11-13 05:59:57 -0800182 return receiver_.RegisterReceiveCodec(receiveCodec, numberOfCores,
183 requireKeyFrame);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000184 }
185
Peter Boström795dbe42015-11-27 14:09:07 +0100186 void RegisterExternalDecoder(VideoDecoder* externalDecoder,
perkj796cfaf2015-12-10 09:27:38 -0800187 uint8_t payloadType) override {
188 receiver_.RegisterExternalDecoder(externalDecoder, payloadType);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000189 }
190
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000191 int32_t RegisterReceiveCallback(
192 VCMReceiveCallback* receiveCallback) override {
pbos0fa9b222015-11-13 05:59:57 -0800193 return receiver_.RegisterReceiveCallback(receiveCallback);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000194 }
195
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000196 int32_t RegisterReceiveStatisticsCallback(
197 VCMReceiveStatisticsCallback* receiveStats) override {
pbos0fa9b222015-11-13 05:59:57 -0800198 return receiver_.RegisterReceiveStatisticsCallback(receiveStats);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000199 }
200
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000201 int32_t RegisterDecoderTimingCallback(
202 VCMDecoderTimingCallback* decoderTiming) override {
pbos0fa9b222015-11-13 05:59:57 -0800203 return receiver_.RegisterDecoderTimingCallback(decoderTiming);
fischman@webrtc.org37bb4972013-10-23 23:59:45 +0000204 }
205
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000206 int32_t RegisterFrameTypeCallback(
207 VCMFrameTypeCallback* frameTypeCallback) override {
pbos0fa9b222015-11-13 05:59:57 -0800208 return receiver_.RegisterFrameTypeCallback(frameTypeCallback);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000209 }
210
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000211 int32_t RegisterPacketRequestCallback(
212 VCMPacketRequestCallback* callback) override {
pbos0fa9b222015-11-13 05:59:57 -0800213 return receiver_.RegisterPacketRequestCallback(callback);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000214 }
215
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000216 int32_t Decode(uint16_t maxWaitTimeMs) override {
pbos0fa9b222015-11-13 05:59:57 -0800217 return receiver_.Decode(maxWaitTimeMs);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000218 }
219
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000220 int32_t ReceiveCodec(VideoCodec* currentReceiveCodec) const override {
pbos0fa9b222015-11-13 05:59:57 -0800221 return receiver_.ReceiveCodec(currentReceiveCodec);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000222 }
223
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000224 VideoCodecType ReceiveCodec() const override {
pbos0fa9b222015-11-13 05:59:57 -0800225 return receiver_.ReceiveCodec();
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000226 }
227
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000228 int32_t IncomingPacket(const uint8_t* incomingPayload,
229 size_t payloadLength,
230 const WebRtcRTPHeader& rtpInfo) override {
pbos0fa9b222015-11-13 05:59:57 -0800231 return receiver_.IncomingPacket(incomingPayload, payloadLength, rtpInfo);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000232 }
233
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000234 int32_t SetMinimumPlayoutDelay(uint32_t minPlayoutDelayMs) override {
pbos0fa9b222015-11-13 05:59:57 -0800235 return receiver_.SetMinimumPlayoutDelay(minPlayoutDelayMs);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000236 }
237
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000238 int32_t SetRenderDelay(uint32_t timeMS) override {
pbos0fa9b222015-11-13 05:59:57 -0800239 return receiver_.SetRenderDelay(timeMS);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000240 }
241
pbos0fa9b222015-11-13 05:59:57 -0800242 int32_t Delay() const override { return receiver_.Delay(); }
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000243
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000244 uint32_t DiscardedPackets() const override {
pbos0fa9b222015-11-13 05:59:57 -0800245 return receiver_.DiscardedPackets();
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000246 }
247
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000248 int SetReceiverRobustnessMode(ReceiverRobustness robustnessMode,
249 VCMDecodeErrorMode errorMode) override {
pbos0fa9b222015-11-13 05:59:57 -0800250 return receiver_.SetReceiverRobustnessMode(robustnessMode, errorMode);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000251 }
252
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000253 void SetNackSettings(size_t max_nack_list_size,
254 int max_packet_age_to_nack,
255 int max_incomplete_time_ms) override {
pbos0fa9b222015-11-13 05:59:57 -0800256 return receiver_.SetNackSettings(max_nack_list_size, max_packet_age_to_nack,
257 max_incomplete_time_ms);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000258 }
259
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000260 void SetDecodeErrorMode(VCMDecodeErrorMode decode_error_mode) override {
pbos0fa9b222015-11-13 05:59:57 -0800261 return receiver_.SetDecodeErrorMode(decode_error_mode);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000262 }
263
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000264 int SetMinReceiverDelay(int desired_delay_ms) override {
pbos0fa9b222015-11-13 05:59:57 -0800265 return receiver_.SetMinReceiverDelay(desired_delay_ms);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000266 }
267
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000268 int32_t SetReceiveChannelParameters(int64_t rtt) override {
pbos0fa9b222015-11-13 05:59:57 -0800269 return receiver_.SetReceiveChannelParameters(rtt);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000270 }
271
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +0000272 void RegisterPostEncodeImageCallback(
273 EncodedImageCallback* observer) override {
andresp@webrtc.org1df9dc32014-01-09 08:01:57 +0000274 post_encode_callback_.Register(observer);
sprang@webrtc.org40709352013-11-26 11:41:59 +0000275 }
276
pbos0fa9b222015-11-13 05:59:57 -0800277 void TriggerDecoderShutdown() override { receiver_.TriggerDecoderShutdown(); }
pbos@webrtc.org4dd40d62015-02-17 13:22:43 +0000278
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000279 private:
andresp@webrtc.org1df9dc32014-01-09 08:01:57 +0000280 EncodedImageCallbackWrapper post_encode_callback_;
pbos0fa9b222015-11-13 05:59:57 -0800281 vcm::VideoSender sender_;
Erik Språng08127a92016-11-16 16:41:30 +0100282 std::unique_ptr<VideoBitrateAllocator> rate_allocator_;
philipel721d4022016-12-15 07:10:57 -0800283 std::unique_ptr<VCMTiming> timing_;
pbos0fa9b222015-11-13 05:59:57 -0800284 vcm::VideoReceiver receiver_;
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000285};
286} // namespace
287
Peter Boström7776e782016-01-07 15:42:47 +0100288void VideoCodingModule::Codec(VideoCodecType codecType, VideoCodec* codec) {
Peter Boström92f8dbd2015-11-24 13:55:55 +0100289 VCMCodecDataBase::Codec(codecType, codec);
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000290}
291
philipel83f831a2016-03-12 03:30:23 -0800292// Create method for the new jitter buffer.
293VideoCodingModule* VideoCodingModule::Create(
294 Clock* clock,
philipel83f831a2016-03-12 03:30:23 -0800295 VCMQMSettingsCallback* qm_settings_callback,
296 NackSender* nack_sender,
sprang3911c262016-04-15 01:24:14 -0700297 KeyFrameRequestSender* keyframe_request_sender,
298 EncodedImageCallback* pre_decode_image_callback) {
perkjf5b2e512016-07-05 08:34:04 -0700299 return new VideoCodingModuleImpl(clock, nullptr, nack_sender,
300 keyframe_request_sender,
Peter Boströmad6fc5a2016-05-12 03:01:31 +0200301 pre_decode_image_callback);
philipel83f831a2016-03-12 03:30:23 -0800302}
303
304// Create method for current interface, will be removed when the
305// new jitter buffer is in place.
philipel5908c712015-12-21 08:23:20 -0800306VideoCodingModule* VideoCodingModule::Create(Clock* clock,
307 EventFactory* event_factory) {
philipel83f831a2016-03-12 03:30:23 -0800308 return VideoCodingModule::Create(clock, event_factory,
309 nullptr, // NackSender
310 nullptr); // KeyframeRequestSender
311}
312
313// Create method for the new jitter buffer.
314VideoCodingModule* VideoCodingModule::Create(
315 Clock* clock,
316 EventFactory* event_factory,
317 NackSender* nack_sender,
318 KeyFrameRequestSender* keyframe_request_sender) {
andresp@webrtc.orgf7eb75b2013-09-14 00:25:28 +0000319 assert(clock);
320 assert(event_factory);
perkjf5b2e512016-07-05 08:34:04 -0700321 return new VideoCodingModuleImpl(clock, event_factory, nack_sender,
Peter Boströmad6fc5a2016-05-12 03:01:31 +0200322 keyframe_request_sender, nullptr);
niklase@google.com470e71d2011-07-07 08:21:25 +0000323}
324
stefan@webrtc.org791eec72011-10-11 07:53:43 +0000325} // namespace webrtc