AudioRecord.read

    <div id="post_detail">

(四)Audio子系统之AudioRecord.read

 

在上一篇文章《(三)Audio子系统之AudioRecord.startRecording》中已经介绍了AudioRecord如何开始录制音频,接下来,继续分析AudioRecord方法中的read的实现

 

  

 

  函数原型:

 

    public int read(byte[] audioData, int offsetInBytes, int sizeInBytes)

 

       作用:

 

    从音频硬件录制缓冲区读取数据,直接复制到指定缓冲区。 如果audioBuffer不是直接的缓冲区,此方法总是返回0

 

  参数:

 

    audioData:写入的音频录制数据

 

    offsetInBytes:audioData的起始偏移值,单位byte

 

    sizeInBytes:读取的最大字节数

 

  返回值:

 

    读入缓冲区的总byte数,如果对象属性没有初始化,则返回ERROR_INVALID_OPERATION,如果参数不能解析成有效的数据或索引,则返回ERROR_BAD_VALUE。 读取的总byte数不会超过sizeInBytes

 

 

 

接下来进入系统分析具体实现

frameworks\base\media\java\android\media\AudioRecord.java

01
02
03
04
05
06
07
08
09
10
11
12
13
public int read( byte [] audioData, int offsetInBytes, int sizeInBytes) {
     if (mState != STATE_INITIALIZED) {
         return ERROR_INVALID_OPERATION;
     }
 
     if ( (audioData == null ) || (offsetInBytes < 0 ) || (sizeInBytes < 0 )
             || (offsetInBytes + sizeInBytes < 0 // detect integer overflow
             || (offsetInBytes + sizeInBytes > audioData.length)) {
         return ERROR_BAD_VALUE;
     }
 
     return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes);
}

这里我们只分析获取byte[]类型的数据

frameworks\base\core\jni\android_media_AudioRecord.cpp

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
                                                         jbyteArray javaAudioData,
                                                         jint offsetInBytes, jint sizeInBytes) {
     jbyte* recordBuff = NULL;
     // get the audio recorder from which we'll read new audio samples
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder == NULL) {
         ALOGE( "Unable to retrieve AudioRecord object, can't record" );
         return 0 ;
     }
 
     if (!javaAudioData) {
         ALOGE( "Invalid Java array to store recorded audio, can't record" );
         return 0 ;
     }
 
     recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
 
     if (recordBuff == NULL) {
         ALOGE( "Error retrieving destination for recorded audio data, can't record" );
         return 0 ;
     }
 
     // read the new audio data from the native AudioRecord object
     ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
 
     ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
                                         sizeInBytes > (jint)recorderBuffSize ?
                                             (jint)recorderBuffSize : sizeInBytes );
     env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0 );
 
     if (readSize < 0 ) {
         readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
     }
     return (jint) readSize;
}
这里根据AudioRecord.cpp中的mFrameCount*mFrameSize 重新计算出Audio缓冲区大小,并与传过来的size比较,选择较小的那个数,然后再调用lpRecorder->read函数,需要注意的是mFrameCount这个值在调用openRecord_l函数的时候更新过,我在实际测试的时候发现mFrameCount重新计算过后为3072,而之前是2048,所以这里传过去的buffSize依然是4092
frameworks\av\media\libmedia\AudioRecord.cpp
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
ssize_t AudioRecord::read( void * buffer, size_t userSize)
{
     if (mTransfer != TRANSFER_SYNC) {
         return INVALID_OPERATION;
     }
 
     if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0 )) {
         // sanity-check. user is most-likely passing an error code, and it would
         // make the return value ambiguous (actualSize vs error).
         ALOGE( "AudioRecord::read(buffer=%p, size=%zu (%zu)" , buffer, userSize, userSize);
         return BAD_VALUE;
     }
 
     ssize_t read = 0 ;
     Buffer audioBuffer;
 
     while (userSize >= mFrameSize) {
         audioBuffer.frameCount = userSize / mFrameSize;
 
         status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever);
         if (err < 0 ) {
             if (read > 0 ) {
                 break ;
             }
             return ssize_t(err);
         }
 
         size_t bytesRead = audioBuffer.size;
         memcpy(buffer, audioBuffer.i8, bytesRead);
         buffer = (( char *) buffer) + bytesRead;
         userSize -= bytesRead;
         
         read += bytesRead;
 
         releaseBuffer(&audioBuffer);
     }
 
     return read;
}

