怎么到國外網(wǎng)站去接模具訂單做東莞seo網(wǎng)站推廣建設(shè)
常用的音頻通話模式包括 VOIP 通話和蜂窩通話。
●?VOIP 通話:VOIP(Voice?over?Internet?Protocol)通話是指基于互聯(lián)網(wǎng)協(xié)議(IP)進行通訊的一種語音通話技術(shù)。VOIP 通話會將通話信息打包成數(shù)據(jù)包,通過網(wǎng)絡(luò)進行傳輸,因此 VOIP 通話對網(wǎng)絡(luò)要求較高,通話質(zhì)量與網(wǎng)絡(luò)連接速度緊密相關(guān)。
●?蜂窩通話(僅對系統(tǒng)應(yīng)用開放)蜂窩通話是指傳統(tǒng)的電話功能,由運營商提供服務(wù),目前僅對系統(tǒng)應(yīng)用開放,未向三方應(yīng)用提供開發(fā)接口。
在開發(fā)音頻通話相關(guān)功能時,開發(fā)者可以根據(jù)實際情況,檢查當(dāng)前的音頻場景模式和鈴聲模式,以使用相應(yīng)的音頻處理策略。
音頻場景模式
應(yīng)用使用音頻通話相關(guān)功能時,系統(tǒng)會切換至與通話相關(guān)的音頻場景模式(AudioScene),當(dāng)前預(yù)置了多種音頻場景,包括響鈴、通話、語音聊天等,在不同的場景下,系統(tǒng)會采用不同的策略來處理音頻。
當(dāng)前預(yù)置的音頻場景:
●?AUDIO_SCENE_DEFAULT:默認音頻場景,音頻通話之外的場景均可使用。
●?AUDIO_SCENE_VOICE_CHAT:語音聊天音頻場景,VOIP 通話時使用。
應(yīng)用可通過AudioManager的 getAudioScene 來獲取當(dāng)前的音頻場景模式。當(dāng)應(yīng)用開始或結(jié)束使用音頻通話相關(guān)功能時,可通過此方法檢查系統(tǒng)是否已切換為合適的音頻場景模式。
鈴聲模式
在用戶進入到音頻通話時,應(yīng)用可以使用鈴聲或振動來提示用戶。系統(tǒng)通過調(diào)整鈴聲模式(AudioRingMode),實現(xiàn)便捷地管理鈴聲音量,并調(diào)整設(shè)備的振動模式。
當(dāng)前預(yù)置的三種鈴聲模式:
●?RINGER_MODE_SILENT:靜音模式,此模式下鈴聲音量為零(即靜音)。
●?RINGER_MODE_VIBRATE:振動模式,此模式下鈴聲音量為零,設(shè)備振動開啟(即響鈴時靜音,觸發(fā)振動)。
●?RINGER_MODE_NORMAL:響鈴模式,此模式下鈴聲音量正常。
應(yīng)用可以調(diào)用AudioVolumeGroupManager中的 getRingerMode 獲取當(dāng)前的鈴聲模式,以便采取合適的提示策略。
如果應(yīng)用希望及時獲取鈴聲模式的變化情況,可以通過 AudioVolumeGroupManager 中的 on('ringerModeChange')監(jiān)聽鈴聲模式變化事件,使應(yīng)用在鈴聲模式發(fā)生變化時及時收到通知,方便應(yīng)用做出相應(yīng)的調(diào)整。
通話場景音頻設(shè)備切換
在通話場景下,系統(tǒng)會根據(jù)默認優(yōu)先級選擇合適的音頻設(shè)備。應(yīng)用可以根據(jù)需要,自主切換音頻設(shè)備。
通信設(shè)備類型(CommunicationDeviceType)是系統(tǒng)預(yù)置的可用于通話場景的設(shè)備,應(yīng)用可以使用AudioRoutingManager的 isCommunicationDeviceActive 函數(shù)獲取指定通信設(shè)備的激活狀態(tài),并且可以使用 AudioRoutingManager 的 setCommunicationDevice 設(shè)置通信設(shè)備的激活狀態(tài),通過激活設(shè)備來實現(xiàn)通話場景音頻設(shè)備的切換。
在音頻通話場景下,音頻輸出(播放對端聲音)和音頻輸入(錄制本端聲音)會同時進行,應(yīng)用可以通過使用 AudioRenderer 來實現(xiàn)音頻輸出,通過使用 AudioCapturer 來實現(xiàn)音頻輸入,同時使用 AudioRenderer 和 AudioCapturer 即可實現(xiàn)音頻通話功能。
開發(fā)音視頻通話功能
在音頻通話開始和結(jié)束時,應(yīng)用可以自行檢查當(dāng)前的音頻場景模式和鈴聲模式,以便采取合適的音頻管理及提示策略。
以下代碼示范了同時使用 AudioRenderer 和 AudioCapturer 實現(xiàn)音頻通話功能的基本過程,其中未包含音頻通話數(shù)據(jù)的傳輸過程,實際開發(fā)中,需要將網(wǎng)絡(luò)傳輸來的對端通話數(shù)據(jù)解碼播放,此處僅以讀取音頻文件的數(shù)據(jù)代替;同時需要將本端錄制的通話數(shù)據(jù)編碼打包,通過網(wǎng)絡(luò)發(fā)送給對端,此處僅以將數(shù)據(jù)寫入音頻文件代替。
使用 AudioRenderer 播放對端的通話聲音
該過程與使用AudioRenderer開發(fā)音頻播放功能過程相似,關(guān)鍵區(qū)別在于 audioRenderInfo 參數(shù)和音頻數(shù)據(jù)來源。audioRenderInfo 參數(shù)中,音頻內(nèi)容類型需設(shè)置為語音,CONTENT_TYPE_SPEECH,音頻流使用類型需設(shè)置為語音通信,STREAM_USAGE_VOICE_COMMUNICATION。
import?audio?from '@ohos.multimedia.audio';
import?fs?from '@ohos.file.fs';
const TAG = 'VoiceCallDemoForAudioRenderer';
//?與使用AudioRenderer開發(fā)音頻播放功能過程相似,關(guān)鍵區(qū)別在于audioRendererInfo參數(shù)和音頻數(shù)據(jù)來源
export default class VoiceCallDemoForAudioRenderer {
private?renderModel?= undefined;
private?audioStreamInfo?= {
????samplingRate:?audio.AudioSamplingRate.SAMPLE_RATE_48000, //?采樣率
????channels:?audio.AudioChannel.CHANNEL_2, //?通道
????sampleFormat:?audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, //?采樣格式
????encodingType:?audio.AudioEncodingType.ENCODING_TYPE_RAW //?編碼格式
}
private?audioRendererInfo?= {
//?需使用通話場景相應(yīng)的參數(shù)
????content:?audio.ContentType.CONTENT_TYPE_SPEECH, //?音頻內(nèi)容類型:語音
????usage:?audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION, //?音頻流使用類型:語音通信
????rendererFlags: 0 //?音頻渲染器標(biāo)志:默認為0即可
}
private?audioRendererOptions?= {
????streamInfo: this.audioStreamInfo,
????rendererInfo: this.audioRendererInfo
}
//?初始化,創(chuàng)建實例,設(shè)置監(jiān)聽事件
init() {
????audio.createAudioRenderer(this.audioRendererOptions, (err,?renderer) => { //?創(chuàng)建AudioRenderer實例
if (!err) {
console.info(`${TAG}:?creating?AudioRenderer?success`);
this.renderModel?=?renderer;
this.renderModel.on('stateChange', (state) => { //?設(shè)置監(jiān)聽事件,當(dāng)轉(zhuǎn)換到指定的狀態(tài)時觸發(fā)回調(diào)
if (state?== 1) {
console.info('audio?renderer?state?is:?STATE_PREPARED');
}
if (state?== 2) {
console.info('audio?renderer?state?is:?STATE_RUNNING');
}
});
this.renderModel.on('markReach', 1000, (position) => { //?訂閱markReach事件,當(dāng)渲染的幀數(shù)達到1000幀時觸發(fā)回調(diào)
if (position?== 1000) {
console.info('ON?Triggered?successfully');
}
});
} else {
console.info(`${TAG}:?creating?AudioRenderer?failed,?error:?${err.message}`);
}
});
}
//?開始一次音頻渲染
async start() {
let?stateGroup?= [audio.AudioState.STATE_PREPARED,?audio.AudioState.STATE_PAUSED,?audio.AudioState.STATE_STOPPED];
if (stateGroup.indexOf(this.renderModel.state) === -1) { //?當(dāng)且僅當(dāng)狀態(tài)為STATE_PREPARED、STATE_PAUSED和STATE_STOPPED之一時才能啟動渲染
console.error(TAG + 'start?failed');
return;
}
await this.renderModel.start(); //?啟動渲染
const?bufferSize?= await this.renderModel.getBufferSize();
//?此處僅以讀取音頻文件的數(shù)據(jù)舉例,實際音頻通話開發(fā)中,需要讀取的是通話對端傳輸來的音頻數(shù)據(jù)
let?context?= getContext(this);
let?path?=?context.filesDir;
const?filePath?=?path?+ '/voice_call_data.wav'; //?沙箱路徑,實際路徑為/data/storage/el2/base/haps/entry/files/voice_call_data.wav
let?file?=?fs.openSync(filePath,?fs.OpenMode.READ_ONLY);
let?stat?= await?fs.stat(filePath);
let?buf?= new ArrayBuffer(bufferSize);
let?len?=?stat.size?%?bufferSize?=== 0 ??Math.floor(stat.size?/?bufferSize) :?Math.floor(stat.size?/?bufferSize?+ 1);
for (let?i?= 0;?i?<?len;?i++) {
let?options?= {
????????offset:?i?*?bufferSize,
????????length:?bufferSize
};
let?readsize?= await?fs.read(file.fd,?buf,?options);
//?buf是要寫入緩沖區(qū)的音頻數(shù)據(jù),在調(diào)用AudioRenderer.write()方法前可以進行音頻數(shù)據(jù)的預(yù)處理,實現(xiàn)個性化的音頻播放功能,AudioRenderer會讀出寫入緩沖區(qū)的音頻數(shù)據(jù)進行渲染
let?writeSize?= await new Promise((resolve,?reject) => {
this.renderModel.write(buf, (err,?writeSize) => {
if (err) {
reject(err);
} else {
resolve(writeSize);
}
});
});
if (this.renderModel.state?===?audio.AudioState.STATE_RELEASED) { //?如果渲染器狀態(tài)為STATE_RELEASED,停止渲染
????????fs.close(file);
await this.renderModel.stop();
}
if (this.renderModel.state?===?audio.AudioState.STATE_RUNNING) {
if (i?===?len?- 1) { //?如果音頻文件已經(jīng)被讀取完,停止渲染
??????????fs.close(file);
await this.renderModel.stop();
}
}
}
}
//?暫停渲染
async pause() {
//?只有渲染器狀態(tài)為STATE_RUNNING的時候才能暫停
if (this.renderModel.state?!==?audio.AudioState.STATE_RUNNING) {
console.info('Renderer?is?not?running');
return;
}
await this.renderModel.pause(); //?暫停渲染
if (this.renderModel.state?===?audio.AudioState.STATE_PAUSED) {
console.info('Renderer?is?paused.');
} else {
console.error('Pausing?renderer?failed.');
}
}
//?停止渲染
async stop() {
//?只有渲染器狀態(tài)為STATE_RUNNING或STATE_PAUSED的時候才可以停止
if (this.renderModel.state?!==?audio.AudioState.STATE_RUNNING && this.renderModel.state?!==?audio.AudioState.STATE_PAUSED) {
console.info('Renderer?is?not?running?or?paused.');
return;
}
await this.renderModel.stop(); //?停止渲染
if (this.renderModel.state?===?audio.AudioState.STATE_STOPPED) {
console.info('Renderer?stopped.');
} else {
console.error('Stopping?renderer?failed.');
}
}
//?銷毀實例,釋放資源
async release() {
//?渲染器狀態(tài)不是STATE_RELEASED狀態(tài),才能release
if (this.renderModel.state?===?audio.AudioState.STATE_RELEASED) {
console.info('Renderer?already?released');
return;
}
await this.renderModel.release(); //?釋放資源
if (this.renderModel.state?===?audio.AudioState.STATE_RELEASED) {
console.info('Renderer?released');
} else {
console.error('Renderer?release?failed.');
}
}
}
使用 AudioCapturer 錄制本端的通話聲音
該過程與使用AudioCapturer開發(fā)音頻錄制功能過程相似,關(guān)鍵區(qū)別在于 audioCapturerInfo 參數(shù)和音頻數(shù)據(jù)流向。audioCapturerInfo 參數(shù)中音源類型需設(shè)置為語音通話,SOURCE_TYPE_VOICE_COMMUNICATION。
import?audio?from '@ohos.multimedia.audio';
import?fs?from '@ohos.file.fs';
const TAG = 'VoiceCallDemoForAudioCapturer';
//?與使用AudioCapturer開發(fā)音頻錄制功能過程相似,關(guān)鍵區(qū)別在于audioCapturerInfo參數(shù)和音頻數(shù)據(jù)流向
export default class VoiceCallDemoForAudioCapturer {
private?audioCapturer?= undefined;
private?audioStreamInfo?= {
????samplingRate:?audio.AudioSamplingRate.SAMPLE_RATE_44100, //?采樣率
????channels:?audio.AudioChannel.CHANNEL_1, //?通道
????sampleFormat:?audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, //?采樣格式
????encodingType:?audio.AudioEncodingType.ENCODING_TYPE_RAW //?編碼格式
}
private?audioCapturerInfo?= {
//?需使用通話場景相應(yīng)的參數(shù)
????source:?audio.SourceType.SOURCE_TYPE_VOICE_COMMUNICATION, //?音源類型:語音通話
????capturerFlags: 0 //?音頻采集器標(biāo)志:默認為0即可
}
private?audioCapturerOptions?= {
????streamInfo: this.audioStreamInfo,
????capturerInfo: this.audioCapturerInfo
}
//?初始化,創(chuàng)建實例,設(shè)置監(jiān)聽事件
init() {
????audio.createAudioCapturer(this.audioCapturerOptions, (err,?capturer) => { //?創(chuàng)建AudioCapturer實例
if (err) {
console.error(`Invoke?createAudioCapturer?failed,?code?is?${err.code},?message?is?${err.message}`);
return;
}
console.info(`${TAG}:?create?AudioCapturer?success`);
this.audioCapturer?=?capturer;
this.audioCapturer.on('markReach', 1000, (position) => { //?訂閱markReach事件,當(dāng)采集的幀數(shù)達到1000時觸發(fā)回調(diào)
if (position?=== 1000) {
console.info('ON?Triggered?successfully');
}
});
this.audioCapturer.on('periodReach', 2000, (position) => { //?訂閱periodReach事件,當(dāng)采集的幀數(shù)達到2000時觸發(fā)回調(diào)
if (position?=== 2000) {
console.info('ON?Triggered?successfully');
}
});
});
}
//?開始一次音頻采集
async start() {
let?stateGroup?= [audio.AudioState.STATE_PREPARED,?audio.AudioState.STATE_PAUSED,?audio.AudioState.STATE_STOPPED];
if (stateGroup.indexOf(this.audioCapturer.state) === -1) { //?當(dāng)且僅當(dāng)狀態(tài)為STATE_PREPARED、STATE_PAUSED和STATE_STOPPED之一時才能啟動采集
console.error(`${TAG}:?start?failed`);
return;
}
await this.audioCapturer.start(); //?啟動采集
//?此處僅以將音頻數(shù)據(jù)寫入文件舉例,實際音頻通話開發(fā)中,需要將本端采集的音頻數(shù)據(jù)編碼打包,通過網(wǎng)絡(luò)發(fā)送給通話對端
let?context?= getContext(this);
const?path?=?context.filesDir?+ '/voice_call_data.wav'; //?采集到的音頻文件存儲路徑
let?file?=?fs.openSync(path, 0o2 | 0o100); //?如果文件不存在則創(chuàng)建文件
let?fd?=?file.fd;
let?numBuffersToCapture?= 150; //?循環(huán)寫入150次
let?count?= 0;
while (numBuffersToCapture) {
let?bufferSize?= await this.audioCapturer.getBufferSize();
let?buffer?= await this.audioCapturer.read(bufferSize, true);
let?options?= {
????????offset:?count?*?bufferSize,
????????length:?bufferSize
};
if (buffer?=== undefined) {
console.error(`${TAG}:?read?buffer?failed`);
} else {
let number =?fs.writeSync(fd,?buffer,?options);
console.info(`${TAG}:?write?date:?${number}`);
}
??????numBuffersToCapture--;
??????count++;
}
}
//?停止采集
async stop() {
//?只有采集器狀態(tài)為STATE_RUNNING或STATE_PAUSED的時候才可以停止
if (this.audioCapturer.state?!==?audio.AudioState.STATE_RUNNING && this.audioCapturer.state?!==?audio.AudioState.STATE_PAUSED) {
console.info('Capturer?is?not?running?or?paused');
return;
}
await this.audioCapturer.stop(); //?停止采集
if (this.audioCapturer.state?===?audio.AudioState.STATE_STOPPED) {
console.info('Capturer?stopped');
} else {
console.error('Capturer?stop?failed');
}
}
//?銷毀實例,釋放資源
async release() {
//?采集器狀態(tài)不是STATE_RELEASED或STATE_NEW狀態(tài),才能release
if (this.audioCapturer.state?===?audio.AudioState.STATE_RELEASED || this.audioCapturer.state?===?audio.AudioState.STATE_NEW) {
console.info('Capturer?already?released');
return;
}
await this.audioCapturer.release(); //?釋放資源
if (this.audioCapturer.state?==?audio.AudioState.STATE_RELEASED) {
console.info('Capturer?released');
} else {
console.error('Capturer?release?failed');
}
}
}