Android音频子系统,音频流的回放(四)

来源:互联网 发布:java try的用法 编辑:程序博客网 时间:2024/06/02 09:25

Audiotrack被用于音频流的回放,用来传输数据。

AudioTrack支持两种数据模式:

一种是Static,静态就是指数据一次性交付给对方,简单高效,一次完成所有数据的传递。适用于铃声、系统提醒等对内存要求小的播放操作。

一种是streaming,流模式和基于网络的音频流回放类似,音频数据严格按照要求不断地传递给接收方,直到结束。通常适用于音频文件较大时;音频属相要求高,如采样率高、深度达的数据;音频数据时实时产生的。

 

源码中有Audiotrack的应用范例:用于测试立体声左右声道最大音量。

Framework/base/media/tests/../MediaAudioTrackTest.java

public void testSetStereoVolumeMax() throws Exception {final int TEST_SR = 22050;    final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;    final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;    final int TEST_MODE = AudioTrack.MODE_STREAM;final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;// step1,计算最小缓冲区大小int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);//step2,生成audiotrack对象。AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, minBuffSize, TEST_MODE);byte data[] = new byte[minBuffSize/2];// step3,写入音频数据track.write(data, 0, data.length);track.write(data, 0, data.length);// step4,开始播放音频track.play();//获取最大音量值。float maxVol = AudioTrack.getMaxVolume();track.release();}


上面的例子,包含了AudioTrack的常规操作

Step1,getMinBufferSize,获取最小的Buffer大小。函数实现如下:

AudioTrack.java

static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {//获取音频的声道数属性。switch(channelConfig) {case AudioFormat.CHANNEL_OUT_MONO:channelCount = 1;break;case AudioFormat.CHANNEL_OUT_STEREO:channelCount = 2;break;}//检查音频采样深度。if (!AudioFormat.isPublicEncoding(audioFormat)) { return ERROR_BAD_VALUE; }//检查采样频率。if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||(sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) { return ERROR_BAD_VALUE; }//最小buffer的计算,取决于采样频率,声道数,采样深度这三个属性。具体计算在native层,android_media_audiotrack.cpp中。int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);}

Step2,有了minbuffersize,就可以创建一个audiotrack对象了。

转到audiotrack.java的构造函数

AudioTrack.java

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,        int bufferSizeInBytes, int mode)throws IllegalArgumentException {// native initialization    int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,        sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);}

AudioTrack的一个重要任务是和Audioflinger建立联系,这是有native代码实现的。

android_media_AudioTrack.cpp

static jint android_media_AudioTrack_setup(…){//创建一个native层的audiotrack。lpTrack = new AudioTrack();//存储音频数据的地方lpJniStorage = new AudioTrackJniStorage();//调用Audiotrack的set函数,设置各种属性    status = lpTrack->set(    AUDIO_STREAM_DEFAULT,            sampleRateInHertz,            format,// word length, PCM            nativeChannelMask,            frameCount,            AUDIO_OUTPUT_FLAG_NONE,            audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack0,//不同的内存模式,这个参数不一样,MODE_STREAM,这个值是0;MODE_STATIC,这个是:lpJniStorage->mMemBase            0,// shared mem             true,// thread can call Java            sessionId,// audio session ID            AudioTrack::TRANSFER_SYNC,            NULL,                         // default offloadInfo            -1, -1,                       // default uid, pid values            paa);}

接着看AudioTrack.cpp中的set函数的实现:

status_t AudioTrack::set(…){//默认刘类型是AUDIO_STREAM_MUSIC    if (streamType == AUDIO_STREAM_DEFAULT) {        streamType = AUDIO_STREAM_MUSIC;}//默认采样深度是16bit。    if (format == AUDIO_FORMAT_DEFAULT) {        format = AUDIO_FORMAT_PCM_16_BIT;}在经过一些有效性检查后,Audiotrack就要使用底层的音频服务了,这里的底层服务是指audioflinger,audiopolicyservice等,android系统在Audiotrack和底层服务之间又加了audiosystem和Audioservice,这就降低了Audiotrack与底层服务间的耦合。就是说,即使不同版本Android的音频系统改动较大,但只要audioSystem,audioservice向上的接口不变,那么audiotrack就需要做任何修改。//step1,创建一个线程AudioTrackThreadmAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);//step2,创建一个IAudioTrack,createTrack_l函数中会经过Auidosystem,进一步调用到audiopolicyservice的服务接口。status_t status = createTrack_l();}

Step1,AudioTrackThread线程,一方面在Audiotrack和audioflinger之间做数据传输,作为客户端audiotrack用这个线程不断的传送数据,作为接收端的audioflinger也有一个线程(playbackthread)用于接收客户端发来的音频数据;另一方面,用于报告数据传输状态,Audiotrack中保存了mCbf变量,是callback_t类型的回调函数,用于回传音频传输过程中的状态给调用者。

Step2,createTrack_l中,会由audiosystem作为中转,调用audiopolicyservice,audioflinger实现的功能,如:getOutputForAttr,getFrameCount等;还有一个重要操作,是建立audiotrack与audioflinger之间跨进程沟通的桥梁IAudioTrack。


status_t AudioTrack::createTrack_l(){status = AudioSystem::getOutputForAttr(attr, &output,…);status = AudioSystem::getFrameCount(output, &mAfFrameCount);sp<IAudioTrack> track = audioFlinger->createTrack(streamType,…);}

AudioSystem::getOutputForAttr(…);这个功能实现,最终还是有AudioPolicyservice来完成的。Getoutput会在当前系统中寻找最适合audiotrack的audio interface,及output输出(由audioflinger通过openoutput打开的通道),然后audiotrack会向这个output申请一个track(PlaybackThread::Track),audiotrack在Audioflinger内部就是以这个track来管理的,因为audiotrack和Audioflinger之间是跨进程的,所以还创建了他们之间的桥梁是IaudioTrack。

 

createTrack中的关键步骤:

AudioFlinger.cpp

sp<IAudioTrack> AudioFlinger::createTrack(…){sp<PlaybackThread::Track> track;sp<TrackHandle> trackHandle;//这里的output (audio_io_handle_t),是在audioflinger::openoutput时产生的,这个值跟playbackthread是对应的。依据audio_io_handle_t全局标记值,找到匹配的playbackthread。然后在其内部创建playbacktrack::track对象,这个track的父类是trackBase,所有track对象都被添加到playbackthread:: mTracks中进行管理。TrackHandle就是Iaudiotrack.。PlaybackThread *thread = checkPlaybackThread_l(output);track = thread->createTrack_l(client, streamType, sampleRate, format,…);trackHandle = new TrackHandle(track);}

Tracks.cpp

在创建Track对象时,同时调用了父类Trackbase的构造函数:

AudioFlinger::PlaybackThread::Track::Track(…) :  TrackBase(…){…}

在trackbase的构造函数中,申请了一块缓冲区,这块空间是可以跨进程共享的。AudioTrack通过track->getCblk();获取的就是这块内存空间,这块内存空间是针对mode_stream流模式下的音频数据的存放的。静态模式下音频数据的存放空间是由AudioTrackJniStorage来申请的。

AudioFlinger::ThreadBase::TrackBase::TrackBase(…){mCblkMemory = client->heap()->allocate(size);}

AudioTrack.cpp

status_t AudioTrack::createTrack_l(){sp<IAudioTrack> track = audioFlinger->createTrack(…);sp<IMemory> iMem = track->getCblk();}


下面是getCblk()的调用堆栈:

sp<IMemory> AudioFlinger::TrackHandle::getCblk() @Tracks.cppconst {    return mTrack->getCblk();}sp<IMemory> getCblk()@TrackBase.h const { return mCblkMemory; }到这里,Audiotrack可以通过IaudioTrack调用audioflinger的服务,应用实例(前面的testSetStereoVolumeMax ()@MediaAudioTrackTest.java)可以通过不断写入音频数据(track.write(data, 0, data.length);)回放声音。