Using Rent-A-Codec for static Codec access in WVoE/MC.
Mostly moved code around in WebRtcVoiceEngine:
- Added new internal class WebRtcVoiceCodecs for static codec functions and the CodecPrefs.
- ConstructCodecs() -> WebRtcVoiceCodecs::SupportedCodecs().
- FindWebRtcCodec -> WebRtcVoiceCodecs::ToCodecInst().
- WebRtcVoiceMediaChannel::SetRecvCodecsInternal() folded into WebRtcVoiceMediaChannel::SetRecvCodecs() (slight logic change).
- Change to how SetRecPayloadType() is implemented in fakewebrtcvoiceengine.h (lines 460-470).
BUG=webrtc:4690
Review URL: https://codereview.webrtc.org/1461333002
Cr-Commit-Position: refs/heads/master@{#10819}
diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h
index f0bd6a6..6ca3341 100644
--- a/talk/media/webrtc/fakewebrtcvoiceengine.h
+++ b/talk/media/webrtc/fakewebrtcvoiceengine.h
@@ -41,6 +41,7 @@
#include "webrtc/base/gunit.h"
#include "webrtc/base/stringutils.h"
#include "webrtc/config.h"
+#include "webrtc/modules/audio_coding/acm2/rent_a_codec.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
namespace cricket {
@@ -63,18 +64,6 @@
#define WEBRTC_CHECK_CHANNEL(channel) \
if (channels_.find(channel) == channels_.end()) return -1;
-#define WEBRTC_ASSERT_CHANNEL(channel) \
- RTC_DCHECK(channels_.find(channel) != channels_.end());
-
-// Verify the header extension ID, if enabled, is within the bounds specified in
-// [RFC5285]: 1-14 inclusive.
-#define WEBRTC_CHECK_HEADER_EXTENSION_ID(enable, id) \
- do { \
- if (enable && (id < 1 || id > 14)) { \
- return -1; \
- } \
- } while (0);
-
class FakeAudioProcessing : public webrtc::AudioProcessing {
public:
FakeAudioProcessing() : experimental_ns_enabled_(false) {}
@@ -189,6 +178,7 @@
nack_max_packets(0),
send_ssrc(0),
associate_send_channel(-1),
+ recv_codecs(),
neteq_capacity(-1),
neteq_fast_accelerate(false) {
memset(&send_codec, 0, sizeof(send_codec));
@@ -219,13 +209,10 @@
bool neteq_fast_accelerate;
};
- FakeWebRtcVoiceEngine(const cricket::AudioCodec* const* codecs,
- int num_codecs)
+ FakeWebRtcVoiceEngine()
: inited_(false),
last_channel_(-1),
fail_create_channel_(false),
- codecs_(codecs),
- num_codecs_(num_codecs),
num_set_send_codecs_(0),
ec_enabled_(false),
ec_metrics_enabled_(false),
@@ -247,12 +234,7 @@
memset(&agc_config_, 0, sizeof(agc_config_));
}
~FakeWebRtcVoiceEngine() {
- // Ought to have all been deleted by the WebRtcVoiceMediaChannel
- // destructors, but just in case ...
- for (std::map<int, Channel*>::const_iterator i = channels_.begin();
- i != channels_.end(); ++i) {
- delete i->second;
- }
+ RTC_CHECK(channels_.empty());
}
bool ec_metrics_enabled() const { return ec_metrics_enabled_; }
@@ -291,7 +273,7 @@
return channels_[channel]->nack_max_packets;
}
const webrtc::PacketTime& GetLastRtpPacketTime(int channel) {
- WEBRTC_ASSERT_CHANNEL(channel);
+ RTC_DCHECK(channels_.find(channel) != channels_.end());
return channels_[channel]->last_rtp_packet_time;
}
int GetSendCNPayloadType(int channel, bool wideband) {
@@ -335,11 +317,8 @@
return -1;
}
Channel* ch = new Channel();
- for (int i = 0; i < NumOfCodecs(); ++i) {
- webrtc::CodecInst codec;
- GetCodec(i, codec);
- ch->recv_codecs.push_back(codec);
- }
+ auto db = webrtc::acm2::RentACodec::Database();
+ ch->recv_codecs.assign(db.begin(), db.end());
if (config.Get<webrtc::NetEqCapacityConfig>().enabled) {
ch->neteq_capacity = config.Get<webrtc::NetEqCapacityConfig>().capacity;
}
@@ -439,22 +418,8 @@
webrtc::RtcEventLog* GetEventLog() { return nullptr; }
// webrtc::VoECodec
- WEBRTC_FUNC(NumOfCodecs, ()) {
- return num_codecs_;
- }
- WEBRTC_FUNC(GetCodec, (int index, webrtc::CodecInst& codec)) {
- if (index < 0 || index >= NumOfCodecs()) {
- return -1;
- }
- const cricket::AudioCodec& c(*codecs_[index]);
- codec.pltype = c.id;
- rtc::strcpyn(codec.plname, sizeof(codec.plname), c.name.c_str());
- codec.plfreq = c.clockrate;
- codec.pacsize = 0;
- codec.channels = c.channels;
- codec.rate = c.bitrate;
- return 0;
- }
+ WEBRTC_STUB(NumOfCodecs, ());
+ WEBRTC_STUB(GetCodec, (int index, webrtc::CodecInst& codec));
WEBRTC_FUNC(SetSendCodec, (int channel, const webrtc::CodecInst& codec)) {
WEBRTC_CHECK_CHANNEL(channel);
// To match the behavior of the real implementation.
@@ -492,16 +457,17 @@
}
}
// Otherwise try to find this codec and update its payload type.
+ int result = -1; // not found
for (std::vector<webrtc::CodecInst>::iterator it = ch->recv_codecs.begin();
it != ch->recv_codecs.end(); ++it) {
if (strcmp(it->plname, codec.plname) == 0 &&
- it->plfreq == codec.plfreq) {
+ it->plfreq == codec.plfreq &&
+ it->channels == codec.channels) {
it->pltype = codec.pltype;
- it->channels = codec.channels;
- return 0;
+ result = 0;
}
}
- return -1; // not found
+ return result;
}
WEBRTC_FUNC(SetSendCNPayloadType, (int channel, int type,
webrtc::PayloadFrequencies frequency)) {
@@ -932,8 +898,6 @@
int last_channel_;
std::map<int, Channel*> channels_;
bool fail_create_channel_;
- const cricket::AudioCodec* const* codecs_;
- int num_codecs_;
int num_set_send_codecs_; // how many times we call SetSendCodec().
bool ec_enabled_;
bool ec_metrics_enabled_;
@@ -957,8 +921,6 @@
FakeAudioProcessing audio_processing_;
};
-#undef WEBRTC_CHECK_HEADER_EXTENSION_ID
-
} // namespace cricket
#endif // TALK_SESSION_PHONE_FAKEWEBRTCVOICEENGINE_H_
diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc
index 721990a..4bd0400 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -53,6 +53,7 @@
#include "webrtc/base/stringutils.h"
#include "webrtc/call/rtc_event_log.h"
#include "webrtc/common.h"
+#include "webrtc/modules/audio_coding/acm2/rent_a_codec.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/system_wrappers/include/field_trial.h"
#include "webrtc/system_wrappers/include/trace.h"
@@ -66,32 +67,6 @@
const int kElevatedTraceFilter = kDefaultTraceFilter | webrtc::kTraceStateInfo |
webrtc::kTraceInfo;
-const int kMaxNumPacketSize = 6;
-struct CodecPref {
- const char* name;
- int clockrate;
- int channels;
- int payload_type;
- bool is_multi_rate;
- int packet_sizes_ms[kMaxNumPacketSize];
-};
-// Note: keep the supported packet sizes in ascending order.
-const CodecPref kCodecPrefs[] = {
- { kOpusCodecName, 48000, 2, 111, true, { 10, 20, 40, 60 } },
- { kIsacCodecName, 16000, 1, 103, true, { 30, 60 } },
- { kIsacCodecName, 32000, 1, 104, true, { 30 } },
- // G722 should be advertised as 8000 Hz because of the RFC "bug".
- { kG722CodecName, 8000, 1, 9, false, { 10, 20, 30, 40, 50, 60 } },
- { kIlbcCodecName, 8000, 1, 102, false, { 20, 30, 40, 60 } },
- { kPcmuCodecName, 8000, 1, 0, false, { 10, 20, 30, 40, 50, 60 } },
- { kPcmaCodecName, 8000, 1, 8, false, { 10, 20, 30, 40, 50, 60 } },
- { kCnCodecName, 32000, 1, 106, false, { } },
- { kCnCodecName, 16000, 1, 105, false, { } },
- { kCnCodecName, 8000, 1, 13, false, { } },
- { kRedCodecName, 8000, 1, 127, false, { } },
- { kDtmfCodecName, 8000, 1, 126, false, { } },
-};
-
// For Linux/Mac, using the default device is done by specifying index 0 for
// VoE 4.0 and not -1 (which was the case for VoE 3.5).
//
@@ -185,13 +160,6 @@
return ss.str();
}
-void LogMultiline(rtc::LoggingSeverity sev, char* text) {
- const char* delim = "\r\n";
- for (char* tok = strtok(text, delim); tok; tok = strtok(NULL, delim)) {
- LOG_V(sev) << tok;
- }
-}
-
bool IsCodec(const AudioCodec& codec, const char* ref_name) {
return (_stricmp(codec.name.c_str(), ref_name) == 0);
}
@@ -200,19 +168,9 @@
return (_stricmp(codec.plname, ref_name) == 0);
}
-bool IsCodecMultiRate(const webrtc::CodecInst& codec) {
- for (size_t i = 0; i < arraysize(kCodecPrefs); ++i) {
- if (IsCodec(codec, kCodecPrefs[i].name) &&
- kCodecPrefs[i].clockrate == codec.plfreq) {
- return kCodecPrefs[i].is_multi_rate;
- }
- }
- return false;
-}
-
bool FindCodec(const std::vector<AudioCodec>& codecs,
- const AudioCodec& codec,
- AudioCodec* found_codec) {
+ const AudioCodec& codec,
+ AudioCodec* found_codec) {
for (const AudioCodec& c : codecs) {
if (c.Matches(codec)) {
if (found_codec != NULL) {
@@ -242,38 +200,8 @@
kParamValueEmpty));
}
-int SelectPacketSize(const CodecPref& codec_pref, int ptime_ms) {
- int selected_packet_size_ms = codec_pref.packet_sizes_ms[0];
- for (int packet_size_ms : codec_pref.packet_sizes_ms) {
- if (packet_size_ms && packet_size_ms <= ptime_ms) {
- selected_packet_size_ms = packet_size_ms;
- }
- }
- return selected_packet_size_ms;
-}
-
-// If the AudioCodec param kCodecParamPTime is set, then we will set it to codec
-// pacsize if it's valid, or we will pick the next smallest value we support.
-// TODO(Brave): Query supported packet sizes from ACM when the API is ready.
-bool SetPTimeAsPacketSize(webrtc::CodecInst* codec, int ptime_ms) {
- for (const CodecPref& codec_pref : kCodecPrefs) {
- if ((IsCodec(*codec, codec_pref.name) &&
- codec_pref.clockrate == codec->plfreq) ||
- IsCodec(*codec, kG722CodecName)) {
- int packet_size_ms = SelectPacketSize(codec_pref, ptime_ms);
- if (packet_size_ms) {
- // Convert unit from milli-seconds to samples.
- codec->pacsize = (codec->plfreq / 1000) * packet_size_ms;
- return true;
- }
- }
- }
- return false;
-}
-
// Return true if codec.params[feature] == "1", false otherwise.
-bool IsCodecFeatureEnabled(const AudioCodec& codec,
- const char* feature) {
+bool IsCodecFeatureEnabled(const AudioCodec& codec, const char* feature) {
int value;
return codec.GetParam(feature, &value) && value == 1;
}
@@ -340,18 +268,6 @@
voe_codec->rate = GetOpusBitrate(codec, *max_playback_rate);
}
-// Changes RTP timestamp rate of G722. This is due to the "bug" in the RFC
-// which says that G722 should be advertised as 8 kHz although it is a 16 kHz
-// codec.
-void MaybeFixupG722(webrtc::CodecInst* voe_codec, int new_plfreq) {
- if (IsCodec(*voe_codec, kG722CodecName)) {
- // If the ASSERT triggers, the codec definition in WebRTC VoiceEngine
- // has changed, and this special case is no longer needed.
- RTC_DCHECK(voe_codec->plfreq != new_plfreq);
- voe_codec->plfreq = new_plfreq;
- }
-}
-
// Gets the default set of options applied to the engine. Historically, these
// were supplied as a combination of flags from the channel manager (ec, agc,
// ns, and highpass) and the rest hardcoded in InitInternal.
@@ -393,54 +309,17 @@
}
return result;
}
-} // namespace {
-WebRtcVoiceEngine::WebRtcVoiceEngine()
- : voe_wrapper_(new VoEWrapper()),
- audio_state_(webrtc::AudioState::Create(MakeAudioStateConfig(voe()))) {
- Construct();
-}
-
-WebRtcVoiceEngine::WebRtcVoiceEngine(VoEWrapper* voe_wrapper)
- : voe_wrapper_(voe_wrapper) {
- Construct();
-}
-
-void WebRtcVoiceEngine::Construct() {
- RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
- LOG(LS_VERBOSE) << "WebRtcVoiceEngine::WebRtcVoiceEngine";
-
- signal_thread_checker_.DetachFromThread();
- std::memset(&default_agc_config_, 0, sizeof(default_agc_config_));
-
- webrtc::Trace::set_level_filter(kDefaultTraceFilter);
- webrtc::Trace::SetTraceCallback(this);
-
- // Load our audio codec list.
- ConstructCodecs();
-
- // Load our RTP Header extensions.
- rtp_header_extensions_.push_back(
- RtpHeaderExtension(kRtpAudioLevelHeaderExtension,
- kRtpAudioLevelHeaderExtensionDefaultId));
- rtp_header_extensions_.push_back(
- RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension,
- kRtpAbsoluteSenderTimeHeaderExtensionDefaultId));
- if (webrtc::field_trial::FindFullName("WebRTC-SendSideBwe") == "Enabled") {
- rtp_header_extensions_.push_back(RtpHeaderExtension(
- kRtpTransportSequenceNumberHeaderExtension,
- kRtpTransportSequenceNumberHeaderExtensionDefaultId));
- }
- options_ = GetDefaultEngineOptions();
-}
-
-void WebRtcVoiceEngine::ConstructCodecs() {
- RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
- LOG(LS_INFO) << "WebRtc VoiceEngine codecs:";
- int ncodecs = voe_wrapper_->codec()->NumOfCodecs();
- for (int i = 0; i < ncodecs; ++i) {
- webrtc::CodecInst voe_codec;
- if (GetVoeCodec(i, &voe_codec)) {
+class WebRtcVoiceCodecs final {
+ public:
+ // TODO(solenberg): Do this filtering once off-line, add a simple AudioCodec
+ // list and add a test which verifies VoE supports the listed codecs.
+ static std::vector<AudioCodec> SupportedCodecs() {
+ LOG(LS_INFO) << "WebRtc VoiceEngine codecs:";
+ std::vector<AudioCodec> result;
+ for (webrtc::CodecInst voe_codec : webrtc::acm2::RentACodec::Database()) {
+ // Change the sample rate of G722 to 8000 to match SDP.
+ MaybeFixupG722(&voe_codec, 8000);
// Skip uncompressed formats.
if (IsCodec(voe_codec, kL16CodecName)) {
continue;
@@ -483,24 +362,181 @@
// TODO(hellner): Add ptime, sprop-stereo, and stereo
// when they can be set to values other than the default.
}
- codecs_.push_back(codec);
+ result.push_back(codec);
} else {
LOG(LS_WARNING) << "Unexpected codec: " << ToString(voe_codec);
}
}
+ // Make sure they are in local preference order.
+ std::sort(result.begin(), result.end(), &AudioCodec::Preferable);
+ return result;
}
- // Make sure they are in local preference order.
- std::sort(codecs_.begin(), codecs_.end(), &AudioCodec::Preferable);
-}
-bool WebRtcVoiceEngine::GetVoeCodec(int index, webrtc::CodecInst* codec) {
- RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
- if (voe_wrapper_->codec()->GetCodec(index, *codec) == -1) {
+ static bool ToCodecInst(const AudioCodec& in,
+ webrtc::CodecInst* out) {
+ for (webrtc::CodecInst voe_codec : webrtc::acm2::RentACodec::Database()) {
+ // Change the sample rate of G722 to 8000 to match SDP.
+ MaybeFixupG722(&voe_codec, 8000);
+ AudioCodec codec(voe_codec.pltype, voe_codec.plname, voe_codec.plfreq,
+ voe_codec.rate, voe_codec.channels, 0);
+ bool multi_rate = IsCodecMultiRate(voe_codec);
+ // Allow arbitrary rates for ISAC to be specified.
+ if (multi_rate) {
+ // Set codec.bitrate to 0 so the check for codec.Matches() passes.
+ codec.bitrate = 0;
+ }
+ if (codec.Matches(in)) {
+ if (out) {
+ // Fixup the payload type.
+ voe_codec.pltype = in.id;
+
+ // Set bitrate if specified.
+ if (multi_rate && in.bitrate != 0) {
+ voe_codec.rate = in.bitrate;
+ }
+
+ // Reset G722 sample rate to 16000 to match WebRTC.
+ MaybeFixupG722(&voe_codec, 16000);
+
+ // Apply codec-specific settings.
+ if (IsCodec(codec, kIsacCodecName)) {
+ // If ISAC and an explicit bitrate is not specified,
+ // enable auto bitrate adjustment.
+ voe_codec.rate = (in.bitrate > 0) ? in.bitrate : -1;
+ }
+ *out = voe_codec;
+ }
+ return true;
+ }
+ }
return false;
}
- // Change the sample rate of G722 to 8000 to match SDP.
- MaybeFixupG722(codec, 8000);
- return true;
+
+ static bool IsCodecMultiRate(const webrtc::CodecInst& codec) {
+ for (size_t i = 0; i < arraysize(kCodecPrefs); ++i) {
+ if (IsCodec(codec, kCodecPrefs[i].name) &&
+ kCodecPrefs[i].clockrate == codec.plfreq) {
+ return kCodecPrefs[i].is_multi_rate;
+ }
+ }
+ return false;
+ }
+
+ // If the AudioCodec param kCodecParamPTime is set, then we will set it to
+ // codec pacsize if it's valid, or we will pick the next smallest value we
+ // support.
+ // TODO(Brave): Query supported packet sizes from ACM when the API is ready.
+ static bool SetPTimeAsPacketSize(webrtc::CodecInst* codec, int ptime_ms) {
+ for (const CodecPref& codec_pref : kCodecPrefs) {
+ if ((IsCodec(*codec, codec_pref.name) &&
+ codec_pref.clockrate == codec->plfreq) ||
+ IsCodec(*codec, kG722CodecName)) {
+ int packet_size_ms = SelectPacketSize(codec_pref, ptime_ms);
+ if (packet_size_ms) {
+ // Convert unit from milli-seconds to samples.
+ codec->pacsize = (codec->plfreq / 1000) * packet_size_ms;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private:
+ static const int kMaxNumPacketSize = 6;
+ struct CodecPref {
+ const char* name;
+ int clockrate;
+ int channels;
+ int payload_type;
+ bool is_multi_rate;
+ int packet_sizes_ms[kMaxNumPacketSize];
+ };
+ // Note: keep the supported packet sizes in ascending order.
+ static const CodecPref kCodecPrefs[12];
+
+ static int SelectPacketSize(const CodecPref& codec_pref, int ptime_ms) {
+ int selected_packet_size_ms = codec_pref.packet_sizes_ms[0];
+ for (int packet_size_ms : codec_pref.packet_sizes_ms) {
+ if (packet_size_ms && packet_size_ms <= ptime_ms) {
+ selected_packet_size_ms = packet_size_ms;
+ }
+ }
+ return selected_packet_size_ms;
+ }
+
+ // Changes RTP timestamp rate of G722. This is due to the "bug" in the RFC
+ // which says that G722 should be advertised as 8 kHz although it is a 16 kHz
+ // codec.
+ static void MaybeFixupG722(webrtc::CodecInst* voe_codec, int new_plfreq) {
+ if (IsCodec(*voe_codec, kG722CodecName)) {
+ // If the ASSERT triggers, the codec definition in WebRTC VoiceEngine
+ // has changed, and this special case is no longer needed.
+ RTC_DCHECK(voe_codec->plfreq != new_plfreq);
+ voe_codec->plfreq = new_plfreq;
+ }
+ }
+};
+
+const WebRtcVoiceCodecs::CodecPref WebRtcVoiceCodecs::kCodecPrefs[12] = {
+ { kOpusCodecName, 48000, 2, 111, true, { 10, 20, 40, 60 } },
+ { kIsacCodecName, 16000, 1, 103, true, { 30, 60 } },
+ { kIsacCodecName, 32000, 1, 104, true, { 30 } },
+ // G722 should be advertised as 8000 Hz because of the RFC "bug".
+ { kG722CodecName, 8000, 1, 9, false, { 10, 20, 30, 40, 50, 60 } },
+ { kIlbcCodecName, 8000, 1, 102, false, { 20, 30, 40, 60 } },
+ { kPcmuCodecName, 8000, 1, 0, false, { 10, 20, 30, 40, 50, 60 } },
+ { kPcmaCodecName, 8000, 1, 8, false, { 10, 20, 30, 40, 50, 60 } },
+ { kCnCodecName, 32000, 1, 106, false, { } },
+ { kCnCodecName, 16000, 1, 105, false, { } },
+ { kCnCodecName, 8000, 1, 13, false, { } },
+ { kRedCodecName, 8000, 1, 127, false, { } },
+ { kDtmfCodecName, 8000, 1, 126, false, { } },
+};
+} // namespace {
+
+bool WebRtcVoiceEngine::ToCodecInst(const AudioCodec& in,
+ webrtc::CodecInst* out) {
+ return WebRtcVoiceCodecs::ToCodecInst(in, out);
+}
+
+WebRtcVoiceEngine::WebRtcVoiceEngine()
+ : voe_wrapper_(new VoEWrapper()),
+ audio_state_(webrtc::AudioState::Create(MakeAudioStateConfig(voe()))) {
+ Construct();
+}
+
+WebRtcVoiceEngine::WebRtcVoiceEngine(VoEWrapper* voe_wrapper)
+ : voe_wrapper_(voe_wrapper) {
+ Construct();
+}
+
+void WebRtcVoiceEngine::Construct() {
+ RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+ LOG(LS_VERBOSE) << "WebRtcVoiceEngine::WebRtcVoiceEngine";
+
+ signal_thread_checker_.DetachFromThread();
+ std::memset(&default_agc_config_, 0, sizeof(default_agc_config_));
+
+ webrtc::Trace::set_level_filter(kDefaultTraceFilter);
+ webrtc::Trace::SetTraceCallback(this);
+
+ // Load our audio codec list.
+ codecs_ = WebRtcVoiceCodecs::SupportedCodecs();
+
+ // Load our RTP Header extensions.
+ rtp_header_extensions_.push_back(
+ RtpHeaderExtension(kRtpAudioLevelHeaderExtension,
+ kRtpAudioLevelHeaderExtensionDefaultId));
+ rtp_header_extensions_.push_back(
+ RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension,
+ kRtpAbsoluteSenderTimeHeaderExtensionDefaultId));
+ if (webrtc::field_trial::FindFullName("WebRTC-SendSideBwe") == "Enabled") {
+ rtp_header_extensions_.push_back(RtpHeaderExtension(
+ kRtpTransportSequenceNumberHeaderExtension,
+ kRtpTransportSequenceNumberHeaderExtensionDefaultId));
+ }
+ options_ = GetDefaultEngineOptions();
}
WebRtcVoiceEngine::~WebRtcVoiceEngine() {
@@ -539,10 +575,15 @@
webrtc::Trace::set_level_filter(kDefaultTraceFilter);
// Log the VoiceEngine version info
- char buffer[1024] = "";
- voe_wrapper_->base()->GetVersion(buffer);
- LOG(LS_INFO) << "WebRtc VoiceEngine Version:";
- LogMultiline(rtc::LS_INFO, buffer);
+ {
+ char buffer[1024] = "";
+ voe_wrapper_->base()->GetVersion(buffer);
+ LOG(LS_INFO) << "WebRtc VoiceEngine Version:";
+ const char* delim = "\r\n";
+ for (char* tok = strtok(buffer, delim); tok; tok = strtok(NULL, delim)) {
+ LOG(LS_INFO) << tok;
+ }
+ }
// Save the default AGC configuration settings. This must happen before
// calling SetOptions or the default will be overwritten.
@@ -1056,55 +1097,6 @@
return codecs_;
}
-bool WebRtcVoiceEngine::FindCodec(const AudioCodec& in) {
- RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
- return FindWebRtcCodec(in, NULL);
-}
-
-// Get the VoiceEngine codec that matches |in|, with the supplied settings.
-bool WebRtcVoiceEngine::FindWebRtcCodec(const AudioCodec& in,
- webrtc::CodecInst* out) {
- RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
- int ncodecs = voe_wrapper_->codec()->NumOfCodecs();
- for (int i = 0; i < ncodecs; ++i) {
- webrtc::CodecInst voe_codec;
- if (GetVoeCodec(i, &voe_codec)) {
- AudioCodec codec(voe_codec.pltype, voe_codec.plname, voe_codec.plfreq,
- voe_codec.rate, voe_codec.channels, 0);
- bool multi_rate = IsCodecMultiRate(voe_codec);
- // Allow arbitrary rates for ISAC to be specified.
- if (multi_rate) {
- // Set codec.bitrate to 0 so the check for codec.Matches() passes.
- codec.bitrate = 0;
- }
- if (codec.Matches(in)) {
- if (out) {
- // Fixup the payload type.
- voe_codec.pltype = in.id;
-
- // Set bitrate if specified.
- if (multi_rate && in.bitrate != 0) {
- voe_codec.rate = in.bitrate;
- }
-
- // Reset G722 sample rate to 16000 to match WebRTC.
- MaybeFixupG722(&voe_codec, 16000);
-
- // Apply codec-specific settings.
- if (IsCodec(codec, kIsacCodecName)) {
- // If ISAC and an explicit bitrate is not specified,
- // enable auto bitrate adjustment.
- voe_codec.rate = (in.bitrate > 0) ? in.bitrate : -1;
- }
- *out = voe_codec;
- }
- return true;
- }
- }
- }
- return false;
-}
-
const std::vector<RtpHeaderExtension>&
WebRtcVoiceEngine::rtp_header_extensions() const {
RTC_DCHECK(signal_thread_checker_.CalledOnValidThread());
@@ -1592,7 +1584,26 @@
PausePlayout();
}
- bool result = SetRecvCodecsInternal(new_codecs);
+ bool result = true;
+ for (const AudioCodec& codec : new_codecs) {
+ webrtc::CodecInst voe_codec;
+ if (WebRtcVoiceEngine::ToCodecInst(codec, &voe_codec)) {
+ LOG(LS_INFO) << ToString(codec);
+ voe_codec.pltype = codec.id;
+ for (const auto& ch : recv_streams_) {
+ if (engine()->voe()->codec()->SetRecPayloadType(
+ ch.second->channel(), voe_codec) == -1) {
+ LOG_RTCERR2(SetRecPayloadType, ch.second->channel(),
+ ToString(voe_codec));
+ result = false;
+ }
+ }
+ } else {
+ LOG(LS_WARNING) << "Unknown codec " << ToString(codec);
+ result = false;
+ break;
+ }
+ }
if (result) {
recv_codecs_ = codecs;
}
@@ -1627,7 +1638,7 @@
// Ignore codecs we don't know about. The negotiation step should prevent
// this, but double-check to be sure.
webrtc::CodecInst voe_codec;
- if (!engine()->FindWebRtcCodec(codec, &voe_codec)) {
+ if (!WebRtcVoiceEngine::ToCodecInst(codec, &voe_codec)) {
LOG(LS_WARNING) << "Unknown codec " << ToString(codec);
continue;
}
@@ -1668,7 +1679,7 @@
// Set packet size if the AudioCodec param kCodecParamPTime is set.
int ptime_ms = 0;
if (codec.GetParam(kCodecParamPTime, &ptime_ms)) {
- if (!SetPTimeAsPacketSize(&send_codec, ptime_ms)) {
+ if (!WebRtcVoiceCodecs::SetPTimeAsPacketSize(&send_codec, ptime_ms)) {
LOG(LS_WARNING) << "Failed to set packet size for codec "
<< send_codec.plname;
return false;
@@ -1746,7 +1757,7 @@
// Ignore codecs we don't know about. The negotiation step should prevent
// this, but double-check to be sure.
webrtc::CodecInst voe_codec;
- if (!engine()->FindWebRtcCodec(codec, &voe_codec)) {
+ if (!WebRtcVoiceEngine::ToCodecInst(codec, &voe_codec)) {
LOG(LS_WARNING) << "Unknown codec " << ToString(codec);
continue;
}
@@ -2116,24 +2127,20 @@
}
// Turn off all supported codecs.
- const int ncodecs = engine()->voe()->codec()->NumOfCodecs();
- for (int i = 0; i < ncodecs; ++i) {
- webrtc::CodecInst voe_codec;
- if (engine()->voe()->codec()->GetCodec(i, voe_codec) != -1) {
- voe_codec.pltype = -1;
- if (engine()->voe()->codec()->SetRecPayloadType(
- channel, voe_codec) == -1) {
- LOG_RTCERR2(SetRecPayloadType, channel, ToString(voe_codec));
- DeleteVoEChannel(channel);
- return false;
- }
+ // TODO(solenberg): Remove once "no codecs" is the default state of a stream.
+ for (webrtc::CodecInst voe_codec : webrtc::acm2::RentACodec::Database()) {
+ voe_codec.pltype = -1;
+ if (engine()->voe()->codec()->SetRecPayloadType(channel, voe_codec) == -1) {
+ LOG_RTCERR2(SetRecPayloadType, channel, ToString(voe_codec));
+ DeleteVoEChannel(channel);
+ return false;
}
}
// Only enable those configured for this channel.
for (const auto& codec : recv_codecs_) {
webrtc::CodecInst voe_codec;
- if (engine()->FindWebRtcCodec(codec, &voe_codec)) {
+ if (WebRtcVoiceEngine::ToCodecInst(codec, &voe_codec)) {
voe_codec.pltype = codec.id;
if (engine()->voe()->codec()->SetRecPayloadType(
channel, voe_codec) == -1) {
@@ -2486,7 +2493,7 @@
return true;
webrtc::CodecInst codec = *send_codec_;
- bool is_multi_rate = IsCodecMultiRate(codec);
+ bool is_multi_rate = WebRtcVoiceCodecs::IsCodecMultiRate(codec);
if (is_multi_rate) {
// If codec is multi-rate then just set the bitrate.
@@ -2635,7 +2642,7 @@
if (codec.id == red_pt) {
// If we find the right codec, that will be the codec we pass to
// SetSendCodec, with the desired payload type.
- if (engine()->FindWebRtcCodec(codec, send_codec)) {
+ if (WebRtcVoiceEngine::ToCodecInst(codec, send_codec)) {
return true;
} else {
break;
@@ -2659,30 +2666,6 @@
}
return true;
}
-
-bool WebRtcVoiceMediaChannel::SetRecvCodecsInternal(
- const std::vector<AudioCodec>& new_codecs) {
- RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
- for (const AudioCodec& codec : new_codecs) {
- webrtc::CodecInst voe_codec;
- if (engine()->FindWebRtcCodec(codec, &voe_codec)) {
- LOG(LS_INFO) << ToString(codec);
- voe_codec.pltype = codec.id;
- for (const auto& ch : recv_streams_) {
- if (engine()->voe()->codec()->SetRecPayloadType(
- ch.second->channel(), voe_codec) == -1) {
- LOG_RTCERR2(SetRecPayloadType, ch.second->channel(),
- ToString(voe_codec));
- return false;
- }
- }
- } else {
- LOG(LS_WARNING) << "Unknown codec " << ToString(codec);
- return false;
- }
- }
- return true;
-}
} // namespace cricket
#endif // HAVE_WEBRTC_VOICE
diff --git a/talk/media/webrtc/webrtcvoiceengine.h b/talk/media/webrtc/webrtcvoiceengine.h
index 9c7f7bb..57c9439 100644
--- a/talk/media/webrtc/webrtcvoiceengine.h
+++ b/talk/media/webrtc/webrtcvoiceengine.h
@@ -57,8 +57,10 @@
// It uses the WebRtc VoiceEngine library for audio handling.
class WebRtcVoiceEngine final : public webrtc::TraceCallback {
friend class WebRtcVoiceMediaChannel;
-
public:
+ // Exposed for the WVoE/MC unit test.
+ static bool ToCodecInst(const AudioCodec& in, webrtc::CodecInst* out);
+
WebRtcVoiceEngine();
// Dependency injection for testing.
explicit WebRtcVoiceEngine(VoEWrapper* voe_wrapper);
@@ -78,9 +80,6 @@
int GetInputLevel();
const std::vector<AudioCodec>& codecs();
- bool FindCodec(const AudioCodec& codec);
- bool FindWebRtcCodec(const AudioCodec& codec, webrtc::CodecInst* gcodec);
-
const std::vector<RtpHeaderExtension>& rtp_header_extensions() const;
// For tracking WebRtc channels. Needed because we have to pause them
@@ -114,8 +113,6 @@
private:
void Construct();
- void ConstructCodecs();
- bool GetVoeCodec(int index, webrtc::CodecInst* codec);
bool InitInternal();
// Every option that is "set" will be applied. Every option not "set" will be
// ignored. This allows us to selectively turn on and off different options
@@ -258,7 +255,6 @@
}
bool SetSendCodecs(int channel, const std::vector<AudioCodec>& codecs);
bool SetSendBitrateInternal(int bps);
- bool SetRecvCodecsInternal(const std::vector<AudioCodec>& new_codecs);
rtc::ThreadChecker worker_thread_checker_;
diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
index b52f6fc..888de7d 100644
--- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
@@ -54,10 +54,6 @@
const cricket::AudioCodec kCn16000Codec(105, "CN", 16000, 0, 1, 0);
const cricket::AudioCodec kTelephoneEventCodec(106, "telephone-event", 8000, 0,
1, 0);
-const cricket::AudioCodec* const kAudioCodecs[] = {
- &kPcmuCodec, &kIsacCodec, &kOpusCodec, &kG722CodecVoE, &kRedCodec,
- &kCn8000Codec, &kCn16000Codec, &kTelephoneEventCodec,
-};
const uint32_t kSsrc1 = 0x99;
const uint32_t kSsrc2 = 0x98;
const uint32_t kSsrcs4[] = { 1, 2, 3, 4 };
@@ -81,7 +77,6 @@
public:
WebRtcVoiceEngineTestFake()
: call_(webrtc::Call::Config()),
- voe_(kAudioCodecs, arraysize(kAudioCodecs)),
engine_(new FakeVoEWrapper(&voe_)),
channel_(nullptr) {
send_parameters_.codecs.push_back(kPcmuCodec);
@@ -451,32 +446,33 @@
cricket::AudioCodec codec;
webrtc::CodecInst codec_inst;
// Find PCMU with explicit clockrate and bitrate.
- EXPECT_TRUE(engine_.FindWebRtcCodec(kPcmuCodec, &codec_inst));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(kPcmuCodec, &codec_inst));
// Find ISAC with explicit clockrate and 0 bitrate.
- EXPECT_TRUE(engine_.FindWebRtcCodec(kIsacCodec, &codec_inst));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(kIsacCodec, &codec_inst));
// Find telephone-event with explicit clockrate and 0 bitrate.
- EXPECT_TRUE(engine_.FindWebRtcCodec(kTelephoneEventCodec, &codec_inst));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(kTelephoneEventCodec,
+ &codec_inst));
// Find ISAC with a different payload id.
codec = kIsacCodec;
codec.id = 127;
- EXPECT_TRUE(engine_.FindWebRtcCodec(codec, &codec_inst));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(codec, &codec_inst));
EXPECT_EQ(codec.id, codec_inst.pltype);
// Find PCMU with a 0 clockrate.
codec = kPcmuCodec;
codec.clockrate = 0;
- EXPECT_TRUE(engine_.FindWebRtcCodec(codec, &codec_inst));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(codec, &codec_inst));
EXPECT_EQ(codec.id, codec_inst.pltype);
EXPECT_EQ(8000, codec_inst.plfreq);
// Find PCMU with a 0 bitrate.
codec = kPcmuCodec;
codec.bitrate = 0;
- EXPECT_TRUE(engine_.FindWebRtcCodec(codec, &codec_inst));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(codec, &codec_inst));
EXPECT_EQ(codec.id, codec_inst.pltype);
EXPECT_EQ(64000, codec_inst.rate);
// Find ISAC with an explicit bitrate.
codec = kIsacCodec;
codec.bitrate = 32000;
- EXPECT_TRUE(engine_.FindWebRtcCodec(codec, &codec_inst));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(codec, &codec_inst));
EXPECT_EQ(codec.id, codec_inst.pltype);
EXPECT_EQ(32000, codec_inst.rate);
}
@@ -539,7 +535,7 @@
cricket::StreamParams::CreateLegacy(kSsrc1)));
int channel_num = voe_.GetLastChannel();
webrtc::CodecInst opus;
- engine_.FindWebRtcCodec(kOpusCodec, &opus);
+ cricket::WebRtcVoiceEngine::ToCodecInst(kOpusCodec, &opus);
// Even without stereo parameters, recv codecs still specify channels = 2.
EXPECT_EQ(2, opus.channels);
EXPECT_EQ(111, opus.pltype);
@@ -562,7 +558,7 @@
cricket::StreamParams::CreateLegacy(kSsrc1)));
int channel_num2 = voe_.GetLastChannel();
webrtc::CodecInst opus;
- engine_.FindWebRtcCodec(kOpusCodec, &opus);
+ cricket::WebRtcVoiceEngine::ToCodecInst(kOpusCodec, &opus);
// Even when stereo is off, recv codecs still specify channels = 2.
EXPECT_EQ(2, opus.channels);
EXPECT_EQ(111, opus.pltype);
@@ -585,7 +581,7 @@
cricket::StreamParams::CreateLegacy(kSsrc1)));
int channel_num2 = voe_.GetLastChannel();
webrtc::CodecInst opus;
- engine_.FindWebRtcCodec(kOpusCodec, &opus);
+ cricket::WebRtcVoiceEngine::ToCodecInst(kOpusCodec, &opus);
EXPECT_EQ(2, opus.channels);
EXPECT_EQ(111, opus.pltype);
EXPECT_STREQ("opus", opus.plname);
@@ -670,7 +666,7 @@
int channel_num = voe_.GetLastChannel();
EXPECT_TRUE(voe_.GetPlayout(channel_num));
webrtc::CodecInst gcodec;
- EXPECT_TRUE(engine_.FindWebRtcCodec(kOpusCodec, &gcodec));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(kOpusCodec, &gcodec));
EXPECT_EQ(kOpusCodec.id, gcodec.pltype);
}
@@ -3246,54 +3242,60 @@
// Tests that the library is configured with the codecs we want.
TEST(WebRtcVoiceEngineTest, HasCorrectCodecs) {
- cricket::WebRtcVoiceEngine engine;
// Check codecs by name.
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "OPUS", 48000, 0, 2, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "ISAC", 16000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "ISAC", 32000, 0, 1, 0)));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "OPUS", 48000, 0, 2, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "ISAC", 16000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "ISAC", 32000, 0, 1, 0), nullptr));
// Check that name matching is case-insensitive.
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "ILBC", 8000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "iLBC", 8000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "PCMU", 8000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "PCMA", 8000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "G722", 8000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "red", 8000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "CN", 32000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "CN", 16000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "CN", 8000, 0, 1, 0)));
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(96, "telephone-event", 8000, 0, 1, 0)));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "ILBC", 8000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "iLBC", 8000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "PCMU", 8000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "PCMA", 8000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "G722", 8000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "red", 8000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "CN", 32000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "CN", 16000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "CN", 8000, 0, 1, 0), nullptr));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(96, "telephone-event", 8000, 0, 1, 0), nullptr));
// Check codecs with an id by id.
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(0, "", 8000, 0, 1, 0))); // PCMU
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(8, "", 8000, 0, 1, 0))); // PCMA
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(9, "", 8000, 0, 1, 0))); // G722
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(13, "", 8000, 0, 1, 0))); // CN
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(0, "", 8000, 0, 1, 0), nullptr)); // PCMU
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(8, "", 8000, 0, 1, 0), nullptr)); // PCMA
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(9, "", 8000, 0, 1, 0), nullptr)); // G722
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(13, "", 8000, 0, 1, 0), nullptr)); // CN
// Check sample/bitrate matching.
- EXPECT_TRUE(engine.FindCodec(
- cricket::AudioCodec(0, "PCMU", 8000, 64000, 1, 0)));
+ EXPECT_TRUE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(0, "PCMU", 8000, 64000, 1, 0), nullptr));
// Check that bad codecs fail.
- EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(99, "ABCD", 0, 0, 1, 0)));
- EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(88, "", 0, 0, 1, 0)));
- EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 0, 0, 2, 0)));
- EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 5000, 0, 1, 0)));
- EXPECT_FALSE(engine.FindCodec(cricket::AudioCodec(0, "", 0, 5000, 1, 0)));
+ EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(99, "ABCD", 0, 0, 1, 0), nullptr));
+ EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(88, "", 0, 0, 1, 0), nullptr));
+ EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(0, "", 0, 0, 2, 0), nullptr));
+ EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(0, "", 5000, 0, 1, 0), nullptr));
+ EXPECT_FALSE(cricket::WebRtcVoiceEngine::ToCodecInst(
+ cricket::AudioCodec(0, "", 0, 5000, 1, 0), nullptr));
+
// Verify the payload id of common audio codecs, including CN, ISAC, and G722.
+ cricket::WebRtcVoiceEngine engine;
for (std::vector<cricket::AudioCodec>::const_iterator it =
engine.codecs().begin(); it != engine.codecs().end(); ++it) {
if (it->name == "CN" && it->clockrate == 16000) {
@@ -3320,7 +3322,6 @@
EXPECT_EQ("1", it->params.find("useinbandfec")->second);
}
}
-
engine.Terminate();
}