马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?快速注册
x
. Q k: s4 F* _& x9 S
Android深入浅出之Audio5 {7 h$ }( h M" H
第一部分 AudioTrack分析: G7 \& l) O0 {8 F
一、目的2 w8 l# ]5 g9 c2 [
本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等。1 Y' J3 d0 h/ b! b7 K
分析的流程是:5 n* t6 |" `0 i% o7 w \! y: S
4 d- |4 i6 k! m% Q- U先从API层对应的某个类开始,用户层先要有一个简单的使用流程。/ G# y9 I* R4 b7 ~& P; T
根据这个流程,一步步进入到JNI,服务层。在此过程中,碰到不熟悉或者第一次见到的类或者方法,都会解释。也就是深度优先的方法。
2 Z1 s5 g' {+ l: J% N$ ], o0 b6 U3 Z" d: ]
1.1 分析工具
* o; h2 v( |% t% f% p: w) F7 ]" C分析工具很简单,就是sourceinsight和android的API doc文档。当然还得有android的源代码。我这里是基于froyo的源码。0 h' @$ F4 P5 |. w G: s, w4 v# u
注意,froyo源码太多了,不要一股脑的加入到sourceinsight中,只要把framwork目录下的源码加进去就可以了,后续如要用的话,再加别的目录。 e$ E2 F8 l: }- |0 C. B5 i* s- M
二、Audio系统
/ T3 r% s, K5 D先看看Audio里边有哪些东西?通过Android的SDK文档,发现主要有三个:! _/ F0 [) J+ @2 \3 ?
% Y1 T& m/ K0 E( {$ d+ vAudioManager:这个主要是用来管理Audio系统的
! l5 r% N7 Q2 g! w2 o% m* ZAudioTrack:这个主要是用来播放声音的
0 N! U6 t; }* ^+ R5 O JAudioRecord:这个主要是用来录音的
4 h% w) j: N, ^; r6 O( ~, T& b其中AudioManager的理解需要考虑整个系统上声音的策略问题,例如来电话铃声,短信铃声等,主要是策略上的问题。一般看来,最简单的就是播放声音了。所以我们打算从AudioTrack开始分析。
8 x6 t1 w# s! K" i3 o
, {( Z2 O( P$ ~$ i( q7 F三、AudioTrack(JAVA层)
3 s( Z% f6 E5 F# B# v: @JAVA的AudioTrack类的代码在:
) V: B2 s& q- s2 H9 nframework/base/media/java/android/media/AudioTrack.java中。
, `/ q8 }" g" Z) ~5 r$ D9 d3.1 AudioTrack API的使用例子
5 d }/ z. K: \. R! Z" Q先看看使用例子,然后跟进去分析。至于AudioTrack的其他使用方法和说明,需要大家自己去看API文档了。
6 c$ x, Y9 V6 b# |//根据采样率,采样精度,单双声道来得到frame的大小。
: v$ v0 b* w) \: U5 O8 a+ u1 Mint bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点) A, p, a7 ?! G5 ]
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
: x, k! X) l3 }" Y, rAudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节
* Q- k( l9 Y" O//注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。6 Z9 [+ J: }" a) }0 z+ j, t# U
//创建AudioTrack6 S& A( L8 I' g! v: G
AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,# T4 ~, J9 ?. d% B
AudioFormat.CHANNEL_CONFIGURATION_ STEREO, }6 G& q# W( {- l0 v+ t
AudioFormat.ENCODING_PCM_16BIT,
8 c& @9 \0 @' M3 X3 l/ Q0 T& n bufsize,7 Q1 ~2 i- M& V
AudioTrack.MODE_STREAM);//& e6 e) {# ]( F( r8 s$ z5 D$ ~' W
trackplayer.play() ;//开始" C2 f* H" |* H7 _2 q# O \
trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据
# z# w- E- q8 q0 e* m….
) t; W6 O/ E; Y- B. |trackplayer.stop();//停止播放
' R* S' j% k1 G% ztrackplayer.release();//释放底层资源。
/ ^2 j0 A* {9 q6 O& E4 @5 k! G! p/ U
这里需要解释下两个东西:, c# m, K* m- z6 W5 M
3 h9 @' T% K+ c4 \AudioTrack.MODE_STREAM的意思:
" v! O# t" _' s1 vAudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。9 M8 J/ m* T3 j) C3 i. v
这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。2 J% ?0 Y+ f) } K
而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。, E: I1 W* ^$ E2 e, E% H1 _
这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
" ^: V# a" R, qStreamType
/ y4 G: ]. N. Q这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。8 ^" O W& t! }
Android将系统的声音分为以下几类常见的(未写全):& Z+ C& p) \$ O
' k" s; O0 C8 F: k) [1 }" g
& o) z- L& k) V( xSTREAM_ALARM:警告声
! V0 D; t# F t6 s( QSTREAM_MUSCI:音乐声,例如music等
1 B7 h6 b. H6 r( ?4 l, [STREAM_RING:铃声3 y1 L$ k! E2 B. w
STREAM_SYSTEM:系统声音
/ b" s5 ^+ ^, m4 n/ a ^; I8 h% bSTREAM_VOCIE_CALL:电话声音
& w# j# S. s, {& e* v
2 m# L$ T1 n- d$ j5 c为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。# c0 R D) N: @% j+ x
其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。
5 P4 `5 T" d" n* ]1 Z3.2 分析之getMinBufferSize, H: p7 i' J& _! i& \- X
AudioTrack的例子就几个函数。先看看第一个函数:- |& M6 e% w$ g" G% k" Y/ X' S; \
AudioTrack.getMinBufferSize(8000,//每秒8K个点& w9 k/ u3 Q- u8 W
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道1 O2 \8 ^! ^7 w% ]
AudioFormat.ENCODING_PCM_16BIT);
: V8 W' G' n9 B$ z) r) }7 ?$ }----->AudioTrack.JAVA+ D3 {; D% @5 X
//注意,这是个static函数
8 R: ]1 @2 A6 T. G/ fstatic public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {% ]# J$ ^4 L: ^6 u
int channelCount = 0;
/ T8 l8 T( y# V7 _4 Q. f! v switch(channelConfig) {
* V& @: p/ q9 Z6 g f- _ case AudioFormat.CHANNEL_OUT_MONO:8 j8 @% s: m! p& |& E0 _
case AudioFormat.CHANNEL_CONFIGURATION_MONO:- k( l$ B* D5 b7 F
channelCount = 1;
. ?. ?: q) Q# o# J! z( S1 A, S; G% @ break;
9 r2 }3 I6 f! c. l8 r7 n case AudioFormat.CHANNEL_OUT_STEREO:% D5 `0 x) T, M
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:; c4 M9 Y8 P0 C7 ?
channelCount = 2;--->看到了吧,外面名字搞得这么酷,其实就是指声道数
) o: X. B) s' h+ [. n" P break;# |& r. E3 h5 U1 g$ D
default:7 l% ?$ t. j& ]
loge("getMinBufferSize(): Invalid channel configuration.");( v- w8 h3 Q8 e! \: ~9 l% K
return AudioTrack.ERROR_BAD_VALUE;
! p9 p! T" [+ w \- I5 z: h }
9 e2 s; e8 x/ g6 q //目前只支持PCM8和PCM16精度的音频
/ I- W0 K7 t; I8 r if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) K4 n/ [( ]0 _
&& (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { B3 a$ F' h8 P3 Y- o; i# P
loge("getMinBufferSize(): Invalid audio format.");* b. ~- x& B: |, ]7 J, v
return AudioTrack.ERROR_BAD_VALUE;
C" p$ R9 q Z, ?+ ^& G- @ }8 n; i0 }2 ~* k0 F6 V1 K1 `! [
//ft,对采样频率也有要求,太低或太高都不行,人耳分辨率在20HZ到40KHZ之间
1 X; H) R, I6 e5 a) s' X/ P: j if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) {
6 q! }2 d7 s* n2 I. v loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate.");
3 q6 {4 K" h. E return AudioTrack.ERROR_BAD_VALUE;6 X2 {% L, m* k2 |4 h, R2 }
}
X- R3 r# G+ J& P5 X4 ]! D6 E //调用native函数,够烦的,什么事情都搞到JNI层去。% r- P, D5 j' W+ o- R7 ]0 Z
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); I4 t3 }7 w1 }
if ((size == -1) || (size == 0)) {
. N+ L( O G2 y$ S7 s% A+ G3 J5 T loge("getMinBufferSize(): error querying hardware");
9 f! E" x" W* T3 y/ f% x! w) _. R7 w return AudioTrack.ERROR;
) r$ }9 \% i7 y7 e }0 m: c! {9 w6 d% ?- S# `
else {
* k3 R8 _! \& T9 X! w) Z' u return size;, E7 d* f/ U% D4 q
}- F- m% t9 v h5 Q9 O& J
native_get_min_buff_size--->在framework/base/core/jni/android_media_track.cpp中实现。(不了解JNI的一定要学习下,否则只能在JAVA层搞,太狭隘了。)最终对应到函数* O8 h: b' T: l" m9 l! K. S4 j' V- M
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,# z2 t( Z9 z% Z& D% t( G
jint sampleRateInHertz, jint nbChannels, jint audioFormat)
+ \6 V! }- E& h. p" ]{//注意我们传入的参数是:
/ F7 A' Y7 ~. T3 ?% x7 O3 g* |//sampleRateInHertz = 8000
/ J' B6 X8 Z% v6 B1 s//nbChannels = 2;/ D& o/ _- _- t; V* U
//audioFormat = AudioFormat.ENCODING_PCM_16BIT
& T! E& o n/ N6 c int afSamplingRate;
# k9 s z0 a+ U. W* V- k# n int afFrameCount;& C/ g8 h( ?( M- m
uint32_t afLatency;
* N4 l' L0 G" T2 P& C* v" ]- X//下面涉及到AudioSystem,这里先不解释了,- U; O6 ?* j$ p2 ?; t! \
//反正知道从AudioSystem那查询了一些信息8 A: g) Q ?7 T
if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
, k$ O/ y: e& K return -1;
; Q* l5 ^. k: X }* V0 x7 D7 c+ b; ?2 K
if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
[% N9 j, l) _4 m/ |. }# N return -1;. V' W' O4 R8 q. v0 f$ L% O" D+ G
}' W+ `# i4 \; n$ N0 L' d9 P
8 \! j) V* G/ p2 N if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {% U ?# l" ?% ^; r+ V t
return -1;
7 n& G1 [) w' B4 J }
8 O& K0 Y/ Q* `' g' V//音频中最常见的是frame这个单位,什么意思?经过多方查找,最后还是在ALSA的wiki中9 A) n( ]. F" E7 j! c
//找到解释了。一个frame就是1个采样点的字节数*声道。为啥搞个frame出来?因为对于多//声道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播出来//才行。所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表示全了。
0 z; U$ a8 N" f+ ~1 f) T6 Q // Ensure that buffer depth covers at least audio hardware latency
Y2 ?2 e/ L7 r; a: g8 o uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);
: ~* [+ h/ M; Z, w. e% ?: _5 X if (minBufCount < 2) minBufCount = 2;
3 P& \& T' w, E; \4 t- yuint32_t minFrameCount =
3 t4 B6 D( V, U* q! a @ (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;( z: O/ ?9 w, v! l6 g% j
//下面根据最小的framecount计算最小的buffersize 7 L7 i/ _; W1 c$ D6 `
int minBuffSize = minFrameCount; t, s# S+ M- J' U: d
* (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)) G3 V. l2 ~0 s( D5 ^9 ]' u+ E
* nbChannels; V3 G' k" V1 k- U% a8 Z
return minBuffSize;! @0 b9 R7 m: T
}
* d- e3 I( l- u5 f# V( d: Y9 |* O
getMinBufSize函数完了后,我们得到一个满足最小要求的缓冲区大小。这样用户分配缓冲区就有了依据。下面就需要创建AudioTrack对象了7 V3 |- v4 L$ |+ U
3.3 分析之new AudioTrack }$ D4 v4 y! i* `
先看看调用函数:5 G' r$ S/ q/ F; s3 { F
AudioTrack trackplayer = new AudioTrack(
4 J2 c" e3 B) |2 zAudioManager.STREAM_MUSIC,
: A; t$ c5 y% s. M8000,
6 N- J4 `1 E. q9 ~1 g9 f B! K AudioFormat.CHANNEL_CONFIGURATION_ STEREO,8 `+ t3 B. R4 e
AudioFormat.ENCODING_PCM_16BIT,
' q, F. O" j; X, ~/ ^7 O, k bufsize,
/ d O5 ~5 [. y, O% CAudioTrack.MODE_STREAM);//
+ V, e4 ~) d3 A( f! u* L! k1 T其实现代码在AudioTrack.java中。; b/ d! {) O5 w$ f
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
: n9 n4 ^; |8 ]2 p0 V% }& G int bufferSizeInBytes, int mode)2 G% x) c1 e, [, e6 a0 ] A8 Z5 C: W5 j
throws IllegalArgumentException {- o7 J3 f: ~$ f/ H% \# Z6 G
mState = STATE_UNINITIALIZED;, [% B4 j* G' R/ Y
' M( l) E7 g3 G0 S
// 获得主线程的Looper,这个在MediaScanner分析中已经讲过了2 ]/ f2 c: Z6 g& i. e
if ((mInitializationLooper = Looper.myLooper()) == null) {* v# q3 R# e) E0 [
mInitializationLooper = Looper.getMainLooper();1 j; d5 S; P' t
}
& X0 X# j- i, D" v" s# x //检查参数是否合法之类的,可以不管它
0 @2 }! \8 s% N) `) m audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);! ?5 f+ M7 `% o& p: T
//我是用getMinBufsize得到的大小,总不会出错吧?( E v5 \" W2 u) j, L0 Z0 D
audioBuffSizeCheck(bufferSizeInBytes);
, y% r/ _- ^4 P$ Q% W" Q+ f
8 d! ^5 ]' G4 \: `( C1 V' e1 Q* w // 调用native层的native_setup,把自己的WeakReference传进去了1 C' I- H( @& l' j
//不了解JAVA WeakReference的可以上网自己查一下,很简单的& Q7 {$ }, P6 i- c
int initResult = native_setup(new WeakReference<AudioTrack>(this),2 h4 a+ z' m/ M; n- f
mStreamType, 这个值是AudioManager.STREAM_MUSIC
: m' z3 F- M6 D( M6 Q4 s$ o% ] mSampleRate, 这个值是8000
. W0 z! D1 r2 A0 h4 O4 J6 W$ k/ f: ?mChannels, 这个值是2: [2 F# f3 S1 X( g0 y' {8 E3 ]& Z2 e
mAudioFormat,这个值是AudioFormat.ENCODING_PCM_16BIT
4 Y- D0 [" D4 U5 _/ ? mNativeBufferSizeInBytes, //这个是刚才getMinBufSize得到的: S% N+ ^+ P: ~4 E2 D; H3 H1 [2 [
mDataLoadMode);DataLoadMode是MODE_STREAM
5 N* Z& p( Y: ^4 [- }6 }# \ ....
- x! N# W* ]% n6 n5 g; d3 I}6 h5 ?$ J R$ Y5 w: m- n) I3 P
" {# p" O+ o6 P/ S5 X0 b) x
上面函数调用最终进入了JNI层android_media_AudioTrack.cpp下面的函数& Y' {& ^$ K- \
static int. ^* v: b, x3 y9 u* a1 ~1 b
android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
% Y6 j7 @+ p% u/ k- v* x jint streamType, jint sampleRateInHertz, jint channels,8 }2 d: {; ?6 i$ U. }
jint audioFormat, jint buffSizeInBytes, jint memoryMode)9 p e8 S) I2 `- e: r7 u
{9 c) }( X1 u9 q. x% U) [
int afSampleRate;
: K0 f% J I c7 ^4 V# ~7 n int afFrameCount;/ N7 O/ b) o8 K0 I
下面又要调用一堆东西,烦不烦呐?具体干什么用的,以后分析到AudioSystem再说。
: U% y" V# N. ^# @! Z AudioSystem::getOutputFrameCount(&afFrameCount, streamType); {8 K$ X# I m4 T- p8 c; I7 c
AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);3 b3 A" W0 u7 e0 D; c6 x
C. h6 i+ g) v0 R
AudioSystem::isOutputChannel(channels);
+ W; S/ v) g9 H/ u. e popCount是统计一个整数中有多少位为1的算法
& W8 ^# Q1 c- t* C+ J: Iint nbChannels = AudioSystem::popCount(channels);
% q2 c" K- Z \3 e+ `2 x% i
& x0 c$ F- h! Z, s2 {0 M' @ if (streamType == javaAudioTrackFields.STREAM_MUSIC) {8 G! b2 v) H9 t) w
atStreamType = AudioSystem::MUSIC;; b5 s. x c- x! E
}
$ P6 J D. u, ~+ Z, C* v int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
; ~6 f. i, p9 H+ G6 q, n int format = audioFormat == javaAudioTrackFields.PCM16 ?
$ m4 K w4 r u/ U4 w3 H AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;+ D/ ]8 R/ x" F
int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);: H( Y8 C9 E& Z) [
//上面是根据Buffer大小和一个Frame大小来计算帧数的。& _5 t7 }- C( ^+ @- }2 X
// AudioTrackJniStorage,就是一个保存一些数据的地方,这4 }# d @9 Y2 _+ o- R) c
//里边有一些有用的知识,下面再详细解释
" K4 R1 Z2 Z, E. ^& B6 Q- J AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();: S$ Y- b# W3 k7 F Z
8 o0 E+ Y: S2 Z* Q( C
jclass clazz = env->GetObjectClass(thiz);/ Z4 {$ f/ I/ ?( h: o) d
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);- F8 q( Y8 y+ b+ R- d, a! D
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
' I6 l/ m5 R, b# r lpJniStorage->mStreamType = atStreamType;( w9 C+ b0 v. c# S/ r$ T( x; P
: A" o* y G; X w//创建真正的AudioTrack对象
$ X% o3 j" k" c, X9 A6 `5 e0 i- M O AudioTrack* lpTrack = new AudioTrack();
1 W X/ s, K5 F5 _- N7 o6 Z e if (memoryMode == javaAudioTrackFields.MODE_STREAM) {
* g' p3 R# ?* u; W4 k: h+ Y //如果是STREAM流方式的话,把刚才那些参数设进去6 M' Q/ `7 P- `+ U; {1 O. H
lpTrack->set(
, m4 f V# ]$ O# L& R atStreamType,// stream type3 K1 s. t$ |8 U- [! z. w
sampleRateInHertz,
# s( s. {$ H( y# i% ?1 W7 r format,// word length, PCM3 [8 o3 _* C- a4 W' W* B! j
channels,3 c7 e% V- H2 Y: N, N: L& V0 p, C B
frameCount,
9 j; B& [7 p) x: D; | 0,// flags
: A0 y% A5 ^4 q' ?! w' i audioCallback,
3 T/ F8 N9 y1 ]+ b: i0 t9 ?&(lpJniStorage->mCallbackData),//callback, callback data (user)5 a( s8 D2 k& V/ |4 j: E) r) S1 b3 [
0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack, `6 N8 @- F/ }4 y- }1 b8 w+ v- g
0,// 共享内存,STREAM模式需要用户一次次写,所以就不用共享内存了& c( {! B7 J1 ?7 Y% g- X7 `; ~
true);// thread can call Java3 H- V7 [7 h! d, d* t3 h' F( O, ]
8 [. I7 l; q1 i } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {3 ]& | f7 \! k) j
//如果是static模式,需要用户一次性把数据写进去,然后
* @ ?5 {( `( u, d8 e. _ //再由audioTrack自己去把数据读出来,所以需要一个共享内存
1 J/ [ N# S0 J! z0 a6 F- _# w3 P//这里的共享内存是指C++AudioTrack和AudioFlinger之间共享的内容: u3 G6 d7 }0 u0 r3 t, a+ Q& v" a
//因为真正播放的工作是由AudioFlinger来完成的。
; | d) G# u7 ~9 U. }8 L! h" O lpJniStorage->allocSharedMem(buffSizeInBytes);
9 ^ j8 A, q) @& x lpTrack->set(. V' A0 P3 t1 A8 z, j5 s; M
atStreamType,// stream type' j: Q' ], K6 N" ]5 ^
sampleRateInHertz,$ p" L" F7 H$ a0 E7 b4 F# F
format,// word length, PCM
! c9 Z0 `# b" f channels," g2 r J0 _' ?* y
frameCount,
' b( b" n; |% ^ 0,// flags9 |: Q( B X9 W# z5 W& n4 v
audioCallback,
. Q8 k# V( N, t&(lpJniStorage->mCallbackData),//callback, callback data (user));
; p7 U8 S, C. F4 f1 { 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
) a$ Z( h3 B( } lpJniStorage->mMemBase,// shared mem" l, c+ z# S. X8 C$ Q5 S
true);// thread can call Java, ]# X( A1 M: f; z4 K% e- K
}
' J* G0 c+ X6 s/ j% I4 w
/ K1 q7 P* J! s: G if (lpTrack->initCheck() != NO_ERROR) {7 d. m; Z/ { l
LOGE("Error initializing AudioTrack");/ b+ g% G2 m: O! }0 c* T
goto native_init_failure;8 A1 [8 H- o6 T( q/ l6 f
}1 ~6 k# T3 B: }4 M3 V
//又来这一招,把C++AudioTrack对象指针保存到JAVA对象的一个变量中2 @+ [% I7 y$ @ l1 n
//这样,Native层的AudioTrack对象就和JAVA层的AudioTrack对象关联起来了。 o( s5 L6 l/ A& V k, {) A
env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
- Y4 M( D( i7 A8 Z4 @) i( k env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);
3 D: \* y4 F9 D% q8 W8 H" v* \ }# C n% i/ e' u* o& S n& `! W
- }. o9 q+ W! Z. h1、AudioTrackJniStorage详解* b1 r( B( H6 {6 U( \4 q
这个类其实就是一个辅助类,但是里边有一些知识很重要,尤其是Android封装的一套共享内存的机制。这里一并讲解,把这块搞清楚了,我们就能轻松得在两个进程间进行内存的拷贝。
; O+ ]2 m; u; w ]4 BAudioTrackJniStorage的代码很简单。6 x; D3 d, G3 t
struct audiotrack_callback_cookie {, V% W+ ]2 E1 O, P) l- R" B$ m3 j i
jclass audioTrack_class;
' W5 S t' [; q' x `9 M1 H jobject audioTrack_ref;
# E3 W; P5 C% F! t7 | }; cookie其实就是把JAVA中的一些东西保存了下,没什么特别的意义" e% _1 ~0 | C
class AudioTrackJniStorage {
5 O. z. j, b* {8 i6 g! A; V public:
8 i& [5 n0 k; e' ?0 w) Y# {$ J sp<MemoryHeapBase> mMemHeap;//这两个Memory很重要& [. y/ r% c) ?* s" }) D
sp<MemoryBase> mMemBase;
j8 h; e+ a$ E7 f; B- a/ o& l# s. d audiotrack_callback_cookie mCallbackData;
" B: ?. D( J9 p+ f i. O int mStreamType;
1 |5 L. k4 i2 b( U
+ q6 v8 {, X9 R1 P bool allocSharedMem(int sizeInBytes) {
! V3 m. G/ m2 p1 u mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");8 p6 M d8 {" V7 A( p0 v2 O& ~
mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
! { G) c3 L8 Q: k//注意用法,先弄一个HeapBase,再把HeapBase传入到MemoryBase中去。4 ~3 `2 n4 q& Z
return true;
2 S1 O* @9 }. C" \" X! H8 } }
X U; k2 @% L7 B: t};
8 J& q5 t$ q" P6 Q: @8 b2 u, P4 Q, e# V" {0 l2 s
2、MemoryHeapBase
3 `! F7 v5 P4 X3 T6 q# M* b7 \$ WMemroyHeapBase也是Android搞的一套基于Binder机制的对内存操作的类。既然是Binder机制,那么肯定有一个服务端(Bnxxx),一个代理端Bpxxx。看看MemoryHeapBase定义:6 I. o! _ M& s- n: [) b
class MemoryHeapBase : public virtual BnMemoryHeap; e- P8 @. E0 s4 U3 K5 E* }
{% f! j, c. H! x9 l* e/ S6 q
果然,从BnMemoryHeap派生,那就是Bn端。这样就和Binder挂上钩了* O$ ]) Z) ^) U, F
//Bp端调用的函数最终都会调到Bn这来
# [1 o5 r; F, }& C对Binder机制不了解的,可以参考:% Z$ H" B# s2 ]
http://blog.csdn.net/Innost/archive/2011/01/08/6124685.aspx
* C" W7 r, r: j! e* y; M" i 有好几个构造函数,我们看看我们使用的:, H4 s) b5 _1 _% g( L6 O9 a
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
v6 d* l; ]) X! N3 A' } : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),! _0 c# A; u5 U l. F8 e3 ]2 v
mDevice(0), mNeedUnmap(false)9 Z8 ?1 ^1 S8 o/ h) O4 q
{
- }$ p" B3 A) [2 j1 x/ X! n4 E9 C const size_t pagesize = getpagesize();' p9 D* U m7 s, x( H7 \
size = ((size + pagesize-1) & ~(pagesize-1));% W: b. U0 b7 G; B' X) |: }) |, [
//创建共享内存,ashmem_create_region这个是系统提供的,可以不管它/ [5 Q1 {7 O/ Z7 S, l& c9 v
//设备上打开的是/dev/ashmem设备,而Host上打开的是一个tmp文件# F0 y) M6 q: C" G; [
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);* T" p \" w1 Z: k4 T* {
mapfd(fd, size);//把刚才那个fd通过mmap方式得到一块内存
# {4 t4 g4 j+ Y/ |- ^: q* G+ p//不明白得去man mmap看看8 Y4 t* O/ d* L
mapfd完了后,mBase变量指向内存的起始位置, mSize是分配的内存大小,mFd是
1 X l5 F" y- Y2 C% r6 zashmem_create_region返回的文件描述符/ V6 o, t% h5 w u
/ x b% y3 t% `' e: W
}
- o6 c2 S6 Z% R @" lMemoryHeapBase提供了一下几个函数,可以获取共享内存的大小和位置。
3 z3 ~ N3 _- p( @" p0 ?# JgetBaseID()--->返回mFd,如果为负数,表明刚才创建共享内存失败了
( i+ l# T7 I& I) |* ]. o+ B5 FgetBase()->返回mBase,内存位置) f* N7 n; E9 {# Z
getSize()->返回mSize,内存大小
7 l1 H g. q, M* X6 g3 z% R5 S
. M; G) Z; L I) x: h3 L有了MemoryHeapBase,又搞了一个MemoryBase,这又是一个和Binder机制挂钩的类。- [/ Z7 q; V' \9 q3 `& ^9 `; B
唉,这个估计是一个在MemoryHeapBase上的方便类吧?因为我看见了offset# P* \# e+ p2 h* I% l& }
那么估计这个类就是一个能返回当前Buffer中写位置(就是offset)的方便类
, n2 [# v+ M* V4 |$ q4 F这样就不用用户到处去计算读写位置了。! H) E$ C6 [: J1 q+ M
class MemoryBase : public BnMemory2 p O) d) B, s, K
{3 L" I, j' |5 l+ w
public:
R$ T" h& c9 l( _' }# c MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);# u& a& X' W3 C4 K, n, {' _
virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;: L% N. M- I/ R2 C2 Q8 \; K
protected:0 Y" a. K- Q0 J8 @
size_t getSize() const { return mSize; }/ A# z7 I1 u, ~3 ]0 u! @5 l# d& |, J; [
ssize_t getOffset() const { return mOffset; }$ [: T/ x1 X: Y5 v
const sp<IMemoryHeap>& getHeap() const { return mHeap; }
0 x% n& K* ]% \* q2 n};7 P. ]8 ~7 d7 V b& |
6 R) z, Z7 P6 b& V1 d
好了,明白上面两个MemoryXXX,我们可以猜测下大概的使用方法了。! E, b+ b+ ?5 x( n
1 E7 ?; X0 i8 Y! b3 O4 s) Q' H% H
BnXXX端先分配BnMemoryHeapBase和BnMemoryBase,/ V+ q2 N" J- O5 m& Z% A
然后把BnMemoryBase传递到BpXXX8 Y _: h$ j' H9 {
BpXXX就可以使用BpMemoryBase得到BnXXX端分配的共享内存了。% g% N9 o* Z4 f
- E0 B8 o3 p: w/ w9 |7 }$ v; W. {$ _ X1 [9 k- d+ c
注意,既然是进程间共享内存,那么Bp端肯定使用memcpy之类的函数来操作内存,这些函数是没有同步保护的,而且Android也不可能在系统内部为这种共享内存去做增加同步保护。所以看来后续在操作这些共享内存的时候,肯定存在一个跨进程的同步保护机制。我们在后面讲实际播放的时候会碰到。
d5 ~6 a1 i8 o, ? V' @另外,这里的SharedBuffer最终会在Bp端也就是AudioFlinger那用到。* `$ u2 f9 J) a- U# I
2 G7 _! |4 w* j- {- c5 \& t* c3.4 分析之play和write+ C$ B3 h& n2 \. l0 m5 o
JAVA层到这一步后就是调用play和write了。JAVA层这两个函数没什么内容,都是直接转到native层干活了。! e$ M8 [( {3 p) M
先看看play函数对应的JNI函数
0 G' `3 X1 B9 r7 }static void
X; D! P- U5 O5 n+ yandroid_media_AudioTrack_start(JNIEnv *env, jobject thiz)+ e% E9 q" U+ z2 D H/ O% N
{
u- K$ K+ K7 k8 l9 t//看见没,从JAVA那个AudioTrack对象获取保存的C++层的AudioTrack对象指针4 N+ ]/ f: N, j
//从int类型直接转换成指针。要是以后ARM变成64位平台了,看google怎么改!( S* r( ?( N$ s0 O/ S [
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(, m3 _& w! o1 Y, u4 P( }0 G3 E
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
8 \0 O$ @+ E' U lpTrack->start(); //这个以后再说
; J: F% z3 n" ~. X3 l1 E! _. S}# h4 h+ T" o' [
$ h4 S1 \6 S$ `$ i% v$ t
下面是write。我们写的是short数组,5 A8 l. g" d! |2 R" U q, n) ?6 [' [
static jint
; p( }) m4 K. t( ~9 q2 j# C- kandroid_media_AudioTrack_native_write_short(JNIEnv *env, jobject thiz,+ b7 E# {# [& e" E! z
jshortArray javaAudioData,
, @4 r$ `* J( l jint offsetInShorts,
, o3 |$ x5 j# f! Z% E4 ?' Kjint sizeInShorts,
" E, A* _9 @" ?- b( @- X jint javaAudioFormat) {2 @% [& U$ f, B+ j. M
return (android_media_AudioTrack_native_write(env, thiz,
; [9 s: V) ]* W* T0 X) t4 A (jbyteArray) javaAudioData,! s8 J/ ~6 m" R1 Q3 v8 s& J
offsetInShorts*2, sizeInShorts*2,
% h! [. Q$ e3 a |# A; T) }$ N1 | javaAudioFormat)
7 O/ y& U9 m- X c* Q$ F / 2);
2 V ]" [. m+ E/ J+ U}) N9 r g" t @4 v
烦人,又根据Byte还是Short封装了下,最终会调到重要函数writeToTrack去7 o$ I. ~3 g+ f9 g% u6 V# W" `
jint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data,
& [; c! J& E7 d6 ` W" E0 q; S jint offsetInBytes, jint sizeInBytes) {5 g/ x$ N$ w+ w% W5 Q
ssize_t written = 0;
4 C( U$ F! K9 B" G // regular write() or copy the data to the AudioTrack's shared memory?$ O0 E0 t6 @% v* U
if (pTrack->sharedBuffer() == 0) {; P u7 r" f) ^7 Q9 t& o( ]
//创建的是流的方式,所以没有共享内存在track中; z# h' x% h9 \1 S3 D) `. x& z
//还记得我们在native_setup中调用的set吗?流模式下AudioTrackJniStorage可没创建
5 \! z' ]) j$ E0 X, ^8 T//共享内存% ?1 g& ]" l* _" L
written = pTrack->write(data + offsetInBytes, sizeInBytes);5 ]% H, i& h J) [, _8 g: f9 U
} else {
* l" i: _( I& |1 z/ J if (audioFormat == javaAudioTrackFields.PCM16) {* h h: G+ b9 |- }$ n4 Q7 o
// writing to shared memory, check for capacity
9 b6 j8 @- K9 k! q if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) {
; R+ d8 m. ?/ J2 S, g: n% e sizeInBytes = pTrack->sharedBuffer()->size(); b& g9 R, A+ S. l2 T' n+ p/ h, U# T Z
}
' v! A p6 P7 o' g8 x) k //看见没?STATIC模式的,就直接把数据拷贝到共享内存里 J( s2 S4 z3 q* T
//当然,这个共享内存是pTrack的,是我们在set时候把AudioTrackJniStorage的, o$ z9 H& D/ e& \* H
//共享设进去的
4 K/ q5 W! v! I$ P6 g8 N2 u/ z memcpy(pTrack->sharedBuffer()->pointer(),1 U% U8 @# W/ N! B2 ?6 \
data + offsetInBytes, sizeInBytes);/ k# [# p# B: ^9 K5 A
written = sizeInBytes;4 E5 G2 k# A Y/ |6 _ m
} else if (audioFormat == javaAudioTrackFields.PCM8) {# }: s) c0 M! f+ [5 b
PCM8格式的要先转换成PCM16
+ u1 O8 `0 @ H5 X' f$ F& z4 S' i / j2 D: R* R0 i# a( N U' R& ^
}, [! N$ v. D! w: s' p
return written;
+ v9 I1 o) ?/ A# ]5 d}
$ u5 ^9 j% I2 N2 Z! v. n
1 T; m. t3 s; p& h$ x) A9 Z0 E到这里,似乎很简单啊,JAVA层的AudioTrack,无非就是调用write函数,而实际由JNI层的C++ AudioTrack write数据。反正JNI这层是再看不出什么有意思的东西了。& @3 x2 K* ]/ ]/ f$ l4 F/ W
四 AudioTrack(C++层)& F/ }3 s+ m/ {( D( k7 m
接上面的内容,我们知道在JNI层,有以下几个步骤:2 g' S3 H! C% p; l' ~
' j2 p2 W1 J6 K; anew了一个AudioTrack
0 x& K7 A1 `% Z, i9 m# d' i调用set函数,把AudioTrackJniStorage等信息传进去
, e: `" x0 e. J v调用了AudioTrack的start函数$ n% @$ k2 U9 C { b
调用AudioTrack的write函数9 p2 Z0 L/ Z6 g2 I& r3 P; [
+ N+ l% X% k' L' b4 Q那么,我们就看看真正干活的的C++AudioTrack吧。1 G' M* x& |$ {2 f
AudioTrack.cpp位于framework/base/libmedia/AudioTrack.cpp
+ F: O9 J9 L6 }9 F4 q4.1 new AudioTrack()和set调用" n2 h* E9 J3 q) Y7 D' }6 k
JNI层调用的是最简单的构造函数:1 o5 j$ t3 m3 T
AudioTrack::AudioTrack()
0 c" u, R- Z& T( S7 u : mStatus(NO_INIT) //把状态初始化成NO_INIT。Android大量使用了设计模式中的state。
* X! X8 Y* ~; Y+ M{
8 D! u8 V( M% C# F}$ J- q- T" _9 \; {! Z5 Z
接下来调用set。我们看看JNI那set了什么3 F5 P+ h% X% ?* B
lpTrack->set(
+ ~# a) j8 U# f atStreamType, //应该是Music吧
2 V: R6 a5 t' O sampleRateInHertz,//8000
3 ?7 \8 _, a# l4 X- V format,// 应该是PCM_16吧/ K& B$ y9 e. n6 v: `
channels,//立体声=22 X/ a2 p: U: \3 ^8 I1 o
frameCount,//
0 g; {( r2 f, @# [ z3 U. Z 0,// flags
' k. y. o4 ]; B audioCallback, //JNI中的一个回调函数
$ D6 @, f) H1 E. j&(lpJniStorage->mCallbackData),//回调函数的参数
% E/ P. Z* x; a; f5 ~4 t& u 0,// 通知回调函数,表示AudioTrack需要数据,不过暂时没用上+ h( P( @9 r& Y3 | b
0,//共享buffer地址,stream模式没有
% X/ A% d& g7 M: `0 z" { e true);//回调线程可以调JAVA的东西* p# f+ N1 \ O
那我们看看set函数把。
`4 \1 O b3 A7 A$ o! z! q3 Ystatus_t AudioTrack::set(
l* R' }/ o0 b8 M& \ int streamType,5 M* n1 A+ g; ^) r! x+ d
uint32_t sampleRate,$ a% V. e' t- Z) R/ j- K
int format,
$ G- c @% Y8 G int channels,5 Z, I0 A! Q' b* y
int frameCount,
1 ]9 b% x7 ?- A1 e, V uint32_t flags,
; y' i8 W$ n7 S7 b! t callback_t cbf,
2 h$ t& F) v4 f$ A9 V. k void* user,
2 x" g4 r# [. |# Z" A X% z int notificationFrames,
0 N2 u1 Z9 r( a& \ const sp<IMemory>& sharedBuffer,( `: W d$ ?9 V4 e7 v% B2 K
bool threadCanCallJava)8 M5 i( C( ?! @5 c! O4 m4 H
{
5 x, n: [4 u- I5 k! l% T ...前面一堆的判断,等以后讲AudioSystem再说
+ {1 a) r, Q) F( D! haudio_io_handle_t output =
" [, s! S x+ H* t7 ^. J, R7 J' XAudioSystem::getOutput((AudioSystem::stream_type)streamType,+ ~9 l/ `( N! U, z% `
sampleRate, format, channels, (AudioSystem::output_flags)flags);6 w) K8 P6 r* v, u! E' L5 p
//createTrack?看来这是真正干活的
* y8 m( u( h1 t" m% x, U8 g. [ status_t status = createTrack(streamType, sampleRate, format, channelCount,/ A) J1 H) \/ L5 C+ ?8 `0 a# C
frameCount, flags, sharedBuffer, output);1 d5 \/ O3 K) s, k$ D6 O
//cbf是JNI传入的回调函数audioCallback
2 L/ X' ~/ |% m" y" l% A if (cbf != 0) { //看来,怎么着也要创建这个线程了!
- v1 h* ^. M" w' z1 f# t+ J% h2 V, ~ mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);, }1 B/ \% d& X, _* k4 ~7 w8 }4 W/ k
}
7 c6 y0 M2 }' H5 I6 t" J% N* Y return NO_ERROR;) a. }# ?! c3 \/ I: e) M3 D. i
}
9 c/ k' E* a6 w, {" |看看真正干活的createTrack( b( V6 O1 a' w" Q5 B% ^
status_t AudioTrack::createTrack(
/ G8 V* }" ~/ t int streamType,6 _( }! `5 L( x# |6 D, {: x5 B- c
uint32_t sampleRate,' a' C7 ~ i! Y, G& @
int format,
2 K& @0 Q0 G' T! x0 o6 | int channelCount,
% A/ C3 N/ F0 `( Q5 Y int frameCount,! \1 i; b( D. g) O" m+ V
uint32_t flags,
: Q1 \9 j! N! d% U9 D: x' P3 \3 J const sp<IMemory>& sharedBuffer,* R; L. F; @% o4 [$ N- t/ W' @0 b
audio_io_handle_t output)
" q2 C3 H+ U; C( T9 D0 |{: `1 K% ], \, Y
status_t status;& j; e+ \. q; l) Q2 _
//啊,看来和audioFlinger挂上关系了呀。# s5 x, a1 R. d
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();! c& k1 D B6 _6 l6 g {
# B4 e& A, z1 v# C
//下面这个调用最终会在AudioFlinger中出现。暂时不管它。
$ {: w) X9 e i0 m7 D P" ^ n sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),3 X4 @' S- x3 h- I3 F r5 Z& z
streamType,
0 d6 u2 f- M, A" V! _ T+ H) W sampleRate,
$ l3 w. Y5 o: {2 C- j& ?) k format,4 l) ?& {: p8 k1 ?) ~3 Y/ Z1 f
channelCount,) ^ n6 W! l4 { C! s- [, X
frameCount,& L. F& |: _: q+ g; K
((uint16_t)flags) << 16,$ O, o( | g6 j
sharedBuffer,
: q( v: A+ m5 {. v& l f output,6 u; {4 X4 m& d) e0 S
&status);
0 L; c# {& b+ ~4 J5 \. H. [
6 h( ^7 I4 m+ y. s //看见没,从track也就是AudioFlinger那边得到一个IMemory接口; N5 y' j2 K/ `/ o: N5 W1 S1 w- B3 w H
//这个看来就是最终write写入的地方; M( b0 P9 e9 Q5 b" U! P7 B
sp<IMemory> cblk = track->getCblk();1 i+ ^; |- d% s" f
mAudioTrack.clear();" G/ R( J, g' `
mAudioTrack = track;8 m7 ]' ?7 @2 l" \
mCblkMemory.clear();//sp<XXX>的clear,就看着做是delete XXX吧
+ K- |" w/ H& o mCblkMemory = cblk;7 N; [4 g7 Y* l" J+ X& v
mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());& V9 F+ x3 q! C( W+ H. M) i: g
mCblk->out = 1;
, I% D/ s$ ^. A0 `
( m) j' I- |0 H1 Y5 D$ L0 i( A mFrameCount = mCblk->frameCount;
& E/ q$ ^1 o, bif (sharedBuffer == 0) {
" }& [0 _! B6 {, ~/ d) s//终于看到buffer相关的了。注意我们这里的情况" G1 ?: T' O8 M, M
//STREAM模式没有传入共享buffer,但是数据确实又需要buffer承载。2 ]' M2 O H$ K5 t
//反正AudioTrack是没有创建buffer,那只能是刚才从AudioFlinger中得到0 j( r# w. ~+ g( L* K4 y
//的buffer了。/ f5 u* G3 e* H8 {$ h$ C5 ]3 F
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);& H- i; ~+ Y( H; c! c
}3 N" e4 K2 W& E }9 x5 ?: d& k
return NO_ERROR;4 r0 g2 r4 }3 @; b+ T; x
}
- P6 r6 x: f. r2 q
) B1 u& {( Y+ R# P: m( I6 p }+ o还记得我们说MemoryXXX没有同步机制,所以这里应该有一个东西能体现同步的,
3 O4 x [! r9 A1 {2 r& f那么我告诉大家,就在audio_track_cblk_t结构中。它的头文件在( c. |8 E" w1 ^( N0 R
framework/base/include/private/media/AudioTrackShared.h
# y" m) g; [4 Y实现文件就在AudioTrack.cpp中
) u3 A/ p' V8 m2 naudio_track_cblk_t::audio_track_cblk_t(), ?- |! ^6 R: D
//看见下面的SHARED没?都是表示跨进程共享的意思。这个我就不跟进去说了
: z+ s0 Z' |" J9 P$ i/ D" _4 u6 ^//等以后介绍同步方面的知识时,再细说
, M2 H" c ^6 L6 a, d! V; h; ^7 E : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),* U% W" A7 b& S; H% c( X
userBase(0), serverBase(0), buffers(0), frameCount(0),) ?, u3 Q1 G6 N" [. M
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),2 ^( @! F0 D6 K4 i [; ^
flowControlFlag(1), forceReady(0)
: t) f& s& W4 G2 `{
& | D9 x8 Z. w) Q) Z! e$ C}
. n+ O3 \. j$ `2 O e0 j, \* _# W; X# S5 H7 g( E
到这里,大家应该都有个大概的全景了。
' o+ Q9 B( G. S* c- b
! C. P- e/ \, a/ q7 {) ?6 {AudioTrack得到AudioFlinger中的一个IAudioTrack对象,这里边有一个很重要的数据结构audio_track_cblk_t,它包括一块缓冲区地址,包括一些进程间同步的内容,可能还有数据位置等内容
' L. n) i- i* SAudioTrack启动了一个线程,叫AudioTrackThread,这个线程干嘛的呢?还不知道5 H/ a. g8 \* u+ k/ y, r: _+ S
AudioTrack调用write函数,肯定是把数据写到那块共享缓冲了,然后IAudioTrack在另外一个进程AudioFlinger中(其实AudioFlinger是一个服务,在mediaservice中运行)接收数据,并最终写到音频设备中。
) N# A6 q. W* p4 @, @. q# n. u) X' g" P1 a& @/ K: S# a
那我们先看看AudioTrackThread干什么了。7 j. T$ ^" c3 d! o, y
调用的语句是:
3 ?9 w1 _: K' r2 T4 H( e a. OmAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
2 H- ^: E/ i# z8 }AudioTrackThread从Thread中派生,这个内容在深入浅出Binder机制讲过了。0 x2 K) _* p, j: V& |
反正最终会调用AudioTrackAThread的threadLoop函数。9 i4 ^4 e8 l/ m) E" x- R
先看看构造函数
7 n; a8 t6 l7 f( P3 F$ JAudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)3 h+ H; _* `0 x2 \; @; e. j+ K/ q
: Thread(bCanCallJava), mReceiver(receiver)0 S V$ a% x: s: P0 a
{ //mReceiver就是AudioTrack对象
& L) A8 B |, A; C9 G7 z // bCanCallJava为TRUE
' M3 @% D4 z" A( I. {}
$ X* C; V+ m' H4 ^6 [7 x6 {. b9 g" {" F4 {, [# I/ O$ H
这个线程的启动由AudioTrack的start函数触发。
9 z S) v6 z4 u( A4 B kvoid AudioTrack::start()1 w% e. C0 a( ]9 n( g+ G
{* E+ _: r8 C- a
//start函数调用AudioTrackThread函数触发产生一个新的线程,执行mAudioTrackThread的# O( Q9 s7 q A% A+ Y7 p i
threadLoop" o: `) T" Q- R# t# ?; X
sp<AudioTrackThread> t = mAudioTrackThread;
$ H' t* S. l4 o& R$ u2 L' U! rt->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);5 y7 f& x/ o, q, g) `
//让AudioFlinger中的track也start, [( M G! N& {
status_t status = mAudioTrack->start();
7 |2 n' i4 B3 \% C% n$ ?}5 f0 b6 b5 B; |# b8 l; w
bool AudioTrack::AudioTrackThread::threadLoop()
6 [4 x( r; q' d, d' ^- h- h5 O! M{
( q( s# m+ Q( L0 J7 U( s; A //太恶心了,又调用AudioTrack的processAudioBuffer函数0 ]9 |& F4 T! r9 p, E
return mReceiver.processAudioBuffer(this);& E! A* M/ {6 \) t
} X9 z' y* l2 V, K/ ^# h0 x9 f& M
bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)4 m; Y* }& @7 ~) e* e5 Z B
{$ {7 [/ _. |$ a8 H* F
Buffer audioBuffer;! u3 l' t j1 P0 f
uint32_t frames;4 P$ R+ n+ ]( a% q
size_t writtenSize;
: _: l: L0 p, ^2 h5 ?2 e ...回调1) c9 z' k9 }2 i$ T3 t& s5 I
mCbf(EVENT_UNDERRUN, mUserData, 0);* e/ y( o0 r- U; i! c
...回调2 都是传递一些信息到JNI里边
; {$ A# I% {: X mCbf(EVENT_BUFFER_END, mUserData, 0);6 p9 ~$ ^* e, p+ ^; [$ J( G/ {! v
// Manage loop end callback" Q) L7 R( C$ `* }& C, K
while (mLoopCount > mCblk->loopCount) {* I, S: K; s" k' h* y8 T
mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);
3 S6 L2 U% w3 x }
5 k. D4 H! {6 y R( `" P% k //下面好像有写数据的东西/ }1 S! ~; T8 ~8 e/ }, c2 w
do {
, ~# B1 ~" T# R9 X; K audioBuffer.frameCount = frames;) y& R" |( G8 d2 y4 h& [, U
//获得buffer,
" }% X9 {7 c2 n+ [: o' u status_t err = obtainBuffer(&audioBuffer, 1);
! a3 f$ s; D2 A# B+ T* i0 Q size_t reqSize = audioBuffer.size;9 I1 @" x v+ p* d6 k
//把buffer回调到JNI那去,这是单独一个线程,而我们还有上层用户在那不停
& N8 c9 G% o! o' B6 i5 W/ ]% @//地write呢,怎么会这样?, ~8 q+ L# e# ]5 B
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);9 G! I" w4 @9 _) J; M* Y. d
audioBuffer.size = writtenSize;: R( Y- m4 G: n3 I, p$ k
frames -= audioBuffer.frameCount;
6 C% P+ |1 {% Q' Z# Q releaseBuffer(&audioBuffer); //释放buffer,和obtain相对应,看来是LOCK和UNLOCK
2 W P; u1 L' N0 ?* K% |操作了
( R9 U4 W" M9 l- S6 e2 B }" Z3 b% g! d1 G% [: c
while (frames);
& z. E Y1 e6 ~ |7 h( \ return true;
1 m. o/ w' \- Y l}5 G, S( g) H/ b) N; y
1 L, {' e/ x- {
难道真的有两处在write数据?看来必须得到mCbf去看看了,传的是EVENT_MORE_DATA标志。9 V2 D' i" y" A8 a8 u0 L% K- z1 U
mCbf由set的时候传入C++的AudioTrack,实际函数是:7 X) W# D h8 [: m4 d# ~) J
static void audioCallback(int event, void* user, void *info) {8 a! V& w, ^. T; a' ^
if (event == AudioTrack::EVENT_MORE_DATA) {
4 |: U6 W p& j$ }" I //哈哈,太好了,这个函数没往里边写数据
5 m% ` e$ w8 Y' t AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info; `9 v$ \: g( T
pBuff->size = 0;
! L4 S9 a5 R6 j5 m8 e- i/ f& e }2 q8 i6 v3 C( t; I& \( `
. l, I9 _4 @, D9 K6 _' W从代码上看,本来google考虑是异步的回调方式来写数据,可惜发现这种方式会比较复杂,尤其是对用户开放的JAVA AudioTrack会很不好处理,所以嘛,偷偷摸摸得给绕过去了。/ l+ Y) n- ]$ z) v! h; h
太好了,看来就只有用户的write会真正的写数据了,这个AudioTrackThread除了通知一下,也没什么实际有意义的操作了。" S6 T2 U0 n+ U8 p% P
让我们看看write吧。
+ ]4 h6 S0 @! f9 a+ m4 d$ Z3 r$ |4.2 write
/ O5 n! G/ R; a+ g Ossize_t AudioTrack::write(const void* buffer, size_t userSize)4 B8 C; ~: j6 h9 k1 Y
{$ L; m% m7 U2 h/ u
够简单,就是obtainBuffer,memcpy数据,然后releasBuffer
1 N/ q& ^) s8 G* P8 r8 I) k眯着眼睛都能想到,obtainBuffer一定是Lock住内存了,releaseBuffer一定是unlock内存了% B0 g, @. D4 u I& T
do {9 o- L( Q' [/ Y7 d& y+ l6 A
audioBuffer.frameCount = userSize/frameSize();+ r- f$ ^; g! r7 L
status_t err = obtainBuffer(&audioBuffer, -1);, T9 [" ]2 Q: N, v
size_t toWrite;6 F$ G, `* {( \, [. V5 x" I
toWrite = audioBuffer.size;
5 K2 n( n9 d+ Q+ }. D" l memcpy(audioBuffer.i8, src, toWrite);
# g! k; @ p2 g0 V src += toWrite;8 R8 N1 M6 z2 t& D( L* ^$ U2 @
}& B1 {, ]7 M6 c. J: A" J
userSize -= toWrite;
) D4 z6 s+ _0 w9 I9 V: ?6 e# T written += toWrite;
6 ~0 V( C% J2 D* M( F releaseBuffer(&audioBuffer);+ }9 @6 J! V/ w6 S: s+ i& l
} while (userSize);
% [3 y N2 H, w; S/ F
9 b0 i, ~6 e- c" ~, P return written;! G% R; L$ }4 E6 `
}
. z& E+ C- _% y% e3 E3 JobtainBuffer太复杂了,不过大家知道其大概工作方式就可以了
4 M/ v2 j) @: b' U4 q4 n6 `" v3 f" p. Ostatus_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
5 a0 h _4 P0 q8 C{2 Q/ T( d" H4 X/ B& o! }
//恕我中间省略太多,大部分都是和当前数据位置相关,% a: C& z: y6 [# K5 x, s. s
uint32_t framesAvail = cblk->framesAvailable();
4 y+ o/ ^" J: Y# S% H/ o3 ]* [ cblk->lock.lock();//看见没,lock了
# P+ }# W' d7 P& C result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));9 {1 Z. F. ^" H- M
//我发现很多地方都要判断远端的AudioFlinger的状态,比如是否退出了之类的,难道
4 E# \2 P7 q S- V' u# U, K//没有一个好的方法来集中处理这种事情吗?
# l. W& y8 Q6 m# ~' e& i8 S( Z if (result == DEAD_OBJECT) {
0 i, e( u' H- c) |, p8 M result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount,
! c) Z6 l5 G1 n( f/ ~, [4 M mFrameCount, mFlags, mSharedBuffer,getOutput());
4 R- v( {6 J0 k' l( I* t }
1 s% G. J0 ^! ^* K) G//得到buffer* g3 m8 {. T3 S7 c
audioBuffer->raw = (int8_t *)cblk->buffer(u);. x& i2 j. Y: [
return active ? status_t(NO_ERROR) : status_t(STOPPED);4 @- x$ P$ |+ r+ g L
}
$ ^( b$ S% ]* \' R+ M在看看releaseBuffer& ~# A* n/ t; C9 ^$ j/ ~
void AudioTrack::releaseBuffer(Buffer* audioBuffer); D/ Y% G! x9 T5 i3 ]% _9 u
{
" |! a0 { O! H5 E9 ?& v/ G' I8 D audio_track_cblk_t* cblk = mCblk;
, U1 ` A: @5 \) Z. _+ Tcblk->stepUser(audioBuffer->frameCount);6 r2 f4 N5 g0 I
}, R8 C2 l2 U* q, c
uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount)* B0 a5 U$ Q. t2 [! y
{
3 H% l; y& T5 k; |2 G* d uint32_t u = this->user;
" ~( ] H0 ]% f, \% O8 [
' T2 s( J" {- m* q2 }7 c/ j u += frameCount;* J7 c9 v/ R. T- W
if (out) {' \, n: P# }0 \( ?4 v
if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) {
+ ?7 P6 J+ f7 q7 U" u bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;6 q6 R! C- y+ G) i8 T
}
! d& b( e0 I! R b5 T' J6 { } else if (u > this->server) {
# `; A* v6 j, c1 M u = this->server;
1 t* Y( b) O* { }% z* U$ a ?$ Y9 d
* _* R, Q+ F5 _. v* [& L! h' U5 b
if (u >= userBase + this->frameCount) {+ X$ m) h# G4 m' m# F: [
userBase += this->frameCount;
! y$ L; g8 Y) y$ _1 f9 L }
! P, Q" U$ b0 ^3 F' x5 b/ P this->user = u;8 ~& w! \1 S7 C
flowControlFlag = 0;
; p; V% U2 F F; G7 c return u;) J4 _) C+ x- v
}% o9 [0 u. U7 \4 o
奇怪了,releaseBuffer没有unlock操作啊?难道我失误了?8 s! O& f* P; V# R
再去看看obtainBuffer?为何写得这么晦涩难懂?
5 w% b: h# C# ?8 H- U Z原来在obtainBuffer中会某一次进去lock,再某一次进去可能就是unlock了。没看到obtainBuffer中到处有lock,unlock,wait等同步操作吗。一定是这个道理。难怪写这么复杂。还使用了少用的goto语句。, y4 H! {- W% Z6 b) d" Z9 p
唉,有必要这样吗!" S9 v. j' Z+ r% u
2 a: n1 v; d F) M1 e( X# o五 AudioTrack总结/ j% y) j+ I% L( z: G0 \; C
通过这一次的分析,我自己觉得有以下几个点:% m, H8 `+ O* E; k; h
c. S- }2 F! d( m8 S
AudioTrack的工作原理,尤其是数据的传递这一块,做了比较细致的分析,包括共享内存,跨进程的同步等,也能解释不少疑惑了。
) b7 V0 o( m* o0 ?0 f& q看起来,最重要的工作是在AudioFlinger中做的。通过AudioTrack的介绍,我们给后续深入分析AudioFlinger提供了一个切入点
" x- e2 ^' ^9 s& J" ~- J/ X, O3 f! S工作原理和流程嘛,再说一次好了,JAVA层就看最前面那个例子吧,实在没什么说的。- J; T, i1 y! y9 A6 o
AudioTrack被new出来,然后set了一堆信息,同时会通过Binder机制调用另外一端的AudioFlinger,得到IAudioTrack对象,通过它和AudioFlinger交互。/ A) k& S) o. ^# F
调用start函数后,会启动一个线程专门做回调处理,代码里边也会有那种数据拷贝的回调,但是JNI层的回调函数实际并没有往里边写数据,大家只要看write就可以了
/ Q/ t) }# C, E0 x; z3 M用户一次次得write,那AudioTrack无非就是把数据memcpy到共享buffer中咯( |+ a4 J J i# d
可想而知,AudioFlinger那一定有一个线程在memcpy数据到音频设备中去。我们拭目以待。; E+ O6 G# }* I& r' _3 x2 O1 r1 r) a
; o z1 V$ D1 X5 I" r& H0 L( o
|