Android深入浅出之Audio_安防、智能控制_音频应用论坛 - Powered by AUIOAPP

音频应用论坛

 找回密码
 快速注册

QQ登录

只需一步,快速开始

搜索
热搜: 音频应用

[其它相关] Android深入浅出之Audio

[复制链接]
xob800kuv5 发表于 2006-8-6 19:01:00

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?快速注册

x
* R! J& u$ k- ~6 z, G/ c- |
Android深入浅出之Audio$ L9 Q+ }% \' d1 F* |. q5 j" s
第一部分 AudioTrack分析
( U  v8 I- c% E/ P6 {2 a! P  g8 t一、目的
9 x5 m* a1 u. x$ C: A: g本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等。) Z' y, A2 R/ c/ G3 {
分析的流程是:1 t! E; x1 G/ m3 h( M
5 \. t: v4 T6 k: |8 I; j+ n+ f
先从API层对应的某个类开始,用户层先要有一个简单的使用流程。, K. u1 L. e# L, D! ~2 K! y
根据这个流程,一步步进入到JNI,服务层。在此过程中,碰到不熟悉或者第一次见到的类或者方法,都会解释。也就是深度优先的方法。! _, R; j+ V9 Y
3 K( n) G- R; n% R
1.1 分析工具) l( K2 n' |' Y! [& o6 k
分析工具很简单,就是sourceinsight和android的API doc文档。当然还得有android的源代码。我这里是基于froyo的源码。3 |3 V9 A" B9 u
注意,froyo源码太多了,不要一股脑的加入到sourceinsight中,只要把framwork目录下的源码加进去就可以了,后续如要用的话,再加别的目录。) @3 y/ P7 H% Z3 j) X
二、Audio系统
9 _& j; N7 U' K( ]& j先看看Audio里边有哪些东西?通过Android的SDK文档,发现主要有三个:; e5 P( s& O  V% v
2 P* G9 _! M) O1 Q! ^1 G5 h& y
AudioManager:这个主要是用来管理Audio系统的5 d6 @6 b# h# E
AudioTrack:这个主要是用来播放声音的5 a+ B: Y& g* Y3 A! T
AudioRecord:这个主要是用来录音的  E- w5 s, G4 P7 b0 q
其中AudioManager的理解需要考虑整个系统上声音的策略问题,例如来电话铃声,短信铃声等,主要是策略上的问题。一般看来,最简单的就是播放声音了。所以我们打算从AudioTrack开始分析。
. {$ q6 Q7 y$ t9 g4 T: C$ t1 N& Z( _+ @% F' W$ G- {3 d
三、AudioTrack(JAVA层)% G. I; x" [  v; K
JAVA的AudioTrack类的代码在:9 [( T0 Q  W* v5 _
framework/base/media/java/android/media/AudioTrack.java中。( A! _( m: |" t0 }3 I
3.1 AudioTrack API的使用例子/ \6 P7 z0 N, N$ y' X$ ]5 Z- M
先看看使用例子,然后跟进去分析。至于AudioTrack的其他使用方法和说明,需要大家自己去看API文档了。* A0 P' c# W2 d, ~# Y1 i
//根据采样率,采样精度,单双声道来得到frame的大小。1 _6 C0 T2 g, h3 Z( r3 g! ?
int bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点6 a" o/ z- p8 B8 l: M3 n4 Y
  AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道0 S5 k6 M' d! f' R
AudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节
  u7 e! y6 d  @7 Z/ \8 [//注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。, y8 G+ G1 D% ^9 G
//创建AudioTrack1 V; W/ z$ p/ @  P5 @+ v9 I
AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,$ v3 M0 f7 C/ K4 h
  AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
! `% M1 g7 w. z. g' H, j  AudioFormat.ENCODING_PCM_16BIT,$ `7 @6 {- _/ s7 t
  bufsize,9 u  T& }& {1 A0 M# _
AudioTrack.MODE_STREAM);//, L, o+ {1 l* E0 s* g. }& s
trackplayer.play() ;//开始3 ~$ A) o9 X' d/ V5 |* `' ?
trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据) I9 w: v: i& @9 d
….
: v% K& t2 F. r9 ]0 A5 v" q" Xtrackplayer.stop();//停止播放
4 ?) m9 s/ P/ P: b2 t7 ktrackplayer.release();//释放底层资源。
* ~, A6 f( M4 s, \5 @4 [# V9 |1 j+ g' z
这里需要解释下两个东西:
+ \+ N6 t1 l, B4 T8 S; W3 N
' p3 j5 h9 u4 ~' o9 O5 a4 X/ }AudioTrack.MODE_STREAM的意思:
, p  l+ @! t% PAudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
7 B4 w% |$ f3 U* C# a6 Z* V# f这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
1 H# Z( z3 v) N( B5 J! J而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。* |1 C0 o$ K3 V
这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
; p9 L, r* u1 P8 IStreamType
2 \  ~+ q- d1 K* Q这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。! f" s4 r6 F& B4 w) F% F
Android将系统的声音分为以下几类常见的(未写全):- `, T' b' j$ G8 c' t  G9 }8 @3 W/ L

- Q* e0 b4 d' G! j! Y, u4 X: {3 c
STREAM_ALARM:警告声0 }& w( l6 B/ I1 ]+ d0 n
STREAM_MUSCI:音乐声,例如music等
& L) m' d; h9 W: @5 W7 n( YSTREAM_RING:铃声
# ~; i& x  j9 NSTREAM_SYSTEM:系统声音( X$ x+ I' A9 a# }9 q
STREAM_VOCIE_CALL:电话声音
- o, r1 {( {1 f& f& O+ a
+ v6 ~; }8 z$ @5 d7 O' j$ V为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。
9 B' h, Z: O0 Y% v: k  H其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。
; ]' ~6 `4 Z3 s: Z. t! \- k3.2 分析之getMinBufferSize
+ n$ K. R$ i5 TAudioTrack的例子就几个函数。先看看第一个函数:9 H7 Q0 l1 B8 p2 e( r3 w+ `$ I
AudioTrack.getMinBufferSize(8000,//每秒8K个点, \- p! \; P7 w! q( O
  AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道( d9 k, F$ E- a# j1 I
AudioFormat.ENCODING_PCM_16BIT);5 W. |# E9 ^( }. \
----->AudioTrack.JAVA
: W. B2 k6 |7 L* H" k5 Q! \1 U/ P0 @//注意,这是个static函数
' f, N& N2 \' ~! Mstatic public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
) T3 u7 v: @8 ^        int channelCount = 0;/ n! ^' P- x( ]% r& y% J& m
        switch(channelConfig) {* j! i5 @: P9 W) U
        case AudioFormat.CHANNEL_OUT_MONO:
7 p: M! u! z) c0 P6 c1 {$ P. x        case AudioFormat.CHANNEL_CONFIGURATION_MONO:& h6 b# A, s+ r) @6 `$ t
            channelCount = 1;8 g; c, ^2 `$ a
            break;0 o1 k4 G0 ]0 Z: H  g, ^6 G0 S/ Q
        case AudioFormat.CHANNEL_OUT_STEREO:
& s8 E6 Q, D% \& F  m! }        case AudioFormat.CHANNEL_CONFIGURATION_STEREO:) Q  [/ ]2 V4 }) D
            channelCount = 2;--->看到了吧,外面名字搞得这么酷,其实就是指声道数1 P) H8 h) G- N2 u: E! A
            break;
% R  a1 F8 Q, ?" E/ r. E3 x        default:3 ^' H  W: U4 _0 ]
            loge("getMinBufferSize(): Invalid channel configuration.");
6 L- c% O' t: B9 ~% \            return AudioTrack.ERROR_BAD_VALUE;
5 h% r' u, \( V2 V% m        }3 o7 M( K. l% c6 C' n( u
    //目前只支持PCM8和PCM16精度的音频   
, O5 W% _% g) a& w' r        if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT). Y) ^" T* B$ N
            && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
% |6 Q% w* X7 K1 ?            loge("getMinBufferSize(): Invalid audio format.");
* |' A* b) [, ]: ]1 c7 x            return AudioTrack.ERROR_BAD_VALUE;" d: a7 c% c- e5 K) F3 _9 ~
        }
% b/ G/ V" ~. `) |2 i      //ft,对采样频率也有要求,太低或太高都不行,人耳分辨率在20HZ到40KHZ之间
0 s$ `4 c, P6 u9 g        if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) {
( w: p. d* Q3 O3 G2 k5 ]            loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate.");
# t( O& y( P4 c7 [) B7 c: z( V            return AudioTrack.ERROR_BAD_VALUE;
; R2 J/ n: ]9 g( _: U        }/ P- b+ B; {2 i/ M; q/ ]
       //调用native函数,够烦的,什么事情都搞到JNI层去。% Q  [+ Y5 k6 R( Z! {- N
        int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
1 I/ a$ `8 q2 W0 I4 K8 z        if ((size == -1) || (size == 0)) {. s) w+ G% q: {
            loge("getMinBufferSize(): error querying hardware");
/ S, j0 r( B$ ?            return AudioTrack.ERROR;5 J! c  k/ _& G' n; r! H; ?" ]
        }
$ e* q! f3 t. R/ y) ~1 G        else {6 P( {. w9 }. U4 i  a# C  ]
            return size;; j; x! }  u$ I
        }. A0 A2 l& M% g; E1 m3 \* H
native_get_min_buff_size--->在framework/base/core/jni/android_media_track.cpp中实现。(不了解JNI的一定要学习下,否则只能在JAVA层搞,太狭隘了。)最终对应到函数4 W0 y; k  R, Q
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
; b8 L7 }9 h) I3 Z' v- Fjint sampleRateInHertz, jint nbChannels, jint audioFormat)4 W; S0 o: r/ ~9 {
{//注意我们传入的参数是:0 R1 ^0 W! `( i3 s4 G
//sampleRateInHertz = 8000
- V# T3 p! F8 q, S3 u1 f//nbChannels = 2;
% Q  h4 z% U+ Z0 V% ?" [3 `//audioFormat = AudioFormat.ENCODING_PCM_16BIT
4 P* |8 d- S5 x/ f' ]    int afSamplingRate;8 t4 r% [1 V/ {3 G6 |+ n
    int afFrameCount;
0 \  b7 a# X2 ~    uint32_t afLatency;
; P2 N7 I3 K) @- e8 j//下面涉及到AudioSystem,这里先不解释了,
0 Y! \. a2 R7 G//反正知道从AudioSystem那查询了一些信息
' o7 H! o& U4 K5 R    if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {3 T- @* f9 k0 S8 `/ s& _1 D
        return -1;2 {6 ~8 T) t0 l0 @( T$ g+ o. s
    }
; T6 ]9 u+ J% L. A1 o5 l' m9 m4 @    if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
. H7 E2 f1 w0 ~9 E        return -1;
9 W( `4 X3 T7 f$ g5 g  A    }
7 E7 c4 ]' P% p2 p$ Z/ Y   
' g- K" i; H9 I9 a. g4 E8 f( O    if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {" c6 d" M8 A/ ?( O- _
        return -1;
+ p1 ~0 W) X7 k8 J- P    }
7 Q3 S4 N% u) r# ^  d+ s% i1 v//音频中最常见的是frame这个单位,什么意思?经过多方查找,最后还是在ALSA的wiki中
& R- V$ k; n( E% u" [//找到解释了。一个frame就是1个采样点的字节数*声道。为啥搞个frame出来?因为对于多//声道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播出来//才行。所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表示全了。
( ]/ F" F& n6 {! s, i0 K, Y    // Ensure that buffer depth covers at least audio hardware latency
5 w8 \/ g3 ]7 w7 j    uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);
8 ~' j! a& K- R5 p3 K    if (minBufCount < 2) minBufCount = 2;
+ j8 f- N* B1 G5 n1 l, i! wuint32_t minFrameCount =
  C& w! Q9 X" p+ N) D2 t! T  Z (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;. l" l0 [, G: d
//下面根据最小的framecount计算最小的buffersize   + C  k: x& v9 C& K: B; ^
int minBuffSize = minFrameCount( b$ u6 A/ c) Y' J. H2 P: S
            * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
$ I7 Y* k  Q+ }3 A2 q            * nbChannels;% G, Q) b6 B9 X$ `' m
    return minBuffSize;
$ t4 }3 @2 `( @- [. m}
0 i& L, z2 z& j- }6 C
1 j9 d, Y& a# O$ ~getMinBufSize函数完了后,我们得到一个满足最小要求的缓冲区大小。这样用户分配缓冲区就有了依据。下面就需要创建AudioTrack对象了  u. D+ n" z( Z
3.3 分析之new AudioTrack
* i. u! w* d, ^$ S先看看调用函数:; c2 B4 d5 g' m! K
AudioTrack trackplayer = new AudioTrack(& G3 @( X) I1 B0 ?1 B" `
AudioManager.STREAM_MUSIC,% t$ Q3 O+ y. z! P2 x& ], ~
8000,' Y8 L! |8 Z, q+ d0 Q& ^- x8 L
  AudioFormat.CHANNEL_CONFIGURATION_ STEREO,0 x) M& X1 r0 s
  AudioFormat.ENCODING_PCM_16BIT,
