本篇介绍
Oboe是一个开源的低延时音频库,本篇从使用到源码研究下Oboe的内容
使用Oboe
oboe的官网提供了相当详细的指导,使用起来也比较简单,以一个播放器为例:
代码语言:javascript复制#include <oboe/Oboe.h>
#include <math.h>
using namespace oboe;
class OboeSinePlayer: public oboe::AudioStreamDataCallback {
public:
virtual ~OboeSinePlayer() = default;
// Call this from Activity onResume()
int32_t startAudio() {
std::lock_guard<std::mutex> lock(mLock);
oboe::AudioStreamBuilder builder;
// The builder set methods can be chained for convenience.
Result result = builder.setSharingMode(oboe::SharingMode::Exclusive)
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
->setChannelCount(kChannelCount)
->setSampleRate(kSampleRate)
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium)
->setFormat(oboe::AudioFormat::Float)
->setDataCallback(this)
->openStream(mStream);
if (result != Result::OK) return (int32_t) result;
// Typically, start the stream after querying some stream information, as well as some input from the user
result = outStream->requestStart();
return (int32_t) result;
}
// Call this from Activity onPause()
void stopAudio() {
// Stop, close and delete in case not already closed.
std::lock_guard<std::mutex> lock(mLock);
if (mStream) {
mStream->stop();
mStream->close();
mStream.reset();
}
}
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override {
float *floatData = (float *) audioData;
for (int i = 0; i < numFrames; i) {
float sampleValue = kAmplitude * sinf(mPhase);
for (int j = 0; j < kChannelCount; j ) {
floatData[i * kChannelCount j] = sampleValue;
}
mPhase = mPhaseIncrement;
if (mPhase >= kTwoPi) mPhase -= kTwoPi;
}
return oboe::DataCallbackResult::Continue;
}
private:
std::mutex mLock;
std::shared_ptr<oboe::AudioStream> mStream;
// Stream params
static int constexpr kChannelCount = 2;
static int constexpr kSampleRate = 48000;
// Wave params, these could be instance variables in order to modify at runtime
static float constexpr kAmplitude = 0.5f;
static float constexpr kFrequency = 440;
static float constexpr kPI = M_PI;
static float constexpr kTwoPi = kPI * 2;
static double constexpr mPhaseIncrement = kFrequency * kTwoPi / (double) kSampleRate;
// Keeps track of where the wave is
float mPhase = 0.0;
};
源码介绍
Oboe的入口是AudioStreamBuilder的openStream:
代码语言:javascript复制Result AudioStreamBuilder::openStream(AudioStream **streamPP) {
auto result = isValidConfig();
if (result != Result::OK) {
LOGW("%s() invalid config %d", __func__, result);
return result;
}
LOGI("%s() %s -------- %s --------",
__func__, getDirection() == Direction::Input ? "INPUT" : "OUTPUT", getVersionText());
if (streamPP == nullptr) {
return Result::ErrorNull;
}
*streamPP = nullptr;
AudioStream *streamP = nullptr;
// Maybe make a FilterInputStream.
AudioStreamBuilder childBuilder(*this);
// Check need for conversion and modify childBuilder for optimal stream.
bool conversionNeeded = QuirksManager::getInstance().isConversionNeeded(*this, childBuilder); // 如果允许调整的话,内部会针对通道数,采样率等进行调整
// Do we need to make a child stream and convert.
// 这儿是一个递归,针对需要调整的场景,可以再创建一个自己去构造,因为childBuilder已经调整了,因此不会陷入无限递归
if (conversionNeeded) {
AudioStream *tempStream;
result = childBuilder.openStream(&tempStream);
if (result != Result::OK) {
return result;
}
if (isCompatible(*tempStream)) {
// The child stream would work as the requested stream so we can just use it directly.
*streamPP = tempStream;
return result;
} else {
AudioStreamBuilder parentBuilder = *this;
// Build a stream that is as close as possible to the childStream.
if (getFormat() == oboe::AudioFormat::Unspecified) {
parentBuilder.setFormat(tempStream->getFormat());
}
if (getChannelCount() == oboe::Unspecified) {
parentBuilder.setChannelCount(tempStream->getChannelCount());
}
if (getSampleRate() == oboe::Unspecified) {
parentBuilder.setSampleRate(tempStream->getSampleRate());
}
if (getFramesPerDataCallback() == oboe::Unspecified) {
parentBuilder.setFramesPerCallback(tempStream->getFramesPerDataCallback());
}
// Use childStream in a FilterAudioStream.
LOGI("%s() create a FilterAudioStream for data conversion.", __func__);
FilterAudioStream *filterStream = new FilterAudioStream(parentBuilder, tempStream);
result = filterStream->configureFlowGraph();
if (result != Result::OK) {
filterStream->close();
delete filterStream;
// Just open streamP the old way.
} else {
streamP = static_cast<AudioStream *>(filterStream);
}
}
}
if (streamP == nullptr) {
streamP = build();
if (streamP == nullptr) {
return Result::ErrorNull;
}
}
// 这个逻辑是oboe的设备兼容性方案
// If MMAP has a problem in this case then disable it temporarily.
bool wasMMapOriginallyEnabled = AAudioExtensions::getInstance().isMMapEnabled();
bool wasMMapTemporarilyDisabled = false;
if (wasMMapOriginallyEnabled) {
bool isMMapSafe = QuirksManager::getInstance().isMMapSafe(childBuilder);
if (!isMMapSafe) {
AAudioExtensions::getInstance().setMMapEnabled(false);
wasMMapTemporarilyDisabled = true;
}
}
result = streamP->open();
if (wasMMapTemporarilyDisabled) {
AAudioExtensions::getInstance().setMMapEnabled(wasMMapOriginallyEnabled); // restore original
}
if (result == Result::OK) {
int32_t optimalBufferSize = -1;
// Use a reasonable default buffer size.
if (streamP->getDirection() == Direction::Input) {
// For input, small size does not improve latency because the stream is usually
// run close to empty. And a low size can result in XRuns so always use the maximum.
optimalBufferSize = streamP->getBufferCapacityInFrames();
} else if (streamP->getPerformanceMode() == PerformanceMode::LowLatency
&& streamP->getDirection() == Direction::Output) { // Output check is redundant.
optimalBufferSize = streamP->getFramesPerBurst() *
kBufferSizeInBurstsForLowLatencyStreams;
}
if (optimalBufferSize >= 0) {
auto setBufferResult = streamP->setBufferSizeInFrames(optimalBufferSize);
if (!setBufferResult) {
LOGW("Failed to setBufferSizeInFrames(%d). Error was %s",
optimalBufferSize,
convertToText(setBufferResult.error()));
}
}
*streamPP = streamP;
} else {
delete streamP;
}
return result;
}
这儿完成的事情如下:
- 是否需要调整参数,如果需要调整,就再创建一个自己去OpenStream
- 创建好Stream后,进行兼容性方案检查,QuirksManager 可以针对不同的芯片制定了对应的兼容方案,业务也可以好好利用这块能力,这就是oboe兼容性好的秘密
- 调用open打开流。
对于android 有opensl和aaudio两种api,先看下类结构:
WechatIMG7515.jpeg
从类图中可以看到如下几个信息:
- Oboe 为了低延时和更好的兼容性,会根据需要再决策一次硬件参数,如果和设置参数不一样,那么内部就会进行重采样
- QuirksManager 内部针对三星和高通芯片进行了兼容性处理
接下来看下opensl的open,以采集为例:
代码语言:javascript复制Result AudioInputStreamOpenSLES::open() {
...
// 初始化 opensl engine
Result oboeResult = AudioStreamOpenSLES::open();
if (Result::OK != oboeResult) return oboeResult;
SLuint32 bitsPerSample = static_cast<SLuint32>(getBytesPerSample() * kBitsPerByte);
// configure audio sink
mBufferQueueLength = calculateOptimalBufferQueueLength(); // 计算最佳buffer,需要至少是2倍的回调数据
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType
static_cast<SLuint32>(mBufferQueueLength)}; // numBuffers
// Define the audio data format.
SLDataFormat_PCM format_pcm = {
SL_DATAFORMAT_PCM, // formatType
static_cast<SLuint32>(mChannelCount), // numChannels
static_cast<SLuint32>(mSampleRate * kMillisPerSecond), // milliSamplesPerSec
bitsPerSample, // bitsPerSample
bitsPerSample, // containerSize;
channelCountToChannelMask(mChannelCount), // channelMask
getDefaultByteOrder(),
};
SLDataSink audioSink = {&loc_bufq, &format_pcm};
/**
* API 23 (Marshmallow) introduced support for floating-point data representation and an
* extended data format type: SLAndroidDataFormat_PCM_EX for recording streams (playback streams
* got this in API 21). If running on API 23 use this newer format type, creating it from our
* original format.
*/
SLAndroidDataFormat_PCM_EX format_pcm_ex;
if (getSdkVersion() >= __ANDROID_API_M__) {
SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat());
// Fill in the format structure.
format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation);
// Use in place of the previous format.
audioSink.pFormat = &format_pcm_ex;
}
// configure audio source
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE,
SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT,
NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
SLresult result = EngineOpenSLES::getInstance().createAudioRecorder(&mObjectInterface,
&audioSrc,
&audioSink);
if (SL_RESULT_SUCCESS != result) {
LOGE("createAudioRecorder() result:%s", getSLErrStr(result));
goto error;
}
// Configure the stream.
result = (*mObjectInterface)->GetInterface(mObjectInterface,
SL_IID_ANDROIDCONFIGURATION,
&configItf);
if (SL_RESULT_SUCCESS != result) {
LOGW("%s() GetInterface(SL_IID_ANDROIDCONFIGURATION) failed with %s",
__func__, getSLErrStr(result));
} else {
if (getInputPreset() == InputPreset::VoicePerformance) {
LOGD("OpenSL ES does not support InputPreset::VoicePerformance. Use VoiceRecognition.");
mInputPreset = InputPreset::VoiceRecognition;
}
SLuint32 presetValue = OpenSLES_convertInputPreset(getInputPreset());
result = (*configItf)->SetConfiguration(configItf,
SL_ANDROID_KEY_RECORDING_PRESET,
&presetValue,
sizeof(SLuint32));
if (SL_RESULT_SUCCESS != result
&& presetValue != SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION) {
presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
LOGD("Setting InputPreset %d failed. Using VoiceRecognition instead.", getInputPreset());
mInputPreset = InputPreset::VoiceRecognition;
(*configItf)->SetConfiguration(configItf,
SL_ANDROID_KEY_RECORDING_PRESET,
&presetValue,
sizeof(SLuint32));
}
result = configurePerformanceMode(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}
}
result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("Realize recorder object result:%s", getSLErrStr(result));
goto error;
}
result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_RECORD, &mRecordInterface);
if (SL_RESULT_SUCCESS != result) {
LOGE("GetInterface RECORD result:%s", getSLErrStr(result));
goto error;
}
result = finishCommonOpen(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}
setState(StreamState::Open);
return Result::OK;
error:
close(); // Clean up various OpenSL objects and prevent resource leaks.
return Result::ErrorInternal; // TODO convert error from SLES to OBOE
}
这儿主要就是使用OpenSL 接口创建采集,这儿的调用就会走到lbOpenSLES里面,比如EngineOpenSLES::getInstance().createAudioRecorder对应的实现就是:
代码语言:javascript复制static SLresult IEngine_CreateAudioRecorder(SLEngineItf self, SLObjectItf *pRecorder,
SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
#if (USE_PROFILES & USE_PROFILES_OPTIONAL) || defined(ANDROID)
if (NULL == pRecorder) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pRecorder = NULL;
unsigned exposedMask;
const ClassTable *pCAudioRecorder_class = objectIDtoClass(SL_OBJECTID_AUDIORECORDER);
if (NULL == pCAudioRecorder_class) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
result = checkInterfaces(pCAudioRecorder_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, NULL);
}
if (SL_RESULT_SUCCESS == result) {
// Construct our new AudioRecorder instance
CAudioRecorder *thiz = (CAudioRecorder *) construct(pCAudioRecorder_class, exposedMask,
self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
do {
// Initialize fields not associated with any interface
// Default data source in case of failure in checkDataSource
thiz->mDataSource.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSource.mFormat.mFormatType = SL_DATAFORMAT_NULL;
// Default data sink in case of failure in checkDataSink
thiz->mDataSink.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSink.mFormat.mFormatType = SL_DATAFORMAT_NULL;
// These fields are set to real values by
// android_audioRecorder_checkSourceSink. Note that the data sink is
// always PCM buffer queue, so we know the channel count and sample rate early.
thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
#ifdef ANDROID
// placement new (explicit constructor)
// 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->mCallbackProtector)
android::sp<android::CallbackProtector>();
thiz->mRecordSource = AUDIO_SOURCE_DEFAULT;
#endif
// Check the source and sink parameters, and make a local copy of all parameters
result = checkDataSource("pAudioSrc", pAudioSrc, &thiz->mDataSource,
DATALOCATOR_MASK_IODEVICE, DATAFORMAT_MASK_NULL);
if (SL_RESULT_SUCCESS != result) {
break;
}
result = checkDataSink("pAudioSnk", pAudioSnk, &thiz->mDataSink,
DATALOCATOR_MASK_URI
#ifdef ANDROID
| DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE
#endif
, DATAFORMAT_MASK_MIME | DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX
);
if (SL_RESULT_SUCCESS != result) {
break;
}
// It would be unsafe to ever refer to the application pointers again
pAudioSrc = NULL;
pAudioSnk = NULL;
// check the audio source and sink parameters against platform support
#ifdef ANDROID
result = android_audioRecorder_checkSourceSink(thiz);
if (SL_RESULT_SUCCESS != result) {
SL_LOGE("Cannot create AudioRecorder: invalid source or sink");
break;
}
#endif
#ifdef ANDROID
// Allocate memory for buffer queue
SLuint32 locatorType = thiz->mDataSink.mLocator.mLocatorType;
if (locatorType == SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE) {
thiz->mBufferQueue.mNumBuffers =
thiz->mDataSink.mLocator.mBufferQueue.numBuffers;
// inline allocation of circular Buffer Queue mArray, up to a typical max
if (BUFFER_HEADER_TYPICAL >= thiz->mBufferQueue.mNumBuffers) {
thiz->mBufferQueue.mArray = thiz->mBufferQueue.mTypical;
} else {
// Avoid possible integer overflow during multiplication; this arbitrary
// maximum is big enough to not interfere with real applications, but
// small enough to not overflow.
if (thiz->mBufferQueue.mNumBuffers >= 256) {
result = SL_RESULT_MEMORY_FAILURE;
break;
}
thiz->mBufferQueue.mArray = (BufferHeader *) malloc((thiz->mBufferQueue.
mNumBuffers 1) * sizeof(BufferHeader));
if (NULL == thiz->mBufferQueue.mArray) {
result = SL_RESULT_MEMORY_FAILURE;
break;
}
}
thiz->mBufferQueue.mFront = thiz->mBufferQueue.mArray;
thiz->mBufferQueue.mRear = thiz->mBufferQueue.mArray;
}
#endif
// platform-specific initialization
#ifdef ANDROID
android_audioRecorder_create(thiz);
#endif
} while (0);
if (SL_RESULT_SUCCESS != result) {
IObject_Destroy(&thiz->mObject.mItf);
} else {
IObject_Publish(&thiz->mObject);
// return the new audio recorder object
*pRecorder = &thiz->mObject.mItf;
}
}
}
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
在libOpenSLES内部有一个结构体SLEngineItf_,包含了不仅音频,还有视频等的操作方法,这儿只关注IEngine_CreateAudioRecorder就好,这儿的construct就会创建一个CAudioRecorder,这个结构内容如下:
代码语言:javascript复制/*typedef*/ struct CAudioRecorder_struct {
// mandated interfaces
IObject mObject;
#ifdef ANDROID
#define INTERFACES_AudioRecorder 14 // see MPH_to_AudioRecorder in MPH_to.c for list of interfaces
#else
#define INTERFACES_AudioRecorder 9 // see MPH_to_AudioRecorder in MPH_to.c for list of interfaces
#endif
SLuint8 mInterfaceStates2[INTERFACES_AudioRecorder - INTERFACES_Default];
IDynamicInterfaceManagement mDynamicInterfaceManagement;
IRecord mRecord;
IAudioEncoder mAudioEncoder;
// optional interfaces
IBassBoost mBassBoost;
IDynamicSource mDynamicSource;
IEqualizer mEqualizer;
IVisualization mVisualization;
IVolume mVolume;
#ifdef ANDROID
IBufferQueue mBufferQueue;
IAndroidConfiguration mAndroidConfiguration;
IAndroidAcousticEchoCancellation mAcousticEchoCancellation;
IAndroidAutomaticGainControl mAutomaticGainControl;
IAndroidNoiseSuppression mNoiseSuppression;
#endif
// remaining are per-instance private fields not associated with an interface
DataLocatorFormat mDataSource;
DataLocatorFormat mDataSink;
// cached data for this instance
SLuint8 mNumChannels; // initially UNKNOWN_NUMCHANNELS, then const once it is known,
// range 1 <= x <= FCC_8
SLuint32 mSampleRateMilliHz;// initially UNKNOWN_SAMPLERATE, then const once it is known
// implementation-specific data for this instance
#ifdef ANDROID
// FIXME consolidate the next several variables into ARecorder class to avoid placement new
enum AndroidObjectType mAndroidObjType;
android::sp<android::AudioRecord> mAudioRecord;
android::sp<android::CallbackProtector> mCallbackProtector;
audio_source_t mRecordSource;
SLuint32 mPerformanceMode;
#endif
} /*CAudioRecorder*/;
这里面包含了采集参数,也有采集对象,接下来就会创建android::AudioRecord并赋值给mAudioRecord,这时候采集就创建好了。
再看下播放的创建:
代码语言:javascript复制Result AudioOutputStreamOpenSLES::open() {
...
// 初始化 opensl
Result oboeResult = AudioStreamOpenSLES::open();
if (Result::OK != oboeResult) return oboeResult;
// 创建 output mixer
SLresult result = OutputMixerOpenSL::getInstance().open();
if (SL_RESULT_SUCCESS != result) {
AudioStreamOpenSLES::close();
return Result::ErrorInternal;
}
SLuint32 bitsPerSample = static_cast<SLuint32>(getBytesPerSample() * kBitsPerByte);
// configure audio source
mBufferQueueLength = calculateOptimalBufferQueueLength();
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType
static_cast<SLuint32>(mBufferQueueLength)}; // numBuffers
// Define the audio data format.
SLDataFormat_PCM format_pcm = {
SL_DATAFORMAT_PCM, // formatType
static_cast<SLuint32>(mChannelCount), // numChannels
static_cast<SLuint32>(mSampleRate * kMillisPerSecond), // milliSamplesPerSec
bitsPerSample, // bitsPerSample
bitsPerSample, // containerSize;
channelCountToChannelMask(mChannelCount), // channelMask
getDefaultByteOrder(),
};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
/**
* API 21 (Lollipop) introduced support for floating-point data representation and an extended
* data format type: SLAndroidDataFormat_PCM_EX. If running on API 21 use this newer format
* type, creating it from our original format.
*/
SLAndroidDataFormat_PCM_EX format_pcm_ex;
if (getSdkVersion() >= __ANDROID_API_L__) {
SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat());
// Fill in the format structure.
format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation);
// Use in place of the previous format.
audioSrc.pFormat = &format_pcm_ex;
}
// 创建player
result = OutputMixerOpenSL::getInstance().createAudioPlayer(&mObjectInterface,
&audioSrc);
if (SL_RESULT_SUCCESS != result) {
LOGE("createAudioPlayer() result:%s", getSLErrStr(result));
goto error;
}
// Configure the stream.
result = (*mObjectInterface)->GetInterface(mObjectInterface,
SL_IID_ANDROIDCONFIGURATION,
(void *)&configItf);
if (SL_RESULT_SUCCESS != result) {
LOGW("%s() GetInterface(SL_IID_ANDROIDCONFIGURATION) failed with %s",
__func__, getSLErrStr(result));
} else {
result = configurePerformanceMode(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}
SLuint32 presetValue = OpenSLES_convertOutputUsage(getUsage());
result = (*configItf)->SetConfiguration(configItf,
SL_ANDROID_KEY_STREAM_TYPE,
&presetValue,
sizeof(presetValue));
if (SL_RESULT_SUCCESS != result) {
goto error;
}
}
result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("Realize player object result:%s", getSLErrStr(result));
goto error;
}
result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_PLAY, &mPlayInterface);
if (SL_RESULT_SUCCESS != result) {
LOGE("GetInterface PLAY result:%s", getSLErrStr(result));
goto error;
}
result = finishCommonOpen(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}
setState(StreamState::Open);
return Result::OK;
error:
close(); // Clean up various OpenSL objects and prevent resource leaks.
return Result::ErrorInternal; // TODO convert error from SLES to OBOE
}
这儿也是通过Opensles接口创建的player,实现是:
代码语言:javascript复制static SLresult IEngine_CreateAudioPlayer(SLEngineItf self, SLObjectItf *pPlayer,
SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces,
const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
SL_ENTER_INTERFACE
if (NULL == pPlayer) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
*pPlayer = NULL;
unsigned exposedMask, requiredMask;
const ClassTable *pCAudioPlayer_class = objectIDtoClass(SL_OBJECTID_AUDIOPLAYER);
assert(NULL != pCAudioPlayer_class);
result = checkInterfaces(pCAudioPlayer_class, numInterfaces,
pInterfaceIds, pInterfaceRequired, &exposedMask, &requiredMask);
if (SL_RESULT_SUCCESS == result) {
// Construct our new AudioPlayer instance
CAudioPlayer *thiz = (CAudioPlayer *) construct(pCAudioPlayer_class, exposedMask, self);
if (NULL == thiz) {
result = SL_RESULT_MEMORY_FAILURE;
} else {
do {
// Initialize private fields not associated with an interface
// Default data source in case of failure in checkDataSource
thiz->mDataSource.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSource.mFormat.mFormatType = SL_DATAFORMAT_NULL;
// Default data sink in case of failure in checkDataSink
thiz->mDataSink.mLocator.mLocatorType = SL_DATALOCATOR_NULL;
thiz->mDataSink.mFormat.mFormatType = SL_DATAFORMAT_NULL;
// Default is no per-channel mute or solo
thiz->mMuteMask = 0;
thiz->mSoloMask = 0;
// Will be set soon for PCM buffer queues, or later by platform-specific code
// during Realize or Prefetch
thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
// More default values, in case destructor needs to be called early
thiz->mDirectLevel = 0; // no attenuation
#ifdef USE_OUTPUTMIXEXT
thiz->mTrack = NULL;
thiz->mGains[0] = 1.0f;
thiz->mGains[1] = 1.0f;
thiz->mDestroyRequested = SL_BOOLEAN_FALSE;
#endif
#ifdef USE_SNDFILE
thiz->mSndFile.mPathname = NULL;
thiz->mSndFile.mSNDFILE = NULL;
memset(&thiz->mSndFile.mSfInfo, 0, sizeof(SF_INFO));
memset(&thiz->mSndFile.mMutex, 0, sizeof(pthread_mutex_t));
thiz->mSndFile.mEOF = SL_BOOLEAN_FALSE;
thiz->mSndFile.mWhich = 0;
memset(thiz->mSndFile.mBuffer, 0, sizeof(thiz->mSndFile.mBuffer));
#endif
#ifdef ANDROID
// placement new (explicit constructor)
// FIXME unnecessary once those fields are encapsulated in one class, rather
// than a structure
//###(void) new (&thiz->mAudioTrack) android::sp<android::AudioTrack>();
(void) new (&thiz->mTrackPlayer) android::sp<android::TrackPlayerBase>();
(void) new (&thiz->mCallbackProtector)
android::sp<android::CallbackProtector>();
(void) new (&thiz->mAuxEffect) android::sp<android::AudioEffect>();
(void) new (&thiz->mAPlayer) android::sp<android::GenericPlayer>();
// Android-specific POD fields are initialized in android_audioPlayer_create,
// and assume calloc or memset 0 during allocation
#endif
// Check the source and sink parameters against generic constraints,
// and make a local copy of all parameters in case other application threads
// change memory concurrently.
result = checkDataSource("pAudioSrc", pAudioSrc, &thiz->mDataSource,
DATALOCATOR_MASK_URI | DATALOCATOR_MASK_ADDRESS |
DATALOCATOR_MASK_BUFFERQUEUE
#ifdef ANDROID
| DATALOCATOR_MASK_ANDROIDFD | DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE
| DATALOCATOR_MASK_ANDROIDBUFFERQUEUE
#endif
, DATAFORMAT_MASK_MIME | DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX);
if (SL_RESULT_SUCCESS != result) {
break;
}
result = checkDataSink("pAudioSnk", pAudioSnk, &thiz->mDataSink,
DATALOCATOR_MASK_OUTPUTMIX // for playback
#ifdef ANDROID
| DATALOCATOR_MASK_ANDROIDSIMPLEBUFFERQUEUE // for decode to a BQ
| DATALOCATOR_MASK_BUFFERQUEUE // for decode to a BQ
#endif
, DATAFORMAT_MASK_NULL
#ifdef ANDROID
| DATAFORMAT_MASK_PCM | DATAFORMAT_MASK_PCM_EX // for decode to PCM
#endif
);
if (SL_RESULT_SUCCESS != result) {
break;
}
// It would be unsafe to ever refer to the application pointers again
pAudioSrc = NULL;
pAudioSnk = NULL;
// Check that the requested interfaces are compatible with data source and sink
result = checkSourceSinkVsInterfacesCompatibility(&thiz->mDataSource,
&thiz->mDataSink, pCAudioPlayer_class, requiredMask);
if (SL_RESULT_SUCCESS != result) {
break;
}
// copy the buffer queue count from source locator (for playback) / from the
// sink locator (for decode on ANDROID build) to the buffer queue interface
// we have already range-checked the value down to a smaller width
SLuint16 nbBuffers = 0;
bool usesAdvancedBufferHeaders = false;
bool usesSimpleBufferQueue = false;
// creating an AudioPlayer which decodes AAC ADTS buffers to a PCM buffer queue
// will cause usesAdvancedBufferHeaders and usesSimpleBufferQueue to be true
switch (thiz->mDataSource.mLocator.mLocatorType) {
case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
usesSimpleBufferQueue = true;
nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mBufferQueue.numBuffers;
assert(SL_DATAFORMAT_PCM == thiz->mDataSource.mFormat.mFormatType
|| SL_ANDROID_DATAFORMAT_PCM_EX
== thiz->mDataSource.mFormat.mFormatType);
thiz->mNumChannels = thiz->mDataSource.mFormat.mPCM.numChannels;
thiz->mSampleRateMilliHz = thiz->mDataSource.mFormat.mPCM.samplesPerSec;
break;
#ifdef ANDROID
case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
usesAdvancedBufferHeaders = true;
nbBuffers = (SLuint16) thiz->mDataSource.mLocator.mABQ.numBuffers;
thiz->mAndroidBufferQueue.mNumBuffers = nbBuffers;
break;
#endif
default:
nbBuffers = 0;
break;
}
#ifdef ANDROID
switch (thiz->mDataSink.mLocator.mLocatorType) {
case SL_DATALOCATOR_BUFFERQUEUE:
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
usesSimpleBufferQueue = true;
nbBuffers = thiz->mDataSink.mLocator.mBufferQueue.numBuffers;
assert(SL_DATAFORMAT_PCM == thiz->mDataSink.mFormat.mFormatType
|| SL_ANDROID_DATAFORMAT_PCM_EX
== thiz->mDataSink.mFormat.mFormatType);
// FIXME The values specified by the app are meaningless. We get the
// real values from the decoder. But the data sink checks currently require
// that the app specify these useless values. Needs doc/fix.
// Instead use the "unknown" values, as needed by prepare completion.
// thiz->mNumChannels = thiz->mDataSink.mFormat.mPCM.numChannels;
// thiz->mSampleRateMilliHz = thiz->mDataSink.mFormat.mPCM.samplesPerSec;
thiz->mNumChannels = UNKNOWN_NUMCHANNELS;
thiz->mSampleRateMilliHz = UNKNOWN_SAMPLERATE;
break;
default:
// leave nbBuffers unchanged
break;
}
#endif
thiz->mBufferQueue.mNumBuffers = nbBuffers;
// check the audio source and sink parameters against platform support
#ifdef ANDROID
result = android_audioPlayer_checkSourceSink(thiz);
if (SL_RESULT_SUCCESS != result) {
break;
}
#endif
#ifdef USE_SNDFILE
result = SndFile_checkAudioPlayerSourceSink(thiz);
if (SL_RESULT_SUCCESS != result) {
break;
}
#endif
#ifdef USE_OUTPUTMIXEXT
result = IOutputMixExt_checkAudioPlayerSourceSink(thiz);
if (SL_RESULT_SUCCESS != result) {
break;
}
#endif
// Allocate memory for buffer queue
if (usesAdvancedBufferHeaders) {
#ifdef ANDROID
// locator is SL_DATALOCATOR_ANDROIDBUFFERQUEUE
result = initializeAndroidBufferQueueMembers(thiz);
#else
assert(false);
#endif
}
if (usesSimpleBufferQueue) {
// locator is SL_DATALOCATOR_BUFFERQUEUE
// or SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
result = initializeBufferQueueMembers(thiz);
}
// used to store the data source of our audio player
thiz->mDynamicSource.mDataSource = &thiz->mDataSource.u.mSource;
// platform-specific initialization
#ifdef ANDROID
android_audioPlayer_create(thiz);
#endif
} while (0);
if (SL_RESULT_SUCCESS != result) {
IObject_Destroy(&thiz->mObject.mItf);
} else {
IObject_Publish(&thiz->mObject);
// return the new audio player object
*pPlayer = &thiz->mObject.mItf;
}
}
}
}
SL_LEAVE_INTERFACE
}
在android_audioPlayer_create中会创建player,不过这时候的player是没有初始化的:
代码语言:javascript复制void android_audioPlayer_create(CAudioPlayer *pAudioPlayer) {
// pAudioPlayer->mAndroidObjType has been set in android_audioPlayer_checkSourceSink()
// and if it was == INVALID_TYPE, then IEngine_CreateAudioPlayer would never call us
assert(INVALID_TYPE != pAudioPlayer->mAndroidObjType);
// These initializations are in the same order as the field declarations in classes.h
// FIXME Consolidate initializations (many of these already in IEngine_CreateAudioPlayer)
// mAndroidObjType: see above comment
pAudioPlayer->mAndroidObjState = ANDROID_UNINITIALIZED;
pAudioPlayer->mSessionId = (audio_session_t) android::AudioSystem::newAudioUniqueId(
AUDIO_UNIQUE_ID_USE_SESSION);
pAudioPlayer->mPIId = PLAYER_PIID_INVALID;
// placeholder: not necessary yet as session ID lifetime doesn't extend beyond player
// android::AudioSystem::acquireAudioSessionId(pAudioPlayer->mSessionId);
pAudioPlayer->mStreamType = ANDROID_DEFAULT_OUTPUT_STREAM_TYPE;
pAudioPlayer->mPerformanceMode = ANDROID_PERFORMANCE_MODE_DEFAULT;
// mAudioTrack lifecycle is handled through mTrackPlayer
pAudioPlayer->mTrackPlayer = new android::TrackPlayerBase();
assert(pAudioPlayer->mTrackPlayer != 0);
pAudioPlayer->mCallbackProtector = new android::CallbackProtector();
// mAPLayer
// mAuxEffect
pAudioPlayer->mAuxSendLevel = 0;
pAudioPlayer->mAmplFromDirectLevel = 1.0f; // matches initial mDirectLevel value
pAudioPlayer->mDeferredStart = false;
// This section re-initializes interface-specific fields that
// can be set or used regardless of whether the interface is
// exposed on the AudioPlayer or not
switch (pAudioPlayer->mAndroidObjType) {
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
pAudioPlayer->mPlaybackRate.mMinRate = AUDIOTRACK_MIN_PLAYBACKRATE_PERMILLE;
pAudioPlayer->mPlaybackRate.mMaxRate = AUDIOTRACK_MAX_PLAYBACKRATE_PERMILLE;
break;
case AUDIOPLAYER_FROM_URIFD:
pAudioPlayer->mPlaybackRate.mMinRate = MEDIAPLAYER_MIN_PLAYBACKRATE_PERMILLE;
pAudioPlayer->mPlaybackRate.mMaxRate = MEDIAPLAYER_MAX_PLAYBACKRATE_PERMILLE;
break;
default:
// use the default range
break;
}
}
对于TrackPlayerBase,这是一个壳子,需要真正的AudioTrack才可以,那是啥时候可以初始化的呢?在open中有一个Opensl interface的Realize调用,对于AuidoPlayer,实现如下:
代码语言:javascript复制SLresult CAudioPlayer_Realize(void *self, SLboolean async)
{
CAudioPlayer *thiz = (CAudioPlayer *) self;
SLresult result = SL_RESULT_SUCCESS;
#ifdef ANDROID
result = android_audioPlayer_realize(thiz, async);
#endif
#ifdef USE_SNDFILE
result = SndFile_Realize(thiz);
#endif
// At this point the channel count and sample rate might still be unknown,
// depending on the data source and the platform implementation.
// If they are unknown here, then they will be determined during prefetch.
return result;
}
再看下android_audioPlayer_realize:
代码语言:javascript复制SLresult android_audioPlayer_realize(CAudioPlayer *pAudioPlayer, SLboolean async) {
SLresult result = SL_RESULT_SUCCESS;
SL_LOGV("Realize pAudioPlayer=%p", pAudioPlayer);
AudioPlayback_Parameters app;
app.sessionId = pAudioPlayer->mSessionId;
app.streamType = pAudioPlayer->mStreamType;
switch (pAudioPlayer->mAndroidObjType) {
//-----------------------------------
// AudioTrack
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE: {
// initialize platform-specific CAudioPlayer fields
SLDataFormat_PCM *df_pcm = (SLDataFormat_PCM *)
pAudioPlayer->mDynamicSource.mDataSource->pFormat;
uint32_t sampleRate = sles_to_android_sampleRate(df_pcm->samplesPerSec);
audio_channel_mask_t channelMask;
channelMask = sles_to_audio_output_channel_mask(df_pcm->channelMask);
// To maintain backward compatibility with previous releases, ignore
// channel masks that are not indexed.
if (channelMask == AUDIO_CHANNEL_INVALID
|| audio_channel_mask_get_representation(channelMask)
== AUDIO_CHANNEL_REPRESENTATION_POSITION) {
channelMask = audio_channel_out_mask_from_count(df_pcm->numChannels);
SL_LOGI("Emulating old channel mask behavior "
"(ignoring positional mask %#x, using default mask %#x based on "
"channel count of %d)", df_pcm->channelMask, channelMask,
df_pcm->numChannels);
}
SL_LOGV("AudioPlayer: mapped SLES channel mask %#x to android channel mask %#x",
df_pcm->channelMask,
channelMask);
checkAndSetPerformanceModePre(pAudioPlayer);
audio_output_flags_t policy;
switch (pAudioPlayer->mPerformanceMode) {
case ANDROID_PERFORMANCE_MODE_POWER_SAVING:
policy = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
break;
case ANDROID_PERFORMANCE_MODE_NONE:
policy = AUDIO_OUTPUT_FLAG_NONE;
break;
case ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS:
policy = AUDIO_OUTPUT_FLAG_FAST;
break;
case ANDROID_PERFORMANCE_MODE_LATENCY:
default:
policy = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_RAW);
break;
}
int32_t notificationFrames;
if ((policy & AUDIO_OUTPUT_FLAG_FAST) != 0) {
// negative notificationFrames is the number of notifications (sub-buffers) per track
// buffer for details see the explanation at frameworks/av/include/media/AudioTrack.h
notificationFrames = -pAudioPlayer->mBufferQueue.mNumBuffers;
} else {
notificationFrames = 0;
}
android::AudioTrack* pat = new android::AudioTrack(
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
notificationFrames, // see comment above
pAudioPlayer->mSessionId);
// Set it here so it can be logged by the destructor if the open failed.
pat->setCallerName(ANDROID_OPENSLES_CALLER_NAME);
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));
// update performance mode according to actual flags granted to AudioTrack
checkAndSetPerformanceModePost(pAudioPlayer);
// initialize platform-independent CAudioPlayer fields
pAudioPlayer->mNumChannels = df_pcm->numChannels;
pAudioPlayer->mSampleRateMilliHz = df_pcm->samplesPerSec; // Note: bad field name in SL ES
// This use case does not have a separate "prepare" step
pAudioPlayer->mAndroidObjState = ANDROID_READY;
// If there is a JavaAudioRoutingProxy associated with this player, hook it up...
JNIEnv* j_env = NULL;
jclass clsAudioTrack = NULL;
jmethodID midRoutingProxy_connect = NULL;
if (pAudioPlayer->mAndroidConfiguration.mRoutingProxy != NULL &&
(j_env = android::AndroidRuntime::getJNIEnv()) != NULL &&
(clsAudioTrack = j_env->FindClass("android/media/AudioTrack")) != NULL &&
(midRoutingProxy_connect =
j_env->GetMethodID(clsAudioTrack, "deferred_connect", "(J)V")) != NULL) {
j_env->ExceptionClear();
j_env->CallVoidMethod(pAudioPlayer->mAndroidConfiguration.mRoutingProxy,
midRoutingProxy_connect,
(jlong)pAudioPlayer->mTrackPlayer->mAudioTrack.get());
if (j_env->ExceptionCheck()) {
SL_LOGE("Java exception releasing player routing object.");
result = SL_RESULT_INTERNAL_ERROR;
pAudioPlayer->mTrackPlayer->mAudioTrack.clear();
return result;
}
}
}
break;
//-----------------------------------
// MediaPlayer
case AUDIOPLAYER_FROM_URIFD: {
pAudioPlayer->mAPlayer = new android::LocAVPlayer(&app, false /*hasVideo*/);
pAudioPlayer->mAPlayer->init(sfplayer_handlePrefetchEvent,
(void*)pAudioPlayer /*notifUSer*/);
switch (pAudioPlayer->mDataSource.mLocator.mLocatorType) {
case SL_DATALOCATOR_URI: {
// The legacy implementation ran Stagefright within the application process, and
// so allowed local pathnames specified by URI that were openable by
// the application but were not openable by mediaserver.
// The current implementation runs Stagefright (mostly) within mediaserver,
// which runs as a different UID and likely a different current working directory.
// For backwards compatibility with any applications which may have relied on the
// previous behavior, we convert an openable file URI into an FD.
// Note that unlike SL_DATALOCATOR_ANDROIDFD, this FD is owned by us
// and so we close it as soon as we've passed it (via Binder dup) to mediaserver.
const char *uri = (const char *)pAudioPlayer->mDataSource.mLocator.mURI.URI;
if (!isDistantProtocol(uri)) {
// don't touch the original uri, we may need it later
const char *pathname = uri;
// skip over an optional leading file:// prefix
if (!strncasecmp(pathname, "file://", 7)) {
pathname = 7;
}
// attempt to open it as a file using the application's credentials
int fd = ::open(pathname, O_RDONLY);
if (fd >= 0) {
// if open is successful, then check to see if it's a regular file
struct stat statbuf;
if (!::fstat(fd, &statbuf) && S_ISREG(statbuf.st_mode)) {
// treat similarly to an FD data locator, but
// let setDataSource take responsibility for closing fd
pAudioPlayer->mAPlayer->setDataSource(fd, 0, statbuf.st_size, true);
break;
}
// we were able to open it, but it's not a file, so let mediaserver try
(void) ::close(fd);
}
}
// if either the URI didn't look like a file, or open failed, or not a file
pAudioPlayer->mAPlayer->setDataSource(uri);
} break;
case SL_DATALOCATOR_ANDROIDFD: {
int64_t offset = (int64_t)pAudioPlayer->mDataSource.mLocator.mFD.offset;
pAudioPlayer->mAPlayer->setDataSource(
(int)pAudioPlayer->mDataSource.mLocator.mFD.fd,
offset == SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE ?
(int64_t)PLAYER_FD_FIND_FILE_SIZE : offset,
(int64_t)pAudioPlayer->mDataSource.mLocator.mFD.length);
}
break;
default:
SL_LOGE(ERROR_PLAYERREALIZE_UNKNOWN_DATASOURCE_LOCATOR);
break;
}
if (pAudioPlayer->mObject.mEngine->mAudioManager == 0) {
SL_LOGE("AudioPlayer realize: no audio service, player will not be registered");
pAudioPlayer->mPIId = 0;
} else {
pAudioPlayer->mPIId = pAudioPlayer->mObject.mEngine->mAudioManager->trackPlayer(
android::PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD,
usageForStreamType(pAudioPlayer->mStreamType), AUDIO_CONTENT_TYPE_UNKNOWN,
pAudioPlayer->mTrackPlayer);
}
}
break;
//-----------------------------------
// StreamPlayer
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: {
android::StreamPlayer* splr = new android::StreamPlayer(&app, false /*hasVideo*/,
&pAudioPlayer->mAndroidBufferQueue, pAudioPlayer->mCallbackProtector);
pAudioPlayer->mAPlayer = splr;
splr->init(sfplayer_handlePrefetchEvent, (void*)pAudioPlayer);
}
break;
//-----------------------------------
// AudioToCbRenderer
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE: {
android::AudioToCbRenderer* decoder = new android::AudioToCbRenderer(&app);
pAudioPlayer->mAPlayer = decoder;
// configures the callback for the sink buffer queue
decoder->setDataPushListener(adecoder_writeToBufferQueue, pAudioPlayer);
// configures the callback for the notifications coming from the SF code
decoder->init(sfplayer_handlePrefetchEvent, (void*)pAudioPlayer);
switch (pAudioPlayer->mDataSource.mLocator.mLocatorType) {
case SL_DATALOCATOR_URI:
decoder->setDataSource(
(const char*)pAudioPlayer->mDataSource.mLocator.mURI.URI);
break;
case SL_DATALOCATOR_ANDROIDFD: {
int64_t offset = (int64_t)pAudioPlayer->mDataSource.mLocator.mFD.offset;
decoder->setDataSource(
(int)pAudioPlayer->mDataSource.mLocator.mFD.fd,
offset == SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE ?
(int64_t)PLAYER_FD_FIND_FILE_SIZE : offset,
(int64_t)pAudioPlayer->mDataSource.mLocator.mFD.length);
}
break;
default:
SL_LOGE(ERROR_PLAYERREALIZE_UNKNOWN_DATASOURCE_LOCATOR);
break;
}
}
break;
//-----------------------------------
// AacBqToPcmCbRenderer
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE: {
android::AacBqToPcmCbRenderer* bqtobq = new android::AacBqToPcmCbRenderer(&app,
&pAudioPlayer->mAndroidBufferQueue);
// configures the callback for the sink buffer queue
bqtobq->setDataPushListener(adecoder_writeToBufferQueue, pAudioPlayer);
pAudioPlayer->mAPlayer = bqtobq;
// configures the callback for the notifications coming from the SF code,
// but also implicitly configures the AndroidBufferQueue from which ADTS data is read
pAudioPlayer->mAPlayer->init(sfplayer_handlePrefetchEvent, (void*)pAudioPlayer);
}
break;
//-----------------------------------
default:
SL_LOGE(ERROR_PLAYERREALIZE_UNEXPECTED_OBJECT_TYPE_D, pAudioPlayer->mAndroidObjType);
result = SL_RESULT_INTERNAL_ERROR;
break;
}
if (result == SL_RESULT_SUCCESS) {
// proceed with effect initialization
// initialize EQ
// FIXME use a table of effect descriptors when adding support for more effects
// No session effects allowed even in latency with effects performance mode because HW
// accelerated effects are only tolerated as post processing in this mode
if ((pAudioPlayer->mAndroidObjType != AUDIOPLAYER_FROM_PCM_BUFFERQUEUE) ||
((pAudioPlayer->mPerformanceMode != ANDROID_PERFORMANCE_MODE_LATENCY) &&
(pAudioPlayer->mPerformanceMode != ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS))) {
if (memcmp(SL_IID_EQUALIZER, &pAudioPlayer->mEqualizer.mEqDescriptor.type,
sizeof(effect_uuid_t)) == 0) {
SL_LOGV("Need to initialize EQ for AudioPlayer=%p", pAudioPlayer);
android_eq_init(pAudioPlayer->mSessionId, &pAudioPlayer->mEqualizer);
}
// initialize BassBoost
if (memcmp(SL_IID_BASSBOOST, &pAudioPlayer->mBassBoost.mBassBoostDescriptor.type,
sizeof(effect_uuid_t)) == 0) {
SL_LOGV("Need to initialize BassBoost for AudioPlayer=%p", pAudioPlayer);
android_bb_init(pAudioPlayer->mSessionId, &pAudioPlayer->mBassBoost);
}
// initialize Virtualizer
if (memcmp(SL_IID_VIRTUALIZER, &pAudioPlayer->mVirtualizer.mVirtualizerDescriptor.type,
sizeof(effect_uuid_t)) == 0) {
SL_LOGV("Need to initialize Virtualizer for AudioPlayer=%p", pAudioPlayer);
android_virt_init(pAudioPlayer->mSessionId, &pAudioPlayer->mVirtualizer);
}
}
}
// initialize EffectSend
// FIXME initialize EffectSend
return result;
}
这儿就会创建AudioTrack,并执行AudioPlayerBase的init,将创建好的AudioTrack传递进去。
接下来再看下AAudio的open流程:
代码语言:javascript复制Result AudioStreamAAudio::open() {
Result result = Result::OK;
if (mAAudioStream != nullptr) {
return Result::ErrorInvalidState;
}
result = AudioStream::open();
if (result != Result::OK) {
return result;
}
AAudioStreamBuilder *aaudioBuilder;
result = static_cast<Result>(mLibLoader->createStreamBuilder(&aaudioBuilder));
if (result != Result::OK) {
return result;
}
// Do not set INPUT capacity below 4096 because that prevents us from getting a FAST track
// when using the Legacy data path.
// If the app requests > 4096 then we allow it but we are less likely to get LowLatency.
// See internal bug b/80308183 for more details.
// Fixed in Q but let's still clip the capacity because high input capacity
// does not increase latency.
int32_t capacity = mBufferCapacityInFrames;
constexpr int kCapacityRequiredForFastLegacyTrack = 4096; // matches value in AudioFinger
if (OboeGlobals::areWorkaroundsEnabled()
&& mDirection == oboe::Direction::Input
&& capacity != oboe::Unspecified
&& capacity < kCapacityRequiredForFastLegacyTrack
&& mPerformanceMode == oboe::PerformanceMode::LowLatency) {
capacity = kCapacityRequiredForFastLegacyTrack;
LOGD("AudioStreamAAudio.open() capacity changed from %d to %d for lower latency",
static_cast<int>(mBufferCapacityInFrames), capacity);
}
mLibLoader->builder_setBufferCapacityInFrames(aaudioBuilder, capacity);
// Channel mask was added in SC_V2. Given the corresponding channel count of selected channel
// mask may be different from selected channel count, the last set value will be respected.
// If channel count is set after channel mask, the previously set channel mask will be cleared.
// If channel mask is set after channel count, the channel count will be automatically
// calculated from selected channel mask. In that case, only set channel mask when the API
// is available and the channel mask is specified.
if (mLibLoader->builder_setChannelMask != nullptr && mChannelMask != ChannelMask::Unspecified) {
mLibLoader->builder_setChannelMask(aaudioBuilder,
static_cast<aaudio_channel_mask_t>(mChannelMask));
} else {
mLibLoader->builder_setChannelCount(aaudioBuilder, mChannelCount);
}
mLibLoader->builder_setDeviceId(aaudioBuilder, mDeviceId);
mLibLoader->builder_setDirection(aaudioBuilder, static_cast<aaudio_direction_t>(mDirection));
mLibLoader->builder_setFormat(aaudioBuilder, static_cast<aaudio_format_t>(mFormat));
mLibLoader->builder_setSampleRate(aaudioBuilder, mSampleRate);
mLibLoader->builder_setSharingMode(aaudioBuilder,
static_cast<aaudio_sharing_mode_t>(mSharingMode));
mLibLoader->builder_setPerformanceMode(aaudioBuilder,
static_cast<aaudio_performance_mode_t>(mPerformanceMode));
// These were added in P so we have to check for the function pointer.
if (mLibLoader->builder_setUsage != nullptr) {
mLibLoader->builder_setUsage(aaudioBuilder,
static_cast<aaudio_usage_t>(mUsage));
}
if (mLibLoader->builder_setContentType != nullptr) {
mLibLoader->builder_setContentType(aaudioBuilder,
static_cast<aaudio_content_type_t>(mContentType));
}
if (mLibLoader->builder_setInputPreset != nullptr) {
aaudio_input_preset_t inputPreset = mInputPreset;
if (getSdkVersion() <= __ANDROID_API_P__ && inputPreset == InputPreset::VoicePerformance) {
LOGD("InputPreset::VoicePerformance not supported before Q. Using VoiceRecognition.");
inputPreset = InputPreset::VoiceRecognition; // most similar preset
}
mLibLoader->builder_setInputPreset(aaudioBuilder,
static_cast<aaudio_input_preset_t>(inputPreset));
}
if (mLibLoader->builder_setSessionId != nullptr) {
mLibLoader->builder_setSessionId(aaudioBuilder,
static_cast<aaudio_session_id_t>(mSessionId));
}
// These were added in S so we have to check for the function pointer.
if (mLibLoader->builder_setPackageName != nullptr && !mPackageName.empty()) {
mLibLoader->builder_setPackageName(aaudioBuilder,
mPackageName.c_str());
}
if (mLibLoader->builder_setAttributionTag != nullptr && !mAttributionTag.empty()) {
mLibLoader->builder_setAttributionTag(aaudioBuilder,
mAttributionTag.c_str());
}
if (isDataCallbackSpecified()) {
mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this);
mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerDataCallback());
if (!isErrorCallbackSpecified()) {
// The app did not specify a callback so we should specify
// our own so the stream gets closed and stopped.
mErrorCallback = &mDefaultErrorCallback;
}
mLibLoader->builder_setErrorCallback(aaudioBuilder, internalErrorCallback, this);
}
// Else if the data callback is not being used then the write method will return an error
// and the app can stop and close the stream.
// ============= OPEN THE STREAM ================
{
AAudioStream *stream = nullptr;
result = static_cast<Result>(mLibLoader->builder_openStream(aaudioBuilder, &stream));
mAAudioStream.store(stream);
}
if (result != Result::OK) {
// Warn developer because ErrorInternal is not very informative.
if (result == Result::ErrorInternal && mDirection == Direction::Input) {
LOGW("AudioStreamAAudio.open() may have failed due to lack of "
"audio recording permission.");
}
goto error2;
}
// Query and cache the stream properties
mDeviceId = mLibLoader->stream_getDeviceId(mAAudioStream);
mChannelCount = mLibLoader->stream_getChannelCount(mAAudioStream);
mSampleRate = mLibLoader->stream_getSampleRate(mAAudioStream);
mFormat = static_cast<AudioFormat>(mLibLoader->stream_getFormat(mAAudioStream));
mSharingMode = static_cast<SharingMode>(mLibLoader->stream_getSharingMode(mAAudioStream));
mPerformanceMode = static_cast<PerformanceMode>(
mLibLoader->stream_getPerformanceMode(mAAudioStream));
mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream);
mBufferSizeInFrames = mLibLoader->stream_getBufferSize(mAAudioStream);
mFramesPerBurst = mLibLoader->stream_getFramesPerBurst(mAAudioStream);
// These were added in P so we have to check for the function pointer.
if (mLibLoader->stream_getUsage != nullptr) {
mUsage = static_cast<Usage>(mLibLoader->stream_getUsage(mAAudioStream));
}
if (mLibLoader->stream_getContentType != nullptr) {
mContentType = static_cast<ContentType>(mLibLoader->stream_getContentType(mAAudioStream));
}
if (mLibLoader->stream_getInputPreset != nullptr) {
mInputPreset = static_cast<InputPreset>(mLibLoader->stream_getInputPreset(mAAudioStream));
}
if (mLibLoader->stream_getSessionId != nullptr) {
mSessionId = static_cast<SessionId>(mLibLoader->stream_getSessionId(mAAudioStream));
} else {
mSessionId = SessionId::None;
}
if (mLibLoader->stream_getChannelMask != nullptr) {
mChannelMask = static_cast<ChannelMask>(mLibLoader->stream_getChannelMask(mAAudioStream));
}
LOGD("AudioStreamAAudio.open() format=%d, sampleRate=%d, capacity = %d",
static_cast<int>(mFormat), static_cast<int>(mSampleRate),
static_cast<int>(mBufferCapacityInFrames));
calculateDefaultDelayBeforeCloseMillis();
error2:
mLibLoader->builder_delete(aaudioBuilder);
LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s",
mLibLoader->convertResultToText(static_cast<aaudio_result_t>(result)));
return result;
}
这儿主要是通过dlopen libaaudio.so,并创建aaudio接口,具体在AAudio中的流程,就是通过audiioservice创建采集。
接下来再看下opensl 启动采集的流程,入口是requestStart,和Start的区别是前者是异步的:
代码语言:javascript复制Result AudioInputStreamOpenSLES::requestStart() {
LOGD("AudioInputStreamOpenSLES(): %s() called", __func__);
std::lock_guard<std::mutex> lock(mLock);
StreamState initialState = getState();
switch (initialState) {
case StreamState::Starting:
case StreamState::Started:
return Result::OK;
case StreamState::Closed:
return Result::ErrorClosed;
default:
break;
}
// We use a callback if the user requests one
// OR if we have an internal callback to fill the blocking IO buffer.
setDataCallbackEnabled(true);
setState(StreamState::Starting);
Result result = setRecordState_l(SL_RECORDSTATE_RECORDING);
if (result == Result::OK) {
setState(StreamState::Started);
// Enqueue the first buffer to start the streaming.
// This does not call the callback function.
enqueueCallbackBuffer(mSimpleBufferQueueInterface);
} else {
setState(initialState);
}
return result;
}
主要实现在setRecordState_l中:
代码语言:javascript复制Result AudioInputStreamOpenSLES::setRecordState_l(SLuint32 newState) {
LOGD("AudioInputStreamOpenSLES::%s(%u)", __func__, newState);
Result result = Result::OK;
if (mRecordInterface == nullptr) {
LOGW("AudioInputStreamOpenSLES::%s() mRecordInterface is null", __func__);
return Result::ErrorInvalidState;
}
SLresult slResult = (*mRecordInterface)->SetRecordState(mRecordInterface, newState);
//LOGD("AudioInputStreamOpenSLES::%s(%u) returned %u", __func__, newState, slResult);
if (SL_RESULT_SUCCESS != slResult) {
LOGE("AudioInputStreamOpenSLES::%s(%u) returned error %s",
__func__, newState, getSLErrStr(slResult));
result = Result::ErrorInternal; // TODO review
}
return result;
}
再看下SetRecordState中:
代码语言:javascript复制static SLresult IRecord_SetRecordState(SLRecordItf self, SLuint32 state)
{
SL_ENTER_INTERFACE
switch (state) {
case SL_RECORDSTATE_STOPPED:
case SL_RECORDSTATE_PAUSED:
case SL_RECORDSTATE_RECORDING:
{
IRecord *thiz = (IRecord *) self;
interface_lock_exclusive(thiz);
thiz->mState = state;
#ifdef ANDROID
android_audioRecorder_setRecordState(InterfaceToCAudioRecorder(thiz), state);
#endif
interface_unlock_exclusive(thiz);
result = SL_RESULT_SUCCESS;
}
break;
default:
result = SL_RESULT_PARAMETER_INVALID;
break;
}
SL_LEAVE_INTERFACE
}
再看下 android_audioRecorder_setRecordState,就可以看到启动采集了:
代码语言:javascript复制void android_audioRecorder_setRecordState(CAudioRecorder* ar, SLuint32 state) {
SL_LOGV("android_audioRecorder_setRecordState(%p, %u) entering", ar, state);
if (ar->mAudioRecord == 0) {
return;
}
switch (state) {
case SL_RECORDSTATE_STOPPED:
ar->mAudioRecord->stop();
break;
case SL_RECORDSTATE_PAUSED:
// Note that pausing is treated like stop as this implementation only records to a buffer
// queue, so there is no notion of destination being "opened" or "closed" (See description
// of SL_RECORDSTATE in specification)
ar->mAudioRecord->stop();
break;
case SL_RECORDSTATE_RECORDING:
ar->mAudioRecord->start();
break;
default:
break;
}
}
这时候就启动采集了,那采集数据如何回调回来呢? 在之前open的时候有一个注册的地方:
代码语言:javascript复制SLresult AudioStreamOpenSLES::finishCommonOpen(SLAndroidConfigurationItf configItf) {
SLresult result = registerBufferQueueCallback();
if (SL_RESULT_SUCCESS != result) {
return result;
}
result = updateStreamParameters(configItf);
if (SL_RESULT_SUCCESS != result) {
return result;
}
Result oboeResult = configureBufferSizes(mSampleRate);
if (Result::OK != oboeResult) {
return (SLresult) oboeResult;
}
allocateFifo();
calculateDefaultDelayBeforeCloseMillis();
return SL_RESULT_SUCCESS;
}
看下registerBufferQueueCallback:
代码语言:javascript复制SLresult AudioStreamOpenSLES::registerBufferQueueCallback() {
// The BufferQueue
SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&mSimpleBufferQueueInterface);
if (SL_RESULT_SUCCESS != result) {
LOGE("get buffer queue interface:%p result:%s",
mSimpleBufferQueueInterface,
getSLErrStr(result));
} else {
// Register the BufferQueue callback
result = (*mSimpleBufferQueueInterface)->RegisterCallback(mSimpleBufferQueueInterface,
bqCallbackGlue, this);
if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback result:%s", getSLErrStr(result));
}
}
return result;
}
这时候就又会回到opensl 里,看下RegisterCallback 的内容:
代码语言:javascript复制SLresult IBufferQueue_RegisterCallback(SLBufferQueueItf self,
slBufferQueueCallback callback, void *pContext)
{
SL_ENTER_INTERFACE
IBufferQueue *thiz = (IBufferQueue *) self;
interface_lock_exclusive(thiz);
// verify pre-condition that media object is in the SL_PLAYSTATE_STOPPED state
if (SL_PLAYSTATE_STOPPED == getAssociatedState(thiz)) {
thiz->mCallback = callback;
thiz->mContext = pContext;
result = SL_RESULT_SUCCESS;
} else {
result = SL_RESULT_PRECONDITIONS_VIOLATED;
}
interface_unlock_exclusive(thiz);
SL_LEAVE_INTERFACE
}
这样在创建AudioRecord的时候也会传递一个callback:
代码语言:javascript复制 ar->mAudioRecord = new android::AudioRecord(
ar->mRecordSource, // source
sampleRate, // sample rate in Hertz
sles_to_android_sampleFormat(df_pcm), // format
channelMask, // channel mask
android::String16(), // app ops
0, // frameCount
audioRecorder_callback,// callback_t // 采集回调
(void*)ar, // user, callback data, here the AudioRecorder
0, // notificationFrames
AUDIO_SESSION_ALLOCATE,
android::AudioRecord::TRANSFER_CALLBACK,
// transfer type
policy);
看下回调内容:
代码语言:javascript复制static void audioRecorder_callback(int event, void* user, void *info) {
//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;
}
} else { // empty queue
// no destination to push the data
pBuff->size = 0;
}
interface_unlock_exclusive(&ar->mBufferQueue);
// notify client
if (NULL != callback) {
(*callback)(&ar->mBufferQueue.mItf, callbackPContext);
}
}
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;
default:
SL_LOGE("Encountered unknown AudioRecord event %d for CAudioRecord %p", event, ar);
break;
}
ar->mCallbackProtector->exitCb();
}
这儿就会回调queue的callback, 看下注册的回调
代码语言:javascript复制// This callback handler is called every time a buffer has been processed by OpenSL ES.
static void bqCallbackGlue(SLAndroidSimpleBufferQueueItf bq, void *context) {
bool shouldStopStream = (reinterpret_cast<AudioStreamOpenSLES *>(context))
->processBufferCallback(bq);
if (shouldStopStream) {
(reinterpret_cast<AudioStreamOpenSLES *>(context))->requestStop();
}
}
再看下processBufferCallback:
代码语言:javascript复制bool AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueItf bq) {
bool shouldStopStream = false;
// Ask the app callback to process the buffer.
DataCallbackResult result =
fireDataCallback(mCallbackBuffer[mCallbackBufferIndex].get(), mFramesPerCallback);
if (result == DataCallbackResult::Continue) {
// Pass the buffer to OpenSLES.
SLresult enqueueResult = enqueueCallbackBuffer(bq);
if (enqueueResult != SL_RESULT_SUCCESS) {
LOGE("%s() returned %d", __func__, enqueueResult);
shouldStopStream = true;
}
// Update Oboe client position with frames handled by the callback.
if (getDirection() == Direction::Input) {
mFramesRead = mFramesPerCallback;
} else {
mFramesWritten = mFramesPerCallback;
}
} else if (result == DataCallbackResult::Stop) {
LOGD("Oboe callback returned Stop");
shouldStopStream = true;
} else {
LOGW("Oboe callback returned unexpected value = %d", result);
shouldStopStream = true;
}
if (shouldStopStream) {
mCallbackBufferIndex = 0;
}
return shouldStopStream;
}
在fireDataCallback 中就会把数据返回给app:
代码语言:javascript复制DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFrames) {
if (!isDataCallbackEnabled()) {
LOGW("AudioStream::%s() called with data callback disabled!", __func__);
return DataCallbackResult::Stop; // Should not be getting called
}
DataCallbackResult result;
if (mDataCallback) {
result = mDataCallback->onAudioReady(this, audioData, numFrames);
} else {
result = onDefaultCallback(audioData, numFrames);
}
// On Oreo, we might get called after returning stop.
// So block that here.
setDataCallbackEnabled(result == DataCallbackResult::Continue);
return result;
}
再看下播放的回调,在创建AudioTrack的时候的回调如下:
代码语言:javascript复制 android::AudioTrack* pat = new android::AudioTrack(
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
notificationFrames, // see comment above
pAudioPlayer->mSessionId);
也就是audioTrack_callBack_pullFromBuffQueue,看下audioTrack_callBack_pullFromBuffQueue的内容:
代码语言:javascript复制//-----------------------------------------------------------------------------
// 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;
}
void * callbackPContext = NULL;
switch (event) {
case android::AudioTrack::EVENT_MORE_DATA: {
//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);
if (ap->mBufferQueue.mCallbackPending) {
// call callback with lock not held
slBufferQueueCallback callback = ap->mBufferQueue.mCallback;
if (NULL != callback) {
callbackPContext = ap->mBufferQueue.mContext;
interface_unlock_exclusive(&ap->mBufferQueue);
(*callback)(&ap->mBufferQueue.mItf, callbackPContext);
interface_lock_exclusive(&ap->mBufferQueue);
ap->mBufferQueue.mCallbackPending = false;
}
}
if (ap->mBufferQueue.mState.count != 0) {
//SL_LOGV("nbBuffers in queue = %u",ap->mBufferQueue.mState.count);
assert(ap->mBufferQueue.mFront != ap->mBufferQueue.mRear);
BufferHeader *oldFront = ap->mBufferQueue.mFront;
BufferHeader *newFront = &oldFront[1];
size_t availSource = oldFront->mSize - ap->mBufferQueue.mSizeConsumed;
size_t availSink = pBuff->size;
size_t bytesToCopy = availSource < availSink ? availSource : availSink;
void *pSrc = (char *)oldFront->mBuffer ap->mBufferQueue.mSizeConsumed;
memcpy(pBuff->raw, pSrc, 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
[ap->mBufferQueue.mNumBuffers 1])
{
newFront = ap->mBufferQueue.mArray;
}
ap->mBufferQueue.mFront = newFront;
ap->mBufferQueue.mState.count--;
ap->mBufferQueue.mState.playIndex ;
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);
// signal underflow to prefetch status itf
if (IsInterfaceInitialized(&ap->mObject, MPH_PREFETCHSTATUS)) {
ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
ap->mPrefetchStatus.mLevel = 0;
// callback or no callback?
prefetchEvents = ap->mPrefetchStatus.mCallbackEventsMask &
(SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE);
if (SL_PREFETCHEVENT_NONE != prefetchEvents) {
prefetchCallback = ap->mPrefetchStatus.mCallback;
prefetchContext = ap->mPrefetchStatus.mContext;
}
}
// stop the track so it restarts playing faster when new data is enqueued
ap->mTrackPlayer->stop();
}
interface_unlock_exclusive(&ap->mBufferQueue);
// notify client
if (NULL != prefetchCallback) {
assert(SL_PREFETCHEVENT_NONE != prefetchEvents);
// spec requires separate callbacks for each event
if (prefetchEvents & SL_PREFETCHEVENT_STATUSCHANGE) {
(*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext,
SL_PREFETCHEVENT_STATUSCHANGE);
}
if (prefetchEvents & SL_PREFETCHEVENT_FILLLEVELCHANGE) {
(*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext,
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();
}
可以看到回调同样也是来自于ap->mBufferQueue.mCallback,和采集一样也是open的时候注册进来的. 再看下aaudio的启动:
代码语言:javascript复制Result AudioStreamAAudio::requestStart() {
std::lock_guard<std::mutex> lock(mLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
// Avoid state machine errors in O_MR1.
if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
if (state == StreamState::Starting || state == StreamState::Started) {
// WARNING: On P, AAudio is returning ErrorInvalidState for Output and OK for Input.
return Result::OK;
}
}
if (isDataCallbackSpecified()) {
setDataCallbackEnabled(true);
}
mStopThreadAllowed = true;
return static_cast<Result>(mLibLoader->stream_requestStart(stream));
} else {
return Result::ErrorClosed;
}
}
就是直接调用libaaudio中的接口了。 再继续看下回调怎么来的,在open aaudio的时候有下面一段逻辑:
代码语言:javascript复制if (isDataCallbackSpecified()) {
mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this);
mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerDataCallback());
if (!isErrorCallbackSpecified()) {
// The app did not specify a callback so we should specify
// our own so the stream gets closed and stopped.
mErrorCallback = &mDefaultErrorCallback;
}
mLibLoader->builder_setErrorCallback(aaudioBuilder, internalErrorCallback, this);
}
再看下oboe_aaudio_data_callback_proc:
代码语言:javascript复制static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc(
AAudioStream *stream,
void *userData,
void *audioData,
int32_t numFrames) {
AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(userData);
if (oboeStream != nullptr) {
return static_cast<aaudio_data_callback_result_t>(
oboeStream->callOnAudioReady(stream, audioData, numFrames));
} else {
return static_cast<aaudio_data_callback_result_t>(DataCallbackResult::Stop);
}
}
mmap 机制不需要拷贝数据,直接传递地址就好:
代码语言:javascript复制DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream * /*stream*/,
void *audioData,
int32_t numFrames) {
DataCallbackResult result = fireDataCallback(audioData, numFrames);
if (result == DataCallbackResult::Continue) {
return result;
} else {
if (result == DataCallbackResult::Stop) {
LOGD("Oboe callback returned DataCallbackResult::Stop");
} else {
LOGE("Oboe callback returned unexpected value = %d", result);
}
// Returning Stop caused various problems before S. See #1230
if (OboeGlobals::areWorkaroundsEnabled() && getSdkVersion() <= __ANDROID_API_R__) {
launchStopThread();
return DataCallbackResult::Continue;
} else {
return DataCallbackResult::Stop; // OK >= API_S
}
}
}
这时候数据就又到fireDataCallback里了,这时候就可以回调给app了:
代码语言:javascript复制DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFrames) {
if (!isDataCallbackEnabled()) {
LOGW("AudioStream::%s() called with data callback disabled!", __func__);
return DataCallbackResult::Stop; // Should not be getting called
}
DataCallbackResult result;
if (mDataCallback) {
result = mDataCallback->onAudioReady(this, audioData, numFrames);
} else {
result = onDefaultCallback(audioData, numFrames);
}
// On Oreo, we might get called after returning stop.
// So block that here.
setDataCallbackEnabled(result == DataCallbackResult::Continue);
return result;
}
这时候就介绍完了Oboe的大致流程,可以看出来Oboe的优势在于自己有一套兼容性机制,又利用了opensl和aaudio的低延时机制, 在设计这块,oboe和aaudio的设计的很像的。