blob: 29807ef30e7a79106e46eff33989c8f82cc6f727 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2004 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#ifndef TALK_MEDIA_WEBRTCVOICEENGINE_H_
29#define TALK_MEDIA_WEBRTCVOICEENGINE_H_
30
31#include <map>
32#include <set>
33#include <string>
34#include <vector>
35
36#include "talk/base/buffer.h"
37#include "talk/base/byteorder.h"
38#include "talk/base/logging.h"
39#include "talk/base/scoped_ptr.h"
40#include "talk/base/stream.h"
41#include "talk/media/base/rtputils.h"
42#include "talk/media/webrtc/webrtccommon.h"
43#include "talk/media/webrtc/webrtcexport.h"
44#include "talk/media/webrtc/webrtcvoe.h"
45#include "talk/session/media/channel.h"
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +000046#include "webrtc/common.h"
47#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000048
49#if !defined(LIBPEERCONNECTION_LIB) && \
50 !defined(LIBPEERCONNECTION_IMPLEMENTATION)
51#error "Bogus include."
52#endif
53
54
55namespace cricket {
56
57// WebRtcSoundclipStream is an adapter object that allows a memory stream to be
58// passed into WebRtc, and support looping.
59class WebRtcSoundclipStream : public webrtc::InStream {
60 public:
61 WebRtcSoundclipStream(const char* buf, size_t len)
62 : mem_(buf, len), loop_(true) {
63 }
64 void set_loop(bool loop) { loop_ = loop; }
65 virtual int Read(void* buf, int len);
66 virtual int Rewind();
67
68 private:
69 talk_base::MemoryStream mem_;
70 bool loop_;
71};
72
73// WebRtcMonitorStream is used to monitor a stream coming from WebRtc.
74// For now we just dump the data.
75class WebRtcMonitorStream : public webrtc::OutStream {
76 virtual bool Write(const void *buf, int len) {
77 return true;
78 }
79};
80
81class AudioDeviceModule;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000082class AudioRenderer;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000083class VoETraceWrapper;
84class VoEWrapper;
85class VoiceProcessor;
86class WebRtcSoundclipMedia;
87class WebRtcVoiceMediaChannel;
88
89// WebRtcVoiceEngine is a class to be used with CompositeMediaEngine.
90// It uses the WebRtc VoiceEngine library for audio handling.
91class WebRtcVoiceEngine
92 : public webrtc::VoiceEngineObserver,
93 public webrtc::TraceCallback,
94 public webrtc::VoEMediaProcess {
95 public:
96 WebRtcVoiceEngine();
97 // Dependency injection for testing.
98 WebRtcVoiceEngine(VoEWrapper* voe_wrapper,
99 VoEWrapper* voe_wrapper_sc,
100 VoETraceWrapper* tracing);
101 ~WebRtcVoiceEngine();
102 bool Init(talk_base::Thread* worker_thread);
103 void Terminate();
104
105 int GetCapabilities();
106 VoiceMediaChannel* CreateChannel();
107
108 SoundclipMedia* CreateSoundclip();
109
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000110 AudioOptions GetOptions() const { return options_; }
111 bool SetOptions(const AudioOptions& options);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112 // Overrides, when set, take precedence over the options on a
113 // per-option basis. For example, if AGC is set in options and AEC
114 // is set in overrides, AGC and AEC will be both be set. Overrides
115 // can also turn off options. For example, if AGC is set to "on" in
116 // options and AGC is set to "off" in overrides, the result is that
117 // AGC will be off until different overrides are applied or until
118 // the overrides are cleared. Only one set of overrides is present
119 // at a time (they do not "stack"). And when the overrides are
120 // cleared, the media engine's state reverts back to the options set
121 // via SetOptions. This allows us to have both "persistent options"
122 // (the normal options) and "temporary options" (overrides).
123 bool SetOptionOverrides(const AudioOptions& options);
124 bool ClearOptionOverrides();
125 bool SetDelayOffset(int offset);
126 bool SetDevices(const Device* in_device, const Device* out_device);
127 bool GetOutputVolume(int* level);
128 bool SetOutputVolume(int level);
129 int GetInputLevel();
130 bool SetLocalMonitor(bool enable);
131
132 const std::vector<AudioCodec>& codecs();
133 bool FindCodec(const AudioCodec& codec);
134 bool FindWebRtcCodec(const AudioCodec& codec, webrtc::CodecInst* gcodec);
135
136 const std::vector<RtpHeaderExtension>& rtp_header_extensions() const;
137
138 void SetLogging(int min_sev, const char* filter);
139
140 bool RegisterProcessor(uint32 ssrc,
141 VoiceProcessor* voice_processor,
142 MediaProcessorDirection direction);
143 bool UnregisterProcessor(uint32 ssrc,
144 VoiceProcessor* voice_processor,
145 MediaProcessorDirection direction);
146
147 // Method from webrtc::VoEMediaProcess
148 virtual void Process(int channel,
149 webrtc::ProcessingTypes type,
150 int16_t audio10ms[],
151 int length,
152 int sampling_freq,
153 bool is_stereo);
154
155 // For tracking WebRtc channels. Needed because we have to pause them
156 // all when switching devices.
157 // May only be called by WebRtcVoiceMediaChannel.
158 void RegisterChannel(WebRtcVoiceMediaChannel *channel);
159 void UnregisterChannel(WebRtcVoiceMediaChannel *channel);
160
161 // May only be called by WebRtcSoundclipMedia.
162 void RegisterSoundclip(WebRtcSoundclipMedia *channel);
163 void UnregisterSoundclip(WebRtcSoundclipMedia *channel);
164
165 // Called by WebRtcVoiceMediaChannel to set a gain offset from
166 // the default AGC target level.
167 bool AdjustAgcLevel(int delta);
168
169 VoEWrapper* voe() { return voe_wrapper_.get(); }
170 VoEWrapper* voe_sc() { return voe_wrapper_sc_.get(); }
171 int GetLastEngineError();
172
173 // Set the external ADMs. This can only be called before Init.
174 bool SetAudioDeviceModule(webrtc::AudioDeviceModule* adm,
175 webrtc::AudioDeviceModule* adm_sc);
176
177 // Check whether the supplied trace should be ignored.
178 bool ShouldIgnoreTrace(const std::string& trace);
179
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000180 // Create a VoiceEngine Channel.
181 int CreateMediaVoiceChannel();
182 int CreateSoundclipVoiceChannel();
183
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000184 private:
185 typedef std::vector<WebRtcSoundclipMedia *> SoundclipList;
186 typedef std::vector<WebRtcVoiceMediaChannel *> ChannelList;
187 typedef sigslot::
188 signal3<uint32, MediaProcessorDirection, AudioFrame*> FrameSignal;
189
190 void Construct();
191 void ConstructCodecs();
192 bool InitInternal();
wu@webrtc.org4551b792013-10-09 15:37:36 +0000193 bool EnsureSoundclipEngineInit();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194 void SetTraceFilter(int filter);
195 void SetTraceOptions(const std::string& options);
196 // Applies either options or overrides. Every option that is "set"
197 // will be applied. Every option not "set" will be ignored. This
198 // allows us to selectively turn on and off different options easily
199 // at any time.
200 bool ApplyOptions(const AudioOptions& options);
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000201 // Configure for using ACM2, if |enable| is true, otherwise configure for
202 // ACM1.
203 void EnableExperimentalAcm(bool enable);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000204 virtual void Print(webrtc::TraceLevel level, const char* trace, int length);
205 virtual void CallbackOnError(int channel, int errCode);
206 // Given the device type, name, and id, find device id. Return true and
207 // set the output parameter rtc_id if successful.
208 bool FindWebRtcAudioDeviceId(
209 bool is_input, const std::string& dev_name, int dev_id, int* rtc_id);
210 bool FindChannelAndSsrc(int channel_num,
211 WebRtcVoiceMediaChannel** channel,
212 uint32* ssrc) const;
213 bool FindChannelNumFromSsrc(uint32 ssrc,
214 MediaProcessorDirection direction,
215 int* channel_num);
216 bool ChangeLocalMonitor(bool enable);
217 bool PauseLocalMonitor();
218 bool ResumeLocalMonitor();
219
220 bool UnregisterProcessorChannel(MediaProcessorDirection channel_direction,
221 uint32 ssrc,
222 VoiceProcessor* voice_processor,
223 MediaProcessorDirection processor_direction);
224
225 void StartAecDump(const std::string& filename);
226 void StopAecDump();
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000227 int CreateVoiceChannel(VoEWrapper* voe);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000228
229 // When a voice processor registers with the engine, it is connected
230 // to either the Rx or Tx signals, based on the direction parameter.
231 // SignalXXMediaFrame will be invoked for every audio packet.
232 FrameSignal SignalRxMediaFrame;
233 FrameSignal SignalTxMediaFrame;
234
235 static const int kDefaultLogSeverity = talk_base::LS_WARNING;
236
237 // The primary instance of WebRtc VoiceEngine.
238 talk_base::scoped_ptr<VoEWrapper> voe_wrapper_;
239 // A secondary instance, for playing out soundclips (on the 'ring' device).
240 talk_base::scoped_ptr<VoEWrapper> voe_wrapper_sc_;
wu@webrtc.org4551b792013-10-09 15:37:36 +0000241 bool voe_wrapper_sc_initialized_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000242 talk_base::scoped_ptr<VoETraceWrapper> tracing_;
243 // The external audio device manager
244 webrtc::AudioDeviceModule* adm_;
245 webrtc::AudioDeviceModule* adm_sc_;
246 int log_filter_;
247 std::string log_options_;
248 bool is_dumping_aec_;
249 std::vector<AudioCodec> codecs_;
250 std::vector<RtpHeaderExtension> rtp_header_extensions_;
251 bool desired_local_monitor_enable_;
252 talk_base::scoped_ptr<WebRtcMonitorStream> monitor_;
253 SoundclipList soundclips_;
254 ChannelList channels_;
255 // channels_ can be read from WebRtc callback thread. We need a lock on that
256 // callback as well as the RegisterChannel/UnregisterChannel.
257 talk_base::CriticalSection channels_cs_;
258 webrtc::AgcConfig default_agc_config_;
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000259
260 webrtc::Config voe_config_;
261 bool use_experimental_acm_;
262
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000263 bool initialized_;
264 // See SetOptions and SetOptionOverrides for a description of the
265 // difference between options and overrides.
266 // options_ are the base options, which combined with the
267 // option_overrides_, create the current options being used.
268 // options_ is stored so that when option_overrides_ is cleared, we
269 // can restore the options_ without the option_overrides.
270 AudioOptions options_;
271 AudioOptions option_overrides_;
272
273 // When the media processor registers with the engine, the ssrc is cached
274 // here so that a look up need not be made when the callback is invoked.
275 // This is necessary because the lookup results in mux_channels_cs lock being
276 // held and if a remote participant leaves the hangout at the same time
277 // we hit a deadlock.
278 uint32 tx_processor_ssrc_;
279 uint32 rx_processor_ssrc_;
280
281 talk_base::CriticalSection signal_media_critical_;
282};
283
284// WebRtcMediaChannel is a class that implements the common WebRtc channel
285// functionality.
286template <class T, class E>
287class WebRtcMediaChannel : public T, public webrtc::Transport {
288 public:
289 WebRtcMediaChannel(E *engine, int channel)
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000290 : engine_(engine), voe_channel_(channel) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000291 E *engine() { return engine_; }
292 int voe_channel() const { return voe_channel_; }
293 bool valid() const { return voe_channel_ != -1; }
294
295 protected:
296 // implements Transport interface
297 virtual int SendPacket(int channel, const void *data, int len) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000298 talk_base::Buffer packet(data, len, kMaxRtpPacketLen);
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000299 if (!T::SendPacket(&packet)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000300 return -1;
301 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000302 return len;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000304
305 virtual int SendRTCPPacket(int channel, const void *data, int len) {
306 talk_base::Buffer packet(data, len, kMaxRtpPacketLen);
307 return T::SendRtcp(&packet) ? len : -1;
308 }
309
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000310 private:
311 E *engine_;
312 int voe_channel_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000313};
314
315// WebRtcVoiceMediaChannel is an implementation of VoiceMediaChannel that uses
316// WebRtc Voice Engine.
317class WebRtcVoiceMediaChannel
318 : public WebRtcMediaChannel<VoiceMediaChannel, WebRtcVoiceEngine> {
319 public:
320 explicit WebRtcVoiceMediaChannel(WebRtcVoiceEngine *engine);
321 virtual ~WebRtcVoiceMediaChannel();
322 virtual bool SetOptions(const AudioOptions& options);
323 virtual bool GetOptions(AudioOptions* options) const {
324 *options = options_;
325 return true;
326 }
327 virtual bool SetRecvCodecs(const std::vector<AudioCodec> &codecs);
328 virtual bool SetSendCodecs(const std::vector<AudioCodec> &codecs);
329 virtual bool SetRecvRtpHeaderExtensions(
330 const std::vector<RtpHeaderExtension>& extensions);
331 virtual bool SetSendRtpHeaderExtensions(
332 const std::vector<RtpHeaderExtension>& extensions);
333 virtual bool SetPlayout(bool playout);
334 bool PausePlayout();
335 bool ResumePlayout();
336 virtual bool SetSend(SendFlags send);
337 bool PauseSend();
338 bool ResumeSend();
339 virtual bool AddSendStream(const StreamParams& sp);
340 virtual bool RemoveSendStream(uint32 ssrc);
341 virtual bool AddRecvStream(const StreamParams& sp);
342 virtual bool RemoveRecvStream(uint32 ssrc);
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000343 virtual bool SetRemoteRenderer(uint32 ssrc, AudioRenderer* renderer);
344 virtual bool SetLocalRenderer(uint32 ssrc, AudioRenderer* renderer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000345 virtual bool GetActiveStreams(AudioInfo::StreamList* actives);
346 virtual int GetOutputLevel();
347 virtual int GetTimeSinceLastTyping();
348 virtual void SetTypingDetectionParameters(int time_window,
349 int cost_per_typing, int reporting_threshold, int penalty_decay,
350 int type_event_delay);
351 virtual bool SetOutputScaling(uint32 ssrc, double left, double right);
352 virtual bool GetOutputScaling(uint32 ssrc, double* left, double* right);
353
354 virtual bool SetRingbackTone(const char *buf, int len);
355 virtual bool PlayRingbackTone(uint32 ssrc, bool play, bool loop);
356 virtual bool CanInsertDtmf();
357 virtual bool InsertDtmf(uint32 ssrc, int event, int duration, int flags);
358
wu@webrtc.org20182692013-12-12 22:54:25 +0000359 virtual void OnPacketReceived(talk_base::Buffer* packet);
360 virtual void OnRtcpReceived(talk_base::Buffer* packet);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000361 virtual void OnReadyToSend(bool ready) {}
362 virtual bool MuteStream(uint32 ssrc, bool on);
363 virtual bool SetSendBandwidth(bool autobw, int bps);
364 virtual bool GetStats(VoiceMediaInfo* info);
365 // Gets last reported error from WebRtc voice engine. This should be only
366 // called in response a failure.
367 virtual void GetLastMediaError(uint32* ssrc,
368 VoiceMediaChannel::Error* error);
369 bool FindSsrc(int channel_num, uint32* ssrc);
370 void OnError(uint32 ssrc, int error);
371
372 bool sending() const { return send_ != SEND_NOTHING; }
373 int GetReceiveChannelNum(uint32 ssrc);
374 int GetSendChannelNum(uint32 ssrc);
375
376 protected:
377 int GetLastEngineError() { return engine()->GetLastEngineError(); }
378 int GetOutputLevel(int channel);
379 bool GetRedSendCodec(const AudioCodec& red_codec,
380 const std::vector<AudioCodec>& all_codecs,
381 webrtc::CodecInst* send_codec);
382 bool EnableRtcp(int channel);
383 bool ResetRecvCodecs(int channel);
384 bool SetPlayout(int channel, bool playout);
385 static uint32 ParseSsrc(const void* data, size_t len, bool rtcp);
386 static Error WebRtcErrorToChannelError(int err_code);
387
388 private:
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000389 struct WebRtcVoiceChannelInfo;
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000390 typedef std::map<uint32, WebRtcVoiceChannelInfo> ChannelMap;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000391
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000392 void SetNack(int channel, bool nack_enabled);
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000393 void SetNack(const ChannelMap& channels, bool nack_enabled);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000394 bool SetSendCodec(const webrtc::CodecInst& send_codec);
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000395 bool SetSendCodec(int channel, const webrtc::CodecInst& send_codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000396 bool ChangePlayout(bool playout);
397 bool ChangeSend(SendFlags send);
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000398 bool ChangeSend(int channel, SendFlags send);
399 void ConfigureSendChannel(int channel);
wu@webrtc.org78187522013-10-07 23:32:02 +0000400 bool ConfigureRecvChannel(int channel);
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000401 bool DeleteChannel(int channel);
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000402 bool InConferenceMode() const {
403 return options_.conference_mode.GetWithDefaultIfUnset(false);
404 }
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000405 bool IsDefaultChannel(int channel_id) const {
406 return channel_id == voe_channel();
407 }
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000408 bool SetSendCodecs(int channel, const std::vector<AudioCodec>& codecs);
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +0000409 bool SetSendBandwidthInternal(bool autobw, int bps);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000410
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000411 talk_base::scoped_ptr<WebRtcSoundclipStream> ringback_tone_;
412 std::set<int> ringback_channels_; // channels playing ringback
413 std::vector<AudioCodec> recv_codecs_;
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000414 std::vector<AudioCodec> send_codecs_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000415 talk_base::scoped_ptr<webrtc::CodecInst> send_codec_;
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +0000416 bool send_bw_setting_;
417 bool send_autobw_;
418 int send_bw_bps_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000419 AudioOptions options_;
420 bool dtmf_allowed_;
421 bool desired_playout_;
422 bool nack_enabled_;
423 bool playout_;
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000424 bool typing_noise_detected_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000425 SendFlags desired_send_;
426 SendFlags send_;
427
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000428 // send_channels_ contains the channels which are being used for sending.
429 // When the default channel (voe_channel) is used for sending, it is
430 // contained in send_channels_, otherwise not.
431 ChannelMap send_channels_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432 uint32 default_receive_ssrc_;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000433 // Note the default channel (voe_channel()) can reside in both
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000434 // receive_channels_ and send_channels_ in non-conference mode and in that
435 // case it will only be there if a non-zero default_receive_ssrc_ is set.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000436 ChannelMap receive_channels_; // for multiple sources
437 // receive_channels_ can be read from WebRtc callback thread. Access from
438 // the WebRtc thread must be synchronized with edits on the worker thread.
439 // Reads on the worker thread are ok.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000440 //
441 // Do not lock this on the VoE media processor thread; potential for deadlock
442 // exists.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000443 mutable talk_base::CriticalSection receive_channels_cs_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000444};
445
446} // namespace cricket
447
448#endif // TALK_MEDIA_WEBRTCVOICEENGINE_H_