1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "AudioBufferProviderSource"
18//#define LOG_NDEBUG 0
19
20#include <cutils/compiler.h>
21#include <utils/Log.h>
22#include <media/nbaio/AudioBufferProviderSource.h>
23
24namespace android {
25
26AudioBufferProviderSource::AudioBufferProviderSource(AudioBufferProvider *provider,
27                                                     const NBAIO_Format& format) :
28    NBAIO_Source(format), mProvider(provider), mConsumed(0)
29{
30    ALOG_ASSERT(provider != NULL);
31    ALOG_ASSERT(Format_isValid(format));
32}
33
34AudioBufferProviderSource::~AudioBufferProviderSource()
35{
36    if (mBuffer.raw != NULL) {
37        mProvider->releaseBuffer(&mBuffer);
38    }
39}
40
41ssize_t AudioBufferProviderSource::availableToRead()
42{
43    if (CC_UNLIKELY(!mNegotiated)) {
44        return NEGOTIATE;
45    }
46    return mBuffer.raw != NULL ? mBuffer.frameCount - mConsumed : 0;
47}
48
49ssize_t AudioBufferProviderSource::read(void *buffer,
50                                        size_t count,
51                                        int64_t readPTS)
52{
53    if (CC_UNLIKELY(!mNegotiated)) {
54        return NEGOTIATE;
55    }
56    if (CC_UNLIKELY(mBuffer.raw == NULL)) {
57        mBuffer.frameCount = count;
58        status_t status = mProvider->getNextBuffer(&mBuffer, readPTS);
59        if (status != OK) {
60            return status == NOT_ENOUGH_DATA ? (ssize_t) WOULD_BLOCK : (ssize_t) status;
61        }
62        ALOG_ASSERT(mBuffer.raw != NULL);
63        // mConsumed is 0 either from constructor or after releaseBuffer()
64    }
65    size_t available = mBuffer.frameCount - mConsumed;
66    if (CC_UNLIKELY(count > available)) {
67        count = available;
68    }
69    // count could be zero, either because count was zero on entry or
70    // available is zero, but both are unlikely so don't check for that
71    memcpy(buffer, (char *) mBuffer.raw + (mConsumed * mFrameSize), count * mFrameSize);
72    if (CC_UNLIKELY((mConsumed += count) >= mBuffer.frameCount)) {
73        mProvider->releaseBuffer(&mBuffer);
74        mBuffer.raw = NULL;
75        mConsumed = 0;
76    }
77    mFramesRead += count;
78    // For better responsiveness with large values of count,
79    // return a short count rather than continuing with next buffer.
80    // This gives the caller a chance to interpolate other actions.
81    return count;
82}
83
84ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *user,
85                                           int64_t readPTS, size_t block)
86{
87    if (CC_UNLIKELY(!mNegotiated)) {
88        return NEGOTIATE;
89    }
90    if (CC_UNLIKELY(block == 0)) {
91        block = ~0;
92    }
93    for (size_t accumulator = 0; ; ) {
94        ALOG_ASSERT(accumulator <= total);
95        size_t count = total - accumulator;
96        if (CC_UNLIKELY(count == 0)) {
97            return accumulator;
98        }
99        if (CC_LIKELY(count > block)) {
100            count = block;
101        }
102        // 1 <= count <= block
103        if (CC_UNLIKELY(mBuffer.raw == NULL)) {
104            mBuffer.frameCount = count;
105            status_t status = mProvider->getNextBuffer(&mBuffer, readPTS);
106            if (CC_LIKELY(status == OK)) {
107                ALOG_ASSERT(mBuffer.raw != NULL && mBuffer.frameCount <= count);
108                // mConsumed is 0 either from constructor or after releaseBuffer()
109                continue;
110            }
111            // FIXME simplify logic - does the initial count and block checks again for no reason;
112            //       don't you just want to fall through to the size_t available line?
113            if (CC_LIKELY(status == NOT_ENOUGH_DATA)) {
114                status = WOULD_BLOCK;
115            }
116            return accumulator > 0 ? accumulator : (ssize_t) status;
117        }
118        size_t available = mBuffer.frameCount - mConsumed;
119        if (CC_UNLIKELY(count > available)) {
120            count = available;
121        }
122        if (CC_LIKELY(count > 0)) {
123            char* readTgt = (char *) mBuffer.raw + (mConsumed * mFrameSize);
124            ssize_t ret = via(user, readTgt, count, readPTS);
125            if (CC_UNLIKELY(ret <= 0)) {
126                if (CC_LIKELY(accumulator > 0)) {
127                    return accumulator;
128                }
129                return ret;
130            }
131            ALOG_ASSERT((size_t) ret <= count);
132            mFramesRead += ret;
133            accumulator += ret;
134            if (CC_LIKELY((mConsumed += ret) < mBuffer.frameCount)) {
135                continue;
136            }
137        }
138        mProvider->releaseBuffer(&mBuffer);
139        mBuffer.raw = NULL;
140        mConsumed = 0;
141        // don't get next buffer until we really need it
142    }
143}
144
145}   // namespace android
146