diff options
27 files changed, 1444 insertions, 329 deletions
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index df59dcff0b..6419a5ce12 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -669,8 +669,14 @@ status_t CameraService::Client::startRecording() LOGD("startRecording (pid %d)", getCallingPid()); if (mMediaPlayerBeep.get() != NULL) { - mMediaPlayerBeep->seekTo(0); - mMediaPlayerBeep->start(); + // do not play record jingle if stream volume is 0 + // (typically because ringer mode is silent). + int index; + AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); + if (index != 0) { + mMediaPlayerBeep->seekTo(0); + mMediaPlayerBeep->start(); + } } mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME); @@ -888,8 +894,14 @@ void CameraService::Client::handleShutter( { // Play shutter sound. if (mMediaPlayerClick.get() != NULL) { - mMediaPlayerClick->seekTo(0); - mMediaPlayerClick->start(); + // do not play shutter sound if stream volume is 0 + // (typically because ringer mode is silent). + int index; + AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); + if (index != 0) { + mMediaPlayerClick->seekTo(0); + mMediaPlayerClick->start(); + } } // Screen goes black after the buffer is unregistered. diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h index 273d92231f..5f9f69c042 100644 --- a/include/binder/IInterface.h +++ b/include/binder/IInterface.h @@ -72,21 +72,24 @@ protected: // ---------------------------------------------------------------------- #define DECLARE_META_INTERFACE(INTERFACE) \ - static const String16 descriptor; \ - static sp<I##INTERFACE> asInterface(const sp<IBinder>& obj); \ - virtual const String16& getInterfaceDescriptor() const; \ + static const android::String16 descriptor; \ + static android::sp<I##INTERFACE> asInterface( \ + const android::sp<android::IBinder>& obj); \ + virtual const android::String16& getInterfaceDescriptor() const; \ I##INTERFACE(); \ virtual ~I##INTERFACE(); \ #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ - const String16 I##INTERFACE::descriptor(NAME); \ - const String16& I##INTERFACE::getInterfaceDescriptor() const { \ + const android::String16 I##INTERFACE::descriptor(NAME); \ + const android::String16& \ + I##INTERFACE::getInterfaceDescriptor() const { \ return I##INTERFACE::descriptor; \ } \ - sp<I##INTERFACE> I##INTERFACE::asInterface(const sp<IBinder>& obj) \ + android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ + const android::sp<android::IBinder>& obj) \ { \ - sp<I##INTERFACE> intr; \ + android::sp<I##INTERFACE> intr; \ if (obj != NULL) { \ intr = static_cast<I##INTERFACE*>( \ obj->queryLocalInterface( \ diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h index 571e47b22d..e81d0f9064 100644..100755 --- a/include/ui/KeycodeLabels.h +++ b/include/ui/KeycodeLabels.h @@ -114,6 +114,10 @@ static const KeycodeLabel KEYCODES[] = { { "MEDIA_REWIND", 89 }, { "MEDIA_FAST_FORWARD", 90 }, { "MUTE", 91 }, + { "PAGE_UP", 92 }, + { "PAGE_DOWN", 93 }, + { "PICTSYMBOLS", 94 }, + { "SWITCH_CHARSET", 95 }, // NOTE: If you add a new keycode here you must also add it to: // (enum KeyCode, in this file) @@ -218,7 +222,11 @@ typedef enum KeyCode { kKeyCodePreviousSong = 88, kKeyCodeRewind = 89, kKeyCodeForward = 90, - kKeyCodeMute = 91 + kKeyCodeMute = 91, + kKeyCodePageUp = 92, + kKeyCodePageDown = 93, + kKeyCodePictSymbols = 94, + kKeyCodeSwitchCharset = 95 } KeyCode; static const KeycodeLabel FLAGS[] = { diff --git a/include/ui/Surface.h b/include/ui/Surface.h index 70303cdb8c..008c297294 100644 --- a/include/ui/Surface.h +++ b/include/ui/Surface.h @@ -109,7 +109,7 @@ private: ~SurfaceControl(); - status_t validate(SharedClient const* cblk) const; + status_t validate() const; void destroy(); sp<SurfaceComposerClient> mClient; @@ -190,7 +190,7 @@ private: status_t getBufferLocked(int index, int usage); - status_t validate(SharedClient const* cblk) const; + status_t validate() const; inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index ebd470f412..ecfe1e0eda 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -674,32 +674,13 @@ void AudioFlinger::binderDied(const wp<IBinder>& who) { } // audioConfigChanged_l() must be called with AudioFlinger::mLock held -void AudioFlinger::audioConfigChanged_l(int event, const sp<ThreadBase>& thread, void *param2) { - int ioHandle = 0; - - for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - if (mPlaybackThreads.valueAt(i) == thread) { - ioHandle = mPlaybackThreads.keyAt(i); - break; - } - } - if (ioHandle == 0) { - for (size_t i = 0; i < mRecordThreads.size(); i++) { - if (mRecordThreads.valueAt(i) == thread) { - ioHandle = mRecordThreads.keyAt(i); - break; - } - } - } - - if (ioHandle != 0) { - size_t size = mNotificationClients.size(); - for (size_t i = 0; i < size; i++) { - sp<IBinder> binder = mNotificationClients.itemAt(i); - LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get()); - sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); - client->ioConfigChanged(event, ioHandle, param2); - } +void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2) { + size_t size = mNotificationClients.size(); + for (size_t i = 0; i < size; i++) { + sp<IBinder> binder = mNotificationClients.itemAt(i); + LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get()); + sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); + client->ioConfigChanged(event, ioHandle, param2); } } @@ -712,10 +693,10 @@ void AudioFlinger::removeClient_l(pid_t pid) // ---------------------------------------------------------------------------- -AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger) +AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id) : Thread(false), mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0), - mFormat(0), mFrameSize(1), mStandby(false) + mFormat(0), mFrameSize(1), mStandby(false), mId(id), mExiting(false) { } @@ -734,6 +715,7 @@ void AudioFlinger::ThreadBase::exit() LOGV("ThreadBase::exit"); { AutoMutex lock(&mLock); + mExiting = true; requestExit(); mWaitWorkCV.signal(); } @@ -870,8 +852,8 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args // ---------------------------------------------------------------------------- -AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) - : ThreadBase(audioFlinger), +AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) + : ThreadBase(audioFlinger, id), mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output), mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false) { @@ -950,6 +932,8 @@ status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String result.append(buffer); snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); result.append(buffer); + snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended); + result.append(buffer); write(fd, result.string(), result.size()); dumpBase(fd, args); @@ -1106,15 +1090,6 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) { status_t status = ALREADY_EXISTS; - // here the track could be either new, or restarted - // in both cases "unstop" the track - if (track->isPaused()) { - track->mState = TrackBase::RESUMING; - LOGV("PAUSED => RESUMING (%d) on thread %p", track->name(), this); - } else { - track->mState = TrackBase::ACTIVE; - LOGV("? => ACTIVE (%d) on thread %p", track->name(), this); - } // set retry count for buffer fill track->mRetryCount = kMaxTrackStartupRetries; if (mActiveTracks.indexOf(track) < 0) { @@ -1138,7 +1113,6 @@ void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track) { track->mState = TrackBase::TERMINATED; if (mActiveTracks.indexOf(track) < 0) { - LOGV("remove track (%d) and delete from mixer", track->name()); mTracks.remove(track); deleteTrackName_l(track->name()); } @@ -1173,7 +1147,7 @@ void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) { break; } Mutex::Autolock _l(mAudioFlinger->mLock); - mAudioFlinger->audioConfigChanged_l(event, this, param2); + mAudioFlinger->audioConfigChanged_l(event, mId, param2); } void AudioFlinger::PlaybackThread::readOutputParameters() @@ -1185,7 +1159,6 @@ void AudioFlinger::PlaybackThread::readOutputParameters() mFrameSize = mOutput->frameSize(); mFrameCount = mOutput->bufferSize() / mFrameSize; - mMinBytesToWrite = (mOutput->latency() * mSampleRate * mFrameSize) / 1000; // FIXME - Current mixer implementation only supports stereo output: Always // Allocate a stereo buffer even if HW output is mono. if (mMixBuffer != NULL) delete mMixBuffer; @@ -1195,8 +1168,8 @@ void AudioFlinger::PlaybackThread::readOutputParameters() // ---------------------------------------------------------------------------- -AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) - : PlaybackThread(audioFlinger, output), +AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) + : PlaybackThread(audioFlinger, output, id), mAudioMixer(0) { mType = PlaybackThread::MIXER; @@ -1215,23 +1188,25 @@ AudioFlinger::MixerThread::~MixerThread() bool AudioFlinger::MixerThread::threadLoop() { - uint32_t sleepTime = 1000; - uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; - size_t enabledTracks = 0; + uint32_t mixerStatus = MIXER_IDLE; nsecs_t standbyTime = systemTime(); size_t mixBufferSize = mFrameCount * mFrameSize; // FIXME: Relaxed timing because of a certain device that can't meet latency // Should be reduced to 2x after the vendor fixes the driver issue nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 3; nsecs_t lastWarning = 0; + bool longStandbyExit = false; + uint32_t activeSleepTime = activeSleepTimeUs(); + uint32_t idleSleepTime = idleSleepTimeUs(); + uint32_t sleepTime = idleSleepTime; while (!exitPending()) { processConfigEvents(); - enabledTracks = 0; + mixerStatus = MIXER_IDLE; { // scope for mLock Mutex::Autolock _l(mLock); @@ -1241,7 +1216,8 @@ bool AudioFlinger::MixerThread::threadLoop() // FIXME: Relaxed timing because of a certain device that can't meet latency // Should be reduced to 2x after the vendor fixes the driver issue maxPeriod = seconds(mFrameCount) / mSampleRate * 3; - maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); + activeSleepTime = activeSleepTimeUs(); + idleSleepTime = idleSleepTimeUs(); } const SortedVector< wp<Track> >& activeTracks = mActiveTracks; @@ -1277,15 +1253,15 @@ bool AudioFlinger::MixerThread::threadLoop() } standbyTime = systemTime() + kStandbyTimeInNsecs; - sleepTime = 1000; + sleepTime = idleSleepTime; continue; } } - enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); + mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); } - if (LIKELY(enabledTracks)) { + if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { // mix buffers... mAudioMixer->process(curBuf); sleepTime = 0; @@ -1294,15 +1270,21 @@ bool AudioFlinger::MixerThread::threadLoop() // If no tracks are ready, sleep once for the duration of an output // buffer size, then write 0s to the output if (sleepTime == 0) { - sleepTime = maxBufferRecoveryInUsecs; - } else if (mBytesWritten != 0) { + if (mixerStatus == MIXER_TRACKS_ENABLED) { + sleepTime = activeSleepTime; + } else { + sleepTime = idleSleepTime; + } + } else if (mBytesWritten != 0 || + (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) { memset (curBuf, 0, mixBufferSize); sleepTime = 0; + LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start"); } } if (mSuspended) { - sleepTime = maxBufferRecoveryInUsecs; + sleepTime = idleSleepTime; } // sleepTime == 0 means we must write to audio hardware if (sleepTime == 0) { @@ -1312,7 +1294,6 @@ bool AudioFlinger::MixerThread::threadLoop() if (bytesWritten > 0) mBytesWritten += bytesWritten; mNumWrites++; mInWrite = false; - mStandby = false; nsecs_t now = systemTime(); nsecs_t delta = now - mLastWriteTime; if (delta > maxPeriod) { @@ -1322,7 +1303,11 @@ bool AudioFlinger::MixerThread::threadLoop() ns2ms(delta), mNumDelayedWrites, this); lastWarning = now; } + if (mStandby) { + longStandbyExit = true; + } } + mStandby = false; } else { usleep(sleepTime); } @@ -1342,10 +1327,10 @@ bool AudioFlinger::MixerThread::threadLoop() } // prepareTracks_l() must be called with ThreadBase::mLock held -size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove) +uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove) { - size_t enabledTracks = 0; + uint32_t mixerStatus = MIXER_IDLE; // find out which tracks need to be processed size_t count = activeTracks.size(); for (size_t i=0 ; i<count ; i++) { @@ -1361,7 +1346,7 @@ size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> if (cblk->framesReady() && (track->isReady() || track->isStopped()) && !track->isPaused()) { - //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this); // compute volume for this track int16_t left, right; @@ -1415,9 +1400,9 @@ size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> // reset retry count track->mRetryCount = kMaxTrackRetries; - enabledTracks++; + mixerStatus = MIXER_TRACKS_READY; } else { - //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); + //LOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", track->name(), cblk->user, cblk->server, this); if (track->isStopped()) { track->reset(); } @@ -1432,16 +1417,11 @@ size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> if (--(track->mRetryCount) <= 0) { LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this); tracksToRemove->add(track); + } else if (mixerStatus != MIXER_TRACKS_READY) { + mixerStatus = MIXER_TRACKS_ENABLED; } - // For tracks using static shared memory buffer, make sure that we have - // written enough data to audio hardware before disabling the track - // NOTE: this condition with arrive before track->mRetryCount <= 0 so we - // don't care about code removing track from active list above. - if ((track->mSharedBuffer == 0) || (mBytesWritten >= mMinBytesToWrite)) { - mAudioMixer->disable(AudioMixer::MIXING); - } else { - enabledTracks++; - } + + mAudioMixer->disable(AudioMixer::MIXING); } } } @@ -1459,7 +1439,7 @@ size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> } } - return enabledTracks; + return mixerStatus; } void AudioFlinger::MixerThread::getTracks( @@ -1532,6 +1512,7 @@ int AudioFlinger::MixerThread::getTrackName_l() // deleteTrackName_l() must be called with ThreadBase::mLock held void AudioFlinger::MixerThread::deleteTrackName_l(int name) { + LOGV("remove track (%d) and delete from mixer", name); mAudioMixer->deleteTrackName(name); } @@ -1621,19 +1602,19 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> return NO_ERROR; } -uint32_t AudioFlinger::MixerThread::getMaxBufferRecoveryInUsecs() +uint32_t AudioFlinger::MixerThread::activeSleepTimeUs() { - uint32_t time = ((mFrameCount * 1000) / mSampleRate) * 1000; - // Add some margin with regard to scheduling precision - if (time > 10000) { - time -= 10000; - } - return time; + return (uint32_t)(mOutput->latency() * 1000) / 2; +} + +uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() +{ + return (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000; } // ---------------------------------------------------------------------------- -AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) - : PlaybackThread(audioFlinger, output), +AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) + : PlaybackThread(audioFlinger, output, id), mLeftVolume (1.0), mRightVolume(1.0) { mType = PlaybackThread::DIRECT; @@ -1646,25 +1627,31 @@ AudioFlinger::DirectOutputThread::~DirectOutputThread() bool AudioFlinger::DirectOutputThread::threadLoop() { - uint32_t sleepTime = 1000; - uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); + uint32_t mixerStatus = MIXER_IDLE; sp<Track> trackToRemove; sp<Track> activeTrack; nsecs_t standbyTime = systemTime(); int8_t *curBuf; size_t mixBufferSize = mFrameCount*mFrameSize; + uint32_t activeSleepTime = activeSleepTimeUs(); + uint32_t idleSleepTime = idleSleepTimeUs(); + uint32_t sleepTime = idleSleepTime; + while (!exitPending()) { processConfigEvents(); + mixerStatus = MIXER_IDLE; + { // scope for the mLock Mutex::Autolock _l(mLock); if (checkForNewParameters_l()) { mixBufferSize = mFrameCount*mFrameSize; - maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); + activeSleepTime = activeSleepTimeUs(); + idleSleepTime = idleSleepTimeUs(); } // put audio hardware into standby after short delay @@ -1698,7 +1685,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } standbyTime = systemTime() + kStandbyTimeInNsecs; - sleepTime = 1000; + sleepTime = idleSleepTime; continue; } } @@ -1753,6 +1740,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() // reset retry count track->mRetryCount = kMaxTrackRetries; activeTrack = t; + mixerStatus = MIXER_TRACKS_READY; } else { //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); if (track->isStopped()) { @@ -1768,16 +1756,10 @@ bool AudioFlinger::DirectOutputThread::threadLoop() if (--(track->mRetryCount) <= 0) { LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); trackToRemove = track; + } else { + mixerStatus = MIXER_TRACKS_ENABLED; } - - // For tracks using static shared memry buffer, make sure that we have - // written enough data to audio hardware before disabling the track - // NOTE: this condition with arrive before track->mRetryCount <= 0 so we - // don't care about code removing track from active list above. - if ((track->mSharedBuffer != 0) && (mBytesWritten < mMinBytesToWrite)) { - activeTrack = t; - } - } + } } } @@ -1791,7 +1773,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } } - if (activeTrack != 0) { + if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { AudioBufferProvider::Buffer buffer; size_t frameCount = mFrameCount; curBuf = (int8_t *)mMixBuffer; @@ -1812,7 +1794,11 @@ bool AudioFlinger::DirectOutputThread::threadLoop() standbyTime = systemTime() + kStandbyTimeInNsecs; } else { if (sleepTime == 0) { - sleepTime = maxBufferRecoveryInUsecs; + if (mixerStatus == MIXER_TRACKS_ENABLED) { + sleepTime = activeSleepTime; + } else { + sleepTime = idleSleepTime; + } } else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) { memset (mMixBuffer, 0, mFrameCount * mFrameSize); sleepTime = 0; @@ -1820,7 +1806,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } if (mSuspended) { - sleepTime = maxBufferRecoveryInUsecs; + sleepTime = idleSleepTime; } // sleepTime == 0 means we must write to audio hardware if (sleepTime == 0) { @@ -1905,15 +1891,22 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() return reconfig; } -uint32_t AudioFlinger::DirectOutputThread::getMaxBufferRecoveryInUsecs() +uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs() { uint32_t time; if (AudioSystem::isLinearPCM(mFormat)) { - time = ((mFrameCount * 1000) / mSampleRate) * 1000; - // Add some margin with regard to scheduling precision - if (time > 10000) { - time -= 10000; - } + time = (uint32_t)(mOutput->latency() * 1000) / 2; + } else { + time = 10000; + } + return time; +} + +uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs() +{ + uint32_t time; + if (AudioSystem::isLinearPCM(mFormat)) { + time = (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000; } else { time = 10000; } @@ -1922,8 +1915,8 @@ uint32_t AudioFlinger::DirectOutputThread::getMaxBufferRecoveryInUsecs() // ---------------------------------------------------------------------------- -AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread) - : MixerThread(audioFlinger, mainThread->getOutput()) +AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id) + : MixerThread(audioFlinger, mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX) { mType = PlaybackThread::DUPLICATING; addOutputTrack(mainThread); @@ -1931,33 +1924,39 @@ AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audio AudioFlinger::DuplicatingThread::~DuplicatingThread() { + for (size_t i = 0; i < mOutputTracks.size(); i++) { + mOutputTracks[i]->destroy(); + } mOutputTracks.clear(); } bool AudioFlinger::DuplicatingThread::threadLoop() { - uint32_t sleepTime = 1000; - uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; - size_t enabledTracks = 0; + uint32_t mixerStatus = MIXER_IDLE; nsecs_t standbyTime = systemTime(); size_t mixBufferSize = mFrameCount*mFrameSize; SortedVector< sp<OutputTrack> > outputTracks; uint32_t writeFrames = 0; + uint32_t activeSleepTime = activeSleepTimeUs(); + uint32_t idleSleepTime = idleSleepTimeUs(); + uint32_t sleepTime = idleSleepTime; while (!exitPending()) { processConfigEvents(); - enabledTracks = 0; + mixerStatus = MIXER_IDLE; { // scope for the mLock Mutex::Autolock _l(mLock); if (checkForNewParameters_l()) { mixBufferSize = mFrameCount*mFrameSize; - maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); + updateWaitTime(); + activeSleepTime = activeSleepTimeUs(); + idleSleepTime = idleSleepTimeUs(); } const SortedVector< wp<Track> >& activeTracks = mActiveTracks; @@ -1997,22 +1996,30 @@ bool AudioFlinger::DuplicatingThread::threadLoop() } standbyTime = systemTime() + kStandbyTimeInNsecs; - sleepTime = 1000; + sleepTime = idleSleepTime; continue; } } - enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); + mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); } - if (LIKELY(enabledTracks)) { + if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { // mix buffers... - mAudioMixer->process(curBuf); + if (outputsReady(outputTracks)) { + mAudioMixer->process(curBuf); + } else { + memset(curBuf, 0, mixBufferSize); + } sleepTime = 0; writeFrames = mFrameCount; } else { if (sleepTime == 0) { - sleepTime = maxBufferRecoveryInUsecs; + if (mixerStatus == MIXER_TRACKS_ENABLED) { + sleepTime = activeSleepTime; + } else { + sleepTime = idleSleepTime; + } } else if (mBytesWritten != 0) { // flush remaining overflow buffers in output tracks for (size_t i = 0; i < outputTracks.size(); i++) { @@ -2026,7 +2033,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() } if (mSuspended) { - sleepTime = maxBufferRecoveryInUsecs; + sleepTime = idleSleepTime; } // sleepTime == 0 means we must write to audio hardware if (sleepTime == 0) { @@ -2047,17 +2054,6 @@ bool AudioFlinger::DuplicatingThread::threadLoop() outputTracks.clear(); } - { // scope for the mLock - - Mutex::Autolock _l(mLock); - if (!mStandby) { - LOGV("DuplicatingThread() exiting out of standby"); - for (size_t i = 0; i < mOutputTracks.size(); i++) { - mOutputTracks[i]->destroy(); - } - } - } - return false; } @@ -2065,6 +2061,7 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) { int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate(); OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread, + this, mSampleRate, mFormat, mChannelCount, @@ -2073,6 +2070,7 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f); mOutputTracks.add(outputTrack); LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread); + updateWaitTime(); } } @@ -2083,12 +2081,50 @@ void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread) if (mOutputTracks[i]->thread() == (ThreadBase *)thread) { mOutputTracks[i]->destroy(); mOutputTracks.removeAt(i); + updateWaitTime(); return; } } LOGV("removeOutputTrack(): unkonwn thread: %p", thread); } +void AudioFlinger::DuplicatingThread::updateWaitTime() +{ + mWaitTimeMs = UINT_MAX; + for (size_t i = 0; i < mOutputTracks.size(); i++) { + sp<ThreadBase> strong = mOutputTracks[i]->thread().promote(); + if (strong != NULL) { + uint32_t waitTimeMs = (strong->frameCount() * 2 * 1000) / strong->sampleRate(); + if (waitTimeMs < mWaitTimeMs) { + mWaitTimeMs = waitTimeMs; + } + } + } +} + + +bool AudioFlinger::DuplicatingThread::outputsReady(SortedVector< sp<OutputTrack> > &outputTracks) +{ + for (size_t i = 0; i < outputTracks.size(); i++) { + sp <ThreadBase> thread = outputTracks[i]->thread().promote(); + if (thread == 0) { + LOGW("DuplicatingThread::outputsReady() could not promote thread on output track %p", outputTracks[i].get()); + return false; + } + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if (playbackThread->standby() && !playbackThread->isSuspended()) { + LOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(), thread.get()); + return false; + } + } + return true; +} + +uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs() +{ + return (mWaitTimeMs * 1000) / 2; +} + // ---------------------------------------------------------------------------- // TrackBase constructor must be called with AudioFlinger::mLock held @@ -2299,6 +2335,12 @@ void AudioFlinger::PlaybackThread::Track::destroy() { // scope for mLock sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { + if (!isOutputTrack()) { + if (mState == ACTIVE || mState == RESUMING) { + AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + } + AudioSystem::releaseOutput(thread->id()); + } Mutex::Autolock _l(thread->mLock); PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); playbackThread->destroyTrack_l(this); @@ -2380,14 +2422,37 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const { status_t AudioFlinger::PlaybackThread::Track::start() { + status_t status = NO_ERROR; LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { Mutex::Autolock _l(thread->mLock); - PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); - playbackThread->addTrack_l(this); + int state = mState; + // here the track could be either new, or restarted + // in both cases "unstop" the track + if (mState == PAUSED) { + mState = TrackBase::RESUMING; + LOGV("PAUSED => RESUMING (%d) on thread %p", mName, this); + } else { + mState = TrackBase::ACTIVE; + LOGV("? => ACTIVE (%d) on thread %p", mName, this); + } + + if (!isOutputTrack() && state != ACTIVE && state != RESUMING) { + thread->mLock.unlock(); + status = AudioSystem::startOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + thread->mLock.lock(); + } + if (status == NO_ERROR) { + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + playbackThread->addTrack_l(this); + } else { + mState = state; + } + } else { + status = BAD_VALUE; } - return NO_ERROR; + return status; } void AudioFlinger::PlaybackThread::Track::stop() @@ -2396,6 +2461,7 @@ void AudioFlinger::PlaybackThread::Track::stop() sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { Mutex::Autolock _l(thread->mLock); + int state = mState; if (mState > STOPPED) { mState = STOPPED; // If the track is not active (PAUSED and buffers full), flush buffers @@ -2405,6 +2471,11 @@ void AudioFlinger::PlaybackThread::Track::stop() } LOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread); } + if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) { + thread->mLock.unlock(); + AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + thread->mLock.lock(); + } } } @@ -2417,6 +2488,11 @@ void AudioFlinger::PlaybackThread::Track::pause() if (mState == ACTIVE || mState == RESUMING) { mState = PAUSING; LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get()); + if (!isOutputTrack()) { + thread->mLock.unlock(); + AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + thread->mLock.lock(); + } } } } @@ -2500,6 +2576,10 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( AudioFlinger::RecordThread::RecordTrack::~RecordTrack() { + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + AudioSystem::releaseInput(thread->id()); + } } status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) @@ -2547,8 +2627,9 @@ status_t AudioFlinger::RecordThread::RecordTrack::start() if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); return recordThread->start(this); + } else { + return BAD_VALUE; } - return NO_INIT; } void AudioFlinger::RecordThread::RecordTrack::stop() @@ -2582,12 +2663,13 @@ void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( const wp<ThreadBase>& thread, + DuplicatingThread *sourceThread, uint32_t sampleRate, int format, int channelCount, int frameCount) : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL), - mActive(false) + mActive(false), mSourceThread(sourceThread) { PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get(); @@ -2596,10 +2678,9 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); mCblk->volume[0] = mCblk->volume[1] = 0x1000; mOutBuffer.frameCount = 0; - mWaitTimeMs = (playbackThread->frameCount() * 2 * 1000) / playbackThread->sampleRate(); playbackThread->mTracks.add(this); - LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p mWaitTimeMs %d", - mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd, mWaitTimeMs); + LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd); } else { LOGW("Error creating output track on thread %p", playbackThread); } @@ -2639,7 +2720,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr inBuffer.frameCount = frames; inBuffer.i16 = data; - uint32_t waitTimeLeftMs = mWaitTimeMs; + uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs(); if (!mActive && frames != 0) { start(); @@ -2678,12 +2759,11 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr mOutBuffer.frameCount = pInBuffer->frameCount; nsecs_t startTime = systemTime(); if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) { - LOGV ("OutputTrack::write() %p no more output buffers", this); + LOGV ("OutputTrack::write() %p thread %p no more output buffers", this, mThread.unsafe_get()); outputBufferFull = true; break; } uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime); - LOGV("OutputTrack::write() to thread %p waitTimeMs %d waitTimeLeftMs %d", mThread.unsafe_get(), waitTimeMs, waitTimeLeftMs); if (waitTimeLeftMs >= waitTimeMs) { waitTimeLeftMs -= waitTimeMs; } else { @@ -2704,7 +2784,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr mBufferQueue.removeAt(0); delete [] pInBuffer->mBuffer; delete pInBuffer; - LOGV("OutputTrack::write() %p released overflow buffer %d", this, mBufferQueue.size()); + LOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); } else { break; } @@ -2713,16 +2793,19 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr // If we could not write all frames, allocate a buffer and queue it for next time. if (inBuffer.frameCount) { - if (mBufferQueue.size() < kMaxOverFlowBuffers) { - pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; - pInBuffer->frameCount = inBuffer.frameCount; - pInBuffer->i16 = pInBuffer->mBuffer; - memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); - mBufferQueue.add(pInBuffer); - LOGV("OutputTrack::write() %p adding overflow buffer %d", this, mBufferQueue.size()); - } else { - LOGW("OutputTrack::write() %p no more overflow buffers", this); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0 && !thread->standby()) { + if (mBufferQueue.size() < kMaxOverFlowBuffers) { + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; + pInBuffer->frameCount = inBuffer.frameCount; + pInBuffer->i16 = pInBuffer->mBuffer; + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + LOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); + } else { + LOGW("OutputTrack::write() %p thread %p no more overflow buffers", mThread.unsafe_get(), this); + } } } @@ -2985,8 +3068,8 @@ status_t AudioFlinger::RecordHandle::onTransact( // ---------------------------------------------------------------------------- -AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels) : - ThreadBase(audioFlinger), +AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels, int id) : + ThreadBase(audioFlinger, id), mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0) { mReqChannelCount = AudioSystem::popCount(channels); @@ -3014,6 +3097,7 @@ void AudioFlinger::RecordThread::onFirstRef() run(buffer, PRIORITY_URGENT_AUDIO); } + bool AudioFlinger::RecordThread::threadLoop() { AudioBufferProvider::Buffer buffer; @@ -3043,22 +3127,37 @@ bool AudioFlinger::RecordThread::threadLoop() } if (mActiveTrack != 0) { if (mActiveTrack->mState == TrackBase::PAUSING) { + if (!mStandby) { + mInput->standby(); + mStandby = true; + } mActiveTrack.clear(); mStartStopCond.broadcast(); } else if (mActiveTrack->mState == TrackBase::RESUMING) { - mRsmpInIndex = mFrameCount; if (mReqChannelCount != mActiveTrack->channelCount()) { mActiveTrack.clear(); - } else { - mActiveTrack->mState = TrackBase::ACTIVE; + mStartStopCond.broadcast(); + } else if (mBytesRead != 0) { + // record start succeeds only if first read from audio input + // succeeds + if (mBytesRead > 0) { + mActiveTrack->mState = TrackBase::ACTIVE; + } else { + mActiveTrack.clear(); + } + mStartStopCond.broadcast(); } - mStartStopCond.broadcast(); + mStandby = false; } - mStandby = false; } } if (mActiveTrack != 0) { + if (mActiveTrack->mState != TrackBase::ACTIVE && + mActiveTrack->mState != TrackBase::RESUMING) { + usleep(5000); + continue; + } buffer.frameCount = mFrameCount; if (LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) { size_t framesOut = buffer.frameCount; @@ -3093,18 +3192,19 @@ bool AudioFlinger::RecordThread::threadLoop() } } if (framesOut && mFrameCount == mRsmpInIndex) { - ssize_t bytesRead; if (framesOut == mFrameCount && (mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) { - bytesRead = mInput->read(buffer.raw, mInputBytes); + mBytesRead = mInput->read(buffer.raw, mInputBytes); framesOut = 0; } else { - bytesRead = mInput->read(mRsmpInBuffer, mInputBytes); + mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes); mRsmpInIndex = 0; } - if (bytesRead < 0) { + if (mBytesRead < 0) { LOGE("Error reading audio input"); - sleep(1); + if (mActiveTrack->mState == TrackBase::ACTIVE) { + sleep(1); + } mRsmpInIndex = mFrameCount; framesOut = 0; buffer.frameCount = 0; @@ -3156,6 +3256,8 @@ bool AudioFlinger::RecordThread::threadLoop() } mActiveTrack.clear(); + mStartStopCond.broadcast(); + LOGV("RecordThread %p exiting", this); return false; } @@ -3163,37 +3265,74 @@ bool AudioFlinger::RecordThread::threadLoop() status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack) { LOGV("RecordThread::start"); - AutoMutex lock(&mLock); - - if (mActiveTrack != 0) { - if (recordTrack != mActiveTrack.get()) return -EBUSY; - - if (mActiveTrack->mState == TrackBase::PAUSING) mActiveTrack->mState = TrackBase::RESUMING; - - return NO_ERROR; - } + sp <ThreadBase> strongMe = this; + status_t status = NO_ERROR; + { + AutoMutex lock(&mLock); + if (mActiveTrack != 0) { + if (recordTrack != mActiveTrack.get()) { + status = -EBUSY; + } else if (mActiveTrack->mState == TrackBase::PAUSING) { + mActiveTrack->mState = TrackBase::ACTIVE; + } + return status; + } - mActiveTrack = recordTrack; - mActiveTrack->mState = TrackBase::RESUMING; - // signal thread to start - LOGV("Signal record thread"); - mWaitWorkCV.signal(); - mStartStopCond.wait(mLock); - if (mActiveTrack != 0) { + recordTrack->mState = TrackBase::IDLE; + mActiveTrack = recordTrack; + mLock.unlock(); + status_t status = AudioSystem::startInput(mId); + mLock.lock(); + if (status != NO_ERROR) { + mActiveTrack.clear(); + return status; + } + mActiveTrack->mState = TrackBase::RESUMING; + mRsmpInIndex = mFrameCount; + mBytesRead = 0; + // signal thread to start + LOGV("Signal record thread"); + mWaitWorkCV.signal(); + // do not wait for mStartStopCond if exiting + if (mExiting) { + mActiveTrack.clear(); + status = INVALID_OPERATION; + goto startError; + } + mStartStopCond.wait(mLock); + if (mActiveTrack == 0) { + LOGV("Record failed to start"); + status = BAD_VALUE; + goto startError; + } LOGV("Record started OK"); - return NO_ERROR; - } else { - LOGV("Record failed to start"); - return BAD_VALUE; + return status; } +startError: + AudioSystem::stopInput(mId); + return status; } void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { LOGV("RecordThread::stop"); - AutoMutex lock(&mLock); - if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) { - mActiveTrack->mState = TrackBase::PAUSING; - mStartStopCond.wait(mLock); + sp <ThreadBase> strongMe = this; + { + AutoMutex lock(&mLock); + if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) { + mActiveTrack->mState = TrackBase::PAUSING; + // do not wait for mStartStopCond if exiting + if (mExiting) { + return; + } + mStartStopCond.wait(mLock); + // if we have been restarted, recordTrack == mActiveTrack.get() here + if (mActiveTrack == 0 || recordTrack != mActiveTrack.get()) { + mLock.unlock(); + AudioSystem::stopInput(mId); + mLock.lock(); + LOGV("Record stopped OK"); + } + } } } @@ -3242,10 +3381,12 @@ status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* int channelCount; if (framesReady == 0) { - ssize_t bytesRead = mInput->read(mRsmpInBuffer, mInputBytes); - if (bytesRead < 0) { + mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes); + if (mBytesRead < 0) { LOGE("RecordThread::getNextBuffer() Error reading audio input"); - sleep(1); + if (mActiveTrack->mState == TrackBase::ACTIVE) { + sleep(1); + } buffer->raw = 0; buffer->frameCount = 0; return NOT_ENOUGH_DATA; @@ -3363,7 +3504,7 @@ void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) { break; } Mutex::Autolock _l(mAudioFlinger->mLock); - mAudioFlinger->audioConfigChanged_l(event, this, param2); + mAudioFlinger->audioConfigChanged_l(event, mId, param2); } void AudioFlinger::RecordThread::readInputParameters() @@ -3451,21 +3592,23 @@ int AudioFlinger::openOutput(uint32_t *pDevices, if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || (format != AudioSystem::PCM_16_BIT) || (channels != AudioSystem::CHANNEL_OUT_STEREO)) { - thread = new DirectOutputThread(this, output); - LOGV("openOutput() created direct output: ID %d thread %p", (mNextThreadId + 1), thread); + thread = new DirectOutputThread(this, output, ++mNextThreadId); + LOGV("openOutput() created direct output: ID %d thread %p", mNextThreadId, thread); } else { - thread = new MixerThread(this, output); - LOGV("openOutput() created mixer output: ID %d thread %p", (mNextThreadId + 1), thread); + thread = new MixerThread(this, output, ++mNextThreadId); + LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread); } - mPlaybackThreads.add(++mNextThreadId, thread); + mPlaybackThreads.add(mNextThreadId, thread); if (pSamplingRate) *pSamplingRate = samplingRate; if (pFormat) *pFormat = format; if (pChannels) *pChannels = channels; if (pLatencyMs) *pLatencyMs = thread->latency(); + + return mNextThreadId; } - return mNextThreadId; + return 0; } int AudioFlinger::openDuplicateOutput(int output1, int output2) @@ -3480,9 +3623,9 @@ int AudioFlinger::openDuplicateOutput(int output1, int output2) } - DuplicatingThread *thread = new DuplicatingThread(this, thread1); + DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId); thread->addOutputTrack(thread2); - mPlaybackThreads.add(++mNextThreadId, thread); + mPlaybackThreads.add(mNextThreadId, thread); return mNextThreadId; } @@ -3509,7 +3652,7 @@ status_t AudioFlinger::closeOutput(int output) } } void *param2 = 0; - audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, thread, param2); + audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2); mPlaybackThreads.removeItem(output); } thread->exit(); @@ -3603,17 +3746,19 @@ int AudioFlinger::openInput(uint32_t *pDevices, if (input != 0) { // Start record thread - thread = new RecordThread(this, input, reqSamplingRate, reqChannels); - mRecordThreads.add(++mNextThreadId, thread); + thread = new RecordThread(this, input, reqSamplingRate, reqChannels, ++mNextThreadId); + mRecordThreads.add(mNextThreadId, thread); LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread); if (pSamplingRate) *pSamplingRate = reqSamplingRate; if (pFormat) *pFormat = format; if (pChannels) *pChannels = reqChannels; input->standby(); + + return mNextThreadId; } - return mNextThreadId; + return 0; } status_t AudioFlinger::closeInput(int input) @@ -3630,7 +3775,7 @@ status_t AudioFlinger::closeInput(int input) LOGV("closeInput() %d", input); void *param2 = 0; - audioConfigChanged_l(AudioSystem::INPUT_CLOSED, thread, param2); + audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2); mRecordThreads.removeItem(input); } thread->exit(); diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 22d15c9300..12c90ebd16 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <sys/types.h> +#include <limits.h> #include <media/IAudioFlinger.h> #include <media/IAudioFlingerClient.h> @@ -208,12 +209,13 @@ private: class PlaybackThread; class MixerThread; class DirectOutputThread; + class DuplicatingThread; class Track; class RecordTrack; class ThreadBase : public Thread { public: - ThreadBase (const sp<AudioFlinger>& audioFlinger); + ThreadBase (const sp<AudioFlinger>& audioFlinger, int id); virtual ~ThreadBase(); status_t dumpBase(int fd, const Vector<String16>& args); @@ -323,6 +325,8 @@ private: void sendConfigEvent(int event, int param = 0); void sendConfigEvent_l(int event, int param = 0); void processConfigEvents(); + int id() const { return mId;} + bool standby() { return mStandby; } mutable Mutex mLock; @@ -349,6 +353,8 @@ private: status_t mParamStatus; Vector<ConfigEvent *> mConfigEvents; bool mStandby; + int mId; + bool mExiting; }; // --- PlaybackThread --- @@ -361,6 +367,12 @@ private: DUPLICATING }; + enum mixer_state { + MIXER_IDLE, + MIXER_TRACKS_ENABLED, + MIXER_TRACKS_READY + }; + // playback track class Track : public TrackBase { public: @@ -415,6 +427,10 @@ private: void setPaused() { mState = PAUSED; } void reset(); + bool isOutputTrack() const { + return (mStreamType == AudioSystem::NUM_STREAM_TYPES); + } + // we don't really need a lock for these float mVolume[2]; volatile bool mMute; @@ -439,6 +455,7 @@ private: }; OutputTrack( const wp<ThreadBase>& thread, + DuplicatingThread *sourceThread, uint32_t sampleRate, int format, int channelCount, @@ -458,16 +475,15 @@ private: void clearBufferQueue(); // Maximum number of pending buffers allocated by OutputTrack::write() - static const uint8_t kMaxOverFlowBuffers = 3; + static const uint8_t kMaxOverFlowBuffers = 10; Vector < Buffer* > mBufferQueue; AudioBufferProvider::Buffer mOutBuffer; - uint32_t mWaitTimeMs; bool mActive; - + DuplicatingThread* mSourceThread; }; // end of OutputTrack - PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); virtual ~PlaybackThread(); virtual status_t dump(int fd, const Vector<String16>& args); @@ -507,6 +523,7 @@ private: virtual int type() const { return mType; } void suspend() { mSuspended++; } void restore() { if (mSuspended) mSuspended--; } + bool isSuspended() { return (mSuspended != 0); } virtual String8 getParameters(const String8& keys); virtual void audioConfigChanged(int event, int param = 0); @@ -530,7 +547,8 @@ private: virtual int getTrackName_l() = 0; virtual void deleteTrackName_l(int name) = 0; - virtual uint32_t getMaxBufferRecoveryInUsecs() = 0; + virtual uint32_t activeSleepTimeUs() = 0; + virtual uint32_t idleSleepTimeUs() = 0; private: @@ -562,12 +580,11 @@ private: int mNumWrites; int mNumDelayedWrites; bool mInWrite; - int mMinBytesToWrite; }; class MixerThread : public PlaybackThread { public: - MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); virtual ~MixerThread(); // Thread virtuals @@ -582,10 +599,11 @@ private: virtual status_t dumpInternals(int fd, const Vector<String16>& args); protected: - size_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove); + uint32_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove); virtual int getTrackName_l(); virtual void deleteTrackName_l(int name); - virtual uint32_t getMaxBufferRecoveryInUsecs(); + virtual uint32_t activeSleepTimeUs(); + virtual uint32_t idleSleepTimeUs(); AudioMixer* mAudioMixer; }; @@ -593,7 +611,7 @@ private: class DirectOutputThread : public PlaybackThread { public: - DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); ~DirectOutputThread(); // Thread virtuals @@ -604,7 +622,8 @@ private: protected: virtual int getTrackName_l(); virtual void deleteTrackName_l(int name); - virtual uint32_t getMaxBufferRecoveryInUsecs(); + virtual uint32_t activeSleepTimeUs(); + virtual uint32_t idleSleepTimeUs(); private: float mLeftVolume; @@ -613,23 +632,30 @@ private: class DuplicatingThread : public MixerThread { public: - DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread); + DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread, int id); ~DuplicatingThread(); // Thread virtuals virtual bool threadLoop(); void addOutputTrack(MixerThread* thread); void removeOutputTrack(MixerThread* thread); + uint32_t waitTimeMs() { return mWaitTimeMs; } + protected: + virtual uint32_t activeSleepTimeUs(); private: + bool outputsReady(SortedVector< sp<OutputTrack> > &outputTracks); + void updateWaitTime(); + SortedVector < sp<OutputTrack> > mOutputTracks; + uint32_t mWaitTimeMs; }; PlaybackThread *checkPlaybackThread_l(int output) const; MixerThread *checkMixerThread_l(int output) const; RecordThread *checkRecordThread_l(int input) const; float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; } - void audioConfigChanged_l(int event, const sp<ThreadBase>& thread, void *param2); + void audioConfigChanged_l(int event, int ioHandle, void *param2); friend class AudioBuffer; @@ -697,7 +723,8 @@ private: RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, - uint32_t channels); + uint32_t channels, + int id); ~RecordThread(); virtual bool threadLoop(); @@ -728,6 +755,7 @@ private: size_t mInputBytes; int mReqChannelCount; uint32_t mReqSampleRate; + ssize_t mBytesRead; }; class RecordHandle : public android::BnAudioRecord { diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index d7daf73427..2d4e10ddde 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -41,7 +41,7 @@ #include <sys/mman.h> #include <sys/stat.h> -#define BINDER_VM_SIZE (1*1024*1024) +#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) static bool gSingleProcess = false; diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index eb51c22bcb..b3fed58a4f 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -22,9 +22,6 @@ LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES ifeq ($(TARGET_BOARD_PLATFORM), msm7k) LOCAL_CFLAGS += -DDIM_WITH_TEXTURE endif -ifeq ($(TARGET_BOARD_PLATFORM), qsd8k) - LOCAL_CFLAGS += -DDIM_WITH_TEXTURE -endif # need "-lrt" on Linux simulator to pick up clock_gettime ifeq ($(TARGET_SIMULATOR),true) diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 1abfd68c76..d9d9bfee77 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -246,10 +246,11 @@ void DisplayHardware::init(uint32_t dpy) LOGI("version : %s", glGetString(GL_VERSION)); LOGI("extensions: %s", gl_extensions); - if (strstr(gl_renderer, "PowerVR SGX 530")) { + if (strstr(gl_renderer, "Adreno")) { LOGD("Assuming uncached graphics buffers."); mFlags &= ~CACHED_BUFFERS; } + if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { mFlags |= NPOT_EXTENSION; } diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index f5a5a0bf2d..1870d3a29c 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -93,6 +93,7 @@ sp<LayerBaseClient::Surface> Layer::createSurface() const status_t Layer::ditch() { // the layer is not on screen anymore. free as much resources as possible + mFreezeLock.clear(); destroy(); return NO_ERROR; } @@ -134,7 +135,14 @@ void Layer::reloadTexture(const Region& dirty) { Mutex::Autolock _l(mLock); sp<GraphicBuffer> buffer(getFrontBufferLocked()); - int index = mFrontBufferIndex; + if (buffer == NULL) { + // this situation can happen if we ran out of memory for instance. + // not much we can do. continue to use whatever texture was bound + // to this context. + return; + } + + const int index = mFrontBufferIndex; // create the new texture name if needed if (UNLIKELY(mTextures[index].name == -1U)) { @@ -515,6 +523,11 @@ void Layer::unlockPageFlip( dirtyRegion.andSelf(visibleRegionScreen); outDirtyRegion.orSelf(dirtyRegion); } + if (visibleRegionScreen.isEmpty()) { + // an invisible layer should not hold a freeze-lock + // (because it may never be updated and thereore never release it) + mFreezeLock.clear(); + } } void Layer::finishPageFlip() diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index a36304c8de..2ff61673df 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -26,6 +26,8 @@ #include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> #include <ui/FramebufferNativeWindow.h> +#include <ui/Rect.h> +#include <ui/Region.h> #include <hardware/copybit.h> @@ -46,12 +48,15 @@ gralloc_module_t const* LayerBuffer::sGrallocModule = 0; LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i) : LayerBaseClient(flinger, display, client, i), - mNeedsBlending(false) + mNeedsBlending(false), mBlitEngine(0) { } LayerBuffer::~LayerBuffer() { + if (mBlitEngine) { + copybit_close(mBlitEngine); + } } void LayerBuffer::onFirstRef() @@ -69,6 +74,10 @@ void LayerBuffer::onFirstRef() sGrallocModule = (gralloc_module_t const *)module; } } + + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } } sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const @@ -109,7 +118,10 @@ uint32_t LayerBuffer::doTransaction(uint32_t flags) sp<Source> source(getSource()); if (source != 0) source->onTransaction(flags); - return LayerBase::doTransaction(flags); + uint32_t res = LayerBase::doTransaction(flags); + // we always want filtering for these surfaces + mUseLinearFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG); + return res; } void LayerBuffer::unlockPageFlip(const Transform& planeTransform, @@ -252,7 +264,16 @@ LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset) : mBufferHeap(buffers) { NativeBuffer& src(mNativeBuffer); - src.img.handle = 0; + src.crop.l = 0; + src.crop.t = 0; + src.crop.r = buffers.w; + src.crop.b = buffers.h; + + src.img.w = buffers.hor_stride ?: buffers.w; + src.img.h = buffers.ver_stride ?: buffers.h; + src.img.format = buffers.format; + src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset); + src.img.handle = 0; gralloc_module_t const * module = LayerBuffer::getGrallocModule(); if (module && module->perform) { @@ -262,19 +283,12 @@ LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset) offset, buffers.heap->base(), &src.img.handle); - if (err == NO_ERROR) { - src.crop.l = 0; - src.crop.t = 0; - src.crop.r = buffers.w; - src.crop.b = buffers.h; - - src.img.w = buffers.hor_stride ?: buffers.w; - src.img.h = buffers.ver_stride ?: buffers.h; - src.img.format = buffers.format; - src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset); - } + LOGE_IF(err, "CREATE_HANDLE_FROM_BUFFER (heapId=%d, size=%d, " + "offset=%ld, base=%p) failed (%s)", + buffers.heap->heapID(), buffers.heap->getSize(), + offset, buffers.heap->base(), strerror(-err)); } -} + } LayerBuffer::Buffer::~Buffer() { @@ -438,15 +452,24 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const #if defined(EGL_ANDROID_image_native_buffer) if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) { - // NOTE: Assume the buffer is allocated with the proper USAGE flags - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( - src.crop.r, src.crop.b, src.img.format, - GraphicBuffer::USAGE_HW_TEXTURE, - src.img.w, src.img.handle, false); - - graphicBuffer->setVerticalStride(src.img.h); + copybit_device_t* copybit = mLayer.mBlitEngine; + if (copybit) { + // create our EGLImageKHR the first time + err = initTempBuffer(); + if (err == NO_ERROR) { + // NOTE: Assume the buffer is allocated with the proper USAGE flags + const NativeBuffer& dst(mTempBuffer); + region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b))); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + err = copybit->stretch(copybit, &dst.img, &src.img, + &dst.crop, &src.crop, &clip); - err = mLayer.initializeEglImage(graphicBuffer, &mTexture); + } + } else { + err = INVALID_OPERATION; + } } #endif else { @@ -471,6 +494,72 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const mLayer.drawWithOpenGL(clip, mTexture); } +status_t LayerBuffer::BufferSource::initTempBuffer() const +{ + // figure out the size we need now + const ISurface::BufferHeap& buffers(mBufferHeap); + uint32_t w = mLayer.mTransformedBounds.width(); + uint32_t h = mLayer.mTransformedBounds.height(); + if (buffers.w * h != buffers.h * w) { + int t = w; w = h; h = t; + } + + if (mTexture.image != EGL_NO_IMAGE_KHR) { + // we have an EGLImage, make sure the needed size didn't change + if (w!=mTexture.width || h!= mTexture.height) { + // delete the EGLImage and texture + EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + glDeleteTextures(1, &mTexture.name); + eglDestroyImageKHR(dpy, mTexture.image); + Texture defaultTexture; + mTexture = defaultTexture; + mTempGraphicBuffer.clear(); + } else { + // we're good, we have an EGLImageKHR and it's (still) the + // right size + return NO_ERROR; + } + } + + // figure out if we need linear filtering + if (buffers.w * h == buffers.h * w) { + // same pixel area, don't use filtering + mLayer.mUseLinearFiltering = false; + } + + // Allocate a temporary buffer and create the corresponding EGLImageKHR + + status_t err; + mTempGraphicBuffer.clear(); + mTempGraphicBuffer = new GraphicBuffer( + w, h, HAL_PIXEL_FORMAT_RGB_565, + GraphicBuffer::USAGE_HW_TEXTURE | + GraphicBuffer::USAGE_HW_2D); + + err = mTempGraphicBuffer->initCheck(); + if (err == NO_ERROR) { + NativeBuffer& dst(mTempBuffer); + dst.img.w = mTempGraphicBuffer->getStride(); + dst.img.h = h; + dst.img.format = mTempGraphicBuffer->getPixelFormat(); + dst.img.handle = (native_handle_t *)mTempGraphicBuffer->handle; + dst.img.base = 0; + dst.crop.l = 0; + dst.crop.t = 0; + dst.crop.r = w; + dst.crop.b = h; + + err = mLayer.initializeEglImage( + mTempGraphicBuffer, &mTexture); + // once the EGLImage has been created (whether it fails + // or not) we don't need the graphic buffer reference + // anymore. + mTempGraphicBuffer.clear(); + } + + return err; +} + // --------------------------------------------------------------------------- LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index 47482f49e4..2ca63aca7a 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -130,13 +130,15 @@ private: virtual bool transformed() const; virtual void destroy() { } private: + status_t initTempBuffer() const; mutable Mutex mBufferSourceLock; sp<Buffer> mBuffer; status_t mStatus; ISurface::BufferHeap mBufferHeap; size_t mBufferSize; - mutable sp<GraphicBuffer> mTempBitmap; mutable LayerBase::Texture mTexture; + mutable NativeBuffer mTempBuffer; + mutable sp<GraphicBuffer> mTempGraphicBuffer; }; class OverlaySource : public Source { @@ -205,6 +207,7 @@ private: sp<Surface> mSurface; bool mInvalidate; bool mNeedsBlending; + copybit_device_t* mBlitEngine; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 9694cf1f20..965b7dd28f 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -417,9 +417,9 @@ void SurfaceFlinger::waitForEvent() { while (true) { nsecs_t timeout = -1; + const nsecs_t freezeDisplayTimeout = ms2ns(5000); if (UNLIKELY(isFrozen())) { // wait 5 seconds - const nsecs_t freezeDisplayTimeout = ms2ns(5000); const nsecs_t now = systemTime(); if (mFreezeDisplayTime == 0) { mFreezeDisplayTime = now; @@ -429,22 +429,26 @@ void SurfaceFlinger::waitForEvent() } MessageList::value_type msg = mEventQueue.waitMessage(timeout); - if (msg != 0) { - mFreezeDisplayTime = 0; - switch (msg->what) { - case MessageQueue::INVALIDATE: - // invalidate message, just return to the main loop - return; - } - } else { - // we timed out - if (isFrozen()) { + + // see if we timed out + if (isFrozen()) { + const nsecs_t now = systemTime(); + nsecs_t frozenTime = (now - mFreezeDisplayTime); + if (frozenTime >= freezeDisplayTimeout) { // we timed out and are still frozen LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d", mFreezeDisplay, mFreezeCount); + mFreezeDisplayTime = 0; mFreezeCount = 0; mFreezeDisplay = false; - return; + } + } + + if (msg != 0) { + switch (msg->what) { + case MessageQueue::INVALIDATE: + // invalidate message, just return to the main loop + return; } } } @@ -1646,6 +1650,7 @@ status_t SurfaceFlinger::onTransact( } case 1007: // set mFreezeCount mFreezeCount = data.readInt32(); + mFreezeDisplayTime = 0; return NO_ERROR; case 1010: // interrogate. reply->writeInt32(0); diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index f9bfe6c7c3..c0ab73d891 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -291,7 +291,11 @@ private: friend class FreezeLock; sp<FreezeLock> getFreezeLock() const; - inline void incFreezeCount() { mFreezeCount++; } + inline void incFreezeCount() { + if (mFreezeCount == 0) + mFreezeDisplayTime = 0; + mFreezeCount++; + } inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; } inline bool hasFreezeRequest() const { return mFreezeDisplay; } inline bool isFrozen() const { diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index e39a357598..4aac455153 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -489,6 +489,7 @@ int EventHub::open_device(const char *deviceName) { int version; int fd; + int attempt; struct pollfd *new_mFDs; device_t **new_devices; char **new_device_names; @@ -500,12 +501,17 @@ int EventHub::open_device(const char *deviceName) LOGV("Opening device: %s", deviceName); AutoMutex _l(mLock); - - fd = open(deviceName, O_RDWR); + + for (attempt = 0; attempt < 10; attempt++) { + fd = open(deviceName, O_RDWR); + if (fd >= 0) break; + usleep(100); + } if(fd < 0) { LOGE("could not open %s, %s\n", deviceName, strerror(errno)); return -1; } + LOGV("Opened device: %s (%d failures)", deviceName, attempt); if(ioctl(fd, EVIOCGVERSION, &version)) { LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno)); diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 0efba9c966..c5e22e5645 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -118,8 +118,6 @@ FramebufferNativeWindow::FramebufferNativeWindow() LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s", fbDev->width, fbDev->height, strerror(-err)); - LOGE("xDpi %d", fbDev->xdpi); - LOGE("yDpi %d", fbDev->ydpi); const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags; const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi; const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi; diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp index f51ca7a953..24ae27f286 100644 --- a/libs/ui/Surface.cpp +++ b/libs/ui/Surface.cpp @@ -152,96 +152,85 @@ bool SurfaceControl::isSameSurface( status_t SurfaceControl::setLayer(int32_t layer) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->setLayer(mToken, layer); } status_t SurfaceControl::setPosition(int32_t x, int32_t y) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->setPosition(mToken, x, y); } status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->setSize(mToken, w, h); } status_t SurfaceControl::hide() { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->hide(mToken); } status_t SurfaceControl::show(int32_t layer) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->show(mToken, layer); } status_t SurfaceControl::freeze() { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->freeze(mToken); } status_t SurfaceControl::unfreeze() { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->unfreeze(mToken); } status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->setFlags(mToken, flags, mask); } status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->setTransparentRegionHint(mToken, transparent); } status_t SurfaceControl::setAlpha(float alpha) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->setAlpha(mToken, alpha); } status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy); } status_t SurfaceControl::setFreezeTint(uint32_t tint) { const sp<SurfaceComposerClient>& client(mClient); - if (client == 0) return NO_INIT; - status_t err = validate(client->mControl); + status_t err = validate(); if (err < 0) return err; return client->setFreezeTint(mToken, tint); } -status_t SurfaceControl::validate(SharedClient const* cblk) const +status_t SurfaceControl::validate() const { if (mToken<0 || mClient==0) { LOGE("invalid token (%d, identity=%u) or client (%p)", mToken, mIdentity, mClient.get()); return NO_INIT; } + SharedClient const* cblk = mClient->mControl; if (cblk == 0) { LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); return NO_INIT; @@ -394,7 +383,7 @@ bool Surface::isValid() { return mToken>=0 && mClient!=0; } -status_t Surface::validate(SharedClient const* cblk) const +status_t Surface::validate() const { sp<SurfaceComposerClient> client(getClient()); if (mToken<0 || mClient==0) { @@ -402,6 +391,7 @@ status_t Surface::validate(SharedClient const* cblk) const mToken, mIdentity, client.get()); return NO_INIT; } + SharedClient const* cblk = mClient->mControl; if (cblk == 0) { LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); return NO_INIT; @@ -488,7 +478,7 @@ status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer) { int Surface::dequeueBuffer(android_native_buffer_t** buffer) { sp<SurfaceComposerClient> client(getClient()); - status_t err = validate(client->mControl); + status_t err = validate(); if (err != NO_ERROR) return err; @@ -533,7 +523,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) int Surface::lockBuffer(android_native_buffer_t* buffer) { sp<SurfaceComposerClient> client(getClient()); - status_t err = validate(client->mControl); + status_t err = validate(); if (err != NO_ERROR) return err; @@ -546,7 +536,7 @@ int Surface::lockBuffer(android_native_buffer_t* buffer) int Surface::queueBuffer(android_native_buffer_t* buffer) { sp<SurfaceComposerClient> client(getClient()); - status_t err = validate(client->mControl); + status_t err = validate(); if (err != NO_ERROR) return err; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 872b2bc70f..450af8df09 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -42,6 +42,7 @@ #define TABLE_GETENTRY(x) //x #define TABLE_SUPER_NOISY(x) //x #define LOAD_TABLE_NOISY(x) //x +#define TABLE_THEME(x) //x namespace android { @@ -1447,18 +1448,23 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, const uint32_t t = Res_GETTYPE(resID); const uint32_t e = Res_GETENTRY(resID); - TABLE_NOISY(LOGV("Looking up attr 0x%08x in theme %p", resID, this)); + TABLE_THEME(LOGI("Looking up attr 0x%08x in theme %p", resID, this)); if (p >= 0) { const package_info* const pi = mPackages[p]; + TABLE_THEME(LOGI("Found package: %p", pi)); if (pi != NULL) { + TABLE_THEME(LOGI("Desired type index is %ld in avail %d", t, pi->numTypes)); if (t < pi->numTypes) { const type_info& ti = pi->types[t]; + TABLE_THEME(LOGI("Desired entry index is %ld in avail %d", e, ti.numEntries)); if (e < ti.numEntries) { const theme_entry& te = ti.entries[e]; - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags |= te.typeSpecFlags; - } + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags |= te.typeSpecFlags; + } + TABLE_THEME(LOGI("Theme value: type=0x%x, data=0x%08x", + te.value.dataType, te.value.data)); const uint8_t type = te.value.dataType; if (type == Res_value::TYPE_ATTRIBUTE) { if (cnt > 0) { @@ -1492,6 +1498,8 @@ ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t newTypeSpecFlags; blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); + TABLE_THEME(LOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=%p\n", + (int)blockIndex, (int)inOutValue->dataType, (void*)inOutValue->data)); if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); if (blockIndex < 0) { @@ -1911,8 +1919,8 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, uint32_t newFlags = 0; const ssize_t newIndex = getResource(value->data, value, true, &newFlags, outConfig); - //LOGI("Resolving reference d=%p: newIndex=%d, t=0x%02x, d=%p\n", - // (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data); + TABLE_THEME(LOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n", + (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data)); //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; if (newIndex < 0) { diff --git a/opengl/include/ETC1/etc1.h b/opengl/include/ETC1/etc1.h new file mode 100644 index 0000000000..0d389052b7 --- /dev/null +++ b/opengl/include/ETC1/etc1.h @@ -0,0 +1,106 @@ +// Copyright 2009 Google 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 +// +// 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 __etc1_h__ +#define __etc1_h__ + +#define ETC1_ENCODED_BLOCK_SIZE 8 +#define ETC1_DECODED_BLOCK_SIZE 48 + +#ifndef ETC1_RGB8_OES +#define ETC1_RGB8_OES 0x8D64 +#endif + +typedef unsigned char etc1_byte; +typedef int etc1_bool; +typedef unsigned int etc1_uint32; + +#ifdef __cplusplus +extern "C" { +#endif + +// Encode a block of pixels. +// +// pIn is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a +// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R +// value of pixel (x, y). +// +// validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether +// the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing. +// +// pOut is an ETC1 compressed version of the data. + +void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 validPixelMask, etc1_byte* pOut); + +// Decode a block of pixels. +// +// pIn is an ETC1 compressed version of the data. +// +// pOut is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a +// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R +// value of pixel (x, y). + +void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut); + +// Return the size of the encoded image data (does not include size of PKM header). + +etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height); + +// Encode an entire image. +// pIn - pointer to the image data. Formatted such that +// pixel (x,y) is at pIn + pixelSize * x + stride * y; +// pOut - pointer to encoded data. Must be large enough to store entire encoded image. +// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image. +// returns non-zero if there is an error. + +int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height, + etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut); + +// Decode an entire image. +// pIn - pointer to encoded data. +// pOut - pointer to the image data. Will be written such that +// pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be +// large enough to store entire image. +// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image. +// returns non-zero if there is an error. + +int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut, + etc1_uint32 width, etc1_uint32 height, + etc1_uint32 pixelSize, etc1_uint32 stride); + +// Size of a PKM header, in bytes. + +#define ETC_PKM_HEADER_SIZE 16 + +// Format a PKM header + +void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height); + +// Check if a PKM header is correctly formatted. + +etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader); + +// Read the image width from a PKM header + +etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader); + +// Read the image height from a PKM header + +etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp index 4878722f0b..71825c502a 100644 --- a/opengl/libagl/array.cpp +++ b/opengl/libagl/array.cpp @@ -1548,24 +1548,36 @@ void glDeleteBuffers(GLsizei n, const GLuint* buffers) GLuint name = buffers[i]; if (name) { // unbind bound deleted buffers... - if (c->arrays.element_array_buffer->name == name) { - c->arrays.element_array_buffer = 0; + if (c->arrays.element_array_buffer) { + if (c->arrays.element_array_buffer->name == name) { + c->arrays.element_array_buffer = 0; + } } - if (c->arrays.array_buffer->name == name) { - c->arrays.array_buffer = 0; + if (c->arrays.array_buffer) { + if (c->arrays.array_buffer->name == name) { + c->arrays.array_buffer = 0; + } } - if (c->arrays.vertex.bo->name == name) { - c->arrays.vertex.bo = 0; + if (c->arrays.vertex.bo) { + if (c->arrays.vertex.bo->name == name) { + c->arrays.vertex.bo = 0; + } } - if (c->arrays.normal.bo->name == name) { - c->arrays.normal.bo = 0; + if (c->arrays.normal.bo) { + if (c->arrays.normal.bo->name == name) { + c->arrays.normal.bo = 0; + } } - if (c->arrays.color.bo->name == name) { - c->arrays.color.bo = 0; + if (c->arrays.color.bo) { + if (c->arrays.color.bo->name == name) { + c->arrays.color.bo = 0; + } } for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) { - if (c->arrays.texture[t].bo->name == name) { - c->arrays.texture[t].bo = 0; + if (c->arrays.texture[t].bo) { + if (c->arrays.texture[t].bo->name == name) { + c->arrays.texture[t].bo = 0; + } } } } diff --git a/opengl/libagl/copybit.cpp b/opengl/libagl/copybit.cpp index a68750e14e..4cacc1125f 100644 --- a/opengl/libagl/copybit.cpp +++ b/opengl/libagl/copybit.cpp @@ -38,7 +38,7 @@ #include <ui/Rect.h> -#define DEBUG_COPYBIT true +#define DEBUG_COPYBIT false // ---------------------------------------------------------------------------- diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 8cfa084bbf..81864bd791 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -876,7 +876,7 @@ struct config_management_t { #define VERSION_MAJOR 1 #define VERSION_MINOR 2 static char const * const gVendorString = "Google Inc."; -static char const * const gVersionString = "1.2 Android Driver"; +static char const * const gVersionString = "1.2 Android Driver 1.1.0"; static char const * const gClientApiString = "OpenGL ES"; static char const * const gExtensionsString = "EGL_KHR_image_base " diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp index a59b3b046e..0f1f27d8cf 100644 --- a/opengl/libagl/state.cpp +++ b/opengl/libagl/state.cpp @@ -37,7 +37,7 @@ namespace android { // ---------------------------------------------------------------------------- static char const * const gVendorString = "Android"; -static char const * const gRendererString = "Android PixelFlinger 1.1"; +static char const * const gRendererString = "Android PixelFlinger 1.2"; static char const * const gVersionString = "OpenGL ES-CM 1.0"; static char const * const gExtensionsString = "GL_OES_byte_coordinates " // OK diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp index 13d078e2a3..89a19b66d5 100644 --- a/opengl/libagl/texture.cpp +++ b/opengl/libagl/texture.cpp @@ -1467,7 +1467,7 @@ void glReadPixels( ogles_error(c, GL_INVALID_VALUE); return; } - if (x<0 || x<0) { + if (x<0 || y<0) { ogles_error(c, GL_INVALID_VALUE); return; } diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index 6d20e80a82..37b9a4349d 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -120,3 +120,19 @@ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) endif include $(BUILD_SHARED_LIBRARY) + +############################################################################### +# Build the ETC1 host static library +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + ETC1/etc1.cpp \ +# + +LOCAL_LDLIBS := -lpthread -ldl +LOCAL_MODULE:= libETC1 + +include $(BUILD_HOST_STATIC_LIBRARY) + diff --git a/opengl/libs/ETC1/etc1.cpp b/opengl/libs/ETC1/etc1.cpp new file mode 100644 index 0000000000..5ed2c3c49f --- /dev/null +++ b/opengl/libs/ETC1/etc1.cpp @@ -0,0 +1,670 @@ +// Copyright 2009 Google 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 +// +// 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 <ETC1/etc1.h> + +#include <string.h> + +/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt + + The number of bits that represent a 4x4 texel block is 64 bits if + <internalformat> is given by ETC1_RGB8_OES. + + The data for a block is a number of bytes, + + {q0, q1, q2, q3, q4, q5, q6, q7} + + where byte q0 is located at the lowest memory address and q7 at + the highest. The 64 bits specifying the block is then represented + by the following 64 bit integer: + + int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7; + + ETC1_RGB8_OES: + + a) bit layout in bits 63 through 32 if diffbit = 0 + + 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 + ----------------------------------------------- + | base col1 | base col2 | base col1 | base col2 | + | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| + ----------------------------------------------- + + 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + --------------------------------------------------- + | base col1 | base col2 | table | table |diff|flip| + | B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + --------------------------------------------------- + + + b) bit layout in bits 63 through 32 if diffbit = 1 + + 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 + ----------------------------------------------- + | base col1 | dcol 2 | base col1 | dcol 2 | + | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | + ----------------------------------------------- + + 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + --------------------------------------------------- + | base col 1 | dcol 2 | table | table |diff|flip| + | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + --------------------------------------------------- + + + c) bit layout in bits 31 through 0 (in both cases) + + 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 + ----------------------------------------------- + | most significant pixel index bits | + | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| + ----------------------------------------------- + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + -------------------------------------------------- + | least significant pixel index bits | + | p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + -------------------------------------------------- + + + Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures: + + table codeword modifier table + ------------------ ---------------------- + 0 -8 -2 2 8 + 1 -17 -5 5 17 + 2 -29 -9 9 29 + 3 -42 -13 13 42 + 4 -60 -18 18 60 + 5 -80 -24 24 80 + 6 -106 -33 33 106 + 7 -183 -47 47 183 + + + Add table 3.17.3 Mapping from pixel index values to modifier values for + ETC1 compressed textures: + + pixel index value + --------------- + msb lsb resulting modifier value + ----- ----- ------------------------- + 1 1 -b (large negative value) + 1 0 -a (small negative value) + 0 0 a (small positive value) + 0 1 b (large positive value) + + + */ + +static const int kModifierTable[] = { +/* 0 */2, 8, -2, -8, +/* 1 */5, 17, -5, -17, +/* 2 */9, 29, -9, -29, +/* 3 */13, 42, -13, -42, +/* 4 */18, 60, -18, -60, +/* 5 */24, 80, -24, -80, +/* 6 */33, 106, -33, -106, +/* 7 */47, 183, -47, -183 }; + +static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 }; + +static inline etc1_byte clamp(int x) { + return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0); +} + +static +inline int convert4To8(int b) { + int c = b & 0xf; + return (c << 4) | c; +} + +static +inline int convert5To8(int b) { + int c = b & 0x1f; + return (c << 3) | (c >> 2); +} + +static +inline int convert6To8(int b) { + int c = b & 0x3f; + return (c << 2) | (c >> 4); +} + +static +inline int divideBy255(int d) { + return (d + 128 + (d >> 8)) >> 8; +} + +static +inline int convert8To4(int b) { + int c = b & 0xff; + return divideBy255(b * 15); +} + +static +inline int convert8To5(int b) { + int c = b & 0xff; + return divideBy255(b * 31); +} + +static +inline int convertDiff(int base, int diff) { + return convert5To8((0x1f & base) + kLookup[0x7 & diff]); +} + +static +void decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table, + etc1_uint32 low, bool second, bool flipped) { + int baseX = 0; + int baseY = 0; + if (second) { + if (flipped) { + baseY = 2; + } else { + baseX = 2; + } + } + for (int i = 0; i < 8; i++) { + int x, y; + if (flipped) { + x = baseX + (i >> 1); + y = baseY + (i & 1); + } else { + x = baseX + (i >> 2); + y = baseY + (i & 3); + } + int k = y + (x * 4); + int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2); + int delta = table[offset]; + etc1_byte* q = pOut + 3 * (x + 4 * y); + *q++ = clamp(r + delta); + *q++ = clamp(g + delta); + *q++ = clamp(b + delta); + } +} + +// Input is an ETC1 compressed version of the data. +// Output is a 4 x 4 square of 3-byte pixels in form R, G, B + +void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) { + etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3]; + etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7]; + int r1, r2, g1, g2, b1, b2; + if (high & 2) { + // differential + int rBase = high >> 27; + int gBase = high >> 19; + int bBase = high >> 11; + r1 = convert5To8(rBase); + r2 = convertDiff(rBase, high >> 24); + g1 = convert5To8(gBase); + g2 = convertDiff(gBase, high >> 16); + b1 = convert5To8(bBase); + b2 = convertDiff(bBase, high >> 8); + } else { + // not differential + r1 = convert4To8(high >> 28); + r2 = convert4To8(high >> 24); + g1 = convert4To8(high >> 20); + g2 = convert4To8(high >> 16); + b1 = convert4To8(high >> 12); + b2 = convert4To8(high >> 8); + } + int tableIndexA = 7 & (high >> 5); + int tableIndexB = 7 & (high >> 2); + const int* tableA = kModifierTable + tableIndexA * 4; + const int* tableB = kModifierTable + tableIndexB * 4; + bool flipped = (high & 1) != 0; + decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped); + decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped); +} + +typedef struct { + etc1_uint32 high; + etc1_uint32 low; + etc1_uint32 score; // Lower is more accurate +} etc_compressed; + +static +inline void take_best(etc_compressed* a, const etc_compressed* b) { + if (a->score > b->score) { + *a = *b; + } +} + +static +void etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask, + etc1_byte* pColors, bool flipped, bool second) { + int r = 0; + int g = 0; + int b = 0; + + if (flipped) { + int by = 0; + if (second) { + by = 2; + } + for (int y = 0; y < 2; y++) { + int yy = by + y; + for (int x = 0; x < 4; x++) { + int i = x + 4 * yy; + if (inMask & (1 << i)) { + const etc1_byte* p = pIn + i * 3; + r += *(p++); + g += *(p++); + b += *(p++); + } + } + } + } else { + int bx = 0; + if (second) { + bx = 2; + } + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 2; x++) { + int xx = bx + x; + int i = xx + 4 * y; + if (inMask & (1 << i)) { + const etc1_byte* p = pIn + i * 3; + r += *(p++); + g += *(p++); + b += *(p++); + } + } + } + } + pColors[0] = (etc1_byte)((r + 4) >> 3); + pColors[1] = (etc1_byte)((g + 4) >> 3); + pColors[2] = (etc1_byte)((b + 4) >> 3); +} + +static +inline int square(int x) { + return x * x; +} + +static etc1_uint32 chooseModifier(const etc1_byte* pBaseColors, + const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex, + const int* pModifierTable) { + etc1_uint32 bestScore = ~0; + int bestIndex = 0; + int pixelR = pIn[0]; + int pixelG = pIn[1]; + int pixelB = pIn[2]; + int r = pBaseColors[0]; + int g = pBaseColors[1]; + int b = pBaseColors[2]; + for (int i = 0; i < 4; i++) { + int modifier = pModifierTable[i]; + int decodedG = clamp(g + modifier); + etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG)); + if (score >= bestScore) { + continue; + } + int decodedR = clamp(r + modifier); + score += (etc1_uint32) (3 * square(decodedR - pixelR)); + if (score >= bestScore) { + continue; + } + int decodedB = clamp(b + modifier); + score += (etc1_uint32) square(decodedB - pixelB); + if (score < bestScore) { + bestScore = score; + bestIndex = i; + } + } + etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1)) + << bitIndex; + *pLow |= lowMask; + return bestScore; +} + +static +void etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask, + etc_compressed* pCompressed, bool flipped, bool second, + const etc1_byte* pBaseColors, const int* pModifierTable) { + int score = pCompressed->score; + if (flipped) { + int by = 0; + if (second) { + by = 2; + } + for (int y = 0; y < 2; y++) { + int yy = by + y; + for (int x = 0; x < 4; x++) { + int i = x + 4 * yy; + if (inMask & (1 << i)) { + score += chooseModifier(pBaseColors, pIn + i * 3, + &pCompressed->low, yy + x * 4, pModifierTable); + } + } + } + } else { + int bx = 0; + if (second) { + bx = 2; + } + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 2; x++) { + int xx = bx + x; + int i = xx + 4 * y; + if (inMask & (1 << i)) { + score += chooseModifier(pBaseColors, pIn + i * 3, + &pCompressed->low, y + xx * 4, pModifierTable); + } + } + } + } + pCompressed->score = score; +} + +static bool inRange4bitSigned(int color) { + return color >= -4 && color <= 3; +} + +static void etc_encodeBaseColors(etc1_byte* pBaseColors, + const etc1_byte* pColors, etc_compressed* pCompressed) { + int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks + bool differential; + { + int r51 = convert8To5(pColors[0]); + int g51 = convert8To5(pColors[1]); + int b51 = convert8To5(pColors[2]); + int r52 = convert8To5(pColors[3]); + int g52 = convert8To5(pColors[4]); + int b52 = convert8To5(pColors[5]); + + r1 = convert5To8(r51); + g1 = convert5To8(g51); + b1 = convert5To8(b51); + + int dr = r52 - r51; + int dg = g52 - g51; + int db = b52 - b51; + + differential = inRange4bitSigned(dr) && inRange4bitSigned(dg) + && inRange4bitSigned(db); + if (differential) { + r2 = convert5To8(r51 + dr); + g2 = convert5To8(g51 + dg); + b2 = convert5To8(b51 + db); + pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19) + | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2; + } + } + + if (!differential) { + int r41 = convert8To4(pColors[0]); + int g41 = convert8To4(pColors[1]); + int b41 = convert8To4(pColors[2]); + int r42 = convert8To4(pColors[3]); + int g42 = convert8To4(pColors[4]); + int b42 = convert8To4(pColors[5]); + r1 = convert4To8(r41); + g1 = convert4To8(g41); + b1 = convert4To8(b41); + r2 = convert4To8(r42); + g2 = convert4To8(g42); + b2 = convert4To8(b42); + pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42 + << 16) | (b41 << 12) | (b42 << 8); + } + pBaseColors[0] = r1; + pBaseColors[1] = g1; + pBaseColors[2] = b1; + pBaseColors[3] = r2; + pBaseColors[4] = g2; + pBaseColors[5] = b2; +} + +static +void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask, + const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) { + pCompressed->score = ~0; + pCompressed->high = (flipped ? 1 : 0); + pCompressed->low = 0; + + etc1_byte pBaseColors[6]; + + etc_encodeBaseColors(pBaseColors, pColors, pCompressed); + + int originalHigh = pCompressed->high; + + const int* pModifierTable = kModifierTable; + for (int i = 0; i < 8; i++, pModifierTable += 4) { + etc_compressed temp; + temp.score = 0; + temp.high = originalHigh | (i << 5); + temp.low = 0; + etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false, + pBaseColors, pModifierTable); + take_best(pCompressed, &temp); + } + pModifierTable = kModifierTable; + etc_compressed firstHalf = *pCompressed; + for (int i = 0; i < 8; i++, pModifierTable += 4) { + etc_compressed temp; + temp.score = firstHalf.score; + temp.high = firstHalf.high | (i << 2); + temp.low = firstHalf.low; + etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true, + pBaseColors + 3, pModifierTable); + if (i == 0) { + *pCompressed = temp; + } else { + take_best(pCompressed, &temp); + } + } +} + +static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) { + pOut[0] = (etc1_byte)(d >> 24); + pOut[1] = (etc1_byte)(d >> 16); + pOut[2] = (etc1_byte)(d >> 8); + pOut[3] = (etc1_byte) d; +} + +// Input is a 4 x 4 square of 3-byte pixels in form R, G, B +// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y) +// pixel is valid or not. Invalid pixel color values are ignored when compressing. +// Output is an ETC1 compressed version of the data. + +void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask, + etc1_byte* pOut) { + etc1_byte colors[6]; + etc1_byte flippedColors[6]; + etc_average_colors_subblock(pIn, inMask, colors, false, false); + etc_average_colors_subblock(pIn, inMask, colors + 3, false, true); + etc_average_colors_subblock(pIn, inMask, flippedColors, true, false); + etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true); + + etc_compressed a, b; + etc_encode_block_helper(pIn, inMask, colors, &a, false); + etc_encode_block_helper(pIn, inMask, flippedColors, &b, true); + take_best(&a, &b); + writeBigEndian(pOut, a.high); + writeBigEndian(pOut + 4, a.low); +} + +// Return the size of the encoded image data (does not include size of PKM header). + +etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) { + return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1; +} + +// Encode an entire image. +// pIn - pointer to the image data. Formatted such that the Red component of +// pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset; +// pOut - pointer to encoded data. Must be large enough to store entire encoded image. + +int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height, + etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) { + if (pixelSize < 2 || pixelSize > 3) { + return -1; + } + static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff }; + static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777, + 0xffff }; + etc1_byte block[ETC1_DECODED_BLOCK_SIZE]; + etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE]; + + etc1_uint32 encodedWidth = (width + 3) & ~3; + etc1_uint32 encodedHeight = (height + 3) & ~3; + + for (etc1_uint32 y = 0; y < encodedHeight; y += 4) { + etc1_uint32 yEnd = height - y; + if (yEnd > 4) { + yEnd = 4; + } + int ymask = kYMask[yEnd]; + for (etc1_uint32 x = 0; x < encodedWidth; x += 4) { + etc1_uint32 xEnd = width - x; + if (xEnd > 4) { + xEnd = 4; + } + int mask = ymask & kXMask[xEnd]; + for (etc1_uint32 cy = 0; cy < yEnd; cy++) { + etc1_byte* q = block + (cy * 4) * 3; + const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy); + if (pixelSize == 3) { + memcpy(q, p, xEnd * 3); + } else { + for (etc1_uint32 cx = 0; cx < xEnd; cx++) { + int pixel = (p[1] << 8) | p[0]; + *q++ = convert5To8(pixel >> 11); + *q++ = convert6To8(pixel >> 5); + *q++ = convert5To8(pixel); + p += pixelSize; + } + } + } + etc1_encode_block(block, mask, encoded); + memcpy(pOut, encoded, sizeof(encoded)); + pOut += sizeof(encoded); + } + } + return 0; +} + +// Decode an entire image. +// pIn - pointer to encoded data. +// pOut - pointer to the image data. Will be written such that the Red component of +// pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be +// large enough to store entire image. + + +int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut, + etc1_uint32 width, etc1_uint32 height, + etc1_uint32 pixelSize, etc1_uint32 stride) { + if (pixelSize < 2 || pixelSize > 3) { + return -1; + } + etc1_byte block[ETC1_DECODED_BLOCK_SIZE]; + + etc1_uint32 encodedWidth = (width + 3) & ~3; + etc1_uint32 encodedHeight = (height + 3) & ~3; + + for (etc1_uint32 y = 0; y < encodedHeight; y += 4) { + etc1_uint32 yEnd = height - y; + if (yEnd > 4) { + yEnd = 4; + } + for (etc1_uint32 x = 0; x < encodedWidth; x += 4) { + etc1_uint32 xEnd = width - x; + if (xEnd > 4) { + xEnd = 4; + } + etc1_decode_block(pIn, block); + pIn += ETC1_ENCODED_BLOCK_SIZE; + for (etc1_uint32 cy = 0; cy < yEnd; cy++) { + const etc1_byte* q = block + (cy * 4) * 3; + etc1_byte* p = pOut + pixelSize * x + stride * (y + cy); + if (pixelSize == 3) { + memcpy(p, q, xEnd * 3); + } else { + for (etc1_uint32 cx = 0; cx < xEnd; cx++) { + etc1_byte r = *q++; + etc1_byte g = *q++; + etc1_byte b = *q++; + etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); + *p++ = (etc1_byte) pixel; + *p++ = (etc1_byte) (pixel >> 8); + } + } + } + } + } + return 0; +} + +static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' }; + +static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6; +static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8; +static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10; +static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12; +static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14; + +static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0; + +static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) { + pOut[0] = (etc1_byte) (data >> 8); + pOut[1] = (etc1_byte) data; +} + +static etc1_uint32 readBEUint16(const etc1_byte* pIn) { + return (pIn[0] << 8) | pIn[1]; +} + +// Format a PKM header + +void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) { + memcpy(pHeader, kMagic, sizeof(kMagic)); + etc1_uint32 encodedWidth = (width + 3) & ~3; + etc1_uint32 encodedHeight = (height + 3) & ~3; + writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS); + writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth); + writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight); + writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width); + writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height); +} + +// Check if a PKM header is correctly formatted. + +etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) { + if (memcmp(pHeader, kMagic, sizeof(kMagic))) { + return false; + } + etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET); + etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET); + etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET); + etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET); + etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET); + return format == ETC1_RGB_NO_MIPMAPS && + encodedWidth >= width && encodedWidth - width < 4 && + encodedHeight >= height && encodedHeight - height < 4; +} + +// Read the image width from a PKM header + +etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) { + return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET); +} + +// Read the image height from a PKM header + +etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){ + return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET); +} diff --git a/opengl/tests/gl_jni/jni/gl_code.cpp b/opengl/tests/gl_jni/jni/gl_code.cpp index 33b25ab6c7..f031c79cd3 100644 --- a/opengl/tests/gl_jni/jni/gl_code.cpp +++ b/opengl/tests/gl_jni/jni/gl_code.cpp @@ -180,4 +180,5 @@ JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_step(JNIEnv * env, jobjec JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_changeBackground(JNIEnv * env, jobject obj) { background = 1.0f - background; -}
\ No newline at end of file +} + |