diff options
Diffstat (limited to 'libs/audioflinger/AudioFlinger.cpp')
-rw-r--r-- | libs/audioflinger/AudioFlinger.cpp | 2474 |
1 files changed, 2474 insertions, 0 deletions
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp new file mode 100644 index 0000000..92c40e9 --- /dev/null +++ b/libs/audioflinger/AudioFlinger.cpp @@ -0,0 +1,2474 @@ +/* //device/include/server/AudioFlinger/AudioFlinger.cpp +** +** Copyright 2007, 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. +*/ + + +#define LOG_TAG "AudioFlinger" +//#define LOG_NDEBUG 0 + +#include <math.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <utils/IServiceManager.h> +#include <utils/Log.h> +#include <utils/Parcel.h> +#include <utils/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/threads.h> + +#include <cutils/properties.h> + +#include <media/AudioTrack.h> +#include <media/AudioRecord.h> + +#include <private/media/AudioTrackShared.h> + +#include <hardware_legacy/AudioHardwareInterface.h> + +#include "AudioMixer.h" +#include "AudioFlinger.h" + +#ifdef WITH_A2DP +#include "A2dpAudioInterface.h" +#endif + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +//static const nsecs_t kStandbyTimeInNsecs = seconds(3); +static const unsigned long kBufferRecoveryInUsecs = 2000; +static const unsigned long kMaxBufferRecoveryInUsecs = 20000; +static const float MAX_GAIN = 4096.0f; + +// retry counts for buffer fill timeout +// 50 * ~20msecs = 1 second +static const int8_t kMaxTrackRetries = 50; +static const int8_t kMaxTrackStartupRetries = 50; + +static const int kStartSleepTime = 30000; +static const int kStopSleepTime = 30000; + +// Maximum number of pending buffers allocated by OutputTrack::write() +static const uint8_t kMaxOutputTrackBuffers = 5; + + +#define AUDIOFLINGER_SECURITY_ENABLED 1 + +// ---------------------------------------------------------------------------- + +static bool recordingAllowed() { +#ifndef HAVE_ANDROID_OS + return true; +#endif +#if AUDIOFLINGER_SECURITY_ENABLED + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO")); + if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO"); + return ok; +#else + if (!checkCallingPermission(String16("android.permission.RECORD_AUDIO"))) + LOGW("WARNING: Need to add android.permission.RECORD_AUDIO to manifest"); + return true; +#endif +} + +static bool settingsAllowed() { +#ifndef HAVE_ANDROID_OS + return true; +#endif +#if AUDIOFLINGER_SECURITY_ENABLED + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); + if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); + return ok; +#else + if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"))) + LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest"); + return true; +#endif +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::AudioFlinger() + : BnAudioFlinger(), + mAudioHardware(0), mA2dpAudioInterface(0), + mA2dpEnabled(false), mA2dpEnabledReq(false), + mForcedSpeakerCount(0), mForcedRoute(0), mRouteRestoreTime(0), mMusicMuteSaved(false) +{ + mHardwareStatus = AUDIO_HW_IDLE; + mAudioHardware = AudioHardwareInterface::create(); + mHardwareStatus = AUDIO_HW_INIT; + if (mAudioHardware->initCheck() == NO_ERROR) { + // open 16-bit output stream for s/w mixer + mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; + status_t status; + AudioStreamOut *hwOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + mHardwareStatus = AUDIO_HW_IDLE; + if (hwOutput) { + mHardwareMixerThread = new MixerThread(this, hwOutput, AudioSystem::AUDIO_OUTPUT_HARDWARE); + } else { + LOGE("Failed to initialize hardware output stream, status: %d", status); + } + +#ifdef WITH_A2DP + // Create A2DP interface + mA2dpAudioInterface = new A2dpAudioInterface(); + AudioStreamOut *a2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + if (a2dpOutput) { + mA2dpMixerThread = new MixerThread(this, a2dpOutput, AudioSystem::AUDIO_OUTPUT_A2DP); + if (hwOutput) { + uint32_t frameCount = ((a2dpOutput->bufferSize()/a2dpOutput->frameSize()) * hwOutput->sampleRate()) / a2dpOutput->sampleRate(); + MixerThread::OutputTrack *a2dpOutTrack = new MixerThread::OutputTrack(mA2dpMixerThread, + hwOutput->sampleRate(), + AudioSystem::PCM_16_BIT, + hwOutput->channelCount(), + frameCount); + mHardwareMixerThread->setOuputTrack(a2dpOutTrack); + } + } else { + LOGE("Failed to initialize A2DP output stream, status: %d", status); + } +#endif + + // FIXME - this should come from settings + setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); + setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); + setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL); + setMode(AudioSystem::MODE_NORMAL); + + setMasterVolume(1.0f); + setMasterMute(false); + + // Start record thread + mAudioRecordThread = new AudioRecordThread(mAudioHardware); + if (mAudioRecordThread != 0) { + mAudioRecordThread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO); + } + } else { + LOGE("Couldn't even initialize the stubbed audio hardware!"); + } +} + +AudioFlinger::~AudioFlinger() +{ + if (mAudioRecordThread != 0) { + mAudioRecordThread->exit(); + mAudioRecordThread.clear(); + } + mHardwareMixerThread.clear(); + delete mAudioHardware; + // deleting mA2dpAudioInterface also deletes mA2dpOutput; +#ifdef WITH_A2DP + mA2dpMixerThread.clear(); + delete mA2dpAudioInterface; +#endif +} + + +#ifdef WITH_A2DP +void AudioFlinger::setA2dpEnabled(bool enable) +{ + LOGV_IF(enable, "set output to A2DP\n"); + LOGV_IF(!enable, "set output to hardware audio\n"); + + mA2dpEnabledReq = enable; + mA2dpMixerThread->wakeUp(); +} +#endif // WITH_A2DP + +bool AudioFlinger::streamForcedToSpeaker(int streamType) +{ + // NOTE that streams listed here must not be routed to A2DP by default: + // AudioSystem::routedToA2dpOutput(streamType) == false + return (streamType == AudioSystem::RING || + streamType == AudioSystem::ALARM || + streamType == AudioSystem::NOTIFICATION); +} + +status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append("Clients:\n"); + for (size_t i = 0; i < mClients.size(); ++i) { + wp<Client> wClient = mClients.valueAt(i); + if (wClient != 0) { + sp<Client> client = wClient.promote(); + if (client != 0) { + snprintf(buffer, SIZE, " pid: %d\n", client->pid()); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + + +status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Hardware status: %d\n", mHardwareStatus); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump AudioFlinger from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::dump(int fd, const Vector<String16>& args) +{ + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + dumpPermissionDenial(fd, args); + } else { + AutoMutex lock(&mLock); + + dumpClients(fd, args); + dumpInternals(fd, args); + mHardwareMixerThread->dump(fd, args); +#ifdef WITH_A2DP + mA2dpMixerThread->dump(fd, args); +#endif + + // dump record client + if (mAudioRecordThread != 0) mAudioRecordThread->dump(fd, args); + + if (mAudioHardware) { + mAudioHardware->dumpState(fd, args); + } + } + return NO_ERROR; +} + +// IAudioFlinger interface + + +sp<IAudioTrack> AudioFlinger::createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + status_t *status) +{ + sp<MixerThread::Track> track; + sp<TrackHandle> trackHandle; + sp<Client> client; + wp<Client> wclient; + status_t lStatus; + + if (streamType >= AudioSystem::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + + wclient = mClients.valueFor(pid); + + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } +#ifdef WITH_A2DP + if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) { + track = mA2dpMixerThread->createTrack(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } else +#endif + { + track = mHardwareMixerThread->createTrack(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } + if (track != NULL) { + trackHandle = new TrackHandle(track); + lStatus = NO_ERROR; + } + } + +Exit: + if(status) { + *status = lStatus; + } + return trackHandle; +} + +uint32_t AudioFlinger::sampleRate(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->sampleRate(); + } +#endif + return mHardwareMixerThread->sampleRate(); +} + +int AudioFlinger::channelCount(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->channelCount(); + } +#endif + return mHardwareMixerThread->channelCount(); +} + +int AudioFlinger::format(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->format(); + } +#endif + return mHardwareMixerThread->format(); +} + +size_t AudioFlinger::frameCount(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->frameCount(); + } +#endif + return mHardwareMixerThread->frameCount(); +} + +uint32_t AudioFlinger::latency(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->latency(); + } +#endif + return mHardwareMixerThread->latency(); +} + +status_t AudioFlinger::setMasterVolume(float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + // when hw supports master volume, don't scale in sw mixer + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + if (mAudioHardware->setMasterVolume(value) == NO_ERROR) { + value = 1.0f; + } + mHardwareStatus = AUDIO_HW_IDLE; + mHardwareMixerThread->setMasterVolume(value); +#ifdef WITH_A2DP + mA2dpMixerThread->setMasterVolume(value); +#endif + + return NO_ERROR; +} + +status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) +{ + status_t err = NO_ERROR; + + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if ((mode < AudioSystem::MODE_CURRENT) || (mode >= AudioSystem::NUM_MODES)) { + LOGW("Illegal value: setRouting(%d, %u, %u)", mode, routes, mask); + return BAD_VALUE; + } + +#ifdef WITH_A2DP + LOGD("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), IPCThreadState::self()->getCallingPid()); + if (mode == AudioSystem::MODE_NORMAL && + (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) { + AutoMutex lock(&mLock); + + bool enableA2dp = false; + if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) { + enableA2dp = true; + } + setA2dpEnabled(enableA2dp); + LOGV("setOutput done\n"); + } +#endif + + // do nothing if only A2DP routing is affected + mask &= ~AudioSystem::ROUTE_BLUETOOTH_A2DP; + if (mask) { + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_GET_ROUTING; + uint32_t r; + err = mAudioHardware->getRouting(mode, &r); + if (err == NO_ERROR) { + r = (r & ~mask) | (routes & mask); + if (mode == AudioSystem::MODE_NORMAL || + (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { + mSavedRoute = r; + r |= mForcedRoute; + LOGV("setRouting mSavedRoute %08x mForcedRoute %08x\n", mSavedRoute, mForcedRoute); + } + mHardwareStatus = AUDIO_HW_SET_ROUTING; + err = mAudioHardware->setRouting(mode, r); + } + mHardwareStatus = AUDIO_HW_IDLE; + } + return err; +} + +uint32_t AudioFlinger::getRouting(int mode) const +{ + uint32_t routes = 0; + if ((mode >= AudioSystem::MODE_CURRENT) && (mode < AudioSystem::NUM_MODES)) { + if (mode == AudioSystem::MODE_NORMAL || + (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { + routes = mSavedRoute; + } else { + mHardwareStatus = AUDIO_HW_GET_ROUTING; + mAudioHardware->getRouting(mode, &routes); + mHardwareStatus = AUDIO_HW_IDLE; + } + } else { + LOGW("Illegal value: getRouting(%d)", mode); + } + return routes; +} + +status_t AudioFlinger::setMode(int mode) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) { + LOGW("Illegal value: setMode(%d)", mode); + return BAD_VALUE; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MODE; + status_t ret = mAudioHardware->setMode(mode); + mHardwareStatus = AUDIO_HW_IDLE; + return ret; +} + +int AudioFlinger::getMode() const +{ + int mode = AudioSystem::MODE_INVALID; + mHardwareStatus = AUDIO_HW_SET_MODE; + mAudioHardware->getMode(&mode); + mHardwareStatus = AUDIO_HW_IDLE; + return mode; +} + +status_t AudioFlinger::setMicMute(bool state) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MIC_MUTE; + status_t ret = mAudioHardware->setMicMute(state); + mHardwareStatus = AUDIO_HW_IDLE; + return ret; +} + +bool AudioFlinger::getMicMute() const +{ + bool state = AudioSystem::MODE_INVALID; + mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; + mAudioHardware->getMicMute(&state); + mHardwareStatus = AUDIO_HW_IDLE; + return state; +} + +status_t AudioFlinger::setMasterMute(bool muted) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + mHardwareMixerThread->setMasterMute(muted); +#ifdef WITH_A2DP + mA2dpMixerThread->setMasterMute(muted); +#endif + return NO_ERROR; +} + +float AudioFlinger::masterVolume() const +{ + return mHardwareMixerThread->masterVolume(); +} + +bool AudioFlinger::masterMute() const +{ + return mHardwareMixerThread->masterMute(); +} + +status_t AudioFlinger::setStreamVolume(int stream, float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + + mHardwareMixerThread->setStreamVolume(stream, value); +#ifdef WITH_A2DP + mA2dpMixerThread->setStreamVolume(stream, value); +#endif + + status_t ret = NO_ERROR; + if (stream == AudioSystem::VOICE_CALL || + stream == AudioSystem::BLUETOOTH_SCO) { + + if (stream == AudioSystem::VOICE_CALL) { + value = (float)AudioSystem::logToLinear(value)/100.0f; + } else { // (type == AudioSystem::BLUETOOTH_SCO) + value = 1.0f; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_VOICE_VOLUME; + ret = mAudioHardware->setVoiceVolume(value); + mHardwareStatus = AUDIO_HW_IDLE; + } + + return ret; +} + +status_t AudioFlinger::setStreamMute(int stream, bool muted) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + +#ifdef WITH_A2DP + mA2dpMixerThread->setStreamMute(stream, muted); +#endif + if (stream == AudioSystem::MUSIC) + { + AutoMutex lock(&mHardwareLock); + if (mForcedRoute != 0) + mMusicMuteSaved = muted; + else + mHardwareMixerThread->setStreamMute(stream, muted); + } else { + mHardwareMixerThread->setStreamMute(stream, muted); + } + + + + return NO_ERROR; +} + +float AudioFlinger::streamVolume(int stream) const +{ + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return 0.0f; + } + return mHardwareMixerThread->streamVolume(stream); +} + +bool AudioFlinger::streamMute(int stream) const +{ + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return true; + } + + if (stream == AudioSystem::MUSIC && mForcedRoute != 0) + { + return mMusicMuteSaved; + } + return mHardwareMixerThread->streamMute(stream); +} + +bool AudioFlinger::isMusicActive() const +{ + #ifdef WITH_A2DP + if (isA2dpEnabled()) { + return mA2dpMixerThread->isMusicActive(); + } + #endif + return mHardwareMixerThread->isMusicActive(); +} + +status_t AudioFlinger::setParameter(const char* key, const char* value) +{ + status_t result, result2; + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_PARAMETER; + + LOGV("setParameter() key %s, value %s, tid %d, calling tid %d", key, value, gettid(), IPCThreadState::self()->getCallingPid()); + result = mAudioHardware->setParameter(key, value); + if (mA2dpAudioInterface) { + result2 = mA2dpAudioInterface->setParameter(key, value); + if (result2) + result = result2; + } + mHardwareStatus = AUDIO_HW_IDLE; + return result; +} + +size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); +} + +void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) +{ + + LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + + sp<IBinder> binder = client->asBinder(); + if (mNotificationClients.indexOf(binder) < 0) { + LOGV("Adding notification client %p", binder.get()); + binder->linkToDeath(this); + mNotificationClients.add(binder); + client->a2dpEnabledChanged(isA2dpEnabled()); + } +} + +void AudioFlinger::binderDied(const wp<IBinder>& who) { + + LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + + IBinder *binder = who.unsafe_get(); + + if (binder != NULL) { + int index = mNotificationClients.indexOf(binder); + if (index >= 0) { + LOGV("Removing notification client %p", binder); + mNotificationClients.removeAt(index); + } + } +} + +void AudioFlinger::handleOutputSwitch() +{ + if (mA2dpEnabled != mA2dpEnabledReq) + { + Mutex::Autolock _l(mLock); + + if (mA2dpEnabled != mA2dpEnabledReq) + { + mA2dpEnabled = mA2dpEnabledReq; + SortedVector < sp<MixerThread::Track> > tracks; + SortedVector < wp<MixerThread::Track> > activeTracks; + + // We hold mA2dpMixerThread mLock already + Mutex::Autolock _l(mHardwareMixerThread->mLock); + + // Transfer tracks playing on MUSIC stream from one mixer to the other + if (mA2dpEnabled) { + mHardwareMixerThread->getTracks(tracks, activeTracks); + mA2dpMixerThread->putTracks(tracks, activeTracks); + } else { + mA2dpMixerThread->getTracks(tracks, activeTracks); + mHardwareMixerThread->putTracks(tracks, activeTracks); + } + + // Notify AudioSystem of the A2DP activation/deactivation + size_t size = mNotificationClients.size(); + for (size_t i = 0; i < size; i++) { + sp<IBinder> binder = mNotificationClients.itemAt(i).promote(); + if (binder != NULL) { + LOGV("Notifying output change to client %p", binder.get()); + sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); + client->a2dpEnabledChanged(mA2dpEnabled); + } + } + + mHardwareMixerThread->wakeUp(); + } + } +} + +void AudioFlinger::removeClient(pid_t pid) +{ + LOGV("removeClient() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + mClients.removeItem(pid); +} + +void AudioFlinger::wakeUp() +{ + mHardwareMixerThread->wakeUp(); +#ifdef WITH_A2DP + mA2dpMixerThread->wakeUp(); +#endif // WITH_A2DP +} + +bool AudioFlinger::isA2dpEnabled() const +{ + return mA2dpEnabledReq; +} + +void AudioFlinger::handleForcedSpeakerRoute(int command) +{ + switch(command) { + case ACTIVE_TRACK_ADDED: + { + AutoMutex lock(mHardwareLock); + if (mForcedSpeakerCount++ == 0) { + mRouteRestoreTime = 0; + mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); + if (mForcedRoute == 0 && !(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + mAudioHardware->setMasterVolume(0); + usleep(mHardwareMixerThread->latency()*1000); + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareStatus = AUDIO_HW_IDLE; + // delay track start so that audio hardware has time to siwtch routes + usleep(kStartSleepTime); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + mAudioHardware->setMasterVolume(mHardwareMixerThread->masterVolume()); + mHardwareStatus = AUDIO_HW_IDLE; + } + mForcedRoute = AudioSystem::ROUTE_SPEAKER; + } + LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount); + } + break; + case ACTIVE_TRACK_REMOVED: + { + AutoMutex lock(mHardwareLock); + if (mForcedSpeakerCount > 0){ + if (--mForcedSpeakerCount == 0) { + mRouteRestoreTime = systemTime() + milliseconds(kStopSleepTime/1000); + } + LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount); + } else { + LOGE("mForcedSpeakerCount is already zero"); + } + } + break; + case CHECK_ROUTE_RESTORE_TIME: + case FORCE_ROUTE_RESTORE: + if (mRouteRestoreTime) { + AutoMutex lock(mHardwareLock); + if (mRouteRestoreTime && + (systemTime() > mRouteRestoreTime || command == FORCE_ROUTE_RESTORE)) { + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, mMusicMuteSaved); + mForcedRoute = 0; + if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute); + mHardwareStatus = AUDIO_HW_IDLE; + LOGV("Route forced to Speaker OFF %08x", mSavedRoute); + } + mRouteRestoreTime = 0; + } + } + break; + } +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType) + : Thread(false), + mAudioFlinger(audioFlinger), mAudioMixer(0), mOutput(output), mOutputType(outputType), + mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0), + mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false), + mInWrite(false) +{ + mSampleRate = output->sampleRate(); + mChannelCount = output->channelCount(); + + // FIXME - Current mixer implementation only supports stereo output + if (mChannelCount == 1) { + LOGE("Invalid audio hardware channel count"); + } + + mFormat = output->format(); + mFrameCount = output->bufferSize() / output->channelCount() / sizeof(int16_t); + mAudioMixer = new AudioMixer(mFrameCount, output->sampleRate()); + + // FIXME - Current mixer implementation only supports stereo output: Always + // Allocate a stereo buffer even if HW output is mono. + mMixBuffer = new int16_t[mFrameCount * 2]; + memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); +} + +AudioFlinger::MixerThread::~MixerThread() +{ + delete [] mMixBuffer; + delete mAudioMixer; +} + +status_t AudioFlinger::MixerThread::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + dumpTracks(fd, args); + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output %d mixer thread tracks\n", mOutputType); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mTracks.size(); ++i) { + wp<Track> wTrack = mTracks[i]; + if (wTrack != 0) { + sp<Track> track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + + snprintf(buffer, SIZE, "Output %d mixer thread active tracks\n", mOutputType); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mActiveTracks.size(); ++i) { + wp<Track> wTrack = mTracks[i]; + if (wTrack != 0) { + sp<Track> track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output %d mixer thread internals\n", mOutputType); + result.append(buffer); + snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); + result.append(buffer); + snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + result.append(buffer); + snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites); + result.append(buffer); + snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites); + result.append(buffer); + snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); + result.append(buffer); + snprintf(buffer, SIZE, "standby: %d\n", mStandby); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// Thread virtuals +bool AudioFlinger::MixerThread::threadLoop() +{ + unsigned long sleepTime = kBufferRecoveryInUsecs; + int16_t* curBuf = mMixBuffer; + Vector< sp<Track> > tracksToRemove; + size_t enabledTracks = 0; + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t); + nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2; + +#ifdef WITH_A2DP + bool outputTrackActive = false; +#endif + + do { + enabledTracks = 0; + { // scope for the mLock + + Mutex::Autolock _l(mLock); + +#ifdef WITH_A2DP + if (mOutputType == AudioSystem::AUDIO_OUTPUT_A2DP) { + mAudioFlinger->handleOutputSwitch(); + } + if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) { + if (outputTrackActive) { + mOutputTrack->stop(); + outputTrackActive = false; + } + } +#endif + + const SortedVector< wp<Track> >& activeTracks = mActiveTracks; + + // put audio hardware into standby after short delay + if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) { + // wait until we have something to do... + LOGV("Audio hardware entering standby, output %d\n", mOutputType); +// mAudioFlinger->mHardwareStatus = AUDIO_HW_STANDBY; + if (!mStandby) { + mOutput->standby(); + mStandby = true; + } + +#ifdef WITH_A2DP + if (outputTrackActive) { + mOutputTrack->stop(); + outputTrackActive = false; + } +#endif + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE); + } +// mHardwareStatus = AUDIO_HW_IDLE; + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + mWaitWorkCV.wait(mLock); + LOGV("Audio hardware exiting standby, output %d\n", mOutputType); + + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); + } + } + + standbyTime = systemTime() + kStandbyTimeInNsecs; + continue; + } + + // Forced route to speaker is handled by hardware mixer thread + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + mAudioFlinger->handleForcedSpeakerRoute(CHECK_ROUTE_RESTORE_TIME); + } + + // find out which tracks need to be processed + size_t count = activeTracks.size(); + for (size_t i=0 ; i<count ; i++) { + sp<Track> t = activeTracks[i].promote(); + if (t == 0) continue; + + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); + + // The first time a track is added we wait + // for all its buffers to be filled before processing it + mAudioMixer->setActiveTrack(track->name()); + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && + !track->isPaused()) + { + //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + + // compute volume for this track + int16_t left, right; + if (track->isMuted() || mMasterMute || track->isPausing()) { + left = right = 0; + if (track->isPausing()) { + LOGV("paused(%d)", track->name()); + track->setPaused(); + } + } else { + float typeVolume = mStreamTypes[track->type()].volume; + float v = mMasterVolume * typeVolume; + float v_clamped = v * cblk->volume[0]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + left = int16_t(v_clamped); + v_clamped = v * cblk->volume[1]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + right = int16_t(v_clamped); + } + + // XXX: these things DON'T need to be done each time + mAudioMixer->setBufferProvider(track); + mAudioMixer->enable(AudioMixer::MIXING); + + int param; + if ( track->mFillingUpStatus == Track::FS_FILLED) { + // no ramp for the first volume setting + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + param = AudioMixer::RAMP_VOLUME; + } else { + param = AudioMixer::VOLUME; + } + } else { + param = AudioMixer::RAMP_VOLUME; + } + mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); + mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::FORMAT, track->format()); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::CHANNEL_COUNT, track->channelCount()); + mAudioMixer->setParameter( + AudioMixer::RESAMPLE, + AudioMixer::SAMPLE_RATE, + int(cblk->sampleRate)); + + // reset retry count + track->mRetryCount = kMaxTrackRetries; + enabledTracks++; + } else { + //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); + if (track->isStopped()) { + track->reset(); + } + if (track->isTerminated() || track->isStopped() || track->isPaused()) { + // We have consumed all the buffers of this track. + // Remove it from the list of active tracks. + LOGV("remove(%d) from active list", track->name()); + tracksToRemove.add(track); + } else { + // No buffers for this track. Give it a few chances to + // fill a buffer, then remove it from active list. + if (--(track->mRetryCount) <= 0) { + LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); + tracksToRemove.add(track); + } + } + // LOGV("disable(%d)", track->name()); + mAudioMixer->disable(AudioMixer::MIXING); + } + } + + // remove all the tracks that need to be... + count = tracksToRemove.size(); + if (UNLIKELY(count)) { + for (size_t i=0 ; i<count ; i++) { + const sp<Track>& track = tracksToRemove[i]; + removeActiveTrack(track); + if (track->isTerminated()) { + mTracks.remove(track); + deleteTrackName(track->mName); + } + } + } + } + + if (LIKELY(enabledTracks)) { + // mix buffers... + mAudioMixer->process(curBuf); + +#ifdef WITH_A2DP + if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { + if (!outputTrackActive) { + LOGV("starting output track in mixer for output %d", mOutputType); + mOutputTrack->start(); + outputTrackActive = true; + } + mOutputTrack->write(curBuf, mFrameCount); + } +#endif + + // output audio to hardware + mLastWriteTime = systemTime(); + mInWrite = true; + mOutput->write(curBuf, mixBufferSize); + mNumWrites++; + mInWrite = false; + mStandby = false; + nsecs_t temp = systemTime(); + standbyTime = temp + kStandbyTimeInNsecs; + nsecs_t delta = temp - mLastWriteTime; + if (delta > maxPeriod) { + LOGW("write blocked for %llu msecs", ns2ms(delta)); + mNumDelayedWrites++; + } + sleepTime = kBufferRecoveryInUsecs; + } else { +#ifdef WITH_A2DP + if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { + if (outputTrackActive) { + mOutputTrack->write(curBuf, 0); + if (mOutputTrack->bufferQueueEmpty()) { + mOutputTrack->stop(); + outputTrackActive = false; + } else { + standbyTime = systemTime() + kStandbyTimeInNsecs; + } + } + } +#endif + // There was nothing to mix this round, which means all + // active tracks were late. Sleep a little bit to give + // them another chance. If we're too late, the audio + // hardware will zero-fill for us. + //LOGV("no buffers - usleep(%lu)", sleepTime); + usleep(sleepTime); + if (sleepTime < kMaxBufferRecoveryInUsecs) { + sleepTime += kBufferRecoveryInUsecs; + } + } + + // finally let go of all our tracks, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + tracksToRemove.clear(); + } while (true); + + return false; +} + +status_t AudioFlinger::MixerThread::readyToRun() +{ + if (mSampleRate == 0) { + LOGE("No working audio driver found."); + return NO_INIT; + } + LOGI("AudioFlinger's thread ready to run for output %d", mOutputType); + return NO_ERROR; +} + +void AudioFlinger::MixerThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "Mixer Thread for output %d", mOutputType); + + run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); +} + + +sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack( + const sp<AudioFlinger::Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer, + status_t *status) +{ + sp<Track> track; + status_t lStatus; + + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (sampleRate > MAX_SAMPLE_RATE || sampleRate > mSampleRate*2) { + LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + + if (mSampleRate == 0) { + LOGE("Audio driver not initialized."); + lStatus = NO_INIT; + goto Exit; + } + + track = new Track(this, client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer); + if (track->getCblk() == NULL) { + track.clear(); + lStatus = NO_MEMORY; + goto Exit; + } + mTracks.add(track); + lStatus = NO_ERROR; + } + +Exit: + if(status) { + *status = lStatus; + } + return track; +} + +void AudioFlinger::MixerThread::getTracks( + SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks) +{ + size_t size = mTracks.size(); + LOGV ("MixerThread::getTracks() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size()); + for (size_t i = 0; i < size; i++) { + sp<Track> t = mTracks[i]; + if (AudioSystem::routedToA2dpOutput(t->mStreamType)) { + tracks.add(t); + int j = mActiveTracks.indexOf(t); + if (j >= 0) { + t = mActiveTracks[j].promote(); + if (t != NULL) { + activeTracks.add(t); + } + } + } + } + + size = activeTracks.size(); + for (size_t i = 0; i < size; i++) { + removeActiveTrack(activeTracks[i]); + } + + size = tracks.size(); + for (size_t i = 0; i < size; i++) { + sp<Track> t = tracks[i]; + mTracks.remove(t); + deleteTrackName(t->name()); + } +} + +void AudioFlinger::MixerThread::putTracks( + SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks) +{ + + LOGV ("MixerThread::putTracks() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size()); + + size_t size = tracks.size(); + for (size_t i = 0; i < size ; i++) { + sp<Track> t = tracks[i]; + int name = getTrackName(); + + if (name < 0) return; + + t->mName = name; + t->mMixerThread = this; + mTracks.add(t); + + int j = activeTracks.indexOf(t); + if (j >= 0) { + addActiveTrack(t); + } + } +} + +uint32_t AudioFlinger::MixerThread::sampleRate() const +{ + return mSampleRate; +} + +int AudioFlinger::MixerThread::channelCount() const +{ + return mChannelCount; +} + +int AudioFlinger::MixerThread::format() const +{ + return mFormat; +} + +size_t AudioFlinger::MixerThread::frameCount() const +{ + return mFrameCount; +} + +uint32_t AudioFlinger::MixerThread::latency() const +{ + if (mOutput) { + return mOutput->latency(); + } + else { + return 0; + } +} + +status_t AudioFlinger::MixerThread::setMasterVolume(float value) +{ + mMasterVolume = value; + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::setMasterMute(bool muted) +{ + mMasterMute = muted; + return NO_ERROR; +} + +float AudioFlinger::MixerThread::masterVolume() const +{ + return mMasterVolume; +} + +bool AudioFlinger::MixerThread::masterMute() const +{ + return mMasterMute; +} + +status_t AudioFlinger::MixerThread::setStreamVolume(int stream, float value) +{ + mStreamTypes[stream].volume = value; + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::setStreamMute(int stream, bool muted) +{ + mStreamTypes[stream].mute = muted; + return NO_ERROR; +} + +float AudioFlinger::MixerThread::streamVolume(int stream) const +{ + return mStreamTypes[stream].volume; +} + +bool AudioFlinger::MixerThread::streamMute(int stream) const +{ + return mStreamTypes[stream].mute; +} + +bool AudioFlinger::MixerThread::isMusicActive() const +{ + size_t count = mActiveTracks.size(); + for (size_t i = 0 ; i < count ; ++i) { + sp<Track> t = mActiveTracks[i].promote(); + if (t == 0) continue; + Track* const track = t.get(); + if (t->mStreamType == AudioSystem::MUSIC) + return true; + } + return false; +} + +status_t AudioFlinger::MixerThread::addTrack(const sp<Track>& track) +{ + status_t status = ALREADY_EXISTS; + Mutex::Autolock _l(mLock); + + // here the track could be either new, or restarted + // in both cases "unstop" the track + if (track->isPaused()) { + track->mState = TrackBase::RESUMING; + LOGV("PAUSED => RESUMING (%d)", track->name()); + } else { + track->mState = TrackBase::ACTIVE; + LOGV("? => ACTIVE (%d)", track->name()); + } + // set retry count for buffer fill + track->mRetryCount = kMaxTrackStartupRetries; + if (mActiveTracks.indexOf(track) < 0) { + // the track is newly added, make sure it fills up all its + // buffers before playing. This is to ensure the client will + // effectively get the latency it requested. + track->mFillingUpStatus = Track::FS_FILLING; + track->mResetDone = false; + addActiveTrack(track); + status = NO_ERROR; + } + + LOGV("mWaitWorkCV.broadcast"); + mWaitWorkCV.broadcast(); + + return status; +} + +void AudioFlinger::MixerThread::removeTrack(wp<Track> track, int name) +{ + Mutex::Autolock _l(mLock); + sp<Track> t = track.promote(); + if (t!=NULL && (t->mState <= TrackBase::STOPPED)) { + remove_track_l(track, name); + } +} + +void AudioFlinger::MixerThread::remove_track_l(wp<Track> track, int name) +{ + sp<Track> t = track.promote(); + if (t!=NULL) { + t->reset(); + } + deleteTrackName(name); + removeActiveTrack(track); + mWaitWorkCV.broadcast(); +} + +void AudioFlinger::MixerThread::destroyTrack(const sp<Track>& track) +{ + // NOTE: We're acquiring a strong reference on the track before + // acquiring the lock, this is to make sure removing it from + // mTracks won't cause the destructor to be called while the lock is + // held (note that technically, 'track' could be a reference to an item + // in mTracks, which is why we need to do this). + sp<Track> keep(track); + Mutex::Autolock _l(mLock); + track->mState = TrackBase::TERMINATED; + if (mActiveTracks.indexOf(track) < 0) { + LOGV("remove track (%d) and delete from mixer", track->name()); + mTracks.remove(track); + deleteTrackName(keep->name()); + } +} + + +void AudioFlinger::MixerThread::addActiveTrack(const wp<Track>& t) +{ + mActiveTracks.add(t); + + // Force routing to speaker for certain stream types + // The forced routing to speaker is managed by hardware mixer + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + sp<Track> track = t.promote(); + if (track == NULL) return; + + if (streamForcedToSpeaker(track->type())) { + mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_ADDED); + } + } +} + +void AudioFlinger::MixerThread::removeActiveTrack(const wp<Track>& t) +{ + mActiveTracks.remove(t); + + // Force routing to speaker for certain stream types + // The forced routing to speaker is managed by hardware mixer + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + sp<Track> track = t.promote(); + if (track == NULL) return; + + if (streamForcedToSpeaker(track->type())) { + mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_REMOVED); + } + } +} + +int AudioFlinger::MixerThread::getTrackName() +{ + return mAudioMixer->getTrackName(); +} + +void AudioFlinger::MixerThread::deleteTrackName(int name) +{ + mAudioMixer->deleteTrackName(name); +} + +size_t AudioFlinger::MixerThread::getOutputFrameCount() +{ + return mOutput->bufferSize() / mOutput->channelCount() / sizeof(int16_t); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::TrackBase::TrackBase( + const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer) + : RefBase(), + mMixerThread(mixerThread), + mClient(client), + mStreamType(streamType), + mFrameCount(0), + mState(IDLE), + mClientTid(-1), + mFormat(format), + mFlags(flags & ~SYSTEM_FLAGS_MASK) +{ + mName = mixerThread->getTrackName(); + LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + if (mName < 0) { + LOGE("no more track names availlable"); + return; + } + + LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + + // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); + size_t size = sizeof(audio_track_cblk_t); + size_t bufferSize = frameCount*channelCount*sizeof(int16_t); + if (sharedBuffer == 0) { + size += bufferSize; + } + + if (client != NULL) { + mCblkMemory = client->heap()->allocate(size); + if (mCblkMemory != 0) { + mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = channelCount; + if (sharedBuffer == 0) { + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + } else { + mBuffer = sharedBuffer->pointer(); + } + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } else { + LOGE("not enough memory for AudioTrack size=%u", size); + client->heap()->dump("AudioTrack"); + return; + } + } else { + mCblk = (audio_track_cblk_t *)(new uint8_t[size]); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = channelCount; + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } +} + +AudioFlinger::MixerThread::TrackBase::~TrackBase() +{ + if (mCblk) { + mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + } + mCblkMemory.clear(); // and free the shared memory + mClient.clear(); +} + +void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + buffer->raw = 0; + mFrameCount = buffer->frameCount; + step(); + buffer->frameCount = 0; +} + +bool AudioFlinger::MixerThread::TrackBase::step() { + bool result; + audio_track_cblk_t* cblk = this->cblk(); + + result = cblk->stepServer(mFrameCount); + if (!result) { + LOGV("stepServer failed acquiring cblk mutex"); + mFlags |= STEPSERVER_FAILED; + } + return result; +} + +void AudioFlinger::MixerThread::TrackBase::reset() { + audio_track_cblk_t* cblk = this->cblk(); + + cblk->user = 0; + cblk->server = 0; + cblk->userBase = 0; + cblk->serverBase = 0; + mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK); + LOGV("TrackBase::reset"); +} + +sp<IMemory> AudioFlinger::MixerThread::TrackBase::getCblk() const +{ + return mCblkMemory; +} + +int AudioFlinger::MixerThread::TrackBase::sampleRate() const { + return mCblk->sampleRate; +} + +int AudioFlinger::MixerThread::TrackBase::channelCount() const { + return mCblk->channels; +} + +void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { + audio_track_cblk_t* cblk = this->cblk(); + int16_t *bufferStart = (int16_t *)mBuffer + (offset-cblk->serverBase)*cblk->channels; + int16_t *bufferEnd = bufferStart + frames * cblk->channels; + + // Check validity of returned pointer in case the track control block would have been corrupted. + if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd) { + LOGW("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \ + server %d, serverBase %d, user %d, userBase %d", + bufferStart, bufferEnd, mBuffer, mBufferEnd, + cblk->server, cblk->serverBase, cblk->user, cblk->userBase); + return 0; + } + + return bufferStart; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::Track::Track( + const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer) + : TrackBase(mixerThread, client, streamType, sampleRate, format, channelCount, frameCount, 0, sharedBuffer) +{ + mVolume[0] = 1.0f; + mVolume[1] = 1.0f; + mMute = false; + mSharedBuffer = sharedBuffer; +} + +AudioFlinger::MixerThread::Track::~Track() +{ + wp<Track> weak(this); // never create a strong ref from the dtor + mState = TERMINATED; + mMixerThread->removeTrack(weak, mName); +} + +void AudioFlinger::MixerThread::Track::destroy() +{ + mMixerThread->destroyTrack(this); +} + +void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %5d %5d %3u %3u %3u %3u %1d %1d %1d %5u %5u %5u %04x %04x\n", + mName - AudioMixer::TRACK0, + (mClient == NULL) ? getpid() : mClient->pid(), + mStreamType, + mFormat, + mCblk->channels, + mFrameCount, + mState, + mMute, + mFillingUpStatus, + mCblk->sampleRate, + mCblk->volume[0], + mCblk->volume[1], + mCblk->server, + mCblk->user); +} + +status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesReady; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesReady = cblk->framesReady(); + + if (LIKELY(framesReady)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd; + if (framesReq > framesReady) { + framesReq = framesReady; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +bool AudioFlinger::MixerThread::Track::isReady() const { + if (mFillingUpStatus != FS_FILLING) return true; + + if (mCblk->framesReady() >= mCblk->frameCount || + mCblk->forceReady) { + mFillingUpStatus = FS_FILLED; + mCblk->forceReady = 0; + LOGV("Track::isReady() track %d for output %d", mName, mMixerThread->mOutputType); + return true; + } + return false; +} + +status_t AudioFlinger::MixerThread::Track::start() +{ + LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); + mMixerThread->addTrack(this); + return NO_ERROR; +} + +void AudioFlinger::MixerThread::Track::stop() +{ + LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState > STOPPED) { + mState = STOPPED; + // If the track is not active (PAUSED and buffers full), flush buffers + if (mMixerThread->mActiveTracks.indexOf(this) < 0) { + reset(); + } + LOGV("(> STOPPED) => STOPPED (%d)", mName); + } +} + +void AudioFlinger::MixerThread::Track::pause() +{ + LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState == ACTIVE || mState == RESUMING) { + mState = PAUSING; + LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName); + } +} + +void AudioFlinger::MixerThread::Track::flush() +{ + LOGV("flush(%d)", mName); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { + return; + } + // No point remaining in PAUSED state after a flush => go to + // STOPPED state + mState = STOPPED; + + // NOTE: reset() will reset cblk->user and cblk->server with + // the risk that at the same time, the AudioMixer is trying to read + // data. In this case, getNextBuffer() would return a NULL pointer + // as audio buffer => the AudioMixer code MUST always test that pointer + // returned by getNextBuffer() is not NULL! + reset(); +} + +void AudioFlinger::MixerThread::Track::reset() +{ + // Do not reset twice to avoid discarding data written just after a flush and before + // the audioflinger thread detects the track is stopped. + if (!mResetDone) { + TrackBase::reset(); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mCblk->forceReady = 0; + mFillingUpStatus = FS_FILLING; + mResetDone = true; + } +} + +void AudioFlinger::MixerThread::Track::mute(bool muted) +{ + mMute = muted; +} + +void AudioFlinger::MixerThread::Track::setVolume(float left, float right) +{ + mVolume[0] = left; + mVolume[1] = right; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::RecordTrack::RecordTrack( + const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags) + : TrackBase(mixerThread, client, streamType, sampleRate, format, + channelCount, frameCount, flags, 0), + mOverflow(false) +{ +} + +AudioFlinger::MixerThread::RecordTrack::~RecordTrack() +{ + mMixerThread->deleteTrackName(mName); +} + +status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesAvail; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesAvail = cblk->framesAvailable_l(); + + if (LIKELY(framesAvail)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +status_t AudioFlinger::MixerThread::RecordTrack::start() +{ + return mMixerThread->mAudioFlinger->startRecord(this); +} + +void AudioFlinger::MixerThread::RecordTrack::stop() +{ + mMixerThread->mAudioFlinger->stopRecord(this); + TrackBase::reset(); + // Force overerrun condition to avoid false overrun callback until first data is + // read from buffer + mCblk->flowControlFlag = 1; +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::OutputTrack::OutputTrack( + const sp<MixerThread>& mixerThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount) + : Track(mixerThread, NULL, AudioSystem::SYSTEM, sampleRate, format, channelCount, frameCount, NULL), + mOutputMixerThread(mixerThread) +{ + + mCblk->out = 1; + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->volume[0] = mCblk->volume[1] = 0x1000; + mOutBuffer.frameCount = 0; + mCblk->bufferTimeoutMs = 10; + + LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd); + +} + +AudioFlinger::MixerThread::OutputTrack::~OutputTrack() +{ + stop(); +} + +status_t AudioFlinger::MixerThread::OutputTrack::start() +{ + status_t status = Track::start(); + + mRetryCount = 127; + return status; +} + +void AudioFlinger::MixerThread::OutputTrack::stop() +{ + Track::stop(); + clearBufferQueue(); + mOutBuffer.frameCount = 0; +} + +void AudioFlinger::MixerThread::OutputTrack::write(int16_t* data, uint32_t frames) +{ + Buffer *pInBuffer; + Buffer inBuffer; + uint32_t channels = mCblk->channels; + + inBuffer.frameCount = frames; + inBuffer.i16 = data; + + if (mCblk->user == 0) { + if (mOutputMixerThread->isMusicActive()) { + mCblk->forceReady = 1; + LOGV("OutputTrack::start() force ready"); + } else if (mCblk->frameCount > frames){ + if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + uint32_t startFrames = (mCblk->frameCount - frames); + LOGV("OutputTrack::start() write %d frames", startFrames); + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[startFrames * channels]; + pInBuffer->frameCount = startFrames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW ("OutputTrack::write() no more buffers"); + } + } + } + + while (1) { + // First write pending buffers, then new data + if (mBufferQueue.size()) { + pInBuffer = mBufferQueue.itemAt(0); + } else { + pInBuffer = &inBuffer; + } + + if (pInBuffer->frameCount == 0) { + break; + } + + if (mOutBuffer.frameCount == 0) { + mOutBuffer.frameCount = pInBuffer->frameCount; + if (obtainBuffer(&mOutBuffer) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + break; + } + } + + uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; + memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t)); + mCblk->stepUser(outFrames); + pInBuffer->frameCount -= outFrames; + pInBuffer->i16 += outFrames * channels; + mOutBuffer.frameCount -= outFrames; + mOutBuffer.i16 += outFrames * channels; + + if (pInBuffer->frameCount == 0) { + if (mBufferQueue.size()) { + mBufferQueue.removeAt(0); + delete [] pInBuffer->mBuffer; + delete pInBuffer; + } else { + break; + } + } + } + + // If we could not write all frames, allocate a buffer and queue it for next time. + if (inBuffer.frameCount) { + if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; + pInBuffer->frameCount = inBuffer.frameCount; + pInBuffer->i16 = pInBuffer->mBuffer; + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW("OutputTrack::write() no more buffers"); + } + } + + // Calling write() with a 0 length buffer, means that no more data will be written: + // If no more buffers are pending, fill output track buffer to make sure it is started + // by output mixer. + if (frames == 0 && mBufferQueue.size() == 0 && mCblk->user < mCblk->frameCount) { + frames = mCblk->frameCount - mCblk->user; + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[frames * channels]; + pInBuffer->frameCount = frames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } + +} + +status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer) +{ + int active; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = buffer->frameCount; + + LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server); + buffer->frameCount = 0; + + uint32_t framesAvail = cblk->framesAvailable(); + + if (framesAvail == 0) { + return AudioTrack::NO_MORE_BUFFERS; + } + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + + buffer->frameCount = framesReq; + buffer->raw = (void *)cblk->buffer(u); + return NO_ERROR; +} + + +void AudioFlinger::MixerThread::OutputTrack::clearBufferQueue() +{ + size_t size = mBufferQueue.size(); + Buffer *pBuffer; + + for (size_t i = 0; i < size; i++) { + pBuffer = mBufferQueue.itemAt(i); + delete [] pBuffer->mBuffer; + delete pBuffer; + } + mBufferQueue.clear(); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) + : RefBase(), + mAudioFlinger(audioFlinger), + mMemoryDealer(new MemoryDealer(1024*1024)), + mPid(pid) +{ + // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer +} + +AudioFlinger::Client::~Client() +{ + mAudioFlinger->removeClient(mPid); +} + +const sp<MemoryDealer>& AudioFlinger::Client::heap() const +{ + return mMemoryDealer; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::MixerThread::Track>& track) + : BnAudioTrack(), + mTrack(track) +{ +} + +AudioFlinger::TrackHandle::~TrackHandle() { + // just stop the track on deletion, associated resources + // will be freed from the main thread once all pending buffers have + // been played. Unless it's not in the active track list, in which + // case we free everything now... + mTrack->destroy(); +} + +status_t AudioFlinger::TrackHandle::start() { + return mTrack->start(); +} + +void AudioFlinger::TrackHandle::stop() { + mTrack->stop(); +} + +void AudioFlinger::TrackHandle::flush() { + mTrack->flush(); +} + +void AudioFlinger::TrackHandle::mute(bool e) { + mTrack->mute(e); +} + +void AudioFlinger::TrackHandle::pause() { + mTrack->pause(); +} + +void AudioFlinger::TrackHandle::setVolume(float left, float right) { + mTrack->setVolume(left, right); +} + +sp<IMemory> AudioFlinger::TrackHandle::getCblk() const { + return mTrack->getCblk(); +} + +status_t AudioFlinger::TrackHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioTrack::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +sp<IAudioRecord> AudioFlinger::openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + status_t *status) +{ + sp<AudioRecordThread> thread; + sp<MixerThread::RecordTrack> recordTrack; + sp<RecordHandle> recordHandle; + sp<Client> client; + wp<Client> wclient; + AudioStreamIn* input = 0; + int inFrameCount; + size_t inputBufferSize; + status_t lStatus; + + // check calling permissions + if (!recordingAllowed()) { + lStatus = PERMISSION_DENIED; + goto Exit; + } + + if (uint32_t(streamType) >= AudioRecord::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + lStatus = BAD_VALUE; + goto Exit; + } + + if (sampleRate > MAX_SAMPLE_RATE) { + LOGE("Sample rate out of range"); + lStatus = BAD_VALUE; + goto Exit; + } + + if (mAudioRecordThread == 0) { + LOGE("Audio record thread not started"); + lStatus = NO_INIT; + goto Exit; + } + + + // Check that audio input stream accepts requested audio parameters + inputBufferSize = mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); + if (inputBufferSize == 0) { + lStatus = BAD_VALUE; + LOGE("Bad audio input parameters: sampling rate %u, format %d, channels %d", sampleRate, format, channelCount); + goto Exit; + } + + // add client to list + { + Mutex::Autolock _l(mLock); + wclient = mClients.valueFor(pid); + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } + } + + // frameCount must be a multiple of input buffer size + inFrameCount = inputBufferSize/channelCount/sizeof(short); + frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount; + + // create new record track and pass to record thread + recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate, + format, channelCount, frameCount, flags); + if (recordTrack->getCblk() == NULL) { + recordTrack.clear(); + lStatus = NO_MEMORY; + goto Exit; + } + + // return to handle to client + recordHandle = new RecordHandle(recordTrack); + lStatus = NO_ERROR; + +Exit: + if (status) { + *status = lStatus; + } + return recordHandle; +} + +status_t AudioFlinger::startRecord(MixerThread::RecordTrack* recordTrack) { + if (mAudioRecordThread != 0) { + return mAudioRecordThread->start(recordTrack); + } + return NO_INIT; +} + +void AudioFlinger::stopRecord(MixerThread::RecordTrack* recordTrack) { + if (mAudioRecordThread != 0) { + mAudioRecordThread->stop(recordTrack); + } +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::MixerThread::RecordTrack>& recordTrack) + : BnAudioRecord(), + mRecordTrack(recordTrack) +{ +} + +AudioFlinger::RecordHandle::~RecordHandle() { + stop(); +} + +status_t AudioFlinger::RecordHandle::start() { + LOGV("RecordHandle::start()"); + return mRecordTrack->start(); +} + +void AudioFlinger::RecordHandle::stop() { + LOGV("RecordHandle::stop()"); + mRecordTrack->stop(); +} + +sp<IMemory> AudioFlinger::RecordHandle::getCblk() const { + return mRecordTrack->getCblk(); +} + +status_t AudioFlinger::RecordHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioRecord::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::AudioRecordThread::AudioRecordThread(AudioHardwareInterface* audioHardware) : + mAudioHardware(audioHardware), + mActive(false) +{ +} + +AudioFlinger::AudioRecordThread::~AudioRecordThread() +{ +} + +bool AudioFlinger::AudioRecordThread::threadLoop() +{ + LOGV("AudioRecordThread: start record loop"); + AudioBufferProvider::Buffer buffer; + int inBufferSize = 0; + int inFrameCount = 0; + AudioStreamIn* input = 0; + + mActive = 0; + + // start recording + while (!exitPending()) { + if (!mActive) { + mLock.lock(); + if (!mActive && !exitPending()) { + LOGV("AudioRecordThread: loop stopping"); + if (input) { + delete input; + input = 0; + } + mRecordTrack.clear(); + mStopped.signal(); + + mWaitWorkCV.wait(mLock); + + LOGV("AudioRecordThread: loop starting"); + if (mRecordTrack != 0) { + input = mAudioHardware->openInputStream(mRecordTrack->format(), + mRecordTrack->channelCount(), + mRecordTrack->sampleRate(), + &mStartStatus, + (AudioSystem::audio_in_acoustics)(mRecordTrack->mFlags >> 16)); + if (input != 0) { + inBufferSize = input->bufferSize(); + inFrameCount = inBufferSize/input->frameSize(); + } + } else { + mStartStatus = NO_INIT; + } + if (mStartStatus !=NO_ERROR) { + LOGW("record start failed, status %d", mStartStatus); + mActive = false; + mRecordTrack.clear(); + } + mWaitWorkCV.signal(); + } + mLock.unlock(); + } else if (mRecordTrack != 0) { + + buffer.frameCount = inFrameCount; + if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR)) { + LOGV("AudioRecordThread read: %d frames", buffer.frameCount); + ssize_t bytesRead = input->read(buffer.raw, inBufferSize); + if (bytesRead < 0) { + LOGE("Error reading audio input"); + sleep(1); + } + mRecordTrack->releaseBuffer(&buffer); + mRecordTrack->overflow(); + } + + // client isn't retrieving buffers fast enough + else { + if (!mRecordTrack->setOverflow()) + LOGW("AudioRecordThread: buffer overflow"); + // Release the processor for a while before asking for a new buffer. + // This will give the application more chance to read from the buffer and + // clear the overflow. + usleep(5000); + } + } + } + + + if (input) { + delete input; + } + mRecordTrack.clear(); + + return false; +} + +status_t AudioFlinger::AudioRecordThread::start(MixerThread::RecordTrack* recordTrack) +{ + LOGV("AudioRecordThread::start"); + AutoMutex lock(&mLock); + mActive = true; + // If starting the active track, just reset mActive in case a stop + // was pending and exit + if (recordTrack == mRecordTrack.get()) return NO_ERROR; + + if (mRecordTrack != 0) return -EBUSY; + + mRecordTrack = recordTrack; + + // signal thread to start + LOGV("Signal record thread"); + mWaitWorkCV.signal(); + mWaitWorkCV.wait(mLock); + LOGV("Record started, status %d", mStartStatus); + return mStartStatus; +} + +void AudioFlinger::AudioRecordThread::stop(MixerThread::RecordTrack* recordTrack) { + LOGV("AudioRecordThread::stop"); + AutoMutex lock(&mLock); + if (mActive && (recordTrack == mRecordTrack.get())) { + mActive = false; + mStopped.wait(mLock); + } +} + +void AudioFlinger::AudioRecordThread::exit() +{ + LOGV("AudioRecordThread::exit"); + { + AutoMutex lock(&mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +status_t AudioFlinger::AudioRecordThread::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + pid_t pid = 0; + + if (mRecordTrack != 0 && mRecordTrack->mClient != 0) { + snprintf(buffer, SIZE, "Record client pid: %d\n", mRecordTrack->mClient->pid()); + result.append(buffer); + } else { + result.append("No record client\n"); + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioFlinger::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- +void AudioFlinger::instantiate() { + defaultServiceManager()->addService( + String16("media.audio_flinger"), new AudioFlinger()); +} + +}; // namespace android |