summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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