Android Audio Tutorial [Part Four] : PlaybackThread


In the previous tutorial we saw how the PlaybackThread is created and how the thread looping starts.

Let us analyze further.


PlaybackThread:

When a PlaybackThread enters the threadLoop() the audio transaction is officially enabled.

If we look closely, we will find that this loop will continue to call several interfaces that start with "threadLoop_", such as threadLoop_mix( ), threadLoop_sleepTime( ), threadLoop_standby( ), etc.
Starting the above with such a prefix is ​​because these functions are invoked in the body of threadLoop which can be said to represent the various steps that the PlaybackThread needs to complete.

We have already seen in the previous tutorial that when the program executes to PlaybackThread::onFirstRef( ) it will actually start a thread just keep running the threadLoop( ) function.

Let us take a look at the processing flow of this loop body. From the file frameworks/av/services/audioflinger/Threads.cpp.



Uffff , Quite a Long function. 


Let us step back and see some of the important points of interest in this function.


while (!exitPending()) :
The condition of the loop is !exitPending() is true.
a.     This function belongs to the Thread class, which mainly determines whether to end the thread by determining the value of the internal variable mExitPending.
b.     The variable mExitPending is fasle when Thread is initialized.
c.     If someone later requests to exit through requestExit(), requestExitAndWait, etc., this value will change, which will cause PlaybackThread to end the loop.

processConfigEvents_l(): PlaybackThread::threadLoop() handles the config event.
a.     When a configuration change event occurs, PlaybackThread can be notified via sendConfigEvent.
b.     This function will add events to the mConfigEvents global variable for processing by processConfigEvents.
c.     Configuration events include the following:






shouldStandby_l(): determine if the current condition of Standby is met, and if so, call threadLoop_standby.

mMixerStatus = prepareTracks_l(&tracksToRemove): data preparation using prepareTracks_l .


Wow, Some big function. Let us not get frightened by this function above and try to see some of the core steps done above.

  



Get the number of currently active tracksmActiveTracks is a SortedVector that records the currently active track. It will expand with the addition of the new AudioTrack, and will also remove the corresponding track if necessary (AudioTrack is over, or something goes wrong, etc.).


Loop through each track, which is the core of the function.
Processing under FastTrack [We will cover Fastmixer threads later].




This is the most important step in the preparation work. It is buffering data. Before learning the code details, let us first understand the underrun conditions that are likely to occur during data transfer.

In a "producer-consumer" relationship between two devices or processes, Underrun occurs if the speed of production is not as fast as the consumer consumes.

Taking audio playback as an example, the sound heard by the user at this time may be intermittent, or the data in the current buffer may be repeatedly played (depending on the specific implementation).


Preparing data is divided into the following sub-sections.




minFrames : How much frame data do I need to prepare before playing back audio?

When track->sharedBuffer() is 0, it indicates that the data is transmitted at once.

The global variable mSampleRate is obtained via mOutput->stream->common.get_sample_rate, which is provided by the HAL and represents the device's Sampling rate.


Check if data is ready.
In the previous steps we had already calculated the minimum frame value of the data, ie minFrames, and then determine whether the current situation meets this criterion.



After getting the values ​​of vl, vr, and va we apply them to AudioMixer.
The real implementation is in threadLoop_mix() for the volume levels which we will discuss later.




After performing the above processing for each track, it returns a result at the end, which usually depends on
whether there is activetrack
active track data is ready




The final value returned will affect the next step in threadLoop.


Let us continue our analysis of prepareTracks_l, we return to the previous threadLoop.



If the data preparation in the previous step has been completed (ie, the return value is MIXER_TRACKS_READY), a real mix operation is started which is threadLoop_mix() else it sleeps for a certain period of time - so it will cycle until it exits the loop.




This will enter AudioMixer's processing function [We will come back to this later].



Going back, If the data is not yet ready, AudioFlinger will call threadLoop_sleepTime( ) to calculate how long to sleep (the variable sleepTime), and perform usleep to sleep at the end of the threadLoop main loop (before the remove track).




Finally, threadLoop_write() writes data to the HAL and writes it in chunks to the hardware device.




The above is composed of two situations:

1.     If an NBAIO sink is present, use it to write the normal mixer's submix.
2.     otherwise use the HAL / AudioStreamOut directly.



Coming back to threadLoop, Finally, remove the Tracks indicated in tracksToRemove.
Whether or not to remove a track is determined in prepareTracks_l( ).



How does prepareTracks_l( ) decide which tracks to remove?

1.     For Fast Track, if its status (mState) is STOPPING_2, PAUSED, TERMINATED, STOPPED, FLUSHED, or the status is ACTIVE but underrun exceeds the limit (mRetryCount), it will be added to the tracksToRemove list.
2.     The current track data is not ready, and is STATICTRACK or has been stopped/paused, will also be added to the tracksToRemove list.
3.     For the tracks in the tracksToRemove list, the output associated with it will receive a stop request (initiated by AudioSystem::stopOutput).

We will continue our analysis of other components in the next tutorial/s.

Comments

Popular posts from this blog

Android Audio Tutorial [Part Three] : AudioFlinger Introduction and Initialization

Android External Storage Support: Volume Daemon (vold) Architecture

Android Audio Tutorial [Part One] : Introduction