|
|
|
@ -69,7 +69,8 @@ AudioRecord::AudioRecord(const String16 &opPackageName)
|
|
|
|
|
: mActive(false), mStatus(NO_INIT), mOpPackageName(opPackageName),
|
|
|
|
|
mSessionId(AUDIO_SESSION_ALLOCATE),
|
|
|
|
|
mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
|
|
|
|
|
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE)
|
|
|
|
|
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
|
|
|
|
|
mPortId(AUDIO_PORT_HANDLE_NONE)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -96,9 +97,10 @@ AudioRecord::AudioRecord(
|
|
|
|
|
mSessionId(AUDIO_SESSION_ALLOCATE),
|
|
|
|
|
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
|
|
|
|
|
mPreviousSchedulingGroup(SP_DEFAULT),
|
|
|
|
|
mProxy(NULL)
|
|
|
|
|
mProxy(NULL),
|
|
|
|
|
mPortId(AUDIO_PORT_HANDLE_NONE)
|
|
|
|
|
{
|
|
|
|
|
(void)set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
|
|
|
|
|
mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
|
|
|
|
|
notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
|
|
|
|
|
uid, pid, pAttributes, selectedDeviceId);
|
|
|
|
|
}
|
|
|
|
@ -149,11 +151,6 @@ status_t AudioRecord::set(
|
|
|
|
|
const audio_attributes_t* pAttributes,
|
|
|
|
|
audio_port_handle_t selectedDeviceId)
|
|
|
|
|
{
|
|
|
|
|
status_t status = NO_ERROR;
|
|
|
|
|
uint32_t channelCount;
|
|
|
|
|
pid_t callingPid;
|
|
|
|
|
pid_t myPid;
|
|
|
|
|
|
|
|
|
|
ALOGV("set(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
|
|
|
|
|
"notificationFrames %u, sessionId %d, transferType %d, flags %#x, opPackageName %s "
|
|
|
|
|
"uid %d, pid %d",
|
|
|
|
@ -173,8 +170,7 @@ status_t AudioRecord::set(
|
|
|
|
|
case TRANSFER_CALLBACK:
|
|
|
|
|
if (cbf == NULL) {
|
|
|
|
|
ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL");
|
|
|
|
|
status = BAD_VALUE;
|
|
|
|
|
goto exit;
|
|
|
|
|
return BAD_VALUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TRANSFER_OBTAIN:
|
|
|
|
@ -182,16 +178,14 @@ status_t AudioRecord::set(
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ALOGE("Invalid transfer type %d", transferType);
|
|
|
|
|
status = BAD_VALUE;
|
|
|
|
|
goto exit;
|
|
|
|
|
return BAD_VALUE;
|
|
|
|
|
}
|
|
|
|
|
mTransfer = transferType;
|
|
|
|
|
|
|
|
|
|
// invariant that mAudioRecord != 0 is true only after set() returns successfully
|
|
|
|
|
if (mAudioRecord != 0) {
|
|
|
|
|
ALOGE("Track already in use");
|
|
|
|
|
status = INVALID_OPERATION;
|
|
|
|
|
goto exit;
|
|
|
|
|
return INVALID_OPERATION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pAttributes == NULL) {
|
|
|
|
@ -215,18 +209,16 @@ status_t AudioRecord::set(
|
|
|
|
|
// AudioFlinger capture only supports linear PCM
|
|
|
|
|
if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
|
|
|
|
|
ALOGE("Format %#x is not linear pcm", format);
|
|
|
|
|
status = BAD_VALUE;
|
|
|
|
|
goto exit;
|
|
|
|
|
return BAD_VALUE;
|
|
|
|
|
}
|
|
|
|
|
mFormat = format;
|
|
|
|
|
|
|
|
|
|
if (!audio_is_input_channel(channelMask)) {
|
|
|
|
|
ALOGE("Invalid channel mask %#x", channelMask);
|
|
|
|
|
status = BAD_VALUE;
|
|
|
|
|
goto exit;
|
|
|
|
|
return BAD_VALUE;
|
|
|
|
|
}
|
|
|
|
|
mChannelMask = channelMask;
|
|
|
|
|
channelCount = audio_channel_count_from_in_mask(channelMask);
|
|
|
|
|
uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
|
|
|
|
|
mChannelCount = channelCount;
|
|
|
|
|
|
|
|
|
|
if (audio_is_linear_pcm(format)) {
|
|
|
|
@ -235,24 +227,28 @@ status_t AudioRecord::set(
|
|
|
|
|
mFrameSize = sizeof(uint8_t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mFrameCount is initialized in createRecord_l
|
|
|
|
|
// mFrameCount is initialized in openRecord_l
|
|
|
|
|
mReqFrameCount = frameCount;
|
|
|
|
|
|
|
|
|
|
mNotificationFramesReq = notificationFrames;
|
|
|
|
|
// mNotificationFramesAct is initialized in createRecord_l
|
|
|
|
|
// mNotificationFramesAct is initialized in openRecord_l
|
|
|
|
|
|
|
|
|
|
mSessionId = sessionId;
|
|
|
|
|
if (sessionId == AUDIO_SESSION_ALLOCATE) {
|
|
|
|
|
mSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
|
|
|
|
|
} else {
|
|
|
|
|
mSessionId = sessionId;
|
|
|
|
|
}
|
|
|
|
|
ALOGV("set(): mSessionId %d", mSessionId);
|
|
|
|
|
|
|
|
|
|
callingPid = IPCThreadState::self()->getCallingPid();
|
|
|
|
|
myPid = getpid();
|
|
|
|
|
if (uid == AUDIO_UID_INVALID || (callingPid != myPid)) {
|
|
|
|
|
int callingpid = IPCThreadState::self()->getCallingPid();
|
|
|
|
|
int mypid = getpid();
|
|
|
|
|
if (uid == AUDIO_UID_INVALID || (callingpid != mypid)) {
|
|
|
|
|
mClientUid = IPCThreadState::self()->getCallingUid();
|
|
|
|
|
} else {
|
|
|
|
|
mClientUid = uid;
|
|
|
|
|
}
|
|
|
|
|
if (pid == -1 || (callingPid != myPid)) {
|
|
|
|
|
mClientPid = callingPid;
|
|
|
|
|
if (pid == -1 || (callingpid != mypid)) {
|
|
|
|
|
mClientPid = callingpid;
|
|
|
|
|
} else {
|
|
|
|
|
mClientPid = pid;
|
|
|
|
|
}
|
|
|
|
@ -267,7 +263,7 @@ status_t AudioRecord::set(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create the IAudioRecord
|
|
|
|
|
status = createRecord_l(0 /*epoch*/, mOpPackageName);
|
|
|
|
|
status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);
|
|
|
|
|
|
|
|
|
|
if (status != NO_ERROR) {
|
|
|
|
|
if (mAudioRecordThread != 0) {
|
|
|
|
@ -275,9 +271,10 @@ status_t AudioRecord::set(
|
|
|
|
|
mAudioRecordThread->requestExitAndWait();
|
|
|
|
|
mAudioRecordThread.clear();
|
|
|
|
|
}
|
|
|
|
|
goto exit;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mStatus = NO_ERROR;
|
|
|
|
|
mUserData = user;
|
|
|
|
|
// TODO: add audio hardware input latency here
|
|
|
|
|
mLatency = (1000LL * mFrameCount) / mSampleRate;
|
|
|
|
@ -292,9 +289,7 @@ status_t AudioRecord::set(
|
|
|
|
|
mFramesRead = 0;
|
|
|
|
|
mFramesReadServerOffset = 0;
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
mStatus = status;
|
|
|
|
|
return status;
|
|
|
|
|
return NO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ -545,29 +540,70 @@ const char * AudioRecord::convertTransferToText(transfer_type transferType) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// must be called with mLock held
|
|
|
|
|
status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
|
|
|
|
|
status_t AudioRecord::openRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
|
|
|
|
|
{
|
|
|
|
|
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
|
|
|
|
|
IAudioFlinger::CreateRecordInput input;
|
|
|
|
|
IAudioFlinger::CreateRecordOutput output;
|
|
|
|
|
audio_session_t originalSessionId;
|
|
|
|
|
sp<media::IAudioRecord> record;
|
|
|
|
|
void *iMemPointer;
|
|
|
|
|
audio_track_cblk_t* cblk;
|
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
|
|
if (audioFlinger == 0) {
|
|
|
|
|
ALOGE("Could not get audioflinger");
|
|
|
|
|
status = NO_INIT;
|
|
|
|
|
goto exit;
|
|
|
|
|
return NO_INIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
audio_io_handle_t input;
|
|
|
|
|
|
|
|
|
|
// mFlags (not mOrigFlags) is modified depending on whether fast request is accepted.
|
|
|
|
|
// After fast request is denied, we will request again if IAudioRecord is re-created.
|
|
|
|
|
|
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
|
|
// Not a conventional loop, but a retry loop for at most two iterations total.
|
|
|
|
|
// Try first maybe with FAST flag then try again without FAST flag if that fails.
|
|
|
|
|
// Exits loop normally via a return at the bottom, or with error via a break.
|
|
|
|
|
// The sp<> references will be dropped when re-entering scope.
|
|
|
|
|
// The lack of indentation is deliberate, to reduce code churn and ease merges.
|
|
|
|
|
for (;;) {
|
|
|
|
|
audio_config_base_t config = {
|
|
|
|
|
.sample_rate = mSampleRate,
|
|
|
|
|
.channel_mask = mChannelMask,
|
|
|
|
|
.format = mFormat
|
|
|
|
|
};
|
|
|
|
|
mRoutedDeviceId = mSelectedDeviceId;
|
|
|
|
|
status = AudioSystem::getInputForAttr(&mAttributes, &input,
|
|
|
|
|
mSessionId,
|
|
|
|
|
// FIXME compare to AudioTrack
|
|
|
|
|
mClientPid,
|
|
|
|
|
mClientUid,
|
|
|
|
|
&config,
|
|
|
|
|
mFlags, &mRoutedDeviceId, &mPortId);
|
|
|
|
|
|
|
|
|
|
if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE) {
|
|
|
|
|
ALOGE("Could not get audio input for session %d, record source %d, sample rate %u, "
|
|
|
|
|
"format %#x, channel mask %#x, flags %#x",
|
|
|
|
|
mSessionId, mAttributes.source, mSampleRate, mFormat, mChannelMask, mFlags);
|
|
|
|
|
return BAD_VALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now that we have a reference to an I/O handle and have not yet handed it off to AudioFlinger,
|
|
|
|
|
// we must release it ourselves if anything goes wrong.
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
size_t afFrameCount;
|
|
|
|
|
status = AudioSystem::getFrameCount(input, &afFrameCount);
|
|
|
|
|
if (status != NO_ERROR) {
|
|
|
|
|
ALOGE("getFrameCount(input=%d) status %d", input, status);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
uint32_t afSampleRate;
|
|
|
|
|
status = AudioSystem::getSamplingRate(input, &afSampleRate);
|
|
|
|
|
if (status != NO_ERROR) {
|
|
|
|
|
ALOGE("getSamplingRate(input=%d) status %d", input, status);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (mSampleRate == 0) {
|
|
|
|
|
mSampleRate = afSampleRate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Client can only express a preference for FAST. Server will perform additional tests.
|
|
|
|
|
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
|
|
|
|
|
bool useCaseAllowed =
|
|
|
|
@ -586,41 +622,66 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String
|
|
|
|
|
if (!useCaseAllowed) {
|
|
|
|
|
ALOGW("AUDIO_INPUT_FLAG_FAST denied, incompatible transfer = %s",
|
|
|
|
|
convertTransferToText(mTransfer));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sample rates must also match
|
|
|
|
|
bool sampleRateAllowed = mSampleRate == afSampleRate;
|
|
|
|
|
if (!sampleRateAllowed) {
|
|
|
|
|
ALOGW("AUDIO_INPUT_FLAG_FAST denied, rates do not match %u Hz, require %u Hz",
|
|
|
|
|
mSampleRate, afSampleRate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool fastAllowed = useCaseAllowed && sampleRateAllowed;
|
|
|
|
|
if (!fastAllowed) {
|
|
|
|
|
mFlags = (audio_input_flags_t) (mFlags & ~(AUDIO_INPUT_FLAG_FAST |
|
|
|
|
|
AUDIO_INPUT_FLAG_RAW));
|
|
|
|
|
AudioSystem::releaseInput(input, mSessionId);
|
|
|
|
|
continue; // retry
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input.attr = mAttributes;
|
|
|
|
|
input.config.sample_rate = mSampleRate;
|
|
|
|
|
input.config.channel_mask = mChannelMask;
|
|
|
|
|
input.config.format = mFormat;
|
|
|
|
|
input.clientInfo.clientUid = mClientUid;
|
|
|
|
|
input.clientInfo.clientPid = mClientPid;
|
|
|
|
|
input.clientInfo.clientTid = -1;
|
|
|
|
|
// The notification frame count is the period between callbacks, as suggested by the client
|
|
|
|
|
// but moderated by the server. For record, the calculations are done entirely on server side.
|
|
|
|
|
size_t notificationFrames = mNotificationFramesReq;
|
|
|
|
|
size_t frameCount = mReqFrameCount;
|
|
|
|
|
|
|
|
|
|
audio_input_flags_t flags = mFlags;
|
|
|
|
|
|
|
|
|
|
pid_t tid = -1;
|
|
|
|
|
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
|
|
|
|
|
if (mAudioRecordThread != 0) {
|
|
|
|
|
input.clientInfo.clientTid = mAudioRecordThread->getTid();
|
|
|
|
|
tid = mAudioRecordThread->getTid();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
input.opPackageName = opPackageName;
|
|
|
|
|
|
|
|
|
|
input.flags = mFlags;
|
|
|
|
|
// The notification frame count is the period between callbacks, as suggested by the client
|
|
|
|
|
// but moderated by the server. For record, the calculations are done entirely on server side.
|
|
|
|
|
input.frameCount = mReqFrameCount;
|
|
|
|
|
input.notificationFrameCount = mNotificationFramesReq;
|
|
|
|
|
input.selectedDeviceId = mSelectedDeviceId;
|
|
|
|
|
input.sessionId = mSessionId;
|
|
|
|
|
originalSessionId = mSessionId;
|
|
|
|
|
|
|
|
|
|
record = audioFlinger->createRecord(input,
|
|
|
|
|
output,
|
|
|
|
|
&status);
|
|
|
|
|
size_t temp = frameCount; // temp may be replaced by a revised value of frameCount,
|
|
|
|
|
// but we will still need the original value also
|
|
|
|
|
audio_session_t originalSessionId = mSessionId;
|
|
|
|
|
|
|
|
|
|
sp<IMemory> iMem; // for cblk
|
|
|
|
|
sp<IMemory> bufferMem;
|
|
|
|
|
sp<media::IAudioRecord> record = audioFlinger->openRecord(input,
|
|
|
|
|
mSampleRate,
|
|
|
|
|
mFormat,
|
|
|
|
|
mChannelMask,
|
|
|
|
|
opPackageName,
|
|
|
|
|
&temp,
|
|
|
|
|
&flags,
|
|
|
|
|
mClientPid,
|
|
|
|
|
tid,
|
|
|
|
|
mClientUid,
|
|
|
|
|
&mSessionId,
|
|
|
|
|
¬ificationFrames,
|
|
|
|
|
iMem,
|
|
|
|
|
bufferMem,
|
|
|
|
|
&status,
|
|
|
|
|
mPortId);
|
|
|
|
|
ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId,
|
|
|
|
|
"session ID changed from %d to %d", originalSessionId, mSessionId);
|
|
|
|
|
|
|
|
|
|
if (status != NO_ERROR) {
|
|
|
|
|
ALOGE("AudioFlinger could not create record track, status: %d", status);
|
|
|
|
|
goto exit;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ALOG_ASSERT(record != 0);
|
|
|
|
|
|
|
|
|
@ -628,41 +689,41 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String
|
|
|
|
|
// so we are no longer responsible for releasing it.
|
|
|
|
|
|
|
|
|
|
mAwaitBoost = false;
|
|
|
|
|
if (output.flags & AUDIO_INPUT_FLAG_FAST) {
|
|
|
|
|
ALOGI("AUDIO_INPUT_FLAG_FAST successful; frameCount %zu -> %zu",
|
|
|
|
|
mReqFrameCount, output.frameCount);
|
|
|
|
|
mAwaitBoost = true;
|
|
|
|
|
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
|
|
|
|
|
if (flags & AUDIO_INPUT_FLAG_FAST) {
|
|
|
|
|
ALOGI("AUDIO_INPUT_FLAG_FAST successful; frameCount %zu -> %zu", frameCount, temp);
|
|
|
|
|
mAwaitBoost = true;
|
|
|
|
|
} else {
|
|
|
|
|
ALOGW("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %zu -> %zu", frameCount, temp);
|
|
|
|
|
mFlags = (audio_input_flags_t) (mFlags & ~(AUDIO_INPUT_FLAG_FAST |
|
|
|
|
|
AUDIO_INPUT_FLAG_RAW));
|
|
|
|
|
continue; // retry
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mFlags = output.flags;
|
|
|
|
|
mRoutedDeviceId = output.selectedDeviceId;
|
|
|
|
|
mSessionId = output.sessionId;
|
|
|
|
|
mSampleRate = output.sampleRate;
|
|
|
|
|
mFlags = flags;
|
|
|
|
|
|
|
|
|
|
if (output.cblk == 0) {
|
|
|
|
|
if (iMem == 0) {
|
|
|
|
|
ALOGE("Could not get control block");
|
|
|
|
|
status = NO_INIT;
|
|
|
|
|
goto exit;
|
|
|
|
|
return NO_INIT;
|
|
|
|
|
}
|
|
|
|
|
iMemPointer = output.cblk ->pointer();
|
|
|
|
|
void *iMemPointer = iMem->pointer();
|
|
|
|
|
if (iMemPointer == NULL) {
|
|
|
|
|
ALOGE("Could not get control block pointer");
|
|
|
|
|
status = NO_INIT;
|
|
|
|
|
goto exit;
|
|
|
|
|
return NO_INIT;
|
|
|
|
|
}
|
|
|
|
|
cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
|
|
|
|
|
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
|
|
|
|
|
|
|
|
|
|
// Starting address of buffers in shared memory.
|
|
|
|
|
// The buffers are either immediately after the control block,
|
|
|
|
|
// or in a separate area at discretion of server.
|
|
|
|
|
void *buffers;
|
|
|
|
|
if (output.buffers == 0) {
|
|
|
|
|
if (bufferMem == 0) {
|
|
|
|
|
buffers = cblk + 1;
|
|
|
|
|
} else {
|
|
|
|
|
buffers = output.buffers->pointer();
|
|
|
|
|
buffers = bufferMem->pointer();
|
|
|
|
|
if (buffers == NULL) {
|
|
|
|
|
ALOGE("Could not get buffer pointer");
|
|
|
|
|
status = NO_INIT;
|
|
|
|
|
goto exit;
|
|
|
|
|
return NO_INIT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -672,42 +733,43 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String
|
|
|
|
|
mDeathNotifier.clear();
|
|
|
|
|
}
|
|
|
|
|
mAudioRecord = record;
|
|
|
|
|
mCblkMemory = output.cblk;
|
|
|
|
|
mBufferMemory = output.buffers;
|
|
|
|
|
mCblkMemory = iMem;
|
|
|
|
|
mBufferMemory = bufferMem;
|
|
|
|
|
IPCThreadState::self()->flushCommands();
|
|
|
|
|
|
|
|
|
|
mCblk = cblk;
|
|
|
|
|
// note that output.frameCount is the (possibly revised) value of mReqFrameCount
|
|
|
|
|
if (output.frameCount < mReqFrameCount || (mReqFrameCount == 0 && output.frameCount == 0)) {
|
|
|
|
|
ALOGW("Requested frameCount %zu but received frameCount %zu",
|
|
|
|
|
mReqFrameCount, output.frameCount);
|
|
|
|
|
// note that temp is the (possibly revised) value of frameCount
|
|
|
|
|
if (temp < frameCount || (frameCount == 0 && temp == 0)) {
|
|
|
|
|
ALOGW("Requested frameCount %zu but received frameCount %zu", frameCount, temp);
|
|
|
|
|
}
|
|
|
|
|
frameCount = temp;
|
|
|
|
|
|
|
|
|
|
// Make sure that application is notified with sufficient margin before overrun.
|
|
|
|
|
// The computation is done on server side.
|
|
|
|
|
if (mNotificationFramesReq > 0 && output.notificationFrameCount != mNotificationFramesReq) {
|
|
|
|
|
if (mNotificationFramesReq > 0 && notificationFrames != mNotificationFramesReq) {
|
|
|
|
|
ALOGW("Server adjusted notificationFrames from %u to %zu for frameCount %zu",
|
|
|
|
|
mNotificationFramesReq, output.notificationFrameCount, output.frameCount);
|
|
|
|
|
mNotificationFramesReq, notificationFrames, frameCount);
|
|
|
|
|
}
|
|
|
|
|
mNotificationFramesAct = (uint32_t)output.notificationFrameCount;
|
|
|
|
|
mNotificationFramesAct = (uint32_t) notificationFrames;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//mInput != input includes the case where mInput == AUDIO_IO_HANDLE_NONE for first creation
|
|
|
|
|
if (mDeviceCallback != 0 && mInput != output.inputId) {
|
|
|
|
|
if (mDeviceCallback != 0 && mInput != input) {
|
|
|
|
|
if (mInput != AUDIO_IO_HANDLE_NONE) {
|
|
|
|
|
AudioSystem::removeAudioDeviceCallback(this, mInput);
|
|
|
|
|
}
|
|
|
|
|
AudioSystem::addAudioDeviceCallback(this, output.inputId);
|
|
|
|
|
AudioSystem::addAudioDeviceCallback(this, input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We retain a copy of the I/O handle, but don't own the reference
|
|
|
|
|
mInput = output.inputId;
|
|
|
|
|
mInput = input;
|
|
|
|
|
mRefreshRemaining = true;
|
|
|
|
|
|
|
|
|
|
mFrameCount = output.frameCount;
|
|
|
|
|
mFrameCount = frameCount;
|
|
|
|
|
// If IAudioRecord is re-created, don't let the requested frameCount
|
|
|
|
|
// decrease. This can confuse clients that cache frameCount().
|
|
|
|
|
if (mFrameCount > mReqFrameCount) {
|
|
|
|
|
mReqFrameCount = mFrameCount;
|
|
|
|
|
if (frameCount > mReqFrameCount) {
|
|
|
|
|
mReqFrameCount = frameCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update proxy
|
|
|
|
@ -718,9 +780,17 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String
|
|
|
|
|
mDeathNotifier = new DeathNotifier(this);
|
|
|
|
|
IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
mStatus = status;
|
|
|
|
|
// sp<IAudioTrack> track destructor will cause releaseOutput() to be called by AudioFlinger
|
|
|
|
|
return NO_ERROR;
|
|
|
|
|
|
|
|
|
|
// End of retry loop.
|
|
|
|
|
// The lack of indentation is deliberate, to reduce code churn and ease merges.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Arrive here on error, via a break
|
|
|
|
|
AudioSystem::releaseInput(input, mSessionId);
|
|
|
|
|
if (status == NO_ERROR) {
|
|
|
|
|
status = NO_INIT;
|
|
|
|
|
}
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1152,12 +1222,12 @@ status_t AudioRecord::restoreRecord_l(const char *from)
|
|
|
|
|
|
|
|
|
|
mFlags = mOrigFlags;
|
|
|
|
|
|
|
|
|
|
// if the new IAudioRecord is created, createRecord_l() will modify the
|
|
|
|
|
// if the new IAudioRecord is created, openRecord_l() will modify the
|
|
|
|
|
// following member variables: mAudioRecord, mCblkMemory, mCblk, mBufferMemory.
|
|
|
|
|
// It will also delete the strong references on previous IAudioRecord and IMemory
|
|
|
|
|
Modulo<uint32_t> position(mProxy->getPosition());
|
|
|
|
|
mNewPosition = position + mUpdatePeriod;
|
|
|
|
|
status_t result = createRecord_l(position, mOpPackageName);
|
|
|
|
|
status_t result = openRecord_l(position, mOpPackageName);
|
|
|
|
|
if (result == NO_ERROR) {
|
|
|
|
|
if (mActive) {
|
|
|
|
|
// callback thread or sync event hasn't changed
|
|
|
|
|