blob: 793bbf4f70fb904403f615e3212103d63045c51f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2**
3** Copyright 2008, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18//#define LOG_NDEBUG 0
19#define LOG_TAG "AudioRecord"
20
21#include <stdint.h>
22#include <sys/types.h>
23
24#include <sched.h>
25#include <sys/resource.h>
26
27#include <private/media/AudioTrackShared.h>
28
29#include <media/AudioSystem.h>
30#include <media/AudioRecord.h>
31
Mathias Agopian07952722009-05-19 19:08:10 -070032#include <binder/IServiceManager.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033#include <utils/Log.h>
Mathias Agopian07952722009-05-19 19:08:10 -070034#include <binder/MemoryDealer.h>
35#include <binder/Parcel.h>
36#include <binder/IPCThreadState.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037#include <utils/Timers.h>
38#include <cutils/atomic.h>
39
40#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
41#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
42
43namespace android {
44
45// ---------------------------------------------------------------------------
46
47AudioRecord::AudioRecord()
48 : mStatus(NO_INIT)
49{
50}
51
52AudioRecord::AudioRecord(
53 int streamType,
54 uint32_t sampleRate,
55 int format,
56 int channelCount,
57 int frameCount,
58 uint32_t flags,
59 callback_t cbf,
60 void* user,
61 int notificationFrames)
62 : mStatus(NO_INIT)
63{
64 mStatus = set(streamType, sampleRate, format, channelCount,
65 frameCount, flags, cbf, user, notificationFrames);
66}
67
68AudioRecord::~AudioRecord()
69{
70 if (mStatus == NO_ERROR) {
71 // Make sure that callback function exits in the case where
72 // it is looping on buffer empty condition in obtainBuffer().
73 // Otherwise the callback thread will never exit.
74 stop();
75 if (mClientRecordThread != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 mClientRecordThread->requestExitAndWait();
77 mClientRecordThread.clear();
78 }
79 mAudioRecord.clear();
80 IPCThreadState::self()->flushCommands();
81 }
82}
83
84status_t AudioRecord::set(
85 int streamType,
86 uint32_t sampleRate,
87 int format,
88 int channelCount,
89 int frameCount,
90 uint32_t flags,
91 callback_t cbf,
92 void* user,
93 int notificationFrames,
94 bool threadCanCallJava)
95{
96
97 LOGV("set(): sampleRate %d, channelCount %d, frameCount %d",sampleRate, channelCount, frameCount);
Eric Laurentef028272009-04-21 07:56:33 -070098 if (mAudioRecord != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 return INVALID_OPERATION;
100 }
101
102 const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
103 if (audioFlinger == 0) {
104 return NO_INIT;
105 }
106
107 if (streamType == DEFAULT_INPUT) {
108 streamType = MIC_INPUT;
109 }
110
111 if (sampleRate == 0) {
112 sampleRate = DEFAULT_SAMPLE_RATE;
113 }
114 // these below should probably come from the audioFlinger too...
115 if (format == 0) {
116 format = AudioSystem::PCM_16_BIT;
117 }
118 if (channelCount == 0) {
119 channelCount = 1;
120 }
121
122 // validate parameters
123 if (format != AudioSystem::PCM_16_BIT) {
124 return BAD_VALUE;
125 }
126 if (channelCount != 1 && channelCount != 2) {
127 return BAD_VALUE;
128 }
129
130 // validate framecount
131 size_t inputBuffSizeInBytes = -1;
132 if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &inputBuffSizeInBytes)
133 != NO_ERROR) {
134 LOGE("AudioSystem could not query the input buffer size.");
135 return NO_INIT;
136 }
137 if (inputBuffSizeInBytes == 0) {
138 LOGE("Recording parameters are not supported: sampleRate %d, channelCount %d, format %d",
139 sampleRate, channelCount, format);
140 return BAD_VALUE;
141 }
142 int frameSizeInBytes = channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1);
143
144 // We use 2* size of input buffer for ping pong use of record buffer.
145 int minFrameCount = 2 * inputBuffSizeInBytes / frameSizeInBytes;
146 LOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
147
148 if (frameCount == 0) {
149 frameCount = minFrameCount;
150 } else if (frameCount < minFrameCount) {
151 return BAD_VALUE;
152 }
153
154 if (notificationFrames == 0) {
155 notificationFrames = frameCount/2;
156 }
157
158 // open record channel
159 status_t status;
160 sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), streamType,
161 sampleRate, format,
162 channelCount,
163 frameCount,
164 ((uint16_t)flags) << 16,
165 &status);
166 if (record == 0) {
167 LOGE("AudioFlinger could not create record track, status: %d", status);
168 return status;
169 }
170 sp<IMemory> cblk = record->getCblk();
171 if (cblk == 0) {
172 return NO_INIT;
173 }
174 if (cbf != 0) {
175 mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava);
176 if (mClientRecordThread == 0) {
177 return NO_INIT;
178 }
179 }
180
181 mStatus = NO_ERROR;
182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 mAudioRecord = record;
184 mCblkMemory = cblk;
185 mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
186 mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
187 mCblk->out = 0;
188 mSampleRate = sampleRate;
189 mFormat = format;
190 // Update buffer size in case it has been limited by AudioFlinger during track creation
191 mFrameCount = mCblk->frameCount;
192 mChannelCount = channelCount;
193 mActive = 0;
194 mCbf = cbf;
195 mNotificationFrames = notificationFrames;
196 mRemainingFrames = notificationFrames;
197 mUserData = user;
198 // TODO: add audio hardware input latency here
199 mLatency = (1000*mFrameCount) / mSampleRate;
200 mMarkerPosition = 0;
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700201 mMarkerReached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 mNewPosition = 0;
203 mUpdatePeriod = 0;
204
205 return NO_ERROR;
206}
207
208status_t AudioRecord::initCheck() const
209{
210 return mStatus;
211}
212
213// -------------------------------------------------------------------------
214
215uint32_t AudioRecord::latency() const
216{
217 return mLatency;
218}
219
220uint32_t AudioRecord::sampleRate() const
221{
222 return mSampleRate;
223}
224
225int AudioRecord::format() const
226{
227 return mFormat;
228}
229
230int AudioRecord::channelCount() const
231{
232 return mChannelCount;
233}
234
235uint32_t AudioRecord::frameCount() const
236{
237 return mFrameCount;
238}
239
240int AudioRecord::frameSize() const
241{
242 return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
243}
244
245// -------------------------------------------------------------------------
246
247status_t AudioRecord::start()
248{
249 status_t ret = NO_ERROR;
250 sp<ClientRecordThread> t = mClientRecordThread;
251
252 LOGV("start");
253
254 if (t != 0) {
255 if (t->exitPending()) {
256 if (t->requestExitAndWait() == WOULD_BLOCK) {
257 LOGE("AudioRecord::start called from thread");
258 return WOULD_BLOCK;
259 }
260 }
261 t->mLock.lock();
262 }
263
264 if (android_atomic_or(1, &mActive) == 0) {
265 mNewPosition = mCblk->user + mUpdatePeriod;
266 mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
267 mCblk->waitTimeMs = 0;
268 if (t != 0) {
269 t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
270 } else {
271 setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
272 }
273 ret = mAudioRecord->start();
274 }
275
276 if (t != 0) {
277 t->mLock.unlock();
278 }
279
280 return ret;
281}
282
283status_t AudioRecord::stop()
284{
285 sp<ClientRecordThread> t = mClientRecordThread;
286
287 LOGV("stop");
288
289 if (t != 0) {
290 t->mLock.lock();
291 }
292
293 if (android_atomic_and(~1, &mActive) == 1) {
Eric Laurentef028272009-04-21 07:56:33 -0700294 mCblk->cv.signal();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 mAudioRecord->stop();
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700296 // the record head position will reset to 0, so if a marker is set, we need
297 // to activate it again
298 mMarkerReached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 if (t != 0) {
300 t->requestExit();
301 } else {
302 setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL);
303 }
304 }
305
306 if (t != 0) {
307 t->mLock.unlock();
308 }
309
310 return NO_ERROR;
311}
312
313bool AudioRecord::stopped() const
314{
315 return !mActive;
316}
317
318status_t AudioRecord::setMarkerPosition(uint32_t marker)
319{
320 if (mCbf == 0) return INVALID_OPERATION;
321
322 mMarkerPosition = marker;
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700323 mMarkerReached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324
325 return NO_ERROR;
326}
327
328status_t AudioRecord::getMarkerPosition(uint32_t *marker)
329{
330 if (marker == 0) return BAD_VALUE;
331
332 *marker = mMarkerPosition;
333
334 return NO_ERROR;
335}
336
337status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
338{
339 if (mCbf == 0) return INVALID_OPERATION;
340
341 uint32_t curPosition;
342 getPosition(&curPosition);
343 mNewPosition = curPosition + updatePeriod;
344 mUpdatePeriod = updatePeriod;
345
346 return NO_ERROR;
347}
348
349status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod)
350{
351 if (updatePeriod == 0) return BAD_VALUE;
352
353 *updatePeriod = mUpdatePeriod;
354
355 return NO_ERROR;
356}
357
358status_t AudioRecord::getPosition(uint32_t *position)
359{
360 if (position == 0) return BAD_VALUE;
361
362 *position = mCblk->user;
363
364 return NO_ERROR;
365}
366
367
368// -------------------------------------------------------------------------
369
370status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
371{
372 int active;
373 int timeout = 0;
374 status_t result;
375 audio_track_cblk_t* cblk = mCblk;
376 uint32_t framesReq = audioBuffer->frameCount;
Eric Laurentef028272009-04-21 07:56:33 -0700377 uint32_t waitTimeMs = (waitCount < 0) ? cblk->bufferTimeoutMs : WAIT_PERIOD_MS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378
379 audioBuffer->frameCount = 0;
380 audioBuffer->size = 0;
381
382 uint32_t framesReady = cblk->framesReady();
383
384 if (framesReady == 0) {
385 Mutex::Autolock _l(cblk->lock);
386 goto start_loop_here;
387 while (framesReady == 0) {
388 active = mActive;
389 if (UNLIKELY(!active))
390 return NO_MORE_BUFFERS;
391 if (UNLIKELY(!waitCount))
392 return WOULD_BLOCK;
393 timeout = 0;
Eric Laurentef028272009-04-21 07:56:33 -0700394 result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 if (__builtin_expect(result!=NO_ERROR, false)) {
Eric Laurentef028272009-04-21 07:56:33 -0700396 cblk->waitTimeMs += waitTimeMs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
398 LOGW( "obtainBuffer timed out (is the CPU pegged?) "
399 "user=%08x, server=%08x", cblk->user, cblk->server);
400 timeout = 1;
401 cblk->waitTimeMs = 0;
402 }
403 if (--waitCount == 0) {
404 return TIMED_OUT;
405 }
406 }
407 // read the server count again
408 start_loop_here:
409 framesReady = cblk->framesReady();
410 }
411 }
412
413 LOGW_IF(timeout,
414 "*** SERIOUS WARNING *** obtainBuffer() timed out "
415 "but didn't need to be locked. We recovered, but "
416 "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server);
417
418 cblk->waitTimeMs = 0;
419
420 if (framesReq > framesReady) {
421 framesReq = framesReady;
422 }
423
424 uint32_t u = cblk->user;
425 uint32_t bufferEnd = cblk->userBase + cblk->frameCount;
426
427 if (u + framesReq > bufferEnd) {
428 framesReq = bufferEnd - u;
429 }
430
431 audioBuffer->flags = 0;
432 audioBuffer->channelCount= mChannelCount;
433 audioBuffer->format = mFormat;
434 audioBuffer->frameCount = framesReq;
435 audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t);
436 audioBuffer->raw = (int8_t*)cblk->buffer(u);
437 active = mActive;
438 return active ? status_t(NO_ERROR) : status_t(STOPPED);
439}
440
441void AudioRecord::releaseBuffer(Buffer* audioBuffer)
442{
443 audio_track_cblk_t* cblk = mCblk;
444 cblk->stepUser(audioBuffer->frameCount);
445}
446
447// -------------------------------------------------------------------------
448
449ssize_t AudioRecord::read(void* buffer, size_t userSize)
450{
451 ssize_t read = 0;
452 Buffer audioBuffer;
453 int8_t *dst = static_cast<int8_t*>(buffer);
454
455 if (ssize_t(userSize) < 0) {
456 // sanity-check. user is most-likely passing an error code.
457 LOGE("AudioRecord::read(buffer=%p, size=%u (%d)",
458 buffer, userSize, userSize);
459 return BAD_VALUE;
460 }
461
462 LOGV("read size: %d", userSize);
463
464 do {
465
466 audioBuffer.frameCount = userSize/mChannelCount/sizeof(int16_t);
467
468 // Calling obtainBuffer() with a negative wait count causes
469 // an (almost) infinite wait time.
470 status_t err = obtainBuffer(&audioBuffer, -1);
471 if (err < 0) {
472 // out of buffers, return #bytes written
473 if (err == status_t(NO_MORE_BUFFERS))
474 break;
475 return ssize_t(err);
476 }
477
478 size_t bytesRead = audioBuffer.size;
479 memcpy(dst, audioBuffer.i8, bytesRead);
480
481 dst += bytesRead;
482 userSize -= bytesRead;
483 read += bytesRead;
484
485 releaseBuffer(&audioBuffer);
486 } while (userSize);
487
488 return read;
489}
490
491// -------------------------------------------------------------------------
492
493bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
494{
495 Buffer audioBuffer;
496 uint32_t frames = mRemainingFrames;
497 size_t readSize;
498
499 // Manage marker callback
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700500 if (!mMarkerReached && (mMarkerPosition > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 if (mCblk->user >= mMarkerPosition) {
502 mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700503 mMarkerReached = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 }
505 }
506
507 // Manage new position callback
508 if (mUpdatePeriod > 0) {
509 while (mCblk->user >= mNewPosition) {
510 mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
511 mNewPosition += mUpdatePeriod;
512 }
513 }
514
515 do {
516 audioBuffer.frameCount = frames;
517 // Calling obtainBuffer() with a wait count of 1
518 // limits wait time to WAIT_PERIOD_MS. This prevents from being
519 // stuck here not being able to handle timed events (position, markers).
520 status_t err = obtainBuffer(&audioBuffer, 1);
521 if (err < NO_ERROR) {
522 if (err != TIMED_OUT) {
Eric Laurentef028272009-04-21 07:56:33 -0700523 LOGE_IF(err != status_t(NO_MORE_BUFFERS), "Error obtaining an audio buffer, giving up.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 return false;
525 }
526 break;
527 }
528 if (err == status_t(STOPPED)) return false;
529
530 size_t reqSize = audioBuffer.size;
531 mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
532 readSize = audioBuffer.size;
533
534 // Sanity check on returned size
Eric Laurent272beb62009-03-24 21:23:54 -0700535 if (ssize_t(readSize) <= 0) {
536 // The callback is done filling buffers
537 // Keep this thread going to handle timed events and
538 // still try to get more data in intervals of WAIT_PERIOD_MS
539 // but don't just loop and block the CPU, so wait
540 usleep(WAIT_PERIOD_MS*1000);
541 break;
542 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 if (readSize > reqSize) readSize = reqSize;
544
545 audioBuffer.size = readSize;
546 audioBuffer.frameCount = readSize/mChannelCount/sizeof(int16_t);
547 frames -= audioBuffer.frameCount;
548
549 releaseBuffer(&audioBuffer);
550
551 } while (frames);
552
553
554 // Manage overrun callback
555 if (mActive && (mCblk->framesAvailable_l() == 0)) {
556 LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag);
557 if (mCblk->flowControlFlag == 0) {
558 mCbf(EVENT_OVERRUN, mUserData, 0);
559 mCblk->flowControlFlag = 1;
560 }
561 }
562
563 if (frames == 0) {
564 mRemainingFrames = mNotificationFrames;
565 } else {
566 mRemainingFrames = frames;
567 }
568 return true;
569}
570
571// =========================================================================
572
573AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava)
574 : Thread(bCanCallJava), mReceiver(receiver)
575{
576}
577
578bool AudioRecord::ClientRecordThread::threadLoop()
579{
580 return mReceiver.processAudioBuffer(this);
581}
582
583// -------------------------------------------------------------------------
584
585}; // namespace android
586