diff options
author | Eric Laurent <elaurent@google.com> | 2012-03-30 16:00:16 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-03-30 16:00:16 -0700 |
commit | 40a1da57d21bbc883630e7a854a279685fe1fa61 (patch) | |
tree | 6155cbbdaa553b0672f27fb298a6fc0f9ac3eb2c | |
parent | 42c56b364a0fcd93568f85b63d52a871a756184c (diff) | |
parent | a011e35b22f95f558d81dc9c94b68b1465c4661d (diff) | |
download | frameworks_av-40a1da57d21bbc883630e7a854a279685fe1fa61.zip frameworks_av-40a1da57d21bbc883630e7a854a279685fe1fa61.tar.gz frameworks_av-40a1da57d21bbc883630e7a854a279685fe1fa61.tar.bz2 |
Merge "implemented synchronous audio capture"
-rw-r--r-- | include/media/AudioRecord.h | 6 | ||||
-rw-r--r-- | include/media/AudioSystem.h | 15 | ||||
-rw-r--r-- | include/media/IAudioRecord.h | 3 | ||||
-rw-r--r-- | include/media/ToneGenerator.h | 3 | ||||
-rw-r--r-- | media/libmedia/AudioRecord.cpp | 12 | ||||
-rw-r--r-- | media/libmedia/IAudioRecord.cpp | 6 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 240 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.h | 92 |
8 files changed, 347 insertions, 30 deletions
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 5bfb65b..80d2d72 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -30,6 +30,7 @@ #include <utils/threads.h> #include <system/audio.h> +#include <media/AudioSystem.h> namespace android { @@ -215,8 +216,11 @@ public: /* After it's created the track is not active. Call start() to * make it active. If set, the callback will start being called. + * if event is not AudioSystem::SYNC_EVENT_NONE, the capture start will be delayed until + * the specified event occurs on the specified trigger session. */ - status_t start(); + status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, + int triggerSession = 0); /* Stop a track. If set, the callback will cease being called and * obtainBuffer returns STOPPED. Note that obtainBuffer() still works diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index cc0a594..e64bc3f 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -145,6 +145,21 @@ public: uint32_t latency; }; + // Events used to synchronize actions between audio sessions. + // For instance SYNC_EVENT_PRESENTATION_COMPLETE can be used to delay recording start until playback + // is complete on another audio session. + // See definitions in MediaSyncEvent.java + enum sync_event_t { + SYNC_EVENT_SAME = -1, // used internally to indicate restart with same event + SYNC_EVENT_NONE = 0, + SYNC_EVENT_PRESENTATION_COMPLETE, + + // + // Define new events here: SYNC_EVENT_START, SYNC_EVENT_STOP, SYNC_EVENT_TIME ... + // + SYNC_EVENT_CNT, + }; + // // IAudioPolicyService interface (see AudioPolicyInterface for method descriptions) // diff --git a/include/media/IAudioRecord.h b/include/media/IAudioRecord.h index 089be3b..c486c6b 100644 --- a/include/media/IAudioRecord.h +++ b/include/media/IAudioRecord.h @@ -25,7 +25,6 @@ #include <binder/IInterface.h> #include <binder/IMemory.h> - namespace android { // ---------------------------------------------------------------------------- @@ -39,7 +38,7 @@ public: * make it active. If set, the callback will start being called. * tid identifies the client callback thread, or 0 if not needed. */ - virtual status_t start(pid_t tid) = 0; + virtual status_t start(pid_t tid, int event, int triggerSession) = 0; /* Stop a track. If set, the callback will cease being called and * obtainBuffer will return an error. Buffers that are already released diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index df0c97e..29c8fd9 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -159,6 +159,9 @@ public: bool isInited() { return (mState == TONE_IDLE)?false:true;} + // returns the audio session this ToneGenerator belongs to or 0 if an error occured. + int getSessionId() { return (mpAudioTrack == NULL) ? 0 : mpAudioTrack->getSessionId(); } + private: enum tone_state { diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 70ec593..444665a 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -279,12 +279,12 @@ audio_source_t AudioRecord::inputSource() const // ------------------------------------------------------------------------- -status_t AudioRecord::start() +status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) { status_t ret = NO_ERROR; sp<ClientRecordThread> t = mClientRecordThread; - ALOGV("start"); + ALOGV("start, sync event %d trigger session %d", event, triggerSession); if (t != 0) { if (t->exitPending()) { @@ -322,7 +322,7 @@ status_t AudioRecord::start() if (!(cblk->flags & CBLK_INVALID_MSK)) { cblk->lock.unlock(); ALOGV("mAudioRecord->start(tid=%d)", tid); - ret = mAudioRecord->start(tid); + ret = mAudioRecord->start(tid, event, triggerSession); cblk->lock.lock(); if (ret == DEAD_OBJECT) { android_atomic_or(CBLK_INVALID_ON, &cblk->flags); @@ -541,7 +541,8 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) ALOGW( "obtainBuffer timed out (is the CPU pegged?) " "user=%08x, server=%08x", cblk->user, cblk->server); cblk->lock.unlock(); - result = mAudioRecord->start(0); // callback thread hasn't changed + // callback thread or sync event hasn't changed + result = mAudioRecord->start(0, AudioSystem::SYNC_EVENT_SAME, 0); cblk->lock.lock(); if (result == DEAD_OBJECT) { android_atomic_or(CBLK_INVALID_ON, &cblk->flags); @@ -779,7 +780,8 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) result = openRecord_l(cblk->sampleRate, mFormat, mChannelMask, mFrameCount, getInput_l()); if (result == NO_ERROR) { - result = mAudioRecord->start(0); // callback thread hasn't changed + // callback thread or sync event hasn't changed + result = mAudioRecord->start(0, AudioSystem::SYNC_EVENT_SAME, 0); } if (result != NO_ERROR) { mActive = false; diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp index 377b9a8..cb5c7f3 100644 --- a/media/libmedia/IAudioRecord.cpp +++ b/media/libmedia/IAudioRecord.cpp @@ -42,11 +42,13 @@ public: { } - virtual status_t start(pid_t tid) + virtual status_t start(pid_t tid, int event, int triggerSession) { Parcel data, reply; data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); data.writeInt32(tid); + data.writeInt32(event); + data.writeInt32(triggerSession); status_t status = remote()->transact(START, data, &reply); if (status == NO_ERROR) { status = reply.readInt32(); @@ -91,7 +93,7 @@ status_t BnAudioRecord::onTransact( } break; case START: { CHECK_INTERFACE(IAudioRecord, data, reply); - reply->writeInt32(start(data.readInt32())); + reply->writeInt32(start(data.readInt32(), data.readInt32(), data.readInt32())); return NO_ERROR; } break; case STOP: { diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index c5ad0f5..8662cb5 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -513,6 +513,17 @@ sp<IAudioTrack> AudioFlinger::createTrack( Mutex::Autolock _sl(effectThread->mLock); moveEffectChain_l(lSessionId, effectThread, thread, true); } + + // Look for sync events awaiting for a session to be used. + for (int i = 0; i < (int)mPendingSyncEvents.size(); i++) { + if (mPendingSyncEvents[i]->triggerSession() == lSessionId) { + if (thread->isValidSyncEvent(mPendingSyncEvents[i])) { + track->setSyncEvent(mPendingSyncEvents[i]); + mPendingSyncEvents.removeAt(i); + i--; + } + } + } } if (lStatus == NO_ERROR) { trackHandle = new TrackHandle(track); @@ -1933,6 +1944,36 @@ uint32_t AudioFlinger::PlaybackThread::activeSleepTimeUs() } } +status_t AudioFlinger::PlaybackThread::setSyncEvent(const sp<SyncEvent>& event) +{ + if (!isValidSyncEvent(event)) { + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + for (size_t i = 0; i < mTracks.size(); ++i) { + sp<Track> track = mTracks[i]; + if (event->triggerSession() == track->sessionId()) { + track->setSyncEvent(event); + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +bool AudioFlinger::PlaybackThread::isValidSyncEvent(const sp<SyncEvent>& event) +{ + switch (event->type()) { + case AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE: + return true; + default: + break; + } + return false; +} + // ---------------------------------------------------------------------------- AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, @@ -2530,7 +2571,15 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac if (track->isTerminated() || track->isStopped() || track->isPaused()) { // We have consumed all the buffers of this track. // Remove it from the list of active tracks. - tracksToRemove->add(track); + // TODO: use actual buffer filling status instead of latency when available from + // audio HAL + size_t audioHALFrames = + (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; + size_t framesWritten = + mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); + if (track->presentationComplete(framesWritten, audioHALFrames)) { + tracksToRemove->add(track); + } } else { // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. @@ -2909,7 +2958,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep if (track->isTerminated() || track->isStopped() || track->isPaused()) { // We have consumed all the buffers of this track. // Remove it from the list of active tracks. - trackToRemove = track; + // TODO: implement behavior for compressed audio + size_t audioHALFrames = + (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; + size_t framesWritten = + mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); + if (track->presentationComplete(framesWritten, audioHALFrames)) { + trackToRemove = track; + } } else { // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. @@ -3466,6 +3522,12 @@ void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t f return bufferStart; } +status_t AudioFlinger::ThreadBase::TrackBase::setSyncEvent(const sp<SyncEvent>& event) +{ + mSyncEvents.add(event); + return NO_ERROR; +} + // ---------------------------------------------------------------------------- // Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held @@ -3488,7 +3550,8 @@ AudioFlinger::PlaybackThread::Track::Track( mName(-1), // see note below mMainBuffer(thread->mixBuffer()), mAuxBuffer(NULL), - mAuxEffectId(0), mHasVolumeController(false) + mAuxEffectId(0), mHasVolumeController(false), + mPresentationCompleteFrames(0) { if (mCblk != NULL) { // NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of @@ -3627,7 +3690,9 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const { return false; } -status_t AudioFlinger::PlaybackThread::Track::start(pid_t tid) +status_t AudioFlinger::PlaybackThread::Track::start(pid_t tid, + AudioSystem::sync_event_t event, + int triggerSession) { status_t status = NO_ERROR; ALOGV("start(%d), calling pid %d session %d tid %d", @@ -3756,6 +3821,7 @@ void AudioFlinger::PlaybackThread::Track::reset() android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags); mFillingUpStatus = FS_FILLING; mResetDone = true; + mPresentationCompleteFrames = 0; } } @@ -3781,6 +3847,39 @@ void AudioFlinger::PlaybackThread::Track::setAuxBuffer(int EffectId, int32_t *bu mAuxBuffer = buffer; } +bool AudioFlinger::PlaybackThread::Track::presentationComplete(size_t framesWritten, + size_t audioHalFrames) +{ + // a track is considered presented when the total number of frames written to audio HAL + // corresponds to the number of frames written when presentationComplete() is called for the + // first time (mPresentationCompleteFrames == 0) plus the buffer filling status at that time. + if (mPresentationCompleteFrames == 0) { + mPresentationCompleteFrames = framesWritten + audioHalFrames; + ALOGV("presentationComplete() reset: mPresentationCompleteFrames %d audioHalFrames %d", + mPresentationCompleteFrames, audioHalFrames); + } + if (framesWritten >= mPresentationCompleteFrames) { + ALOGV("presentationComplete() session %d complete: framesWritten %d", + mSessionId, framesWritten); + triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE); + mPresentationCompleteFrames = 0; + return true; + } + return false; +} + +void AudioFlinger::PlaybackThread::Track::triggerEvents(AudioSystem::sync_event_t type) +{ + for (int i = 0; i < (int)mSyncEvents.size(); i++) { + if (mSyncEvents[i]->type() == type) { + mSyncEvents[i]->trigger(); + mSyncEvents.removeAt(i); + i--; + } + } +} + + // timed audio tracks sp<AudioFlinger::PlaybackThread::TimedTrack> @@ -4241,12 +4340,14 @@ getNextBuffer_exit: return NOT_ENOUGH_DATA; } -status_t AudioFlinger::RecordThread::RecordTrack::start(pid_t tid) +status_t AudioFlinger::RecordThread::RecordTrack::start(pid_t tid, + AudioSystem::sync_event_t event, + int triggerSession) { sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); - return recordThread->start(this, tid); + return recordThread->start(this, tid, event, triggerSession); } else { return BAD_VALUE; } @@ -4312,9 +4413,11 @@ AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack() clearBufferQueue(); } -status_t AudioFlinger::PlaybackThread::OutputTrack::start(pid_t tid) +status_t AudioFlinger::PlaybackThread::OutputTrack::start(pid_t tid, + AudioSystem::sync_event_t event, + int triggerSession) { - status_t status = Track::start(tid); + status_t status = Track::start(tid, event, triggerSession); if (status != NO_ERROR) { return status; } @@ -4757,9 +4860,9 @@ sp<IMemory> AudioFlinger::RecordHandle::getCblk() const { return mRecordTrack->getCblk(); } -status_t AudioFlinger::RecordHandle::start(pid_t tid) { +status_t AudioFlinger::RecordHandle::start(pid_t tid, int event, int triggerSession) { ALOGV("RecordHandle::start()"); - return mRecordTrack->start(tid); + return mRecordTrack->start(tid, (AudioSystem::sync_event_t)event, triggerSession); } void AudioFlinger::RecordHandle::stop() { @@ -4968,7 +5071,16 @@ bool AudioFlinger::RecordThread::threadLoop() } } - mActiveTrack->releaseBuffer(&buffer); + if (mFramestoDrop == 0) { + mActiveTrack->releaseBuffer(&buffer); + } else { + if (mFramestoDrop > 0) { + mFramestoDrop -= buffer.frameCount; + if (mFramestoDrop < 0) { + mFramestoDrop = 0; + } + } + } mActiveTrack->overflow(); } // client isn't retrieving buffers fast enough @@ -5050,11 +5162,26 @@ Exit: return track; } -status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack, pid_t tid) +status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack, + pid_t tid, AudioSystem::sync_event_t event, + int triggerSession) { - ALOGV("RecordThread::start tid=%d", tid); + ALOGV("RecordThread::start tid=%d, event %d, triggerSession %d", tid, event, triggerSession); sp<ThreadBase> strongMe = this; status_t status = NO_ERROR; + + if (event == AudioSystem::SYNC_EVENT_NONE) { + mSyncStartEvent.clear(); + mFramestoDrop = 0; + } else if (event != AudioSystem::SYNC_EVENT_SAME) { + mSyncStartEvent = mAudioFlinger->createSyncEvent(event, + triggerSession, + recordTrack->sessionId(), + syncStartEventCallback, + this); + mFramestoDrop = -1; + } + { AutoMutex lock(mLock); if (mActiveTrack != 0) { @@ -5073,6 +5200,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac mLock.lock(); if (status != NO_ERROR) { mActiveTrack.clear(); + clearSyncStartEvent(); return status; } mRsmpInIndex = mFrameCount; @@ -5101,9 +5229,44 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac } startError: AudioSystem::stopInput(mId); + clearSyncStartEvent(); return status; } +void AudioFlinger::RecordThread::clearSyncStartEvent() +{ + if (mSyncStartEvent != 0) { + mSyncStartEvent->cancel(); + } + mSyncStartEvent.clear(); +} + +void AudioFlinger::RecordThread::syncStartEventCallback(const wp<SyncEvent>& event) +{ + sp<SyncEvent> strongEvent = event.promote(); + + if (strongEvent != 0) { + RecordThread *me = (RecordThread *)strongEvent->cookie(); + me->handleSyncStartEvent(strongEvent); + } +} + +void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event) +{ + ALOGV("handleSyncStartEvent() mActiveTrack %p session %d event->listenerSession() %d", + mActiveTrack.get(), + mActiveTrack.get() ? mActiveTrack->sessionId() : 0, + event->listenerSession()); + + if (mActiveTrack != 0 && + event == mSyncStartEvent) { + // TODO: use actual buffer filling status instead of 2 buffers when info is available + // from audio HAL + mFramestoDrop = mFrameCount * 2; + mSyncStartEvent.clear(); + } +} + void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { ALOGV("RecordThread::stop"); sp<ThreadBase> strongMe = this; @@ -5127,6 +5290,26 @@ void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { } } +bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event) +{ + return false; +} + +status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event) +{ + if (!isValidSyncEvent(event)) { + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + if (mTrack != NULL && event->triggerSession() == mTrack->sessionId()) { + mTrack->setSyncEvent(event); + return NO_ERROR; + } + return NAME_NOT_FOUND; +} + status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; @@ -5899,6 +6082,37 @@ uint32_t AudioFlinger::primaryOutputDevice_l() const return thread->device(); } +sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type, + int triggerSession, + int listenerSession, + sync_event_callback_t callBack, + void *cookie) +{ + Mutex::Autolock _l(mLock); + + sp<SyncEvent> event = new SyncEvent(type, triggerSession, listenerSession, callBack, cookie); + status_t playStatus = NAME_NOT_FOUND; + status_t recStatus = NAME_NOT_FOUND; + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + playStatus = mPlaybackThreads.valueAt(i)->setSyncEvent(event); + if (playStatus == NO_ERROR) { + return event; + } + } + for (size_t i = 0; i < mRecordThreads.size(); i++) { + recStatus = mRecordThreads.valueAt(i)->setSyncEvent(event); + if (recStatus == NO_ERROR) { + return event; + } + } + if (playStatus == NAME_NOT_FOUND || recStatus == NAME_NOT_FOUND) { + mPendingSyncEvents.add(event); + } else { + ALOGV("createSyncEvent() invalid event %d", event->type()); + event.clear(); + } + return event; +} // ---------------------------------------------------------------------------- // Effect management diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 795807d..2376aff 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -204,6 +204,44 @@ public: // end of IAudioFlinger interface + class SyncEvent; + + typedef void (*sync_event_callback_t)(const wp<SyncEvent>& event) ; + + class SyncEvent : public RefBase { + public: + SyncEvent(AudioSystem::sync_event_t type, + int triggerSession, + int listenerSession, + sync_event_callback_t callBack, + void *cookie) + : mType(type), mTriggerSession(triggerSession), mListenerSession(listenerSession), + mCallback(callBack), mCookie(cookie) + {} + + virtual ~SyncEvent() {} + + void trigger() { Mutex::Autolock _l(mLock); if (mCallback) mCallback(this); } + void cancel() {Mutex::Autolock _l(mLock); mCallback = NULL; } + AudioSystem::sync_event_t type() const { return mType; } + int triggerSession() const { return mTriggerSession; } + int listenerSession() const { return mListenerSession; } + void *cookie() const { return mCookie; } + + private: + const AudioSystem::sync_event_t mType; + const int mTriggerSession; + const int mListenerSession; + sync_event_callback_t mCallback; + void * const mCookie; + Mutex mLock; + }; + + sp<SyncEvent> createSyncEvent(AudioSystem::sync_event_t type, + int triggerSession, + int listenerSession, + sync_event_callback_t callBack, + void *cookie); private: audio_mode_t getMode() const { return mMode; } @@ -334,11 +372,14 @@ private: int sessionId); virtual ~TrackBase(); - virtual status_t start(pid_t tid) = 0; + virtual status_t start(pid_t tid, + AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, + int triggerSession = 0) = 0; virtual void stop() = 0; sp<IMemory> getCblk() const { return mCblkMemory; } audio_track_cblk_t* cblk() const { return mCblk; } int sessionId() const { return mSessionId; } + virtual status_t setSyncEvent(const sp<SyncEvent>& event); protected: TrackBase(const TrackBase&); @@ -385,6 +426,7 @@ private: const int mSessionId; uint8_t mChannelCount; uint32_t mChannelMask; + Vector < sp<SyncEvent> >mSyncEvents; }; class ConfigEvent { @@ -499,6 +541,11 @@ private: void checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect, bool enabled, int sessionId = AUDIO_SESSION_OUTPUT_MIX); + + virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0; + virtual bool isValidSyncEvent(const sp<SyncEvent>& event) = 0; + + mutable Mutex mLock; protected: @@ -619,7 +666,9 @@ private: virtual ~Track(); void dump(char* buffer, size_t size); - virtual status_t start(pid_t tid); + virtual status_t start(pid_t tid, + AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, + int triggerSession = 0); virtual void stop(); void pause(); @@ -670,6 +719,9 @@ private: return (mStreamType == AUDIO_STREAM_CNT); } + bool presentationComplete(size_t framesWritten, size_t audioHalFrames); + void triggerEvents(AudioSystem::sync_event_t type); + public: virtual bool isTimedTrack() const { return false; } protected: @@ -688,6 +740,8 @@ private: int32_t *mAuxBuffer; int mAuxEffectId; bool mHasVolumeController; + size_t mPresentationCompleteFrames; // number of frames written to the audio HAL + // when this track will be fully rendered }; // end of Track class TimedTrack : public Track { @@ -782,7 +836,9 @@ private: int frameCount); virtual ~OutputTrack(); - virtual status_t start(pid_t tid); + virtual status_t start(pid_t tid, + AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, + int triggerSession = 0); virtual void stop(); bool write(int16_t* data, uint32_t frames); bool bufferQueueEmpty() const { return mBufferQueue.size() == 0; } @@ -885,6 +941,9 @@ public: void setStreamValid(audio_stream_type_t streamType, bool valid); + virtual status_t setSyncEvent(const sp<SyncEvent>& event); + virtual bool isValidSyncEvent(const sp<SyncEvent>& event); + protected: int16_t* mMixBuffer; uint32_t mSuspended; // suspend count, > 0 means suspended @@ -1145,7 +1204,9 @@ private: int sessionId); virtual ~RecordTrack(); - virtual status_t start(pid_t tid); + virtual status_t start(pid_t tid, + AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, + int triggerSession = 0); virtual void stop(); bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } @@ -1192,8 +1253,9 @@ private: int sessionId, status_t *status); - status_t start(RecordTrack* recordTrack); - status_t start(RecordTrack* recordTrack, pid_t tid); + status_t start(RecordTrack* recordTrack, pid_t tid, + AudioSystem::sync_event_t event, + int triggerSession); void stop(RecordTrack* recordTrack); status_t dump(int fd, const Vector<String16>& args); AudioStreamIn* getInput() const; @@ -1215,7 +1277,15 @@ private: virtual uint32_t hasAudioSession(int sessionId); RecordTrack* track(); + virtual status_t setSyncEvent(const sp<SyncEvent>& event); + virtual bool isValidSyncEvent(const sp<SyncEvent>& event); + + static void syncStartEventCallback(const wp<SyncEvent>& event); + void handleSyncStartEvent(const sp<SyncEvent>& event); + private: + void clearSyncStartEvent(); + RecordThread(); AudioStreamIn *mInput; RecordTrack* mTrack; @@ -1229,6 +1299,11 @@ private: const int mReqChannelCount; const uint32_t mReqSampleRate; ssize_t mBytesRead; + // sync event triggering actual audio capture. Frames read before this event will + // be dropped and therefore not read by the application. + sp<SyncEvent> mSyncStartEvent; + // number of captured frames to drop after the start sync event has been received. + ssize_t mFramestoDrop; }; // server side of the client's IAudioRecord @@ -1237,7 +1312,7 @@ private: RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack); virtual ~RecordHandle(); virtual sp<IMemory> getCblk() const; - virtual status_t start(pid_t tid); + virtual status_t start(pid_t tid, int event, int triggerSession); virtual void stop(); virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); @@ -1676,6 +1751,9 @@ mutable Mutex mLock; // mutex for process, commands and handl float masterVolumeSW_l() const { return mMasterVolumeSW; } bool masterMute_l() const { return mMasterMute; } + Vector < sp<SyncEvent> > mPendingSyncEvents; // sync events awaiting for a session + // to be created + private: sp<Client> registerPid_l(pid_t pid); // always returns non-0 |