• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "MonoPipe"
18 //#define LOG_NDEBUG 0
19 
20 #include <cutils/atomic.h>
21 #include <cutils/compiler.h>
22 #include <utils/Log.h>
23 #include <utils/Trace.h>
24 #include "MonoPipe.h"
25 #include "roundup.h"
26 
27 namespace android {
28 
MonoPipe(size_t reqFrames,NBAIO_Format format,bool writeCanBlock)29 MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) :
30         NBAIO_Sink(format),
31         mReqFrames(reqFrames),
32         mMaxFrames(roundup(reqFrames)),
33         mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
34         mFront(0),
35         mRear(0),
36         mWriteTsValid(false),
37         // mWriteTs
38         mSetpoint((reqFrames * 11) / 16),
39         mWriteCanBlock(writeCanBlock)
40 {
41 }
42 
~MonoPipe()43 MonoPipe::~MonoPipe()
44 {
45     free(mBuffer);
46 }
47 
availableToWrite() const48 ssize_t MonoPipe::availableToWrite() const
49 {
50     if (CC_UNLIKELY(!mNegotiated)) {
51         return NEGOTIATE;
52     }
53     // uses mMaxFrames not mReqFrames, so allows "over-filling" the pipe beyond requested limit
54     ssize_t ret = mMaxFrames - (mRear - android_atomic_acquire_load(&mFront));
55     ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames));
56     return ret;
57 }
58 
write(const void * buffer,size_t count)59 ssize_t MonoPipe::write(const void *buffer, size_t count)
60 {
61     if (CC_UNLIKELY(!mNegotiated)) {
62         return NEGOTIATE;
63     }
64     size_t totalFramesWritten = 0;
65     while (count > 0) {
66         // can't return a negative value, as we already checked for !mNegotiated
67         size_t avail = availableToWrite();
68         size_t written = avail;
69         if (CC_LIKELY(written > count)) {
70             written = count;
71         }
72         size_t rear = mRear & (mMaxFrames - 1);
73         size_t part1 = mMaxFrames - rear;
74         if (part1 > written) {
75             part1 = written;
76         }
77         if (CC_LIKELY(part1 > 0)) {
78             memcpy((char *) mBuffer + (rear << mBitShift), buffer, part1 << mBitShift);
79             if (CC_UNLIKELY(rear + part1 == mMaxFrames)) {
80                 size_t part2 = written - part1;
81                 if (CC_LIKELY(part2 > 0)) {
82                     memcpy(mBuffer, (char *) buffer + (part1 << mBitShift), part2 << mBitShift);
83                 }
84             }
85             android_atomic_release_store(written + mRear, &mRear);
86             totalFramesWritten += written;
87         }
88         if (!mWriteCanBlock) {
89             break;
90         }
91         count -= written;
92         buffer = (char *) buffer + (written << mBitShift);
93         // Simulate blocking I/O by sleeping at different rates, depending on a throttle.
94         // The throttle tries to keep the mean pipe depth near the setpoint, with a slight jitter.
95         uint32_t ns;
96         if (written > 0) {
97             size_t filled = (mMaxFrames - avail) + written;
98             // FIXME cache these values to avoid re-computation
99             if (filled <= mSetpoint / 2) {
100                 // pipe is (nearly) empty, fill quickly
101                 ns = written * ( 500000000 / Format_sampleRate(mFormat));
102             } else if (filled <= (mSetpoint * 3) / 4) {
103                 // pipe is below setpoint, fill at slightly faster rate
104                 ns = written * ( 750000000 / Format_sampleRate(mFormat));
105             } else if (filled <= (mSetpoint * 5) / 4) {
106                 // pipe is at setpoint, fill at nominal rate
107                 ns = written * (1000000000 / Format_sampleRate(mFormat));
108             } else if (filled <= (mSetpoint * 3) / 2) {
109                 // pipe is above setpoint, fill at slightly slower rate
110                 ns = written * (1150000000 / Format_sampleRate(mFormat));
111             } else if (filled <= (mSetpoint * 7) / 4) {
112                 // pipe is overflowing, fill slowly
113                 ns = written * (1350000000 / Format_sampleRate(mFormat));
114             } else {
115                 // pipe is severely overflowing
116                 ns = written * (1750000000 / Format_sampleRate(mFormat));
117             }
118         } else {
119             ns = count * (1350000000 / Format_sampleRate(mFormat));
120         }
121         if (ns > 999999999) {
122             ns = 999999999;
123         }
124         struct timespec nowTs;
125         bool nowTsValid = !clock_gettime(CLOCK_MONOTONIC, &nowTs);
126         // deduct the elapsed time since previous write() completed
127         if (nowTsValid && mWriteTsValid) {
128             time_t sec = nowTs.tv_sec - mWriteTs.tv_sec;
129             long nsec = nowTs.tv_nsec - mWriteTs.tv_nsec;
130             if (nsec < 0) {
131                 --sec;
132                 nsec += 1000000000;
133             }
134             if (sec == 0) {
135                 if ((long) ns > nsec) {
136                     ns -= nsec;
137                 } else {
138                     ns = 0;
139                 }
140             }
141         }
142         if (ns > 0) {
143             const struct timespec req = {0, ns};
144             nanosleep(&req, NULL);
145         }
146         // record the time that this write() completed
147         if (nowTsValid) {
148             mWriteTs = nowTs;
149             if ((mWriteTs.tv_nsec += ns) >= 1000000000) {
150                 mWriteTs.tv_nsec -= 1000000000;
151                 ++mWriteTs.tv_sec;
152             }
153         }
154         mWriteTsValid = nowTsValid;
155     }
156     mFramesWritten += totalFramesWritten;
157     return totalFramesWritten;
158 }
159 
setAvgFrames(size_t setpoint)160 void MonoPipe::setAvgFrames(size_t setpoint)
161 {
162     mSetpoint = setpoint;
163 }
164 
165 }   // namespace android
166