Esta serie de artículos enlace Maestro: sub-directorio temático Android Framework Subsistema de audio
Resumen y descripción de puntos clave en este capítulo:
Este capítulo se centra principalmente en el análisis de PlaybackThread en la esquina superior izquierda del mapa mental de arriba. Analiza principalmente el principio de mezcla y el proceso de mezcla.
1 Explicación del principio de mezcla
Cuando se reproducen varias aplicaciones, cada aplicación creará un AudioTrack, y cada AudioTrack transferirá datos a través de la pista de la memoria compartida y el hilo de reproducción. El formato de datos de audio enviado por cada aplicación puede ser diferente, y la tarjeta de sonido solo admite un número fijo de Formato, a menos que el formato enviado sea el formato admitido por la propia tarjeta de sonido, de lo contrario, debe volver a muestrearse / mezclarse (algunas operaciones de mAudioMixer en el hilo de reproducción convierten el formato de audio que no es compatible con el hardware al formato de audio compatible con el hardware. Este proceso se llama pesado. Muestreo). Aquí interpretamos el tipo de audio AudioMixer de la variable mAudioMixer en MixerThread. El código de referencia de AudioMixer es el siguiente:
class MixerThread : public PlaybackThread {
{
public:
MixerThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
audio_io_handle_t id,
audio_devices_t device,
type_t type = MIXER);
virtual ~MixerThread();
//...
AudioMixer* mAudioMixer; // normal mixer
//...
};
La clase AudioMixer aquí se implementa de la siguiente manera:
class AudioMixer
{
public:
AudioMixer(size_t frameCount, uint32_t sampleRate,uint32_t maxNumTracks = MAX_NUM_TRACKS);
//...
//给track使用的hook函数整理
/*进行重采样*/
static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
int32_t* aux);
/*不做任何处理*/
static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
int32_t* aux);
static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
int32_t* aux);
//...
// multi-format track hooks
template <int MIXTYPE, typename TO, typename TI, typename TA>
static void track__Resample(track_t* t, TO* out, size_t frameCount,
TO* temp __unused, TA* aux);
template <int MIXTYPE, typename TO, typename TI, typename TA>
static void track__NoResample(track_t* t, TO* out, size_t frameCount,
TO* temp __unused, TA* aux);
//...
//给mState使用的hook函数整理
static void process__validate(state_t* state, int64_t pts);
/* no-op,如果静音了不做任何处理*/
static void process__nop(state_t* state, int64_t pts);
/*如果传递过来的数据,是声卡直接支持的格式,则不需要重新采样*/
static void process__genericNoResampling(state_t* state, int64_t pts);
/*如果需要重新采样,则会调用该函数*/
static void process__genericResampling(state_t* state, int64_t pts);
static void process__OneTrack16BitsStereoNoResampling(state_t* state,int64_t pts);
//...
// hook types
enum {
PROCESSTYPE_NORESAMPLEONETRACK,
};
enum {
TRACKTYPE_NOP,
TRACKTYPE_RESAMPLE,
TRACKTYPE_NORESAMPLE,
TRACKTYPE_NORESAMPLEMONO,
};
//...
state_t mState __attribute__((aligned(32)));
//...
}
Hay un miembro mstate en mAudioMixer, y la estructura state_t se define de la siguiente manera:
// pad to 32-bytes to fill cache line
struct state_t {
uint32_t enabledTracks;
uint32_t needsChanged;
size_t frameCount;
process_hook_t hook; // one of process__*, never NULL
int32_t *outputTemp;
int32_t *resampleTemp;
NBLog::Writer* mLog;
int32_t reserved[1];
// FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
};
mstate contiene una función de enlace que apunta a diferentes funciones de procesamiento. Para diferentes situaciones, los ganchos apuntarán a diferentes funciones. Si el puntero de la función de enlace apunta a process__genericNoResampling, habrá un área de caché temporal de outputTemp, y también hay pistas (variables en el miembro mState), cada pista corresponde al AudioTrack de la aplicación, y la definición de la estructura track_t es la siguiente:
struct track_t {
uint32_t needs;
union {
int16_t volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
int32_t volumeRL;
};
int32_t prevVolume[MAX_NUM_VOLUMES];
int32_t volumeInc[MAX_NUM_VOLUMES];
int32_t auxInc;
int32_t prevAuxLevel;
int16_t auxLevel; // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
uint16_t frameCount;
uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
uint8_t unused_padding; // formerly format, was always 16
uint16_t enabled; // actually bool
audio_channel_mask_t channelMask;
AudioBufferProvider* bufferProvider;
// 16-byte boundary
mutable AudioBufferProvider::Buffer buffer; // 8 bytes
hook_t hook; //指向不同的处理函数
const void* in; // current location in buffer
// 16-byte boundary
AudioResampler* resampler; //重采样器
uint32_t sampleRate;
int32_t* mainBuffer;//保存重采样之后的最终数据
int32_t* auxBuffer;
//...
bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
bool doesResample() const { return resampler != NULL; }
void resetResampler() { if (resampler != NULL) resampler->reset(); }
void adjustVolumeRamp(bool aux, bool useFloat = false);
size_t getUnreleasedFrames() const { return resampler != NULL ?
resampler->getUnreleasedFrames() : 0; };
};
A través de la comprensión anterior de AudioMixer, state_t, track_t y otros tipos, podemos ver dos tipos de ganchos:
- gancho en estado_t mState: gancho total
- gancho en track_t track: solo gancho
AudioTrack transfiere datos a MixerThread (subclase PlaybackThread) a través de la memoria compartida, analiza el formato de mTracks en MixerThread para ver si es un formato compatible con el hardware, establece diferentes funciones de enlace (determina si se volverá a muestrear) de acuerdo con diferentes situaciones, y la configuración se completa Después de eso, configure la función de enlace total (en mState), resumida de la siguiente manera:
- Determine el enlace: analice los datos de mState.tracks [x] uno por uno, determine las pistas [x] .hook según su formato y luego determine el mState.hook total
- Enlace de llamada: simplemente llame al mState.hook total, llamará a cada mState.tracks [x] .hook
El destino de los datos después del remuestreo: hay datos originales en la memoria compartida. Como se mencionó anteriormente en outputTemp, los datos procesados de cada pista ingresan al área de almacenamiento temporal (como el remuestreo). Entonces outputTemp almacena los datos superpuestos después de que se procesa cada pista. Estos datos temporales finalmente se guardan en mainBuffer. MainBuffer en cada pista apunta al mMixerBuffer mixto en PlaybackThread. Los datos en mMixerBuffer se pueden reproducir, pero no se envían directamente al hardware, sino a mSinkbuffer. Por supuesto, mEffectBuffer también se puede usar para procesar efectos de sonido en el sistema Android. Finalmente enviarán el búfer al hardware mSinkbuffer. Si se utiliza el efecto de sonido, la fuente puede ser mEffectBuffer y mMixerBuffer.
2 Análisis de mezcla de código fuente
Aquí, el análisis se basa principalmente en MixerThread, comenzando desde el hilo de reproducción de la clase principal de MixerThread, revisando el código fuente del inicio de AudioPloicyService y el inicio de AudioFlinger, y la pila de análisis del proceso de creación deheadtrhead es la siguiente:
AudioPolicyService::onFirstRef(...)
->AudioPolicyService::createAudioPolicyManager(...);
-->AudioPolicyManager(...);
--->AudioFlinger::loadHwModule(...);
--->AudioFlinger::openOutput(...);
---->AudioFlinger::openOutput_l(...);
----->创建thread(MixerThread(多数)、OffloadThread、DirectOutputThread中的一个)
----->mPlaybackThreads.add(*output, thread);将thread加入到mPlaybackThreads中
Es decir, desde el comienzo de la creación de AudioPolicyService, se crea el hilo de salida correspondiente (MixerThread (most), OffloadThread, DirectOutputThread). Por lo tanto, a partir del análisis del hilo del hilo de reproducción, el código del constructor es el siguiente:
AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
//...
mLatchDValid(false), mLatchQValid(false)
{
//设置mName
snprintf(mName, kNameLength, "AudioOut_%X", id);
//...
}
La implementación del código en onFirstRef es la siguiente:
void AudioFlinger::PlaybackThread::onFirstRef()
{
run(mName, ANDROID_PRIORITY_URGENT_AUDIO);
}
Después de crear la clase de hilo de reproducción (así como la subclase MixerThread, etc.), el hilo comienza aquí. En el hilo de Android, threadloop es el cuerpo de ejecución real del hilo, y el código se implementa de la siguiente manera:
bool AudioFlinger::PlaybackThread::threadLoop()
{
//...
while (!exitPending())
{
cpuStats.sample(myName);
Vector< sp<EffectChain> > effectChains;
{ // scope for mLock
Mutex::Autolock _l(mLock);
/*处理配置信息*/
processConfigEvents_l();
//...
//如果数据为0,则让声卡进入休眠状态
if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
isSuspended()) {
// put audio hardware into standby after short delay
if (shouldStandby_l()) {
//声卡休眠
threadLoop_standby();
mStandby = true;
}
if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
IPCThreadState::self()->flushCommands();
//...
//线程休眠点,直到AudioTrack发送广播唤醒
mWaitWorkCV.wait(mLock);
//...
continue;
}
}
//关键点1:混音前的准备工作
mMixerStatus = prepareTracks_l(&tracksToRemove);
//...
lockEffectChains_l(effectChains);
} // mLock scope ends
if (mBytesRemaining == 0) {
mCurrentWriteLength = 0;
if (mMixerStatus == MIXER_TRACKS_READY) {
//关键点2:混音
threadLoop_mix();
} else if ((mMixerStatus != MIXER_DRAIN_TRACK)
&& (mMixerStatus != MIXER_DRAIN_ALL)) {
threadLoop_sleepTime();
if (sleepTime == 0) {
mCurrentWriteLength = mSinkBufferSize;
}
}
if (mMixerBufferValid) {
void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;
audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;
//把数据从thread.mMixerBuffer复制到thread.mSinkBuffer
memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
mNormalFrameCount * mChannelCount);
}
//...
}
//...
if (mEffectBufferValid) {
//把数据从thread.mEffectBuffer复制到thread.mSinkBuffer
memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
mNormalFrameCount * mChannelCount);
}
// enable changes in effect chain
unlockEffectChains(effectChains);
if (!waitingAsyncCallback()) {
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
if (mBytesRemaining) {
//关键点3:音频输出
ssize_t ret = threadLoop_write();
//...
} else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
(mMixerStatus == MIXER_DRAIN_ALL)) {
threadLoop_drain();
}
//...
} else {
usleep(sleepTime);
}
}
//...
}
threadLoop_exit();
if (!mStandby) {
threadLoop_standby();
mStandby = true;
}
//...
return false;
}
playthread es responsable de crear hilos, pero los métodos clave a analizar aquí se implementan en MixerThread. Los tres métodos clave analizados son prepareTracks_l, threadLoop_mix y threadLoop_write.
2.1 análisis de prepareTracks_l
La implementación del código de MixerThread :: prepareTracks_l es la siguiente:
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove)
{
//默认为空闲状态
mixer_state mixerStatus = MIXER_IDLE;
size_t count = mActiveTracks.size();
//...
//对于所有在mActiveTracks里面的Track,都需要进行设置
for (size_t i=0 ; i<count ; i++) {
const sp<Track> t = mActiveTracks[i].promote();
if (t == 0) {
continue;
}
// this const just means the local variable doesn't change
Track* const track = t.get();
// fastTrack不会在这里进行混音,略过
if (track->isFastTrack()) {
//...
}
{ // local variable scope to avoid goto warning
audio_track_cblk_t* cblk = track->cblk();
/* 获取track的name,这是个索引,AudioMixer会最多维护32个track,分别对应int的32个位,
* 如果track的name还没定下来的话,会自行选择一个空位
*/
int name = track->name();
size_t desiredFrames;
uint32_t sr = track->sampleRate();
if (sr == mSampleRate) {
desiredFrames = mNormalFrameCount;
} else {
desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
}
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
minFrames = desiredFrames;
}
//混音的状态下,frameReady = 1,那么会进入下面的条件,进行AudioMixer参数设置
size_t framesReady = track->framesReady();
if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
//音量参数设置
//...
//设置AudioMixer参数
mAudioMixer->setBufferProvider(name, track);//源Buffer
mAudioMixer->enable(name);//使能该track,可以混音
//音轨 左 右 aux
mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);
//音频格式
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::FORMAT, (void *)track->format());
//音轨mask,哪个需要或者不需要混音
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mChannelMask);
// limit track sample rate to 2 x output sample rate, which changes at re-configuration
uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX;
uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate();
if (reqSampleRate == 0) {
reqSampleRate = mSampleRate;
} else if (reqSampleRate > maxSampleRate) {
reqSampleRate = maxSampleRate;
}
/*进行重采样
*注意:安卓的MixerThread会对所有的track进行重采样
*那么在混音的时候会调用重采样的混音方法。
*/
mAudioMixer->setParameter(
name,
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
(void *)(uintptr_t)reqSampleRate);
if (mMixerBufferEnabled
&& (track->mainBuffer() == mSinkBuffer
|| track->mainBuffer() == mMixerBuffer)) {
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
//目的buffer
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
// TODO: override track->mainBuffer()?
mMixerBufferValid = true;
} else {
//...
}
//aux buffer
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
// reset retry count
track->mRetryCount = kMaxTrackRetries;
if (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||
mixerStatus != MIXER_TRACKS_ENABLED) {
//状态为 ready表示可以混音
mixerStatus = MIXER_TRACKS_READY;
}
} else {
//...
}
} // local variable scope to avoid goto warning
track_is_ready: ;
}
//...
//从mActiveTracks删除需要移除的track
removeTracks_l(*tracksToRemove);
//...
if (fastTracks > 0) {
//正常混音准备时,这里返回的是MIXER_TRACK_READY
mixerStatus = MIXER_TRACKS_READY;
}
return mixerStatus;
}
En el proceso de preparación de la mezcla, existen principalmente algunas cosas:
- Establezca los parámetros necesarios para la mezcla, incluidos: volumen, búfer de origen, búfer de destino, formato de audio, si desea volver a muestrear, etc.
- Eliminar pistas agregadas a tracksToRemove
- Devuelve el estado actual mMixerStatus
2.2 análisis threadLoop_mix
Solo cuando el valor mMixerStatus devuelto por prepareTrack_l es MIXER_TRACK_READY, podemos ingresar threadLoop_mix para mezclar. El código se implementa de la siguiente manera:
void AudioFlinger::MixerThread::threadLoop_mix()
{
int64_t pts;
status_t status = INVALID_OPERATION;
//获取timestamps,即输出时间戳,用于seek到源buffer的某个位置进行混音
if (mNormalSink != 0) {
status = mNormalSink->getNextWriteTimestamp(&pts);
} else {
status = mOutputSink->getNextWriteTimestamp(&pts);
}
if (status != NO_ERROR) {
pts = AudioBufferProvider::kInvalidPTS;
}
//AudioMixer混音
mAudioMixer->process(pts);
//混音了多少音频数据
mCurrentWriteLength = mSinkBufferSize;
if ((sleepTime == 0) && (sleepTimeShift > 0)) {
sleepTimeShift--;
}
//等不需要睡眠时直接输出音频
sleepTime = 0;
//待机时间更新
standbyTime = systemTime() + standbyDelay;
}
Con los parámetros establecidos por prepareTrack_l, todo lo que necesita hacer en threadLoop_mix es llamar al método de proceso AudioMixer para mezclar.
2.3 threadLoop_write 分析
threadLoop_write se usa para la salida de audio después de la mezcla, y el código se implementa de la siguiente manera:
ssize_t AudioFlinger::MixerThread::threadLoop_write()
{
if (mFastMixer != 0) {
//...fastMixer处理
}
return PlaybackThread::threadLoop_write();
}
Continúe analizando PlaybackThread :: threadLoop_write (), la implementación del código es la siguiente:
// shared by MIXER and DIRECT, overridden by DUPLICATING
ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
{
// FIXME rewrite to reduce number of system calls
mLastWriteTime = systemTime();
mInWrite = true;
ssize_t bytesWritten;
const size_t offset = mCurrentWriteLength - mBytesRemaining;
// If an NBAIO sink is present, use it to write the normal mixer's submix
if (mNormalSink != 0) {
//将Buffer写到声卡上
ssize_t framesWritten = mNormalSink->write((char *)mSinkBuffer + offset, count);
//...
// otherwise use the HAL / AudioStreamOut directly
} else {
//如果用fastMixer的话其实会走该分支,先忽略
// Direct output and offload threads
bytesWritten = mOutput->stream->write(mOutput->stream,
(char *)mSinkBuffer + offset, mBytesRemaining);
//...
}
//...
mNumWrites++;
mInWrite = false;
mStandby = false;
return bytesWritten;//返回输出的音频数据量
}
2.4 Resumen del proceso
Organice el trabajo realizado en threadloop de la siguiente manera:
@ 1 prepareTracks_l:
- 确定 pista habilitada, pista deshabilitada
- Para la pista habilitada, configure los parámetros en mState.tracks [x]
@ 2 threadLoop_mix: procesamiento de datos (como remuestreo), mezcla
- Determine el enlace: analice los datos de mState.tracks [x] uno por uno, determine las pistas [x] .hook según su formato y luego determine el mState.hook total
- Enlace de llamada: simplemente llame al mState.hook total, llamará a cada mState.tracks [x] .hook
- Los datos mixtos se colocarán en el BUFFER temporal de mState.outputTemp
- Luego convierta el formato y guárdelo en thread.mMixerBuffer
@ 3 memcpy_by_audio_format: copie datos de thread.mMixerBuffer o thread.mEffectBuffer a thread.mSinkBuffer
@ 4 threadLoop_write: escriba thread.mSinkBuffer a la tarjeta de sonido
@ 5 threadLoop_exit