- Added to Settings possibility to choose which audio modules are loaded.

Codecs provided by loaded modules are available for accounts to use.
This commit is contained in:
Juha Heinanen
2019-10-22 17:33:46 +03:00
parent cf3e02c46d
commit 53f751454d
9 changed files with 178 additions and 69 deletions
+1 -1
View File
@@ -23,8 +23,8 @@ net_prefer_ipv6 no
module opus.so module opus.so
module amr.so module amr.so
module ilbc.so module ilbc.so
module g7221.so
module g722.so module g722.so
module g7221.so
module g726.so module g726.so
module g711.so module g711.so
module webrtc_aec.so module webrtc_aec.so
@@ -1,6 +1,8 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.content.Context import android.content.Context
import java.util.*
import kotlin.collections.ArrayList
class Account(val accp: String) { class Account(val accp: String) {
@@ -151,6 +153,27 @@ class Account(val accp: String) {
return aor.split("@")[1] return aor.split("@")[1]
} }
private fun removeAudioCodecsStartingWith(prefix: String) {
val newCodecs = ArrayList<String>()
for (acSpec in audioCodec)
if (!acSpec.toLowerCase(Locale.ROOT).startsWith(prefix)) newCodecs.add(acSpec)
audioCodec = newCodecs
}
fun removeAudioCodecs(codecModule: String) {
when (codecModule) {
"g711" -> {
removeAudioCodecsStartingWith("pcm")
}
"g722" -> {
removeAudioCodecsStartingWith("g722/")
}
else -> {
removeAudioCodecsStartingWith(codecModule)
}
}
}
companion object { companion object {
fun accounts(): ArrayList<Account> { fun accounts(): ArrayList<Account> {
@@ -186,7 +186,7 @@ class BaresipService: Service() {
showStatusNotification() showStatusNotification()
if (Config.variable("dyn_dns")[0] == "yes") if (Config.variable("dyn_dns")[0] == "yes")
Config.remove("dns_server") Config.removeVariable("dns_server")
if (AccountsActivity.noAccounts()) { if (AccountsActivity.noAccounts()) {
val newIntent = Intent(this, MainActivity::class.java) val newIntent = Intent(this, MainActivity::class.java)
@@ -712,9 +712,9 @@ class BaresipService: Service() {
Log.d(LOG_TAG, "Received 'stopped' from baresip with param '$error'") Log.d(LOG_TAG, "Received 'stopped' from baresip with param '$error'")
isServiceRunning = false isServiceRunning = false
if (error == "ua_init") { if (error == "ua_init") {
Config.remove("sip_listen") Config.removeVariable("sip_listen")
Config.remove("sip_certificate") Config.removeVariable("sip_certificate")
Config.remove("sip_cafile") Config.removeVariable("sip_cafile")
} }
val intent = Intent("service event") val intent = Intent("service event")
intent.putExtra("event", "stopped") intent.putExtra("event", "stopped")
@@ -21,17 +21,6 @@ object Config {
config = "${config}ausrc_format s16\nauplay_format s16\nauenc_format s16\naudec_format s16\nmodule webrtc_aec.so\n" config = "${config}ausrc_format s16\nauplay_format s16\nauenc_format s16\naudec_format s16\nmodule webrtc_aec.so\n"
write = true write = true
} }
if (!config.contains(Regex("module[ ]+amr.so"))) {
config = config.replace(Regex("module[ ]+g7...?.so\n"), "")
config = config.replace(Regex("module[ ]+ilbc.so\n"), "")
config = "${config}module amr.so\n"
config = "${config}module ilbc.so\n"
config = "${config}module g7221.so\n"
config = "${config}module g722.so\n"
config = "${config}module g726.so\n"
config = "${config}module g711.so\n"
write = true
}
if (config.contains(Regex("#module_app[ ]+mwi.so"))) { if (config.contains(Regex("#module_app[ ]+mwi.so"))) {
config = config.replace(Regex("#module_app[ ]+mwi.so"), config = config.replace(Regex("#module_app[ ]+mwi.so"),
"module_app mwi.so") "module_app mwi.so")
@@ -69,7 +58,7 @@ object Config {
} }
val preferIpv6 = variable("prefer_ipv6") val preferIpv6 = variable("prefer_ipv6")
if (preferIpv6.size > 0) { if (preferIpv6.size > 0) {
remove("prefer_ipv6") removeVariable("prefer_ipv6")
config = "net_prefer_ipv6 ${preferIpv6[0]}\n${config}" config = "net_prefer_ipv6 ${preferIpv6[0]}\n${config}"
write = true write = true
} else { } else {
@@ -112,18 +101,21 @@ object Config {
return result return result
} }
fun add(variable: String, value: String) { fun addLine(line: String) {
config += "$variable $value\n" config += "$line\n"
} }
fun remove(variable: String) { fun removeLine(line: String) {
config = Utils.removeLinesStartingWithName(config, variable) config = Utils.removeLinesStartingWithString(config, line)
Utils.putFileContents(configPath, config.toByteArray())
} }
fun replace(variable: String, value: String) { fun removeVariable(variable: String) {
remove(variable) config = Utils.removeLinesStartingWithString(config, "$variable ")
add(variable, value) }
fun replaceVariable(variable: String, value: String) {
removeVariable(variable)
addLine("$variable $value")
} }
fun reset(ctx: Context) { fun reset(ctx: Context) {
@@ -3,14 +3,19 @@ package com.tutpro.baresip
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.util.TypedValue
import android.view.Gravity
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.* import android.widget.*
import android.widget.RelativeLayout.LayoutParams
import android.widget.AdapterView
class ConfigActivity : AppCompatActivity() { class ConfigActivity : AppCompatActivity() {
@@ -35,6 +40,7 @@ class ConfigActivity : AppCompatActivity() {
private var oldDnsServers = "" private var oldDnsServers = ""
private var oldCertificateFile = false private var oldCertificateFile = false
private var oldCAFile = false private var oldCAFile = false
private var oldAudioModules = mutableMapOf<String, Boolean>()
private var oldAec = false private var oldAec = false
private var oldOpusBitrate = "" private var oldOpusBitrate = ""
private var oldOpusPacketLoss = "" private var oldOpusPacketLoss = ""
@@ -43,6 +49,7 @@ class ConfigActivity : AppCompatActivity() {
private var callVolume = BaresipService.callVolume private var callVolume = BaresipService.callVolume
private var save = false private var save = false
private var restart = false private var restart = false
private val audioModules = listOf("opus", "amr", "ilbc", "g722", "g7221", "g726", "g711")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -91,6 +98,37 @@ class ConfigActivity : AppCompatActivity() {
oldCAFile = Config.variable("sip_cafile").isNotEmpty() oldCAFile = Config.variable("sip_cafile").isNotEmpty()
caFile.isChecked = oldCAFile caFile.isChecked = oldCAFile
val audioModulesList = findViewById(R.id.AudioModulesList) as LinearLayout
var id = 1000
for (module in audioModules) {
val rl = RelativeLayout(this)
val rlParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
rlParams.marginStart = 16
rl.layoutParams = rlParams
val tv = TextView(this)
tv.id = id++
val tvParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
tvParams.addRule(RelativeLayout.ALIGN_PARENT_START)
tvParams.addRule(RelativeLayout.CENTER_VERTICAL)
tvParams.addRule(RelativeLayout.START_OF, id)
tv.layoutParams = tvParams
tv.text = module
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f)
tv.setTextColor(Color.BLACK)
rl.addView(tv)
val cb = CheckBox(this)
cb.id = id++
val cbParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
cbParams.addRule(RelativeLayout.ALIGN_PARENT_END)
cbParams.addRule(RelativeLayout.CENTER_VERTICAL)
cb.layoutParams = cbParams
cb.gravity = Gravity.END
cb.isChecked = Config.variable("module $module.so").size > 0
oldAudioModules.put(module, cb.isChecked)
rl.addView(cb)
audioModulesList.addView(rl)
}
aec = findViewById(R.id.Aec) as CheckBox aec = findViewById(R.id.Aec) as CheckBox
val aecCv = Config.variable("module") val aecCv = Config.variable("module")
oldAec = aecCv.contains("webrtc_aec.so") oldAec = aecCv.contains("webrtc_aec.so")
@@ -166,7 +204,7 @@ class ConfigActivity : AppCompatActivity() {
var autoStartString = "no" var autoStartString = "no"
if (autoStart.isChecked) autoStartString = "yes" if (autoStart.isChecked) autoStartString = "yes"
if (oldAutoStart != autoStartString) { if (oldAutoStart != autoStartString) {
Config.replace("auto_start", autoStartString) Config.replaceVariable("auto_start", autoStartString)
save = true save = true
restart = false restart = false
} }
@@ -178,8 +216,8 @@ class ConfigActivity : AppCompatActivity() {
"${getString(R.string.invalid_listen_address)}: $listenAddr") "${getString(R.string.invalid_listen_address)}: $listenAddr")
return false return false
} }
Config.remove("sip_listen") Config.removeVariable("sip_listen")
if (listenAddr != "") Config.add("sip_listen", listenAddr) if (listenAddr != "") Config.addLine("sip_listen $listenAddr")
save = true save = true
restart = true restart = true
} }
@@ -191,8 +229,8 @@ class ConfigActivity : AppCompatActivity() {
"${getString(R.string.invalid_bind_address)}: $bindAddr") "${getString(R.string.invalid_bind_address)}: $bindAddr")
return false return false
} }
Config.remove("net_interface") Config.removeVariable("net_interface")
if (bindAddr != "") Config.add("net_interface", bindAddr) if (bindAddr != "") Config.addLine("net_interface $bindAddr")
save = true save = true
restart = true restart = true
} }
@@ -200,7 +238,7 @@ class ConfigActivity : AppCompatActivity() {
var preferIPv6String = "no" var preferIPv6String = "no"
if (preferIPv6.isChecked) preferIPv6String = "yes" if (preferIPv6.isChecked) preferIPv6String = "yes"
if (oldPreferIPv6 != preferIPv6String) { if (oldPreferIPv6 != preferIPv6String) {
Config.replace("net_prefer_ipv6", preferIPv6String) Config.replaceVariable("net_prefer_ipv6", preferIPv6String)
save = true save = true
restart = true restart = true
} }
@@ -212,19 +250,19 @@ class ConfigActivity : AppCompatActivity() {
"${getString(R.string.invalid_dns_servers)}: $dnsServers") "${getString(R.string.invalid_dns_servers)}: $dnsServers")
return false return false
} }
Config.remove("dyn_dns") Config.removeVariable("dyn_dns")
Config.remove("dns_server") Config.removeVariable("dns_server")
if (dnsServers.isNotEmpty()) { if (dnsServers.isNotEmpty()) {
for (server in dnsServers.split(",")) for (server in dnsServers.split(","))
Config.add("dns_server", server) Config.addLine("dns_server $server")
Config.add("dyn_dns", "no") Config.addLine("dyn_dns no")
if (Api.net_use_nameserver(dnsServers) != 0) { if (Api.net_use_nameserver(dnsServers) != 0) {
Utils.alertView(this, getString(R.string.error), Utils.alertView(this, getString(R.string.error),
"${getString(R.string.failed_to_set_dns_servers)}: $dnsServers") "${getString(R.string.failed_to_set_dns_servers)}: $dnsServers")
return false return false
} }
} else { } else {
Config.add("dyn_dns", "yes") Config.addLine("dyn_dns yes")
if (Build.VERSION.SDK_INT >= 23) { if (Build.VERSION.SDK_INT >= 23) {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
Config.updateDnsServers(cm.getLinkProperties(cm.activeNetwork).getDnsServers()) Config.updateDnsServers(cm.getLinkProperties(cm.activeNetwork).getDnsServers())
@@ -249,10 +287,10 @@ class ConfigActivity : AppCompatActivity() {
return false return false
} }
Utils.putFileContents(BaresipService.filesPath + "/cert.pem", content) Utils.putFileContents(BaresipService.filesPath + "/cert.pem", content)
Config.remove("sip_certificate") Config.removeVariable("sip_certificate")
Config.add("sip_certificate", BaresipService.filesPath + "/cert.pem") Config.addLine("sip_certificate ${BaresipService.filesPath}/cert.pem")
} else { } else {
Config.remove("sip_certificate") Config.removeVariable("sip_certificate")
} }
save = true save = true
restart = true restart = true
@@ -272,22 +310,44 @@ class ConfigActivity : AppCompatActivity() {
} }
Utils.putFileContents(BaresipService.filesPath + "/ca_certs.crt", Utils.putFileContents(BaresipService.filesPath + "/ca_certs.crt",
content) content)
Config.remove("sip_cafile") Config.removeVariable("sip_cafile")
Config.add("sip_cafile", BaresipService.filesPath + "/ca_certs.crt") Config.addLine("sip_cafile ${BaresipService.filesPath}/ca_certs.crt")
} else { } else {
Config.remove("sip_cafile") Config.removeVariable("sip_cafile")
} }
save = true save = true
restart = true restart = true
} }
var id = 1001
for (module in audioModules) {
val box = findViewById<CheckBox>(id++)
if (box.isChecked && !oldAudioModules[module]!!) {
if (Api.module_load("$module.so") != 0) {
Utils.alertView(this, getString(R.string.error),
"${getString(R.string.failed_to_load_module)}: $module.so")
return false
}
Config.addLine("module $module.so")
save = true
}
if (!box.isChecked && oldAudioModules[module]!!) {
Api.module_unload("$module.so")
Config.removeLine("module $module.so")
for (ua in UserAgent.uas()) ua.account.removeAudioCodecs(module)
AccountsActivity.saveAccounts()
save = true
}
id++
}
if (aec.isChecked != oldAec) { if (aec.isChecked != oldAec) {
Config.remove("module webrtc_aec.so") Config.removeLine("module webrtc_aec.so")
if (aec.isChecked) { if (aec.isChecked) {
Config.add("module", "webrtc_aec.so") Config.addLine("module webrtc_aec.so")
if (Api.module_load("webrtc_aec.so") != 0) { if (Api.module_load("webrtc_aec.so") != 0) {
Utils.alertView(this, getString(R.string.error), Utils.alertView(this, getString(R.string.error),
getString(R.string.failed_to_load_aec_module)) getString(R.string.failed_to_load_module))
aec.isChecked = false aec.isChecked = false
return false return false
} }
@@ -304,8 +364,8 @@ class ConfigActivity : AppCompatActivity() {
"${getString(R.string.invalid_opus_bitrate)}: $opusBitRate.") "${getString(R.string.invalid_opus_bitrate)}: $opusBitRate.")
return false return false
} }
Config.remove("opus_bitrate") Config.removeVariable("opus_bitrate")
Config.add("opus_bitrate", opusBitRate) Config.addLine("opus_bitrate $opusBitRate")
save = true save = true
restart = true restart = true
} }
@@ -317,11 +377,11 @@ class ConfigActivity : AppCompatActivity() {
"${getString(R.string.invalid_opus_packet_loss)}: $opusPacketLoss") "${getString(R.string.invalid_opus_packet_loss)}: $opusPacketLoss")
return false return false
} }
Config.remove("opus_inbandfec") Config.removeVariable("opus_inbandfec")
Config.remove("opus_packet_loss") Config.removeVariable("opus_packet_loss")
if (opusPacketLoss != "0") { if (opusPacketLoss != "0") {
Config.add("opus_inbandfec", "yes") Config.addLine("opus_inbandfec yes")
Config.add("opus_packet_loss", opusPacketLoss) Config.addLine("opus_packet_loss $opusPacketLoss")
} }
save = true save = true
restart = true restart = true
@@ -330,21 +390,21 @@ class ConfigActivity : AppCompatActivity() {
var iceModeString = "full" var iceModeString = "full"
if (iceLite.isChecked) iceModeString = "lite" if (iceLite.isChecked) iceModeString = "lite"
if (oldIceMode != iceModeString) { if (oldIceMode != iceModeString) {
Config.replace("ice_mode", iceModeString) Config.replaceVariable("ice_mode", iceModeString)
save = true save = true
restart = true restart = true
} }
if (BaresipService.callVolume != callVolume) { if (BaresipService.callVolume != callVolume) {
BaresipService.callVolume = callVolume BaresipService.callVolume = callVolume
Config.replace("call_volume", callVolume.toString()) Config.replaceVariable("call_volume", callVolume.toString())
save = true save = true
} }
var logLevelString = "2" var logLevelString = "2"
if (debug.isChecked) logLevelString = "0" if (debug.isChecked) logLevelString = "0"
if (oldLogLevel != logLevelString) { if (oldLogLevel != logLevelString) {
Config.replace("log_level", logLevelString) Config.replaceVariable("log_level", logLevelString)
Api.log_level_set(logLevelString.toInt()) Api.log_level_set(logLevelString.toInt())
Log.logLevelSet(logLevelString.toInt()) Log.logLevelSet(logLevelString.toInt())
save = true save = true
@@ -415,6 +475,10 @@ class ConfigActivity : AppCompatActivity() {
Utils.alertView(this, getString(R.string.tls_ca_file), Utils.alertView(this, getString(R.string.tls_ca_file),
getString(R.string.tls_ca_file_help)) getString(R.string.tls_ca_file_help))
} }
findViewById(R.id.AudioModulesTitle) as TextView -> {
Utils.alertView(this, getString(R.string.audio_modules_title),
getString(R.string.audio_modules_help))
}
findViewById(R.id.AecTitle) as TextView-> { findViewById(R.id.AecTitle) as TextView-> {
Utils.alertView(this, getString(R.string.aec), Utils.alertView(this, getString(R.string.aec),
getString(R.string.aec_help)) getString(R.string.aec_help))
@@ -38,10 +38,10 @@ object Utils {
return result return result
} }
fun removeLinesStartingWithName(string: String, name: String): String { fun removeLinesStartingWithString(lines: String, string: String): String {
var result = "" var result = ""
for (line in string.split("\n")) for (line in lines.split("\n"))
if (!line.startsWith(name) && (line.length > 0)) result += line + "\n" if (!line.startsWith(string) && (line.length > 0)) result += line + "\n"
return result return result
} }
+23 -1
View File
@@ -192,7 +192,29 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
android:orientation="horizontal" > android:orientation="vertical" >
<TextView
android:id="@+id/AudioModulesTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="@string/audio_modules_title" >
</TextView>
<LinearLayout
android:id="@+id/AudioModulesList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="24dp"
android:orientation="vertical" >
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp" >
<TextView <TextView
android:id="@+id/AecTitle" android:id="@+id/AecTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
+15 -10
View File
@@ -186,9 +186,23 @@
DNS-palvelijoihin.</string> DNS-palvelijoihin.</string>
<string name="invalid_dns_servers">Virheelliset DNS-palvelimet</string> <string name="invalid_dns_servers">Virheelliset DNS-palvelimet</string>
<string name="failed_to_set_dns_servers">DNS-palvelinten asetus epäonnistui</string> <string name="failed_to_set_dns_servers">DNS-palvelinten asetus epäonnistui</string>
<string name="tls_certificate_file">TLS-varmennintiedosto</string>
<string name="tls_certificate_file_help">Jos merkitty, tiedosto \'cert.pem\', joka sisältää
tämän baresip-sovelluksen julkisen ja yksityisen TLS-varmentimen, on joko jo ladattu tai tullaan
lataamaan Download-hakemistosta. Turvallisuusyistä tuhoa tiedosto heti lataamisen jälkeen.
</string>
<string name="tls_ca_file">TLS CA-tiedosto</string>
<string name="tls_ca_file_help">Jos merkitty, tiedosto \'ca_certs.crt\', joka sisältää
TLS-varmenninauktoriteettien julkiset varmentimen, on joko jo ladattu tai tullaan lataamaan
Download-hakemistosta.
</string>
<string name="audio_modules_title">Audio-modulit</string>
<string name="audio_modules_help">Merkittyjen modulien tarjoamat audio-koodekit ovat tilien
käytettävissä.
</string>
<string name="failed_to_load_module">Modulin lataaminen epäonnistui</string>
<string name="aec">Akustinen kaiun poisto</string> <string name="aec">Akustinen kaiun poisto</string>
<string name="aec_help">Jos merkitty, kaikua yritetään poistaa puheluiden aikana.</string> <string name="aec_help">Jos merkitty, kaikua yritetään poistaa puheluiden aikana.</string>
<string name="failed_to_load_aec_module">Kaiunpoistomodulin lataaminen epäonnistui.</string>
<string name="opus_bit_rate">Opus-koodekin bittinopeus</string> <string name="opus_bit_rate">Opus-koodekin bittinopeus</string>
<string name="opus_bit_rate_help">Opus-koodekin käyttämä <string name="opus_bit_rate_help">Opus-koodekin käyttämä
keskimääräinen enimmäisnopeus. Mahdollisia arvoja ovat keskimääräinen enimmäisnopeus. Mahdollisia arvoja ovat
@@ -306,15 +320,6 @@
<string name="call_is_secure">Tämä pulelu on turvallinen ja kohde on <string name="call_is_secure">Tämä pulelu on turvallinen ja kohde on
todennettu! Haluatko poistaa todennuksen?</string> todennettu! Haluatko poistaa todennuksen?</string>
<string name="unverify">Poista todennus</string> <string name="unverify">Poista todennus</string>
<string name="tls_ca_file">TLS CA-tiedosto</string>
<string name="tls_ca_file_help">Jos merkitty, tiedosto \'ca_certs.crt\', joka sisältää
todennusauktoriteettien TLS-sertifikaatit, on ladattu tai ladataan Download-kansiosta.
</string>
<string name="tls_certificate_file">TLS sertifikaattitiedosto</string>
<string name="tls_certificate_file_help">"Jos merkitty, tiedosto \'cert.pem\', joka sisältää
tämän baresip-sovelluksen TLS-sertifikaatin ja yksityisen avaimen, on ladattu tai ladataan
Download-kansiosta."
</string>
<string name="backed_up">Sovelluksen tiedot talletettiin Download-kansion tiedostoon <string name="backed_up">Sovelluksen tiedot talletettiin Download-kansion tiedostoon
\'baresip.bs\'. \'baresip.bs\'.
</string> </string>
+4 -1
View File
@@ -181,9 +181,12 @@
<string name="tls_ca_file">TLS CA File</string> <string name="tls_ca_file">TLS CA File</string>
<string name="tls_ca_file_help">If checked, file \'ca_certs.crt\' containing TLS certificates <string name="tls_ca_file_help">If checked, file \'ca_certs.crt\' containing TLS certificates
of Certificate Authorities has been or will be loaded from Download directory.</string> of Certificate Authorities has been or will be loaded from Download directory.</string>
<string name="audio_modules_title">Audio Modules</string>
<string name="audio_modules_help">Audio codecs provided by the checked modules are
available for use by the accounts.</string>
<string name="failed_to_load_module">Failed to load module.</string>
<string name="aec">Acoustic Echo Cancellation</string> <string name="aec">Acoustic Echo Cancellation</string>
<string name="aec_help">If checked, echo cancellation is attempted on call audio.</string> <string name="aec_help">If checked, echo cancellation is attempted on call audio.</string>
<string name="failed_to_load_aec_module">Failed to load Acoustic Echo Cancellation module.</string>
<string name="opus_bit_rate">Opus Bit Rate</string> <string name="opus_bit_rate">Opus Bit Rate</string>
<string name="opus_bit_rate_help">Average maximum bit rate used by Opus audio stream. <string name="opus_bit_rate_help">Average maximum bit rate used by Opus audio stream.
Valid values are 6000-510000. Factory default is 28000.</string> Valid values are 6000-510000. Factory default is 28000.</string>