+ Z& F' ^' Z% M, z2 b  bufsize,0 j" W9 v# c/ v
AudioTrack.MODE_STREAM);//0 A! l7 V, @7 \  z  N
其实现代码在AudioTrack.java中。: G3 J. M+ w- N' @/ [( _; v% C
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
* [' y0 h& z# ]            int bufferSizeInBytes, int mode)* A0 s! H# ~" p
    throws IllegalArgumentException {
+ O& y5 J5 F# ]- L0 m1 r        mState = STATE_UNINITIALIZED;
$ ?. r. o- o0 A- z3 V' X- R       ! r" Q, G4 J3 B% O0 X; ?9 Y
        // 获得主线程的Looper,这个在MediaScanner分析中已经讲过了$ G  p/ a3 k' F: E- N$ Q  W6 X
        if ((mInitializationLooper = Looper.myLooper()) == null) {
# o9 x$ b) Z: j: _1 X            mInitializationLooper = Looper.getMainLooper();: B$ C8 v, _! T4 N
        }
+ Z8 [2 I& ~* i) @2 R/ W    //检查参数是否合法之类的,可以不管它* y  d+ V2 A; s  d; G
        audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);
5 E5 y9 u3 M3 R   //我是用getMinBufsize得到的大小,总不会出错吧?
& h7 n. K7 k' l+ p( F8 g4 [" N/ O        audioBuffSizeCheck(bufferSizeInBytes);
& ^" B+ W# \1 I
* G4 f4 l! t/ w8 ]        // 调用native层的native_setup,把自己的WeakReference传进去了
& ?/ F8 p# f: s( ~5 z9 m5 F) O     //不了解JAVA WeakReference的可以上网自己查一下,很简单的
2 y3 J4 y9 J( ^/ |* G1 ?$ k        int initResult = native_setup(new WeakReference<AudioTrack>(this),+ L# V3 f! _2 m
                mStreamType, 这个值是AudioManager.STREAM_MUSIC
' k5 P7 D  _/ x mSampleRate, 这个值是8000
9 P; G5 S  x6 [1 rmChannels, 这个值是2
) v1 p) W- y8 p2 Y" s4 K- amAudioFormat,这个值是AudioFormat.ENCODING_PCM_16BIT
1 B9 X  }9 U- ?- z1 j" i                mNativeBufferSizeInBytes, //这个是刚才getMinBufSize得到的
1 p4 I. {4 g+ [: n; z, ?- \! c! HmDataLoadMode);DataLoadMode是MODE_STREAM+ i! a# p- d8 `! a  }3 e
         ....1 \. V. I: J5 n! }0 @5 ]  Q
}
( h" b3 N2 U  u. _1 }. G3 V$ _4 z1 R+ `
上面函数调用最终进入了JNI层android_media_AudioTrack.cpp下面的函数* e4 C. W1 A' c' F" O9 w
static int! h: N7 D$ F1 L# r
android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,2 _3 G& O  U6 t. x& e
        jint streamType, jint sampleRateInHertz, jint channels,3 W: i3 T9 ~1 I# B" M, U4 q
        jint audioFormat, jint buffSizeInBytes, jint memoryMode)  L0 i9 H" F3 d) a* u
{
8 Z) ?8 V( O2 [3 }    int afSampleRate;8 U3 A- t, q) E2 S0 b& c( \6 l
    int afFrameCount;0 T: O" `& S/ A" K
   下面又要调用一堆东西,烦不烦呐?具体干什么用的,以后分析到AudioSystem再说。
5 K- q  Y# I  v/ D4 o2 q6 e    AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
) N2 o6 K0 k9 Y. E$ P1 Z   AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);" G9 B0 I; ?# h3 W$ v
8 p$ b# \3 m3 M' X
   AudioSystem::isOutputChannel(channels);  T; x7 W1 z+ g3 v
    popCount是统计一个整数中有多少位为1的算法7 z% X* Y2 w! g5 Y$ S* b; V0 \9 }  T