这个mFrameSize是通过channelCount*采样精度所占字节计算得出的,所以每次通过obtainBuffer获取共享内存中的数据,然后通过memcpy把数据拷贝到应用层的buffer中,直到把整个userSize都拷贝到buffer中为止。

这里就详细分析下obtainBuffer函数

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
         struct timespec *elapsed, size_t *nonContig)
{
     // previous and new IAudioRecord sequence numbers are used to detect track re-creation
     uint32_t oldSequence = 0 ;
     uint32_t newSequence;
 
     Proxy::Buffer buffer;
     status_t status = NO_ERROR;
 
     static const int32_t kMaxTries = 5 ;
     int32_t tryCounter = kMaxTries;
 
     do {
         // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to
         // keep them from going away if another thread re-creates the track during obtainBuffer()
         sp<AudioRecordClientProxy> proxy;
         sp<IMemory> iMem;
         sp<IMemory> bufferMem;
         {
             // start of lock scope
             AutoMutex lock(mLock);
 
             newSequence = mSequence;
             // did previous obtainBuffer() fail due to media server death or voluntary invalidation?
             if (status == DEAD_OBJECT) {
                 // re-create track, unless someone else has already done so
                 if (newSequence == oldSequence) {
 
                     status = restoreRecord_l( "obtainBuffer" );
                     if (status != NO_ERROR) {
                         buffer.mFrameCount = 0 ;
                         buffer.mRaw = NULL;
                         buffer.mNonContig = 0 ;
                         break ;
                     }
                 }
             }
             oldSequence = newSequence;
 
             // Keep the extra references
             proxy = mProxy;
             iMem = mCblkMemory;
             bufferMem = mBufferMemory;
 
             // Non-blocking if track is stopped
             if (!mActive) {
                 requested = &ClientProxy::kNonBlocking;
             }
 
         }   // end of lock scope
 
         buffer.mFrameCount = audioBuffer->frameCount;
         // FIXME starts the requested timeout and elapsed over from scratch
         status = proxy->obtainBuffer(&buffer, requested, elapsed);
 
     } while ((status == DEAD_OBJECT) && (tryCounter-- > 0 ));
 
     audioBuffer->frameCount = buffer.mFrameCount;
     audioBuffer->size = buffer.mFrameCount * mFrameSize;
     audioBuffer->raw = buffer.mRaw;
 
     if (nonContig != NULL) {
         *nonContig = buffer.mNonContig;
     }
     return status;
}

在这个函数中的主要工作如下:

    1.获取AudioRecordClientProxy代理,mCblkMemory与mBufferMemory,他们是在构建AudioRecord对象的时候,通过AF端获取的,即RecordThread::RecordTrack->getCblk()与RecordThread::RecordTrack->getBuffers()

    2.在start中,AudioRecordThread.resume之前,mActive已经标记为true了,所以requested还是&ClientProxy::kForever;

    3.调用proxy->obtainBuffer继续获取数据;

    3.更新audioBuffer的frameCount,size以及pcm数据;

这里继续分析第3步:ClientProxy::obtainBuffer

