androidaudio架構
㈠ android.media.AsyncPlayer這個類應該怎麼用
代碼結構:
Open Core 的代碼在Android 代碼的 External/Opencore 目錄中 。這個目錄是OpenCore
的根目錄,其中包含的子目錄如下所示 :
android :這裡面是一個上層的庫,它實現了一個為Android 使用的音視頻採集,播放的介面,和DRM 數字版權管理的介面實現。
baselibs :包含數據結構和線程安全等內容的底層庫
codecs_v2 :音視頻的編解碼器,基於 OpenMAX 實現
engines :核心部分 ,多媒體 引擎的實現
extern_libs_v2 :包含了 khronos 的 OpenMAX 的頭文件
fileformats :文件格式的解析( parser )工具
nodes :提供一些PVMF 的NODE ,主要是編解碼和文件解析方面的。
oscl :操作系統兼容庫
pvmi : 輸入輸出控制的抽象介面
protocols :主要是與網路相關的 RTSP 、 RTP 、 HTTP 等協議 的相關內容
pvcommon : pvcommon 庫文件的 Android.mk 文件,沒有源文件。
pvplayer : pvplayer 庫文件的 Android.mk 文件,沒有源文件。
pvauthor : pvauthor 庫文件的 Android.mk 文件,沒有源文件。
tools_v2 :編譯工具以及一些可注冊的模塊。
本文主要介紹Android MediaPlayer的架構,主要由OpenCore 里的PV Player來實現的。
1.概述
Android的MediaPlayer包含了Audio和Video的播放功能,Music和Video兩個應用程序都是調用MediaPlayer實現的。
代碼主要分布在以下的目錄中:
java程序的路徑:
packages/apps/Music/src/com/android/music/
JAVA類的路徑:
frameworks/base/media/java/android/media/MediaPlayer.java
JAVA本地調用部分(JNI):
frameworks/base/media/jni/android_media_MediaPlayer.cpp
編譯為 libmedia_jni.so
頭文件:
frameworks/base/include/media/
多媒體庫:
frameworks/base/media/libmedia/
編譯為 libmedia.so
多媒體服務:
frameworks/base/media/libmediaplayerservice/
編譯為 libmediaplayerservice.so
具體實現:
external/opencore/
編譯為 libopencoreplayer.so
libopencoreplayer.so是主要的實現部分,其他的庫基本上都是在其上建立的封裝和為建立進程間通訊的機制。
2.框架
運行的時候,大致可以分成Client和Server兩個部分,分別在兩個進程中運行,使用Binder機制實現IPC通訊。從框架結構上來看,IMediaPlayerService.h、IMediaPlayerClient.h和MediaPlayer.h三個類定義了MeidaPlayer的介面和架構,MediaPlayerService.cpp和mediaplayer.cpp兩個文件用於MeidaPlayer架構的實現,MeidaPlayer的具體功能在PVPlayer(庫libopencoreplayer.so)中的實現。
2.1 IMediaPlayerClient.h
描述一個MediaPlayer客戶端的介面
class IMediaPlayerClient: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerClient);
virtual void notify(int msg, int ext1, int ext2) = 0;
};
class BnMediaPlayerClient: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在定義中,IMediaPlayerClient類繼承IInterface,並定義了一個MediaPlayer客戶端的介面,BnMediaPlayerClient繼承了BnInterface,這是為基於Android的基礎類Binder機制實現在進程通訊而構建的。事實上,根據BnInterface類模版的定義,BnInterface類相當於雙繼承了BnInterface和ImediaPlayerClient,這是Android一種常用的定義方式。
2.2 mediaplayer.h
對外的介面類,它最主要是定義了一個MediaPlayer類:
class MediaPlayer : public BnMediaPlayerClient
{
public:
MediaPlayer();
~MediaPlayer();
void onFirstRef();
void disconnect();
status_t setDataSource(const char *url);
status_t setDataSource(int fd, int64_t offset, int64_t length);
status_t setVideoSurface(const sp& surface);
status_t setListener(const sp& listener);
status_t prepare();
status_t prepareAsync();
status_t start();
status_t stop();
status_t pause();
bool isPlaying();
status_t getVideoWidth(int *w);
status_t getVideoHeight(int *h);
status_t seekTo(int msec);
status_t getCurrentPosition(int *msec);
status_t getDuration(int *msec);
status_t reset();
status_t setAudioStreamType(int type);
status_t setLooping(int loop);
status_t setVolume(float leftVolume, float rightVolume);
void notify(int msg, int ext1, int ext2);
static sp decode(const char* url, uint32_t *pSampleRate, int*
pNumChannels);
static sp decode(int fd, int64_t offset, int64_t length, uint32_t
*pSampleRate, int* pNumChannels);
//……
}
從介面中可以看出MediaPlayer類剛好實現了一個MediaPlayer的基本操作,例如播放(start)、停止(stop)、暫停(pause)等。
另外的一個類DeathNotifier在MediaPlayer類中定義,它繼承了IBinder類中的DeathRecipient類:
class DeathNotifier: public IBinder:: DeathRecipient
{
public:
DeathNotifier() {}
virtual ~DeathNotifier();
virtual void binderDied(const wp& who);
};
事實上,MediaPlayer類正是間接地繼承了IBinder,而MediaPlayer:: DeathNotifier類繼承了IBinder::
DeathRecipient,這都是為了實現進程間通訊而構建的。
2.3 IMediaPlayer.h
主要的的內容是一個實現MediaPlayer功能的介面:
class IMediaPlayer: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayer);
virtual void disconnect() = 0;
virtual status_t setVideoSurface(const sp& surface) = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
virtual status_t pause() = 0;
virtual status_t isPlaying(bool* state) = 0;
virtual status_t getVideoSize(int* w, int* h) = 0;
virtual status_t seekTo(int msec) = 0;
virtual status_t getCurrentPosition(int* msec) = 0;
virtual status_t getDuration(int* msec) = 0;
virtual status_t reset() = 0;
virtual status_t setAudioStreamType(int type) = 0;
virtual status_t setLooping(int loop) = 0;
virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
};
class BnMediaPlayer: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在IMediaPlayer類中,主要定義MediaPlayer的功能介面,這個類必須被繼承才能夠使用。值得注意的是,這些介面和MediaPlayer類的介面有些類似,但是它們並沒有直接的關系。事實上,在MediaPlayer類的各種實現中,一般都會通過調用IMediaPlayer類的實現類來完成。
2.4 頭文件IMediaPlayerService.h
描述一個MediaPlayer的服務,定義方式如下所示:
class IMediaPlayerService: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerService);
virtual sp create(pid_t pid, const
sp& client, const char* url) = 0;
virtual sp create(pid_t pid, const
sp& client, int fd, int64_t offset, int64_t length) =
0;
virtual sp decode(const char* url, uint32_t *pSampleRate, int*
pNumChannels) = 0;
virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t
*pSampleRate, int* pNumChannels) = 0;
};
class BnMediaPlayerService: public BnInterface
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
由於有純虛函數,IMediaPlayerService
以及BnMediaPlayerService必須被繼承實現才能夠使用,在IMediaPlayerService定義的create和decode等介面,事實上是必須被繼承者實現的內容。注意,create的返回值的類型是sp,這個IMediaPlayer正是提供實現功能的介面。
3 實現
3.1 App
在packages/apps/Music/src/com/android/music/里的MediaPlaybackService.java文件中,包含了對MediaPlayer的調用。
在MediaPlaybackService.java中包含對包的引用:
import android.media.MediaPlayer;
在MediaPlaybackService類的內部,定義了MultiPlayer類:
private class MultiPlayer {
private MediaPlayer mMediaPlayer = new MediaPlayer();
}
MultiPlayer類中使用了MediaPlayer類,其中有一些對這個MediaPlayer的調用,調用的過程如下所示:
mMediaPlayer.reset();
mMediaPlayer.setDataSource(path);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
reset,setDataSource和setAudioStreamType等介面就是通過JAVA本地調用(JNI)來實現的。
3.2 Jni
在frameworks/base/media/jni/android_media_MediaPlayer.cpp中實現,其中android_media_MediaPlayer_reset函數的實現如下所示:
static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
{
sp mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
}
先獲取一個MediaPlayer指針,通過對它的調用來實現實際的功能。
register_android_media_MediaPlayer用於將gMethods注冊為的類"android/media/MediaPlayer",其實現如下所示。
static int register_android_media_MediaPlayer(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
// ......
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
"android/media/MediaPlayer"對應JAVA的類android.media.MediaPlayer。
3.3 libmedia.so
frameworks/base/media/libmedia/mediaplayer.cpp文件實現mediaplayer.h提供的介面,其中一個重要的片段如下所示:
const sp& MediaPlayer::getMediaPlayerService()
{
Mutex::Autolock _l(mServiceLock);
if (mMediaPlayerService.get() == 0) {
sp sm = defaultServiceManager();
sp binder;
do {
binder = sm->getService(String16("media.player"));
if (binder != 0)
break;
LOGW("MediaPlayerService not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
if (mDeathNotifier == NULL) {
mDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(mDeathNotifier);
mMediaPlayerService = interface_cast(binder);
}
LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");
return mMediaPlayerService;
}
其中最重要的一點是binder =
sm->getService(String16("media.player"));這個調用用來得到一個名稱為"media.player"的服務,這個調用返回值的類型為IBinder,根據實現將其轉換成類型IMediaPlayerService使用。
一個具體的函數setDataSource如下所示:
status_t MediaPlayer::setDataSource(const char *url)
{
LOGV("setDataSource(%s)", url);
status_t err = UNKNOWN_ERROR;
if (url != NULL) {
const sp& service(getMediaPlayerService());
if (service != 0) {
sp player(service->create(getpid(), this, url));
err = setDataSource(player);
}
}
return err;
}
㈡ 有沒有用過android sdk裡面的AudioEffect設置音效的
在Android2.3中增加了對音頻混響的支持,這些API包含在android.media.audiofx包中。
一、概述
AudioEffect是android audio framework(android 音頻框架)提供的音頻效果控制的基類。開發者不能直接使用此類,應該使用它的派生類。下面列出它的派生類。
Equalizer
Virtualizer
BassBoost
PresetReverb
EnvironmentalReverb
當創建AudioEffect時,如果音頻效果應用到一個具體的AudioTrack和MediaPlayer的實例,應用程序必須指定該實例的音頻session ID,如果要應用Global音頻輸出混響的效果必須制定Session 0。
要創建音頻輸出混響(音頻 Session 0)要求要有 MODIFY_AUDIO_SETTINGS許可權。
如果要創建的效果在audio framework不存在,那麼直接創建該效果,如果已經存在那麼直接使用此效果。如果優先順序高的對象要在低級別的對象使用該效果時,那麼控制將轉移到優先順序高的對象上,否則繼續停留在此對象上。在這種情況下,新的申請將被監聽器通知。
㈢ android audio 怎麼切換到dmic
android audio 切換到dmic 的辦法
tinyalsa訪問設備節點應該是/dev/snd/pcmCxDxp, /dev/snd/pcmCxDxc和/dev/snd/controlCx,畫圖時沒有確認造成筆誤這個框圖大致把soundrecorder從app到framework到HAL的類圖畫了出來
2.有些子類父類繼承關系沒有表示出來,從soundrecorder到AudioRecord基本就是一條線下來
3.我也沒有詳細去看代碼,stagefright這一塊很龐大,實現了多種mime格式的編解碼
音頻數據生成文件保存在sd卡中應該是在MPEG4Writer這里完成的,這個沒有細看
我們重點看下AudioSystem,AudioPolicyService,AudioFlinger和AudioHardware這塊。
4.以open_record()和get_input()這兩個方法為例我們看下這幾個類之間的調用關系
從AudioRecord.cpp開始,文件位置:frameworksasemedialibmediaAudioRecord.cpp
㈣ Android Audio簡述
audio是Android系統是比較重要的一個模塊,本人也涉足時間不是很長,經驗還是很少的,只是把自己在工作中所遇到的問題記錄。
Audio 即音頻, 也就是控制著手機中的各種聲音的輸出,比如說,音樂的播放,音量大小,按鍵音,插入耳機,聲音從耳機播放,連接藍牙,聲音從藍牙耳機中播放。還有一些HiFi播放, offload播放,高清播放。
常見的問題如下:
POP音,漏音,聲音卡頓,耳機無法識別,還有一些音頻通路等問題,還有一些穩定性的問題如:ANR、CRASH、tombstone。還有一些安全漏洞的問題(主要是核心庫那裡)。
回歸正題
-------華麗的分割線-------
audio分為 應用層,fwk層,native fwk, hal層。
常見的文件有MediaPlayer.java、 AudioSystem.java、 AudioService.java、AudioManager.java 文件路徑(framework/base/media)
AudioFlinger.cpp、AudioTrack.cpp、 AudioPolicyManager.cpp、 Threads.cpp、Tracks.cpp、 Engine.cpp、 Audiosystem.cpp 文件路徑(framework/av)
audio_hw.c 文件路徑(vendor/)
java文件我在這里就不過多的說了,沒啥好講的,主要說一下c++文件吧。
AudioFlinger 是音頻策略的執行者, AudioPolicyManager是製作音頻策略的,AudioTrack是負責播放從上層傳過來的PCM數據,簡單的說就是負責播放的。
audio_hw 是每個HAL層的文件,每個手機廠商自己定製的文件。當然Google也有。
一般呢,我們處理音頻相關的問題呢,有一些特定的套路,需要AP 側的log, 有時還需要kernel 的log, 當然最主要的是需要音頻數據。也就是出現問題時,的聲音數據,讓我們可以快速的定位出現問題的位置。
簡單的說下播放一首歌曲的流程:(以Android O 為例)
上層創建一個MediaPlayer對象,然後調用NuPlayer框架(播放器),NuPlayer先將當前歌曲的文件信息讀取,采樣率,比特率,等之類的東西。然後開始調用audio decoder (音頻解碼器) 將解碼出來的PCM數據傳給AudioTrack, Audiotrack 會創建一個Track,(每一個播放都會創建一個屬於自己的track,不用了就銷毀,最多可以同時創建32個),經過AudioFlinger重采樣之後,送到HAL層,HAL層在經過一些混音,降噪之類的處理,將聲音送到Codec,然後送給硬體輸出,進行播放。
這個是一個大概的播放流程,如果我們在播放過程中遇到了一些問題,比如說是fwk層的問題,我們就在AudioTrack與AudioFlinger之間尋問題的原因。
比如說,播放無聲,我們需要看AudioPolicyManager中的一些策略是不是將當前的track給mute了。或者是一些其他的原因等。
這里只是簡單的介紹下Audio,Audio算是一個較為復雜的模塊,還需要好好的研究。
㈤ Android Audio System 之一:AudioTrack如何與AudioFlinger交換
引子Android Framework的音頻子系統中,每一個音頻流對應著一個AudioTrack類的一個實例,每個AudioTrack會在創建時注冊到 AudioFlinger中,由AudioFlinger把所有的AudioTrack進行混合(Mixer),然後輸送到AudioHardware中 進行播放,目前Android的Froyo版本設定了同時最多可以創建32個音頻流,也就是說,Mixer最多會同時處理32個AudioTrack的數 據流。如何使用AudioTrackAudioTrack的主要代碼位於 frameworks/base/media/libmedia/audiotrack.cpp中。現在先通過一個例子來了解一下如何使用 AudioTrack,ToneGenerator是android中產生電話撥號音和其他音調波形的一個實現,我們就以它為例子:ToneGenerator的初始化函數:bool ToneGenerator::initAudioTrack() { // Open audio track in mono, PCM 16bit//, default sampling rate, default buffer size mpAudioTrack = new AudioTrack(); mpAudioTrack->set(mStreamType, 0, AudioSystem::PCM_16_BIT, AudioSystem::CHANNEL_OUT_MONO, 0, 0, audioCallback, this, 0, 0, mThreadCanCallJava); if (mpAudioTrack->initCheck() != NO_ERROR) { LOGE("AudioTrack->initCheck failed"); goto initAudioTrack_exit; } mpAudioTrack->setVolume(mVolume, mVolume); mState = TONE_INIT; ...... } 可見,創建步驟很簡單,先new一個AudioTrack的實例,然後調用set成員函數完成參數的設置並注冊到AudioFlinger中,然後可以調 用其他諸如設置音量等函數進一步設置音頻參數。其中,一個重要的參數是audioCallback,audioCallback是一個回調函數,負責響應 AudioTrack的通知,例如填充數據、循環播放、播放位置觸發等等。回調函數的寫法通常像這樣:void ToneGenerator::audioCallback(int event, void* user, void *info) { if (event != AudioTrack::EVENT_MORE_DATA) return; AudioTrack::Buffer *buffer = static_cast<AudioTrack::Buffer *>(info); ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user); short *lpOut = buffer->i16; unsigned int lNumSmp = buffer->size/sizeof(short); const ToneDescriptor *lpToneDesc = lpToneGen->mpToneDesc; if (buffer->size == 0) return; // Clear output buffer: WaveGenerator accumulates into lpOut buffer memset(lpOut, 0, buffer->size); ...... // 以下是產生音調數據的代碼,略.... } 該函數首先判斷事件的類型是否是EVENT_MORE_DATA,如果是,則後續的代碼會填充相應的音頻數據後返回,當然你可以處理其他事件,以下是可用的事件類型:enum event_type { EVENT_MORE_DATA = 0,// Request to write more data to PCM buffer. EVENT_UNDERRUN = 1,// PCM buffer underrun occured. EVENT_LOOP_END = 2,// Sample loop end was reached; playback restarted from loop start if loop count was not 0. EVENT_MARKER = 3,// Playback head is at the specified marker position (See setMarkerPosition()). EVENT_NEW_POS = 4,// Playback head is at a new position (See setPositionUpdatePeriod()). EVENT_BUFFER_END = 5// Playback head is at the end of the buffer. }; 開始播放:mpAudioTrack->start(); 停止播放:mpAudioTrack->stop(); 只要簡單地調用成員函數start()和stop()即可。AudioTrack和AudioFlinger的通信機制通常,AudioTrack和AudioFlinger並不在同一個進程中,它們通過android中的binder機制建立聯系。AudioFlinger是android中的一個service,在android啟動時就已經被載入。下面這張圖展示了他們兩個的關系:圖一AudioTrack和AudioFlinger的關系我們可以這樣理解這張圖的含義:audio_track_cblk_t實現了一個環形FIFO;AudioTrack是FIFO的數據生產者;AudioFlinger是FIFO的數據消費者。建立聯系的過程下面的序列圖展示了AudioTrack和AudioFlinger建立聯系的過程:圖二AudioTrack和AudioFlinger建立聯系解釋一下過程:Framework或者Java層通過JNI,new AudioTrack();根據StreamType等參數,通過一系列的調用getOutput();如有必要,AudioFlinger根據StreamType打開不同硬體設備;AudioFlinger為該輸出設備創建混音線程: MixerThread(),並把該線程的id作為getOutput()的返回值返回給AudioTrack;AudioTrack通過binder機制調用AudioFlinger的createTrack();AudioFlinger注冊該AudioTrack到MixerThread中;AudioFlinger創建一個用於控制的TrackHandle,並以IAudioTrack這一介面作為createTrack()的返回值;AudioTrack通過IAudioTrack介面,得到在AudioFlinger中創建的FIFO(audio_track_cblk_t);AudioTrack創建自己的監控線程:AudioTrackThread;自此,AudioTrack建立了和AudioFlinger的全部聯系工作,接下來,AudioTrack可以:通過IAudioTrack介面控制該音軌的狀態,例如start,stop,pause等等;通過對FIFO的寫入,實現連續的音頻播放;監控線程監控事件的發生,並通過audioCallback回調函數與用戶程序進行交互;FIFO的管理 audio_track_cblk_taudio_track_cblk_t這個結構是FIFO實現的關鍵,該結構是在createTrack的時候,由AudioFlinger申請相 應的內存,然後通過IMemory介面返回AudioTrack的,這樣AudioTrack和AudioFlinger管理著同一個 audio_track_cblk_t,通過它實現了環形FIFO,AudioTrack向FIFO中寫入音頻數據,AudioFlinger從FIFO 中讀取音頻數據,經Mixer後送給AudioHardware進行播放。audio_track_cblk_t的主要數據成員: user -- AudioTrack當前的寫位置的偏移 userBase -- AudioTrack寫偏移的基準位置,結合user的值方可確定真實的FIFO地址指針 server -- AudioFlinger當前的讀位置的偏移 serverBase -- AudioFlinger讀偏移的基準位置,結合server的值方可確定真實的FIFO地址指針 frameCount -- FIFO的大小,以音頻數據的幀為單位,16bit的音頻每幀的大小是2位元組 buffers -- 指向FIFO的起始地址 out -- 音頻流的方向,對於AudioTrack,out=1,對於AudioRecord,out=0audio_track_cblk_t的主要成員函數:framesAvailable_l()和framesAvailable()用於獲取FIFO中可寫的空閑空間的大小,只是加鎖和不加鎖的區別。uint32_t audio_track_cblk_t::framesAvailable_l() { uint32_t u = this->user; uint32_t s = this->server; if (out) { uint32_t limit = (s < loopStart) ? s : loopStart; return limit + frameCount - u; } else { return frameCount + u - s; } } framesReady()用於獲取FIFO中可讀取的空間大小。uint32_t audio_track_cblk_t::framesReady() { uint32_t u = this->user; uint32_t s = this->server; if (out) { if (u < loopEnd) { return u - s; } else { Mutex::Autolock _l(lock); if (loopCount >= 0) { return (loopEnd - loopStart)*loopCount + u - s; } else { return UINT_MAX; } } } else { return s - u; } } 我們看看下面的示意圖: _____________________________________________ ^ ^ ^ ^ buffer_start server(s) user(u) buffer_end 很明顯,frameReady = u - s,frameAvalible = frameCount - frameReady = frameCount - u + s 可能有人會問,應為這是一個環形的buffer,一旦user越過了buffer_end以後,應該會發生下面的情況: _____________________________________________ ^ ^ ^ ^ buffer_start user(u) server(s) buffer_end這時候u在s的前面,用上面的公式計算就會錯誤,但是android使用了一些技巧,保證了上述公式一直成立。我們先看完下面三個函數的代碼再分析:uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) { uint32_t u = this->user; u += frameCount; ...... if (u >= userBase + this->frameCount) { userBase += this->frameCount; } this->user = u; ...... return u; } bool audio_track_cblk_t::stepServer(uint32_t frameCount) { // the code below simulates lock-with-timeout // we MUST do this to protect the AudioFlinger server // as this lock is shared with the client. status_t err; err = lock.tryLock(); if (err == -EBUSY) { // just wait a bit usleep(1000); err = lock.tryLock(); } if (err != NO_ERROR) { // probably, the client just died. return false; } uint32_t s = this->server; s += frameCount; // 省略部分代碼 // ...... if (s >= serverBase + this->frameCount) { serverBase += this->frameCount; } this->server = s; cv.signal(); lock.unlock(); return true; } void* audio_track_cblk_t::buffer(uint32_t offset) const { return (int8_t *)this->buffers + (offset - userBase) * this->frameSize; } stepUser()和stepServer的作用是調整當前偏移的位置,可以看到,他們僅僅是把成員變數user或server的值加上需要移動 的數量,user和server的值並不考慮FIFO的邊界問題,隨著數據的不停寫入和讀出,user和server的值不斷增加,只要處理得 當,user總是出現在server的後面,因此frameAvalible()和frameReady()中的演算法才會一直成立。根據這種算 法,user和server的值都可能大於FIFO的大小:framCount,那麼,如何確定真正的寫指針的位置呢?這里需要用到userBase這一 成員變數,在stepUser()中,每當user的值越過(userBase+frameCount),userBase就會增加 frameCount,這樣,映射到FIFO中的偏移總是可以通過(user-userBase)獲得。因此,獲得當前FIFO的寫地址指針可以通過成員 函數buffer()返回:p = mClbk->buffer(mclbk->user);在AudioTrack中,封裝了兩個函數:obtainBuffer()和releaseBuffer()操作 FIFO,obtainBuffer()獲得當前可寫的數量和寫指針的位置,releaseBuffer()則在寫入數據後被調用,它其實就是簡單地調用 stepUser()來調整偏移的位置。IMemory介面在createTrack的過程中,AudioFlinger會根據傳入的frameCount參數,申請一塊內存,AudioTrack可以通過 IAudioTrack介面的getCblk()函數獲得指向該內存塊的IMemory介面,然後AudioTrack通過該IMemory介面的 pointer()函數獲得指向該內存塊的指針,這塊內存的開始部分就是audio_track_cblk_t結構,緊接著是大小為frameSize的 FIFO內存。IMemory->pointer() ---->|_______________________________________________________ |__audio_track_cblk_t__|_______buffer of FIFO(size==frameCount)____|看看AudioTrack的createTrack()的代碼就明白了:sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), streamType, sampleRate, format, channelCount, frameCount, ((uint16_t)flags) << 16, sharedBuffer, output, &status); // 得到IMemory介面 sp<IMemory> cblk = track->getCblk(); mAudioTrack.clear(); mAudioTrack = track; mCblkMemory.clear(); mCblkMemory = cblk; // 得到audio_track_cblk_t結構 mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); // 該FIFO用於輸出 mCblk->out = 1; // Update buffer size in case it has been limited by AudioFlinger ring track creation mFrameCount = mCblk->frameCount; if (sharedBuffer == 0) { // 給FIFO的起始地址賦值 mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); } else { .......... } (DroidPhone)
㈥ 安卓系統為什麼音質不好
Android 基於Linux,我們先來了解一下Linux的特點。Linux使用ALSA作為其音頻架構,其全稱Advanced Linux Sound Architecture,即高級Linux聲音架構的意思,在2.6核心之後,ALSA成為了Linux系統默認的音頻子架構。取代了之前的OSS[Open Sound System,開放式聲音系統]。
ALSA並不太好理解,它首先是一個驅動庫,包含了大量的音效卡設備的開源驅動,並提供了核心層API與ALSA庫通信,而ALSA庫則是應用程序訪問和操控音頻硬體的中間層,這個中間層有標准介面,開發者可以無須考慮硬體差異性進行開發,它對提升開發效率是大有幫助的。ALSA可以向下兼容OSS,因為OSS已經被淘汰,其兼容的工作模式不再討論。
這個體系被繼承到了Android當中。在Android2.2[含2,2]之前,系統文件夾中能找到一個LibAudioALSA.so的文件,這就是ALSA庫文件,其他應用程序調用它,與音效卡設備進行指令和數據通信。Android音頻架構與Linux的並無本質區別。
在桌面版本的Linux當中,為了兼容各類音效卡,Linux也設置了一個SRC[Sample Rate Converter,采樣頻率轉換]的環節,當當前采樣率低於48kHz時強制SRC到48kHz輸出。這個SRC環節位於ALSA的插件模塊中的混音器部分。Android針對這個進行了改進。
什麼是SRC?SRC即Sample Rate Converter,中文意思為采樣頻率轉換。它被音效卡愛好者所關注,大部分發燒友視SRC為音質殺手。
Android增加了一個AudioFinger,這個可以簡單的理解為Android的ALSA音頻子系統的標准化的插件模塊,它包含了AudioMixer[混音器]、AudioResampler[重采樣]等子模塊,AudioResampler即我們理解的SRC,Android換了一個新名稱而已。針對SRC,Android做了改進,但改進並不是以去除SRC為目的,而是修改了默認的輸出頻率,Android的SRC目標采樣率為44.1kHz,非該值的采樣率都將SRC處理。例如播放48kHz采樣率的信號,輸出的最終是44.1kHz,這對音質將產生負面影響。
ALSA是一個針對Linux 桌面版本設計的音頻架構,它實際上是不適合智能終端設備的,起碼裡面大量的開源驅動代碼是可以去除的,對與Android來說,這些都是廢代碼。從Android2.3起,啟用了一個新的音頻架構。它放棄了一直使用的ALSA架構,因此系統文件夾中,也不再有LibAudioALSA.so這個文件。
Android2.3起,架構已經做了修改,在針對內部代碼進行了優化,去除了冗餘代碼,理論上讓系統能變得更加高效,可以將新架構理解為一個精簡的或者為智能終端設備定製的ALSA架構。遺憾的是,它同樣存在SRC嚴重劣化的問題,通過測試可以證明。
Android 3.0專門為平板電腦設計,影音體驗變得更加重要了,是不是新系統在音質方面會有新的的進步呢,測試結果依然是令人失望的。
Android系統將采樣率同一為44.1kHz輸出,這造成了諸多限制,它將無法實現96kHz、192kHz高清音頻節目的良好回放,大量視頻節目源自DVD或者藍光碟,其採用率多為48kHz,Android設備在回放這些視頻節目時,音質也將大打折扣。
理論上軟體SRC可以通過更換演算法來實現音質提升,但卻不太現實,智能終端所採用的CPU多為ARM,ARM晶元的浮點運算力有限,而SRC需要大量的浮點運算的資源,即便有了高質量的SRC演算法,其運算也是以犧牲設備性能和耗電量為代價的,實用性差。
從Android的音頻架構及流程分析,可以認為,播放44.1kHz采樣率的音樂節目時,不會引發SRC,音質因此可以獲得保證,理論上確實如此。但它同樣存在問題,不管是之前的ALSA架構還是Android2.3之後改良的架構,其驅動庫都位於核心層,也就意味著音頻設備廠商、用戶無法象PC平台那樣安裝驅動來改善音質。實際測試也表明,Android設備音質普遍偏差,Soomal有大量測試可以證明。
我們再把目光投向iOS,iOS非常封閉,我們甚至無法獲知其架構的具體構成,但iOS設備不存在硬體設備多樣性的問題,因此要實現更好音質也會更加簡單。iOS可以實現針對性的開發和改良,以實現更好的音質。實際情況也是如此,目前為止,還沒有一款Android設備的音質可以媲美任意一款iOS設備,這種差距,我們認為不是來自硬體,而是操作系統。
Android音頻架構的局限性也使得其難以成為優質的影音平台,如果你希望設計一款基於Android的高清影音播放器,那麼首先需要做的不是設計硬體,而是去修改現有架構的不足,或者乾脆設計一個專用的架構來取代Android的通用架構。從源代碼分析,Android和原生的Linux底層能支持各種采樣率,開源也使得其具有改造基礎,因此,在技術實力強勁的公司手裡,Android也可以烏雞變鳳凰。
㈦ Android audio_policy_configuration.xml
前言
Android的audioserver 進程啟動時,會創建AudioPolicyManager,在構造函數中,首先會去解析audio_policy_configuration.xml文件。
audio音頻數據從一個源走到一個目的都是需要根據配置文件audio_policy_configuration.xml來決定,所以理解configuration配置文件中各個標簽項轉化為c++實體類的及各成員至關重要。
audio_policy_configuration.xml為音頻audio的設備、流以及路由等配置文件,裡面寫明了audio音頻部分有哪些設備、哪些流以及它們支持的編碼、格式以及通道存儲布局等等
audio_policy_configuration.xml中 的<moles>對應每一個audio hal 的so,mole中列出的mixPorts,devicePorts和routes解析之後完整的描述了音頻的路由規則
mole name
支持「primary」 (用於車載使用場景), "A2DP", "remote_submix"和"USB"。模塊名稱和相應音頻驅動程序應編譯到 audio.primary.$(variant).so 中
devicePorts
包含可從此模塊訪問的所有輸入和輸出設備(包括永久連接的設備和可移除設備)的設備描述符列表。設備的output和input,不是像mixport那樣以role來分,而是以type中有關鍵字「IN」和「OUT」來區分
mixPorts
包含由音頻 HAL 提供的所有輸出流和輸入流的列表。每個 mixPort 實例都可被視為傳輸到 Android AudioService 的物理音頻流,stream配置了自己的格式、采樣率以及mask,並且分為輸出、輸入流。一個mixPort標簽可能有多個profile屬性,也就是支持很多編碼格式屬性
routes
定義輸入和輸出設備之間或音頻流和設備之間可能存在的連接的列表。route是把deviceport和mixport連接起來的路由,數據由一個stream輸出到另一個device,或者從一個device輸出到另一個stream。
㈧ 如何在Android平台上使用USB Audio設備
需求:USB Headset插上去後,聲音要從本地CODEC切換到USB Headset輸出/輸入。 上網搜了有關USB Audio Hotplug的東西,比較適用的資源如下:1、Hotplugging USB audio devices (Howto) 題目看起來很吻合我們的問題,事實上並沒有多少參考價值。其中腳本 /etc/hotplug/usb/extigy或許可以捕捉到USB Audio設備的熱插拔事件,應該可以進一步驗證和利用,留意這點。 2、Example to map USB Ports to ALSA card numbers and add each sound card to a combined, single interface device 這是利用udev來獲取USB熱插拔事件,雖然Android沒有udev,但例子程序對熱插拔事件字元串的處理值得參考。 3、USB mic on Linux 其實我們工作的第一步:驗證USB Headset是否可以回放錄音。 3.1、插上USB Headset,可以看到alsa的確載入了USB Audio,如下: ~ # cat /proc/asound/cards 0 [WMTSOC ]: HWDAC - WMT_SOC WMT_SOC (HWDAC) 1 [default ]: USB-Audio - C-Media USB Headphone Set 3.2、參考了這個鏈接,寫了如下的配置文件/etc/asond.conf: pcm.!default {type asymplayback.pcm {type plugslave.pcm hw:1,0}capture.pcm {type plugslave.pcm hw:1,0}} 重啟後,聲音就從Headset出來了。 hw:1,0對應card1即USB-Audio - C-Media USB Headphone Set4、Linux下USB設備熱插拔 到此,需要考慮在Android平台切換USB Audio的實現問題了。有幾個途徑:1/ hotplug/usb;2/ udev;3/ netlink。這里就是netlink的實現方式,鏈接里有個證實可用的例子程序,目前可能需要做熱插拔事件字元串的處理。 難點:Android音頻設備的切換底層入口是alsa_default.cpp,目前看來需要在asound.conf定義好local CODEC和USB Audio的plug;還需要修改 alsa_default.cpp,最主要Android要知道USBAudio插上時打開 USB Audio的plug, USB Audio拔下時打開local CODEC的plug。這樣一想,修改的幅度還是蠻大的。而且未能確定如果在播放的過程中,切換音頻設備是否有影響?如果alsa允許只是配置好asound.conf達到同樣的目的,那就好辦了,可惜目前找不到這方面的資料,應該沒有這個便利了。 進展:2011/9/19:按照以上難點分析,大致完成了整個Android框架層的代碼和ALSA配置文件,基本實現了USB Audio熱插拔時的音頻設備切換。但有個很大的問題:在播放時切換音頻設備會導致AudioFlinger服務crash(之前做2G通話時也遇到這個問題,用其他辦法規避了)。看來在切換音頻設備時,應該停止播放;等切換完成後,再恢復播放。
㈨ audioserver是手機自帶的嗎
audioserver是手機自帶的,通過本文可了解Android系統的音頻架構,基本組件及功能,大概了解常用的播放模式,音頻流傳輸路徑,低延遲音頻的一些能力。
㈩ Android 中怎麼實現微信內置瀏覽器audio的自動播放
參考下面方法
加入stalled事件處理,發生stalled則重新audio.load() ; audio.play(); 或者保證audio.load()後,在canplaythrogh事件(或者readyState大於2後)進行audio.play()