blob: 584dad650c53e1469258a90b56668190efc19149 [file] [log] [blame] [edit]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <inttypes.h>
#include <stdlib.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioSource"
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <media/AidlConversion.h>
#include <media/AudioRecord.h>
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <cutils/properties.h>
namespace android {
using content::AttributionSourceState;
void AudioSource::onOverrun() {
ALOGW("AudioRecord reported overrun!");
}
AudioSource::AudioSource(
const audio_attributes_t *attr, const AttributionSourceState& attributionSource,
uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
audio_port_handle_t selectedDeviceId,
audio_microphone_direction_t selectedMicDirection,
float selectedMicFieldDimension)
{
set(attr, attributionSource, sampleRate, channelCount, outSampleRate, selectedDeviceId,
selectedMicDirection, selectedMicFieldDimension);
}
AudioSource::AudioSource(
const audio_attributes_t *attr, const String16 &opPackageName,
uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
uid_t uid, pid_t pid, audio_port_handle_t selectedDeviceId,
audio_microphone_direction_t selectedMicDirection,
float selectedMicFieldDimension)
{
// TODO b/182392769: use attribution source util
AttributionSourceState attributionSource;
attributionSource.packageName = VALUE_OR_FATAL(legacy2aidl_String16_string(opPackageName));
attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(uid));
attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(pid));
attributionSource.token = sp<BBinder>::make();
set(attr, attributionSource, sampleRate, channelCount, outSampleRate, selectedDeviceId,
selectedMicDirection, selectedMicFieldDimension);
}
void AudioSource::set(
const audio_attributes_t *attr, const AttributionSourceState& attributionSource,
uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
audio_port_handle_t selectedDeviceId,
audio_microphone_direction_t selectedMicDirection,
float selectedMicFieldDimension)
{
mStarted = false;
mSampleRate = sampleRate;
mOutSampleRate = outSampleRate > 0 ? outSampleRate : sampleRate;
mTrackMaxAmplitude = false;
mStartTimeUs = 0;
mStopSystemTimeUs = -1;
mLastFrameTimestampUs = 0;
mMaxAmplitude = 0;
mPrevSampleTimeUs = 0;
mInitialReadTimeUs = 0;
mNumFramesReceived = 0;
mNumFramesSkipped = 0;
mNumFramesLost = 0;
mNumClientOwnedBuffers = 0;
mNoMoreFramesToRead = false;
ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u",
sampleRate, outSampleRate, channelCount);
CHECK(channelCount == 1 || channelCount == 2);
CHECK(sampleRate > 0);
size_t minFrameCount;
status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
sampleRate,
AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount));
if (status == OK) {
// make sure that the AudioRecord callback never returns more than the maximum
// buffer size
uint32_t frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
// make sure that the AudioRecord total buffer size is large enough
size_t bufCount = 2;
while ((bufCount * frameCount) < minFrameCount) {
bufCount++;
}
mRecord = new AudioRecord(
AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount),
attributionSource,
(size_t) (bufCount * frameCount),
wp<AudioRecord::IAudioRecordCallback>{this},
frameCount /*notificationFrames*/,
AUDIO_SESSION_ALLOCATE,
AudioRecord::TRANSFER_DEFAULT,
AUDIO_INPUT_FLAG_NONE,
attr,
selectedDeviceId,
selectedMicDirection,
selectedMicFieldDimension);
// Set caller name so it can be logged in destructor.
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
mRecord->setCallerName("media");
mInitCheck = mRecord->initCheck();
if (mInitCheck != OK) {
mRecord.clear();
}
} else {
mInitCheck = status;
}
}
AudioSource::~AudioSource() {
if (mStarted) {
reset();
}
}
status_t AudioSource::initCheck() const {
return mInitCheck;
}
status_t AudioSource::start(MetaData *params) {
Mutex::Autolock autoLock(mLock);
if (mStarted) {
return UNKNOWN_ERROR;
}
if (mInitCheck != OK) {
return NO_INIT;
}
mTrackMaxAmplitude = false;
mMaxAmplitude = 0;
mInitialReadTimeUs = 0;
mStartTimeUs = 0;
int64_t startTimeUs;
if (params && params->findInt64(kKeyTime, &startTimeUs)) {
mStartTimeUs = startTimeUs;
}
status_t err = mRecord->start();
if (err == OK) {
mStarted = true;
} else {
mRecord.clear();
}
return err;
}
void AudioSource::releaseQueuedFrames_l() {
ALOGV("releaseQueuedFrames_l");
List<MediaBuffer *>::iterator it;
while (!mBuffersReceived.empty()) {
it = mBuffersReceived.begin();
(*it)->release();
mBuffersReceived.erase(it);
}
}
void AudioSource::waitOutstandingEncodingFrames_l() {
ALOGV("waitOutstandingEncodingFrames_l: %" PRId64, mNumClientOwnedBuffers);
while (mNumClientOwnedBuffers > 0) {
mFrameEncodingCompletionCondition.wait(mLock);
}
}
status_t AudioSource::reset() {
Mutex::Autolock autoLock(mLock);
if (!mStarted) {
return UNKNOWN_ERROR;
}
if (mInitCheck != OK) {
return NO_INIT;
}
mStarted = false;
mStopSystemTimeUs = -1;
mNoMoreFramesToRead = false;
mFrameAvailableCondition.signal();
mRecord->stop();
waitOutstandingEncodingFrames_l();
releaseQueuedFrames_l();
return OK;
}
sp<MetaData> AudioSource::getFormat() {
Mutex::Autolock autoLock(mLock);
if (mInitCheck != OK) {
return 0;
}
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
meta->setInt32(kKeySampleRate, mSampleRate);
meta->setInt32(kKeyChannelCount, mRecord->channelCount());
meta->setInt32(kKeyMaxInputSize, kMaxBufferSize);
meta->setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit);
return meta;
}
void AudioSource::rampVolume(
int32_t startFrame, int32_t rampDurationFrames,
uint8_t *data, size_t bytes) {
const int32_t kShift = 14;
int32_t fixedMultiplier = (startFrame << kShift) / rampDurationFrames;
const int32_t nChannels = mRecord->channelCount();
int32_t stopFrame = startFrame + bytes / sizeof(int16_t);
int16_t *frame = (int16_t *) data;
if (stopFrame > rampDurationFrames) {
stopFrame = rampDurationFrames;
}
while (startFrame < stopFrame) {
if (nChannels == 1) { // mono
frame[0] = (frame[0] * fixedMultiplier) >> kShift;
++frame;
++startFrame;
} else { // stereo
frame[0] = (frame[0] * fixedMultiplier) >> kShift;
frame[1] = (frame[1] * fixedMultiplier) >> kShift;
frame += 2;
startFrame += 2;
}
// Update the multiplier every 4 frames
if ((startFrame & 3) == 0) {
fixedMultiplier = (startFrame << kShift) / rampDurationFrames;
}
}
}
status_t AudioSource::read(
MediaBufferBase **out, const ReadOptions * /* options */) {
Mutex::Autolock autoLock(mLock);
*out = NULL;
if (mInitCheck != OK) {
return NO_INIT;
}
while (mStarted && mBuffersReceived.empty()) {
mFrameAvailableCondition.wait(mLock);
if (mNoMoreFramesToRead) {
return OK;
}
}
if (!mStarted) {
return OK;
}
MediaBuffer *buffer = *mBuffersReceived.begin();
mBuffersReceived.erase(mBuffersReceived.begin());
++mNumClientOwnedBuffers;
buffer->setObserver(this);
buffer->add_ref();
// Mute/suppress the recording sound
int64_t timeUs;
CHECK(buffer->meta_data().findInt64(kKeyTime, &timeUs));
int64_t elapsedTimeUs = timeUs - mStartTimeUs;
if (elapsedTimeUs < kAutoRampStartUs) {
memset((uint8_t *) buffer->data(), 0, buffer->range_length());
} else if (elapsedTimeUs < kAutoRampStartUs + kAutoRampDurationUs) {
int32_t autoRampDurationFrames =
((int64_t)kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL; //Need type casting
int32_t autoRampStartFrames =
((int64_t)kAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL; //Need type casting
int32_t nFrames = mNumFramesReceived - autoRampStartFrames;
rampVolume(nFrames, autoRampDurationFrames,
(uint8_t *) buffer->data(), buffer->range_length());
}
// Track the max recording signal amplitude.
if (mTrackMaxAmplitude) {
trackMaxAmplitude(
(int16_t *) buffer->data(), buffer->range_length() >> 1);
}
if (mSampleRate != mOutSampleRate) {
timeUs *= (int64_t)mSampleRate / (int64_t)mOutSampleRate;
buffer->meta_data().setInt64(kKeyTime, timeUs);
}
*out = buffer;
return OK;
}
status_t AudioSource::setStopTimeUs(int64_t stopTimeUs) {
Mutex::Autolock autoLock(mLock);
ALOGV("Set stoptime: %lld us", (long long)stopTimeUs);
if (stopTimeUs < -1) {
ALOGE("Invalid stop time %lld us", (long long)stopTimeUs);
return BAD_VALUE;
} else if (stopTimeUs == -1) {
ALOGI("reset stopTime to be -1");
}
mStopSystemTimeUs = stopTimeUs;
return OK;
}
void AudioSource::signalBufferReturned(MediaBufferBase *buffer) {
ALOGV("signalBufferReturned: %p", buffer->data());
Mutex::Autolock autoLock(mLock);
--mNumClientOwnedBuffers;
buffer->setObserver(0);
buffer->release();
mFrameEncodingCompletionCondition.signal();
return;
}
size_t AudioSource::onMoreData(const AudioRecord::Buffer& audioBuffer) {
int64_t timeUs, position, timeNs;
ExtendedTimestamp ts;
ExtendedTimestamp::Location location;
const int32_t usPerSec = 1000000;
if (mRecord->getTimestamp(&ts) == OK &&
ts.getBestTimestamp(&position, &timeNs, ExtendedTimestamp::TIMEBASE_MONOTONIC,
&location) == OK) {
// Use audio timestamp.
timeUs = timeNs / 1000 -
(position - mNumFramesSkipped -
mNumFramesReceived + mNumFramesLost) * usPerSec / mSampleRate;
} else {
// This should not happen in normal case.
ALOGW("Failed to get audio timestamp, fallback to use systemclock");
timeUs = systemTime() / 1000LL;
// Estimate the real sampling time of the 1st sample in this buffer
// from AudioRecord's latency. (Apply this adjustment first so that
// the start time logic is not affected.)
timeUs -= mRecord->latency() * 1000LL;
}
ALOGV("dataCallbackTimestamp: %" PRId64 " us", timeUs);
Mutex::Autolock autoLock(mLock);
if (!mStarted) {
ALOGW("Spurious callback from AudioRecord. Drop the audio data.");
return audioBuffer.size();
}
// Drop retrieved and previously lost audio data.
if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) {
(void) mRecord->getInputFramesLost();
int64_t receievedFrames = audioBuffer.size() / mRecord->frameSize();
ALOGV("Drop audio data(%" PRId64 " frames) at %" PRId64 "/%" PRId64 " us",
receievedFrames, timeUs, mStartTimeUs);
mNumFramesSkipped += receievedFrames;
return audioBuffer.size();
}
if (mStopSystemTimeUs != -1 && timeUs >= mStopSystemTimeUs) {
ALOGV("Drop Audio frame at %lld stop time: %lld us",
(long long)timeUs, (long long)mStopSystemTimeUs);
mNoMoreFramesToRead = true;
mFrameAvailableCondition.signal();
return audioBuffer.size();
}
if (mNumFramesReceived == 0 && mPrevSampleTimeUs == 0) {
mInitialReadTimeUs = timeUs;
// Initial delay
if (mStartTimeUs > 0) {
mStartTimeUs = timeUs - mStartTimeUs;
}
mPrevSampleTimeUs = mStartTimeUs;
}
mLastFrameTimestampUs = timeUs;
uint64_t numLostBytes = 0; // AudioRecord::getInputFramesLost() returns uint32_t
if (mNumFramesReceived > 0) { // Ignore earlier frame lost
// getInputFramesLost() returns the number of lost frames.
// Convert number of frames lost to number of bytes lost.
numLostBytes = (uint64_t)mRecord->getInputFramesLost() * mRecord->frameSize();
}
CHECK_EQ(numLostBytes & 1, 0u);
CHECK_EQ(audioBuffer.size() & 1, 0u);
if (numLostBytes > 0) {
// Loss of audio frames should happen rarely; thus the LOGW should
// not cause a logging spam
ALOGW("Lost audio record data: %" PRIu64 " bytes", numLostBytes);
}
while (numLostBytes > 0) {
uint64_t bufferSize = numLostBytes;
if (numLostBytes > kMaxBufferSize) {
numLostBytes -= kMaxBufferSize;
bufferSize = kMaxBufferSize;
} else {
numLostBytes = 0;
}
MediaBuffer *lostAudioBuffer = new MediaBuffer(bufferSize);
memset(lostAudioBuffer->data(), 0, bufferSize);
lostAudioBuffer->set_range(0, bufferSize);
mNumFramesLost += bufferSize / mRecord->frameSize();
queueInputBuffer_l(lostAudioBuffer, timeUs);
}
if (audioBuffer.size() == 0) {
ALOGW("Nothing is available from AudioRecord callback buffer");
return audioBuffer.size();
}
MediaBuffer *buffer = new MediaBuffer(audioBuffer.size());
memcpy((uint8_t *) buffer->data(),
audioBuffer.data(), audioBuffer.size());
buffer->set_range(0, audioBuffer.size());
queueInputBuffer_l(buffer, timeUs);
return audioBuffer.size();
}
void AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) {
const size_t bufferSize = buffer->range_length();
const size_t frameSize = mRecord->frameSize();
if (mNumFramesReceived == 0) {
buffer->meta_data().setInt64(kKeyAnchorTime, mStartTimeUs);
}
mNumFramesReceived += bufferSize / frameSize;
const int64_t timestampUs =
mStartTimeUs +
((1000000LL * mNumFramesReceived) +
(mSampleRate >> 1)) / mSampleRate;
buffer->meta_data().setInt64(kKeyTime, mPrevSampleTimeUs);
buffer->meta_data().setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs);
mPrevSampleTimeUs = timestampUs;
mBuffersReceived.push_back(buffer);
mFrameAvailableCondition.signal();
}
void AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) {
for (int i = nSamples; i > 0; --i) {
int16_t value = *data++;
if (value < 0) {
value = -value;
}
if (mMaxAmplitude < value) {
mMaxAmplitude = value;
}
}
}
int16_t AudioSource::getMaxAmplitude() {
// First call activates the tracking.
if (!mTrackMaxAmplitude) {
mTrackMaxAmplitude = true;
}
int16_t value = mMaxAmplitude;
mMaxAmplitude = 0;
ALOGV("max amplitude since last call: %d", value);
return value;
}
status_t AudioSource::setInputDevice(audio_port_handle_t deviceId) {
if (mRecord != 0) {
return mRecord->setInputDevice(deviceId);
}
return NO_INIT;
}
status_t AudioSource::getRoutedDeviceId(audio_port_handle_t* deviceId) {
if (mRecord != 0) {
*deviceId = mRecord->getRoutedDeviceId();
return NO_ERROR;
}
return NO_INIT;
}
status_t AudioSource::addAudioDeviceCallback(
const sp<AudioSystem::AudioDeviceCallback>& callback) {
if (mRecord != 0) {
return mRecord->addAudioDeviceCallback(callback);
}
return NO_INIT;
}
status_t AudioSource::removeAudioDeviceCallback(
const sp<AudioSystem::AudioDeviceCallback>& callback) {
if (mRecord != 0) {
return mRecord->removeAudioDeviceCallback(callback);
}
return NO_INIT;
}
status_t AudioSource::getActiveMicrophones(
std::vector<media::MicrophoneInfoFw>* activeMicrophones) {
if (mRecord != 0) {
return mRecord->getActiveMicrophones(activeMicrophones);
}
return NO_INIT;
}
status_t AudioSource::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) {
ALOGV("setPreferredMicrophoneDirection(%d)", direction);
if (mRecord != 0) {
return mRecord->setPreferredMicrophoneDirection(direction);
}
return NO_INIT;
}
status_t AudioSource::setPreferredMicrophoneFieldDimension(float zoom) {
ALOGV("setPreferredMicrophoneFieldDimension(%f)", zoom);
if (mRecord != 0) {
return mRecord->setPreferredMicrophoneFieldDimension(zoom);
}
return NO_INIT;
}
status_t AudioSource::getPortId(audio_port_handle_t *portId) const {
if (mRecord != 0) {
*portId = mRecord->getPortId();
return NO_ERROR;
}
return NO_INIT;
}
} // namespace android