blob: 4c8b02a7bec33a61872d635e4730f04d00d66b74 [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
32#include <utils/IServiceManager.h>
33#include <utils/Log.h>
34#include <utils/MemoryDealer.h>
35#include <utils/Parcel.h>
36#include <utils/IPCThreadState.h>
37#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(
Eric Laurent4bc035a2009-05-22 09:18:15 -070053 int inputSource,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 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{
Eric Laurent4bc035a2009-05-22 09:18:15 -070064 mStatus = set(inputSource, sampleRate, format, channelCount,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 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(
Eric Laurent4bc035a2009-05-22 09:18:15 -070085 int inputSource,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 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
Eric Laurent4bc035a2009-05-22 09:18:15 -0700107 if (inputSource == DEFAULT_INPUT) {
108 inputSource = MIC_INPUT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 }
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;
Eric Laurent4bc035a2009-05-22 09:18:15 -0700160 sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), inputSource,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 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;
Eric Laurent4bc035a2009-05-22 09:18:15 -0700204 mInputSource = (uint8_t)inputSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205
206 return NO_ERROR;
207}
208
209status_t AudioRecord::initCheck() const
210{
211 return mStatus;
212}
213
214// -------------------------------------------------------------------------
215
216uint32_t AudioRecord::latency() const
217{
218 return mLatency;
219}
220
221uint32_t AudioRecord::sampleRate() const
222{
223 return mSampleRate;
224}
225
226int AudioRecord::format() const
227{
228 return mFormat;
229}
230
231int AudioRecord::channelCount() const
232{
233 return mChannelCount;
234}
235
236uint32_t AudioRecord::frameCount() const
237{
238 return mFrameCount;
239}
240
241int AudioRecord::frameSize() const
242{
243 return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
244}
245
Eric Laurent4bc035a2009-05-22 09:18:15 -0700246int AudioRecord::inputSource() const
247{
248 return (int)mInputSource;
249}
250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251// -------------------------------------------------------------------------
252
253status_t AudioRecord::start()
254{
255 status_t ret = NO_ERROR;
256 sp<ClientRecordThread> t = mClientRecordThread;
257
258 LOGV("start");
259
260 if (t != 0) {
261 if (t->exitPending()) {
262 if (t->requestExitAndWait() == WOULD_BLOCK) {
263 LOGE("AudioRecord::start called from thread");
264 return WOULD_BLOCK;
265 }
266 }
267 t->mLock.lock();
268 }
269
270 if (android_atomic_or(1, &mActive) == 0) {
271 mNewPosition = mCblk->user + mUpdatePeriod;
272 mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
273 mCblk->waitTimeMs = 0;
274 if (t != 0) {
275 t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
276 } else {
277 setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
278 }
279 ret = mAudioRecord->start();
280 }
281
282 if (t != 0) {
283 t->mLock.unlock();
284 }
285
286 return ret;
287}
288
289status_t AudioRecord::stop()
290{
291 sp<ClientRecordThread> t = mClientRecordThread;
292
293 LOGV("stop");
294
295 if (t != 0) {
296 t->mLock.lock();
297 }
298
299 if (android_atomic_and(~1, &mActive) == 1) {
Eric Laurentef028272009-04-21 07:56:33 -0700300 mCblk->cv.signal();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 mAudioRecord->stop();
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700302 // the record head position will reset to 0, so if a marker is set, we need
303 // to activate it again
304 mMarkerReached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 if (t != 0) {
306 t->requestExit();
307 } else {
308 setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL);
309 }
310 }
311
312 if (t != 0) {
313 t->mLock.unlock();
314 }
315
316 return NO_ERROR;
317}
318
319bool AudioRecord::stopped() const
320{
321 return !mActive;
322}
323
324status_t AudioRecord::setMarkerPosition(uint32_t marker)
325{
326 if (mCbf == 0) return INVALID_OPERATION;
327
328 mMarkerPosition = marker;
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700329 mMarkerReached = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330
331 return NO_ERROR;
332}
333
334status_t AudioRecord::getMarkerPosition(uint32_t *marker)
335{
336 if (marker == 0) return BAD_VALUE;
337
338 *marker = mMarkerPosition;
339
340 return NO_ERROR;
341}
342
343status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
344{
345 if (mCbf == 0) return INVALID_OPERATION;
346
347 uint32_t curPosition;
348 getPosition(&curPosition);
349 mNewPosition = curPosition + updatePeriod;
350 mUpdatePeriod = updatePeriod;
351
352 return NO_ERROR;
353}
354
355status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod)
356{
357 if (updatePeriod == 0) return BAD_VALUE;
358
359 *updatePeriod = mUpdatePeriod;
360
361 return NO_ERROR;
362}
363
364status_t AudioRecord::getPosition(uint32_t *position)
365{
366 if (position == 0) return BAD_VALUE;
367
368 *position = mCblk->user;
369
370 return NO_ERROR;
371}
372
373
374// -------------------------------------------------------------------------
375
376status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
377{
378 int active;
379 int timeout = 0;
380 status_t result;
381 audio_track_cblk_t* cblk = mCblk;
382 uint32_t framesReq = audioBuffer->frameCount;
Eric Laurentef028272009-04-21 07:56:33 -0700383 uint32_t waitTimeMs = (waitCount < 0) ? cblk->bufferTimeoutMs : WAIT_PERIOD_MS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384
385 audioBuffer->frameCount = 0;
386 audioBuffer->size = 0;
387
388 uint32_t framesReady = cblk->framesReady();
389
390 if (framesReady == 0) {
391 Mutex::Autolock _l(cblk->lock);
392 goto start_loop_here;
393 while (framesReady == 0) {
394 active = mActive;
395 if (UNLIKELY(!active))
396 return NO_MORE_BUFFERS;
397 if (UNLIKELY(!waitCount))
398 return WOULD_BLOCK;
399 timeout = 0;
Eric Laurentef028272009-04-21 07:56:33 -0700400 result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 if (__builtin_expect(result!=NO_ERROR, false)) {
Eric Laurentef028272009-04-21 07:56:33 -0700402 cblk->waitTimeMs += waitTimeMs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
404 LOGW( "obtainBuffer timed out (is the CPU pegged?) "
405 "user=%08x, server=%08x", cblk->user, cblk->server);
406 timeout = 1;
407 cblk->waitTimeMs = 0;
408 }
409 if (--waitCount == 0) {
410 return TIMED_OUT;
411 }
412 }
413 // read the server count again
414 start_loop_here:
415 framesReady = cblk->framesReady();
416 }
417 }
418
419 LOGW_IF(timeout,
420 "*** SERIOUS WARNING *** obtainBuffer() timed out "
421 "but didn't need to be locked. We recovered, but "
422 "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server);
423
424 cblk->waitTimeMs = 0;
425
426 if (framesReq > framesReady) {
427 framesReq = framesReady;
428 }
429
430 uint32_t u = cblk->user;
431 uint32_t bufferEnd = cblk->userBase + cblk->frameCount;
432
433 if (u + framesReq > bufferEnd) {
434 framesReq = bufferEnd - u;
435 }
436
437 audioBuffer->flags = 0;
438 audioBuffer->channelCount= mChannelCount;
439 audioBuffer->format = mFormat;
440 audioBuffer->frameCount = framesReq;
441 audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t);
442 audioBuffer->raw = (int8_t*)cblk->buffer(u);
443 active = mActive;
444 return active ? status_t(NO_ERROR) : status_t(STOPPED);
445}
446
447void AudioRecord::releaseBuffer(Buffer* audioBuffer)
448{
449 audio_track_cblk_t* cblk = mCblk;
450 cblk->stepUser(audioBuffer->frameCount);
451}
452
453// -------------------------------------------------------------------------
454
455ssize_t AudioRecord::read(void* buffer, size_t userSize)
456{
457 ssize_t read = 0;
458 Buffer audioBuffer;
459 int8_t *dst = static_cast<int8_t*>(buffer);
460
461 if (ssize_t(userSize) < 0) {
462 // sanity-check. user is most-likely passing an error code.
463 LOGE("AudioRecord::read(buffer=%p, size=%u (%d)",
464 buffer, userSize, userSize);
465 return BAD_VALUE;
466 }
467
468 LOGV("read size: %d", userSize);
469
470 do {
471
472 audioBuffer.frameCount = userSize/mChannelCount/sizeof(int16_t);
473
474 // Calling obtainBuffer() with a negative wait count causes
475 // an (almost) infinite wait time.
476 status_t err = obtainBuffer(&audioBuffer, -1);
477 if (err < 0) {
478 // out of buffers, return #bytes written
479 if (err == status_t(NO_MORE_BUFFERS))
480 break;
481 return ssize_t(err);
482 }
483
484 size_t bytesRead = audioBuffer.size;
485 memcpy(dst, audioBuffer.i8, bytesRead);
486
487 dst += bytesRead;
488 userSize -= bytesRead;
489 read += bytesRead;
490
491 releaseBuffer(&audioBuffer);
492 } while (userSize);
493
494 return read;
495}
496
497// -------------------------------------------------------------------------
498
499bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
500{
501 Buffer audioBuffer;
502 uint32_t frames = mRemainingFrames;
503 size_t readSize;
504
505 // Manage marker callback
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700506 if (!mMarkerReached && (mMarkerPosition > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 if (mCblk->user >= mMarkerPosition) {
508 mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
Jean-Michel Trivi78c13142009-03-24 19:48:58 -0700509 mMarkerReached = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 }
511 }
512
513 // Manage new position callback
514 if (mUpdatePeriod > 0) {
515 while (mCblk->user >= mNewPosition) {
516 mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
517 mNewPosition += mUpdatePeriod;
518 }
519 }
520
521 do {
522 audioBuffer.frameCount = frames;
523 // Calling obtainBuffer() with a wait count of 1
524 // limits wait time to WAIT_PERIOD_MS. This prevents from being
525 // stuck here not being able to handle timed events (position, markers).
526 status_t err = obtainBuffer(&audioBuffer, 1);
527 if (err < NO_ERROR) {
528 if (err != TIMED_OUT) {
Eric Laurentef028272009-04-21 07:56:33 -0700529 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 -0800530 return false;
531 }
532 break;
533 }
534 if (err == status_t(STOPPED)) return false;
535
536 size_t reqSize = audioBuffer.size;
537 mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
538 readSize = audioBuffer.size;
539
540 // Sanity check on returned size
Eric Laurent272beb62009-03-24 21:23:54 -0700541 if (ssize_t(readSize) <= 0) {
542 // The callback is done filling buffers
543 // Keep this thread going to handle timed events and
544 // still try to get more data in intervals of WAIT_PERIOD_MS
545 // but don't just loop and block the CPU, so wait
546 usleep(WAIT_PERIOD_MS*1000);
547 break;
548 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 if (readSize > reqSize) readSize = reqSize;
550
551 audioBuffer.size = readSize;
552 audioBuffer.frameCount = readSize/mChannelCount/sizeof(int16_t);
553 frames -= audioBuffer.frameCount;
554
555 releaseBuffer(&audioBuffer);
556
557 } while (frames);
558
559
560 // Manage overrun callback
561 if (mActive && (mCblk->framesAvailable_l() == 0)) {
562 LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag);
563 if (mCblk->flowControlFlag == 0) {
564 mCbf(EVENT_OVERRUN, mUserData, 0);
565 mCblk->flowControlFlag = 1;
566 }
567 }
568
569 if (frames == 0) {
570 mRemainingFrames = mNotificationFrames;
571 } else {
572 mRemainingFrames = frames;
573 }
574 return true;
575}
576
577// =========================================================================
578
579AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava)
580 : Thread(bCanCallJava), mReceiver(receiver)
581{
582}
583
584bool AudioRecord::ClientRecordThread::threadLoop()
585{
586 return mReceiver.processAudioBuffer(this);
587}
588
589// -------------------------------------------------------------------------
590
591}; // namespace android
592