/* * 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