/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "AudioBufferProviderSource"
//#define LOG_NDEBUG 0
#include <cutils/compiler.h>
#include <utils/Log.h>
#include <media/nbaio/AudioBufferProviderSource.h>
namespace android {
AudioBufferProviderSource::AudioBufferProviderSource(AudioBufferProvider *provider,
NBAIO_Format format) :
NBAIO_Source(format), mProvider(provider), mConsumed(0)
{
ALOG_ASSERT(provider != NULL);
ALOG_ASSERT(format != Format_Invalid);
}
AudioBufferProviderSource::~AudioBufferProviderSource()
{
if (mBuffer.raw != NULL) {
mProvider->releaseBuffer(&mBuffer);
}
}
ssize_t AudioBufferProviderSource::availableToRead()
{
if (CC_UNLIKELY(!mNegotiated)) {
return NEGOTIATE;
}
return mBuffer.raw != NULL ? mBuffer.frameCount - mConsumed : 0;
}
ssize_t AudioBufferProviderSource::read(void *buffer,
size_t count,
int64_t readPTS)
{
if (CC_UNLIKELY(!mNegotiated)) {
return NEGOTIATE;
}
if (CC_UNLIKELY(mBuffer.raw == NULL)) {
mBuffer.frameCount = count;
status_t status = mProvider->getNextBuffer(&mBuffer, readPTS);
if (status != OK) {
return status == NOT_ENOUGH_DATA ? (ssize_t) WOULD_BLOCK : (ssize_t) status;
}
ALOG_ASSERT(mBuffer.raw != NULL);
// mConsumed is 0 either from constructor or after releaseBuffer()
}
size_t available = mBuffer.frameCount - mConsumed;
if (CC_UNLIKELY(count > available)) {
count = available;
}
// count could be zero, either because count was zero on entry or
// available is zero, but both are unlikely so don't check for that
memcpy(buffer, (char *) mBuffer.raw + (mConsumed << mBitShift), count << mBitShift);
if (CC_UNLIKELY((mConsumed += count) >= mBuffer.frameCount)) {
mProvider->releaseBuffer(&mBuffer);
mBuffer.raw = NULL;
mConsumed = 0;
}
mFramesRead += count;
// For better responsiveness with large values of count,
// return a short count rather than continuing with next buffer.
// This gives the caller a chance to interpolate other actions.
return count;
}
ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *user,
int64_t readPTS, size_t block)
{
if (CC_UNLIKELY(!mNegotiated)) {
return NEGOTIATE;
}
if (CC_UNLIKELY(block == 0)) {
block = ~0;
}
for (size_t accumulator = 0; ; ) {
ALOG_ASSERT(accumulator <= total);
size_t count = total - accumulator;
if (CC_UNLIKELY(count == 0)) {
return accumulator;
}
if (CC_LIKELY(count > block)) {
count = block;
}
// 1 <= count <= block
if (CC_UNLIKELY(mBuffer.raw == NULL)) {
mBuffer.frameCount = count;
status_t status = mProvider->getNextBuffer(&mBuffer, readPTS);
if (CC_LIKELY(status == OK)) {
ALOG_ASSERT(mBuffer.raw != NULL && mBuffer.frameCount <= count);
// mConsumed is 0 either from constructor or after releaseBuffer()
continue;
}
// FIXME simplify logic - does the initial count and block checks again for no reason;
// don't you just want to fall through to the size_t available line?
if (CC_LIKELY(status == NOT_ENOUGH_DATA)) {
status = WOULD_BLOCK;
}
return accumulator > 0 ? accumulator : (ssize_t) status;
}
size_t available = mBuffer.frameCount - mConsumed;
if (CC_UNLIKELY(count > available)) {
count = available;
}
if (CC_LIKELY(count > 0)) {
char* readTgt = (char *) mBuffer.raw + (mConsumed << mBitShift);
ssize_t ret = via(user, readTgt, count, readPTS);
if (CC_UNLIKELY(ret <= 0)) {
if (CC_LIKELY(accumulator > 0)) {
return accumulator;
}
return ret;
}
ALOG_ASSERT((size_t) ret <= count);
mFramesRead += ret;
accumulator += ret;
if (CC_LIKELY((mConsumed += ret) < mBuffer.frameCount)) {
continue;
}
}
mProvider->releaseBuffer(&mBuffer);
mBuffer.raw = NULL;
mConsumed = 0;
// don't get next buffer until we really need it
}
}
} // namespace android