diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
commit | e09fd9e819c23dc90bca68375645e15544861330 (patch) | |
tree | 9a9fdadd1301625f875a3c126c986c79e3363ac4 /libs | |
parent | 7c1b96a165f970a09ed239bb4fb3f1b0d8f2a407 (diff) | |
download | frameworks_native-e09fd9e819c23dc90bca68375645e15544861330.zip frameworks_native-e09fd9e819c23dc90bca68375645e15544861330.tar.gz frameworks_native-e09fd9e819c23dc90bca68375645e15544861330.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'libs')
57 files changed, 2871 insertions, 1837 deletions
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp new file mode 100644 index 0000000..d54795c --- /dev/null +++ b/libs/audioflinger/A2dpAudioInterface.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2008 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 <math.h> + +#define LOG_NDEBUG 0 +#define LOG_TAG "A2dpAudioInterface" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "A2dpAudioInterface.h" +#include "audio/liba2dp.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioInterface() : + mOutput(0) +{ +} + +A2dpAudioInterface::~A2dpAudioInterface() +{ + delete mOutput; +} + +status_t A2dpAudioInterface::initCheck() +{ + return 0; +} + +AudioStreamOut* A2dpAudioInterface::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + LOGD("A2dpAudioInterface::openOutputStream %d, %d, %d\n", format, channelCount, sampleRate); + Mutex::Autolock lock(mLock); + status_t err = 0; + + // only one output stream allowed + if (mOutput) { + if (status) + *status = -1; + return NULL; + } + + // create new output stream + A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); + if ((err = out->set(format, channelCount, sampleRate)) == NO_ERROR) { + mOutput = out; + } else { + delete out; + } + + if (status) + *status = err; + return mOutput; +} + +AudioStreamIn* A2dpAudioInterface::openInputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + if (status) + *status = -1; + return NULL; +} + +status_t A2dpAudioInterface::standby() +{ + return 0; +} + +status_t A2dpAudioInterface::setMicMute(bool state) +{ + return 0; +} + +status_t A2dpAudioInterface::getMicMute(bool* state) +{ + return 0; +} + +status_t A2dpAudioInterface::setParameter(const char *key, const char *value) +{ + LOGD("setParameter %s,%s\n", key, value); + return 0; +} + +status_t A2dpAudioInterface::setVoiceVolume(float v) +{ + return 0; +} + +status_t A2dpAudioInterface::setMasterVolume(float v) +{ + return 0; +} + +status_t A2dpAudioInterface::doRouting() +{ + return 0; +} + +status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args) +{ + return 0; +} + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : + mFd(-1), mStartCount(0), mRetryCount(0), mData(NULL), + mInitialized(false), mBufferRemaining(0) +{ +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::set( + int format, int channels, uint32_t rate) +{ + LOGD("A2dpAudioStreamOut::set %d, %d, %d\n", format, channels, rate); + + // fix up defaults + if (format == 0) format = AudioSystem::PCM_16_BIT; + if (channels == 0) channels = channelCount(); + if (rate == 0) rate = sampleRate(); + + // check values + if ((format != AudioSystem::PCM_16_BIT) || + (channels != channelCount()) || + (rate != sampleRate())) + return BAD_VALUE; + + return NO_ERROR; +} + +A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() +{ + if (mData) + a2dp_cleanup(mData); +} + +ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) +{ + if (!mInitialized) { + int ret = a2dp_init("00:00:00:00:00:00", 44100, 2, &mData); + if (ret) + return ret; + mInitialized = true; + } + + size_t remaining = bytes; + while (remaining > 0) { + int written = a2dp_write(mData, buffer, remaining); + remaining -= written; + buffer = ((char *)buffer) + written; + } + + return bytes; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() +{ + return 0; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args) +{ + return NO_ERROR; +} + + +}; // namespace android diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h new file mode 100644 index 0000000..03bf933 --- /dev/null +++ b/libs/audioflinger/A2dpAudioInterface.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef A2DP_AUDIO_HARDWARE_H +#define A2DP_AUDIO_HARDWARE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> + +#include <hardware/AudioHardwareBase.h> + + +namespace android { + +class A2dpAudioInterface : public AudioHardwareBase +{ + class A2dpAudioStreamOut; + +public: + A2dpAudioInterface(); + virtual ~A2dpAudioInterface(); + virtual status_t initCheck(); + virtual status_t standby(); + + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + // Temporary interface, do not use + // TODO: Replace with a more generic key:value get/set mechanism + virtual status_t setParameter(const char *key, const char *value); + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate, + status_t *status); + +protected: + virtual status_t doRouting(); + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + class A2dpAudioStreamOut : public AudioStreamOut { + public: + A2dpAudioStreamOut(); + virtual ~A2dpAudioStreamOut(); + status_t set(int format, + int channelCount, + uint32_t sampleRate); + virtual uint32_t sampleRate() const { return 44100; } + // must be 32-bit aligned - driver only seems to like 4800 + virtual size_t bufferSize() const { return 5120; } + virtual int channelCount() const { return 2; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 0; } + virtual status_t setVolume(float volume) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); + + private: + int mFd; + int mStartCount; + int mRetryCount; + void* mData; + bool mInitialized; + +#define kBufferSize 50000 + char mBuffer[kBufferSize]; + int mBufferRemaining; + }; + + Mutex mLock; + A2dpAudioStreamOut* mOutput; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // A2DP_AUDIO_HARDWARE_H diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk index a9cb303..d16e3e1 100644 --- a/libs/audioflinger/Android.mk +++ b/libs/audioflinger/Android.mk @@ -45,9 +45,12 @@ endif LOCAL_MODULE:= libaudioflinger -ifeq ($(TARGET_ARCH),arm) # not simulator - LOCAL_CFLAGS += -DWITH_BLUETOOTH +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_SRC_FILES += A2dpAudioInterface.cpp + LOCAL_SHARED_LIBRARIES += liba2dp + LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs) + LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils) endif include $(BUILD_SHARED_LIBRARY) diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp index 5ff2f18..8eee9cc 100644 --- a/libs/audioflinger/AudioDumpInterface.cpp +++ b/libs/audioflinger/AudioDumpInterface.cpp @@ -2,16 +2,16 @@ ** ** Copyright 2008, 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 +** 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 +** 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 +** 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. */ @@ -28,6 +28,8 @@ namespace android { +bool gFirst = true; // true if first write after a standby + // ---------------------------------------------------------------------------- AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) @@ -40,17 +42,25 @@ AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) } +AudioDumpInterface::~AudioDumpInterface() +{ + if(mFinalInterface) delete mFinalInterface; + if(mStreamOut) delete mStreamOut; +} + + status_t AudioDumpInterface::standby() { if(mStreamOut) mStreamOut->Close(); + gFirst = true; return mFinalInterface->standby(); } AudioStreamOut* AudioDumpInterface::openOutputStream( - int format, int channelCount, uint32_t sampleRate) + int format, int channelCount, uint32_t sampleRate, status_t *status) { - AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate); + AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate, status); if(outFinal) { mStreamOut = new AudioStreamOutDump(outFinal); @@ -69,13 +79,26 @@ AudioStreamOutDump::AudioStreamOutDump( AudioStreamOut* finalStream) mOutFile = 0; } + +AudioStreamOutDump::~AudioStreamOutDump() +{ + Close(); + delete mFinalStream; +} + ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) { ssize_t ret; - + ret = mFinalStream->write(buffer, bytes); - if(!mOutFile) { - mOutFile = fopen(FLINGER_DUMP_NAME, "ab"); + if(!mOutFile && gFirst) { + gFirst = false; + // check if dump file exist + mOutFile = fopen(FLINGER_DUMP_NAME, "r"); + if(mOutFile) { + fclose(mOutFile); + mOutFile = fopen(FLINGER_DUMP_NAME, "ab"); + } } if (mOutFile) { fwrite(buffer, bytes, 1, mOutFile); diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h index 732b97d..a65e56a 100644 --- a/libs/audioflinger/AudioDumpInterface.h +++ b/libs/audioflinger/AudioDumpInterface.h @@ -2,16 +2,16 @@ ** ** Copyright 2008, 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 +** 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 +** 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 +** 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. */ @@ -21,33 +21,35 @@ #include <stdint.h> #include <sys/types.h> -#include <hardware/AudioHardwareInterface.h> +#include <hardware/AudioHardwareBase.h> namespace android { -#define FLINGER_DUMP_NAME "/tmp/FlingerOut.pcm" // name of file used for dump +#define FLINGER_DUMP_NAME "/data/FlingerOut.pcm" // name of file used for dump class AudioStreamOutDump : public AudioStreamOut { public: AudioStreamOutDump( AudioStreamOut* FinalStream); + ~AudioStreamOutDump(); virtual ssize_t write(const void* buffer, size_t bytes); - + virtual uint32_t sampleRate() const { return mFinalStream->sampleRate(); } virtual size_t bufferSize() const { return mFinalStream->bufferSize(); } virtual int channelCount() const { return mFinalStream->channelCount(); } virtual int format() const { return mFinalStream->format(); } + virtual uint32_t latency() const { return mFinalStream->latency(); } virtual status_t setVolume(float volume) { return mFinalStream->setVolume(volume); } virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalStream->dump(fd, args); } void Close(void); private: - AudioStreamOut *mFinalStream; - FILE *mOutFile; // output file + AudioStreamOut *mFinalStream; + FILE *mOutFile; // output file }; -class AudioDumpInterface : public AudioHardwareInterface +class AudioDumpInterface : public AudioHardwareBase { public: @@ -56,10 +58,10 @@ public: virtual AudioStreamOut* openOutputStream( int format=0, int channelCount=0, - uint32_t sampleRate=0); + uint32_t sampleRate=0, + status_t *status=0); + virtual ~AudioDumpInterface(); - virtual ~AudioDumpInterface() - {delete mFinalInterface;} virtual status_t initCheck() {return mFinalInterface->initCheck();} virtual status_t setVoiceVolume(float volume) @@ -67,13 +69,6 @@ public: virtual status_t setMasterVolume(float volume) {return mFinalInterface->setMasterVolume(volume);} - virtual status_t setRouting(int mode, uint32_t routes) - {return mFinalInterface->setRouting(mode, routes);} - virtual status_t getRouting(int mode, uint32_t* routes) - {return mFinalInterface->getRouting(mode, routes);} - virtual status_t getMode(int* mode) - {return mFinalInterface->getMode(mode);} - // mic mute virtual status_t setMicMute(bool state) {return mFinalInterface->setMicMute(state);} @@ -83,17 +78,17 @@ public: virtual status_t setParameter(const char* key, const char* value) {return mFinalInterface->setParameter(key, value);} - virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate) - {return mFinalInterface->openInputStream( format, channelCount, sampleRate);} + virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate, status_t *status) + {return mFinalInterface->openInputStream( format, channelCount, sampleRate, status);} virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); } protected: - virtual status_t doRouting() {return 0;} - + virtual status_t doRouting() {return mFinalInterface->setRouting(mMode, mRoutes[mMode]);} + AudioHardwareInterface *mFinalInterface; AudioStreamOutDump *mStreamOut; - + }; }; // namespace android diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index fb21629..53b18ad 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -31,6 +31,8 @@ #include <utils/String16.h> #include <utils/threads.h> +#include <cutils/properties.h> + #include <media/AudioTrack.h> #include <media/AudioRecord.h> @@ -41,9 +43,13 @@ #include "AudioMixer.h" #include "AudioFlinger.h" +#ifdef WITH_A2DP +#include "A2dpAudioInterface.h" +#endif + namespace android { -static const nsecs_t kStandbyTimeInNsecs = seconds(3); +//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; @@ -93,8 +99,9 @@ static bool settingsAllowed() { AudioFlinger::AudioFlinger() : BnAudioFlinger(), Thread(false), - mMasterVolume(0), mMasterMute(true), - mAudioMixer(0), mAudioHardware(0), mOutput(0), mAudioRecordThread(0), + mMasterVolume(0), mMasterMute(true), mHardwareAudioMixer(0), mA2dpAudioMixer(0), + mAudioMixer(0), mAudioHardware(0), mA2dpAudioInterface(0), + mHardwareOutput(0), mA2dpOutput(0), mOutput(0), mAudioRecordThread(0), mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0), mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false), mInWrite(false) @@ -105,17 +112,14 @@ AudioFlinger::AudioFlinger() if (mAudioHardware->initCheck() == NO_ERROR) { // open 16-bit output stream for s/w mixer mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; - mOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT); + status_t status; + mHardwareOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); mHardwareStatus = AUDIO_HW_IDLE; - if (mOutput) { - mSampleRate = mOutput->sampleRate(); - mChannelCount = mOutput->channelCount(); - mFormat = mOutput->format(); - mMixBufferSize = mOutput->bufferSize(); - mFrameCount = mMixBufferSize / mChannelCount / sizeof(int16_t); - mMixBuffer = new int16_t[mFrameCount * mChannelCount]; - memset(mMixBuffer, 0, mMixBufferSize); - mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); + if (mHardwareOutput) { + mSampleRate = mHardwareOutput->sampleRate(); + mHardwareAudioMixer = new AudioMixer(getOutputFrameCount(mHardwareOutput), mSampleRate); + setOutput(mHardwareOutput); + // FIXME - this should come from settings setMasterVolume(1.0f); setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); @@ -124,20 +128,87 @@ AudioFlinger::AudioFlinger() setMode(AudioSystem::MODE_NORMAL); mMasterMute = false; } else { - LOGE("Failed to initialize output stream"); + LOGE("Failed to initialize output stream, status: %d", status); } - } else { + +#ifdef WITH_A2DP + // Create A2DP interface + mA2dpAudioInterface = new A2dpAudioInterface(); + mA2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + mA2dpAudioMixer = new AudioMixer(getOutputFrameCount(mA2dpOutput), mA2dpOutput->sampleRate()); + + // create a buffer big enough for both hardware and A2DP audio output. + size_t hwFrameCount = getOutputFrameCount(mHardwareOutput); + size_t a2dpFrameCount = getOutputFrameCount(mA2dpOutput); + size_t frameCount = (hwFrameCount > a2dpFrameCount ? hwFrameCount : a2dpFrameCount); +#else + size_t frameCount = getOutputFrameCount(mHardwareOutput); +#endif + // FIXME - Current mixer implementation only supports stereo output: Always + // Allocate a stereo buffer even if HW output is mono. + mMixBuffer = new int16_t[frameCount * 2]; + memset(mMixBuffer, 0, frameCount * 2 * sizeof(int16_t)); + + // 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!"); } + + char value[PROPERTY_VALUE_MAX]; + // FIXME: What property should this be??? + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + mMasterMute = true; + } } AudioFlinger::~AudioFlinger() { + if (mAudioRecordThread != 0) { + mAudioRecordThread->exit(); + mAudioRecordThread.clear(); + } delete mOutput; + delete mA2dpOutput; delete mAudioHardware; + delete mA2dpAudioInterface; delete [] mMixBuffer; - delete mAudioMixer; - mAudioRecordThread.clear(); + delete mHardwareAudioMixer; + delete mA2dpAudioMixer; +} + +void AudioFlinger::setOutput(AudioStreamOut* output) +{ + // lock on mOutputLock to prevent threadLoop() from starving us + Mutex::Autolock _l2(mOutputLock); + + // to synchronize with threadLoop() + Mutex::Autolock _l(mLock); + + if (mOutput != output) { + 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 = getOutputFrameCount(output); + + mAudioMixer = (output == mA2dpOutput ? mA2dpAudioMixer : mHardwareAudioMixer); + mOutput = output; + } +} + +size_t AudioFlinger::getOutputFrameCount(AudioStreamOut* output) +{ + return output->bufferSize() / output->channelCount() / sizeof(int16_t); } status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) @@ -201,8 +272,8 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) const size_t SIZE = 256; char buffer[SIZE]; String8 result; - - snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", audioMixer().trackNames()); + + snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", audioMixer()->trackNames()); result.append(buffer); snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); result.append(buffer); @@ -254,17 +325,26 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) // Thread virtuals bool AudioFlinger::threadLoop() { - nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2; unsigned long sleepTime = kBufferRecoveryInUsecs; - const size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t); int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; - size_t enabledTracks; + size_t enabledTracks = 0; nsecs_t standbyTime = systemTime(); + AudioMixer* mixer = 0; + size_t frameCount = 0; + int channelCount = 0; + uint32_t sampleRate = 0; + AudioStreamOut* output = 0; do { enabledTracks = 0; - { // scope for the lock + { // scope for the mLock + + // locking briefly on the secondary mOutputLock is necessary to avoid + // having this thread starve the thread that called setOutput() + mOutputLock.lock(); + mOutputLock.unlock(); + Mutex::Autolock _l(mLock); const SortedVector< wp<Track> >& activeTracks = mActiveTracks; @@ -286,6 +366,15 @@ bool AudioFlinger::threadLoop() continue; } + // get active mixer and output parameter while the lock is held and keep them + // consistent till the next loop. + + mixer = audioMixer(); + frameCount = mFrameCount; + channelCount = mChannelCount; + sampleRate = mSampleRate; + output = mOutput; + // find out which tracks need to be processed size_t count = activeTracks.size(); for (size_t i=0 ; i<count ; i++) { @@ -294,13 +383,11 @@ bool AudioFlinger::threadLoop() Track* const track = t.get(); audio_track_cblk_t* cblk = track->cblk(); - uint32_t u = cblk->user; - uint32_t s = cblk->server; // The first time a track is added we wait // for all its buffers to be filled before processing it - audioMixer().setActiveTrack(track->name()); - if ((u > s) && (track->isReady(u, s) || track->isStopped()) && + mixer->setActiveTrack(track->name()); + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && !track->isPaused()) { //LOGD("u=%08x, s=%08x [OK]", u, s); @@ -325,9 +412,8 @@ bool AudioFlinger::threadLoop() } // XXX: these things DON'T need to be done each time - AudioMixer& mixer(audioMixer()); - mixer.setBufferProvider(track); - mixer.enable(AudioMixer::MIXING); + mixer->setBufferProvider(track); + mixer->enable(AudioMixer::MIXING); int param; if ( track->mFillingUpStatus == Track::FS_FILLED) { @@ -342,15 +428,15 @@ bool AudioFlinger::threadLoop() } else { param = AudioMixer::RAMP_VOLUME; } - mixer.setParameter(param, AudioMixer::VOLUME0, left); - mixer.setParameter(param, AudioMixer::VOLUME1, right); - mixer.setParameter( + mixer->setParameter(param, AudioMixer::VOLUME0, left); + mixer->setParameter(param, AudioMixer::VOLUME1, right); + mixer->setParameter( AudioMixer::TRACK, AudioMixer::FORMAT, track->format()); - mixer.setParameter( + mixer->setParameter( AudioMixer::TRACK, AudioMixer::CHANNEL_COUNT, track->channelCount()); - mixer.setParameter( + mixer->setParameter( AudioMixer::RESAMPLE, AudioMixer::SAMPLE_RATE, int(cblk->sampleRate)); @@ -361,8 +447,7 @@ bool AudioFlinger::threadLoop() } else { //LOGD("u=%08x, s=%08x [NOT READY]", u, s); if (track->isStopped()) { - track->mFillingUpStatus = Track::FS_FILLING; - track->mFlags = 0; + track->reset(); } if (track->isTerminated() || track->isStopped() || track->isPaused()) { // We have consumed all the buffers of this track. @@ -378,7 +463,7 @@ bool AudioFlinger::threadLoop() } } // LOGV("disable(%d)", track->name()); - audioMixer().disable(AudioMixer::MIXING); + mixer->disable(AudioMixer::MIXING); } } @@ -390,26 +475,27 @@ bool AudioFlinger::threadLoop() mActiveTracks.remove(track); if (track->isTerminated()) { mTracks.remove(track); - audioMixer().deleteTrackName(track->mName); + mixer->deleteTrackName(track->mName); } } - } - } - + } + } if (LIKELY(enabledTracks)) { // mix buffers... - audioMixer().process(curBuf); + mixer->process(curBuf); // output audio to hardware mLastWriteTime = systemTime(); mInWrite = true; - mOutput->write(curBuf, mixBufferSize); + size_t mixBufferSize = frameCount*channelCount*sizeof(int16_t); + output->write(curBuf, mixBufferSize); mNumWrites++; mInWrite = false; mStandby = false; nsecs_t temp = systemTime(); standbyTime = temp + kStandbyTimeInNsecs; nsecs_t delta = temp - mLastWriteTime; + nsecs_t maxPeriod = seconds(frameCount) / sampleRate * 2; if (delta > maxPeriod) { LOGW("write blocked for %llu msecs", ns2ms(delta)); mNumDelayedWrites++; @@ -458,43 +544,60 @@ sp<IAudioTrack> AudioFlinger::createTrack( uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags) + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + status_t *status) { + sp<Track> track; + sp<TrackHandle> trackHandle; + sp<Client> client; + wp<Client> wclient; + status_t lStatus; + if (streamType >= AudioTrack::NUM_STREAM_TYPES) { LOGE("invalid stream type"); - return NULL; + lStatus = BAD_VALUE; + goto Exit; } - if (sampleRate > MAX_SAMPLE_RATE) { + // 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", sampleRate); - return NULL; + lStatus = BAD_VALUE; + goto Exit; } - sp<Track> track; - sp<TrackHandle> trackHandle; - Mutex::Autolock _l(mLock); + { + Mutex::Autolock _l(mLock); - if (mSampleRate == 0) { - LOGE("Audio driver not initialized."); - return trackHandle; - } + if (mSampleRate == 0) { + LOGE("Audio driver not initialized."); + lStatus = NO_INIT; + goto Exit; + } - sp<Client> client; - wp<Client> wclient = mClients.valueFor(pid); + wclient = mClients.valueFor(pid); - if (wclient != NULL) { - client = wclient.promote(); - } else { - client = new Client(this, pid); - mClients.add(pid, client); + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } + + track = new Track(this, client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer); + mTracks.add(track); + trackHandle = new TrackHandle(track); + + lStatus = NO_ERROR; } - // FIXME: Buffer size should be based on sample rate for consistent latency - track = new Track(this, client, streamType, sampleRate, format, - channelCount, bufferCount, channelCount == 1 ? mMixBufferSize>>1 : mMixBufferSize); - mTracks.add(track); - trackHandle = new TrackHandle(track); +Exit: + if(status) { + *status = lStatus; + } return trackHandle; } @@ -518,6 +621,16 @@ size_t AudioFlinger::frameCount() const return mFrameCount; } +uint32_t AudioFlinger::latency() const +{ + if (mOutput) { + return mOutput->latency(); + } + else { + return 0; + } +} + status_t AudioFlinger::setMasterVolume(float value) { // check calling permissions @@ -549,6 +662,21 @@ status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) return BAD_VALUE; } +#ifdef WITH_A2DP + LOGD("setRouting %d %d %d\n", mode, routes, mask); + if (mode == AudioSystem::MODE_NORMAL && + (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) { + if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) { + LOGD("set output to A2DP\n"); + setOutput(mA2dpOutput); + } else { + LOGD("set output to hardware audio\n"); + setOutput(mHardwareOutput); + } + LOGD("setOutput done\n"); + } +#endif + AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_GET_ROUTING; uint32_t r; @@ -656,7 +784,7 @@ status_t AudioFlinger::setStreamVolume(int stream, float value) if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { return BAD_VALUE; } - + mStreamTypes[stream].volume = value; status_t ret = NO_ERROR; if (stream == AudioTrack::VOICE_CALL) { @@ -750,6 +878,7 @@ status_t AudioFlinger::addTrack(const sp<Track>& track) // buffers before playing. This is to ensure the client will // effectively get the latency it requested. track->mFillingUpStatus = Track::FS_FILLING; + track->mResetDone = false; mActiveTracks.add(track); return NO_ERROR; } @@ -771,7 +900,7 @@ void AudioFlinger::remove_track_l(wp<Track> track, int name) if (t!=NULL) { t->reset(); } - audioMixer().deleteTrackName(name); + audioMixer()->deleteTrackName(name); mActiveTracks.remove(track); mWaitWorkCV.broadcast(); } @@ -789,7 +918,7 @@ void AudioFlinger::destroyTrack(const sp<Track>& track) if (mActiveTracks.indexOf(track) < 0) { LOGV("remove track (%d) and delete from mixer", track->name()); mTracks.remove(track); - audioMixer().deleteTrackName(keep->name()); + audioMixer()->deleteTrackName(keep->name()); } } @@ -823,38 +952,53 @@ AudioFlinger::TrackBase::TrackBase( uint32_t sampleRate, int format, int channelCount, - int bufferCount, - int bufferSize) + int frameCount, + const sp<IMemory>& sharedBuffer) : RefBase(), mAudioFlinger(audioFlinger), mClient(client), mStreamType(streamType), - mFormat(format), - mChannelCount(channelCount), - mBufferCount(bufferCount), - mFlags(0), - mBufferSize(bufferSize), + mFrameCount(0), mState(IDLE), - mClientTid(-1) + mClientTid(-1), + mFormat(format), + mFlags(0) { - mName = audioFlinger->audioMixer().getTrackName(); + mName = audioFlinger->audioMixer()->getTrackName(); 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) + bufferCount * bufferSize; + size_t size = sizeof(audio_track_cblk_t); + size_t bufferSize = frameCount*channelCount*sizeof(int16_t); + if (sharedBuffer == 0) { + size += bufferSize; + } + 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->size = bufferSize; + mCblk->frameCount = frameCount; mCblk->sampleRate = sampleRate; - mBuffers = (char*)mCblk + sizeof(audio_track_cblk_t); - memset(mBuffers, 0, bufferCount * bufferSize); + 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); @@ -873,15 +1017,16 @@ AudioFlinger::TrackBase::~TrackBase() void AudioFlinger::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) { buffer->raw = 0; - buffer->frameCount = 0; + mFrameCount = buffer->frameCount; step(); + buffer->frameCount = 0; } bool AudioFlinger::TrackBase::step() { bool result; audio_track_cblk_t* cblk = this->cblk(); - - result = cblk->stepServer(bufferCount()); + + result = cblk->stepServer(mFrameCount); if (!result) { LOGV("stepServer failed acquiring cblk mutex"); mFlags |= STEPSERVER_FAILED; @@ -894,7 +1039,10 @@ void AudioFlinger::TrackBase::reset() { cblk->user = 0; cblk->server = 0; - mFlags = 0; + cblk->userBase = 0; + cblk->serverBase = 0; + mFlags = 0; + LOGV("TrackBase::reset"); } sp<IMemory> AudioFlinger::TrackBase::getCblk() const @@ -906,6 +1054,27 @@ int AudioFlinger::TrackBase::sampleRate() const { return mCblk->sampleRate; } +int AudioFlinger::TrackBase::channelCount() const { + return mCblk->channels; +} + +void* AudioFlinger::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::Track::Track( @@ -915,13 +1084,14 @@ AudioFlinger::Track::Track( uint32_t sampleRate, int format, int channelCount, - int bufferCount, - int bufferSize) - : TrackBase(audioFlinger, client, streamType, sampleRate, format, channelCount, bufferCount, bufferSize) + int frameCount, + const sp<IMemory>& sharedBuffer) + : TrackBase(audioFlinger, client, streamType, sampleRate, format, channelCount, frameCount, sharedBuffer) { mVolume[0] = 1.0f; mVolume[1] = 1.0f; mMute = false; + mSharedBuffer = sharedBuffer; } AudioFlinger::Track::~Track() @@ -943,8 +1113,8 @@ void AudioFlinger::Track::dump(char* buffer, size_t size) mClient->pid(), mStreamType, mFormat, - mChannelCount, - mBufferCount, + mCblk->channels, + mFrameCount, mState, mMute, mFillingUpStatus, @@ -958,36 +1128,50 @@ void AudioFlinger::Track::dump(char* buffer, size_t size) status_t AudioFlinger::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) { audio_track_cblk_t* cblk = this->cblk(); - uint32_t u = cblk->user; - uint32_t s = cblk->server; - - // Check if last stepServer failed, try to step now + 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; } - if (LIKELY(u > s)) { - int index = s & audio_track_cblk_t::BUFFER_MASK; - buffer->raw = getBuffer(index); - buffer->frameCount = mAudioFlinger->frameCount(); - return NO_ERROR; + 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::Track::isReady(uint32_t u, int32_t s) const { +bool AudioFlinger::Track::isReady() const { if (mFillingUpStatus != FS_FILLING) return true; - const uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; - const uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK; - const uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; - const uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK; - if (u_seq > s_seq && u_buf == s_buf) { + + if (mCblk->framesReady() >= mCblk->frameCount || + mCblk->forceReady) { mFillingUpStatus = FS_FILLED; + mCblk->forceReady = 0; return true; } return false; @@ -1006,7 +1190,7 @@ void AudioFlinger::Track::stop() Mutex::Autolock _l(mAudioFlinger->mLock); if (mState > STOPPED) { mState = STOPPED; - // If the track is not active (PAUSED and buffers full), flush buffers + // If the track is not active (PAUSED and buffers full), flush buffers if (mAudioFlinger->mActiveTracks.indexOf(this) < 0) { reset(); } @@ -1038,15 +1222,24 @@ void AudioFlinger::Track::flush() // 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! + // as audio buffer => the AudioMixer code MUST always test that pointer + // returned by getNextBuffer() is not NULL! reset(); } void AudioFlinger::Track::reset() { - TrackBase::reset(); - mFillingUpStatus = FS_FILLING; + // 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::Track::mute(bool muted) @@ -1112,26 +1305,15 @@ status_t AudioFlinger::TrackHandle::onTransact( // ---------------------------------------------------------------------------- -sp<AudioFlinger::AudioRecordThread> AudioFlinger::audioRecordThread() -{ - Mutex::Autolock _l(mLock); - return mAudioRecordThread; -} - -void AudioFlinger::endRecord() -{ - Mutex::Autolock _l(mLock); - mAudioRecordThread.clear(); -} - sp<IAudioRecord> AudioFlinger::openRecord( pid_t pid, int streamType, uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags) + int frameCount, + uint32_t flags, + status_t *status) { sp<AudioRecordThread> thread; sp<RecordTrack> recordTrack; @@ -1139,46 +1321,46 @@ sp<IAudioRecord> AudioFlinger::openRecord( 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 (mSampleRate == 0) { LOGE("Audio driver not initialized"); + lStatus = NO_INIT; goto Exit; } - // Create audio thread - take mutex to prevent race condition - { - Mutex::Autolock _l(mLock); - if (mAudioRecordThread != 0) { - LOGE("Record channel already open"); - goto Exit; - } - thread = new AudioRecordThread(this); - mAudioRecordThread = thread; - } - // It's safe to release the mutex here since the client doesn't get a - // handle until we return from this call - - // open driver, initialize h/w - input = mAudioHardware->openInputStream( - AudioSystem::PCM_16_BIT, channelCount, sampleRate); - if (!input) { - LOGE("Error opening input stream"); - mAudioRecordThread.clear(); + 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; } @@ -1194,37 +1376,38 @@ sp<IAudioRecord> AudioFlinger::openRecord( } } + // 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 RecordTrack(this, client, streamType, sampleRate, - format, channelCount, bufferCount, input->bufferSize()); - - // spin up record thread - thread->open(recordTrack, input); - thread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO); + format, channelCount, frameCount); // return to handle to client recordHandle = new RecordHandle(recordTrack); + lStatus = NO_ERROR; Exit: + if (status) { + *status = lStatus; + } return recordHandle; } -status_t AudioFlinger::startRecord() { - sp<AudioRecordThread> t = audioRecordThread(); - if (t == 0) return NO_INIT; - return t->start(); +status_t AudioFlinger::startRecord(RecordTrack* recordTrack) { + if (mAudioRecordThread != 0) { + return mAudioRecordThread->start(recordTrack); + } + return NO_INIT; } -void AudioFlinger::stopRecord() { - sp<AudioRecordThread> t = audioRecordThread(); - if (t != 0) t->stop(); +void AudioFlinger::stopRecord(RecordTrack* recordTrack) { + if (mAudioRecordThread != 0) { + mAudioRecordThread->stop(recordTrack); + } } -void AudioFlinger::exitRecord() -{ - sp<AudioRecordThread> t = audioRecordThread(); - if (t != 0) t->exit(); -} // ---------------------------------------------------------------------------- @@ -1235,55 +1418,69 @@ AudioFlinger::RecordTrack::RecordTrack( uint32_t sampleRate, int format, int channelCount, - int bufferCount, - int bufferSize) + int frameCount) : TrackBase(audioFlinger, client, streamType, sampleRate, format, - channelCount, bufferCount, bufferSize), + channelCount, frameCount, 0), mOverflow(false) { } AudioFlinger::RecordTrack::~RecordTrack() { - mAudioFlinger->audioMixer().deleteTrackName(mName); - mAudioFlinger->exitRecord(); + mAudioFlinger->audioMixer()->deleteTrackName(mName); } status_t AudioFlinger::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) { - audio_track_cblk_t* cblk = this->cblk(); - const uint32_t u_seq = cblk->user & audio_track_cblk_t::SEQUENCE_MASK; - const uint32_t u_buf = cblk->user & audio_track_cblk_t::BUFFER_MASK; - const uint32_t s_seq = cblk->server & audio_track_cblk_t::SEQUENCE_MASK; - const uint32_t s_buf = cblk->server & audio_track_cblk_t::BUFFER_MASK; - - // 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; - } + 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; + } - if (LIKELY(s_seq == u_seq || s_buf != u_buf)) { - buffer->raw = getBuffer(s_buf); - buffer->frameCount = mAudioFlinger->frameCount(); - return NO_ERROR; - } + framesAvail = cblk->framesAvailable_l(); -getNextBuffer_exit: - buffer->raw = 0; - buffer->frameCount = 0; - return NOT_ENOUGH_DATA; + 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::RecordTrack::start() { - return mAudioFlinger->startRecord(); + return mAudioFlinger->startRecord(this); } void AudioFlinger::RecordTrack::stop() { - mAudioFlinger->stopRecord(); + mAudioFlinger->stopRecord(this); + TrackBase::reset(); + // Force overerrun condition to avoid false overrun callback until first data is + // read from buffer + mCblk->flowControlFlag = 1; } // ---------------------------------------------------------------------------- @@ -1294,7 +1491,9 @@ AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordTrack>& re { } -AudioFlinger::RecordHandle::~RecordHandle() {} +AudioFlinger::RecordHandle::~RecordHandle() { + stop(); +} status_t AudioFlinger::RecordHandle::start() { LOGV("RecordHandle::start()"); @@ -1318,10 +1517,8 @@ status_t AudioFlinger::RecordHandle::onTransact( // ---------------------------------------------------------------------------- -AudioFlinger::AudioRecordThread::AudioRecordThread(const sp<AudioFlinger>& audioFlinger) : - mAudioFlinger(audioFlinger), - mRecordTrack(0), - mInput(0), +AudioFlinger::AudioRecordThread::AudioRecordThread(AudioHardwareInterface* audioHardware) : + mAudioHardware(audioHardware), mActive(false) { } @@ -1333,108 +1530,123 @@ 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(); + mWaitWorkCV.wait(mLock); + LOGV("AudioRecordThread: loop starting"); + if (mRecordTrack != 0) { + input = mAudioHardware->openInputStream(mRecordTrack->format(), + mRecordTrack->channelCount(), + mRecordTrack->sampleRate(), + &mStartStatus); + 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 { - // promote strong ref so track isn't deleted while we access it - sp<RecordTrack> t = mRecordTrack.promote(); + } else if (mRecordTrack != 0){ - // if we lose the weak reference, client is gone. - if (t == 0) { - LOGV("AudioRecordThread: client deleted track"); - break; - } - - if (LIKELY(t->getNextBuffer(&mBuffer) == NO_ERROR)) { - if (mInput->read(mBuffer.raw, t->mBufferSize) < 0) { + buffer.frameCount = inFrameCount; + if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR)) { + LOGV("AudioRecordThread read: %d frames", buffer.frameCount); + if (input->read(buffer.raw, inBufferSize) < 0) { LOGE("Error reading audio input"); sleep(1); } - t->releaseBuffer(&mBuffer); + mRecordTrack->releaseBuffer(&buffer); + mRecordTrack->overflow(); } // client isn't retrieving buffers fast enough else { - if (!t->setOverflow()) + 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); } } - }; - - // close hardware - close(); + } - // delete this object - no more data references after this call - mAudioFlinger->endRecord(); - return false; -} -status_t AudioFlinger::AudioRecordThread::open(const sp<RecordTrack>& recordTrack, AudioStreamIn *input) { - LOGV("AudioRecordThread::open"); - // check for record channel already open - AutoMutex lock(&mLock); - if (mRecordTrack != NULL) { - LOGE("Record channel already open"); - return ALREADY_EXISTS; + if (input) { + delete input; } - mRecordTrack = recordTrack; - mInput = input; - return NO_ERROR; + mRecordTrack.clear(); + + return false; } -status_t AudioFlinger::AudioRecordThread::start() +status_t AudioFlinger::AudioRecordThread::start(RecordTrack* recordTrack) { LOGV("AudioRecordThread::start"); AutoMutex lock(&mLock); - if (mActive) return -EBUSY; + 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; - sp<RecordTrack> t = mRecordTrack.promote(); - if (t == 0) return UNKNOWN_ERROR; + mRecordTrack = recordTrack; // signal thread to start LOGV("Signal record thread"); - mActive = true; mWaitWorkCV.signal(); - return NO_ERROR; + mWaitWorkCV.wait(mLock); + LOGV("Record started, status %d", mStartStatus); + return mStartStatus; } -void AudioFlinger::AudioRecordThread::stop() { +void AudioFlinger::AudioRecordThread::stop(RecordTrack* recordTrack) { LOGV("AudioRecordThread::stop"); AutoMutex lock(&mLock); - if (mActive) { + if (mActive && (recordTrack == mRecordTrack.get())) { mActive = false; - mWaitWorkCV.signal(); } } void AudioFlinger::AudioRecordThread::exit() { LOGV("AudioRecordThread::exit"); - AutoMutex lock(&mLock); - requestExit(); - mWaitWorkCV.signal(); + { + AutoMutex lock(&mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); } -status_t AudioFlinger::AudioRecordThread::close() -{ - LOGV("AudioRecordThread::close"); - AutoMutex lock(&mLock); - if (!mInput) return NO_INIT; - delete mInput; - mInput = 0; - return NO_ERROR; -} - status_t AudioFlinger::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 8c02617..d9f7b49 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -43,13 +43,17 @@ class audio_track_cblk_t; class AudioMixer; class AudioBuffer; + // ---------------------------------------------------------------------------- #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + // ---------------------------------------------------------------------------- +static const nsecs_t kStandbyTimeInNsecs = seconds(3); + class AudioFlinger : public BnAudioFlinger, protected Thread { public: @@ -69,13 +73,16 @@ public: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags); + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + status_t *status); virtual uint32_t sampleRate() const; virtual int channelCount() const; virtual int format() const; virtual size_t frameCount() const; + virtual size_t latency() const; virtual status_t setMasterVolume(float value); virtual status_t setMasterMute(bool muted); @@ -128,8 +135,9 @@ public: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - uint32_t flags); + int frameCount, + uint32_t flags, + status_t *status); virtual status_t onTransact( uint32_t code, @@ -141,12 +149,15 @@ private: AudioFlinger(); virtual ~AudioFlinger(); + void setOutput(AudioStreamOut* output); + size_t getOutputFrameCount(AudioStreamOut* output); + // Internal dump utilites. status_t dumpPermissionDenial(int fd, const Vector<String16>& args); status_t dumpClients(int fd, const Vector<String16>& args); status_t dumpTracks(int fd, const Vector<String16>& args); status_t dumpInternals(int fd, const Vector<String16>& args); - + // --- Client --- class Client : public RefBase { public: @@ -183,17 +194,17 @@ private: }; enum track_flags { - STEPSERVER_FAILED = 0x01 // StepServer could not acquire cblk->lock mutex + STEPSERVER_FAILED = 0x01 // StepServer could not acquire cblk->lock mutex }; - + TrackBase( const sp<AudioFlinger>& audioFlinger, const sp<Client>& client, int streamType, uint32_t sampleRate, int format, int channelCount, - int bufferCount, - int bufferSize); + int frameCount, + const sp<IMemory>& sharedBuffer); ~TrackBase(); virtual status_t start() = 0; @@ -203,6 +214,7 @@ private: protected: friend class AudioFlinger; friend class RecordHandle; + friend class AudioRecordThread; TrackBase(const TrackBase&); TrackBase& operator = (const TrackBase&); @@ -222,19 +234,11 @@ private: return mFormat; } - int channelCount() const { - return mChannelCount; - } - - int bufferCount() const { - return mBufferCount; - } + int channelCount() const ; int sampleRate() const; - void* getBuffer(int n) const { - return (char*)mBuffers + n * mBufferSize; - } + void* getBuffer(uint32_t offset, uint32_t frames) const; int name() const { return mName; @@ -256,16 +260,15 @@ private: sp<IMemory> mCblkMemory; audio_track_cblk_t* mCblk; int mStreamType; - uint8_t mFormat; - uint8_t mChannelCount; - uint8_t mBufferCount; - uint8_t mFlags; - void* mBuffers; - size_t mBufferSize; + void* mBuffer; + void* mBufferEnd; + uint32_t mFrameCount; int mName; // we don't really need a lock for these int mState; int mClientTid; + uint8_t mFormat; + uint8_t mFlags; }; // playback track @@ -277,8 +280,8 @@ private: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - int bufferSize); + int frameCount, + const sp<IMemory>& sharedBuffer); ~Track(); void dump(char* buffer, size_t size); @@ -312,7 +315,7 @@ private: return mState == PAUSED; } - bool isReady(uint32_t u, int32_t s) const; + bool isReady() const; void setPaused() { mState = PAUSED; } void reset(); @@ -324,6 +327,8 @@ private: enum {FS_FILLING, FS_FILLED, FS_ACTIVE}; mutable uint8_t mFillingUpStatus; int8_t mRetryCount; + sp<IMemory> mSharedBuffer; + bool mResetDone; }; // end of Track friend class AudioBuffer; @@ -366,8 +371,8 @@ private: void remove_track_l(wp<Track> track, int name); void destroyTrack(const sp<Track>& track); - AudioMixer& audioMixer() { - return *mAudioMixer; + AudioMixer* audioMixer() { + return mAudioMixer; } // record track @@ -379,8 +384,7 @@ private: uint32_t sampleRate, int format, int channelCount, - int bufferCount, - int bufferSize); + int frameCount); ~RecordTrack(); virtual status_t start(); @@ -419,43 +423,34 @@ private: class AudioRecordThread : public Thread { public: - AudioRecordThread(const sp<AudioFlinger>& audioFlinger); + AudioRecordThread(AudioHardwareInterface* audioHardware); virtual ~AudioRecordThread(); virtual bool threadLoop(); virtual status_t readyToRun() { return NO_ERROR; } virtual void onFirstRef() {} - status_t open(const sp<RecordTrack>& recordTrack, AudioStreamIn *input); - status_t start(); - void stop(); - status_t close(); + status_t start(RecordTrack* recordTrack); + void stop(RecordTrack* recordTrack); void exit(); - - bool isOpen() { return bool(mRecordTrack != NULL); } private: AudioRecordThread(); - sp<AudioFlinger> mAudioFlinger; - wp<RecordTrack> mRecordTrack; - AudioStreamIn* mInput; + AudioHardwareInterface *mAudioHardware; + sp<RecordTrack> mRecordTrack; Mutex mLock; Condition mWaitWorkCV; - AudioBufferProvider::Buffer mBuffer; volatile bool mActive; + status_t mStartStatus; }; friend class AudioRecordThread; - sp<AudioRecordThread> audioRecordThread(); - void endRecord(); - status_t startRecord(); - void stopRecord(); - void exitRecord(); - - AudioHardwareInterface* audioHardware() { return mAudioHardware; } + status_t startRecord(RecordTrack* recordTrack); + void stopRecord(RecordTrack* recordTrack); mutable Mutex mHardwareLock; mutable Mutex mLock; + mutable Mutex mOutputLock; mutable Condition mWaitWorkCV; DefaultKeyedVector< pid_t, wp<Client> > mClients; SortedVector< wp<Track> > mActiveTracks; @@ -465,15 +460,19 @@ private: bool mMasterMute; stream_type_t mStreamTypes[AudioTrack::NUM_STREAM_TYPES]; + AudioMixer* mHardwareAudioMixer; + AudioMixer* mA2dpAudioMixer; AudioMixer* mAudioMixer; AudioHardwareInterface* mAudioHardware; + AudioHardwareInterface* mA2dpAudioInterface; + AudioStreamOut* mHardwareOutput; + AudioStreamOut* mA2dpOutput; AudioStreamOut* mOutput; sp<AudioRecordThread> mAudioRecordThread; uint32_t mSampleRate; size_t mFrameCount; int mChannelCount; int mFormat; - int mMixBufferSize; int16_t* mMixBuffer; mutable int mHardwareStatus; nsecs_t mLastWriteTime; diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp index b1e5b7f..e6a163b 100644 --- a/libs/audioflinger/AudioHardwareGeneric.cpp +++ b/libs/audioflinger/AudioHardwareGeneric.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -68,16 +68,25 @@ status_t AudioHardwareGeneric::standby() } AudioStreamOut* AudioHardwareGeneric::openOutputStream( - int format, int channelCount, uint32_t sampleRate) + int format, int channelCount, uint32_t sampleRate, status_t *status) { AutoMutex lock(mLock); // only one output stream allowed - if (mOutput) return 0; + if (mOutput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } // create new output stream AudioStreamOutGeneric* out = new AudioStreamOutGeneric(); - if (out->set(this, mFd, format, channelCount, sampleRate) == NO_ERROR) { + status_t lStatus = out->set(this, mFd, format, channelCount, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { mOutput = out; } else { delete out; @@ -90,16 +99,25 @@ void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) { } AudioStreamIn* AudioHardwareGeneric::openInputStream( - int format, int channelCount, uint32_t sampleRate) + int format, int channelCount, uint32_t sampleRate, status_t *status) { AutoMutex lock(mLock); // only one input stream allowed - if (mInput) return 0; + if (mInput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } // create new output stream AudioStreamInGeneric* in = new AudioStreamInGeneric(); - if (in->set(this, mFd, format, channelCount, sampleRate) == NO_ERROR) { + status_t lStatus = in->set(this, mFd, format, channelCount, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { mInput = in; } else { delete in; diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h index 10cc45d..a2342cd 100644 --- a/libs/audioflinger/AudioHardwareGeneric.h +++ b/libs/audioflinger/AudioHardwareGeneric.h @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -23,7 +23,7 @@ #include <utils/threads.h> -#include <hardware/AudioHardwareInterface.h> +#include <hardware/AudioHardwareBase.h> namespace android { @@ -47,6 +47,7 @@ public: virtual size_t bufferSize() const { return 4096; } virtual int channelCount() const { return 2; } virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 0; } virtual status_t setVolume(float volume) { return INVALID_OPERATION; } virtual ssize_t write(const void* buffer, size_t bytes); virtual status_t dump(int fd, const Vector<String16>& args); @@ -76,6 +77,7 @@ public: virtual status_t setGain(float gain) { return INVALID_OPERATION; } virtual ssize_t read(void* buffer, ssize_t bytes); virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby() { return NO_ERROR; } private: AudioHardwareGeneric *mAudioHardware; @@ -84,7 +86,7 @@ private: }; -class AudioHardwareGeneric : public AudioHardwareInterface +class AudioHardwareGeneric : public AudioHardwareBase { public: AudioHardwareGeneric(); @@ -105,12 +107,14 @@ public: virtual AudioStreamOut* openOutputStream( int format=0, int channelCount=0, - uint32_t sampleRate=0); + uint32_t sampleRate=0, + status_t *status=0); virtual AudioStreamIn* openInputStream( int format, int channelCount, - uint32_t sampleRate); + uint32_t sampleRate, + status_t *status); void closeOutputStream(AudioStreamOutGeneric* out); void closeInputStream(AudioStreamInGeneric* in); @@ -120,7 +124,7 @@ protected: private: status_t dumpInternals(int fd, const Vector<String16>& args); - + Mutex mLock; AudioStreamOutGeneric *mOutput; AudioStreamInGeneric *mInput; diff --git a/libs/audioflinger/AudioHardwareInterface.cpp b/libs/audioflinger/AudioHardwareInterface.cpp index 7387b3d..ac76a19 100644 --- a/libs/audioflinger/AudioHardwareInterface.cpp +++ b/libs/audioflinger/AudioHardwareInterface.cpp @@ -26,7 +26,7 @@ #include "AudioHardwareStub.h" #include "AudioHardwareGeneric.h" -// #define DUMP_FLINGER_OUT // if defined allows recording samples in a file +//#define DUMP_FLINGER_OUT // if defined allows recording samples in a file #ifdef DUMP_FLINGER_OUT #include "AudioDumpInterface.h" #endif @@ -54,6 +54,7 @@ static const char* routeStrings[] = "SPEAKER ", "BLUETOOTH ", "HEADSET " + "BLUETOOTH_A2DP " }; static const char* routeNone = "NONE"; @@ -115,24 +116,10 @@ AudioHardwareInterface* AudioHardwareInterface::create() // This code adds a record of buffers in a file to write calls made by AudioFlinger. // It replaces the current AudioHardwareInterface object by an intermediate one which // will record buffers in a file (after sending them to hardware) for testing purpose. - // This feature is enabled by defining symbol DUMP_FLINGER_OUT and setting environement - // "audioflinger.dump = 1". The output file is "tmp/FlingerOut.pcm". Pause are not recorded - // in the file. + // This feature is enabled by defining symbol DUMP_FLINGER_OUT. + // The output file is FLINGER_DUMP_NAME. Pause are not recorded in the file. - // read dump mode - property_get("audioflinger.dump", value, "0"); - switch(value[0]) { - case '1': - LOGV("Dump mode"); - hw = new AudioDumpInterface(hw); // replace interface - return hw; - break; - case '0': - default: - LOGV("No Dump mode"); - return hw; - break; - } + hw = new AudioDumpInterface(hw); // replace interface #endif return hw; } @@ -143,7 +130,7 @@ AudioStreamOut::~AudioStreamOut() AudioStreamIn::~AudioStreamIn() {} -AudioHardwareInterface::AudioHardwareInterface() +AudioHardwareBase::AudioHardwareBase() { // force a routing update on initialization memset(&mRoutes, 0, sizeof(mRoutes)); @@ -151,7 +138,7 @@ AudioHardwareInterface::AudioHardwareInterface() } // generics for audio routing - the real work is done in doRouting -status_t AudioHardwareInterface::setRouting(int mode, uint32_t routes) +status_t AudioHardwareBase::setRouting(int mode, uint32_t routes) { #if LOG_ROUTING_CALLS LOGD("setRouting: mode=%s, routes=[%s]", displayMode(mode), displayRoutes(routes)); @@ -173,7 +160,7 @@ status_t AudioHardwareInterface::setRouting(int mode, uint32_t routes) return doRouting(); } -status_t AudioHardwareInterface::getRouting(int mode, uint32_t* routes) +status_t AudioHardwareBase::getRouting(int mode, uint32_t* routes) { if (mode == AudioSystem::MODE_CURRENT) mode = mMode; @@ -187,7 +174,7 @@ status_t AudioHardwareInterface::getRouting(int mode, uint32_t* routes) return NO_ERROR; } -status_t AudioHardwareInterface::setMode(int mode) +status_t AudioHardwareBase::setMode(int mode) { #if LOG_ROUTING_CALLS LOGD("setMode(%s)", displayMode(mode)); @@ -204,25 +191,45 @@ status_t AudioHardwareInterface::setMode(int mode) return doRouting(); } -status_t AudioHardwareInterface::getMode(int* mode) +status_t AudioHardwareBase::getMode(int* mode) { // Implement: set audio routing *mode = mMode; return NO_ERROR; } -status_t AudioHardwareInterface::setParameter(const char* key, const char* value) +status_t AudioHardwareBase::setParameter(const char* key, const char* value) { // default implementation is to ignore return NO_ERROR; } -status_t AudioHardwareInterface::dumpState(int fd, const Vector<String16>& args) + +// default implementation +size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + if (sampleRate != 8000) { + LOGW("getInputBufferSize bad sampling rate: %d", sampleRate); + return 0; + } + if (format != AudioSystem::PCM_16_BIT) { + LOGW("getInputBufferSize bad format: %d", format); + return 0; + } + if (channelCount != 1) { + LOGW("getInputBufferSize bad channel count: %d", channelCount); + return 0; + } + + return 320; +} + +status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "AudioHardwareInterface::dumpState\n"); + snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n"); result.append(buffer); snprintf(buffer, SIZE, "\tmMode: %d\n", mMode); result.append(buffer); diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp index 0046db8..d309902 100644 --- a/libs/audioflinger/AudioHardwareStub.cpp +++ b/libs/audioflinger/AudioHardwareStub.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -47,20 +47,28 @@ status_t AudioHardwareStub::standby() } AudioStreamOut* AudioHardwareStub::openOutputStream( - int format, int channelCount, uint32_t sampleRate) + int format, int channelCount, uint32_t sampleRate, status_t *status) { AudioStreamOutStub* out = new AudioStreamOutStub(); - if (out->set(format, channelCount, sampleRate) == NO_ERROR) + status_t lStatus = out->set(format, channelCount, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) return out; delete out; return 0; } AudioStreamIn* AudioHardwareStub::openInputStream( - int format, int channelCount, uint32_t sampleRate) + int format, int channelCount, uint32_t sampleRate, status_t *status) { AudioStreamInStub* in = new AudioStreamInStub(); - if (in->set(format, channelCount, sampleRate) == NO_ERROR) + status_t lStatus = in->set(format, channelCount, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) return in; delete in; return 0; @@ -102,7 +110,7 @@ status_t AudioStreamOutStub::set(int format, int channels, uint32_t rate) if (format == 0) format = AudioSystem::PCM_16_BIT; if (channels == 0) channels = channelCount(); if (rate == 0) rate = sampleRate(); - + if ((format == AudioSystem::PCM_16_BIT) && (channels == channelCount()) && (rate == sampleRate())) @@ -129,7 +137,7 @@ status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args) snprintf(buffer, SIZE, "\tformat: %d\n", format()); result.append(buffer); ::write(fd, result.string(), result.size()); - return NO_ERROR; + return NO_ERROR; } // ---------------------------------------------------------------------------- diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h index 1a61552..5316d60 100644 --- a/libs/audioflinger/AudioHardwareStub.h +++ b/libs/audioflinger/AudioHardwareStub.h @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -21,7 +21,7 @@ #include <stdint.h> #include <sys/types.h> -#include <hardware/AudioHardwareInterface.h> +#include <hardware/AudioHardwareBase.h> namespace android { @@ -34,6 +34,7 @@ public: virtual size_t bufferSize() const { return 4096; } virtual int channelCount() const { return 2; } virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 0; } virtual status_t setVolume(float volume) { return NO_ERROR; } virtual ssize_t write(const void* buffer, size_t bytes); virtual status_t dump(int fd, const Vector<String16>& args); @@ -49,9 +50,10 @@ public: virtual status_t setGain(float gain) { return NO_ERROR; } virtual ssize_t read(void* buffer, ssize_t bytes); virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby() { return NO_ERROR; } }; -class AudioHardwareStub : public AudioHardwareInterface +class AudioHardwareStub : public AudioHardwareBase { public: AudioHardwareStub(); @@ -72,12 +74,14 @@ public: virtual AudioStreamOut* openOutputStream( int format=0, int channelCount=0, - uint32_t sampleRate=0); + uint32_t sampleRate=0, + status_t *status=0); virtual AudioStreamIn* openInputStream( int format, int channelCount, - uint32_t sampleRate); + uint32_t sampleRate, + status_t *status); protected: virtual status_t doRouting() { return NO_ERROR; } diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp index 9f1b17f..b03467f 100644 --- a/libs/audioflinger/AudioMixer.cpp +++ b/libs/audioflinger/AudioMixer.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -247,8 +247,8 @@ inline void AudioMixer::track_t::adjustVolumeRamp() { for (int i=0 ; i<2 ; i++) { - if (((volumeInc[i]>0) && ((prevVolume[i]>>16) >= volume[i])) || - ((volumeInc[i]<0) && ((prevVolume[i]>>16) <= volume[i]))) { + if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || + ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) { volumeInc[i] = 0; prevVolume[i] = volume[i]<<16; } @@ -307,7 +307,7 @@ void AudioMixer::process__validate(state_t* state, void* output) n |= NEEDS_CHANNEL_1 + t.channelCount - 1; n |= NEEDS_FORMAT_16; n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED; - + if (t.volumeInc[0]|t.volumeInc[1]) { volumeRamp = 1; } else if (!t.doesResample() && t.volumeRL == 0) { @@ -370,7 +370,7 @@ void AudioMixer::process__validate(state_t* state, void* output) state->hook(state, output); - // Now that the volume ramp has been done, set optimal state and + // Now that the volume ramp has been done, set optimal state and // track hooks for subsequent mixer process if (countActiveTracks) { int allMuted = 1; @@ -397,7 +397,7 @@ void AudioMixer::process__validate(state_t* state, void* output) } } -static inline +static inline int32_t mulAdd(int16_t in, int16_t v, int32_t a) { #if defined(__arm__) && !defined(__thumb__) @@ -412,7 +412,7 @@ int32_t mulAdd(int16_t in, int16_t v, int32_t a) #endif } -static inline +static inline int32_t mul(int16_t in, int16_t v) { #if defined(__arm__) && !defined(__thumb__) @@ -427,7 +427,7 @@ int32_t mul(int16_t in, int16_t v) #endif } -static inline +static inline int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a) { #if defined(__arm__) && !defined(__thumb__) @@ -453,7 +453,7 @@ int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a) #endif } -static inline +static inline int32_t mulRL(int left, uint32_t inRL, uint32_t vRL) { #if defined(__arm__) && !defined(__thumb__) @@ -513,7 +513,7 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i //LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], // (vl + vlInc*frameCount)/65536.0f, frameCount); - + // ramp volume do { *out++ += (vl >> 16) * (*temp++ >> 12); @@ -548,7 +548,7 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount vl += vlInc; vr += vrInc; } while (--frameCount); - + t->prevVolume[0] = vl; t->prevVolume[1] = vr; t->adjustVolumeRamp(); @@ -590,7 +590,7 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, vl += vlInc; vr += vrInc; } while (--frameCount); - + t->prevVolume[0] = vl; t->prevVolume[1] = vr; t->adjustVolumeRamp(); @@ -609,7 +609,7 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, t->in = in; } -inline +inline void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) { for (size_t i=0 ; i<c ; i++) { @@ -633,8 +633,12 @@ void AudioMixer::process__nop(state_t* state, void* output) const int i = 31 - __builtin_clz(en); en &= ~(1<<i); track_t& t = state->tracks[i]; - t.bufferProvider->getNextBuffer(&t.buffer); - if (t.buffer.raw) { + size_t outFrames = state->frameCount; + while (outFrames) { + t.buffer.frameCount = outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + if (!t.buffer.raw) break; + outFrames -= t.buffer.frameCount; t.bufferProvider->releaseBuffer(&t.buffer); } } @@ -652,12 +656,14 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output) const int i = 31 - __builtin_clz(en); en &= ~(1<<i); track_t& t = state->tracks[i]; + t.buffer.frameCount = state->frameCount; t.bufferProvider->getNextBuffer(&t.buffer); + t.frameCount = t.buffer.frameCount; t.in = t.buffer.raw; // t.in == NULL can happen if the track was flushed just after having // been enabled for mixing. if (t.in == NULL) - enabledTracks &= ~(1<<i); + enabledTracks &= ~(1<<i); } // this assumes output 16 bits stereo, no resampling @@ -671,12 +677,31 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output) const int i = 31 - __builtin_clz(en); en &= ~(1<<i); track_t& t = state->tracks[i]; - (t.hook)(&t, outTemp, BLOCKSIZE, state->resampleTemp); + size_t outFrames = BLOCKSIZE; + + while (outFrames) { + size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; + if (inFrames) { + (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp); + t.frameCount -= inFrames; + outFrames -= inFrames; + } + if (t.frameCount == 0 && outFrames) { + t.bufferProvider->releaseBuffer(&t.buffer); + t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames); + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + if (t.in == NULL) { + enabledTracks &= ~(1<<i); + break; + } + t.frameCount = t.buffer.frameCount; + } + } } ditherAndClamp(out, outTemp, BLOCKSIZE); out += BLOCKSIZE; - numFrames -= BLOCKSIZE; } while (numFrames); @@ -713,12 +738,19 @@ void AudioMixer::process__genericResampling(state_t* state, void* output) if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { (t.hook)(&t, outTemp, numFrames, state->resampleTemp); } else { - t.bufferProvider->getNextBuffer(&t.buffer); - t.in = t.buffer.raw; - // t.in == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (t.in) { - (t.hook)(&t, outTemp, numFrames, state->resampleTemp); + + size_t outFrames = numFrames; + + while (outFrames) { + t.buffer.frameCount = outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + // t.in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == NULL) break; + + (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp); + outFrames -= t.buffer.frameCount; t.bufferProvider->releaseBuffer(&t.buffer); } } @@ -734,45 +766,51 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* const track_t& t = state->tracks[i]; AudioBufferProvider::Buffer& b(t.buffer); - t.bufferProvider->getNextBuffer(&b); - int16_t const *in = t.buffer.i16; - - // in == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (in == NULL) { - memset(output, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t)); - return; - } - + int32_t* out = static_cast<int32_t*>(output); size_t numFrames = state->frameCount; + const int16_t vl = t.volume[0]; const int16_t vr = t.volume[1]; const uint32_t vrl = t.volumeRL; - if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { - // volume is boosted, so we might need to clamp even though - // we process only one track. - do { - uint32_t rl = *reinterpret_cast<uint32_t const *>(in); - in += 2; - int32_t l = mulRL(1, rl, vrl) >> 12; - int32_t r = mulRL(0, rl, vrl) >> 12; - // clamping... - l = clamp16(l); - r = clamp16(r); - *out++ = (r<<16) | (l & 0xFFFF); - } while (--numFrames); - } else { - do { - uint32_t rl = *reinterpret_cast<uint32_t const *>(in); - in += 2; - int32_t l = mulRL(1, rl, vrl) >> 12; - int32_t r = mulRL(0, rl, vrl) >> 12; - *out++ = (r<<16) | (l & 0xFFFF); - } while (--numFrames); - } + while (numFrames) { + b.frameCount = numFrames; + t.bufferProvider->getNextBuffer(&b); + int16_t const *in = b.i16; - t.bufferProvider->releaseBuffer(&b); + // in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (in == NULL) { + memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t)); + return; + } + size_t outFrames = b.frameCount; + + if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { + // volume is boosted, so we might need to clamp even though + // we process only one track. + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } else { + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } + numFrames -= b.frameCount; + t.bufferProvider->releaseBuffer(&b); + } } // 2 tracks is also a common case @@ -784,71 +822,89 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void i = 31 - __builtin_clz(en); const track_t& t0 = state->tracks[i]; AudioBufferProvider::Buffer& b0(t0.buffer); - t0.bufferProvider->getNextBuffer(&b0); en &= ~(1<<i); i = 31 - __builtin_clz(en); const track_t& t1 = state->tracks[i]; AudioBufferProvider::Buffer& b1(t1.buffer); - t1.bufferProvider->getNextBuffer(&b1); - + int16_t const *in0; const int16_t vl0 = t0.volume[0]; const int16_t vr0 = t0.volume[1]; + size_t frameCount0 = 0; + int16_t const *in1; const int16_t vl1 = t1.volume[0]; const int16_t vr1 = t1.volume[1]; - size_t numFrames = state->frameCount; + size_t frameCount1 = 0; + int32_t* out = static_cast<int32_t*>(output); - - // t0/1.buffer.i16 == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (t0.buffer.i16 != NULL) { - in0 = t0.buffer.i16; - if (t1.buffer.i16 != NULL) { - in1 = t1.buffer.i16; - } else { - in1 = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; - memset((void *)in1, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t)); + size_t numFrames = state->frameCount; + int16_t const *buff = NULL; + + + while (numFrames) { + + if (frameCount0 == 0) { + b0.frameCount = numFrames; + t0.bufferProvider->getNextBuffer(&b0); + if (b0.i16 == NULL) { + if (buff == NULL) { + buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + } + in0 = buff; + b0.frameCount = numFrames; + } else { + in0 = b0.i16; + } + frameCount0 = b0.frameCount; } - } else { - in0 = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; - memset((void *)in0, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t)); - if (t1.buffer.i16 != NULL) { - in1 = t1.buffer.i16; - } else { - in1 = in0; + if (frameCount1 == 0) { + b1.frameCount = numFrames; + t1.bufferProvider->getNextBuffer(&b1); + if (b1.i16 == NULL) { + if (buff == NULL) { + buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + } + in1 = buff; + b1.frameCount = numFrames; + } else { + in1 = b1.i16; + } + frameCount1 = b1.frameCount; } - } - - do { - int32_t l0 = *in0++; - int32_t r0 = *in0++; - l0 = mul(l0, vl0); - r0 = mul(r0, vr0); - int32_t l = *in1++; - int32_t r = *in1++; - l = mulAdd(l, vl1, l0) >> 12; - r = mulAdd(r, vr1, r0) >> 12; - // clamping... - l = clamp16(l); - r = clamp16(r); - *out++ = (r<<16) | (l & 0xFFFF); - } while (--numFrames); + + size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1; - - if (t0.buffer.i16 != NULL) { - t0.bufferProvider->releaseBuffer(&b0); - if (t1.buffer.i16 != NULL) { - t1.bufferProvider->releaseBuffer(&b1); - } else { - delete [] in1; + numFrames -= outFrames; + frameCount0 -= outFrames; + frameCount1 -= outFrames; + + do { + int32_t l0 = *in0++; + int32_t r0 = *in0++; + l0 = mul(l0, vl0); + r0 = mul(r0, vr0); + int32_t l = *in1++; + int32_t r = *in1++; + l = mulAdd(l, vl1, l0) >> 12; + r = mulAdd(r, vr1, r0) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + + if (frameCount0 == 0) { + t0.bufferProvider->releaseBuffer(&b0); } - } else { - delete [] in0; - if (t1.buffer.i16 != NULL) { + if (frameCount1 == 0) { t1.bufferProvider->releaseBuffer(&b1); } + } + + if (buff != NULL) { + delete [] buff; } } diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h index 9ca109f..72ca28a 100644 --- a/libs/audioflinger/AudioMixer.h +++ b/libs/audioflinger/AudioMixer.h @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -130,7 +130,7 @@ private: int32_t volumeInc[2]; - uint16_t reserved; + uint16_t frameCount; uint8_t channelCount : 4; uint8_t enabled : 1; diff --git a/libs/audioflinger/AudioResampler.cpp b/libs/audioflinger/AudioResampler.cpp index c93ead3..5dabacb 100644 --- a/libs/audioflinger/AudioResampler.cpp +++ b/libs/audioflinger/AudioResampler.cpp @@ -14,17 +14,23 @@ * limitations under the License. */ +#define LOG_TAG "AudioResampler" +//#define LOG_NDEBUG 0 + #include <stdint.h> #include <stdlib.h> #include <sys/types.h> #include <cutils/log.h> #include <cutils/properties.h> - #include "AudioResampler.h" #include "AudioResamplerSinc.h" #include "AudioResamplerCubic.h" namespace android { + +#ifdef __ARM_ARCH_5E__ // optimized asm option + #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 +#endif // __ARM_ARCH_5E__ // ---------------------------------------------------------------------------- class AudioResamplerOrder1 : public AudioResampler { @@ -46,6 +52,15 @@ private: AudioBufferProvider* provider); void resampleStereo16(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement); + void AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement); +#endif // ASM_ARM_RESAMP1 + static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) { return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits); } @@ -73,20 +88,23 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, if (quality == DEFAULT) quality = LOW_QUALITY; - + switch (quality) { default: case LOW_QUALITY: + LOGV("Create linear Resampler"); resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate); break; case MED_QUALITY: + LOGV("Create cubic Resampler"); resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate); break; case HIGH_QUALITY: + LOGV("Create sinc Resampler"); resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate); break; } - + // initialize resampler resampler->init(); return resampler; @@ -103,10 +121,10 @@ AudioResampler::AudioResampler(int bitDepth, int inChannelCount, inChannelCount); // LOG_ASSERT(0); } - + // initialize common members mVolume[0] = mVolume[1] = 0; - mBuffer.raw = NULL; + mBuffer.frameCount = 0; // save format for quick lookup if (inChannelCount == 1) { @@ -160,19 +178,31 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n", - // outFrameCount, inputIndex, phaseFraction, phaseIncrement); + // outFrameCount, inputIndex, phaseFraction, phaseIncrement); while (outputIndex < outputSampleCount) { // buffer is empty, fetch a new one - if (mBuffer.raw == NULL) { + while (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer); - if (mBuffer.raw == NULL) - break; + if (mBuffer.raw == NULL) { + goto resampleStereo16_exit; + } + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + if (mBuffer.frameCount > inputIndex) break; + + inputIndex -= mBuffer.frameCount; + mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; + mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; + provider->releaseBuffer(&mBuffer); + // mBuffer.frameCount == 0 now so we reload a new buffer } + int16_t *in = mBuffer.i16; // handle boundary case @@ -187,34 +217,47 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, // process input samples // LOGE("general case\n"); - while (outputIndex < outputSampleCount) { + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + if (inputIndex + 2 < mBuffer.frameCount) { + int32_t* maxOutPt; + int32_t maxInIdx; + + maxOutPt = out + (outputSampleCount - 2); // 2 because 2 frames per loop + maxInIdx = mBuffer.frameCount - 2; + AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, + phaseFraction, phaseIncrement); + } +#endif // ASM_ARM_RESAMP1 + + while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { out[outputIndex++] += vl * Interp(in[inputIndex*2-2], in[inputIndex*2], phaseFraction); out[outputIndex++] += vr * Interp(in[inputIndex*2-1], in[inputIndex*2+1], phaseFraction); Advance(&inputIndex, &phaseFraction, phaseIncrement); - if (inputIndex >= mBuffer.frameCount) - break; } + // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); // if done with buffer, save samples if (inputIndex >= mBuffer.frameCount) { inputIndex -= mBuffer.frameCount; - // LOGE("buffer done, new input index", inputIndex); + // LOGE("buffer done, new input index %d", inputIndex); mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; provider->releaseBuffer(&mBuffer); - // verify that the releaseBuffer NULLS the buffer pointer - // LOG_ASSERT(mBuffer.raw == NULL); + // verify that the releaseBuffer resets the buffer frameCount + // LOG_ASSERT(mBuffer.frameCount == 0); } } // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); +resampleStereo16_exit: // save state mInputIndex = inputIndex; mPhaseFraction = phaseFraction; @@ -231,18 +274,27 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n", // outFrameCount, inputIndex, phaseFraction, phaseIncrement); - while (outputIndex < outputSampleCount) { - // buffer is empty, fetch a new one - if (mBuffer.raw == NULL) { + while (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer); - if (mBuffer.raw == NULL) - break; + if (mBuffer.raw == NULL) { + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; + goto resampleMono16_exit; + } // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + if (mBuffer.frameCount > inputIndex) break; + + inputIndex -= mBuffer.frameCount; + mX0L = mBuffer.i16[mBuffer.frameCount-1]; + provider->releaseBuffer(&mBuffer); + // mBuffer.frameCount == 0 now so we reload a new buffer } int16_t *in = mBuffer.i16; @@ -259,38 +311,284 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, // process input samples // LOGE("general case\n"); - while (outputIndex < outputSampleCount) { + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + if (inputIndex + 2 < mBuffer.frameCount) { + int32_t* maxOutPt; + int32_t maxInIdx; + + maxOutPt = out + (outputSampleCount - 2); + maxInIdx = (int32_t)mBuffer.frameCount - 2; + AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, + phaseFraction, phaseIncrement); + } +#endif // ASM_ARM_RESAMP1 + + while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { int32_t sample = Interp(in[inputIndex-1], in[inputIndex], phaseFraction); out[outputIndex++] += vl * sample; out[outputIndex++] += vr * sample; Advance(&inputIndex, &phaseFraction, phaseIncrement); - if (inputIndex >= mBuffer.frameCount) - break; } + + // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); // if done with buffer, save samples if (inputIndex >= mBuffer.frameCount) { inputIndex -= mBuffer.frameCount; - // LOGE("buffer done, new input index", inputIndex); + // LOGE("buffer done, new input index %d", inputIndex); mX0L = mBuffer.i16[mBuffer.frameCount-1]; provider->releaseBuffer(&mBuffer); - // verify that the releaseBuffer NULLS the buffer pointer - // LOG_ASSERT(mBuffer.raw == NULL); + // verify that the releaseBuffer resets the buffer frameCount + // LOG_ASSERT(mBuffer.frameCount == 0); } } // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); +resampleMono16_exit: // save state mInputIndex = inputIndex; mPhaseFraction = phaseFraction; } +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + +/******************************************************************* +* +* AsmMono16Loop +* asm optimized monotonic loop version; one loop is 2 frames +* Input: +* in : pointer on input samples +* maxOutPt : pointer on first not filled +* maxInIdx : index on first not used +* outputIndex : pointer on current output index +* out : pointer on output buffer +* inputIndex : pointer on current input index +* vl, vr : left and right gain +* phaseFraction : pointer on current phase fraction +* phaseIncrement +* Ouput: +* outputIndex : +* out : updated buffer +* inputIndex : index of next to use +* phaseFraction : phase fraction for next interpolation +* +*******************************************************************/ +void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement) +{ +#define MO_PARAM5 "36" // offset of parameter 5 (outputIndex) + + asm( + "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" + // get parameters + " ldr r6, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction + " ldr r6, [r6]\n" // phaseFraction + " ldr r7, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex + " ldr r7, [r7]\n" // inputIndex + " ldr r8, [sp, #" MO_PARAM5 " + 4]\n" // out + " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex + " ldr r0, [r0]\n" // outputIndex + " add r8, r0, asl #2\n" // curOut + " ldr r9, [sp, #" MO_PARAM5 " + 24]\n" // phaseIncrement + " ldr r10, [sp, #" MO_PARAM5 " + 12]\n" // vl + " ldr r11, [sp, #" MO_PARAM5 " + 16]\n" // vr + + // r0 pin, x0, Samp + + // r1 in + // r2 maxOutPt + // r3 maxInIdx + + // r4 x1, i1, i3, Out1 + // r5 out0 + + // r6 frac + // r7 inputIndex + // r8 curOut + + // r9 inc + // r10 vl + // r11 vr + + // r12 + // r13 sp + // r14 + + // the following loop works on 2 frames + + ".Y4L01:\n" + " cmp r8, r2\n" // curOut - maxCurOut + " bcs .Y4L02\n" + +#define MO_ONE_FRAME \ + " add r0, r1, r7, asl #1\n" /* in + inputIndex */\ + " ldrsh r4, [r0]\n" /* in[inputIndex] */\ + " ldr r5, [r8]\n" /* out[outputIndex] */\ + " ldrsh r0, [r0, #-2]\n" /* in[inputIndex-1] */\ + " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ + " sub r4, r4, r0\n" /* in[inputIndex] - in[inputIndex-1] */\ + " mov r4, r4, lsl #2\n" /* <<2 */\ + " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ + " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ + " add r0, r0, r4\n" /* x0 - (..) */\ + " mla r5, r0, r10, r5\n" /* vl*interp + out[] */\ + " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ + " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ + " mla r4, r0, r11, r4\n" /* vr*interp + out[] */\ + " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */\ + " str r4, [r8], #4\n" /* out[outputIndex++] = ... */ + + MO_ONE_FRAME // frame 1 + MO_ONE_FRAME // frame 2 + + " cmp r7, r3\n" // inputIndex - maxInIdx + " bcc .Y4L01\n" + ".Y4L02:\n" + + " bic r6, r6, #0xC0000000\n" // phaseFraction & ... + // save modified values + " ldr r0, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction + " str r6, [r0]\n" // phaseFraction + " ldr r0, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex + " str r7, [r0]\n" // inputIndex + " ldr r0, [sp, #" MO_PARAM5 " + 4]\n" // out + " sub r8, r0\n" // curOut - out + " asr r8, #2\n" // new outputIndex + " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex + " str r8, [r0]\n" // save outputIndex + + " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n" + ); +} + +/******************************************************************* +* +* AsmStereo16Loop +* asm optimized stereo loop version; one loop is 2 frames +* Input: +* in : pointer on input samples +* maxOutPt : pointer on first not filled +* maxInIdx : index on first not used +* outputIndex : pointer on current output index +* out : pointer on output buffer +* inputIndex : pointer on current input index +* vl, vr : left and right gain +* phaseFraction : pointer on current phase fraction +* phaseIncrement +* Ouput: +* outputIndex : +* out : updated buffer +* inputIndex : index of next to use +* phaseFraction : phase fraction for next interpolation +* +*******************************************************************/ +void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement) +{ +#define ST_PARAM5 "40" // offset of parameter 5 (outputIndex) + asm( + "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n" + // get parameters + " ldr r6, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction + " ldr r6, [r6]\n" // phaseFraction + " ldr r7, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex + " ldr r7, [r7]\n" // inputIndex + " ldr r8, [sp, #" ST_PARAM5 " + 4]\n" // out + " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex + " ldr r0, [r0]\n" // outputIndex + " add r8, r0, asl #2\n" // curOut + " ldr r9, [sp, #" ST_PARAM5 " + 24]\n" // phaseIncrement + " ldr r10, [sp, #" ST_PARAM5 " + 12]\n" // vl + " ldr r11, [sp, #" ST_PARAM5 " + 16]\n" // vr + + // r0 pin, x0, Samp + + // r1 in + // r2 maxOutPt + // r3 maxInIdx + + // r4 x1, i1, i3, out1 + // r5 out0 + + // r6 frac + // r7 inputIndex + // r8 curOut + + // r9 inc + // r10 vl + // r11 vr + + // r12 temporary + // r13 sp + // r14 + + ".Y5L01:\n" + " cmp r8, r2\n" // curOut - maxCurOut + " bcs .Y5L02\n" + +#define ST_ONE_FRAME \ + " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ +\ + " add r0, r1, r7, asl #2\n" /* in + 2*inputIndex */\ +\ + " ldrsh r4, [r0]\n" /* in[2*inputIndex] */\ + " ldr r5, [r8]\n" /* out[outputIndex] */\ + " ldrsh r12, [r0, #-4]\n" /* in[2*inputIndex-2] */\ + " sub r4, r4, r12\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ + " mov r4, r4, lsl #2\n" /* <<2 */\ + " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ + " add r12, r12, r4\n" /* x0 - (..) */\ + " mla r5, r12, r10, r5\n" /* vl*interp + out[] */\ + " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ + " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ +\ + " ldrsh r12, [r0, #+2]\n" /* in[2*inputIndex+1] */\ + " ldrsh r0, [r0, #-2]\n" /* in[2*inputIndex-1] */\ + " sub r12, r12, r0\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ + " mov r12, r12, lsl #2\n" /* <<2 */\ + " smulwt r12, r12, r6\n" /* (x1-x0)*.. */\ + " add r12, r0, r12\n" /* x0 - (..) */\ + " mla r4, r12, r11, r4\n" /* vr*interp + out[] */\ + " str r4, [r8], #4\n" /* out[outputIndex++] = ... */\ +\ + " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ + " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */ + + ST_ONE_FRAME // frame 1 + ST_ONE_FRAME // frame 1 + + " cmp r7, r3\n" // inputIndex - maxInIdx + " bcc .Y5L01\n" + ".Y5L02:\n" + + " bic r6, r6, #0xC0000000\n" // phaseFraction & ... + // save modified values + " ldr r0, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction + " str r6, [r0]\n" // phaseFraction + " ldr r0, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex + " str r7, [r0]\n" // inputIndex + " ldr r0, [sp, #" ST_PARAM5 " + 4]\n" // out + " sub r8, r0\n" // curOut - out + " asr r8, #2\n" // new outputIndex + " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex + " str r8, [r0]\n" // save outputIndex + + " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n" + ); +} + +#endif // ASM_ARM_RESAMP1 + + // ---------------------------------------------------------------------------- } ; // namespace android diff --git a/libs/audioflinger/AudioResamplerCubic.cpp b/libs/audioflinger/AudioResamplerCubic.cpp index 4f437bf..1d247bd 100644 --- a/libs/audioflinger/AudioResamplerCubic.cpp +++ b/libs/audioflinger/AudioResamplerCubic.cpp @@ -60,9 +60,11 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; - + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + // fetch first buffer - if (mBuffer.raw == NULL) { + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer); if (mBuffer.raw == NULL) return; @@ -79,7 +81,7 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, out[outputIndex++] += vl * interp(&left, x); out[outputIndex++] += vr * interp(&right, x); // out[outputIndex++] += vr * in[inputIndex*2]; - + // increment phase phaseFraction += phaseIncrement; uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); @@ -92,6 +94,7 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, if (inputIndex == mBuffer.frameCount) { inputIndex = 0; provider->releaseBuffer(&mBuffer); + mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer); if (mBuffer.raw == NULL) goto save_state; // ugly, but efficient @@ -122,9 +125,11 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; - + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + // fetch first buffer - if (mBuffer.raw == NULL) { + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer); if (mBuffer.raw == NULL) return; @@ -141,7 +146,7 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, sample = interp(&left, x); out[outputIndex++] += vl * sample; out[outputIndex++] += vr * sample; - + // increment phase phaseFraction += phaseIncrement; uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); @@ -154,6 +159,7 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, if (inputIndex == mBuffer.frameCount) { inputIndex = 0; provider->releaseBuffer(&mBuffer); + mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer); if (mBuffer.raw == NULL) goto save_state; // ugly, but efficient diff --git a/libs/audioflinger/AudioResamplerSinc.cpp b/libs/audioflinger/AudioResamplerSinc.cpp index e710d16..9e5e254 100644 --- a/libs/audioflinger/AudioResamplerSinc.cpp +++ b/libs/audioflinger/AudioResamplerSinc.cpp @@ -25,18 +25,18 @@ namespace android { * These coeficients are computed with the "fir" utility found in * tools/resampler_tools * TODO: A good optimization would be to transpose this matrix, to take - * better advantage of the data-cache. + * better advantage of the data-cache. */ const int32_t AudioResamplerSinc::mFirCoefsUp[] = { - 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621, - 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9, - 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9, - 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798, - 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636, - 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2, - 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070, - 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, - 0x00000000 // this one is needed for lerping the last coefficient + 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621, + 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9, + 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9, + 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798, + 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636, + 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2, + 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070, + 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, + 0x00000000 // this one is needed for lerping the last coefficient }; /* @@ -46,20 +46,20 @@ const int32_t AudioResamplerSinc::mFirCoefsUp[] = { * these coefficient from the above by "Stretching" them in time). */ const int32_t AudioResamplerSinc::mFirCoefsDown[] = { - 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540, - 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4, - 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa, - 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066, - 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf, - 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d, - 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a, - 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000, - 0x00000000 // this one is needed for lerping the last coefficient + 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540, + 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4, + 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa, + 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066, + 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf, + 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d, + 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a, + 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000, + 0x00000000 // this one is needed for lerping the last coefficient }; // ---------------------------------------------------------------------------- -static inline +static inline int32_t mulRL(int left, int32_t in, uint32_t vRL) { #if defined(__arm__) && !defined(__thumb__) @@ -85,7 +85,7 @@ int32_t mulRL(int left, int32_t in, uint32_t vRL) #endif } -static inline +static inline int32_t mulAdd(int16_t in, int32_t v, int32_t a) { #if defined(__arm__) && !defined(__thumb__) @@ -95,12 +95,14 @@ int32_t mulAdd(int16_t in, int32_t v, int32_t a) : [in]"%r"(in), [v]"r"(v), [a]"r"(a) : ); return out; -#else - return a + ((in * int32_t(v))>>16); +#else + return a + in * (v>>16); + // improved precision + // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16); #endif } -static inline +static inline int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) { #if defined(__arm__) && !defined(__thumb__) @@ -119,9 +121,11 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) return out; #else if (left) { - return a + ((int16_t(inRL&0xFFFF) * int32_t(v))>>16); + return a + (int16_t(inRL&0xFFFF) * (v>>16)); + //improved precision + // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16); } else { - return a + ((int16_t(inRL>>16) * int32_t(v))>>16); + return a + (int16_t(inRL>>16) * (v>>16)); } #endif } @@ -129,37 +133,37 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) // ---------------------------------------------------------------------------- AudioResamplerSinc::AudioResamplerSinc(int bitDepth, - int inChannelCount, int32_t sampleRate) - : AudioResampler(bitDepth, inChannelCount, sampleRate), - mState(0) + int inChannelCount, int32_t sampleRate) + : AudioResampler(bitDepth, inChannelCount, sampleRate), + mState(0) { - /* - * Layout of the state buffer for 32 tap: - * - * "present" sample beginning of 2nd buffer - * v v - * 0 01 2 23 3 - * 0 F0 0 F0 F - * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn] - * ^ ^ head - * - * p = past samples, convoluted with the (p)ositive side of sinc() - * n = future samples, convoluted with the (n)egative side of sinc() - * r = extra space for implementing the ring buffer - * - */ + /* + * Layout of the state buffer for 32 tap: + * + * "present" sample beginning of 2nd buffer + * v v + * 0 01 2 23 3 + * 0 F0 0 F0 F + * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn] + * ^ ^ head + * + * p = past samples, convoluted with the (p)ositive side of sinc() + * n = future samples, convoluted with the (n)egative side of sinc() + * r = extra space for implementing the ring buffer + * + */ - const size_t numCoefs = 2*halfNumCoefs; - const size_t stateSize = numCoefs * inChannelCount * 2; - mState = new int16_t[stateSize]; - memset(mState, 0, sizeof(int16_t)*stateSize); - mImpulse = mState + (halfNumCoefs-1)*inChannelCount; - mRingFull = mImpulse + (numCoefs+1)*inChannelCount; + const size_t numCoefs = 2*halfNumCoefs; + const size_t stateSize = numCoefs * inChannelCount * 2; + mState = new int16_t[stateSize]; + memset(mState, 0, sizeof(int16_t)*stateSize); + mImpulse = mState + (halfNumCoefs-1)*inChannelCount; + mRingFull = mImpulse + (numCoefs+1)*inChannelCount; } AudioResamplerSinc::~AudioResamplerSinc() { - delete [] mState; + delete [] mState; } void AudioResamplerSinc::init() { @@ -168,9 +172,9 @@ void AudioResamplerSinc::init() { void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider) { - mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown; + mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown; - // select the appropriate resampler + // select the appropriate resampler switch (mChannelCount) { case 1: resample<1>(out, outFrameCount, provider); @@ -193,43 +197,68 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; AudioBufferProvider::Buffer& buffer(mBuffer); while (outputIndex < outputSampleCount) { // buffer is empty, fetch a new one - if (buffer.raw == NULL) { + while (buffer.frameCount == 0) { + buffer.frameCount = inFrameCount; provider->getNextBuffer(&buffer); - if (buffer.raw == NULL) - break; - const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; - if (phaseIndex) { - read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + if (buffer.raw == NULL) { + goto resample_exit; } + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex == 1) { + // read one frame + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + } else if (phaseIndex == 2) { + // read 2 frames + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + inputIndex++; + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + provider->releaseBuffer(&buffer); + } else { + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + } + } } int16_t *in = buffer.i16; - const size_t frameCount = buffer.frameCount; + const size_t frameCount = buffer.frameCount; - // Always read-in the first samples from the input buffer - int16_t* head = impulse + halfNumCoefs*CHANNELS; - head[0] = in[inputIndex*CHANNELS + 0]; - if (CHANNELS == 2) - head[1] = in[inputIndex*CHANNELS + 1]; + // Always read-in the first samples from the input buffer + int16_t* head = impulse + halfNumCoefs*CHANNELS; + head[0] = in[inputIndex*CHANNELS + 0]; + if (CHANNELS == 2) + head[1] = in[inputIndex*CHANNELS + 1]; // handle boundary case - int32_t l, r; + int32_t l, r; while (outputIndex < outputSampleCount) { - filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse); - out[outputIndex++] = mulRL(1, l, vRL); - out[outputIndex++] = mulRL(0, r, vRL); + filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse); + out[outputIndex++] += 2 * mulRL(1, l, vRL); + out[outputIndex++] += 2 * mulRL(0, r, vRL); - phaseFraction += phaseIncrement; - const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; - if (phaseIndex) { - inputIndex += phaseIndex; - if (inputIndex >= frameCount) - break; - read<CHANNELS>(impulse, phaseFraction, in, inputIndex); - } + phaseFraction += phaseIncrement; + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex == 1) { + inputIndex++; + if (inputIndex >= frameCount) + break; // need a new buffer + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + } else if(phaseIndex == 2) { // maximum value + inputIndex++; + if (inputIndex >= frameCount) + break; // 0 frame available, 2 frames needed + // read first frame + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + inputIndex++; + if (inputIndex >= frameCount) + break; // 0 frame available, 1 frame needed + // read second frame + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + } } // if done with buffer, save samples @@ -239,80 +268,89 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, } } +resample_exit: mImpulse = impulse; mInputIndex = inputIndex; mPhaseFraction = phaseFraction; } template<int CHANNELS> +/*** +* read() +* +* This function reads only one frame from input buffer and writes it in +* state buffer +* +**/ void AudioResamplerSinc::read( - int16_t*& impulse, uint32_t& phaseFraction, - int16_t const* in, size_t inputIndex) + int16_t*& impulse, uint32_t& phaseFraction, + int16_t const* in, size_t inputIndex) { - // read new samples into the ring buffer - while (phaseFraction >> kNumPhaseBits) { - const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; - impulse += CHANNELS; - phaseFraction -= 1LU<<kNumPhaseBits; - if (impulse >= mRingFull) { - const size_t stateSize = (halfNumCoefs*2)*CHANNELS; - memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize); - impulse -= stateSize; - } - int16_t* head = impulse + halfNumCoefs*CHANNELS; - head[0] = in[inputIndex*CHANNELS + 0]; - if (CHANNELS == 2) - head[1] = in[inputIndex*CHANNELS + 1]; - } + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + impulse += CHANNELS; + phaseFraction -= 1LU<<kNumPhaseBits; + if (impulse >= mRingFull) { + const size_t stateSize = (halfNumCoefs*2)*CHANNELS; + memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize); + impulse -= stateSize; + } + int16_t* head = impulse + halfNumCoefs*CHANNELS; + head[0] = in[inputIndex*CHANNELS + 0]; + if (CHANNELS == 2) + head[1] = in[inputIndex*CHANNELS + 1]; } template<int CHANNELS> void AudioResamplerSinc::filterCoefficient( - int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples) -{ - // compute the index of the coefficient on the positive side and - // negative side - uint32_t indexP = (phase & cMask) >> cShift; - uint16_t lerpP = (phase & pMask) >> pShift; - uint32_t indexN = (-phase & cMask) >> cShift; - uint16_t lerpN = (-phase & pMask) >> pShift; - - l = 0; - r = 0; - int32_t const* coefs = mFirCoefs; - int16_t const *sP = samples; - int16_t const *sN = samples+CHANNELS; - for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) { - interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); - interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); - sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples) +{ + // compute the index of the coefficient on the positive side and + // negative side + uint32_t indexP = (phase & cMask) >> cShift; + uint16_t lerpP = (phase & pMask) >> pShift; + uint32_t indexN = (-phase & cMask) >> cShift; + uint16_t lerpN = (-phase & pMask) >> pShift; + if ((indexP == 0) && (lerpP == 0)) { + indexN = cMask >> cShift; + lerpN = pMask >> pShift; + } + + l = 0; + r = 0; + int32_t const* coefs = mFirCoefs; + int16_t const *sP = samples; + int16_t const *sN = samples+CHANNELS; + for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) { interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); - sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); - sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); - sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; - } + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + } } template<int CHANNELS> void AudioResamplerSinc::interpolate( int32_t& l, int32_t& r, - int32_t const* coefs, int16_t lerp, int16_t const* samples) + int32_t const* coefs, int16_t lerp, int16_t const* samples) { - int32_t c0 = coefs[0]; - int32_t c1 = coefs[1]; - int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0); - if (CHANNELS == 2) { - uint32_t rl = *reinterpret_cast<uint32_t const*>(samples); - l = mulAddRL(1, rl, sinc, l); - r = mulAddRL(0, rl, sinc, r); - } else { - r = l = mulAdd(samples[0], sinc, l); - } + int32_t c0 = coefs[0]; + int32_t c1 = coefs[1]; + int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0); + if (CHANNELS == 2) { + uint32_t rl = *reinterpret_cast<uint32_t const*>(samples); + l = mulAddRL(1, rl, sinc, l); + r = mulAddRL(0, rl, sinc, r); + } else { + r = l = mulAdd(samples[0], sinc, l); + } } // ---------------------------------------------------------------------------- diff --git a/libs/audioflinger/AudioResamplerSinc.h b/libs/audioflinger/AudioResamplerSinc.h index 89b9577..e6cb90b 100644 --- a/libs/audioflinger/AudioResamplerSinc.h +++ b/libs/audioflinger/AudioResamplerSinc.h @@ -24,19 +24,20 @@ #include "AudioResampler.h" namespace android { + // ---------------------------------------------------------------------------- class AudioResamplerSinc : public AudioResampler { public: - AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate); + AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate); + + ~AudioResamplerSinc(); - ~AudioResamplerSinc(); - virtual void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); private: void init(); - + template<int CHANNELS> void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); @@ -52,12 +53,12 @@ private: template<int CHANNELS> inline void read(int16_t*& impulse, uint32_t& phaseFraction, - int16_t const* in, size_t inputIndex); + int16_t const* in, size_t inputIndex); int16_t *mState; int16_t *mImpulse; int16_t *mRingFull; - + int32_t const * mFirCoefs; static const int32_t mFirCoefsDown[]; static const int32_t mFirCoefsUp[]; @@ -67,15 +68,15 @@ private: static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4; // we have 16 coefs samples per zero-crossing - static const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS; - static const int cShift = kNumPhaseBits - coefsBits; - static const uint32_t cMask = ((1<<coefsBits)-1) << cShift; + static const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS; // 4 + static const int cShift = kNumPhaseBits - coefsBits; // 26 + static const uint32_t cMask = ((1<<coefsBits)-1) << cShift; // 0xf<<26 = 3c00 0000 // and we use 15 bits to interpolate between these samples // this cannot change because the mul below rely on it. static const int pLerpBits = 15; - static const int pShift = kNumPhaseBits - coefsBits - pLerpBits; - static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift; + static const int pShift = kNumPhaseBits - coefsBits - pLerpBits; // 11 + static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift; // 0x7fff << 11 // number of zero-crossing on each side static const unsigned int halfNumCoefs = RESAMPLE_FIR_NUM_COEF; diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index 7741456..d14cebf 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -31,6 +31,7 @@ ifeq ($(TARGET_SIMULATOR),true) endif LOCAL_SHARED_LIBRARIES := \ + libhardware \ libutils \ libcutils \ libui \ diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 5dd9446..cd72179 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -19,6 +19,7 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <math.h> #include <GLES/egl.h> @@ -27,7 +28,9 @@ #include <ui/EGLDisplaySurface.h> #include "DisplayHardware/DisplayHardware.h" -#include "ui/BlitHardware.h" + +#include <hardware/copybit.h> +#include <hardware/overlay.h> using namespace android; @@ -91,19 +94,13 @@ DisplayHardware::~DisplayHardware() fini(); } -float DisplayHardware::getDpiX() const { return mDpiX; } -float DisplayHardware::getDpiY() const { return mDpiY; } -float DisplayHardware::getRefreshRate() const { return mRefreshRate; } - -int DisplayHardware::getWidth() const { - return mWidth; -} -int DisplayHardware::getHeight() const { - return mHeight; -} -PixelFormat DisplayHardware::getFormat() const { - return mFormat; -} +float DisplayHardware::getDpiX() const { return mDpiX; } +float DisplayHardware::getDpiY() const { return mDpiY; } +float DisplayHardware::getDensity() const { return mDensity; } +float DisplayHardware::getRefreshRate() const { return mRefreshRate; } +int DisplayHardware::getWidth() const { return mWidth; } +int DisplayHardware::getHeight() const { return mHeight; } +PixelFormat DisplayHardware::getFormat() const { return mFormat; } void DisplayHardware::init(uint32_t dpy) { @@ -195,6 +192,12 @@ void DisplayHardware::init(uint32_t dpy) mDpiY = 25.4f * float(value)/EGL_DISPLAY_SCALING; } mRefreshRate = 60.f; // TODO: get the real refresh rate + + // compute a "density" automatically as a scale factor from 160 dpi + // TODO: this value should be calculated a compile time based on the + // board. + mDensity = floorf((mDpiX>mDpiY ? mDpiX : mDpiY)*0.1f + 0.5f) * (10.0f/160.0f); + LOGI("density = %f", mDensity); /* * Create our OpenGL ES context @@ -237,8 +240,18 @@ void DisplayHardware::init(uint32_t dpy) mSurface = surface; mContext = context; mFormat = GGL_PIXEL_FORMAT_RGB_565; + + hw_module_t const* module; - mBlitEngine = copybit_init(); + mBlitEngine = NULL; + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } + + mOverlayEngine = NULL; + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + overlay_open(module, &mOverlayEngine); + } } /* @@ -252,7 +265,8 @@ void DisplayHardware::fini() { eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mDisplay); - copybit_term(mBlitEngine); + copybit_close(mBlitEngine); + overlay_close(mOverlayEngine); } void DisplayHardware::releaseScreen() const diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h index 299e236..de4a2cc 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -26,6 +26,8 @@ #include "DisplayHardware/DisplayHardwareBase.h" +struct overlay_device_t; +struct copybit_device_t; struct copybit_image_t; struct copybit_t; @@ -64,6 +66,7 @@ public: float getDpiX() const; float getDpiY() const; float getRefreshRate() const; + float getDensity() const; int getWidth() const; int getHeight() const; PixelFormat getFormat() const; @@ -74,7 +77,8 @@ public: void getDisplaySurface(copybit_image_t* img) const; void getDisplaySurface(GGLSurface* fb) const; EGLDisplay getEGLDisplay() const { return mDisplay; } - copybit_t* getBlitEngine() const { return mBlitEngine; } + copybit_device_t* getBlitEngine() const { return mBlitEngine; } + overlay_device_t* getOverlayEngine() const { return mOverlayEngine; } Rect bounds() const { return Rect(mWidth, mHeight); @@ -91,13 +95,15 @@ private: float mDpiX; float mDpiY; float mRefreshRate; + float mDensity; int mWidth; int mHeight; PixelFormat mFormat; uint32_t mFlags; mutable Region mDirty; sp<EGLDisplaySurface> mDisplaySurface; - copybit_t* mBlitEngine; + copybit_device_t* mBlitEngine; + overlay_device_t* mOverlayEngine; }; }; // namespace android diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp index 90f6287..f75e5c2 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -49,8 +49,10 @@ // ---------------------------------------------------------------------------- namespace android { -static char const * const kSleepFileName = "/sys/android_power/wait_for_fb_sleep"; -static char const * const kWakeFileName = "/sys/android_power/wait_for_fb_wake"; +static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep"; +static char const * kWakeFileName = "/sys/power/wait_for_fb_wake"; +static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep"; +static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake"; // This dir exists if the framebuffer console is present, either built into // the kernel or loaded as a module. @@ -123,16 +125,22 @@ status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const status_t DisplayHardwareBase::DisplayEventThread::readyToRun() { if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) { - LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName); - return NO_INIT; + if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) { + LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName); + return NO_INIT; + } + kSleepFileName = kOldSleepFileName; + kWakeFileName = kOldWakeFileName; } return NO_ERROR; } status_t DisplayHardwareBase::DisplayEventThread::initCheck() const { - return (access(kSleepFileName, R_OK) == 0 && - access(kWakeFileName, R_OK) == 0 && + return (((access(kSleepFileName, R_OK) == 0 && + access(kWakeFileName, R_OK) == 0) || + (access(kOldSleepFileName, R_OK) == 0 && + access(kOldWakeFileName, R_OK) == 0)) && access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT; } diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp index b24a0f2..eb75f99 100644 --- a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp +++ b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp @@ -30,6 +30,7 @@ #include <cutils/log.h> #include <cutils/properties.h> +#include <utils/IBinder.h> #include <utils/MemoryDealer.h> #include <utils/MemoryBase.h> #include <utils/MemoryHeapPmem.h> @@ -48,36 +49,113 @@ #include "GPUHardware/GPUHardware.h" + /* - * This file manages the GPU if there is one. The intent is that this code - * needs to be different for every devce. Currently there is no abstraction, - * but in the long term, this code needs to be refactored so that API and - * implementation are separated. + * Manage the GPU. This implementation is very specific to the G1. + * There are no abstraction here. + * + * All this code will soon go-away and be replaced by a new architecture + * for managing graphics accelerators. * - * In this particular implementation, the GPU, its memory and register are - * managed here. Clients (such as OpenGL ES) request the GPU when then need - * it and are given a revokable heap containing the registers on memory. + * In the meantime, it is conceptually possible to instantiate a + * GPUHardwareInterface for another GPU (see GPUFactory at the bottom + * of this file); practically... doubtful. * */ namespace android { + // --------------------------------------------------------------------------- +class GPUClientHeap; +class GPUAreaHeap; + +class GPUHardware : public GPUHardwareInterface, public IBinder::DeathRecipient +{ +public: + static const int GPU_RESERVED_SIZE; + static const int GPUR_SIZE; + + GPUHardware(); + virtual ~GPUHardware(); + + virtual void revoke(int pid); + virtual sp<MemoryDealer> request(int pid); + virtual status_t request(int pid, + const sp<IGPUCallback>& callback, + ISurfaceComposer::gpu_info_t* gpu); + + virtual status_t friendlyRevoke(); + virtual void unconditionalRevoke(); + + virtual pid_t getOwner() const { return mOwner; } + + // used for debugging only... + virtual sp<SimpleBestFitAllocator> getAllocator() const; + +private: + + + enum { + NO_OWNER = -1, + }; + + struct GPUArea { + sp<GPUAreaHeap> heap; + sp<MemoryHeapPmem> clientHeap; + sp<IMemory> map(); + }; + + struct Client { + pid_t pid; + GPUArea smi; + GPUArea ebi; + GPUArea reg; + void createClientHeaps(); + void revokeAllHeaps(); + }; + + Client& getClientLocked(pid_t pid); + status_t requestLocked(int pid); + void releaseLocked(); + void takeBackGPULocked(); + void registerCallbackLocked(const sp<IGPUCallback>& callback, + Client& client); + + virtual void binderDied(const wp<IBinder>& who); + + mutable Mutex mLock; + sp<GPUAreaHeap> mSMIHeap; + sp<GPUAreaHeap> mEBIHeap; + sp<GPUAreaHeap> mREGHeap; + + KeyedVector<pid_t, Client> mClients; + DefaultKeyedVector< wp<IBinder>, pid_t > mRegisteredClients; + + pid_t mOwner; + + sp<MemoryDealer> mCurrentAllocator; + sp<IGPUCallback> mCallback; + + sp<SimpleBestFitAllocator> mAllocator; + + Condition mCondition; +}; + // size reserved for GPU surfaces // 1200 KB fits exactly: // - two 320*480 16-bits double-buffered surfaces // - one 320*480 32-bits double-buffered surface -// - one 320*240 16-bits double-bufferd, 4x anti-aliased surface -static const int GPU_RESERVED_SIZE = 1200 * 1024; - -static const int GPUR_SIZE = 1 * 1024 * 1024; +// - one 320*240 16-bits double-buffered, 4x anti-aliased surface +const int GPUHardware::GPU_RESERVED_SIZE = 1200 * 1024; +const int GPUHardware::GPUR_SIZE = 1 * 1024 * 1024; // --------------------------------------------------------------------------- /* * GPUHandle is a special IMemory given to the client. It represents their * handle to the GPU. Once they give it up, they loose GPU access, or if - * they explicitely revoke their acces through the binder code 1000. + * they explicitly revoke their access through the binder code 1000. * In both cases, this triggers a callback to revoke() * first, and then actually powers down the chip. * @@ -92,42 +170,99 @@ static const int GPUR_SIZE = 1 * 1024 * 1024; * */ -class GPUHandle : public BnMemory +class GPUClientHeap : public MemoryHeapPmem { public: - GPUHandle(const sp<GPUHardware>& gpu, const sp<IMemoryHeap>& heap) - : mGPU(gpu), mClientHeap(heap) { - } - virtual ~GPUHandle(); - virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const; - virtual status_t onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); - void setOwner(int owner) { mOwner = owner; } + GPUClientHeap(const wp<GPUHardware>& gpu, + const sp<MemoryHeapBase>& heap) + : MemoryHeapPmem(heap), mGPU(gpu) { } +protected: + wp<GPUHardware> mGPU; +}; + +class GPUAreaHeap : public MemoryHeapBase +{ +public: + GPUAreaHeap(const wp<GPUHardware>& gpu, + const char* const vram, size_t size=0, size_t reserved=0) + : MemoryHeapBase(vram, size), mGPU(gpu) { + if (base() != MAP_FAILED) { + if (reserved == 0) + reserved = virtualSize(); + mAllocator = new SimpleBestFitAllocator(reserved); + } + } + virtual sp<MemoryHeapPmem> createClientHeap() { + sp<MemoryHeapBase> parentHeap(this); + return new GPUClientHeap(mGPU, parentHeap); + } + virtual const sp<SimpleBestFitAllocator>& getAllocator() const { + return mAllocator; + } private: - void revokeNotification(); + sp<SimpleBestFitAllocator> mAllocator; +protected: wp<GPUHardware> mGPU; - sp<IMemoryHeap> mClientHeap; - int mOwner; }; -GPUHandle::~GPUHandle() { +class GPURegisterHeap : public GPUAreaHeap +{ +public: + GPURegisterHeap(const sp<GPUHardware>& gpu) + : GPUAreaHeap(gpu, "/dev/hw3d", GPUHardware::GPUR_SIZE) { } + virtual sp<MemoryHeapPmem> createClientHeap() { + sp<MemoryHeapBase> parentHeap(this); + return new MemoryHeapRegs(mGPU, parentHeap); + } +private: + class MemoryHeapRegs : public GPUClientHeap { + public: + MemoryHeapRegs(const wp<GPUHardware>& gpu, + const sp<MemoryHeapBase>& heap) + : GPUClientHeap(gpu, heap) { } + sp<MemoryHeapPmem::MemoryPmem> createMemory(size_t offset, size_t size); + virtual void revoke(); + private: + class GPUHandle : public MemoryHeapPmem::MemoryPmem { + public: + GPUHandle(const sp<GPUHardware>& gpu, + const sp<MemoryHeapPmem>& heap) + : MemoryHeapPmem::MemoryPmem(heap), + mGPU(gpu), mOwner(gpu->getOwner()) { } + virtual ~GPUHandle(); + virtual sp<IMemoryHeap> getMemory( + ssize_t* offset, size_t* size) const; + virtual void revoke() { }; + virtual status_t onTransact( + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + private: + void revokeNotification(); + wp<GPUHardware> mGPU; + pid_t mOwner; + }; + }; +}; + +GPURegisterHeap::MemoryHeapRegs::GPUHandle::~GPUHandle() { //LOGD("GPUHandle %p released, revoking GPU", this); revokeNotification(); } - -void GPUHandle::revokeNotification() { +void GPURegisterHeap::MemoryHeapRegs::GPUHandle::revokeNotification() { sp<GPUHardware> hw(mGPU.promote()); if (hw != 0) { hw->revoke(mOwner); } } -sp<IMemoryHeap> GPUHandle::getMemory(ssize_t* offset, size_t* size) const +sp<IMemoryHeap> GPURegisterHeap::MemoryHeapRegs::GPUHandle::getMemory( + ssize_t* offset, size_t* size) const { + sp<MemoryHeapPmem> heap = getHeap(); if (offset) *offset = 0; - if (size) *size = mClientHeap !=0 ? mClientHeap->virtualSize() : 0; - return mClientHeap; + if (size) *size = heap !=0 ? heap->virtualSize() : 0; + return heap; } -status_t GPUHandle::onTransact( +status_t GPURegisterHeap::MemoryHeapRegs::GPUHandle::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { status_t err = BnMemory::onTransact(code, data, reply, flags); @@ -150,22 +285,14 @@ status_t GPUHandle::onTransact( // --------------------------------------------------------------------------- -class MemoryHeapRegs : public MemoryHeapPmem -{ -public: - MemoryHeapRegs(const wp<GPUHardware>& gpu, const sp<MemoryHeapBase>& heap); - virtual ~MemoryHeapRegs(); - sp<IMemory> mapMemory(size_t offset, size_t size); - virtual void revoke(); -private: - wp<GPUHardware> mGPU; -}; -MemoryHeapRegs::MemoryHeapRegs(const wp<GPUHardware>& gpu, const sp<MemoryHeapBase>& heap) - : MemoryHeapPmem(heap), mGPU(gpu) +sp<MemoryHeapPmem::MemoryPmem> GPURegisterHeap::MemoryHeapRegs::createMemory( + size_t offset, size_t size) { + sp<GPUHandle> memory; + sp<GPUHardware> gpu = mGPU.promote(); + if (heapID()>0 && gpu!=0) { #if HAVE_ANDROID_OS - if (heapID()>0) { /* this is where the GPU is powered on and the registers are mapped * in the client */ //LOGD("ioctl(HW3D_GRANT_GPU)"); @@ -174,27 +301,16 @@ MemoryHeapRegs::MemoryHeapRegs(const wp<GPUHardware>& gpu, const sp<MemoryHeapBa // it can happen if the master heap has been closed already // in which case the GPU already is revoked (app crash for // instance). - //LOGW("HW3D_GRANT_GPU failed (%s), mFD=%d, base=%p", - // strerror(errno), heapID(), base()); + LOGW("HW3D_GRANT_GPU failed (%s), mFD=%d, base=%p", + strerror(errno), heapID(), base()); } - } -#endif -} - -MemoryHeapRegs::~MemoryHeapRegs() -{ -} - -sp<IMemory> MemoryHeapRegs::mapMemory(size_t offset, size_t size) -{ - sp<GPUHandle> memory; - sp<GPUHardware> gpu = mGPU.promote(); - if (heapID()>0 && gpu!=0) memory = new GPUHandle(gpu, this); +#endif + } return memory; } -void MemoryHeapRegs::revoke() +void GPURegisterHeap::MemoryHeapRegs::revoke() { MemoryHeapPmem::revoke(); #if HAVE_ANDROID_OS @@ -207,25 +323,6 @@ void MemoryHeapRegs::revoke() #endif } -// --------------------------------------------------------------------------- - -class GPURegisterHeap : public PMemHeapInterface -{ -public: - GPURegisterHeap(const sp<GPUHardware>& gpu) - : PMemHeapInterface("/dev/hw3d", GPUR_SIZE), mGPU(gpu) - { - } - virtual ~GPURegisterHeap() { - } - virtual sp<MemoryHeapPmem> createClientHeap() { - sp<MemoryHeapBase> parentHeap(this); - return new MemoryHeapRegs(mGPU, parentHeap); - } -private: - wp<GPUHardware> mGPU; -}; - /*****************************************************************************/ GPUHardware::GPUHardware() @@ -237,85 +334,87 @@ GPUHardware::~GPUHardware() { } -sp<MemoryDealer> GPUHardware::request(int pid) +status_t GPUHardware::requestLocked(int pid) { - sp<MemoryDealer> dealer; - - LOGD("pid %d requesting gpu surface (current owner = %d)", pid, mOwner); - const int self_pid = getpid(); if (pid == self_pid) { // can't use GPU from surfaceflinger's process - return dealer; + return PERMISSION_DENIED; } - Mutex::Autolock _l(mLock); - if (mOwner != pid) { - // someone already has the gpu. - takeBackGPULocked(); - - // releaseLocked() should be a no-op most of the time - releaseLocked(); - - requestLocked(); + if (mREGHeap != 0) { + if (mOwner != NO_OWNER) { + // someone already has the gpu. + takeBackGPULocked(); + releaseLocked(); + } + } else { + // first time, initialize the stuff. + if (mSMIHeap == 0) + mSMIHeap = new GPUAreaHeap(this, "/dev/pmem_gpu0"); + if (mEBIHeap == 0) + mEBIHeap = new GPUAreaHeap(this, + "/dev/pmem_gpu1", 0, GPU_RESERVED_SIZE); + mREGHeap = new GPURegisterHeap(this); + mAllocator = mEBIHeap->getAllocator(); + if (mAllocator == NULL) { + // something went terribly wrong. + mSMIHeap.clear(); + mEBIHeap.clear(); + mREGHeap.clear(); + return INVALID_OPERATION; + } + } + Client& client = getClientLocked(pid); + mCurrentAllocator = new MemoryDealer(client.ebi.clientHeap, mAllocator); + mOwner = pid; } + return NO_ERROR; +} - dealer = mAllocator; - mOwner = pid; - if (dealer == 0) { - mOwner = SURFACE_FAILED; +sp<MemoryDealer> GPUHardware::request(int pid) +{ + sp<MemoryDealer> dealer; + Mutex::Autolock _l(mLock); + Client* client; + LOGD("pid %d requesting gpu surface (current owner = %d)", pid, mOwner); + if (requestLocked(pid) == NO_ERROR) { + dealer = mCurrentAllocator; + LOGD_IF(dealer!=0, "gpu surface granted to pid %d", mOwner); } - - LOGD_IF(dealer!=0, "gpu surface granted to pid %d", mOwner); return dealer; } -status_t GPUHardware::request(const sp<IGPUCallback>& callback, +status_t GPUHardware::request(int pid, const sp<IGPUCallback>& callback, ISurfaceComposer::gpu_info_t* gpu) { - sp<IMemory> gpuHandle; - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int self_pid = getpid(); + if (callback == 0) + return BAD_VALUE; + sp<IMemory> gpuHandle; LOGD("pid %d requesting gpu core (owner = %d)", pid, mOwner); - - if (pid == self_pid) { - // can't use GPU from surfaceflinger's process - return PERMISSION_DENIED; - } - Mutex::Autolock _l(mLock); - if (mOwner != pid) { - // someone already has the gpu. - takeBackGPULocked(); - - // releaseLocked() should be a no-op most of the time - releaseLocked(); - - requestLocked(); - } - - if (mHeapR.isValid()) { + status_t err = requestLocked(pid); + if (err == NO_ERROR) { + // it's guaranteed to be there, be construction + Client& client = mClients.editValueFor(pid); + registerCallbackLocked(callback, client); gpu->count = 2; - gpu->regions[0].region = mHeap0.map(true); - gpu->regions[0].reserved = mHeap0.reserved; - gpu->regions[1].region = mHeap1.map(true); - gpu->regions[1].reserved = mHeap1.reserved; - gpu->regs = mHeapR.map(); + gpu->regions[0].region = client.smi.map(); + gpu->regions[1].region = client.ebi.map(); + gpu->regs = client.reg.map(); + gpu->regions[0].reserved = 0; + gpu->regions[1].reserved = GPU_RESERVED_SIZE; if (gpu->regs != 0) { - static_cast< GPUHandle* >(gpu->regs.get())->setOwner(pid); + //LOGD("gpu core granted to pid %d, handle base=%p", + // mOwner, gpu->regs->pointer()); } mCallback = callback; - mOwner = pid; - //LOGD("gpu core granted to pid %d, handle base=%p", - // mOwner, gpu->regs->pointer()); } else { LOGW("couldn't grant gpu core to pid %d", pid); } - - return NO_ERROR; + return err; } void GPUHardware::revoke(int pid) @@ -330,16 +429,16 @@ void GPUHardware::revoke(int pid) // mOwner could be <0 if the same process acquired the GPU // several times without releasing it first. mCondition.signal(); - releaseLocked(true); + releaseLocked(); } } status_t GPUHardware::friendlyRevoke() { Mutex::Autolock _l(mLock); - takeBackGPULocked(); //LOGD("friendlyRevoke owner=%d", mOwner); - releaseLocked(true); + takeBackGPULocked(); + releaseLocked(); return NO_ERROR; } @@ -353,90 +452,37 @@ void GPUHardware::takeBackGPULocked() } } -void GPUHardware::requestLocked() +void GPUHardware::releaseLocked() { - if (mAllocator == 0) { - GPUPart* part = 0; - sp<PMemHeap> surfaceHeap; - if (mHeap1.promote() == false) { - //LOGD("requestLocked: (1) creating new heap"); - mHeap1.set(new PMemHeap("/dev/pmem_gpu1", 0, GPU_RESERVED_SIZE)); - } - if (mHeap1.isValid()) { - //LOGD("requestLocked: (1) heap is valid"); - // NOTE: if GPU1 is available we use it for our surfaces - // this could be device specific, so we should do something more - // generic - surfaceHeap = static_cast< PMemHeap* >( mHeap1.getHeap().get() ); - part = &mHeap1; - if (mHeap0.promote() == false) { - //LOGD("requestLocked: (0) creating new heap"); - mHeap0.set(new PMemHeap("/dev/pmem_gpu0")); - } - } else { - //LOGD("requestLocked: (1) heap is not valid"); - // No GPU1, use GPU0 only - if (mHeap0.promote() == false) { - //LOGD("requestLocked: (0) creating new heap"); - mHeap0.set(new PMemHeap("/dev/pmem_gpu0", 0, GPU_RESERVED_SIZE)); - } - if (mHeap0.isValid()) { - //LOGD("requestLocked: (0) heap is valid"); - surfaceHeap = static_cast< PMemHeap* >( mHeap0.getHeap().get() ); - part = &mHeap0; - } - } - - if (mHeap0.isValid() || mHeap1.isValid()) { - if (mHeapR.promote() == false) { - //LOGD("requestLocked: (R) creating new register heap"); - mHeapR.set(new GPURegisterHeap(this)); - } - } else { - // we got nothing... - mHeap0.clear(); - mHeap1.clear(); - } - - if (mHeapR.isValid() == false) { - //LOGD("requestLocked: (R) register heap not valid!!!"); - // damn, couldn't get the gpu registers! - mHeap0.clear(); - mHeap1.clear(); - surfaceHeap.clear(); - part = NULL; - } - - if (surfaceHeap != 0 && part && part->getClientHeap()!=0) { - part->reserved = GPU_RESERVED_SIZE; - part->surface = true; - mAllocatorDebug = static_cast<SimpleBestFitAllocator*>( - surfaceHeap->getAllocator().get()); - mAllocator = new MemoryDealer( - part->getClientHeap(), - surfaceHeap->getAllocator()); + //LOGD("revoking gpu from pid %d", mOwner); + if (mOwner != NO_OWNER) { + // this may fail because the client might have died, and have + // been removed from the list. + ssize_t index = mClients.indexOfKey(mOwner); + if (index >= 0) { + Client& client(mClients.editValueAt(index)); + client.revokeAllHeaps(); } + mOwner = NO_OWNER; + mCurrentAllocator.clear(); + mCallback.clear(); } } -void GPUHardware::releaseLocked(bool dispose) +GPUHardware::Client& GPUHardware::getClientLocked(pid_t pid) { - /* - * if dispose is set, we will force the destruction of the heap, - * so it is given back to other systems, such as camera. - * Otherwise, we'll keep a weak pointer to it, this way we might be able - * to reuse it later if it's still around. - */ - //LOGD("revoking gpu from pid %d", mOwner); - mOwner = NO_OWNER; - mAllocator.clear(); - mCallback.clear(); - - /* if we're asked for a full revoke, dispose only of the heap - * we're not using for surface (as we might need it while drawing) */ - mHeap0.release(mHeap0.surface ? false : dispose); - mHeap1.release(mHeap1.surface ? false : dispose); - mHeapR.release(false); + ssize_t index = mClients.indexOfKey(pid); + if (index < 0) { + Client client; + client.pid = pid; + client.smi.heap = mSMIHeap; + client.ebi.heap = mEBIHeap; + client.reg.heap = mREGHeap; + index = mClients.add(pid, client); + } + Client& client(mClients.editValueAt(index)); + client.createClientHeaps(); + return client; } // ---------------------------------------------------------------------------- @@ -444,8 +490,7 @@ void GPUHardware::releaseLocked(bool dispose) sp<SimpleBestFitAllocator> GPUHardware::getAllocator() const { Mutex::Autolock _l(mLock); - sp<SimpleBestFitAllocator> allocator = mAllocatorDebug.promote(); - return allocator; + return mAllocator; } void GPUHardware::unconditionalRevoke() @@ -456,100 +501,79 @@ void GPUHardware::unconditionalRevoke() // --------------------------------------------------------------------------- - -GPUHardware::GPUPart::GPUPart() - : surface(false), reserved(0) -{ -} - -GPUHardware::GPUPart::~GPUPart() { -} - -const sp<PMemHeapInterface>& GPUHardware::GPUPart::getHeap() const { - return mHeap; -} - -const sp<MemoryHeapPmem>& GPUHardware::GPUPart::getClientHeap() const { - return mClientHeap; -} - -bool GPUHardware::GPUPart::isValid() const { - return ((mHeap!=0) && (mHeap->base() != MAP_FAILED)); +sp<IMemory> GPUHardware::GPUArea::map() { + sp<IMemory> memory; + if (clientHeap != 0 && heap != 0) { + memory = clientHeap->mapMemory(0, heap->virtualSize()); + } + return memory; } -void GPUHardware::GPUPart::clear() +void GPUHardware::Client::createClientHeaps() { - mHeap.clear(); - mHeapWeak.clear(); - mClientHeap.clear(); - surface = false; + if (smi.clientHeap == 0) + smi.clientHeap = smi.heap->createClientHeap(); + if (ebi.clientHeap == 0) + ebi.clientHeap = ebi.heap->createClientHeap(); + if (reg.clientHeap == 0) + reg.clientHeap = reg.heap->createClientHeap(); } -void GPUHardware::GPUPart::set(const sp<PMemHeapInterface>& heap) +void GPUHardware::Client::revokeAllHeaps() { - mHeapWeak.clear(); - if (heap!=0 && heap->base() == MAP_FAILED) { - mHeap.clear(); - mClientHeap.clear(); - } else { - mHeap = heap; - mClientHeap = mHeap->createClientHeap(); - } + if (smi.clientHeap != 0) + smi.clientHeap->revoke(); + if (ebi.clientHeap != 0) + ebi.clientHeap->revoke(); + if (reg.clientHeap != 0) + reg.clientHeap->revoke(); } -bool GPUHardware::GPUPart::promote() +void GPUHardware::registerCallbackLocked(const sp<IGPUCallback>& callback, + Client& client) { - //LOGD("mHeapWeak=%p, mHeap=%p", mHeapWeak.unsafe_get(), mHeap.get()); - if (mHeap == 0) { - mHeap = mHeapWeak.promote(); + sp<IBinder> binder = callback->asBinder(); + if (mRegisteredClients.add(binder, client.pid) >= 0) { + binder->linkToDeath(this); } - if (mHeap != 0) { - if (mClientHeap != 0) { - mClientHeap->revoke(); - } - mClientHeap = mHeap->createClientHeap(); - } else { - surface = false; - } - return mHeap != 0; } -sp<IMemory> GPUHardware::GPUPart::map(bool clear) +void GPUHardware::binderDied(const wp<IBinder>& who) { - sp<IMemory> memory; - if (mClientHeap != NULL) { - memory = mClientHeap->mapMemory(0, mHeap->virtualSize()); - if (clear && memory!=0) { - //StopWatch sw("memset"); - memset(memory->pointer(), 0, memory->size()); + Mutex::Autolock _l(mLock); + pid_t pid = mRegisteredClients.valueFor(who); + if (pid != 0) { + ssize_t index = mClients.indexOfKey(pid); + if (index >= 0) { + //LOGD("*** removing client at %d", index); + Client& client(mClients.editValueAt(index)); + client.revokeAllHeaps(); // not really needed in theory + mClients.removeItemsAt(index); + if (mClients.size() == 0) { + //LOGD("*** was last client closing everything"); + mCallback.clear(); + mAllocator.clear(); + mCurrentAllocator.clear(); + mSMIHeap.clear(); + mREGHeap.clear(); + + // NOTE: we cannot clear the EBI heap because surfaceflinger + // itself may be using it, since this is where surfaces + // are allocated. if we're in the middle of compositing + // a surface (even if its process just died), we cannot + // rip the heap under our feet. + + mOwner = NO_OWNER; + } } } - return memory; } -void GPUHardware::GPUPart::release(bool dispose) +// --------------------------------------------------------------------------- + +sp<GPUHardwareInterface> GPUFactory::getGPU() { - if (mClientHeap != 0) { - mClientHeap->revoke(); - mClientHeap.clear(); - } - if (dispose) { - if (mHeapWeak!=0 && mHeap==0) { - mHeap = mHeapWeak.promote(); - } - if (mHeap != 0) { - mHeap->dispose(); - mHeapWeak.clear(); - mHeap.clear(); - } else { - surface = false; - } - } else { - if (mHeap != 0) { - mHeapWeak = mHeap; - mHeap.clear(); - } - } + return new GPUHardware(); } // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.h b/libs/surfaceflinger/GPUHardware/GPUHardware.h index 9a78b99..3354528 100644 --- a/libs/surfaceflinger/GPUHardware/GPUHardware.h +++ b/libs/surfaceflinger/GPUHardware/GPUHardware.h @@ -22,92 +22,39 @@ #include <utils/RefBase.h> #include <utils/threads.h> +#include <utils/KeyedVector.h> + +#include <ui/ISurfaceComposer.h> namespace android { // --------------------------------------------------------------------------- -class GPUHardwareInterface : public RefBase +class IGPUCallback; + +class GPUHardwareInterface : public virtual RefBase { public: virtual void revoke(int pid) = 0; virtual sp<MemoryDealer> request(int pid) = 0; - virtual status_t request(const sp<IGPUCallback>& callback, + virtual status_t request(int pid, const sp<IGPUCallback>& callback, ISurfaceComposer::gpu_info_t* gpu) = 0; virtual status_t friendlyRevoke() = 0; - virtual void unconditionalRevoke() = 0; // used for debugging only... virtual sp<SimpleBestFitAllocator> getAllocator() const = 0; virtual pid_t getOwner() const = 0; + virtual void unconditionalRevoke() = 0; }; // --------------------------------------------------------------------------- -class IMemory; -class MemoryHeapPmem; -class PMemHeap; - -class GPUHardware : public GPUHardwareInterface -{ +class GPUFactory +{ public: - GPUHardware(); - virtual ~GPUHardware(); - - virtual void revoke(int pid); - virtual sp<MemoryDealer> request(int pid); - virtual status_t request(const sp<IGPUCallback>& callback, - ISurfaceComposer::gpu_info_t* gpu); - - virtual status_t friendlyRevoke(); - virtual void unconditionalRevoke(); - - // used for debugging only... - virtual sp<SimpleBestFitAllocator> getAllocator() const; - virtual pid_t getOwner() const { return mOwner; } - -private: - enum { - NO_OWNER = -1, - SURFACE_FAILED = -2 - }; - - void requestLocked(); - void releaseLocked(bool dispose = false); - void takeBackGPULocked(); - - class GPUPart - { - public: - bool surface; - size_t reserved; - GPUPart(); - ~GPUPart(); - const sp<PMemHeapInterface>& getHeap() const; - const sp<MemoryHeapPmem>& getClientHeap() const; - bool isValid() const; - void clear(); - void set(const sp<PMemHeapInterface>& heap); - bool promote(); - sp<IMemory> map(bool clear = false); - void release(bool dispose); - private: - sp<PMemHeapInterface> mHeap; - wp<PMemHeapInterface> mHeapWeak; - sp<MemoryHeapPmem> mClientHeap; - }; - - mutable Mutex mLock; - GPUPart mHeap0; // SMI - GPUPart mHeap1; // EBI1 - GPUPart mHeapR; - sp<MemoryDealer> mAllocator; - pid_t mOwner; - sp<IGPUCallback> mCallback; - wp<SimpleBestFitAllocator> mAllocatorDebug; - - Condition mCondition; + // the gpu factory + static sp<GPUHardwareInterface> getGPU(); }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index 4f6bae1..8ba0851 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -92,26 +92,29 @@ status_t Layer::setBuffers( Client* client, status_t err = getPixelFormatInfo(format, &info); if (err) return err; - // TODO: if eHardware is explicitely requested, we should fail + // TODO: if eHardware is explicitly requested, we should fail // on systems where we can't allocate memory that can be used with // DMA engines for instance. + + // FIXME: we always ask for hardware for now (this should come from copybit) + flags |= ISurfaceComposer::eHardware; - int memory_type = NATIVE_MEMORY_TYPE_PMEM; + const uint32_t memory_flags = flags & + (ISurfaceComposer::eGPU | + ISurfaceComposer::eHardware | + ISurfaceComposer::eSecure); // pixel-alignment. the final alignment may be bigger because // we always force a 4-byte aligned bpr. uint32_t alignment = 1; - const uint32_t mask = ISurfaceComposer::eGPU | ISurfaceComposer::eSecure; - if ((flags & mask) == ISurfaceComposer::eGPU) { - // don't grant GPU memory if GPU is disabled - char value[PROPERTY_VALUE_MAX]; - property_get("debug.egl.hw", value, "1"); - if (atoi(value) != 0) { - flags |= ISurfaceComposer::eHardware; - memory_type = NATIVE_MEMORY_TYPE_GPU; - // TODO: this value should come from the h/w - alignment = 8; + if (flags & ISurfaceComposer::eGPU) { + // FIXME: this value should come from the h/w + alignment = 8; + // FIXME: this is msm7201A specific, as its GPU only supports + // BGRA_8888. + if (format == PIXEL_FORMAT_RGBA_8888) { + format = PIXEL_FORMAT_BGRA_8888; } } @@ -119,7 +122,7 @@ status_t Layer::setBuffers( Client* client, mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; sp<MemoryDealer> allocators[2]; for (int i=0 ; i<2 ; i++) { - allocators[i] = client->createAllocator(memory_type); + allocators[i] = client->createAllocator(memory_flags); if (allocators[i] == 0) return NO_MEMORY; mBuffers[i].init(allocators[i]); @@ -133,7 +136,7 @@ status_t Layer::setBuffers( Client* client, mSurface = new Surface(clientIndex(), allocators[0]->getMemoryHeap(), allocators[1]->getMemoryHeap(), - memory_type, mIdentity); + mIdentity); return NO_ERROR; } @@ -180,7 +183,7 @@ void Layer::onDraw(const Region& clip) const front.getBitmapSurface(&src); copybit_rect_t srect = { 0, 0, t.width, t.height }; - copybit_t* copybit = mFlinger->getBlitEngine(); + copybit_device_t* copybit = mFlinger->getBlitEngine(); copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation()); copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); copybit->set_parameter(copybit, COPYBIT_DITHER, diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index 17c9f42..af353e2 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -30,7 +30,7 @@ #include "DisplayHardware/DisplayHardware.h" -// We don't honor the premultipliad alpha flags, which means that +// We don't honor the premultiplied alpha flags, which means that // premultiplied surface may be composed using a non-premultiplied // equation. We do this because it may be a lot faster on some hardware // The correct value is HONOR_PREMULTIPLIED_ALPHA = 1 @@ -256,7 +256,7 @@ void LayerBase::validateVisibility(const Transform& planeTransform) // see if we can/should use 2D h/w with the new configuration mCanUseCopyBit = false; - copybit_t* copybit = mFlinger->getBlitEngine(); + copybit_device_t* copybit = mFlinger->getBlitEngine(); if (copybit) { const int step = copybit->get(copybit, COPYBIT_ROTATION_STEP_DEG); const int scaleBits = copybit->get(copybit, COPYBIT_SCALING_FRAC_BITS); @@ -413,7 +413,7 @@ void LayerBase::drawWithOpenGL(const Region& clip, // premultiplied alpha. // If the texture doesn't have an alpha channel we can - // use REPLACE and switch to non premultiplied-alpha + // use REPLACE and switch to non premultiplied alpha // blending (SRCA/ONE_MINUS_SRCA). GLenum env, src; @@ -431,11 +431,11 @@ void LayerBase::drawWithOpenGL(const Region& clip, glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); } else { glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4x(0x10000, 0x10000, 0x10000, 0x10000); if (needsBlending()) { GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; glEnable(GL_BLEND); glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); - glColor4x(0x10000, 0x10000, 0x10000, 0x10000); } else { glDisable(GL_BLEND); } @@ -463,7 +463,7 @@ void LayerBase::drawWithOpenGL(const Region& clip, glMatrixMode(GL_TEXTURE); glLoadIdentity(); if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { - // find the smalest power-of-two that will accomodate our surface + // find the smallest power-of-two that will accommodate our surface GLuint tw = 1 << (31 - clz(t.width)); GLuint th = 1 << (31 - clz(t.height)); if (tw < t.width) tw <<= 1; @@ -556,7 +556,7 @@ void LayerBase::loadTexture(const Region& dirty, GLuint texture_w = tw; GLuint texture_h = th; if (!(flags & DisplayHardware::NPOT_EXTENSION)) { - // find the smalest power-of-two that will accomodate our surface + // find the smallest power-of-two that will accommodate our surface texture_w = 1 << (31 - clz(t.width)); texture_h = 1 << (31 - clz(t.height)); if (texture_w < t.width) texture_w <<= 1; @@ -582,6 +582,8 @@ void LayerBase::loadTexture(const Region& dirty, glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, t.data); + } else if (t.format == GGL_PIXEL_FORMAT_BGRA_8888) { + // TODO: add GL_BGRA extension } else { // oops, we don't handle this format, try the regular path goto regular; @@ -592,7 +594,7 @@ void LayerBase::loadTexture(const Region& dirty, regular: Rect bounds(dirty.bounds()); GLvoid* data = 0; - if (texture_w!=textureWidth || texture_w!=textureHeight) { + if (texture_w!=textureWidth || texture_h!=textureHeight) { // texture size changed, we need to create a new one if (!textureWidth || !textureHeight) { @@ -606,31 +608,36 @@ regular: bounds.set(Rect(tw, th)); } } - + if (t.format == GGL_PIXEL_FORMAT_RGB_565) { glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGB, tw, th, 0, + GL_RGB, texture_w, texture_h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, tw, th, 0, + GL_RGBA, texture_w, texture_h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, tw, th, 0, + GL_RGBA, texture_w, texture_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { // just show the Y plane of YUV buffers data = t.data; glTexImage2D(GL_TEXTURE_2D, 0, - GL_LUMINANCE, tw, th, 0, + GL_LUMINANCE, texture_w, texture_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + // oops, we don't handle this format! + LOGE("layer %p, texture=%d, using format %d, which is not " + "supported by the GL", this, textureName, t.format); + textureName = -1; } - textureWidth = tw; - textureHeight = th; + textureWidth = texture_w; + textureHeight = texture_h; } - if (!data) { + if (!data && textureName>=0) { if (t.format == GGL_PIXEL_FORMAT_RGB_565) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, bounds.top, t.width, bounds.height(), diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index 10c1bc1..b3f3771 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -23,6 +23,8 @@ #include <private/ui/LayerState.h> #include <ui/Region.h> +#include <ui/Overlay.h> + #include <pixelflinger/pixelflinger.h> #include "Transform.h" @@ -223,16 +225,14 @@ public: Surface(SurfaceID id, int identity) { mParams.token = id; mParams.identity = identity; - mParams.type = 0; } Surface(SurfaceID id, const sp<IMemoryHeap>& heap0, const sp<IMemoryHeap>& heap1, - int memory_type, int identity) + int identity) { mParams.token = id; mParams.identity = identity; - mParams.type = memory_type; mParams.heap[0] = heap0; mParams.heap[1] = heap1; } @@ -240,8 +240,8 @@ public: // TODO: We now have a point here were we can clean-up the // client's mess. // This is also where surface id should be recycled. - //LOGD("Surface %d, heaps={%p, %p}, type=%d destroyed", - // mId, mHeap[0].get(), mHeap[1].get(), mMemoryType); + //LOGD("Surface %d, heaps={%p, %p} destroyed", + // mId, mHeap[0].get(), mHeap[1].get()); } virtual void getSurfaceData( @@ -254,6 +254,10 @@ public: { return INVALID_OPERATION; } virtual void postBuffer(ssize_t offset) { } virtual void unregisterBuffers() { }; + virtual sp<Overlay> createOverlay( + uint32_t w, uint32_t h, int32_t format) { + return NULL; + }; private: ISurfaceFlingerClient::surface_data_t mParams; diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp index 192ceda..e3ae7fb 100644 --- a/libs/surfaceflinger/LayerBlur.cpp +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -145,11 +145,13 @@ void LayerBlur::onDraw(const Region& clip) const mRefreshCache = false; mAutoRefreshPending = false; - uint16_t* const pixels = (uint16_t*)malloc(w*h*2); + // allocate enough memory for 4-bytes (2 pixels) aligned data + const int32_t s = (w + 1) & ~1; + uint16_t* const pixels = (uint16_t*)malloc(s*h*2); - // this reads the frame-buffer, so a h/w GL would have to + // This reads the frame-buffer, so a h/w GL would have to // finish() its rendering first. we don't want to do that - // too often. + // too often. Read data is 4-bytes aligned. glReadPixels(X, Y, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); // blur that texture. @@ -157,7 +159,7 @@ void LayerBlur::onDraw(const Region& clip) const bl.version = sizeof(GGLSurface); bl.width = w; bl.height = h; - bl.stride = w; + bl.stride = s; bl.format = GGL_PIXEL_FORMAT_RGB_565; bl.data = (GGLubyte*)pixels; blurFilter(&bl, 8, 2); diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index d871fc3..3861e68 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -104,7 +104,7 @@ void LayerBuffer::onDraw(const Region& clip) const * the requested scale factor, in which case we perform the scaling * in several passes. */ - copybit_t* copybit = mFlinger->getBlitEngine(); + copybit_device_t* copybit = mFlinger->getBlitEngine(); const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); @@ -123,7 +123,7 @@ void LayerBuffer::onDraw(const Region& clip) const if (UNLIKELY(mTemporaryDealer == 0)) { // allocate a memory-dealer for this the first time mTemporaryDealer = mFlinger->getSurfaceHeapManager() - ->createHeap(NATIVE_MEMORY_TYPE_PMEM); + ->createHeap(ISurfaceComposer::eHardware); mTempBitmap.init(mTemporaryDealer); } @@ -230,7 +230,18 @@ sp<LayerBaseClient::Surface> LayerBuffer::getSurface() const status_t LayerBuffer::registerBuffers(int w, int h, int hstride, int vstride, PixelFormat format, const sp<IMemoryHeap>& memoryHeap) { - status_t err = (memoryHeap!=0 && memoryHeap->heapID() >= 0) ? NO_ERROR : NO_INIT; + if (memoryHeap == NULL) { + // this is allowed, but in this case, it is illegal to receive + // postBuffer(). The surface just erases the framebuffer with + // fully transparent pixels. + mHeap.clear(); + mWidth = w; + mHeight = h; + mNeedsBlending = false; + return NO_ERROR; + } + + status_t err = (memoryHeap->heapID() >= 0) ? NO_ERROR : NO_INIT; if (err != NO_ERROR) return err; @@ -281,6 +292,32 @@ void LayerBuffer::unregisterBuffers() invalidateLocked(); } +sp<Overlay> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t format) +{ + sp<Overlay> result; + Mutex::Autolock _l(mLock); + if (mHeap != 0 || mBuffer != 0) { + // we're a push surface. error. + return result; + } + + overlay_device_t* overlay_dev = mFlinger->getOverlayEngine(); + if (overlay_dev == NULL) { + // overlays not supported + return result; + } + + overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format); + if (overlay == NULL) { + // couldn't create the overlay (no memory? no more overlays?) + return result; + } + + /* TODO: implement the real stuff here */ + + return result; +} + sp<LayerBuffer::Buffer> LayerBuffer::getBuffer() const { Mutex::Autolock _l(mLock); @@ -330,6 +367,15 @@ void LayerBuffer::SurfaceBuffer::unregisterBuffers() owner->unregisterBuffers(); } +sp<Overlay> LayerBuffer::SurfaceBuffer::createOverlay( + uint32_t w, uint32_t h, int32_t format) { + sp<Overlay> result; + LayerBuffer* owner(getOwner()); + if (owner) + result = owner->createOverlay(w, h, format); + return result; +} + void LayerBuffer::SurfaceBuffer::disown() { Mutex::Autolock _l(mLock); diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index ef473dd..3e616f2 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -33,6 +33,7 @@ namespace android { class MemoryDealer; class Region; +class Overlay; class LayerBuffer : public LayerBaseClient { @@ -56,6 +57,7 @@ public: PixelFormat format, const sp<IMemoryHeap>& heap); void postBuffer(ssize_t offset); void unregisterBuffers(); + sp<Overlay> createOverlay(uint32_t w, uint32_t h, int32_t format); void invalidate(); void invalidateLocked(); @@ -107,7 +109,9 @@ private: PixelFormat format, const sp<IMemoryHeap>& heap); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); - void disown(); + virtual sp<Overlay> createOverlay( + uint32_t w, uint32_t h, int32_t format); + void disown(); private: LayerBuffer* getOwner() const { Mutex::Autolock _l(mLock); diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp index fc23d53..0c347cc 100644 --- a/libs/surfaceflinger/LayerDim.cpp +++ b/libs/surfaceflinger/LayerDim.cpp @@ -48,7 +48,7 @@ void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) { // must only be called once. mDimmerDealer = flinger->getSurfaceHeapManager() - ->createHeap(NATIVE_MEMORY_TYPE_PMEM); + ->createHeap(ISurfaceComposer::eHardware); if (mDimmerDealer != 0) { mDimmerBitmap.init(mDimmerDealer); mDimmerBitmap.setBits(w, h, 1, PIXEL_FORMAT_RGB_565); @@ -81,7 +81,7 @@ void LayerDim::onDraw(const Region& clip) const mDimmerBitmap.getBitmapSurface(&src); const copybit_rect_t& srect(drect); - copybit_t* copybit = mFlinger->getBlitEngine(); + copybit_device_t* copybit = mFlinger->getBlitEngine(); copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 45496b2..e8de21a 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -38,7 +38,6 @@ #include <utils/String16.h> #include <utils/StopWatch.h> -#include <ui/BlitHardware.h> #include <ui/PixelFormat.h> #include <ui/DisplayInfo.h> #include <ui/EGLDisplaySurface.h> @@ -202,23 +201,6 @@ void SurfaceFlinger::init() { LOGI("SurfaceFlinger is starting"); - // create the shared control-block - mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY); - LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); - - mServerCblkMemory = mServerHeap->allocate(4096); - LOGE_IF(mServerCblkMemory==0, "can't create shared control block"); - - mServerCblk = static_cast<surface_flinger_cblk_t *>(mServerCblkMemory->pointer()); - LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); - new(mServerCblk) surface_flinger_cblk_t; - - // create the surface Heap manager, which manages the heaps - // (be it in RAM or VRAM) where surfaces are allocated - // We give 8 MB per client. - mSurfaceHeapManager = new SurfaceHeapManager(8 << 20); - mGPU = new GPUHardware(); - // debugging stuff... char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.showupdates", value, "0"); @@ -244,11 +226,16 @@ SurfaceFlinger::~SurfaceFlinger() glDeleteTextures(1, &mWormholeTexName); } -copybit_t* SurfaceFlinger::getBlitEngine() const +copybit_device_t* SurfaceFlinger::getBlitEngine() const { return graphicPlane(0).displayHardware().getBlitEngine(); } +overlay_device_t* SurfaceFlinger::getOverlayEngine() const +{ + return graphicPlane(0).displayHardware().getOverlayEngine(); +} + sp<IMemory> SurfaceFlinger::getCblk() const { return mServerCblkMemory; @@ -257,7 +244,9 @@ sp<IMemory> SurfaceFlinger::getCblk() const status_t SurfaceFlinger::requestGPU(const sp<IGPUCallback>& callback, gpu_info_t* gpu) { - status_t err = mGPU->request(callback, gpu); + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + status_t err = mGPU->request(pid, callback, gpu); return err; } @@ -360,7 +349,26 @@ status_t SurfaceFlinger::readyToRun() LOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); - // + // create the shared control-block + mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY); + LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); + + mServerCblkMemory = mServerHeap->allocate(4096); + LOGE_IF(mServerCblkMemory==0, "can't create shared control block"); + + mServerCblk = static_cast<surface_flinger_cblk_t *>(mServerCblkMemory->pointer()); + LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); + new(mServerCblk) surface_flinger_cblk_t; + + // get a reference to the GPU if we have one + mGPU = GPUFactory::getGPU(); + + // create the surface Heap manager, which manages the heaps + // (be it in RAM or VRAM) where surfaces are allocated + // We give 8 MB per client. + mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20); + + GLES_localSurfaceManager = static_cast<ISurfaceComposer*>(this); // we only support one display currently @@ -395,7 +403,7 @@ status_t SurfaceFlinger::readyToRun() dcblk->xdpi = hw.getDpiX(); dcblk->ydpi = hw.getDpiY(); dcblk->fps = hw.getRefreshRate(); - dcblk->density = 1.0f; // XXX: do someting more real here... + dcblk->density = hw.getDensity(); asm volatile ("":::"memory"); // Initialize OpenGL|ES @@ -407,6 +415,7 @@ status_t SurfaceFlinger::readyToRun() glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); glEnableClientState(GL_VERTEX_ARRAY); glEnable(GL_SCISSOR_TEST); glShadeModel(GL_FLAT); @@ -1679,7 +1688,7 @@ status_t SurfaceFlinger::onTransact( Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger) : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger) { - mSharedHeapAllocator = getSurfaceHeapManager()->createHeap(NATIVE_MEMORY_TYPE_HEAP); + mSharedHeapAllocator = getSurfaceHeapManager()->createHeap(); const int pgsize = getpagesize(); const int cblksize=((sizeof(per_client_cblk_t)+(pgsize-1))&~(pgsize-1)); mCblkHeap = new MemoryDealer(cblksize); @@ -1703,10 +1712,6 @@ const sp<SurfaceHeapManager>& Client::getSurfaceHeapManager() const { return mFlinger->getSurfaceHeapManager(); } -const sp<GPUHardwareInterface>& Client::getGPU() const { - return mFlinger->getGPU(); -} - int32_t Client::generateId(int pid) { const uint32_t i = clz( ~mBitmap ); @@ -1734,25 +1739,11 @@ void Client::free(int32_t id) } } -sp<MemoryDealer> Client::createAllocator(int memory_type) +sp<MemoryDealer> Client::createAllocator(uint32_t flags) { sp<MemoryDealer> allocator; - if (memory_type == NATIVE_MEMORY_TYPE_GPU) { - allocator = getGPU()->request(getClientPid()); - if (allocator == 0) - memory_type = NATIVE_MEMORY_TYPE_PMEM; - } - if (memory_type == NATIVE_MEMORY_TYPE_PMEM) { - allocator = mPMemAllocator; - if (allocator == 0) { - allocator = getSurfaceHeapManager()->createHeap( - NATIVE_MEMORY_TYPE_PMEM); - mPMemAllocator = allocator; - } - } - if (allocator == 0) - allocator = mSharedHeapAllocator; - + allocator = getSurfaceHeapManager()->createHeap( + flags, getClientPid(), mSharedHeapAllocator); return allocator; } diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index 1581474..92021d0 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -41,7 +41,8 @@ #include "BootAnimation.h" #include "Barrier.h" -struct copybit_t; +struct copybit_device_t; +struct overlay_device_t; namespace android { @@ -74,7 +75,7 @@ public: int32_t generateId(int pid); void free(int32_t id); status_t bindLayer(LayerBaseClient* layer, int32_t id); - sp<MemoryDealer> createAllocator(int memory_type); + sp<MemoryDealer> createAllocator(uint32_t memory_type); inline bool isValid(int32_t i) const; inline const uint8_t* inUseArray() const; @@ -92,7 +93,6 @@ public: private: int getClientPid() const { return mPid; } - const sp<GPUHardwareInterface>& getGPU() const; int mPid; uint32_t mBitmap; @@ -179,7 +179,8 @@ public: return mGPU; } - copybit_t* getBlitEngine() const; + copybit_device_t* getBlitEngine() const; + overlay_device_t* getOverlayEngine() const; private: friend class BClient; diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp index 3852d51..77bc576 100644 --- a/libs/surfaceflinger/VRamHeap.cpp +++ b/libs/surfaceflinger/VRamHeap.cpp @@ -37,6 +37,8 @@ #include <GLES/eglnatives.h> +#include "GPUHardware/GPUHardware.h" +#include "SurfaceFlinger.h" #include "VRamHeap.h" #if HAVE_ANDROID_OS @@ -59,8 +61,9 @@ int SurfaceHeapManager::global_pmem_heap = 0; // --------------------------------------------------------------------------- -SurfaceHeapManager::SurfaceHeapManager(size_t clientHeapSize) - : mClientHeapSize(clientHeapSize) +SurfaceHeapManager::SurfaceHeapManager(const sp<SurfaceFlinger>& flinger, + size_t clientHeapSize) + : mFlinger(flinger), mClientHeapSize(clientHeapSize) { SurfaceHeapManager::global_pmem_heap = 1; } @@ -81,25 +84,53 @@ void SurfaceHeapManager::onFirstRef() } } -sp<MemoryDealer> SurfaceHeapManager::createHeap(int type) +sp<MemoryDealer> SurfaceHeapManager::createHeap( + uint32_t flags, + pid_t client_pid, + const sp<MemoryDealer>& defaultAllocator) { - if (!global_pmem_heap && type==NATIVE_MEMORY_TYPE_PMEM) - type = NATIVE_MEMORY_TYPE_HEAP; - - const sp<PMemHeap>& heap(mPMemHeap); sp<MemoryDealer> dealer; - switch (type) { - case NATIVE_MEMORY_TYPE_HEAP: - dealer = new MemoryDealer(mClientHeapSize, 0, "SFNativeHeap"); - break; - case NATIVE_MEMORY_TYPE_PMEM: - if (heap != 0) { - dealer = new MemoryDealer( - heap->createClientHeap(), - heap->getAllocator()); + if (flags & ISurfaceComposer::eGPU) { + // don't grant GPU memory if GPU is disabled + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.hw", value, "1"); + if (atoi(value) == 0) { + flags &= ~ISurfaceComposer::eGPU; + } + } + + if (flags & ISurfaceComposer::eGPU) { + // FIXME: this is msm7201A specific, where gpu surfaces may not be secure + if (!(flags & ISurfaceComposer::eSecure)) { + // if GPU doesn't work, we try eHardware + flags |= ISurfaceComposer::eHardware; + // asked for GPU memory, try that first + dealer = mFlinger->getGPU()->request(client_pid); + } + } + + if (dealer == NULL) { + if (defaultAllocator != NULL) + // if a default allocator is given, use that + dealer = defaultAllocator; + } + + if (dealer == NULL) { + // always try h/w accelerated memory first + if (global_pmem_heap) { + const sp<PMemHeap>& heap(mPMemHeap); + if (dealer == NULL && heap != NULL) { + dealer = new MemoryDealer( + heap->createClientHeap(), + heap->getAllocator()); + } } - break; + } + + if (dealer == NULL) { + // return the ashmem allocator (software rendering) + dealer = new MemoryDealer(mClientHeapSize, 0, "SFNativeHeap"); } return dealer; } @@ -122,22 +153,8 @@ sp<SimpleBestFitAllocator> SurfaceHeapManager::getAllocator(int type) const // --------------------------------------------------------------------------- -PMemHeapInterface::PMemHeapInterface(int fd, size_t size) - : MemoryHeapBase(fd, size) { -} -PMemHeapInterface::PMemHeapInterface(const char* device, size_t size) - : MemoryHeapBase(device, size) { -} -PMemHeapInterface::PMemHeapInterface(size_t size, uint32_t flags, char const * name) - : MemoryHeapBase(size, flags, name) { -} -PMemHeapInterface::~PMemHeapInterface() { -} - -// --------------------------------------------------------------------------- - PMemHeap::PMemHeap(const char* const device, size_t size, size_t reserved) - : PMemHeapInterface(device, size) + : MemoryHeapBase(device, size) { //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID()); if (base() != MAP_FAILED) { diff --git a/libs/surfaceflinger/VRamHeap.h b/libs/surfaceflinger/VRamHeap.h index 03e0336..9140167 100644 --- a/libs/surfaceflinger/VRamHeap.h +++ b/libs/surfaceflinger/VRamHeap.h @@ -27,16 +27,19 @@ namespace android { class PMemHeap; class MemoryHeapPmem; +class SurfaceFlinger; // --------------------------------------------------------------------------- class SurfaceHeapManager : public RefBase { public: - SurfaceHeapManager(size_t clientHeapSize); + SurfaceHeapManager(const sp<SurfaceFlinger>& flinger, size_t clientHeapSize); virtual ~SurfaceHeapManager(); virtual void onFirstRef(); - sp<MemoryDealer> createHeap(int type); + /* use ISurfaceComposer flags eGPU|eHArdware|eSecure */ + sp<MemoryDealer> createHeap(uint32_t flags=0, pid_t client_pid = 0, + const sp<MemoryDealer>& defaultAllocator = 0); // used for debugging only... sp<SimpleBestFitAllocator> getAllocator(int type) const; @@ -44,6 +47,7 @@ public: private: sp<PMemHeap> getHeap(int type) const; + sp<SurfaceFlinger> mFlinger; mutable Mutex mLock; size_t mClientHeapSize; sp<PMemHeap> mPMemHeap; @@ -52,19 +56,7 @@ private: // --------------------------------------------------------------------------- -class PMemHeapInterface : public MemoryHeapBase -{ -public: - PMemHeapInterface(int fd, size_t size); - PMemHeapInterface(const char* device, size_t size = 0); - PMemHeapInterface(size_t size, uint32_t flags = 0, char const * name = NULL); - virtual ~PMemHeapInterface(); - virtual sp<MemoryHeapPmem> createClientHeap() = 0; -}; - -// --------------------------------------------------------------------------- - -class PMemHeap : public PMemHeapInterface +class PMemHeap : public MemoryHeapBase { public: PMemHeap(const char* const vram, diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 71579c5..7b51300 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - BlitHardware.cpp \ Camera.cpp \ CameraParameters.cpp \ EGLDisplaySurface.cpp \ @@ -14,10 +13,12 @@ LOCAL_SRC_FILES:= \ ICamera.cpp \ ICameraClient.cpp \ ICameraService.cpp \ + IOverlay.cpp \ ISurfaceComposer.cpp \ ISurface.cpp \ ISurfaceFlingerClient.cpp \ LayerState.cpp \ + Overlay.cpp \ PixelFormat.cpp \ Point.cpp \ Rect.cpp \ diff --git a/libs/ui/BlitHardware.cpp b/libs/ui/BlitHardware.cpp deleted file mode 100644 index 90838b4..0000000 --- a/libs/ui/BlitHardware.cpp +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (C) 2008 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 "SurfaceFlinger" - -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> - -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/mman.h> - -#include <cutils/log.h> - -#include <utils/Errors.h> - -#if HAVE_ANDROID_OS -#include <linux/fb.h> -#include <linux/msm_mdp.h> -#endif - -#include <ui/BlitHardware.h> - -/******************************************************************************/ - -namespace android { -class CopybitMSM7K : public copybit_t { -public: - CopybitMSM7K(); - ~CopybitMSM7K(); - - status_t getStatus() const { - if (mFD<0) return mFD; - return NO_ERROR; - } - - status_t setParameter(int name, int value); - - status_t get(int name); - - status_t blit( - const copybit_image_t& dst, - const copybit_image_t& src, - copybit_region_t const* region); - - status_t stretch( - const copybit_image_t& dst, - const copybit_image_t& src, - const copybit_rect_t& dst_rect, - const copybit_rect_t& src_rect, - copybit_region_t const* region); - -#if HAVE_ANDROID_OS -private: - static int copybit_set_parameter(copybit_t* handle, int name, int value); - static int copybit_blit( copybit_t* handle, - copybit_image_t const* dst, copybit_image_t const* src, - copybit_region_t const* region); - static int copybit_stretch(copybit_t* handle, - copybit_image_t const* dst, copybit_image_t const* src, - copybit_rect_t const* dst_rect, copybit_rect_t const* src_rect, - copybit_region_t const* region); - static int copybit_get(copybit_t* handle, int name); - - int getFormat(int format); - void setImage(mdp_img* img, const copybit_image_t& rhs); - void setRects(mdp_blit_req* req, const copybit_rect_t& dst, - const copybit_rect_t& src, const copybit_rect_t& scissor); - void setInfos(mdp_blit_req* req); - static void intersect(copybit_rect_t* out, - const copybit_rect_t& lhs, const copybit_rect_t& rhs); - status_t msm_copybit(void const* list); -#endif - int mFD; - uint8_t mAlpha; - uint8_t mFlags; -}; -}; // namespace android - -using namespace android; - -/******************************************************************************/ - -struct copybit_t* copybit_init() -{ - CopybitMSM7K* engine = new CopybitMSM7K(); - if (engine->getStatus() != NO_ERROR) { - delete engine; - engine = 0; - } - return (struct copybit_t*)engine; - -} - -int copybit_term(copybit_t* handle) -{ - delete static_cast<CopybitMSM7K*>(handle); - return NO_ERROR; -} - -namespace android { -/******************************************************************************/ - -static inline -int min(int a, int b) { - return (a<b) ? a : b; -} - -static inline -int max(int a, int b) { - return (a>b) ? a : b; -} - -static inline -void MULDIV(uint32_t& a, uint32_t& b, int mul, int div) -{ - if (mul != div) { - a = (mul * a) / div; - b = (mul * b) / div; - } -} - -//----------------------------------------------------------------------------- - -#if HAVE_ANDROID_OS - -int CopybitMSM7K::copybit_set_parameter(copybit_t* handle, int name, int value) -{ - return static_cast<CopybitMSM7K*>(handle)->setParameter(name, value); -} - -int CopybitMSM7K::copybit_get(copybit_t* handle, int name) -{ - return static_cast<CopybitMSM7K*>(handle)->get(name); -} - -int CopybitMSM7K::copybit_blit( - copybit_t* handle, - copybit_image_t const* dst, - copybit_image_t const* src, - struct copybit_region_t const* region) -{ - return static_cast<CopybitMSM7K*>(handle)->blit(*dst, *src, region); -} - -int CopybitMSM7K::copybit_stretch( - copybit_t* handle, - copybit_image_t const* dst, - copybit_image_t const* src, - copybit_rect_t const* dst_rect, - copybit_rect_t const* src_rect, - struct copybit_region_t const* region) -{ - return static_cast<CopybitMSM7K*>(handle)->stretch( - *dst, *src, *dst_rect, *src_rect, region); -} - -//----------------------------------------------------------------------------- - -CopybitMSM7K::CopybitMSM7K() - : mFD(-1), mAlpha(MDP_ALPHA_NOP), mFlags(0) -{ - int fd = open("/dev/graphics/fb0", O_RDWR, 0); - if (fd > 0) { - struct fb_fix_screeninfo finfo; - if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == 0) { - if (!strcmp(finfo.id, "msmfb")) { - mFD = fd; - copybit_t::set_parameter = copybit_set_parameter; - copybit_t::get = copybit_get; - copybit_t::blit = copybit_blit; - copybit_t::stretch = copybit_stretch; - } - } - } - if (fd<0 || mFD<0) { - if (fd>0) { close(fd); } - mFD = -errno; - } -} - -CopybitMSM7K::~CopybitMSM7K() -{ - if (mFD > 0){ - close(mFD); - } -} - -status_t CopybitMSM7K::setParameter(int name, int value) -{ - switch(name) { - case COPYBIT_ROTATION_DEG: - switch (value) { - case 0: - mFlags &= ~0x7; - break; - case 90: - mFlags &= ~0x7; - mFlags |= MDP_ROT_90; - break; - case 180: - mFlags &= ~0x7; - mFlags |= MDP_ROT_180; - break; - case 270: - mFlags &= ~0x7; - mFlags |= MDP_ROT_270; - break; - default: - return BAD_VALUE; - } - break; - case COPYBIT_PLANE_ALPHA: - if (value < 0) value = 0; - if (value >= 256) value = 255; - mAlpha = value; - break; - case COPYBIT_DITHER: - if (value == COPYBIT_ENABLE) { - mFlags |= MDP_DITHER; - } else if (value == COPYBIT_DISABLE) { - mFlags &= ~MDP_DITHER; - } - break; - case COPYBIT_TRANSFORM: - mFlags &= ~0x7; - mFlags |= value & 0x7; - break; - default: - return BAD_VALUE; - } - return NO_ERROR; -} - -status_t CopybitMSM7K::get(int name) -{ - switch(name) { - case COPYBIT_MINIFICATION_LIMIT: - return 4; - case COPYBIT_MAGNIFICATION_LIMIT: - return 4; - case COPYBIT_SCALING_FRAC_BITS: - return 32; - case COPYBIT_ROTATION_STEP_DEG: - return 90; - } - return BAD_VALUE; -} - -status_t CopybitMSM7K::blit( - const copybit_image_t& dst, - const copybit_image_t& src, - copybit_region_t const* region) -{ - - copybit_rect_t dr = { 0, 0, dst.w, dst.h }; - copybit_rect_t sr = { 0, 0, src.w, src.h }; - return CopybitMSM7K::stretch(dst, src, dr, sr, region); -} - -status_t CopybitMSM7K::stretch( - const copybit_image_t& dst, - const copybit_image_t& src, - const copybit_rect_t& dst_rect, - const copybit_rect_t& src_rect, - copybit_region_t const* region) -{ - struct { - uint32_t count; - struct mdp_blit_req req[12]; - } list; - - if (mAlpha<255) { - switch (src.format) { - // we dont' support plane alpha with RGBA formats - case COPYBIT_RGBA_8888: - case COPYBIT_RGBA_5551: - case COPYBIT_RGBA_4444: - return INVALID_OPERATION; - } - } - - const uint32_t maxCount = sizeof(list.req)/sizeof(list.req[0]); - const copybit_rect_t bounds = { 0, 0, dst.w, dst.h }; - copybit_rect_t clip; - list.count = 0; - int err = 0; - while (!err && region->next(region, &clip)) { - intersect(&clip, bounds, clip); - setInfos(&list.req[list.count]); - setImage(&list.req[list.count].dst, dst); - setImage(&list.req[list.count].src, src); - setRects(&list.req[list.count], dst_rect, src_rect, clip); - if (++list.count == maxCount) { - err = msm_copybit(&list); - list.count = 0; - } - } - if (!err && list.count) { - err = msm_copybit(&list); - } - return err; -} - -status_t CopybitMSM7K::msm_copybit(void const* list) -{ - int err = ioctl(mFD, MSMFB_BLIT, static_cast<mdp_blit_req_list const*>(list)); - LOGE_IF(err<0, "copyBits failed (%s)", strerror(errno)); - if (err == 0) - return NO_ERROR; - return -errno; -} - -int CopybitMSM7K::getFormat(int format) -{ - switch (format) { - case COPYBIT_RGBA_8888: return MDP_RGBA_8888; - case COPYBIT_RGB_565: return MDP_RGB_565; - case COPYBIT_YCbCr_422_SP: return MDP_Y_CBCR_H2V1; - case COPYBIT_YCbCr_420_SP: return MDP_Y_CBCR_H2V2; - } - return -1; -} - -void CopybitMSM7K::setInfos(mdp_blit_req* req) -{ - req->alpha = mAlpha; - req->transp_mask = MDP_TRANSP_NOP; - req->flags = mFlags; -} - -void CopybitMSM7K::setImage(mdp_img* img, const copybit_image_t& rhs) -{ - img->width = rhs.w; - img->height = rhs.h; - img->format = getFormat(rhs.format); - img->offset = rhs.offset; - img->memory_id = rhs.fd; -} - -void CopybitMSM7K::setRects(mdp_blit_req* e, - const copybit_rect_t& dst, const copybit_rect_t& src, - const copybit_rect_t& scissor) -{ - copybit_rect_t clip; - intersect(&clip, scissor, dst); - - e->dst_rect.x = clip.l; - e->dst_rect.y = clip.t; - e->dst_rect.w = clip.r - clip.l; - e->dst_rect.h = clip.b - clip.t; - - uint32_t W, H; - if (mFlags & COPYBIT_TRANSFORM_ROT_90) { - e->src_rect.x = (clip.t - dst.t) + src.t; - e->src_rect.y = (dst.r - clip.r) + src.l; - e->src_rect.w = (clip.b - clip.t); - e->src_rect.h = (clip.r - clip.l); - W = dst.b - dst.t; - H = dst.r - dst.l; - } else { - e->src_rect.x = (clip.l - dst.l) + src.l; - e->src_rect.y = (clip.t - dst.t) + src.t; - e->src_rect.w = (clip.r - clip.l); - e->src_rect.h = (clip.b - clip.t); - W = dst.r - dst.l; - H = dst.b - dst.t; - } - MULDIV(e->src_rect.x, e->src_rect.w, src.r - src.l, W); - MULDIV(e->src_rect.y, e->src_rect.h, src.b - src.t, H); - if (mFlags & COPYBIT_TRANSFORM_FLIP_V) { - e->src_rect.y = e->src.height - (e->src_rect.y + e->src_rect.h); - } - if (mFlags & COPYBIT_TRANSFORM_FLIP_H) { - e->src_rect.x = e->src.width - (e->src_rect.x + e->src_rect.w); - } -} - -void CopybitMSM7K::intersect(copybit_rect_t* out, - const copybit_rect_t& lhs, const copybit_rect_t& rhs) -{ - out->l = max(lhs.l, rhs.l); - out->t = max(lhs.t, rhs.t); - out->r = min(lhs.r, rhs.r); - out->b = min(lhs.b, rhs.b); -} - -/******************************************************************************/ -#else // HAVE_ANDROID_OS - -CopybitMSM7K::CopybitMSM7K() - : mFD(-1) -{ -} - -CopybitMSM7K::~CopybitMSM7K() -{ -} - -status_t CopybitMSM7K::setParameter(int name, int value) -{ - return NO_INIT; -} - -status_t CopybitMSM7K::get(int name) -{ - return BAD_VALUE; -} - -status_t CopybitMSM7K::blit( - const copybit_image_t& dst, - const copybit_image_t& src, - copybit_region_t const* region) -{ - return NO_INIT; -} - -status_t CopybitMSM7K::stretch( - const copybit_image_t& dst, - const copybit_image_t& src, - const copybit_rect_t& dst_rect, - const copybit_rect_t& src_rect, - copybit_region_t const* region) -{ - return NO_INIT; -} - -#endif // HAVE_ANDROID_OS - -/******************************************************************************/ -}; // namespace android diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp index 1528e6e..9527009 100644 --- a/libs/ui/Camera.cpp +++ b/libs/ui/Camera.cpp @@ -1,28 +1,28 @@ /* ** -** Copyright 2008, The Android Open Source Project +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. ** -** 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 +** 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 +** 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 +** 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_NDEBUG 0 #define LOG_TAG "Camera" #include <utils/Log.h> - #include <utils/IServiceManager.h> #include <utils/threads.h> #include <utils/IMemory.h> #include <ui/Surface.h> - #include <ui/Camera.h> #include <ui/ICameraService.h> @@ -60,20 +60,36 @@ const sp<ICameraService>& Camera::getCameraService() // --------------------------------------------------------------------------- Camera::Camera() - : mStatus(UNKNOWN_ERROR), - mShutterCallback(0), - mShutterCallbackCookie(0), - mRawCallback(0), - mRawCallbackCookie(0), - mJpegCallback(0), - mJpegCallbackCookie(0), - mFrameCallback(0), - mFrameCallbackCookie(0), - mErrorCallback(0), - mErrorCallbackCookie(0), - mAutoFocusCallback(0), - mAutoFocusCallbackCookie(0) { + init(); +} + +Camera::Camera(const sp<ICamera>& camera) +{ + init(); + // connect this client to existing camera remote + if (camera->connect(this) == NO_ERROR) { + mStatus = NO_ERROR; + mCamera = camera; + camera->asBinder()->linkToDeath(this); + } +} + +void Camera::init() +{ + mStatus = UNKNOWN_ERROR; + mShutterCallback = 0; + mShutterCallbackCookie = 0; + mRawCallback = 0; + mRawCallbackCookie = 0; + mJpegCallback = 0; + mJpegCallbackCookie = 0; + mFrameCallback = 0; + mFrameCallbackCookie = 0; + mErrorCallback = 0; + mErrorCallbackCookie = 0; + mAutoFocusCallback = 0; + mAutoFocusCallbackCookie = 0; } Camera::~Camera() @@ -83,6 +99,7 @@ Camera::~Camera() sp<Camera> Camera::connect() { + LOGV("connect"); sp<Camera> c = new Camera(); const sp<ICameraService>& cs = getCameraService(); if (cs != 0) { @@ -97,6 +114,7 @@ sp<Camera> Camera::connect() void Camera::disconnect() { + LOGV("disconnect"); if (mCamera != 0) { mErrorCallback = 0; mCamera->disconnect(); @@ -104,9 +122,24 @@ void Camera::disconnect() } } +status_t Camera::reconnect() +{ + LOGV("reconnect"); + if (mCamera != 0) { + return mCamera->connect(this); + } + return NO_INIT; +} + +sp<ICamera> Camera::remote() +{ + return mCamera; +} + // pass the buffered ISurface to the camera service status_t Camera::setPreviewDisplay(const sp<Surface>& surface) { + LOGV("setPreviewDisplay"); if (surface == 0) { LOGE("app passed NULL surface"); return NO_INIT; @@ -114,81 +147,105 @@ status_t Camera::setPreviewDisplay(const sp<Surface>& surface) return mCamera->setPreviewDisplay(surface->getISurface()); } +status_t Camera::setPreviewDisplay(const sp<ISurface>& surface) +{ + LOGV("setPreviewDisplay"); + if (surface == 0) { + LOGE("app passed NULL surface"); + return NO_INIT; + } + return mCamera->setPreviewDisplay(surface); +} + + // start preview mode, must call setPreviewDisplay first status_t Camera::startPreview() { + LOGV("startPreview"); return mCamera->startPreview(); } // stop preview mode void Camera::stopPreview() { + LOGV("stopPreview"); mCamera->stopPreview(); } status_t Camera::autoFocus() { + LOGV("autoFocus"); return mCamera->autoFocus(); } // take a picture status_t Camera::takePicture() { + LOGV("takePicture"); return mCamera->takePicture(); } // set preview/capture parameters - key/value pairs status_t Camera::setParameters(const String8& params) { + LOGV("setParameters"); return mCamera->setParameters(params); } // get preview/capture parameters - key/value pairs String8 Camera::getParameters() const { + LOGV("getParameters"); String8 params = mCamera->getParameters(); return params; } void Camera::setAutoFocusCallback(autofocus_callback cb, void *cookie) { + LOGV("setAutoFocusCallback"); mAutoFocusCallback = cb; mAutoFocusCallbackCookie = cookie; } void Camera::setShutterCallback(shutter_callback cb, void *cookie) { + LOGV("setShutterCallback"); mShutterCallback = cb; mShutterCallbackCookie = cookie; } void Camera::setRawCallback(frame_callback cb, void *cookie) { + LOGV("setRawCallback"); mRawCallback = cb; mRawCallbackCookie = cookie; } void Camera::setJpegCallback(frame_callback cb, void *cookie) { + LOGV("setJpegCallback"); mJpegCallback = cb; mJpegCallbackCookie = cookie; } -void Camera::setFrameCallback(frame_callback cb, void *cookie) +void Camera::setFrameCallback(frame_callback cb, void *cookie, int frame_callback_flag) { + LOGV("setFrameCallback"); mFrameCallback = cb; mFrameCallbackCookie = cookie; - mCamera->setHasFrameCallback(cb != NULL); + mCamera->setFrameCallbackFlag(frame_callback_flag); } void Camera::setErrorCallback(error_callback cb, void *cookie) { + LOGV("setErrorCallback"); mErrorCallback = cb; mErrorCallbackCookie = cookie; } void Camera::autoFocusCallback(bool focused) { + LOGV("autoFocusCallback"); if (mAutoFocusCallback) { mAutoFocusCallback(focused, mAutoFocusCallbackCookie); } @@ -196,6 +253,7 @@ void Camera::autoFocusCallback(bool focused) void Camera::shutterCallback() { + LOGV("shutterCallback"); if (mShutterCallback) { mShutterCallback(mShutterCallbackCookie); } @@ -203,6 +261,7 @@ void Camera::shutterCallback() void Camera::rawCallback(const sp<IMemory>& picture) { + LOGV("rawCallback"); if (mRawCallback) { mRawCallback(picture, mRawCallbackCookie); } @@ -211,6 +270,7 @@ void Camera::rawCallback(const sp<IMemory>& picture) // callback from camera service when image is ready void Camera::jpegCallback(const sp<IMemory>& picture) { + LOGV("jpegCallback"); if (mJpegCallback) { mJpegCallback(picture, mJpegCallbackCookie); } @@ -219,6 +279,7 @@ void Camera::jpegCallback(const sp<IMemory>& picture) // callback from camera service when video frame is ready void Camera::frameCallback(const sp<IMemory>& frame) { + LOGV("frameCallback"); if (mFrameCallback) { mFrameCallback(frame, mFrameCallbackCookie); } @@ -227,19 +288,21 @@ void Camera::frameCallback(const sp<IMemory>& frame) // callback from camera service when an error occurs in preview or takePicture void Camera::errorCallback(status_t error) { + LOGV("errorCallback"); if (mErrorCallback) { mErrorCallback(error, mErrorCallbackCookie); } } -void Camera::binderDied(const wp<IBinder>& who) { +void Camera::binderDied(const wp<IBinder>& who) { LOGW("ICamera died"); if (mErrorCallback) { mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie); } } -void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) { +void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) { + LOGV("binderDied"); Mutex::Autolock _l(Camera::mLock); Camera::mCameraService.clear(); LOGW("Camera server died!"); diff --git a/libs/ui/EGLDisplaySurface.cpp b/libs/ui/EGLDisplaySurface.cpp index ea245f5..44258a8 100644 --- a/libs/ui/EGLDisplaySurface.cpp +++ b/libs/ui/EGLDisplaySurface.cpp @@ -28,11 +28,15 @@ #include <cutils/log.h> #include <cutils/atomic.h> +#include <cutils/properties.h> + +#include <hardware/copybit.h> #include <ui/SurfaceComposerClient.h> #include <ui/DisplayInfo.h> #include <ui/Rect.h> #include <ui/Region.h> +#include <ui/EGLDisplaySurface.h> #if HAVE_ANDROID_OS #include <linux/msm_mdp.h> @@ -42,7 +46,6 @@ #include <pixelflinger/format.h> -#include <ui/EGLDisplaySurface.h> // ---------------------------------------------------------------------------- @@ -78,7 +81,12 @@ EGLDisplaySurface::EGLDisplaySurface() mBlitEngine = 0; egl_native_window_t::fd = mapFrameBuffer(); if (egl_native_window_t::fd >= 0) { - mBlitEngine = copybit_init(); + + hw_module_t const* module; + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } + const float in2mm = 25.4f; float refreshRate = 1000000000000000LLU / ( float( mInfo.upper_margin + mInfo.lower_margin + mInfo.yres ) @@ -108,7 +116,7 @@ EGLDisplaySurface::EGLDisplaySurface() EGLDisplaySurface::~EGLDisplaySurface() { magic = 0; - copybit_term(mBlitEngine); + copybit_close(mBlitEngine); mBlitEngine = 0; close(egl_native_window_t::fd); munmap(mFb[0].data, mSize); @@ -147,9 +155,6 @@ void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h) uint32_t EGLDisplaySurface::swapBuffers() { - if (!(mFlags & PAGE_FLIP)) - return 0; - #define SHOW_FPS 0 #if SHOW_FPS nsecs_t now = systemTime(); @@ -171,6 +176,11 @@ uint32_t EGLDisplaySurface::swapBuffers() } } #endif + /* If we can't do the page_flip, just copy the back buffer to the front */ + if (!(mFlags & PAGE_FLIP)) { + memcpy(mFb[0].data, mFb[1].data, mInfo.xres*mInfo.yres*2); + return 0; + } // do the actual flip mIndex = 1 - mIndex; @@ -192,7 +202,7 @@ uint32_t EGLDisplaySurface::swapBuffers() * with msm7k. */ if (egl_native_window_t::memory_type == NATIVE_MEMORY_TYPE_GPU && oem[0] && mBlitEngine) { - copybit_t *copybit = mBlitEngine; + copybit_device_t *copybit = mBlitEngine; copybit_rect_t sdrect = { 0, 0, egl_native_window_t::width, egl_native_window_t::height }; copybit_image_t dst = { @@ -273,6 +283,12 @@ void EGLDisplaySurface::copyFrontToBack(const Region& copyback) } else #endif { + /* no extra copy needed since we copied back to front instead of + * flipping */ + if (!(mFlags & PAGE_FLIP)) { + return; + } + Region::iterator iterator(copyback); if (iterator) { Rect r; @@ -373,12 +389,11 @@ status_t EGLDisplaySurface::mapFrameBuffer() // bleagh, bad info from the driver refreshRate = 60*1000; // 60 Hz } - if (int(info.width) <= 0 || int(info.height) <= 0) { // the driver doesn't return that information // default to 160 dpi - info.width = 51; - info.height = 38; + info.width = ((info.xres * 25.4f)/160.0f + 0.5f); + info.height = ((info.yres * 25.4f)/160.0f + 0.5f); } float xdpi = (info.xres * 25.4f) / info.width; diff --git a/libs/ui/EGLNativeWindowSurface.cpp b/libs/ui/EGLNativeWindowSurface.cpp index 0b6afc0..d55fb70 100644 --- a/libs/ui/EGLNativeWindowSurface.cpp +++ b/libs/ui/EGLNativeWindowSurface.cpp @@ -163,7 +163,12 @@ void EGLNativeWindowSurface::connect() egl_native_window_t::format = info.format; egl_native_window_t::base = intptr_t(info.base); egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); - egl_native_window_t::memory_type = mSurface->getMemoryType(); + // FIXME: egl_native_window_t::memory_type used to be set from + // mSurface, but we wanted to break this dependency. We set it to + // GPU because the software rendered doesn't care, but the h/w + // accelerator needs it. Eventually, this value should go away + // completely, since memory will be managed by OpenGL. + egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_GPU; egl_native_window_t::fd = 0; } } diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index f0c77ba..abe7407 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -315,9 +315,8 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, } //printf("poll %d, returned %d\n", mFDCount, pollres); - if(mFDs[0].revents & POLLIN) { - read_notify(mFDs[0].fd); - } + + // mFDs[0] is used for inotify, so process regular events starting at mFDs[1] for(i = 1; i < mFDCount; i++) { if(mFDs[i].revents) { LOGV("revents for %d = 0x%08x", i, mFDs[i].revents); @@ -357,6 +356,12 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, } } } + + // read_notify() will modify mFDs and mFDCount, so this must be done after + // processing all other events. + if(mFDs[0].revents & POLLIN) { + read_notify(mFDs[0].fd); + } } } @@ -607,7 +612,7 @@ int EventHub::open_device(const char *deviceName) sprintf(propName, "hw.keyboards.%u.devname", publicID); property_set(propName, devname); - LOGI("New keyboard: publicID=%d device->id=%d devname='%s propName='%s' keylayout='%s'\n", + LOGI("New keyboard: publicID=%d device->id=%d devname='%s' propName='%s' keylayout='%s'\n", publicID, device->id, devname, propName, keylayoutFilename); } diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp index 420bb49..6a2dc6b 100644 --- a/libs/ui/ICamera.cpp +++ b/libs/ui/ICamera.cpp @@ -2,41 +2,40 @@ ** ** Copyright 2008, 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 +** 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 +** 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 +** 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_NDEBUG 0 +#define LOG_TAG "ICamera" +#include <utils/Log.h> #include <stdint.h> #include <sys/types.h> - #include <utils/Parcel.h> - #include <ui/ICamera.h> -#define LOG_TAG "@@@@@@@@@@@ CAMERA @@@@@@@@@@@" -#include <utils/Log.h> - namespace android { enum { DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, SET_PREVIEW_DISPLAY, - SET_HAS_FRAME_CALLBACK, + SET_FRAME_CALLBACK_FLAG, START_PREVIEW, STOP_PREVIEW, AUTO_FOCUS, TAKE_PICTURE, SET_PARAMETERS, - GET_PARAMETERS + GET_PARAMETERS, + CONNECT }; class BpCamera: public BpInterface<ICamera> @@ -50,6 +49,7 @@ public: // disconnect from camera service void disconnect() { + LOGV("disconnect"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); remote()->transact(DISCONNECT, data, &reply); @@ -58,25 +58,29 @@ public: // pass the buffered ISurface to the camera service status_t setPreviewDisplay(const sp<ISurface>& surface) { + LOGV("setPreviewDisplay"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); data.writeStrongBinder(surface->asBinder()); remote()->transact(SET_PREVIEW_DISPLAY, data, &reply); return reply.readInt32(); } - - // tell the service whether to callback with each preview frame - void setHasFrameCallback(bool installed) + + // set the frame callback flag to affect how the received frames from + // preview are handled. + void setFrameCallbackFlag(int frame_callback_flag) { + LOGV("setFrameCallbackFlag(%d)", frame_callback_flag); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - data.writeInt32((int32_t)installed); - remote()->transact(SET_HAS_FRAME_CALLBACK, data, &reply); + data.writeInt32(frame_callback_flag); + remote()->transact(SET_FRAME_CALLBACK_FLAG, data, &reply); } // start preview mode, must call setPreviewDisplay first status_t startPreview() { + LOGV("startPreview"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); remote()->transact(START_PREVIEW, data, &reply); @@ -86,6 +90,7 @@ public: // stop preview mode void stopPreview() { + LOGV("stopPreview"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); remote()->transact(STOP_PREVIEW, data, &reply); @@ -94,6 +99,7 @@ public: // auto focus status_t autoFocus() { + LOGV("autoFocus"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); remote()->transact(AUTO_FOCUS, data, &reply); @@ -104,6 +110,7 @@ public: // take a picture - returns an IMemory (ref-counted mmap) status_t takePicture() { + LOGV("takePicture"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); remote()->transact(TAKE_PICTURE, data, &reply); @@ -114,6 +121,7 @@ public: // set preview/capture parameters - key/value pairs status_t setParameters(const String8& params) { + LOGV("setParameters"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); data.writeString8(params); @@ -124,11 +132,20 @@ public: // get preview/capture parameters - key/value pairs String8 getParameters() const { + LOGV("getParameters"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); remote()->transact(GET_PARAMETERS, data, &reply); return reply.readString8(); } + virtual status_t connect(const sp<ICameraClient>& cameraClient) + { + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeStrongBinder(cameraClient->asBinder()); + remote()->transact(CONNECT, data, &reply); + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(Camera, "android.hardware.ICamera"); @@ -146,53 +163,68 @@ status_t BnCamera::onTransact( { switch(code) { case DISCONNECT: { + LOGV("DISCONNECT"); CHECK_INTERFACE(ICamera, data, reply); disconnect(); return NO_ERROR; } break; case SET_PREVIEW_DISPLAY: { + LOGV("SET_PREVIEW_DISPLAY"); CHECK_INTERFACE(ICamera, data, reply); sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder()); reply->writeInt32(setPreviewDisplay(surface)); return NO_ERROR; } break; - case SET_HAS_FRAME_CALLBACK: { + case SET_FRAME_CALLBACK_FLAG: { + LOGV("SET_FRAME_CALLBACK_TYPE"); CHECK_INTERFACE(ICamera, data, reply); - bool installed = (bool)data.readInt32(); - setHasFrameCallback(installed); + int frame_callback_flag = data.readInt32(); + setFrameCallbackFlag(frame_callback_flag); return NO_ERROR; } break; case START_PREVIEW: { + LOGV("START_PREVIEW"); CHECK_INTERFACE(ICamera, data, reply); reply->writeInt32(startPreview()); return NO_ERROR; } break; case STOP_PREVIEW: { + LOGV("STOP_PREVIEW"); CHECK_INTERFACE(ICamera, data, reply); stopPreview(); return NO_ERROR; } break; case AUTO_FOCUS: { + LOGV("AUTO_FOCUS"); CHECK_INTERFACE(ICamera, data, reply); reply->writeInt32(autoFocus()); return NO_ERROR; } break; case TAKE_PICTURE: { + LOGV("TAKE_PICTURE"); CHECK_INTERFACE(ICamera, data, reply); reply->writeInt32(takePicture()); return NO_ERROR; } break; case SET_PARAMETERS: { + LOGV("SET_PARAMETERS"); CHECK_INTERFACE(ICamera, data, reply); - String8 params(data.readString8()); - reply->writeInt32(setParameters(params)); + String8 params(data.readString8()); + reply->writeInt32(setParameters(params)); return NO_ERROR; } break; case GET_PARAMETERS: { + LOGV("GET_PARAMETERS"); CHECK_INTERFACE(ICamera, data, reply); reply->writeString8(getParameters()); return NO_ERROR; } break; + case CONNECT: { + CHECK_INTERFACE(ICamera, data, reply); + sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder()); + reply->writeInt32(connect(cameraClient)); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/ui/ICameraClient.cpp b/libs/ui/ICameraClient.cpp index 3737034..c5d6d52 100644 --- a/libs/ui/ICameraClient.cpp +++ b/libs/ui/ICameraClient.cpp @@ -15,9 +15,11 @@ ** limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "ICameraClient" +#include <utils/Log.h> #include <stdint.h> #include <sys/types.h> - #include <ui/ICameraClient.h> namespace android { @@ -42,6 +44,7 @@ public: // callback to let the app know the shutter has closed, ideal for playing the shutter sound void shutterCallback() { + LOGV("shutterCallback"); Parcel data, reply; data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); remote()->transact(SHUTTER_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); @@ -50,6 +53,7 @@ public: // callback from camera service to app with picture data void rawCallback(const sp<IMemory>& picture) { + LOGV("rawCallback"); Parcel data, reply; data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeStrongBinder(picture->asBinder()); @@ -59,6 +63,7 @@ public: // callback from camera service to app with picture data void jpegCallback(const sp<IMemory>& picture) { + LOGV("jpegCallback"); Parcel data, reply; data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeStrongBinder(picture->asBinder()); @@ -68,6 +73,7 @@ public: // callback from camera service to app with video frame data void frameCallback(const sp<IMemory>& frame) { + LOGV("frameCallback"); Parcel data, reply; data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeStrongBinder(frame->asBinder()); @@ -77,6 +83,7 @@ public: // callback from camera service to app to report error void errorCallback(status_t error) { + LOGV("errorCallback"); Parcel data, reply; data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeInt32(error); @@ -86,6 +93,7 @@ public: // callback from camera service to app to report autofocus completion void autoFocusCallback(bool focused) { + LOGV("autoFocusCallback"); Parcel data, reply; data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); data.writeInt32(focused); @@ -108,35 +116,41 @@ status_t BnCameraClient::onTransact( { switch(code) { case SHUTTER_CALLBACK: { + LOGV("SHUTTER_CALLBACK"); CHECK_INTERFACE(ICameraClient, data, reply); shutterCallback(); return NO_ERROR; } break; case RAW_CALLBACK: { + LOGV("RAW_CALLBACK"); CHECK_INTERFACE(ICameraClient, data, reply); sp<IMemory> picture = interface_cast<IMemory>(data.readStrongBinder()); rawCallback(picture); return NO_ERROR; } break; case JPEG_CALLBACK: { + LOGV("JPEG_CALLBACK"); CHECK_INTERFACE(ICameraClient, data, reply); sp<IMemory> picture = interface_cast<IMemory>(data.readStrongBinder()); jpegCallback(picture); return NO_ERROR; } break; case FRAME_CALLBACK: { + LOGV("FRAME_CALLBACK"); CHECK_INTERFACE(ICameraClient, data, reply); sp<IMemory> frame = interface_cast<IMemory>(data.readStrongBinder()); frameCallback(frame); return NO_ERROR; } break; case ERROR_CALLBACK: { + LOGV("ERROR_CALLBACK"); CHECK_INTERFACE(ICameraClient, data, reply); status_t error = data.readInt32(); errorCallback(error); return NO_ERROR; } break; case AUTOFOCUS_CALLBACK: { + LOGV("AUTOFOCUS_CALLBACK"); CHECK_INTERFACE(ICameraClient, data, reply); bool focused = (bool)data.readInt32(); autoFocusCallback(focused); diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp new file mode 100644 index 0000000..59d1ea0 --- /dev/null +++ b/libs/ui/IOverlay.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 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. + */ + +#include <stdio.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> +#include <utils/IInterface.h> + +#include <ui/IOverlay.h> + +namespace android { + +enum { + DESTROY = IBinder::FIRST_CALL_TRANSACTION, // one-way transaction + SWAP_BUFFERS, +}; + +class BpOverlay : public BpInterface<IOverlay> +{ +public: + BpOverlay(const sp<IBinder>& impl) + : BpInterface<IOverlay>(impl) + { + } + + virtual void destroy() + { + Parcel data, reply; + data.writeInterfaceToken(IOverlay::getInterfaceDescriptor()); + remote()->transact(DESTROY, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual ssize_t swapBuffers() + { + Parcel data, reply; + data.writeInterfaceToken(IOverlay::getInterfaceDescriptor()); + remote()->transact(SWAP_BUFFERS, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnOverlay::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DESTROY: { + CHECK_INTERFACE(IOverlay, data, reply); + destroy(); + return NO_ERROR; + } break; + case SWAP_BUFFERS: { + CHECK_INTERFACE(IOverlay, data, reply); + ssize_t offset = swapBuffers(); + reply->writeInt32(offset); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp index 817f4d9..c1c9596 100644 --- a/libs/ui/ISurface.cpp +++ b/libs/ui/ISurface.cpp @@ -22,6 +22,7 @@ #include <utils/IMemory.h> #include <ui/ISurface.h> +#include <ui/Overlay.h> namespace android { @@ -30,6 +31,7 @@ enum { REGISTER_BUFFERS = IBinder::FIRST_CALL_TRANSACTION, UNREGISTER_BUFFERS, POST_BUFFER, // one-way transaction + CREATE_OVERLAY, }; class BpSurface : public BpInterface<ISurface> @@ -70,6 +72,18 @@ public: data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); remote()->transact(UNREGISTER_BUFFERS, data, &reply); } + + virtual sp<Overlay> createOverlay( + uint32_t w, uint32_t h, int32_t format) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + remote()->transact(CREATE_OVERLAY, data, &reply); + return Overlay::readFromParcel(reply); + } }; IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface"); @@ -109,6 +123,14 @@ status_t BnSurface::onTransact( postBuffer(offset); return NO_ERROR; } break; + case CREATE_OVERLAY: { + CHECK_INTERFACE(ISurface, data, reply); + int w = data.readInt32(); + int h = data.readInt32(); + int f = data.readInt32(); + sp<Overlay> o = createOverlay(w, h, w); + return Overlay::writeToParcel(reply, o); + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp index 9444af7..dd6a798 100644 --- a/libs/ui/ISurfaceFlingerClient.cpp +++ b/libs/ui/ISurfaceFlingerClient.cpp @@ -82,7 +82,7 @@ public: data.writeInt32(format); data.writeInt32(flags); remote()->transact(CREATE_SURFACE, data, &reply); - params->readFromParcel(data); + params->readFromParcel(reply); return interface_cast<ISurface>(reply.readStrongBinder()); } @@ -191,7 +191,6 @@ status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& par { token = parcel.readInt32(); identity = parcel.readInt32(); - type = parcel.readInt32(); heap[0] = interface_cast<IMemoryHeap>(parcel.readStrongBinder()); heap[1] = interface_cast<IMemoryHeap>(parcel.readStrongBinder()); return NO_ERROR; @@ -201,7 +200,6 @@ status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) co { parcel->writeInt32(token); parcel->writeInt32(identity); - parcel->writeInt32(type); parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); return NO_ERROR; diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp new file mode 100644 index 0000000..2267c3e --- /dev/null +++ b/libs/ui/Overlay.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 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. + */ + +#include <utils/IMemory.h> +#include <utils/Parcel.h> + +#include <ui/IOverlay.h> +#include <ui/Overlay.h> + +namespace android { + +Overlay::Overlay(overlay_handle_t* handle, + const sp<IOverlay>& o, const sp<IMemoryHeap>& heap, + uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs) + : mOverlay(o), mHeap(heap), mCurrentBufferOffset(0), mOverlayHandle(handle), + mWidth(w), mHeight(h), mFormat(f), mWidthStride(ws), mHeightStride(hs) +{ +} + +Overlay::Overlay(overlay_t* overlay, + const sp<IOverlay>& o, const sp<IMemoryHeap>& heap) + : mOverlay(o), mHeap(heap) +{ + mCurrentBufferOffset = 0; + mOverlayHandle = overlay->getHandleRef(overlay); + mWidth = overlay->w; + mHeight = overlay->h; + mFormat = overlay->format; + mWidthStride = overlay->w_stride; + mHeightStride = overlay->h_stride; +} + + +Overlay::~Overlay() { +} + +void Overlay::destroy() { + mOverlay->destroy(); +} + +status_t Overlay::swapBuffers() { + ssize_t result = mOverlay->swapBuffers(); + if (result < 0) + return status_t(result); + mCurrentBufferOffset = result; + return NO_ERROR; +} + +overlay_handle_t const* Overlay::getHandleRef() const { + return mOverlayHandle; +} + +size_t Overlay::getBufferOffset() const { + return mCurrentBufferOffset; +} + +sp<IMemoryHeap> Overlay::getHeap() const { + return mHeap; +} + +uint32_t Overlay::getWidth() const { + return mWidth; +} + +uint32_t Overlay::getHeight() const { + return mHeight; +} + +int32_t Overlay::getFormat() const { + return mFormat; +} + +int32_t Overlay::getWidthStride() const { + return mWidthStride; +} + +int32_t Overlay::getHeightStride() const { + return mHeightStride; +} + +sp<Overlay> Overlay::readFromParcel(const Parcel& data) { + sp<Overlay> result; + sp<IOverlay> overlay = IOverlay::asInterface(data.readStrongBinder()); + if (overlay != NULL) { + sp<IMemoryHeap> heap = IMemoryHeap::asInterface(data.readStrongBinder()); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t f = data.readInt32(); + uint32_t ws = data.readInt32(); + uint32_t hs = data.readInt32(); + /* FIXME: handles should be promoted to "real" API and be handled by + * the framework */ + int numfd = data.readInt32(); + int numint = data.readInt32(); + overlay_handle_t* handle = (overlay_handle_t*)malloc( + sizeof(overlay_handle_t) + numint*sizeof(int)); + for (int i=0 ; i<numfd ; i++) + handle->fds[i] = data.readFileDescriptor(); + for (int i=0 ; i<numint ; i++) + handle->data[i] = data.readInt32(); + result = new Overlay(handle, overlay, heap, w, h, f, ws, hs); + } + return result; +} + +status_t Overlay::writeToParcel(Parcel* reply, const sp<Overlay>& o) { + if (o != NULL) { + reply->writeStrongBinder(o->mOverlay->asBinder()); + reply->writeStrongBinder(o->mHeap->asBinder()); + reply->writeInt32(o->mWidth); + reply->writeInt32(o->mHeight); + reply->writeInt32(o->mFormat); + reply->writeInt32(o->mWidthStride); + reply->writeInt32(o->mHeightStride); + /* FIXME: handles should be promoted to "real" API and be handled by + * the framework */ + reply->writeInt32(o->mOverlayHandle->numFds); + reply->writeInt32(o->mOverlayHandle->numInts); + for (int i=0 ; i<o->mOverlayHandle->numFds ; i++) + reply->writeFileDescriptor(o->mOverlayHandle->fds[i]); + for (int i=0 ; i<o->mOverlayHandle->numInts ; i++) + reply->writeInt32(o->mOverlayHandle->data[i]); + } else { + reply->writeStrongBinder(NULL); + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp index 0a9aaad..4ea9ae2 100644 --- a/libs/ui/Surface.cpp +++ b/libs/ui/Surface.cpp @@ -47,7 +47,7 @@ Surface::Surface(const sp<SurfaceComposerClient>& client, const ISurfaceFlingerClient::surface_data_t& data, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, bool owner) - : mClient(client), mSurface(surface), mMemoryType(data.type), + : mClient(client), mSurface(surface), mToken(data.token), mIdentity(data.identity), mFormat(format), mFlags(flags), mOwner(owner) { @@ -67,7 +67,6 @@ Surface::Surface(Surface const* rhs) mSurface = rhs->mSurface; mHeap[0] = rhs->mHeap[0]; mHeap[1] = rhs->mHeap[1]; - mMemoryType = rhs->mMemoryType; mFormat = rhs->mFormat; mFlags = rhs->mFlags; mSurfaceHeapBase[0] = rhs->mSurfaceHeapBase[0]; @@ -186,7 +185,6 @@ sp<Surface> Surface::readFromParcel(Parcel* parcel) sp<ISurface> surface = interface_cast<ISurface>(parcel->readStrongBinder()); data.heap[0] = interface_cast<IMemoryHeap>(parcel->readStrongBinder()); data.heap[1] = interface_cast<IMemoryHeap>(parcel->readStrongBinder()); - data.type = parcel->readInt32(); data.token = parcel->readInt32(); data.identity = parcel->readInt32(); PixelFormat format = parcel->readInt32(); @@ -207,7 +205,6 @@ status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel) sp<SurfaceComposerClient> client; sp<ISurface> sur; sp<IMemoryHeap> heap[2]; - int type = 0; if (surface->isValid()) { token = surface->mToken; identity = surface->mIdentity; @@ -215,7 +212,6 @@ status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel) sur = surface->mSurface; heap[0] = surface->mHeap[0]; heap[1] = surface->mHeap[1]; - type = surface->mMemoryType; format = surface->mFormat; flags = surface->mFlags; } @@ -223,7 +219,6 @@ status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel) parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); - parcel->writeInt32(type); parcel->writeInt32(token); parcel->writeInt32(identity); parcel->writeInt32(format); diff --git a/libs/ui/Time.cpp b/libs/ui/Time.cpp index c98667f..b553913 100644 --- a/libs/ui/Time.cpp +++ b/libs/ui/Time.cpp @@ -85,10 +85,10 @@ Time::switchTimezone(const char* timezone) } String8 -Time::format(const char *format) const +Time::format(const char *format, const struct strftime_locale *locale) const { char buf[257]; - int n = strftime(buf, 257, format, &(this->t)); + int n = strftime_tz(buf, 257, format, &(this->t), locale); if (n > 0) { return String8(buf); } else { diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 4a68dc1..cdb8ca2 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -139,6 +139,14 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libcutils +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) +# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp +LOCAL_SHARED_LIBRARIES += \ + libdl +endif # linux-x86 +endif # sim + LOCAL_MODULE:= libutils #LOCAL_CFLAGS+= diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 4968666..26fb22a 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -79,35 +79,35 @@ int backtrace(const void** addrs, size_t ignore, size_t size) /*****************************************************************************/ static -const char *lookup_symbol(const void* addr, uint32_t *offset, char* name, size_t bufSize) +const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize) { #if HAVE_DLADDR - Dl_info info; - if (dladdr(addr, &info)) { - *offset = (uint32_t)info.dli_saddr; - return info.dli_sname; - } + Dl_info info; + if (dladdr(addr, &info)) { + *offset = info.dli_saddr; + return info.dli_sname; + } #endif - return NULL; + return NULL; } static int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) { - size_t out_len = 0; + size_t out_len = 0; #if HAVE_CXXABI - int status = 0; - char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); - if (status == 0) { - // OK - if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); - else out_len = 0; - free(demangled); - } else { - out_len = 0; - } + int status = 0; + char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); + if (status == 0) { + // OK + if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); + else out_len = 0; + free(demangled); + } else { + out_len = 0; + } #endif - return out_len; + return out_len; } /*****************************************************************************/ @@ -115,12 +115,12 @@ int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size class MapInfo { struct mapinfo { struct mapinfo *next; - unsigned start; - unsigned end; + uint64_t start; + uint64_t end; char name[]; }; - const char *map_to_name(unsigned pc, const char* def) { + const char *map_to_name(uint64_t pc, const char* def) { mapinfo* mi = getMapInfoList(); while(mi) { if ((pc >= mi->start) && (pc < mi->end)) @@ -139,8 +139,8 @@ class MapInfo { if (line[20] != 'x') return 0; mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); if (mi == 0) return 0; - mi->start = strtoul(line, 0, 16); - mi->end = strtoul(line + 9, 0, 16); + mi->start = strtoull(line, 0, 16); + mi->end = strtoull(line + 9, 0, 16); mi->next = 0; strcpy(mi->name, line + 49); return mi; @@ -184,7 +184,7 @@ public: } static const char *mapAddressToName(const void* pc, const char* def) { - return sMapInfo.map_to_name((unsigned)pc, def); + return sMapInfo.map_to_name((uint64_t)pc, def); } }; @@ -278,7 +278,7 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const char tmp[256]; char tmp1[32]; char tmp2[32]; - uint32_t offs; + void *offs; const void* ip = mStack[level]; if (!ip) return res; @@ -291,14 +291,14 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const if (name) { if (linux_gcc_demangler(name, tmp, 256) != 0) name = tmp; - snprintf(tmp1, 32, "0x%08x: <", (size_t)ip); - snprintf(tmp2, 32, ">+0x%08x", offs); + snprintf(tmp1, 32, "0x%p: <", ip); + snprintf(tmp2, 32, ">+0x%p", offs); res.append(tmp1); res.append(name); res.append(tmp2); } else { name = MapInfo::mapAddressToName(ip, "<unknown>"); - snprintf(tmp, 256, "pc %08x %s", (size_t)ip, name); + snprintf(tmp, 256, "pc %p %s", ip, name); res.append(tmp); } res.append("\n"); diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp index ca49d9a..04ae142 100644 --- a/libs/utils/IPCThreadState.cpp +++ b/libs/utils/IPCThreadState.cpp @@ -391,6 +391,29 @@ void IPCThreadState::joinThreadPool(bool isMain) status_t result; do { int32_t cmd; + + // When we've cleared the incoming command queue, process any pending derefs + if (mIn.dataPosition() >= mIn.dataSize()) { + size_t numPending = mPendingWeakDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + RefBase::weakref_type* refs = mPendingWeakDerefs[i]; + refs->decWeak(mProcess.get()); + } + mPendingWeakDerefs.clear(); + } + + numPending = mPendingStrongDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + BBinder* obj = mPendingStrongDerefs[i]; + obj->decStrong(mProcess.get()); + } + mPendingStrongDerefs.clear(); + } + } + + // now get the next command to be processed, waiting if necessary result = talkWithDriver(); if (result >= NO_ERROR) { size_t IN = mIn.dataAvail(); @@ -832,7 +855,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); obj->printRefs(); } - obj->decStrong(mProcess.get()); + mPendingStrongDerefs.push(obj); break; case BR_INCREFS: @@ -853,7 +876,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) //LOG_ASSERT(refs->refBase() == obj, // "BR_DECREFS: object %p does not match cookie %p (expected %p)", // refs, obj, refs->refBase()); - refs->decWeak(mProcess.get()); + mPendingWeakDerefs.push(refs); break; case BR_ATTEMPT_ACQUIRE: diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp index e64f794..55c1b99 100644 --- a/libs/utils/LogSocket.cpp +++ b/libs/utils/LogSocket.cpp @@ -16,7 +16,7 @@ #ifndef HAVE_WINSOCK -#define SOCKETLOG +//#define SOCKETLOG #endif #ifdef SOCKETLOG diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp index e6d1d18..cf8201b 100644 --- a/libs/utils/MemoryDealer.cpp +++ b/libs/utils/MemoryDealer.cpp @@ -387,21 +387,23 @@ SimpleMemory::~SimpleMemory() start = (start + pagesize-1) & ~(pagesize-1); end &= ~(pagesize-1); - void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); - size_t size = end-start; + if (start < end) { + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); + size_t size = end-start; #ifndef NDEBUG - memset(start_ptr, 0xdf, size); + memset(start_ptr, 0xdf, size); #endif - -// MADV_REMOVE is not defined on Dapper based Goobuntu + + // MADV_REMOVE is not defined on Dapper based Goobuntu #ifdef MADV_REMOVE - if (size) { - int err = madvise(start_ptr, size, MADV_REMOVE); - LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", - start_ptr, size, err<0 ? strerror(errno) : "Ok"); - } + if (size) { + int err = madvise(start_ptr, size, MADV_REMOVE); + LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", + start_ptr, size, err<0 ? strerror(errno) : "Ok"); + } #endif + } } }; // namespace android diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp index 1e5a1cc..eba2b30 100644 --- a/libs/utils/MemoryHeapPmem.cpp +++ b/libs/utils/MemoryHeapPmem.cpp @@ -38,9 +38,20 @@ namespace android { // --------------------------------------------------------------------------- -class MemoryHeapPmem; +MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp<MemoryHeapPmem>& heap) + : BnMemory(), mClientHeap(heap) +{ +} + +MemoryHeapPmem::MemoryPmem::~MemoryPmem() { + if (mClientHeap != NULL) { + mClientHeap->remove(this); + } +} -class SubRegionMemory : public BnMemory { +// --------------------------------------------------------------------------- + +class SubRegionMemory : public MemoryHeapPmem::MemoryPmem { public: SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size); virtual ~SubRegionMemory(); @@ -50,15 +61,14 @@ private: void revoke(); size_t mSize; ssize_t mOffset; - sp<MemoryHeapPmem> mClientHeap; }; SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size) - : mSize(size), mOffset(offset), mClientHeap(heap) + : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset) { #ifndef NDEBUG - void* const start_ptr = (void*)(intptr_t(mClientHeap->base()) + offset); + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset); memset(start_ptr, 0xda, size); #endif @@ -80,7 +90,7 @@ sp<IMemoryHeap> SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const { if (offset) *offset = mOffset; if (size) *size = mSize; - return mClientHeap; + return getHeap(); } SubRegionMemory::~SubRegionMemory() @@ -98,8 +108,9 @@ void SubRegionMemory::revoke() // promote() it. #if HAVE_ANDROID_OS - if (mClientHeap != NULL) { - int our_fd = mClientHeap->heapID(); + if (mSize != NULL) { + const sp<MemoryHeapPmem>& heap(getHeap()); + int our_fd = heap->heapID(); struct pmem_region sub; sub.offset = mOffset; sub.len = mSize; @@ -107,7 +118,7 @@ void SubRegionMemory::revoke() LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " "mFD=%d, sub.offset=%lu, sub.size=%lu", strerror(errno), our_fd, sub.offset, sub.len); - mClientHeap.clear(); + mSize = 0; } #endif } @@ -157,10 +168,7 @@ MemoryHeapPmem::~MemoryHeapPmem() sp<IMemory> MemoryHeapPmem::mapMemory(size_t offset, size_t size) { - sp<SubRegionMemory> memory; - if (heapID() > 0) - memory = new SubRegionMemory(this, offset, size); - + sp<MemoryPmem> memory = createMemory(offset, size); if (memory != 0) { Mutex::Autolock _l(mLock); mAllocations.add(memory); @@ -168,6 +176,15 @@ sp<IMemory> MemoryHeapPmem::mapMemory(size_t offset, size_t size) return memory; } +sp<MemoryHeapPmem::MemoryPmem> MemoryHeapPmem::createMemory( + size_t offset, size_t size) +{ + sp<SubRegionMemory> memory; + if (heapID() > 0) + memory = new SubRegionMemory(this, offset, size); + return memory; +} + status_t MemoryHeapPmem::slap() { #if HAVE_ANDROID_OS @@ -206,21 +223,26 @@ status_t MemoryHeapPmem::unslap() void MemoryHeapPmem::revoke() { - Vector< wp<SubRegionMemory> > allocations; + SortedVector< wp<MemoryPmem> > allocations; { // scope for lock Mutex::Autolock _l(mLock); allocations = mAllocations; - mAllocations.clear(); } ssize_t count = allocations.size(); for (ssize_t i=0 ; i<count ; i++) { - sp<SubRegionMemory> memory(allocations[i].promote()); + sp<MemoryPmem> memory(allocations[i].promote()); if (memory != 0) memory->revoke(); } } +void MemoryHeapPmem::remove(const wp<MemoryPmem>& memory) +{ + Mutex::Autolock _l(mLock); + mAllocations.remove(memory); +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index a5fe9fb..5a09fb4 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -164,7 +164,11 @@ void Res_png_9patch::fileToDevice() size_t Res_png_9patch::serializedSize() { - return sizeof(Res_png_9patch) + // The size of this struct is 32 bytes on the 32-bit target system + // 4 * int8_t + // 4 * int32_t + // 3 * pointer + return 32 + numXDivs * sizeof(int32_t) + numYDivs * sizeof(int32_t) + numColors * sizeof(uint32_t); @@ -180,8 +184,10 @@ void* Res_png_9patch::serialize() void Res_png_9patch::serialize(void * outData) { char* data = (char*) outData; - memmove(data, this, sizeof(Res_png_9patch)); - data += sizeof(Res_png_9patch); + memmove(data, &wasDeserialized, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + memmove(data + 12, &paddingLeft, 16); // copy paddingXXXX + data += 32; + memmove(data, this->xDivs, numXDivs * sizeof(int32_t)); data += numXDivs * sizeof(int32_t); memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); @@ -189,27 +195,32 @@ void Res_png_9patch::serialize(void * outData) memmove(data, this->colors, numColors * sizeof(uint32_t)); } -Res_png_9patch* Res_png_9patch::deserialize(const void* inData) -{ - deserialize(inData, (Res_png_9patch*) inData); - return (Res_png_9patch*) inData; -} - -void Res_png_9patch::deserialize(const void* inData, Res_png_9patch* outData) { - Res_png_9patch* patch = (Res_png_9patch*) inData; +static void deserializeInternal(const void* inData, Res_png_9patch* outData) { + char* patch = (char*) inData; if (inData != outData) { - memcpy(outData, inData, patch->serializedSize()); + memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors } outData->wasDeserialized = true; char* data = (char*)outData; data += sizeof(Res_png_9patch); outData->xDivs = (int32_t*) data; - data += patch->numXDivs * sizeof(int32_t); + data += outData->numXDivs * sizeof(int32_t); outData->yDivs = (int32_t*) data; - data += patch->numYDivs * sizeof(int32_t); + data += outData->numYDivs * sizeof(int32_t); outData->colors = (uint32_t*) data; } +Res_png_9patch* Res_png_9patch::deserialize(const void* inData) +{ + if (sizeof(void*) != sizeof(int32_t)) { + LOGE("Cannot deserialize on non 32-bit system\n"); + return NULL; + } + deserializeInternal(inData, (Res_png_9patch*) inData); + return (Res_png_9patch*) inData; +} + // -------------------------------------------------------------------- // -------------------------------------------------------------------- // -------------------------------------------------------------------- @@ -3863,7 +3874,7 @@ void ResTable::print() const } for (size_t configIndex=0; configIndex<NTC; configIndex++) { const ResTable_type* type = typeConfigs->configs[configIndex]; - if ((((int)type)&0x3) != 0) { + if ((((uint64_t)type)&0x3) != 0) { printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c index c13760d..ba19520 100644 --- a/libs/utils/futex_synchro.c +++ b/libs/utils/futex_synchro.c @@ -25,8 +25,8 @@ #include <private/utils/futex_synchro.h> -// This futex glue code is need on desktop linux, but is part of klibc on ARM -#if !defined(__arm__) +// This futex glue code is need on desktop linux, but is already part of bionic. +#if !defined(HAVE_FUTEX_WRAPPERS) #include <sys/syscall.h> typedef unsigned int u32; @@ -76,7 +76,7 @@ int __atomic_cmpxchg(int old, int _new, volatile int *ptr); int __atomic_swap(int _new, volatile int *ptr); int __atomic_dec(volatile int *ptr); -#endif // !defined(__arm__) +#endif // !defined(HAVE_FUTEX_WRAPPERS) // lock states |