欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?WebRTC?對?AudioRecord?的使用技術分享

 更新時間:2022年02月23日 09:09:37   作者:anyRTC  
這篇文章主要介紹了Android?WebRTC?對?AudioRecord?的使用技術分享,AudioRecord?是?Android?基于原始PCM音頻數(shù)據(jù)錄制的類,接下來和小編進入文章了解更詳細的內(nèi)容吧

前言:

AudioRecord 是 Android 基于原始PCM音頻數(shù)據(jù)錄制的類,WebRCT 對其封裝的代碼位置位于org/webrtc/audio/WebRtcAudioRecord.java,接下來我們學習一下 AudioRecord 是如何創(chuàng)建啟動,讀取音頻采集數(shù)據(jù)以及銷毀等功能的。

一、創(chuàng)建和初始化

 private int initRecording(int sampleRate, int channels) {
? ? Logging.d(TAG, "initRecording(sampleRate=" + sampleRate + ", channels=" + channels + ")");
? ? if (audioRecord != null) {
? ? ? reportWebRtcAudioRecordInitError("InitRecording called twice without StopRecording.");
? ? ? return -1;
? ? }
? ? final int bytesPerFrame = channels * (BITS_PER_SAMPLE / 8);
? ? final int framesPerBuffer = sampleRate / BUFFERS_PER_SECOND;
? ? byteBuffer = ByteBuffer.allocateDirect(bytesPerFrame * framesPerBuffer);
? ? Logging.d(TAG, "byteBuffer.capacity: " + byteBuffer.capacity());
? ? emptyBytes = new byte[byteBuffer.capacity()];
? ? // Rather than passing the ByteBuffer with every callback (requiring
? ? // the potentially expensive GetDirectBufferAddress) we simply have the
? ? // the native class cache the address to the memory once.
? ? nativeCacheDirectBufferAddress(byteBuffer, nativeAudioRecord);

? ? // Get the minimum buffer size required for the successful creation of
? ? // an AudioRecord object, in byte units.
? ? // Note that this size doesn't guarantee a smooth recording under load.
? ? final int channelConfig = channelCountToConfiguration(channels);
? ? int minBufferSize =
? ? ? ? AudioRecord.getMinBufferSize(sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT);
? ? if (minBufferSize == AudioRecord.ERROR || minBufferSize == AudioRecord.ERROR_BAD_VALUE) {
? ? ? reportWebRtcAudioRecordInitError("AudioRecord.getMinBufferSize failed: " + minBufferSize);
? ? ? return -1;
? ? }
? ? Logging.d(TAG, "AudioRecord.getMinBufferSize: " + minBufferSize);

? ? // Use a larger buffer size than the minimum required when creating the
? ? // AudioRecord instance to ensure smooth recording under load. It has been
? ? // verified that it does not increase the actual recording latency.
? ? int bufferSizeInBytes = Math.max(BUFFER_SIZE_FACTOR * minBufferSize, byteBuffer.capacity());
? ? Logging.d(TAG, "bufferSizeInBytes: " + bufferSizeInBytes);
? ? try {
? ? ? audioRecord = new AudioRecord(audioSource, sampleRate, channelConfig,
? ? ? ? ? AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes);
? ? } catch (IllegalArgumentException e) {
? ? ? reportWebRtcAudioRecordInitError("AudioRecord ctor error: " + e.getMessage());
? ? ? releaseAudioResources();
? ? ? return -1;
? ? }
? ? if (audioRecord == null || audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
? ? ? reportWebRtcAudioRecordInitError("Failed to create a new AudioRecord instance");
? ? ? releaseAudioResources();
? ? ? return -1;
? ? }
? ? if (effects != null) {
? ? ? effects.enable(audioRecord.getAudioSessionId());
? ? }
? ? logMainParameters();
? ? logMainParametersExtended();
? ? return framesPerBuffer;
? }

在初始化的方法中,主要做了兩件事。

