summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2012-03-29 15:51:43 -0700
committerEric Laurent <elaurent@google.com>2012-03-29 19:50:49 -0700
commita011e35b22f95f558d81dc9c94b68b1465c4661d (patch)
tree3f8c46c8c7e30f36c3b629174a26bc4ca2a860b2
parentb4c0c4b800332fe55c60aa328ece8d849cee3a4d (diff)
downloadframeworks_av-a011e35b22f95f558d81dc9c94b68b1465c4661d.zip
frameworks_av-a011e35b22f95f558d81dc9c94b68b1465c4661d.tar.gz
frameworks_av-a011e35b22f95f558d81dc9c94b68b1465c4661d.tar.bz2
implemented synchronous audio capture
Added the infrastructure to support the synchronization of playback and capture actions on specific events. The first requirement for this feature is to synchronize the audio capture start with the full rendering of a given audio content. The applications can further be extended to other use cases (synchronized playback start...) by adding new synchronization events and new synchronous control methods on player or recorders. Also added a method to query the audio session from a ToneGenerator. Change-Id: I51f1167290d9cafdf2fbcdf9e4785156973af44c
-rw-r--r--include/media/AudioRecord.h6
-rw-r--r--include/media/AudioSystem.h15
-rw-r--r--include/media/IAudioRecord.h3
-rw-r--r--include/media/ToneGenerator.h3
-rw-r--r--media/libmedia/AudioRecord.cpp12
-rw-r--r--media/libmedia/IAudioRecord.cpp6
-rw-r--r--services/audioflinger/AudioFlinger.cpp240
-rw-r--r--services/audioflinger/AudioFlinger.h92
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