frameworks\av\media\libmedia\AudioTrackShared.cpp

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
         struct timespec *elapsed)
{
     LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0 );
     struct timespec total;          // total elapsed time spent waiting
     total.tv_sec = 0 ;
     total.tv_nsec = 0 ;
     bool measure = elapsed != NULL; // whether to measure total elapsed time spent waiting
 
     status_t status;
     enum {
         TIMEOUT_ZERO,       // requested == NULL || *requested == 0
         TIMEOUT_INFINITE,   // *requested == infinity
         TIMEOUT_FINITE,     // 0 < *requested < infinity
         TIMEOUT_CONTINUE,   // additional chances after TIMEOUT_FINITE
     } timeout;
     if (requested == NULL) {
         timeout = TIMEOUT_ZERO;
     } else if (requested->tv_sec == 0 && requested->tv_nsec == 0 ) {
         timeout = TIMEOUT_ZERO;
     } else if (requested->tv_sec == INT_MAX) {
         timeout = TIMEOUT_INFINITE;
     } else {
         timeout = TIMEOUT_FINITE;
         if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) {
             measure = true ;
         }
     }
     struct timespec before;
     bool beforeIsValid = false ;
     audio_track_cblk_t* cblk = mCblk;
     bool ignoreInitialPendingInterrupt = true ;
     // check for shared memory corruption
     if (mIsShutdown) {
         status = NO_INIT;
         goto end;
     }
     for (;;) {
         int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->mFlags);
         // check for track invalidation by server, or server death detection
         if (flags & CBLK_INVALID) {
             ALOGV( "Track invalidated" );
             status = DEAD_OBJECT;
             goto end;
         }
         // check for obtainBuffer interrupted by client
         if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) {
             ALOGV( "obtainBuffer() interrupted by client" );
             status = -EINTR;
             goto end;
         }
         ignoreInitialPendingInterrupt = false ;
         // compute number of frames available to write (AudioTrack) or read (AudioRecord)
         int32_t front;
         int32_t rear;
 
         if (mIsOut) {
             // The barrier following the read of mFront is probably redundant.
             // We're about to perform a conditional branch based on 'filled',
             // which will force the processor to observe the read of mFront
             // prior to allowing data writes starting at mRaw.
             // However, the processor may support speculative execution,
             // and be unable to undo speculative writes into shared memory.
             // The barrier will prevent such speculative execution.
             front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
             rear = cblk->u.mStreaming.mRear;
         } else {
             // On the other hand, this barrier is required.
             rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
             front = cblk->u.mStreaming.mFront;
         }
         ssize_t filled = rear - front;
 
         // pipe should not be overfull
         if (!( 0 <= filled && (size_t) filled <= mFrameCount)) {
             if (mIsOut) {
                 ALOGE( "Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); "
                         "shutting down" , filled, mFrameCount);
                 mIsShutdown = true ;
                 status = NO_INIT;
                 goto end;
             }
             // for input, sync up on overrun
             filled = 0 ;
             cblk->u.mStreaming.mFront = rear;
             ( void ) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
         }
         // don't allow filling pipe beyond the nominal size
         size_t avail = mIsOut ? mFrameCount - filled : filled;
         if (avail > 0 ) {
             // 'avail' may be non-contiguous, so return only the first contiguous chunk
             size_t part1;
             if (mIsOut) {
                 rear &= mFrameCountP2 - 1 ;
                 part1 = mFrameCountP2 - rear;
             } else {
                 front &= mFrameCountP2 - 1 ;
                 part1 = mFrameCountP2 - front;
             }
             if (part1 > avail) {
                 part1 = avail;
             }
             if (part1 > buffer->mFrameCount) {
                 part1 = buffer->mFrameCount;
             }
             buffer->mFrameCount = part1;
             buffer->mRaw = part1 > 0 ?
                     &(( char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
             buffer->mNonContig = avail - part1;
             mUnreleased = part1;
             status = NO_ERROR;
 
             break ;
         }
         struct timespec remaining;
         const struct timespec *ts;
 
         switch (timeout) {
         case TIMEOUT_ZERO:
             status = WOULD_BLOCK;
             goto end;
         case TIMEOUT_INFINITE:
             ts = NULL;
             break ;
         case TIMEOUT_FINITE:
             timeout = TIMEOUT_CONTINUE;
             if (MAX_SEC == 0 ) {
                 ts = requested;
                 break ;
             }
             // fall through
         case TIMEOUT_CONTINUE:
             // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
             if (!measure || requested->tv_sec < total.tv_sec ||
                     (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
                 status = TIMED_OUT;
                 goto end;
             }
             remaining.tv_sec = requested->tv_sec - total.tv_sec;
             if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0 ) {
                 remaining.tv_nsec += 1000000000 ;
                 remaining.tv_sec++;
             }
             if ( 0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
                 remaining.tv_sec = MAX_SEC;
                 remaining.tv_nsec = 0 ;
             }
             ts = &remaining;
             break ;
         default :
             LOG_ALWAYS_FATAL( "obtainBuffer() timeout=%d" , timeout);
             ts = NULL;
             break ;
         }
 
         int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
 
         if (!(old & CBLK_FUTEX_WAKE)) {
             if (measure && !beforeIsValid) {
                 clock_gettime(CLOCK_MONOTONIC, &before);
                 beforeIsValid = true ;
             }
             errno = 0 ;
 
             ( void ) syscall(__NR_futex, &cblk->mFutex,
                     mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
 
             // update total elapsed time spent waiting
             if (measure) {
                 struct timespec after;
                 clock_gettime(CLOCK_MONOTONIC, &after);
                 total.tv_sec += after.tv_sec - before.tv_sec;
                 long deltaNs = after.tv_nsec - before.tv_nsec;
                 if (deltaNs < 0 ) {
                     deltaNs += 1000000000 ;
                     total.tv_sec--;
                 }
                 if ((total.tv_nsec += deltaNs) >= 1000000000 ) {
                     total.tv_nsec -= 1000000000 ;
                     total.tv_sec++;
                 }
                 before = after;
                 beforeIsValid = true ;
             }
 
             switch (errno) {
             case 0 :            // normal wakeup by server, or by binderDied()
             case EWOULDBLOCK:  // benign race condition with server
             case EINTR:        // wait was interrupted by signal or other spurious wakeup
             case ETIMEDOUT:    // time-out expired
                 // FIXME these error/non-0 status are being dropped
                 break ;
             default :
                 status = errno;
                 ALOGE( "%s unexpected error %s" , __func__, strerror(status));
                 goto end;
             }
         }
 
     }
 
end:
     if (status != NO_ERROR) {
         buffer->mFrameCount = 0 ;
         buffer->mRaw = NULL;
         buffer->mNonContig = 0 ;
         mUnreleased = 0 ;
     }
     if (elapsed != NULL) {
         *elapsed = total;
     }
     if (requested == NULL) {
         requested = &kNonBlocking;
     }
     if (measure) {
         ALOGV( "requested %ld.%03ld elapsed %ld.%03ld" ,
               requested->tv_sec, requested->tv_nsec / 1000000 ,
               total.tv_sec, total.tv_nsec / 1000000 );
     }
     return status;
}

这个函数的主要工作如下:

    1.从ClientProxy::kForever的定义可知,这个tv_sec是INT_MAX,所以timeout为TIMEOUT_INFINITE;

    2.从cblk中获取到rear以及front,之前分析过了,这两个指针是在RecordThread线程中维护的,他一边在读取pcm数据,一直在更新最新数据指针的位置;

    3.如果发现获取到的数据小于mFrameCount,或者没有获取到数据,那么也就是表示应用读的太快了,其实应该说RecordThread读的太慢了导致,这时候也需要设置为OVERRUN,则更新cblk的指针,重置filled为0;

    4.如果获取到了数据,则把录音数据放到mRaw中,把获取到的数据大小放到mFrameCount中。

 

总结:

    到这里,整个获取pcm数据的流程就结束了,到这里我们应该能理解整个Audio系统中对AudioBuffer的管理策略了,即通过RecordThread线程把数据从硬件层读取到IMemory中,然后应用层在去IMemory中去读取。

 

由于作者内功有限,若文章中存在错误或不足的地方,还请给位大佬指出,不胜感激!

作者:pngcui
博客园:http://www.cnblogs.com/pngcui/
github:https://github.com/pngcui
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。

分类: Android系统, Audio
0
0
« 上一篇: (五)Audio子系统之AudioRecord.stop
» 下一篇: (三)Audio子系统之AudioRecord.startRecording
	</div>
	<div class="postDesc">posted @ <span id="post-date">2019-01-01 00:38</span> <a href="https://www.cnblogs.com/pngcui/">某某璀</a> 阅读(<span id="post_view_count">157</span>) 评论(<span id="post_comment_count">0</span>)  <a href="https://i.cnblogs.com/EditPosts.aspx?postid=10016588" rel="nofollow">编辑</a> <a href="#" onclick="AddToWz(10016588);return false;">收藏</a></div>
</div>
<script type="text/javascript">var allowComments=true,cb_blogId=217947,cb_entryId=10016588,cb_blogApp=currentBlogApp,cb_blogUserGuid='28f32b1b-4dc9-e411-b908-9dcfd8948a71',cb_entryCreatedDate='2019/1/1 0:38:00';loadViewCount(cb_entryId);var cb_postType=1;var isMarkdown=false;</script>
</div><!--end: forFlow -->
</div><!--end: mainContent 主体内容容器-->

<div id="sideBar">
	<div id="sideBarMain">
		
		<div id="blog-calendar" style=""><table id="blogCalendar" class="Cal" cellspacing="0" cellpadding="0" title="Calendar">
<tbody><tr><td colspan="7"><table class="CalTitle" cellspacing="0">
	<tbody><tr><td class="CalNextPrev"><a href="javascript:void(0);" onclick="loadBlogCalendar('2019/03/01');return false;">&lt;</a></td><td align="center">2019年4月</td><td class="CalNextPrev" align="right"><a href="javascript:void(0);" onclick="loadBlogCalendar('2019/05/01');return false;">&gt;</a></td></tr>
</tbody></table></td></tr><tr><th class="CalDayHeader" align="center" abbr="日" scope="col">日</th><th class="CalDayHeader" align="center" abbr="一" scope="col">一</th><th class="CalDayHeader" align="center" abbr="二" scope="col">二</th><th class="CalDayHeader" align="center" abbr="三" scope="col">三</th><th class="CalDayHeader" align="center" abbr="四" scope="col">四</th><th class="CalDayHeader" align="center" abbr="五" scope="col">五</th><th class="CalDayHeader" align="center" abbr="六" scope="col">六</th></tr><tr><td class="CalOtherMonthDay" align="center">31</td><td align="center">1</td><td align="center">2</td><td align="center">3</td><td align="center">4</td><td align="center">5</td><td class="CalWeekendDay" align="center">6</td></tr><tr><td class="CalWeekendDay" align="center">7</td><td align="center">8</td><td align="center">9</td><td align="center">10</td><td align="center">11</td><td align="center">12</td><td class="CalWeekendDay" align="center">13</td></tr><tr><td class="CalWeekendDay" align="center">14</td><td align="center">15</td><td align="center">16</td><td align="center">17</td><td align="center">18</td><td align="center">19</td><td class="CalWeekendDay" align="center">20</td></tr><tr><td class="CalWeekendDay" align="center">21</td><td align="center">22</td><td align="center">23</td><td align="center">24</td><td align="center">25</td><td align="center">26</td><td class="CalTodayDay" align="center">27</td></tr><tr><td class="CalWeekendDay" align="center">28</td><td align="center">29</td><td align="center">30</td><td class="CalOtherMonthDay" align="center">1</td><td class="CalOtherMonthDay" align="center">2</td><td class="CalOtherMonthDay" align="center">3</td><td class="CalOtherMonthDay" align="center">4</td></tr><tr><td class="CalOtherMonthDay" align="center">5</td><td class="CalOtherMonthDay" align="center">6</td><td class="CalOtherMonthDay" align="center">7</td><td class="CalOtherMonthDay" align="center">8</td><td class="CalOtherMonthDay" align="center">9</td><td class="CalOtherMonthDay" align="center">10</td><td class="CalOtherMonthDay" align="center">11</td></tr>
		<div id="leftcontentcontainer">
			<div id="blog-sidecolumn"><div id="sidebar_search" class="sidebar-block"></div><div id="sidebar_recentposts" class="sidebar-block">
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值