int nbChannels = AudioSystem::popCount(channels);. D- i$ u' ]" Z+ ~" F
   # i- E. |% p; T8 G
    if (streamType == javaAudioTrackFields.STREAM_MUSIC) {- Z6 d) K. W% O& \
        atStreamType = AudioSystem::MUSIC;: z, w1 H) [1 O
    }! B8 f/ w2 ]3 L/ u# B. ]+ w% k! J% W: s
   int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
% U8 v7 L# t) K7 ~1 K2 f/ T6 B3 w4 k    int format = audioFormat == javaAudioTrackFields.PCM16 ?
; \4 J) u, p7 V5 \3 Q; A            AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
, j+ \* {; R; s' ~+ K/ m    int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);0 W2 o" o7 r3 \9 g# k7 V
//上面是根据Buffer大小和一个Frame大小来计算帧数的。
8 j* C1 m) Y' L8 \  Y// AudioTrackJniStorage,就是一个保存一些数据的地方,这
6 M8 P/ M" t' E0 U//里边有一些有用的知识,下面再详细解释! W. j3 |6 I- C3 h: {' T3 Z# Z9 ~
    AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
, U/ Q4 }& b8 j( v3 x. D  i5 c   " L. T! s3 E- C% c  D" \! e
     jclass clazz = env->GetObjectClass(thiz);9 ?1 `. @; X' z$ O2 z/ i( Z
      lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);: a, |3 @" n$ [/ {5 L$ A* P* ]
     lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
; w( V9 x0 q% F9 @     lpJniStorage->mStreamType = atStreamType;& v1 q2 ?) |  a7 {( K
     
& C' c! h4 e. k* n7 ]//创建真正的AudioTrack对象. {3 ?: @% C# q  k, \
    AudioTrack* lpTrack = new AudioTrack();2 {: L  g5 \) ]
       if (memoryMode == javaAudioTrackFields.MODE_STREAM) {8 `1 t8 _9 I- ~  Q3 L+ }2 j9 Q
//如果是STREAM流方式的话,把刚才那些参数设进去: U# l6 k/ W' Q* F$ X* g. q8 L
       lpTrack->set(
8 i" ]5 G, t" i# Y: O8 N$ f            atStreamType,// stream type
( h3 o7 F: r8 I3 ?8 w  M            sampleRateInHertz,. g, o8 h/ }# V. l) @4 q
            format,// word length, PCM$ @1 L9 W/ H2 d) R: }
            channels,
! {8 n* R' k6 d# T) G5 n            frameCount,% d6 b5 a& }  W5 h) k
            0,// flags
% `! K' Y, _- N9 A            audioCallback,7 r3 D! _$ ~9 }& R
&(lpJniStorage->mCallbackData),//callback, callback data (user)  k; Y! b& U' o+ {( ^/ U
            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack1 k; x8 U. Y% V+ B; X5 }
            0,// 共享内存,STREAM模式需要用户一次次写,所以就不用共享内存了
  ]8 `" P9 U* h* L. Z9 m1 s            true);// thread can call Java# D8 E1 y3 b& _( A7 j4 S* |
           1 D. d3 D* X7 b" N! `
    } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {
6 t! P' j1 q% _# \         //如果是static模式,需要用户一次性把数据写进去,然后
" [1 f+ \& x3 `* q( `  I       //再由audioTrack自己去把数据读出来,所以需要一个共享内存% K. M: V9 A: a) o7 d
//这里的共享内存是指C++AudioTrack和AudioFlinger之间共享的内容
+ G" \$ ]* M( v1 t. Y //因为真正播放的工作是由AudioFlinger来完成的。
7 O! L+ s, `1 f: e, T          lpJniStorage->allocSharedMem(buffSizeInBytes);
( o; [( u* F/ G7 s+ l$ ]          lpTrack->set(
* Y% R  }# w) l6 v! v            atStreamType,// stream type
7 V) p+ S3 E$ t            sampleRateInHertz,
' ^& J9 B5 o4 e) l' v. n9 H            format,// word length, PCM* M" d: N3 V* Y/ F
            channels,
  G- @) |+ x. H- X5 k6 q            frameCount,  d% p3 d# A! N
            0,// flags
5 A! V) F% ^8 X! N' l4 |0 a            audioCallback,
! j" p) i5 u3 q; b9 J! D& n&(lpJniStorage->mCallbackData),//callback, callback data (user));
* {( |8 u4 Q- U. h2 L            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
- ?' r) K( @# S            lpJniStorage->mMemBase,// shared mem4 n: E7 J; n/ S& I$ L6 a, a1 F6 k& A
            true);// thread can call Java$ s- H, S* t! `. L
    }( ^+ r- G! C/ Q6 b! h
  ~( J# O  d( {( I3 M
    if (lpTrack->initCheck() != NO_ERROR) {
; t2 h, e* h  c7 D7 i$ H# `% t        LOGE("Error initializing AudioTrack");( \0 ~  e5 f# v/ j: r
        goto native_init_failure;1 N- O0 a: y6 P! t
    }
+ x3 ^# d( Y& |+ s  Y* M  V5 @6 H//又来这一招,把C++AudioTrack对象指针保存到JAVA对象的一个变量中( ?3 z6 r6 V$ b; s
//这样,Native层的AudioTrack对象就和JAVA层的AudioTrack对象关联起来了。3 x; w- r0 X. `( _- M  p
    env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
6 q8 @& D! ^) [% @) u6 I, O    env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);
2 m2 ~5 C& k  o6 Q }
5 ~8 u) f9 S7 m) l4 ?5 v/ o$ w0 k' E' x
1、AudioTrackJniStorage详解
/ H2 x0 x, u% Q* d这个类其实就是一个辅助类,但是里边有一些知识很重要,尤其是Android封装的一套共享内存的机制。这里一并讲解,把这块搞清楚了,我们就能轻松得在两个进程间进行内存的拷贝。
! ^& `% K3 o5 A# ?( d4 TAudioTrackJniStorage的代码很简单。
/ J* g6 ?' v$ `* V. `2 W  @0 s! a8 ?struct audiotrack_callback_cookie {
- R; C) I# p. D4 o5 A. g    jclass      audioTrack_class;
6 u3 }5 T! ^; }+ e$ i    jobject     audioTrack_ref;
, l; R) p( B/ b) K; V }; cookie其实就是把JAVA中的一些东西保存了下,没什么特别的意义
) X0 S. p3 G" V2 U( m+ b+ Q( gclass AudioTrackJniStorage {
+ L2 l2 V# Y1 `: L  ~2 X3 m4 O    public:) K4 O! w3 K9 O5 h1 W! u
        sp<MemoryHeapBase>         mMemHeap;//这两个Memory很重要$ ]$ y  r0 W! H
        sp<MemoryBase>             mMemBase;
