One of Android audio systems: How does audiotrack exchange audio data with audioflinger?

Source: Internet
Author: User
Introduction

In the Audio Subsystem of the android framework, each audio stream corresponds to an audiotrack instance. Each audiotrack is registered to audioflinger at creation, audioflinger is used to mix all audiotracks and then deliver them to audiohardware for playback. Currently, froyo of Android allows you to create up to 32 audio streams at the same time. That is to say, mixer can process up to 32 audiotrack data streams simultaneously.

How to Use audiotrack

The main code of audiotrack is in frameworks/base/Media/libmedia/audiotrack. cpp. Now let's take an example to learn how to use audiotrack. tonegenerator is an implementation of Telephone Dialing and other tone waveforms in Android. We will take it as an example:

 

The initialization function of tonegenerator:

Bool tonegenerator: initaudiotrack () {<br/> // open audio track in mono, PCM 16bit, default sampling rate, default buffer size <br/> mpaudiotrack = new audiotrack (); <br/> mpaudiotrack-> set (mstreamtype, <br/> 0, <br/> audiosystem: pcm_16_bit, <br/> audiosystem: channel_out_mono, <br/> 0, <br/> 0, <br/> audiocallback, <br/> This, <br/> 0, <br/> 0, <br/> mthreadcancalljava); <br/> If (mpaudiotrack-> Initcheck ()! = No_error) {<br/> LogE ("audiotrack-> initcheck failed"); <br/> goto initaudiotrack_exit; <br/>}< br/> mpaudiotrack-> setvolume (mvolume, mvolume); <br/> mstate = tone_init; <br/> ...... <br/>}< br/>

 

It can be seen that the creation process is very simple. First, a new audiotrack instance is created, and then the set member function is called to complete parameter settings and register it in audioflinger, then, you can call other functions such as setting the volume to further set audio parameters. An important parameter is audiocallback. audiocallback is a callback function that responds to audiotrack notifications, such as data filling, loop playback, and playing position triggering. The callback function is usually written as follows:

Void tonegenerator: audiocallback (INT event, void * user, void * info) {<br/> If (event! = Audiotrack: event_more_data) return; <br/> audiotrack: Buffer * buffer = static_cast <audiotrack: Buffer *> (Info ); <br/> tonegenerator * lptonegen = static_cast <tonegenerator *> (User); <br/> short * lpout = buffer-> I16; <br/> unsigned int lnumsmp = buffer-> size/sizeof (short); <br/> const tonedescriptor * lptonedesc = lptonegen-> mptonedesc; <br/> If (buffer-> size = 0) return; </P> <p> // clear output buffer: wavegenerator accumulates into lpout buffer <br/> memset (lpout, 0, buffer-> size); <br/> ...... <br/> // the following code generates tone data .... <br/>}

This function first determines whether the event type is event_more_data. If yes, the subsequent code will fill in the corresponding audio data and return it. Of course, you can handle other events. The following are available event types:

Enum event_type {<br/> event_more_data = 0, // request to write more data to PCM buffer. <br/> event_underrun = 1, // PCM buffer underrun occured. <br/> event_loop_end = 2, // Sample loop end was reached; playback restarted from loop start if loop count was not 0. <br/> event_marker = 3, // playback head is at the specified marker position (see setmarkerposition ()). <br/> event_new_pos = 4, // playback head is at a new position (see setpositionupdateperiod ()). <br/> event_buffer_end = 5 // playback head is at the end of the buffer. <br/> };

Start playing:

Mpaudiotrack-> Start ();

Stop playing:

Mpaudiotrack-> stop ();

Simply call the member functions start () and stop.

 

Communication between audiotrack and audioflinger

Generally, audiotrack and audioflinger are not in the same process. They are connected through the Binder Mechanism in Android.

Audioflinger is a service in Android and has been loaded at Android startup. The following figure shows the relationship between them:

Figure 1 Relationship Between audiotrack and audioflinger

We can understand the meaning of this image as follows:

  • Audio_track_cblk_t implements a circular FIFO;
  • Audiotrack is a FIFO data producer;
  • Audioflinger is a FIFO data consumer.

 

Establishing a connection

The following sequence diagram shows how audiotrack and audioflinger establish connections:

Figure 2 associate audiotrack with audioflinger

Explain the process:

  • The framework or Java layer uses JNI, new audiotrack ();
  • Getoutput () is called in a series of ways based on streamtype and other parameters ();
  • If necessary, audioflinger enables different hardware devices based on streamtype;
  • Audioflinger creates a mixing thread for the output device: mixerthread (), and returns the thread ID to audiotrack as the return value of getoutput;
  • Audiotrack calls audioflinger's createtrack () through the Binder Mechanism ();
  • Audioflinger registers the audiotrack to mixerthread;
  • Audioflinger creates a trackhandle for control and uses the iaudiotrack interface as the return value of createtrack;
  • Audiotrack uses the iaudiotrack interface to obtain the FIFO (audio_track_cblk_t) created in audioflinger );
  • Audiotrack creates its own monitoring thread: audiotrackthread;

Since then, audiotrack has established all contact work with audioflinger. Next, audiotrack can:

  • Use the iaudiotrack interface to control the status of the audio track, such as start, stop, and pause;
  • Writes data to the FIFO to enable continuous audio playback;
  • Monitors the occurrence of events and interacts with user programs through the audiocallback callback function;

 

Audio_track_cblk_t for FIFO management

Audio_track_cblk_t is the key to implement FIFO. This structure is applied for memory by audioflinger during createtrack, and then returns audiotrack through the imemory interface, in this way, audiotrack and audioflinger manage the same audio_track_cblk_t through which audio data is written to the first-in-first-out (FIFO) and audioflinger reads audio data from the first-in-first-out (FIFO, after being mixer, it is sent to audiohardware for playback.

The main data member of audio_track_cblk_t:

User -- audiotrack current write location offset
Userbase -- the reference position of the audiotrack write offset. The real FIFO address pointer can be determined based on the user value.
Server -- audioflinger current read location offset
Serverbase -- the reference location of the audioflinger read offset. The real FIFO address pointer can be determined based on the server value.

Framecount -- the size of the FIFO, in the unit of audio data frames. The size of each 16-bit audio frame is 2 bytes.

Buffers -- the starting address pointing to the FIFO address

Out -- audio stream direction. For audiotrack, out = 1, for audiorecord, out = 0

The main member functions of audio_track_cblk_t:

Framesavailable_l () and framesavailable () are used to obtain the size of the writable free space in the first-in-first-out (only the difference between locking and non-locking.

Uint32_t audio_track_cblk_t: framesavailable_l () <br/>{< br/> uint32_t u = This-> User; <br/> uint32_t S = This-> server; <br/> If (out) {<br/> uint32_t Limit = (S <loopstart )? S: loopstart; <br/> return limit + framecount-U; <br/>}else {<br/> return framecount + u-s; <br/>}< br/>}

 

Framesready () is used to obtain the read space in the FIFO.

Uint32_t audio_track_cblk_t: framesready () <br/>{< br/> uint32_t u = This-> User; <br/> uint32_t S = This-> server; <br/> If (out) {<br/> If (U <loopend) {<br/> return u-s; <br/>} else {<br/> mutex: autolock _ L (Lock); <br/> If (loopcount> = 0) {<br/> return (loopend-loopstart) * loopcount + u-s; <br/>} else {<br/> return uint_max; <br/>}< br/>} else {<br/> return S-U; <br/>}< br/>

 

Let's take a look at the following:

_____________________________________________

^

Buffer_start server (s) user (u) buffer_end

 

Obviously, frameready = u-s, frameavalible = framecount-frameready = framecount-U + S

 

Someone may ask, it should be a circular buffer. Once the user has crossed buffer_end, the following situation should occur:

_____________________________________________

^

Buffer_start user (u) server (s) buffer_end

At this time, U is in front of S, and it will be wrong to use the formula above, but Android uses some techniques to ensure that the above formula has always been true. We will analyze the code of the following three functions first:

Uint32_t audio_track_cblk_t: stepuser (uint32_t framecount) <br/>{< br/> uint32_t u = This-> User; <br/> U + = framecount; <br/> ...... <br/> If (u> = userbase + this-> framecount) {<br/> userbase + = This-> framecount; <br/>}< br/> This-> User = u; <br/> ...... <br/> return U; <br/>}< br/>

Bool audio_track_cblk_t: stepserver (uint32_t framecount) <br/> {<br/> // The code below simulates lock-with-Timeout <br/> // we must do this to protect the audioflinger server <br/>/ /As this lock is shared with the client. <br/> status_t err; <br/> err = lock. trylock (); <br/> If (ERR =-ebusy) {// just wait a bit <br/> usleep (1000); <br/> err = lock. trylock (); <br/>}< br/> If (Err! = No_error) {<br/> // probably, the client just died. <br/> return false; <br/>}< br/> uint32_t S = This-> server; <br/> S + = framecount; <br/> // omit some code <br/> //...... <br/> If (S> = serverbase + this-> framecount) {<br/> serverbase + = This-> framecount; <br/>}< br/> This-> Server = s; <br/> cv. signal (); <br/> lock. unlock (); <br/> return true; <br/>}

Void * audio_track_cblk_t: buffer (uint32_t offset) const <br/>{< br/> return (int8_t *) This-> buffers + (offset-userbase) * This-> framesize; <br/>}

 

Stepuser () and stepserver are used to adjust the current offset. We can see that they only add the value of the member variable user or server to the number to be moved, the user and server values do not take into account the FIFO boundary issue. As the data is constantly written and read, the user and server values increase constantly. As long as the processing is proper, the user always appears behind the server, therefore, the algorithms in frameavalile () and frameready () will remain valid. According to this algorithm, the user and server values may be greater than the first-in-first-out size (framcount). How can we determine the true position of the write pointer? Here, the userbase member variable is used. In stepuser (), userbase adds framecount whenever the user value crosses (userbase + framecount, the offset mapped to the FIFO can always be obtained through (User-userbase. Therefore, the write address pointer for the current FIFO can be returned through the member function Buffer:

P = mclbk-> buffer (mclbk-> User );

 

In audiotrack, two functions are encapsulated: obtainbuffer () and releasebuffer () operation FIFO, obtainbuffer () to obtain the number of currently writable and the position of the write pointer, releasebuffer () it is called after data is written. In fact, stepuser () is called to adjust the offset position.

 

Imemory Interface

During the createtrack process, audioflinger applies for a piece of Memory Based on the passed framecount parameter. audiotrack can obtain the imemory interface pointing to the memory block through the getcblk () function of the iaudiotrack interface, then audiotrack uses the pointer () function of the imemory interface to obtain the pointer to the memory block. The starting part of the memory is the audio_track_cblk_t structure, followed by the FIFO memory with the size of framesize.

 

Imemory-> pointer () ----> | _______________________________________________________

|__ Audio_track_cblk_t __| _______ buffer of FIFO (size = framecount) ____ |

 

 

You can see the createtrack () Code of audiotrack:

Sp <iaudiotrack> track = audioflinger-> createtrack (getpid (), <br/> streamtype, <br/> samplerate, <br/> Format, <br/> channelcount, <br/> framecount, <br/> (uint16_t) flags) <16, <br/> sharedbuffer, <br/> output, <br/> & status ); <br/> // obtain the imemory interface <br/> sp <imemory> cblk = track-> getcblk (); <br/> maudiotrack. clear (); <br/> maudiotrack = track; <br/> mcblkmemory. clear (); <br/> mcblkmemory = cblk; <br/> // obtain the audio_track_cblk_t structure <br/> mcblk = static_cast <audio_track_cblk_t *> (cblk-> pointer ()); <br/> // This FIFO is used for output <br/> mcblk-> out = 1; <br/> // update buffer size in case it has been limited by audioflinger during track creation <br/> mframecount = mcblk-> framecount; <br/> If (sharedbuffer = 0) {<br/> // assign a value to the start address of the FIFO <br/> mcblk-> buffers = (char *) mcblk + sizeof (audio_track_cblk_t); <br/>}else {<br/> .......... <br/>}

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.