創(chuàng)建緩沖區(qū):

  • 由于實際使用數(shù)據(jù)的代碼在native層,因此這里創(chuàng)建了一個Java的direct buffer,而且AudioRecord也有通過ByteBuffer讀數(shù)據(jù)的接口,并且實際把數(shù)據(jù)復制到ByteBuffer的代碼也在native層,所以這里使用direct buffer效率會更高。
  • ByteBuffer的容量為單次讀取數(shù)據(jù)的大小。Android的數(shù)據(jù)格式是打包格式(packed),在多個聲道時,同一個樣點的不同聲道連續(xù)存儲在一起,接著存儲下一個樣點的不同聲道;一幀就是一個樣點的所有聲道數(shù)據(jù)的合集,一次讀取的幀數(shù)是10ms的樣點數(shù)(采樣率除以100,樣點個數(shù)等于采樣率時對應于1s的數(shù)據(jù),所以除以100就是10ms的數(shù)據(jù));ByteBuffer的容量為幀數(shù)乘聲道數(shù)乘每個樣點的字節(jié)數(shù)(PCM 16 bit表示每個樣點為兩個字節(jié))。
  • 這里調(diào)用的nativeCacheDirectBufferAddress JNI函數(shù)會在native層把ByteBuffer的訪問地址提前保存下來,避免每次讀到音頻數(shù)據(jù)后,還需要調(diào)用接口獲取訪問地址。

創(chuàng)建 AudioRecord對象,構造函數(shù)有很多參數(shù),分析如下:

  • audioSource:指的是音頻采集模式,默認是 VOICE_COMMUNICATION,該模式會使用硬件AEC(回聲抑制)
  • sampleRate:采樣率
  • channelConfig:聲道數(shù)
  • audioFormat:音頻數(shù)據(jù)格式,這里實際使用的是 AudioFormat.ENCODING_PCM_16BIT,即PCM 16 bit的數(shù)據(jù)格式。
  • bufferSize:系統(tǒng)創(chuàng)建AudioRecord時使用的緩沖區(qū)大小,這里使用了兩個數(shù)值的較大者:通過AudioRecord.getMinBufferSize接口獲取的最小緩沖區(qū)大小的兩倍,讀取數(shù)據(jù)的ByteBuffer的容量。通過注釋我們可以了解到,考慮最小緩沖區(qū)的兩倍是為了確保系統(tǒng)負載較高的情況下音頻采集仍能平穩(wěn)運行,而且這里設置更大的緩沖區(qū)并不會增加音頻采集的延遲。

二、啟動

private boolean startRecording() {
? ? Logging.d(TAG, "startRecording");
? ? assertTrue(audioRecord != null);
? ? assertTrue(audioThread == null);
? ? try {
? ? ? audioRecord.startRecording();
? ? } catch (IllegalStateException e) {
? ? ? reportWebRtcAudioRecordStartError(AudioRecordStartErrorCode.AUDIO_RECORD_START_EXCEPTION,
? ? ? ? ? "AudioRecord.startRecording failed: " + e.getMessage());
? ? ? return false;
? ? }
? ? if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
? ? ? reportWebRtcAudioRecordStartError(
? ? ? ? ? AudioRecordStartErrorCode.AUDIO_RECORD_START_STATE_MISMATCH,
? ? ? ? ? "AudioRecord.startRecording failed - incorrect state :"
? ? ? ? ? + audioRecord.getRecordingState());
? ? ? return false;
? ? }
? ? audioThread = new AudioRecordThread("AudioRecordJavaThread");
? ? audioThread.start();
? ? return true;
? }

? 在該方法中,首先啟動了 audioRecord,接著判斷了讀取線程事都正在錄制中。

三、讀數(shù)據(jù)

