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