php网站建设论文,东莞横沥医院,wordpress小标签,网站源码官网1、背景
在学习audio的过程中#xff0c;看到了大神zyuanyun的博客#xff0c;在博客的结尾#xff0c;大神留下了这些问题#xff1a; 但是大神没有出后续的博文来说明audio环形缓冲队列的具体实现#xff0c;这勾起了我强烈的好奇心。经过一段时间的走读代码#xff…1、背景
在学习audio的过程中看到了大神zyuanyun的博客在博客的结尾大神留下了这些问题 但是大神没有出后续的博文来说明audio环形缓冲队列的具体实现这勾起了我强烈的好奇心。经过一段时间的走读代码同时阅读其他大佬的博文把环形缓冲队列的内容整理出来。
2、AudioPolicyService、AudioFlinger及相关类
AudioPolicyService简称APS是负责音频策略的制定者比如什么时候打开音频接口设备、某种Stream类型的音频对应什么设备等等AudioFlinger简称AF负责音频策略的具体执行比如如何与音频设备通信如何维护现有系统中的音频设备以及多个音频流的混音如何处理等。 环形缓冲队列大致可以以下面这幅图来描述其流程
3、Track的创建
AudioTrack的创建经过漫长的调用链最终是/frameworks/av/services/audioflinger/Tracks.cpp里完成创建的。
// TrackBase constructor must be called with AudioFlinger::mLock held
AudioFlinger::ThreadBase::TrackBase::TrackBase(){//计算最小帧大小size_t minBufferSize buffer NULL ? roundup(frameCount) : frameCount;……minBufferSize * mFrameSize;size_t size sizeof(audio_track_cblk_t);if (buffer NULL alloc ALLOC_CBLK) {// check overflow when computing allocation size for streaming tracks.if (size SIZE_MAX - bufferSize) {android_errorWriteLog(0x534e4554, 34749571);return;}size bufferSize;}if (client ! 0) {//为客户端分配内存mCblkMemory client-heap()-allocate(size);……} else {mCblk (audio_track_cblk_t *) malloc(size);……}// construct the shared structure in-place.if (mCblk ! NULL) {// 这是 C 的 placement new定位创建对象语法new(BUFFER) CLASS();// 可以在特定内存位置上构造一个对象// 这里在匿名共享内存首地址上构造了一个 audio_track_cblk_t 对象// 这样 AudioTrack 与 AudioFlinger 都能访问这个 audio_track_cblk_t 对象了new(mCblk) audio_track_cblk_t();switch (alloc) {……case ALLOC_CBLK:// clear all buffersif (buffer NULL) {// 数据 FIFO 的首地址紧靠控制块audio_track_cblk_t之后// | |// | ------------------- mCblkMemory --------------------- |// | |// --------------------------------------------------------// | audio_track_cblk_t | Buffer |// --------------------------------------------------------// ^ ^// | |// mCblk mBuffer//这里mCblk被强制转型成占用内存1字节的char类型这个1在后面会用到mBuffer (char*)mCblk sizeof(audio_track_cblk_t);memset(mBuffer, 0, bufferSize);} else {// 数据传输模式为 MODE_STATIC/TRANSFER_SHARED 时直接指向 sharedBuffer// sharedBuffer 是应用进程分配的匿名共享内存应用进程已经一次性把数据// 写到 sharedBuffer 来了AudioFlinger 可以直接从这里读取// -------------------- -----------------------------------// | audio_track_cblk_t | | sharedBuffer |// -------------------- -----------------------------------// ^ ^// | |// mCblk mBuffermBuffer buffer;}break;……}……}
}4、生产者向共享内存写入数据
4.1
4.2 向共享内存写入数据
//framework/av/media/libaudioclient/AudioTrack.cpp
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{……size_t written 0;Buffer audioBuffer;while (userSize mFrameSize) {// 单帧数据量 frameSize channelCount * bytesPerSample// 对于双声道16位采样的音频数据来说frameSize 2 * 2 4(bytes)// 用户传入的数据帧数 frameCount userSize / frameSizeaudioBuffer.frameCount userSize / mFrameSize;// obtainBuffer() 从 FIFO 上得到一块可用区间status_t err obtainBuffer(audioBuffer,blocking ? ClientProxy::kForever : ClientProxy::kNonBlocking);……size_t toWrite audioBuffer.size;memcpy(audioBuffer.i8, buffer, toWrite);buffer ((const char *) buffer) toWrite;userSize - toWrite;written toWrite;releaseBuffer(audioBuffer);}……return written;
}status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,struct timespec *elapsed, size_t *nonContig)
{// previous and new IAudioTrack sequence numbers are used to detect track re-creationuint32_t oldSequence 0;uint32_t newSequence;Proxy::Buffer buffer;status_t status NO_ERROR;static const int32_t kMaxTries 5;int32_t tryCounter kMaxTries;do {// obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to// keep them from going away if another thread re-creates the track during obtainBuffer()spAudioTrackClientProxy proxy;spIMemory iMem;{ // start of lock scopeAutoMutex lock(mLock);……// Keep the extra referencesproxy mProxy;iMem mCblkMemory;……} // end of lock scopebuffer.mFrameCount audioBuffer-frameCount;// FIXME starts the requested timeout and elapsed over from scratchstatus proxy-obtainBuffer(buffer, requested, elapsed);} while (((status DEAD_OBJECT) || (status NOT_ENOUGH_DATA)) (tryCounter-- 0));audioBuffer-frameCount buffer.mFrameCount;audioBuffer-size buffer.mFrameCount * mFrameSize;audioBuffer-raw buffer.mRaw;if (nonContig ! NULL) {*nonContig buffer.mNonContig;}return status;
}//framework/av/media/libaudioclient/AudioTrackShared.cpp
__attribute__((no_sanitize(integer)))
status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,struct timespec *elapsed)
{……struct timespec before;bool beforeIsValid false;audio_track_cblk_t* cblk mCblk;bool ignoreInitialPendingInterrupt true;for (;;) {int32_t flags android_atomic_and(~CBLK_INTERRUPT, cblk-mFlags);……int32_t front;int32_t rear;if (mIsOut) {front android_atomic_acquire_load(cblk-u.mStreaming.mFront);rear cblk-u.mStreaming.mRear;} else {// On the other hand, this barrier is required.rear android_atomic_acquire_load(cblk-u.mStreaming.mRear);front cblk-u.mStreaming.mFront;}// write to rear, read from frontssize_t filled audio_utils::safe_sub_overflow(rear, front);……ssize_t adjustableSize (ssize_t) getBufferSizeInFrames();ssize_t avail (mIsOut) ? adjustableSize - filled : filled;if (avail 0) {avail 0;} else if (avail 0) {// avail may be non-contiguous, so return only the first contiguous chunksize_t part1;if (mIsOut) {rear mFrameCountP2 - 1;part1 mFrameCountP2 - rear;} else {front mFrameCountP2 - 1;part1 mFrameCountP2 - front;}if (part1 (size_t)avail) {part1 avail;}if (part1 buffer-mFrameCount) {part1 buffer-mFrameCount;}buffer-mFrameCount part1;buffer-mRaw part1 0 ?((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;buffer-mNonContig avail - part1;mUnreleased part1;status NO_ERROR;break;}struct timespec remaining;const struct timespec *ts;……int32_t old android_atomic_and(~CBLK_FUTEX_WAKE, cblk-mFutex);if (!(old CBLK_FUTEX_WAKE)) {……errno 0;(void) syscall(__NR_futex, cblk-mFutex,mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old ~CBLK_FUTEX_WAKE, ts);……}}end:……return status;
}void ClientProxy::releaseBuffer(Buffer* buffer)
{size_t stepCount buffer-mFrameCount;……mUnreleased - stepCount;audio_track_cblk_t* cblk mCblk;// Both of these barriers are requiredif (mIsOut) {int32_t rear cblk-u.mStreaming.mRear;android_atomic_release_store(stepCount rear, cblk-u.mStreaming.mRear);} else {int32_t front cblk-u.mStreaming.mFront;android_atomic_release_store(stepCount front, cblk-u.mStreaming.mFront);}
}4、消费者从共享内存读数据
找到当前活动的track需要经过很长的准备及调用链可以参考这篇博客。
4.1 PlaybackThread的混音
代码路径frameworks/av/services/audioflinger/Threads.cpp
void AudioFlinger::MixerThread::threadLoop_mix()
{// mix buffers...mAudioMixer-process();mCurrentWriteLength mSinkBufferSize;……
}process()方法之后进入track的混音流程代码位于frameworks/av/media/libaudioprocessing/AudioMixer.cpp。来看process()的定义 using process_hook_t void(AudioMixer::*)();process_hook_t mHook AudioMixer::process__nop; void invalidate() {mHook AudioMixer::process__validate;}void process__validate();void process__nop();void process__genericNoResampling();void process__genericResampling();void process__oneTrack16BitsStereoNoResampling();template int MIXTYPE, typename TO, typename TI, typename TAvoid process__noResampleOneTrack();
hook是一个函数指针根据不同场景会分别指向不同函数实现。详细可以参考这篇博客以及这篇博客。以process__nop方法为例
void AudioMixer::process__nop()
{for (const auto pair : mGroups) {const auto group pair.second;const std::shared_ptrTrack t mTracks[group[0]];memset(t-mainBuffer, 0,mFrameCount * audio_bytes_per_frame(t-mMixerChannelCount t-mMixerHapticChannelCount, t-mMixerFormat));// now consume datafor (const int name : group) {const std::shared_ptrTrack t mTracks[name];size_t outFrames mFrameCount;while (outFrames) {t-buffer.frameCount outFrames;t-bufferProvider-getNextBuffer(t-buffer);if (t-buffer.raw NULL) break;outFrames - t-buffer.frameCount;t-bufferProvider-releaseBuffer(t-buffer);}}}
}frameworks/av/services/audioflinger/Tracks.cpp
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{ServerProxy::Buffer buf;size_t desiredFrames buffer-frameCount;buf.mFrameCount desiredFrames;status_t status mServerProxy-obtainBuffer(buf);buffer-frameCount buf.mFrameCount;buffer-raw buf.mRaw;if (buf.mFrameCount 0 !isStopping() !isStopped() !isPaused()) {mAudioTrackServerProxy-tallyUnderrunFrames(desiredFrames);} else {mAudioTrackServerProxy-tallyUnderrunFrames(0);}return status;
}void AudioFlinger::PlaybackThread::Track::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{interceptBuffer(*buffer);TrackBase::releaseBuffer(buffer);
}/frameworks/av/media/libaudioclient/AudioTrackShared.cpp
status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
{{audio_track_cblk_t* cblk mCblk;// compute number of frames available to write (AudioTrack) or read (AudioRecord),// or use previous cached value from framesReady(), with added barrier if it omits.int32_t front;int32_t rear;// See notes on barriers at ClientProxy::obtainBuffer()if (mIsOut) {flushBufferIfNeeded(); // might modify mFrontrear getRear();front cblk-u.mStreaming.mFront;} else {front android_atomic_acquire_load(cblk-u.mStreaming.mFront);rear cblk-u.mStreaming.mRear;}……// availToServer may be non-contiguous, so return only the first contiguous chunksize_t part1;if (mIsOut) {front mFrameCountP2 - 1;part1 mFrameCountP2 - front;} else {rear mFrameCountP2 - 1;part1 mFrameCountP2 - rear;}if (part1 availToServer) {part1 availToServer;}size_t ask buffer-mFrameCount;……
}int32_t AudioTrackServerProxy::getRear() const
{const int32_t stop android_atomic_acquire_load(mCblk-u.mStreaming.mStop);const int32_t rear android_atomic_acquire_load(mCblk-u.mStreaming.mRear);const int32_t stopLast mStopLast.load(std::memory_order_acquire);……return rear;
}5、总结
经过一段时间的代码走读能够回答文章开头提出的部分问题但是诸如“读写指针线程安全”、“Futex同步机制”等问题现阶段还回答不上来以后有机会再深入研究下。