?private class AudioRecordThread extends Thread {
? ? private volatile boolean keepAlive = true;

? ? public AudioRecordThread(String name) {
? ? ? super(name);
? ? }

? ? // TODO(titovartem) make correct fix during webrtc:9175
? ? @SuppressWarnings("ByteBufferBackingArray")
? ? @Override
? ? public void run() {
? ? ? Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
? ? ? Logging.d(TAG, "AudioRecordThread" + WebRtcAudioUtils.getThreadInfo());
? ? ? assertTrue(audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING);

? ? ? long lastTime = System.nanoTime();
? ? ? while (keepAlive) {
? ? ? ? int bytesRead = audioRecord.read(byteBuffer, byteBuffer.capacity());
? ? ? ? if (bytesRead == byteBuffer.capacity()) {
? ? ? ? ? if (microphoneMute) {
? ? ? ? ? ? byteBuffer.clear();
? ? ? ? ? ? byteBuffer.put(emptyBytes);
? ? ? ? ? }
? ? ? ? ? // It's possible we've been shut down during the read, and stopRecording() tried and
? ? ? ? ? // failed to join this thread. To be a bit safer, try to avoid calling any native methods
? ? ? ? ? // in case they've been unregistered after stopRecording() returned.
? ? ? ? ? if (keepAlive) {
? ? ? ? ? ? nativeDataIsRecorded(bytesRead, nativeAudioRecord);
? ? ? ? ? }
? ? ? ? ? if (audioSamplesReadyCallback != null) {
? ? ? ? ? ? // Copy the entire byte buffer array. ?Assume that the start of the byteBuffer is
? ? ? ? ? ? // at index 0.
? ? ? ? ? ? byte[] data = Arrays.copyOf(byteBuffer.array(), byteBuffer.capacity());
? ? ? ? ? ? audioSamplesReadyCallback.onWebRtcAudioRecordSamplesReady(
? ? ? ? ? ? ? ? new AudioSamples(audioRecord, data));
? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? String errorMessage = "AudioRecord.read failed: " + bytesRead;
? ? ? ? ? Logging.e(TAG, errorMessage);
? ? ? ? ? if (bytesRead == AudioRecord.ERROR_INVALID_OPERATION) {
? ? ? ? ? ? keepAlive = false;
? ? ? ? ? ? reportWebRtcAudioRecordError(errorMessage);
? ? ? ? ? }
? ? ? ? }
? ? ? ? if (DEBUG) {
? ? ? ? ? long nowTime = System.nanoTime();
? ? ? ? ? long durationInMs = TimeUnit.NANOSECONDS.toMillis((nowTime - lastTime));
? ? ? ? ? lastTime = nowTime;
? ? ? ? ? Logging.d(TAG, "bytesRead[" + durationInMs + "] " + bytesRead);
? ? ? ? }
? ? ? }

? ? ? try {
? ? ? ? if (audioRecord != null) {
? ? ? ? ? audioRecord.stop();
? ? ? ? }
? ? ? } catch (IllegalStateException e) {
? ? ? ? Logging.e(TAG, "AudioRecord.stop failed: " + e.getMessage());
? ? ? }
? ? }

? ? // Stops the inner thread loop and also calls AudioRecord.stop().
? ? // Does not block the calling thread.
? ? public void stopThread() {
? ? ? Logging.d(TAG, "stopThread");
? ? ? keepAlive = false;
? ? }
? }

? 從 AudioRecord去數(shù)據(jù)的邏輯在 AudioRecordThread 線程的 Run函數(shù)中。

  • 在線程啟動的地方,先設置線程的優(yōu)先級為URGENT_AUDIO,這里調(diào)用的是Process.setThreadPriority。
  • 在一個循環(huán)中不停地調(diào)用audioRecord.read讀取數(shù)據(jù),把采集到的數(shù)據(jù)讀到ByteBuffer中,然后調(diào)用nativeDataIsRecorded JNI函數(shù)通知native層數(shù)據(jù)已經(jīng)讀到,進行下一步處理。

四、停止和銷毀

? private boolean stopRecording() {
? ? Logging.d(TAG, "stopRecording");
? ? assertTrue(audioThread != null);
? ? audioThread.stopThread();
? ? if (!ThreadUtils.joinUninterruptibly(audioThread, AUDIO_RECORD_THREAD_JOIN_TIMEOUT_MS)) {
? ? ? Logging.e(TAG, "Join of AudioRecordJavaThread timed out");
? ? ? WebRtcAudioUtils.logAudioState(TAG);
? ? }
? ? audioThread = null;
? ? if (effects != null) {
? ? ? effects.release();
? ? }
? ? releaseAudioResources();
? ? return true;
? }

? 可以看到,這里首先把AudioRecordThread讀數(shù)據(jù)循環(huán)的keepAlive條件置為false,接著調(diào)用ThreadUtils.joinUninterruptibly等待AudioRecordThread線程退出。

這里有一點值得一提,keepAlive變量加了volatile關鍵字進行修飾,這是因為修改和讀取這個變量的操作可能發(fā)生在不同的線程,使用volatile關鍵字進行修飾,可以保證修改之后能被立即讀取到。

AudioRecordThread線程退出循環(huán)后,會調(diào)用audioRecord.stop()停止采集;線程退出之后,會調(diào)用audioRecord.release()釋放AudioRecord對象。

? 以上,就是 Android WebRTC 音頻采集 Java 層的大致流程。

到此這篇關于Android WebRTC 對 AudioRecord 的使用技術分享的文章就介紹到這了,更多相關Android WebRTC 對 AudioRecord 的使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論