diff options
author | Atneya Nair <atneya@google.com> | 2022-01-18 16:55:22 -0500 |
---|---|---|
committer | Atneya Nair <atneya@google.com> | 2022-02-09 11:06:52 -0500 |
commit | 045e21dfc1d2b7d29f49cd5d81d2fb6c7735615b (patch) | |
tree | 5b450ed5a37ea85187be342cea42c558e4695d75 | |
parent | 48cf30d4a6b635c55a71fdd40ab3b2dca4d73477 (diff) | |
download | wilhelm-045e21dfc1d2b7d29f49cd5d81d2fb6c7735615b.tar.gz |
Refactor for AudioTrack/Record callback interface
Replace libaudioclient callback functions with appropriate
interfaces. Control callback object lifetime with ref-counting.
Misc cleanup including using sp<> where appropriate.
Test: OboeTester Output/Input streams
Bug: 199156212
Bug: 216175830
Change-Id: I366c543e85a62f878908836e9ad1914182dc9e6f
-rw-r--r-- | src/android/AudioPlayer_to_android.cpp | 77 | ||||
-rw-r--r-- | src/android/AudioRecordCallback.h | 80 | ||||
-rw-r--r-- | src/android/AudioRecorder_to_android.cpp | 132 | ||||
-rw-r--r-- | src/android/AudioTrackCallback.h | 98 | ||||
-rw-r--r-- | src/classes.h | 2 | ||||
-rw-r--r-- | src/itf/IEngine.cpp | 1 |
6 files changed, 246 insertions, 144 deletions
diff --git a/src/android/AudioPlayer_to_android.cpp b/src/android/AudioPlayer_to_android.cpp index a324b4a..ef466b6 100644 --- a/src/android/AudioPlayer_to_android.cpp +++ b/src/android/AudioPlayer_to_android.cpp @@ -19,6 +19,7 @@ #include "android/android_AudioToCbRenderer.h" #include "android/android_StreamPlayer.h" #include "android/android_LocAVPlayer.h" +#include "android/AudioTrackCallback.h" #include "android/include/AacBqToPcmCbRenderer.h" #include "android/channels.h" @@ -1233,23 +1234,15 @@ SLresult android_audioPlayer_checkSourceSink(CAudioPlayer *pAudioPlayer) //----------------------------------------------------------------------------- // Callback associated with an AudioTrack of an SL ES AudioPlayer that gets its data // from a buffer queue. This will not be called once the AudioTrack has been destroyed. -static void audioTrack_callBack_pullFromBuffQueue(int event, void* user, void *info) { - CAudioPlayer *ap = (CAudioPlayer *)user; - - if (!android::CallbackProtector::enterCbIfOk(ap->mCallbackProtector)) { - // it is not safe to enter the callback (the track is about to go away) - return; - } +size_t audioTrack_handleMoreData_lockPlay(CAudioPlayer* ap, + const android::AudioTrack::Buffer& buffer) { void * callbackPContext = NULL; - switch (event) { - - case android::AudioTrack::EVENT_MORE_DATA: { + size_t bytesWritten = 0; //SL_LOGV("received event EVENT_MORE_DATA from AudioTrack TID=%d", gettid()); slPrefetchCallback prefetchCallback = NULL; void *prefetchContext = NULL; SLuint32 prefetchEvents = SL_PREFETCHEVENT_NONE; - android::AudioTrack::Buffer* pBuff = (android::AudioTrack::Buffer*)info; // retrieve data from the buffer queue interface_lock_exclusive(&ap->mBufferQueue); @@ -1274,17 +1267,15 @@ static void audioTrack_callBack_pullFromBuffQueue(int event, void* user, void *i BufferHeader *newFront = &oldFront[1]; size_t availSource = oldFront->mSize - ap->mBufferQueue.mSizeConsumed; - size_t availSink = pBuff->size; + size_t availSink = buffer.size(); size_t bytesToCopy = availSource < availSink ? availSource : availSink; void *pSrc = (char *)oldFront->mBuffer + ap->mBufferQueue.mSizeConsumed; - memcpy(pBuff->raw, pSrc, bytesToCopy); - + memcpy(buffer.data(), pSrc, bytesToCopy); + bytesWritten = bytesToCopy; if (bytesToCopy < availSource) { ap->mBufferQueue.mSizeConsumed += bytesToCopy; - // pBuff->size is already equal to bytesToCopy in this case } else { // consumed an entire buffer, dequeue - pBuff->size = bytesToCopy; ap->mBufferQueue.mSizeConsumed = 0; if (newFront == &ap->mBufferQueue.mArray @@ -1299,8 +1290,6 @@ static void audioTrack_callBack_pullFromBuffQueue(int event, void* user, void *i ap->mBufferQueue.mCallbackPending = true; } } else { // empty queue - // signal no data available - pBuff->size = 0; // signal we're at the end of the content, but don't pause (see note in function) audioPlayer_dispatch_headAtEnd_lockPlay(ap, false /*set state to paused?*/, false); @@ -1336,41 +1325,8 @@ static void audioTrack_callBack_pullFromBuffQueue(int event, void* user, void *i SL_PREFETCHEVENT_FILLLEVELCHANGE); } } - } - break; - case android::AudioTrack::EVENT_MARKER: - //SL_LOGI("received event EVENT_MARKER from AudioTrack"); - audioTrack_handleMarker_lockPlay(ap); - break; - - case android::AudioTrack::EVENT_NEW_POS: - //SL_LOGI("received event EVENT_NEW_POS from AudioTrack"); - audioTrack_handleNewPos_lockPlay(ap); - break; - - case android::AudioTrack::EVENT_UNDERRUN: - //SL_LOGI("received event EVENT_UNDERRUN from AudioTrack"); - audioTrack_handleUnderrun_lockPlay(ap); - break; - - case android::AudioTrack::EVENT_NEW_IAUDIOTRACK: - // ignore for now - break; - - case android::AudioTrack::EVENT_BUFFER_END: - case android::AudioTrack::EVENT_LOOP_END: - case android::AudioTrack::EVENT_STREAM_END: - // These are unexpected so fall through - FALLTHROUGH_INTENDED; - default: - // FIXME where does the notification of SL_PLAYEVENT_HEADMOVING fit? - SL_LOGE("Encountered unknown AudioTrack event %d for CAudioPlayer %p", event, - (CAudioPlayer *)user); - break; - } - - ap->mCallbackProtector->exitCb(); + return bytesWritten; } @@ -1684,16 +1640,15 @@ SLresult android_audioPlayer_realize(CAudioPlayer *pAudioPlayer, SLboolean async } else { notificationFrames = 0; } - - android::AudioTrack* pat = new android::AudioTrack( + const auto callbackHandle = android::sp<android::AudioTrackCallback>::make(pAudioPlayer); + const auto pat = android::sp<android::AudioTrack>::make( pAudioPlayer->mStreamType, // streamType sampleRate, // sampleRate sles_to_android_sampleFormat(df_pcm), // format channelMask, // channel mask 0, // frameCount policy, // flags - audioTrack_callBack_pullFromBuffQueue, // callback - (void *) pAudioPlayer, // user + callbackHandle, // callback notificationFrames, // see comment above pAudioPlayer->mSessionId); @@ -1702,17 +1657,17 @@ SLresult android_audioPlayer_realize(CAudioPlayer *pAudioPlayer, SLboolean async android::status_t status = pat->initCheck(); if (status != android::NO_ERROR) { - // AudioTracks are meant to be refcounted, so their dtor is protected. - static_cast<void>(android::sp<android::AudioTrack>(pat)); - SL_LOGE("AudioTrack::initCheck status %u", status); // FIXME should return a more specific result depending on status result = SL_RESULT_CONTENT_UNSUPPORTED; return result; } - pAudioPlayer->mTrackPlayer->init(pat, android::PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE, - usageForStreamType(pAudioPlayer->mStreamType), pAudioPlayer->mSessionId); + pAudioPlayer->mTrackPlayer->init( + pat, callbackHandle, + android::PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE, + usageForStreamType(pAudioPlayer->mStreamType), + pAudioPlayer->mSessionId); // update performance mode according to actual flags granted to AudioTrack checkAndSetPerformanceModePost(pAudioPlayer); diff --git a/src/android/AudioRecordCallback.h b/src/android/AudioRecordCallback.h new file mode 100644 index 0000000..53b7e35 --- /dev/null +++ b/src/android/AudioRecordCallback.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifndef SL_PREFETCHEVENT_NONE // This is defined in slesl_allinclusive, which isn't guarded +#include "sles_allinclusive.h" +#endif + +#include "media/AudioRecord.h" + +void audioRecorder_handleOverrun_lockRecord(CAudioRecorder* ar); +void audioRecorder_handleNewPos_lockRecord(CAudioRecorder* ar); +void audioRecorder_handleMarker_lockRecord(CAudioRecorder* ar); +size_t audioRecorder_handleMoreData_lockRecord(CAudioRecorder* ar, + const android::AudioRecord::Buffer&); +//-------------------------------------------------------------------------------------------------- +namespace android { + +class AudioRecordCallback : public android::AudioRecord::IAudioRecordCallback { + public: + AudioRecordCallback(CAudioRecorder * audioRecorder) : mAr(audioRecorder) {} + AudioRecordCallback(const AudioRecordCallback&) = delete; + AudioRecordCallback& operator=(const AudioRecordCallback&) = delete; + + private: + size_t onMoreData(const android::AudioRecord::Buffer& buffer) override { + if (!android::CallbackProtector::enterCbIfOk(mAr->mCallbackProtector)) { + // it is not safe to enter the callback (the track is about to go away) + return buffer.size(); // replicate existing behavior + } + size_t bytesRead = audioRecorder_handleMoreData_lockRecord(mAr, buffer); + mAr->mCallbackProtector->exitCb(); + return bytesRead; + } + + + void onOverrun() override { + if (!android::CallbackProtector::enterCbIfOk(mAr->mCallbackProtector)) { + // it is not safe to enter the callback (the track is about to go away) + return; + } + audioRecorder_handleOverrun_lockRecord(mAr); + mAr->mCallbackProtector->exitCb(); + } + void onMarker(uint32_t) override { + if (!android::CallbackProtector::enterCbIfOk(mAr->mCallbackProtector)) { + // it is not safe to enter the callback (the track is about to go away) + return; + } + + audioRecorder_handleMarker_lockRecord(mAr); + mAr->mCallbackProtector->exitCb(); + } + void onNewPos(uint32_t) override { + if (!android::CallbackProtector::enterCbIfOk(mAr->mCallbackProtector)) { + // it is not safe to enter the callback (the track is about to go away) + return; + } + + audioRecorder_handleNewPos_lockRecord(mAr); + mAr->mCallbackProtector->exitCb(); + } + CAudioRecorder * const mAr; +}; + +} // namespace android diff --git a/src/android/AudioRecorder_to_android.cpp b/src/android/AudioRecorder_to_android.cpp index 411beff..d4b2964 100644 --- a/src/android/AudioRecorder_to_android.cpp +++ b/src/android/AudioRecorder_to_android.cpp @@ -24,6 +24,7 @@ #include <SLES/OpenSLES_Android.h> #include <android_runtime/AndroidRuntime.h> +#include <android/AudioRecordCallback.h> #define KEY_RECORDING_SOURCE_PARAMSIZE sizeof(SLuint32) #define KEY_RECORDING_PRESET_PARAMSIZE sizeof(SLuint32) @@ -307,96 +308,58 @@ SLresult android_audioRecorder_checkSourceSink(CAudioRecorder* ar) { return SL_RESULT_SUCCESS; } //----------------------------------------------------------------------------- -static void audioRecorder_callback(int event, void* user, void *info) { +size_t audioRecorder_handleMoreData_lockRecord(CAudioRecorder* ar, + const android::AudioRecord::Buffer& buffer) { //SL_LOGV("audioRecorder_callback(%d, %p, %p) entering", event, user, info); - CAudioRecorder *ar = (CAudioRecorder *)user; - - if (!android::CallbackProtector::enterCbIfOk(ar->mCallbackProtector)) { - // it is not safe to enter the callback (the track is about to go away) - return; - } - void * callbackPContext = NULL; - - switch (event) { - case android::AudioRecord::EVENT_MORE_DATA: { - slBufferQueueCallback callback = NULL; - android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info; - - // push data to the buffer queue - interface_lock_exclusive(&ar->mBufferQueue); - - if (ar->mBufferQueue.mState.count != 0) { - assert(ar->mBufferQueue.mFront != ar->mBufferQueue.mRear); - - BufferHeader *oldFront = ar->mBufferQueue.mFront; - BufferHeader *newFront = &oldFront[1]; - - size_t availSink = oldFront->mSize - ar->mBufferQueue.mSizeConsumed; - size_t availSource = pBuff->size; - size_t bytesToCopy = availSink < availSource ? availSink : availSource; - void *pDest = (char *)oldFront->mBuffer + ar->mBufferQueue.mSizeConsumed; - memcpy(pDest, pBuff->raw, bytesToCopy); - - if (bytesToCopy < availSink) { - // can't consume the whole or rest of the buffer in one shot - ar->mBufferQueue.mSizeConsumed += availSource; - // pBuff->size is already equal to bytesToCopy in this case - } else { - // finish pushing the buffer or push the buffer in one shot - pBuff->size = bytesToCopy; - ar->mBufferQueue.mSizeConsumed = 0; - if (newFront == &ar->mBufferQueue.mArray[ar->mBufferQueue.mNumBuffers + 1]) { - newFront = ar->mBufferQueue.mArray; - } - ar->mBufferQueue.mFront = newFront; - - ar->mBufferQueue.mState.count--; - ar->mBufferQueue.mState.playIndex++; - - // data has been copied to the buffer, and the buffer queue state has been updated - // we will notify the client if applicable - callback = ar->mBufferQueue.mCallback; - // save callback data - callbackPContext = ar->mBufferQueue.mContext; + size_t bytesRead = 0; + slBufferQueueCallback callback = NULL; + + // push data to the buffer queue + interface_lock_exclusive(&ar->mBufferQueue); + + if (ar->mBufferQueue.mState.count != 0) { + assert(ar->mBufferQueue.mFront != ar->mBufferQueue.mRear); + + BufferHeader *oldFront = ar->mBufferQueue.mFront; + BufferHeader *newFront = &oldFront[1]; + + size_t availSink = oldFront->mSize - ar->mBufferQueue.mSizeConsumed; + size_t availSource = buffer.size(); + size_t bytesToCopy = availSink < availSource ? availSink : availSource; + void *pDest = (char *)oldFront->mBuffer + ar->mBufferQueue.mSizeConsumed; + memcpy(pDest, buffer.data(), bytesToCopy); + bytesRead = bytesToCopy; + if (bytesToCopy < availSink) { + // can't consume the whole or rest of the buffer in one shot + ar->mBufferQueue.mSizeConsumed += availSource; + } else { + // finish pushing the buffer or push the buffer in one shot + ar->mBufferQueue.mSizeConsumed = 0; + if (newFront == &ar->mBufferQueue.mArray[ar->mBufferQueue.mNumBuffers + 1]) { + newFront = ar->mBufferQueue.mArray; } - } else { // empty queue - // no destination to push the data - pBuff->size = 0; - } + ar->mBufferQueue.mFront = newFront; - interface_unlock_exclusive(&ar->mBufferQueue); + ar->mBufferQueue.mState.count--; + ar->mBufferQueue.mState.playIndex++; - // notify client - if (NULL != callback) { - (*callback)(&ar->mBufferQueue.mItf, callbackPContext); + // data has been copied to the buffer, and the buffer queue state has been updated + // we will notify the client if applicable + callback = ar->mBufferQueue.mCallback; + // save callback data + callbackPContext = ar->mBufferQueue.mContext; } - } - break; - - case android::AudioRecord::EVENT_OVERRUN: - audioRecorder_handleOverrun_lockRecord(ar); - break; - - case android::AudioRecord::EVENT_MARKER: - audioRecorder_handleMarker_lockRecord(ar); - break; - - case android::AudioRecord::EVENT_NEW_POS: - audioRecorder_handleNewPos_lockRecord(ar); - break; + } - case android::AudioRecord::EVENT_NEW_IAUDIORECORD: - // ignore for now - break; + interface_unlock_exclusive(&ar->mBufferQueue); - default: - SL_LOGE("Encountered unknown AudioRecord event %d for CAudioRecord %p", event, ar); - break; + // notify client + if (NULL != callback) { + (*callback)(&ar->mBufferQueue.mItf, callbackPContext); } - - ar->mCallbackProtector->exitCb(); + return bytesRead; } @@ -420,6 +383,7 @@ SLresult android_audioRecorder_create(CAudioRecorder* ar) { // microphone to simple buffer queue ar->mAndroidObjType = AUDIORECORDER_FROM_MIC_TO_PCM_BUFFERQUEUE; ar->mAudioRecord.clear(); + ar->mCallbackHandle.clear(); ar->mCallbackProtector = new android::CallbackProtector(); ar->mRecordSource = AUDIO_SOURCE_DEFAULT; ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_DEFAULT; @@ -694,7 +658,7 @@ SLresult android_audioRecorder_realize(CAudioRecorder* ar, SLboolean async) { attributionSource.uid = VALUE_OR_FATAL(android::legacy2aidl_uid_t_int32_t(getuid())); attributionSource.pid = VALUE_OR_FATAL(android::legacy2aidl_pid_t_int32_t(getpid())); attributionSource.token = android::sp<android::BBinder>::make(); - + ar->mCallbackHandle = android::sp<android::AudioRecordCallback>::make(ar); // initialize platform-specific CAudioRecorder fields ar->mAudioRecord = new android::AudioRecord( ar->mRecordSource, // source @@ -703,8 +667,7 @@ SLresult android_audioRecorder_realize(CAudioRecorder* ar, SLboolean async) { channelMask, // channel mask attributionSource, 0, // frameCount - audioRecorder_callback,// callback_t - (void*)ar, // user, callback data, here the AudioRecorder + ar->mCallbackHandle, 0, // notificationFrames AUDIO_SESSION_ALLOCATE, android::AudioRecord::TRANSFER_CALLBACK, @@ -721,6 +684,7 @@ SLresult android_audioRecorder_realize(CAudioRecorder* ar, SLboolean async) { // FIXME should return a more specific result depending on status result = SL_RESULT_CONTENT_UNSUPPORTED; ar->mAudioRecord.clear(); + ar->mCallbackHandle.clear(); return result; } @@ -744,6 +708,7 @@ SLresult android_audioRecorder_realize(CAudioRecorder* ar, SLboolean async) { SL_LOGE("Java exception releasing recorder routing object."); result = SL_RESULT_INTERNAL_ERROR; ar->mAudioRecord.clear(); + ar->mCallbackHandle.clear(); return result; } } @@ -810,6 +775,7 @@ void android_audioRecorder_destroy(CAudioRecorder* ar) { ar->mAudioRecord.clear(); } // explicit destructor + ar->mCallbackHandle.~sp(); ar->mAudioRecord.~sp(); ar->mCallbackProtector.~sp(); } diff --git a/src/android/AudioTrackCallback.h b/src/android/AudioTrackCallback.h new file mode 100644 index 0000000..7020606 --- /dev/null +++ b/src/android/AudioTrackCallback.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifndef SL_PREFETCHEVENT_NONE // This is defined in slesl_allinclusive, which isn't guarded +#include "sles_allinclusive.h" +#endif + +#include "media/AudioTrack.h" + +void audioPlayer_dispatch_headAtEnd_lockPlay(CAudioPlayer*, bool, bool); + +void audioTrack_handleUnderrun_lockPlay(CAudioPlayer* ap); +void audioTrack_handleMarker_lockPlay(CAudioPlayer* ap); +void audioTrack_handleNewPos_lockPlay(CAudioPlayer* ap); +size_t audioTrack_handleMoreData_lockPlay(CAudioPlayer* ap, + const android::AudioTrack::Buffer& buffer); +//-------------------------------------------------------------------------------------------------- +namespace android { +class AudioTrackCallback : public AudioTrack::IAudioTrackCallback { + public: + AudioTrackCallback(CAudioPlayer * player) : mAp(player) {} + + size_t onMoreData(const AudioTrack::Buffer& buffer) override { + if (!android::CallbackProtector::enterCbIfOk(mAp->mCallbackProtector)) { + // it is not safe to enter the callback (the track is about to go away) + return buffer.size(); // duplicate existing behavior + } + size_t bytesCopied = audioTrack_handleMoreData_lockPlay(mAp, buffer); + mAp->mCallbackProtector->exitCb(); + return bytesCopied; + } + + void onUnderrun() override { + if (!android::CallbackProtector::enterCbIfOk(mAp->mCallbackProtector)) { + // it is not safe to enter the callback (the track is about to go away) + return; + } + audioTrack_handleUnderrun_lockPlay(mAp); + mAp->mCallbackProtector->exitCb(); + } + + void onLoopEnd([[maybe_unused]] int32_t loopsRemaining) override { + SL_LOGE("Encountered loop end for CAudioPlayer %p", mAp); + } + void onMarker([[maybe_unused]] uint32_t markerPosition) override { + if (!android::CallbackProtector::enterCbIfOk(mAp->mCallbackProtector)) { + // it is not safe to enter the callback (the track is about to go away) + return; + } + audioTrack_handleMarker_lockPlay(mAp); + mAp->mCallbackProtector->exitCb(); + } + + void onNewPos([[maybe_unused]] uint32_t newPos) override { + if (!android::CallbackProtector::enterCbIfOk(mAp->mCallbackProtector)) { + // it is not safe to enter the callback (the track is about to go away) + return; + } + audioTrack_handleNewPos_lockPlay(mAp); + mAp->mCallbackProtector->exitCb(); + } + void onBufferEnd() override { + SL_LOGE("Encountered buffer end for CAudioPlayer %p", mAp); + } + // Ignore + void onNewIAudioTrack() override {} + void onStreamEnd() override { + SL_LOGE("Encountered buffer end for CAudioPlayer %p", mAp); + } + void onNewTimestamp([[maybe_unused]] AudioTimestamp timestamp) { + SL_LOGE("Encountered write more data for CAudioPlayer %p", mAp); + } + size_t onCanWriteMoreData([[maybe_unused]] const AudioTrack::Buffer& buffer) { + SL_LOGE("Encountered write more data for CAudioPlayer %p", mAp); + return 0; + } + + private: + AudioTrackCallback(const AudioTrackCallback&) = delete; + AudioTrackCallback& operator=(const AudioTrackCallback&) = delete; + CAudioPlayer* const mAp; +}; +} // namespace android diff --git a/src/classes.h b/src/classes.h index fc64f25..c42fde4 100644 --- a/src/classes.h +++ b/src/classes.h @@ -21,6 +21,7 @@ #include "android/android_GenericPlayer.h" #include <media/TrackPlayerBase.h> #include <audiomanager/IAudioManager.h> +namespace android { class AudioRecordCallback; }; #endif // Class structures @@ -161,6 +162,7 @@ enum AndroidObjectType mAndroidObjType; android::sp<android::AudioRecord> mAudioRecord; android::sp<android::CallbackProtector> mCallbackProtector; + android::sp<android::AudioRecordCallback> mCallbackHandle; audio_source_t mRecordSource; SLuint32 mPerformanceMode; #endif diff --git a/src/itf/IEngine.cpp b/src/itf/IEngine.cpp index da5623c..be2d2b0 100644 --- a/src/itf/IEngine.cpp +++ b/src/itf/IEngine.cpp @@ -459,6 +459,7 @@ static SLresult IEngine_CreateAudioRecorder(SLEngineItf self, SLObjectItf *pReco // FIXME unnecessary once those fields are encapsulated in one class, rather // than a structure (void) new (&thiz->mAudioRecord) android::sp<android::AudioRecord>(); + (void) new (&thiz->mCallbackHandle) android::sp<android::AudioRecordCallback>(); (void) new (&thiz->mCallbackProtector) android::sp<android::CallbackProtector>(); thiz->mRecordSource = AUDIO_SOURCE_DEFAULT; |