0 n& \/ M" ^" j  x6 h6 E* r' l        audiotrack_callback_cookie mCallbackData;" T& `1 q$ G, ]8 [: j, H# _1 E1 E
        int                        mStreamType;
- ?* y5 ]# H4 B" K, \# W$ E4 i
- i7 ^3 U) k# Y1 p0 ^/ [      bool allocSharedMem(int sizeInBytes) {- M; D  x5 D. e' ?! X
        mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
7 W1 }7 ^$ }  a" _0 F4 s* @5 D        mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
# }( M: j5 U2 x9 ^) r( C- O( R//注意用法,先弄一个HeapBase,再把HeapBase传入到MemoryBase中去。2 e/ s% m% ^( f8 X' [- K, [
        return true;  ^" z  Z4 V% {  z2 C% M; p/ R
    }
* l. y/ y  Q' Z! `' I5 u) g};5 B  h0 Z( R- W5 }! m) o, g

% W% h. z* e# m  s# o' P% Y! E2、MemoryHeapBase$ e$ @9 U& S! c$ f5 W
MemroyHeapBase也是Android搞的一套基于Binder机制的对内存操作的类。既然是Binder机制,那么肯定有一个服务端(Bnxxx),一个代理端Bpxxx。看看MemoryHeapBase定义:1 c6 @7 d) }4 l# u% O! H% P3 Q
class MemoryHeapBase : public virtual BnMemoryHeap3 \6 o; p& a' ?1 p* t* G1 a4 U6 [
{: Q! r. H8 D! p+ t( j
果然,从BnMemoryHeap派生,那就是Bn端。这样就和Binder挂上钩了
8 ^2 B' C* L$ G, [) ]//Bp端调用的函数最终都会调到Bn这来* S4 M/ f3 i: h% U+ W5 N6 x
对Binder机制不了解的,可以参考:
( g+ U! E8 ~- x7 x9 ]http://blog.csdn.net/Innost/archive/2011/01/08/6124685.aspx
# _0 Z# K2 X9 @" r0 w: a 有好几个构造函数,我们看看我们使用的:; f1 o7 o5 g. e, q; H& E( U! H
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)% U2 [( Z- w: k) j$ f* Y5 s
    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
: ]* W2 V- r+ [- Z: m$ Y6 G+ X* s: l      mDevice(0), mNeedUnmap(false)5 q- c  i0 G5 q  I! `, ?+ c
{" k+ b2 U  L" L. @- b4 i& }7 y9 C
    const size_t pagesize = getpagesize();1 w( J0 Y) f% n
size = ((size + pagesize-1) & ~(pagesize-1));$ L" D  x5 W6 ]$ b9 ]% N7 C8 [
//创建共享内存,ashmem_create_region这个是系统提供的,可以不管它. p- F& o, g0 ~4 Z' Y8 _
//设备上打开的是/dev/ashmem设备,而Host上打开的是一个tmp文件$ X; Q8 ^6 ^& `( {
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
$ n6 Y: n4 b2 [mapfd(fd, size);//把刚才那个fd通过mmap方式得到一块内存
  E, n( Z3 A% Y! j* N* }//不明白得去man mmap看看
9 h2 ~+ K  Y) z4 |2 ~mapfd完了后,mBase变量指向内存的起始位置, mSize是分配的内存大小,mFd是: a5 E% Y% r8 d  K4 q; z) J
ashmem_create_region返回的文件描述符5 I/ V& k- ?4 p0 x
+ \  h9 z1 X# T4 G2 X$ T+ f
}- p; [  [* S3 b1 m) N
MemoryHeapBase提供了一下几个函数,可以获取共享内存的大小和位置。# X# a& R" d( D. ?0 {( W! ~
getBaseID()--->返回mFd,如果为负数,表明刚才创建共享内存失败了
) r3 r& C6 H% a! {! JgetBase()->返回mBase,内存位置. W0 ~1 Y, h4 g! Y( g
getSize()->返回mSize,内存大小
; Y% |+ g* `& h) Z8 s9 C3 X/ d8 x. G3 i0 L. t3 R
有了MemoryHeapBase,又搞了一个MemoryBase,这又是一个和Binder机制挂钩的类。" p" [% w  C9 B- g& a+ H2 w  {
唉,这个估计是一个在MemoryHeapBase上的方便类吧?因为我看见了offset
$ v) B  p# I. u7 l5 U% \2 H那么估计这个类就是一个能返回当前Buffer中写位置(就是offset)的方便类
# G) |* b0 S, `/ m3 G; b+ i/ y  d这样就不用用户到处去计算读写位置了。
) u9 f+ ^; [' X* U! ?$ P( cclass MemoryBase : public BnMemory) t( F6 F, S& h3 j' A9 G7 Q
{$ U, s9 u5 b( A2 H- Q
public:- y9 Q* C. ?. n( Q- o/ Q$ i- j  J
    MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
" v- [) u7 `% ~6 a/ M    virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;7 s) {% i" B  H" X! d8 w8 l( |. y5 c7 x
protected:
* `6 A3 i* ?6 T& ]3 z& Q& Z0 y    size_t getSize() const { return mSize; }" Y9 d2 _$ E4 g& r' P
    ssize_t getOffset() const { return mOffset; }
3 O5 G  K: \% K- C& H, V6 x    const sp<IMemoryHeap>& getHeap() const { return mHeap; }; o: v/ K  g( L# Q
};9 E" N( p" z) w2 w6 s' y2 a/ f

+ ^5 [7 C6 [( V9 j2 E/ D7 Z好了,明白上面两个MemoryXXX,我们可以猜测下大概的使用方法了。
4 L: C' o& X# ~3 h6 U" l7 L" |) O& g+ B% ?$ Z# M: D
BnXXX端先分配BnMemoryHeapBase和BnMemoryBase,0 B$ S1 u8 B& G2 X2 q4 v
然后把BnMemoryBase传递到BpXXX
: Q  S; G9 Z: G( c% ?1 h, X0 lBpXXX就可以使用BpMemoryBase得到BnXXX端分配的共享内存了。% ~/ b0 K& [" F) y# G

- e+ O1 B6 P& G% J  H$ K/ @8 c8 Z+ k- ]  U1 N8 t
注意,既然是进程间共享内存,那么Bp端肯定使用memcpy之类的函数来操作内存,这些函数是没有同步保护的,而且Android也不可能在系统内部为这种共享内存去做增加同步保护。所以看来后续在操作这些共享内存的时候,肯定存在一个跨进程的同步保护机制。我们在后面讲实际播放的时候会碰到。9 r1 X/ K% q8 [* T3 X) q3 m: L
另外,这里的SharedBuffer最终会在Bp端也就是AudioFlinger那用到。: _) o6 z5 `3 d2 \& Z) m. F0 o

: J5 {4 K+ S+ W/ U$ q* X) E2 O3.4 分析之play和write$ J- G% c. M0 f/ S
JAVA层到这一步后就是调用play和write了。JAVA层这两个函数没什么内容,都是直接转到native层干活了。: w' F" U) h3 D( d2 e4 c9 D+ c
先看看play函数对应的JNI函数
# x% G  R: H& p& s$ f5 [static void
: l; S( n( V6 \! Q+ E$ \  Y% p' Xandroid_media_AudioTrack_start(JNIEnv *env, jobject thiz)
1 v" A! z. S1 i8 k, N2 a5 ]$ @  L{7 A! y, {$ ?& ]8 P8 E6 _. L
//看见没,从JAVA那个AudioTrack对象获取保存的C++层的AudioTrack对象指针
' I1 R7 ]9 a. {//从int类型直接转换成指针。要是以后ARM变成64位平台了,看google怎么改!* _$ v: ^, }. ~
    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
3 _+ p: ^& \+ w7 [        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
7 w$ a3 \# [1 G0 S6 Q    lpTrack->start(); //这个以后再说
" J. G' P1 i8 L* |: q7 c6 z}
! N  X' Y5 o3 \" P7 W% _. b7 O: M9 x0 B
下面是write。我们写的是short数组,
, ~8 S. G/ X0 f3 C5 dstatic jint* t8 Q' ^2 J, D- J8 ~
android_media_AudioTrack_native_write_short(JNIEnv *env, jobject thiz,$ p, \9 C! E5 I# S
                                                  jshortArray javaAudioData,
- F- z2 ^. ]2 z# T3 p                                                  jint offsetInShorts,
# V" l9 |+ [& _jint sizeInShorts,
& M9 H$ x  ~) A                                                  jint javaAudioFormat) {0 |& Q) D' o7 L- E5 s3 f" x) ]- Q/ [
    return (android_media_AudioTrack_native_write(env, thiz,
- h2 P1 v$ i. T" }                                                 (jbyteArray) javaAudioData,
+ y+ {/ ^2 d! K                                                 offsetInShorts*2, sizeInShorts*2,+ _0 D6 c1 I9 Y6 F, n* J8 u
                                                 javaAudioFormat)& |' S* i1 m( l& z7 W8 _2 Y
            / 2);- \! ?) k# l- I7 l$ w
}8 W& E$ J9 v* R" w! ]8 g
烦人,又根据Byte还是Short封装了下,最终会调到重要函数writeToTrack去
5 E. m; f7 G) s* P) L; ijint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data,
, H! c% @' ]+ R7 v                  jint offsetInBytes, jint sizeInBytes) {% Z4 f" d: ^! l2 r4 h
      ssize_t written = 0;
1 L6 V# F/ M% `+ z$ N& R% e9 j    // regular write() or copy the data to the AudioTrack's shared memory?% n5 J8 i* W  q6 q/ F# L
if (pTrack->sharedBuffer() == 0) {3 K+ K7 ]8 [3 ?# u6 t
//创建的是流的方式,所以没有共享内存在track中
' H( ]  T' z0 P  k+ M  F7 f+ Z//还记得我们在native_setup中调用的set吗?流模式下AudioTrackJniStorage可没创建
% }3 q6 Z! o7 @0 \, e* e7 Q. F//共享内存$ }" C" L+ w& }) R% f
        written = pTrack->write(data + offsetInBytes, sizeInBytes);0 R4 W) ^* n' c, Y7 ^
    } else {/ a0 w4 r" U4 c% j7 [  U% z2 A' R3 D
        if (audioFormat == javaAudioTrackFields.PCM16) {
2 g+ K0 @# d' o: }            // writing to shared memory, check for capacity
. n$ D8 i, E0 F, k6 D+ H8 c            if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) {
. t& z8 I8 h- L. C6 {& g8 ]: a9 `                sizeInBytes = pTrack->sharedBuffer()->size();
  t6 j/ |( d4 s5 T+ ^            }% W& r3 V* |, a+ ]0 d+ }& o
           //看见没?STATIC模式的,就直接把数据拷贝到共享内存里  F; A4 m0 k; W: f/ Z" X
          //当然,这个共享内存是pTrack的,是我们在set时候把AudioTrackJniStorage的5 _$ c- _& j4 C) y
//共享设进去的' s/ E, D; |1 K7 K# \, b( h) x
            memcpy(pTrack->sharedBuffer()->pointer(),. Q) u$ d2 k2 j9 U* s0 r
data + offsetInBytes, sizeInBytes);
; W0 I. k+ J' i% v; o: g% K            written = sizeInBytes;  v* w  r) ]% e+ |: S( A
        } else if (audioFormat == javaAudioTrackFields.PCM8) {- h+ n7 j! Q! K+ o7 n) G) Y; [3 E9 i
           PCM8格式的要先转换成PCM16
2 h7 o, _* g0 m" Z# P           * T$ `6 I: @& ~% F5 \
    }$ A9 k' X" c( U8 u) D6 J1 g/ u
    return written;
* F" C. K- @& v5 i" S4 ]4 b+ T}
% ]8 `  L! t+ W0 H" R" o. B
5 ~3 C8 o9 g+ G" _) Q; a$ `* G! i到这里,似乎很简单啊,JAVA层的AudioTrack,无非就是调用write函数,而实际由JNI层的C++ AudioTrack write数据。反正JNI这层是再看不出什么有意思的东西了。
: `) v! z' |) O四 AudioTrack(C++层)
" _) _- G6 k$ O2 H: b+ Q接上面的内容,我们知道在JNI层,有以下几个步骤:
! s6 A6 A' u5 R, ^1 j- @8 @! @- z$ O8 Q
new了一个AudioTrack
& }' F: }1 i. v; Z4 W* J& Z调用set函数,把AudioTrackJniStorage等信息传进去
. J: V) z$ q+ Z1 H5 [! u调用了AudioTrack的start函数
4 {, T  j+ G' v调用AudioTrack的write函数4 M9 b# H. T# h) t$ w$ \

+ d: \; b" r: ~那么,我们就看看真正干活的的C++AudioTrack吧。( r7 U: w- d0 C; Z
AudioTrack.cpp位于framework/base/libmedia/AudioTrack.cpp
! ~3 u$ }. ^5 h1 j, x& }6 A1 `4.1 new AudioTrack()和set调用
4 B1 H, I9 B. I( dJNI层调用的是最简单的构造函数:
% A4 G4 t+ u  F! {0 ZAudioTrack::AudioTrack()
% T2 t) S3 Y# M1 ^' j2 [    : mStatus(NO_INIT) //把状态初始化成NO_INIT。Android大量使用了设计模式中的state。7 @9 s0 l1 _1 V" W- u
{+ x+ [5 p& T9 L4 _
}
+ |  p# z! a) I+ a: O' K接下来调用set。我们看看JNI那set了什么( c) o( t/ h' W( d8 W4 H
lpTrack->set(
$ l# B$ s4 k  y; C& \6 m7 B2 [+ Z            atStreamType, //应该是Music吧
$ a, [5 k6 V# u: s$ `( V; m" A4 g            sampleRateInHertz,//8000+ w% K: Z( Q" _# V( n
            format,// 应该是PCM_16吧
* |7 X2 D% l) [9 C            channels,//立体声=2) \. s6 g! m! ^0 R0 p5 ~
            frameCount,//% ]' w8 _+ T1 M
            0,// flags
7 u8 |3 N! {. z' f            audioCallback, //JNI中的一个回调函数  W7 \# p8 Y8 @- |/ F
&(lpJniStorage->mCallbackData),//回调函数的参数4 f$ \* p6 Z7 c
            0,// 通知回调函数,表示AudioTrack需要数据,不过暂时没用上$ [. X  b' O' P6 y/ @0 }
            0,//共享buffer地址,stream模式没有, _- N2 F8 f8 c5 t4 J
            true);//回调线程可以调JAVA的东西
* R+ h  E+ P$ P1 B) A4 d( {那我们看看set函数把。; Q* o4 V. E3 I/ b! X: D! @" G( I! m5 w
status_t AudioTrack::set(, _  Y& ?; `+ b7 }6 t! n
        int streamType,
7 k, Q7 c+ b7 U3 U1 G% G( I        uint32_t sampleRate,: [: {$ g4 Y2 ~& o& Q& M
        int format,
6 @( L3 @" m! V& C4 A/ y8 Z& A, a        int channels,
$ C: j7 n* G! q% @3 T        int frameCount,, J% y* Q1 L% v/ f6 z. D- X- b5 {
        uint32_t flags,
) a; ]2 C) j0 S4 Y        callback_t cbf,
* \% T5 S- t! i2 E        void* user,3 i5 [- B8 T: `( S" t- }
        int notificationFrames,
9 O* }4 p7 e+ k* t. t3 D        const sp<IMemory>& sharedBuffer,8 W4 W( @0 m) b
        bool threadCanCallJava)$ X* N- B7 Q! s1 n+ Y" A
{! C# {1 f; A  q6 U: R( Q6 F, j2 Y/ T
   ...前面一堆的判断,等以后讲AudioSystem再说3 {; H9 {5 C  v( u8 h
audio_io_handle_t output =3 h1 |0 Y8 Y! v$ [# M
AudioSystem::getOutput((AudioSystem::stream_type)streamType,
" Z, y- L) i; ?9 w. n            sampleRate, format, channels, (AudioSystem::output_flags)flags);
  g) ], |  b. @/ G( ?8 q  //createTrack?看来这是真正干活的  x; e2 K6 ?% n5 j$ }# ^! B8 L' }
    status_t status = createTrack(streamType, sampleRate, format, channelCount,
' Z/ F* i+ _0 d$ S3 v                                  frameCount, flags, sharedBuffer, output);
5 u- V3 \9 V  a9 } //cbf是JNI传入的回调函数audioCallback! G3 n* d  \& Z( i+ P
     if (cbf != 0) { //看来,怎么着也要创建这个线程了!, O+ ~" `' ~: C- q; o9 U
        mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
2 o" A# z. S9 F7 U       }0 b" u4 S+ ^9 w. Y! m' C
   return NO_ERROR;
, L+ u. |+ E4 R  W6 }1 ]% A}- {. s6 s  R& |% W0 b# g2 Y, j
看看真正干活的createTrack
" ^" h4 l8 g; \% g8 c& I( ]* T5 qstatus_t AudioTrack::createTrack(1 }* f2 T8 B+ W4 I
       int streamType,4 i' A3 _# q) \" K* z( r# L
        uint32_t sampleRate,
& K) m) A. g! {# b4 {        int format,
$ S. k" J" ?; T& ]9 k& e0 }6 ^        int channelCount,0 |  J6 n5 k7 s4 b# @4 I/ r
        int frameCount,
, W" Y) s" S( I3 j, L        uint32_t flags,
! O- N8 X* Q8 b* D4 P0 ~' k        const sp<IMemory>& sharedBuffer,
! n7 _, ?1 {$ Q1 w9 m6 ]        audio_io_handle_t output)
- f. V1 A/ y% n{
# Z" i$ @# {  _# O6 jstatus_t status;9 W, {+ Y/ A/ W0 R/ [$ w
//啊,看来和audioFlinger挂上关系了呀。7 g. R; D* I; ~& N8 `1 B& e" m5 n
    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();  q' g. k3 @. G+ w; q4 p
  8 [1 D3 z# [  [/ N- _2 b1 I: _2 I% A
//下面这个调用最终会在AudioFlinger中出现。暂时不管它。4 r1 I" N1 Z+ {9 L% Z' O. I" q
    sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),$ n, w; {" G' M: Z
                                                      streamType,
5 z4 d4 M9 l) Q/ O  Q9 V                                                      sampleRate,! G5 i. c' K2 s/ O5 X
                                                      format,
- Q/ t6 u2 K1 c7 f; N                                                      channelCount,
( h$ T& [" X& c) j$ S9 ~, f                                                      frameCount,) m9 o" A) z2 m8 x9 K( t
                                                      ((uint16_t)flags) << 16,8 j. C; U' |( K$ h( b6 W' X
                                                      sharedBuffer,
$ p2 J2 F* X7 j2 f: q% n                                                      output,; e$ s9 x  l) G
                                                      &status);8 q$ v  |' d, N7 p7 B, `

6 t" f! S' f& |( W; O& A7 R1 o+ X) H   //看见没,从track也就是AudioFlinger那边得到一个IMemory接口
1 }) U* X; p. p4 D6 ~: b//这个看来就是最终write写入的地方
: T" O/ g* B/ y2 E, M    sp<IMemory> cblk = track->getCblk();
8 Z4 E$ k, ^6 r& s7 {  Y! H    mAudioTrack.clear();5 q' ?& t8 M) d3 O6 o
    mAudioTrack = track;" |" m7 n1 |5 V, ~, e, S( m
    mCblkMemory.clear();//sp<XXX>的clear,就看着做是delete XXX吧8 K) z2 a) l6 f' V( g1 n+ Q
    mCblkMemory = cblk;4 Z" k" _5 b. v: U) s: \- u
    mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());, U2 d) d( f$ a2 y- ^
    mCblk->out = 1;& G1 w* Y8 C2 W
   4 `  s: o; E  A7 k/ {( I0 e
    mFrameCount = mCblk->frameCount;$ B8 o+ J# L! J1 x
if (sharedBuffer == 0) {
! U2 D& x/ f0 a+ N# X. `5 D//终于看到buffer相关的了。注意我们这里的情况
9 q: d1 C: m5 e+ l//STREAM模式没有传入共享buffer,但是数据确实又需要buffer承载。+ |* o' v3 J0 N! [- K0 x
//反正AudioTrack是没有创建buffer,那只能是刚才从AudioFlinger中得到: f0 I8 v* Y$ }! y8 l0 i
//的buffer了。
2 H. r& Q3 ~5 b8 X: G/ J; m6 T        mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
2 c" |' Z8 {& s6 C# S( {+ l+ O    }  k7 F8 ]7 w4 R9 C
    return NO_ERROR;' J  r$ \9 g, i" o7 q6 d) o
}
' |3 ^/ f9 H& R, o; f4 l( F3 K* B3 F. i7 r
还记得我们说MemoryXXX没有同步机制,所以这里应该有一个东西能体现同步的,# U2 y* r. }% x. X' ~% H4 D
那么我告诉大家,就在audio_track_cblk_t结构中。它的头文件在1 }6 F  s  e& e+ V; ~7 C7 R
framework/base/include/private/media/AudioTrackShared.h
! D. C! ?4 {0 }实现文件就在AudioTrack.cpp中9 E) Y- m0 D. ~5 v. [+ g( Z
audio_track_cblk_t::audio_track_cblk_t()/ M2 @) |: F, C2 n
//看见下面的SHARED没?都是表示跨进程共享的意思。这个我就不跟进去说了
/ j  N' b  S, N, D) S: J//等以后介绍同步方面的知识时,再细说& \& v+ W" X' Z- l. u1 v5 h
    : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
- M1 v& M6 c6 Y4 X3 _, y0 z! ~2 {    userBase(0), serverBase(0), buffers(0), frameCount(0),$ X6 \- h# t) t
    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),) J8 x# c' Y$ ^; r- N- v* [
    flowControlFlag(1), forceReady(0). h! I% l# L' a4 m
{
" R" d% g4 {! i}! d1 ]! o! E! K* J: J! r

# u0 K7 c" [! [& J3 V) \到这里,大家应该都有个大概的全景了。
/ n; r, D, g9 \. o* P, v9 V2 O7 q
AudioTrack得到AudioFlinger中的一个IAudioTrack对象,这里边有一个很重要的数据结构audio_track_cblk_t,它包括一块缓冲区地址,包括一些进程间同步的内容,可能还有数据位置等内容
! D3 m3 H9 T$ S, l/ p4 y( _) \& A7 ^AudioTrack启动了一个线程,叫AudioTrackThread,这个线程干嘛的呢?还不知道8 W' _; x' x  X3 b
AudioTrack调用write函数,肯定是把数据写到那块共享缓冲了,然后IAudioTrack在另外一个进程AudioFlinger中(其实AudioFlinger是一个服务,在mediaservice中运行)接收数据,并最终写到音频设备中。5 m. {4 x, j2 {

+ @; o3 _* ^1 V# L) [( [  G那我们先看看AudioTrackThread干什么了。# W; n: C- R5 O% `% e4 G4 |
调用的语句是:& s$ W/ b3 {( c6 S4 q3 G" ~
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
/ ]( k  O4 }7 P' j& xAudioTrackThread从Thread中派生,这个内容在深入浅出Binder机制讲过了。
3 V7 H( y5 o& l: i反正最终会调用AudioTrackAThread的threadLoop函数。
  p" y" S  i, s- {( b; A先看看构造函数
: @; y. a$ Y$ _9 ~$ E8 yAudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
3 ^0 Q; z; O; Z& E2 U    : Thread(bCanCallJava), mReceiver(receiver)
  V' z2 O$ Z6 F6 v! O& S{ //mReceiver就是AudioTrack对象( N' _0 _, @0 X6 @/ @7 r
// bCanCallJava为TRUE3 v6 c. e5 d0 o1 i! b3 Q4 [
}
% @$ i' d1 f3 R1 A# G9 o4 B+ @2 J1 v) @+ C2 C/ f7 [
这个线程的启动由AudioTrack的start函数触发。
9 }5 W+ I9 {8 j; O/ d' C4 @. Ovoid AudioTrack::start()
7 |# @* f% U8 A3 ^" P, h- n. |{
( }, }/ X. Z0 R" N! N6 S) G! E( n //start函数调用AudioTrackThread函数触发产生一个新的线程,执行mAudioTrackThread的
% u% T7 @7 q5 o$ D$ c$ A+ U$ kthreadLoop
- G3 P6 D0 D0 n  n5 _7 p$ E    sp<AudioTrackThread> t = mAudioTrackThread;- c. ]% ]% N, x- x
t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
9 L. h( |! Y$ l. L5 q1 r4 S//让AudioFlinger中的track也start
& x: U) t( J6 `' Y/ W    status_t status = mAudioTrack->start();
, f/ A' q  x) [7 R, p: [" Y- j}
* z- n1 \3 K( G- D* E  y- Pbool AudioTrack::AudioTrackThread::threadLoop()
) N* a$ d  i' N6 y/ J. O  ~" w{
, F( ?0 g$ y- A% y  f4 \+ Z0 I: \ //太恶心了,又调用AudioTrack的processAudioBuffer函数
, u8 Z# \6 x9 C& ?& O: p. J' p  [3 X9 @return mReceiver.processAudioBuffer(this);* A; |8 \# i% s9 A+ B+ J. m4 p+ ]
}
3 ]% M/ g/ E. P: B$ V3 h% d+ g% _bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)8 j5 e! {) |( i* N
{
& s; T- k! c" O1 T  x. `  j* P# VBuffer audioBuffer;8 E" |9 \0 u! ~+ B6 i# @
    uint32_t frames;1 ~  {3 k9 s% e! R% ~# ~& Q- v  @
    size_t writtenSize;
* J. ^  H% i3 u      ...回调1
3 T+ j. b6 D5 h) ]$ z3 A         mCbf(EVENT_UNDERRUN, mUserData, 0);6 c4 F) g8 I- b, s$ D! \1 U
...回调2 都是传递一些信息到JNI里边2 ~$ p1 C' M$ |% i; x2 J& u4 `
         mCbf(EVENT_BUFFER_END, mUserData, 0);5 }( X7 ~% J4 i& _- }! o, x
         // Manage loop end callback
# U, m* Z0 v: S& e  C    while (mLoopCount > mCblk->loopCount) {
2 f8 O" {* T  B# l7 O3 M        mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);
* q  b) x* Y; @- I    }
7 C. k6 H. D* g //下面好像有写数据的东西5 q8 u% v9 t# S1 z) k- A  m, \' b) }
      do {
% U9 ~- [% r2 ]3 j: e/ `       audioBuffer.frameCount = frames;$ e8 u, Q6 P* ]8 [) E5 y: ]
//获得buffer,6 l# _) b! X9 ]: `$ Z! d) `  m8 C
       status_t err = obtainBuffer(&audioBuffer, 1);! v3 E8 j2 \! c3 [% Z$ X- p% w
        size_t reqSize = audioBuffer.size;
: o! u" j$ E% j  Y//把buffer回调到JNI那去,这是单独一个线程,而我们还有上层用户在那不停
* }3 y6 F5 u1 H/ w2 \//地write呢,怎么会这样?
2 i! f) S8 C  O; F2 f; x        mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);1 {( I- Q$ B' e0 \
         audioBuffer.size = writtenSize;# e' D8 d+ Y" n: Y7 E
         frames -= audioBuffer.frameCount;
, Q* W  r! I2 W: G! n* \* ~       releaseBuffer(&audioBuffer); //释放buffer,和obtain相对应,看来是LOCK和UNLOCK/ v7 t" m4 W2 `% t$ Y9 Z
操作了
; s  E7 o; l6 x6 D6 O1 i+ b    }; L+ }+ R( X7 B; ?8 C; C6 G
    while (frames);
" R, j7 U# _  R7 M2 m( l   return true;6 B4 g9 b- O1 |, C
}
: _3 _& \$ U, {8 c! B6 y
) t0 w0 {$ W( @7 g难道真的有两处在write数据?看来必须得到mCbf去看看了,传的是EVENT_MORE_DATA标志。# h  [! r/ Z: e5 x
mCbf由set的时候传入C++的AudioTrack,实际函数是:: l2 v) q/ B: t. d
static void audioCallback(int event, void* user, void *info) {
* d$ w- h" o: A5 a    if (event == AudioTrack::EVENT_MORE_DATA) {
+ o' Q' a( d; ?9 z; {" d" T# u         //哈哈,太好了,这个函数没往里边写数据0 U7 v% ~7 ^0 R
        AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;1 t. t. X, ?0 @2 O7 f+ Z
        pBuff->size = 0; ) O1 ?) ?% p. G& N9 K
     }
- x, Q3 W# I3 W- S/ o% y1 a2 o& ^! G1 q: r8 `
从代码上看,本来google考虑是异步的回调方式来写数据,可惜发现这种方式会比较复杂,尤其是对用户开放的JAVA AudioTrack会很不好处理,所以嘛,偷偷摸摸得给绕过去了。3 u4 P1 z2 F6 i, c; K' I
太好了,看来就只有用户的write会真正的写数据了,这个AudioTrackThread除了通知一下,也没什么实际有意义的操作了。5 f2 U% E5 J' Z0 Z* K
让我们看看write吧。
: r* p9 z$ {1 W4.2 write" l. P/ C1 a4 U, `2 R. b  j
ssize_t AudioTrack::write(const void* buffer, size_t userSize)
$ y2 E( f4 z7 t" Z0 M{
. V( ~& X. h4 c 够简单,就是obtainBuffer,memcpy数据,然后releasBuffer9 d% p, ]. x. ^
眯着眼睛都能想到,obtainBuffer一定是Lock住内存了,releaseBuffer一定是unlock内存了
" c3 P3 _- _: m! q     do {2 s% V0 v' |9 a1 G0 b) \1 t, m
        audioBuffer.frameCount = userSize/frameSize();9 [  [4 y" s# W8 I  d4 Z
        status_t err = obtainBuffer(&audioBuffer, -1);
" ^( }  p; L5 \3 B. _3 ^& f         size_t toWrite;0 y( t+ m- j! t4 k0 U
         toWrite = audioBuffer.size;
6 f8 n& C# f% O         memcpy(audioBuffer.i8, src, toWrite);
" f! k9 k3 G  K         src += toWrite;* s% i! p* l0 l, \7 i, h( O( I
        }2 q; F; c8 S% Z: p5 O, ]- t
        userSize -= toWrite;) j$ y$ f; L% K% S
        written += toWrite;
) O( p1 s: Y5 E% w        releaseBuffer(&audioBuffer);( ^: o. f8 _8 Y1 q
    } while (userSize);8 u5 W7 m+ m& H; e# B
/ K6 a- B4 I, G  A3 W
    return written;! j% A* p8 q4 x2 i6 Q: E- u
}9 v  `/ d2 N# V* q; `% Z
obtainBuffer太复杂了,不过大家知道其大概工作方式就可以了
; l# r" t5 E& B; D6 @4 pstatus_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)9 o5 @, a9 X. e' b% {, Z
{3 A* p4 w5 B' U! c4 `6 K8 ?
   //恕我中间省略太多,大部分都是和当前数据位置相关,' V$ g# [  {3 B% j
uint32_t framesAvail = cblk->framesAvailable();
& m$ R7 k" q$ O5 F4 C     cblk->lock.lock();//看见没,lock了& I* K* p8 c4 S( ^' L3 q- M" `
     result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));. L, P/ G" ?+ i. K: q& p' s3 @
//我发现很多地方都要判断远端的AudioFlinger的状态,比如是否退出了之类的,难道
5 M2 O' h+ m* V* t0 a- T2 a$ k//没有一个好的方法来集中处理这种事情吗?
* [9 j2 R) N# q  n& j: y2 n      if (result == DEAD_OBJECT) {
2 y# @* \3 Y& ]: z7 r* V$ A$ J        result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount,& _) G# _6 C6 G
          mFrameCount, mFlags, mSharedBuffer,getOutput());
# ]# t. d2 }1 F' {        }
, N% w/ }3 J0 \% ]1 {5 V$ r* k//得到buffer
+ d, }  f( v) y# K" }) @8 D    audioBuffer->raw = (int8_t *)cblk->buffer(u);
+ u/ N$ W& `, ~  } return active ? status_t(NO_ERROR) : status_t(STOPPED);
. w, @8 ~4 e& u  d+ i}( k3 n0 L- @1 l; p# z
在看看releaseBuffer
  D" E7 {8 q2 f+ @. c" r2 l& p  xvoid AudioTrack::releaseBuffer(Buffer* audioBuffer)
1 |/ s0 f! x# }8 C{
, U* c6 b+ [! A1 d+ F% L* ~3 j: O    audio_track_cblk_t* cblk = mCblk;: n3 V0 K3 r1 {
cblk->stepUser(audioBuffer->frameCount);8 h' C8 t) n/ d+ }0 {# H
}7 ?+ P- _' \4 ~9 [
uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount)
- @( R. Y* v! [4 T{
$ L+ X/ r) W- a; w. y% p3 [    uint32_t u = this->user;
* ?, v+ s; n. y; w4 w
: X" Y0 c* U$ d9 ~" j) ^0 d, d    u += frameCount;; i2 w# a) C& {' T4 q3 C' T. G
     if (out) {4 @  @. M& ]# n' L2 A1 T3 T: W
          if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) {
9 D5 \  Z& a8 C            bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
( U! l2 h$ v) h$ J: d. W& b        }
; A2 U5 E. y3 Y- F2 U/ {    } else if (u > this->server) {5 y; B8 X" T2 @. v' V
         u = this->server;
" D6 F+ ]& u8 Z- v% Q    }3 v3 y2 z- \7 n/ _7 x; O% W

7 l7 a$ _3 `. x9 ^( e: n6 \( P    if (u >= userBase + this->frameCount) {1 T+ w3 G% E0 m" Y6 w  |
        userBase += this->frameCount;
% b4 q& X) ~6 W( J    }
2 `+ f2 e9 J! s- H   this->user = u;
, K2 K% G0 @' M/ G) J flowControlFlag = 0;
7 L9 F8 h- P2 ^; v' m return u;/ q8 [& l& Y# g3 f8 g/ s0 _
}
7 k9 W$ x. j8 d. ?2 c8 p7 m奇怪了,releaseBuffer没有unlock操作啊?难道我失误了?, a% \# Y) ~. O$ p
再去看看obtainBuffer?为何写得这么晦涩难懂?* h2 [5 y. F( ^  A3 P7 n
原来在obtainBuffer中会某一次进去lock,再某一次进去可能就是unlock了。没看到obtainBuffer中到处有lock,unlock,wait等同步操作吗。一定是这个道理。难怪写这么复杂。还使用了少用的goto语句。( P" v4 P7 Y5 ?) `- V/ F
唉,有必要这样吗!  s$ O" z) I, {6 W* u& _7 V: X& M
* k% d% B0 j- o5 x
五 AudioTrack总结6 ]0 I1 R! u. {) S) O
通过这一次的分析,我自己觉得有以下几个点:
- @% c, S, B# U9 _4 F# M0 P. E" w$ u, }, q/ ~
AudioTrack的工作原理,尤其是数据的传递这一块,做了比较细致的分析,包括共享内存,跨进程的同步等,也能解释不少疑惑了。/ [) x% t) i: |/ R% n% a
看起来,最重要的工作是在AudioFlinger中做的。通过AudioTrack的介绍,我们给后续深入分析AudioFlinger提供了一个切入点1 }  t$ I9 I8 l4 B' D* I0 r
工作原理和流程嘛,再说一次好了,JAVA层就看最前面那个例子吧,实在没什么说的。
: N1 o# J- L8 H3 B; D% H% tAudioTrack被new出来,然后set了一堆信息,同时会通过Binder机制调用另外一端的AudioFlinger,得到IAudioTrack对象,通过它和AudioFlinger交互。/ D* E2 }( P9 t  a( q) a+ B2 y
调用start函数后,会启动一个线程专门做回调处理,代码里边也会有那种数据拷贝的回调,但是JNI层的回调函数实际并没有往里边写数据,大家只要看write就可以了
2 G1 P1 Q2 v* ^5 m用户一次次得write,那AudioTrack无非就是把数据memcpy到共享buffer中咯
1 @! `/ R9 e$ v可想而知,AudioFlinger那一定有一个线程在memcpy数据到音频设备中去。我们拭目以待。9 B( G& i0 _6 r( H

2 ]( d; b8 @9 {+ i5 Z
您需要登录后才可以回帖 登录 | 快速注册

本版积分规则

软硬产品代理咨询服务:声卡:雅马哈UR22C、罗兰声卡、福克斯特、艾肯、阿波罗 、M-audio 、普瑞声纳 、福克斯特、 达思冠系列 音箱:吸顶NS-IW560C、吸顶NS-IW660 、NS-AW350、低音NS-SW050、低音NS-SW100;JBL 吸顶8124、CSS-8006BM;香蕉猴 gibbon系列;普瑞声纳E5XT、E4.5、 E3.5BT 有线话筒:舒尔PGA27、PGA48、MV51、mv88、mv88+、SM27 ;森海E945 、MK4、E835S;舒伯乐top248s;罗德NT1-A、VIDEOMIC、VIDEOMIC GO、VideoMicro、VideoMic NTG 无线领夹麦克风:罗德 wireless go II 一拖一、一拖二;猛犸lark150 耳机:森海HD300 pro、美奇 CR-Buds 、索尼7506、爱科技K240S、K240 MKII、K271 MKII、K52、K72、K92、先锋、飞利浦 1:飞利浦会务通/会议摄像头/全向麦克风/执法仪/录音笔 2:洋铭便携式移动演播室 / 切换台 / 摄控一体摄像机 / 虚拟演播室 / 微金课教室 / 色键器 3:逻兰音视频切换台 / 声卡 / 电子鼓 /电钢琴 /耳机 4:Blackmagic专业摄影机 /调色台 / 切换台/ 广播级转换器 / 监视器 / 采集卡 5:索尼专业摄像机/佳能专业摄像机/松下专业摄像机/ insta360专业摄像机 6:话筒:铁三角/ 舒尔/ 森海塞尔 / AKG / RODE/ BBS 7:音响:YAMAHA/ 声艺 / 皇冠 /JBL / 真力/咪宝/BOSE /美奇 8:声卡:RME/羚羊/IXI /艾肯/PreSonus普瑞声纳/Focusrite福克斯特/YAMAHA/雅马哈/ickb 9:耳机:铁三角/beyerdynamic拜亚动力/AKG爱科技/索尼/RunningMan/美技 10:思锐三脚架 /防潮箱 /米泊三脚架/意美捷三脚架/曼富图三脚架 11:XSW系列,300.500代理商,EWD数字系列代理,6000.9000定制产品,还包销了全国三个型号:XSW1-825,EW100 G4-945,EWD- kk 205

小黑屋|手机版|Archiver|音频应用 (鄂ICP备13005321号-1)

Powered by Audio app

快速回复 返回顶部 返回列表