Merge changes I52ec5b02,I376656e3,I3f005583,Icba4e33d

* changes:
  Use PassthruPatchRecord for DIRECT to DIRECT connections
  Add PassthruPatchRecord for low latency software patches
  Abstract access to HAL stream via Source in RecordThread
  Move PlaybackThread::Track::writeFrames to PatchRecord
gugelfrei
TreeHugger Robot 5 years ago committed by Android (Google) Code Review
commit 374bffd4f5

@ -547,6 +547,16 @@ private:
bool mute;
};
// Abstraction for the Audio Source for the RecordThread (HAL or PassthruPatchRecord).
struct Source
{
virtual ~Source() = default;
// The following methods have the same signatures as in StreamHalInterface.
virtual status_t read(void *buffer, size_t bytes, size_t *read) = 0;
virtual status_t getCapturePosition(int64_t *frames, int64_t *time) = 0;
virtual status_t standby() = 0;
};
// --- PlaybackThread ---
#ifdef FLOAT_EFFECT_CHAIN
#define EFFECT_BUFFER_FORMAT AUDIO_FORMAT_PCM_FLOAT
@ -749,7 +759,7 @@ using effect_buffer_t = int16_t;
// For emphasis, we could also make all pointers to them be "const *",
// but that would clutter the code unnecessarily.
struct AudioStreamIn {
struct AudioStreamIn : public Source {
AudioHwDevice* const audioHwDev;
sp<StreamInHalInterface> stream;
audio_input_flags_t flags;
@ -758,6 +768,13 @@ using effect_buffer_t = int16_t;
AudioStreamIn(AudioHwDevice *dev, sp<StreamInHalInterface> in, audio_input_flags_t flags) :
audioHwDev(dev), stream(in), flags(flags) {}
status_t read(void *buffer, size_t bytes, size_t *read) override {
return stream->read(buffer, bytes, read);
}
status_t getCapturePosition(int64_t *frames, int64_t *time) override {
return stream->getCapturePosition(frames, time);
}
status_t standby() override { return stream->standby(); }
};
struct TeePatch {

@ -483,19 +483,6 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel)
// Fast mode is not available in this case.
inputFlags = (audio_input_flags_t) (inputFlags & ~AUDIO_INPUT_FLAG_FAST);
}
sp<RecordThread::PatchRecord> tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord(
mRecord.thread().get(),
sampleRate,
inChannelMask,
format,
frameCount,
NULL,
(size_t)0 /* bufferSize */,
inputFlags);
status = mRecord.checkTrack(tempRecordTrack.get());
if (status != NO_ERROR) {
return status;
}
audio_output_flags_t outputFlags = mAudioPatch.sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ?
mAudioPatch.sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE;
@ -512,9 +499,34 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel)
outputFlags = (audio_output_flags_t) (outputFlags & ~AUDIO_OUTPUT_FLAG_FAST);
}
sp<RecordThread::PatchRecord> tempRecordTrack;
if ((inputFlags & AUDIO_INPUT_FLAG_DIRECT) && (outputFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
tempRecordTrack = new RecordThread::PassthruPatchRecord(
mRecord.thread().get(),
sampleRate,
inChannelMask,
format,
frameCount,
inputFlags);
} else {
tempRecordTrack = new RecordThread::PatchRecord(
mRecord.thread().get(),
sampleRate,
inChannelMask,
format,
frameCount,
nullptr,
(size_t)0 /* bufferSize */,
inputFlags);
}
status = mRecord.checkTrack(tempRecordTrack.get());
if (status != NO_ERROR) {
return status;
}
// create a special playback track to render to playback thread.
// this track is given the same buffer as the PatchRecord buffer
sp<PlaybackThread::PatchTrack> tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack(
sp<PlaybackThread::PatchTrack> tempPatchTrack = new PlaybackThread::PatchTrack(
mPlayback.thread().get(),
streamType,
sampleRate,

@ -266,8 +266,6 @@ protected:
private:
void interceptBuffer(const AudioBufferProvider::Buffer& buffer);
/** Write the source data in the buffer provider. @return written frame count. */
size_t writeFrames(AudioBufferProvider* dest, const void* src, size_t frameCount);
template <class F>
void forEachTeePatchTrack(F f) {
for (auto& tp : mTeePatches) { f(tp.patchTrack); }
@ -387,6 +385,8 @@ public:
const Timeout& timeout = {});
virtual ~PatchTrack();
size_t framesReady() const override;
virtual status_t start(AudioSystem::sync_event_t event =
AudioSystem::SYNC_EVENT_NONE,
audio_session_t triggerSession = AUDIO_SESSION_NONE);

@ -167,6 +167,8 @@ public:
const Timeout& timeout = {});
virtual ~PatchRecord();
virtual Source* getSource() { return nullptr; }
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
@ -175,4 +177,71 @@ public:
virtual status_t obtainBuffer(Proxy::Buffer *buffer,
const struct timespec *timeOut = NULL);
virtual void releaseBuffer(Proxy::Buffer *buffer);
size_t writeFrames(const void* src, size_t frameCount, size_t frameSize) {
return writeFrames(this, src, frameCount, frameSize);
}
protected:
/** Write the source data into the buffer provider. @return written frame count. */
static size_t writeFrames(AudioBufferProvider* dest, const void* src,
size_t frameCount, size_t frameSize);
}; // end of PatchRecord
class PassthruPatchRecord : public PatchRecord, public Source {
public:
PassthruPatchRecord(RecordThread *recordThread,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
audio_format_t format,
size_t frameCount,
audio_input_flags_t flags);
Source* getSource() override { return static_cast<Source*>(this); }
// Source interface
status_t read(void *buffer, size_t bytes, size_t *read) override;
status_t getCapturePosition(int64_t *frames, int64_t *time) override;
status_t standby() override;
// AudioBufferProvider interface
// This interface is used by RecordThread to pass the data obtained
// from HAL or other source to the client. PassthruPatchRecord receives
// the data in 'obtainBuffer' so these calls are stubbed out.
status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override;
void releaseBuffer(AudioBufferProvider::Buffer* buffer) override;
// PatchProxyBufferProvider interface
// This interface is used from DirectOutputThread to acquire data from HAL.
bool producesBufferOnDemand() const override { return true; }
status_t obtainBuffer(Proxy::Buffer *buffer, const struct timespec *timeOut = nullptr) override;
void releaseBuffer(Proxy::Buffer *buffer) override;
private:
// This is to use with PatchRecord::writeFrames
struct PatchRecordAudioBufferProvider : public AudioBufferProvider {
explicit PatchRecordAudioBufferProvider(PassthruPatchRecord& passthru) :
mPassthru(passthru) {}
status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override {
return mPassthru.PatchRecord::getNextBuffer(buffer);
}
void releaseBuffer(AudioBufferProvider::Buffer* buffer) override {
return mPassthru.PatchRecord::releaseBuffer(buffer);
}
private:
PassthruPatchRecord& mPassthru;
};
sp<StreamInHalInterface> obtainStream(sp<ThreadBase>* thread);
PatchRecordAudioBufferProvider mPatchRecordAudioBufferProvider;
std::unique_ptr<void, decltype(free)*> mSinkBuffer; // frame size aligned continuous buffer
std::unique_ptr<void, decltype(free)*> mStubBuffer; // buffer used for AudioBufferProvider
size_t mUnconsumedFrames = 0;
std::mutex mReadLock;
std::condition_variable mReadCV;
size_t mReadBytes = 0; // GUARDED_BY(mReadLock)
status_t mReadError = NO_ERROR; // GUARDED_BY(mReadLock)
int64_t mLastReadFrames = 0; // accessed on RecordThread only
};

@ -6679,6 +6679,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady),
mInput(input),
mSource(mInput),
mActiveTracks(&this->mLocalLog),
mRsmpInBuffer(NULL),
// mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l()
@ -7131,7 +7132,7 @@ reacquire_wakelock:
} else {
ATRACE_BEGIN("read");
size_t bytesRead;
status_t result = mInput->stream->read(
status_t result = mSource->read(
(uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize, &bytesRead);
ATRACE_END();
if (result < 0) {
@ -7153,7 +7154,7 @@ reacquire_wakelock:
int64_t position, time;
if (mStandby) {
mTimestampVerifier.discontinuity();
} else if (mInput->stream->getCapturePosition(&position, &time) == NO_ERROR
} else if (mSource->getCapturePosition(&position, &time) == NO_ERROR
&& time > mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]) {
mTimestampVerifier.add(position, time, mSampleRate);
@ -7434,7 +7435,7 @@ void AudioFlinger::RecordThread::inputStandBy()
sq->end(false /*didModify*/);
}
}
status_t result = mInput->stream->standby();
status_t result = mSource->standby();
ALOGE_IF(result != OK, "Error when putting input stream into standby: %d", result);
// If going into standby, flush the pipe source.
@ -8420,11 +8421,17 @@ void AudioFlinger::RecordThread::addPatchTrack(const sp<PatchRecord>& record)
{
Mutex::Autolock _l(mLock);
mTracks.add(record);
if (record->getSource()) {
mSource = record->getSource();
}
}
void AudioFlinger::RecordThread::deletePatchTrack(const sp<PatchRecord>& record)
{
Mutex::Autolock _l(mLock);
if (mSource == record->getSource()) {
mSource = mInput;
}
destroyTrack_l(record);
}

@ -1647,6 +1647,7 @@ private:
void checkBtNrec_l();
AudioStreamIn *mInput;
Source *mSource;
SortedVector < sp<RecordTrack> > mTracks;
// mActiveTracks has dual roles: it indicates the current active track(s), and
// is used together with mStartStopCond to indicate start()/stop() progress

@ -337,6 +337,7 @@ public:
virtual ~PatchProxyBufferProvider() {}
virtual bool producesBufferOnDemand() const = 0;
virtual status_t obtainBuffer(Proxy::Buffer* buffer,
const struct timespec *requested = NULL) = 0;
virtual void releaseBuffer(Proxy::Buffer* buffer) = 0;
@ -359,6 +360,8 @@ public:
mPeerProxy = nullptr;
}
bool producesBufferOnDemand() const override { return false; }
protected:
const sp<ClientProxy> mProxy;
sp<RefBase> mPeerReferenceHold; // keeps mPeerProxy alive during access.

@ -823,16 +823,9 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer(
}
for (auto& teePatch : mTeePatches) {
RecordThread::PatchRecord* patchRecord = teePatch.patchRecord.get();
size_t framesWritten = writeFrames(patchRecord, sourceBuffer.i8, frameCount);
// On buffer wrap, the buffer frame count will be less than requested,
// when this happens a second buffer needs to be used to write the leftover audio
size_t framesLeft = frameCount - framesWritten;
if (framesWritten != 0 && framesLeft != 0) {
framesWritten +=
writeFrames(patchRecord, sourceBuffer.i8 + framesWritten * mFrameSize, framesLeft);
framesLeft = frameCount - framesWritten;
}
const size_t framesWritten = patchRecord->writeFrames(
sourceBuffer.i8, frameCount, mFrameSize);
const size_t framesLeft = frameCount - framesWritten;
ALOGW_IF(framesLeft != 0, "%s(%d) PatchRecord %d can not provide big enough "
"buffer %zu/%zu, dropping %zu frames", __func__, mId, patchRecord->mId,
framesWritten, frameCount, framesLeft);
@ -844,26 +837,6 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer(
spent.count(), mTeePatches.size());
}
size_t AudioFlinger::PlaybackThread::Track::writeFrames(AudioBufferProvider* dest,
const void* src,
size_t frameCount) {
AudioBufferProvider::Buffer patchBuffer;
patchBuffer.frameCount = frameCount;
auto status = dest->getNextBuffer(&patchBuffer);
if (status != NO_ERROR) {
ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
__func__, status, strerror(-status));
return 0;
}
ALOG_ASSERT(patchBuffer.frameCount <= frameCount);
memcpy(patchBuffer.raw, src, patchBuffer.frameCount * mFrameSize);
auto framesWritten = patchBuffer.frameCount;
dest->releaseBuffer(&patchBuffer);
return framesWritten;
}
// releaseBuffer() is not overridden
// ExtendedAudioBufferProvider interface
// framesReady() may return an approximation of the number of frames if called
@ -1813,6 +1786,15 @@ AudioFlinger::PlaybackThread::PatchTrack::~PatchTrack()
ALOGV("%s(%d)", __func__, mId);
}
size_t AudioFlinger::PlaybackThread::PatchTrack::framesReady() const
{
if (mPeerProxy && mPeerProxy->producesBufferOnDemand()) {
return std::numeric_limits<size_t>::max();
} else {
return Track::framesReady();
}
}
status_t AudioFlinger::PlaybackThread::PatchTrack::start(AudioSystem::sync_event_t event,
audio_session_t triggerSession)
{
@ -2405,6 +2387,39 @@ AudioFlinger::RecordThread::PatchRecord::~PatchRecord()
ALOGV("%s(%d)", __func__, mId);
}
static size_t writeFramesHelper(
AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize)
{
AudioBufferProvider::Buffer patchBuffer;
patchBuffer.frameCount = frameCount;
auto status = dest->getNextBuffer(&patchBuffer);
if (status != NO_ERROR) {
ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
__func__, status, strerror(-status));
return 0;
}
ALOG_ASSERT(patchBuffer.frameCount <= frameCount);
memcpy(patchBuffer.raw, src, patchBuffer.frameCount * frameSize);
size_t framesWritten = patchBuffer.frameCount;
dest->releaseBuffer(&patchBuffer);
return framesWritten;
}
// static
size_t AudioFlinger::RecordThread::PatchRecord::writeFrames(
AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize)
{
size_t framesWritten = writeFramesHelper(dest, src, frameCount, frameSize);
// On buffer wrap, the buffer frame count will be less than requested,
// when this happens a second buffer needs to be used to write the leftover audio
const size_t framesLeft = frameCount - framesWritten;
if (framesWritten != 0 && framesLeft != 0) {
framesWritten += writeFramesHelper(dest, (const char*)src + framesWritten * frameSize,
framesLeft, frameSize);
}
return framesWritten;
}
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::PatchRecord::getNextBuffer(
AudioBufferProvider::Buffer* buffer)
@ -2449,6 +2464,180 @@ void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffe
mProxy->releaseBuffer(buffer);
}
#undef LOG_TAG
#define LOG_TAG "AF::PthrPatchRecord"
static std::unique_ptr<void, decltype(free)*> allocAligned(size_t alignment, size_t size)
{
void *ptr = nullptr;
(void)posix_memalign(&ptr, alignment, size);
return std::unique_ptr<void, decltype(free)*>(ptr, free);
}
AudioFlinger::RecordThread::PassthruPatchRecord::PassthruPatchRecord(
RecordThread *recordThread,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
audio_format_t format,
size_t frameCount,
audio_input_flags_t flags)
: PatchRecord(recordThread, sampleRate, channelMask, format, frameCount,
nullptr /*buffer*/, 0 /*bufferSize*/, flags),
mPatchRecordAudioBufferProvider(*this),
mSinkBuffer(allocAligned(32, mFrameCount * mFrameSize)),
mStubBuffer(allocAligned(32, mFrameCount * mFrameSize))
{
memset(mStubBuffer.get(), 0, mFrameCount * mFrameSize);
}
sp<StreamInHalInterface> AudioFlinger::RecordThread::PassthruPatchRecord::obtainStream(
sp<ThreadBase>* thread)
{
*thread = mThread.promote();
if (!*thread) return nullptr;
RecordThread *recordThread = static_cast<RecordThread*>((*thread).get());
Mutex::Autolock _l(recordThread->mLock);
return recordThread->mInput ? recordThread->mInput->stream : nullptr;
}
// PatchProxyBufferProvider methods are called on DirectOutputThread
status_t AudioFlinger::RecordThread::PassthruPatchRecord::obtainBuffer(
Proxy::Buffer* buffer, const struct timespec* timeOut)
{
if (mUnconsumedFrames) {
buffer->mFrameCount = std::min(buffer->mFrameCount, mUnconsumedFrames);
// mUnconsumedFrames is decreased in releaseBuffer to use actual frame consumption figure.
return PatchRecord::obtainBuffer(buffer, timeOut);
}
// Otherwise, execute a read from HAL and write into the buffer.
nsecs_t startTimeNs = 0;
if (timeOut && (timeOut->tv_sec != 0 || timeOut->tv_nsec != 0) && timeOut->tv_sec != INT_MAX) {
// Will need to correct timeOut by elapsed time.
startTimeNs = systemTime();
}
const size_t framesToRead = std::min(buffer->mFrameCount, mFrameCount);
buffer->mFrameCount = 0;
buffer->mRaw = nullptr;
sp<ThreadBase> thread;
sp<StreamInHalInterface> stream = obtainStream(&thread);
if (!stream) return NO_INIT; // If there is no stream, RecordThread is not reading.
status_t result = NO_ERROR;
struct timespec newTimeOut = *timeOut;
size_t bytesRead = 0;
{
ATRACE_NAME("read");
result = stream->read(mSinkBuffer.get(), framesToRead * mFrameSize, &bytesRead);
if (result != NO_ERROR) goto stream_error;
if (bytesRead == 0) return NO_ERROR;
}
{
std::lock_guard<std::mutex> lock(mReadLock);
mReadBytes += bytesRead;
mReadError = NO_ERROR;
}
mReadCV.notify_one();
// writeFrames handles wraparound and should write all the provided frames.
// If it couldn't, there is something wrong with the client/server buffer of the software patch.
buffer->mFrameCount = writeFrames(
&mPatchRecordAudioBufferProvider,
mSinkBuffer.get(), bytesRead / mFrameSize, mFrameSize);
ALOGW_IF(buffer->mFrameCount < bytesRead / mFrameSize,
"Lost %zu frames obtained from HAL", bytesRead / mFrameSize - buffer->mFrameCount);
mUnconsumedFrames = buffer->mFrameCount;
// Correct newTimeOut by elapsed time.
if (startTimeNs) {
nsecs_t newTimeOutNs =
audio_utils_ns_from_timespec(&newTimeOut) - (systemTime() - startTimeNs);
if (newTimeOutNs < 0) newTimeOutNs = 0;
newTimeOut.tv_sec = newTimeOutNs / NANOS_PER_SECOND;
newTimeOut.tv_nsec = newTimeOutNs - newTimeOut.tv_sec * NANOS_PER_SECOND;
}
return PatchRecord::obtainBuffer(buffer, &newTimeOut);
stream_error:
stream->standby();
{
std::lock_guard<std::mutex> lock(mReadLock);
mReadError = result;
}
mReadCV.notify_one();
return result;
}
void AudioFlinger::RecordThread::PassthruPatchRecord::releaseBuffer(Proxy::Buffer* buffer)
{
if (buffer->mFrameCount <= mUnconsumedFrames) {
mUnconsumedFrames -= buffer->mFrameCount;
} else {
ALOGW("Write side has consumed more frames than we had: %zu > %zu",
buffer->mFrameCount, mUnconsumedFrames);
mUnconsumedFrames = 0;
}
PatchRecord::releaseBuffer(buffer);
}
// AudioBufferProvider and Source methods are called on RecordThread
// 'read' emulates actual audio data with 0's. This is OK as 'getNextBuffer'
// and 'releaseBuffer' are stubbed out and ignore their input.
// It's not possible to retrieve actual data here w/o blocking 'obtainBuffer'
// until we copy it.
status_t AudioFlinger::RecordThread::PassthruPatchRecord::read(
void* buffer, size_t bytes, size_t* read)
{
bytes = std::min(bytes, mFrameCount * mFrameSize);
{
std::unique_lock<std::mutex> lock(mReadLock);
mReadCV.wait(lock, [&]{ return mReadError != NO_ERROR || mReadBytes != 0; });
if (mReadError != NO_ERROR) {
mLastReadFrames = 0;
return mReadError;
}
*read = std::min(bytes, mReadBytes);
mReadBytes -= *read;
}
mLastReadFrames = *read / mFrameSize;
memset(buffer, 0, *read);
return 0;
}
status_t AudioFlinger::RecordThread::PassthruPatchRecord::getCapturePosition(
int64_t* frames, int64_t* time)
{
sp<ThreadBase> thread;
sp<StreamInHalInterface> stream = obtainStream(&thread);
return stream ? stream->getCapturePosition(frames, time) : NO_INIT;
}
status_t AudioFlinger::RecordThread::PassthruPatchRecord::standby()
{
// RecordThread issues 'standby' command in two major cases:
// 1. Error on read--this case is handled in 'obtainBuffer'.
// 2. Track is stopping--as PassthruPatchRecord assumes continuous
// output, this can only happen when the software patch
// is being torn down. In this case, the RecordThread
// will terminate and close the HAL stream.
return 0;
}
// As the buffer gets filled in obtainBuffer, here we only simulate data consumption.
status_t AudioFlinger::RecordThread::PassthruPatchRecord::getNextBuffer(
AudioBufferProvider::Buffer* buffer)
{
buffer->frameCount = mLastReadFrames;
buffer->raw = buffer->frameCount != 0 ? mStubBuffer.get() : nullptr;
return NO_ERROR;
}
void AudioFlinger::RecordThread::PassthruPatchRecord::releaseBuffer(
AudioBufferProvider::Buffer* buffer)
{
buffer->frameCount = 0;
buffer->raw = nullptr;
}
// ----------------------------------------------------------------------------
#undef LOG_TAG
#define LOG_TAG "AF::MmapTrack"

Loading…
Cancel
Save