xref: /aosp_15_r20/frameworks/av/media/libdatasource/NuCachedSource2.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker  * Copyright (C) 2010 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker  *
4*ec779b8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker  *
8*ec779b8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker  *
10*ec779b8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker  * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker  */
16*ec779b8eSAndroid Build Coastguard Worker 
17*ec779b8eSAndroid Build Coastguard Worker #include <inttypes.h>
18*ec779b8eSAndroid Build Coastguard Worker 
19*ec779b8eSAndroid Build Coastguard Worker //#define LOG_NDEBUG 0
20*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "NuCachedSource2"
21*ec779b8eSAndroid Build Coastguard Worker #include <utils/Log.h>
22*ec779b8eSAndroid Build Coastguard Worker 
23*ec779b8eSAndroid Build Coastguard Worker #include <datasource/NuCachedSource2.h>
24*ec779b8eSAndroid Build Coastguard Worker #include <datasource/HTTPBase.h>
25*ec779b8eSAndroid Build Coastguard Worker 
26*ec779b8eSAndroid Build Coastguard Worker #include <cutils/properties.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/foundation/ADebug.h>
28*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/foundation/AMessage.h>
29*ec779b8eSAndroid Build Coastguard Worker #include <media/stagefright/MediaErrors.h>
30*ec779b8eSAndroid Build Coastguard Worker 
31*ec779b8eSAndroid Build Coastguard Worker namespace android {
32*ec779b8eSAndroid Build Coastguard Worker 
33*ec779b8eSAndroid Build Coastguard Worker struct PageCache {
34*ec779b8eSAndroid Build Coastguard Worker     explicit PageCache(size_t pageSize);
35*ec779b8eSAndroid Build Coastguard Worker     ~PageCache();
36*ec779b8eSAndroid Build Coastguard Worker 
37*ec779b8eSAndroid Build Coastguard Worker     struct Page {
38*ec779b8eSAndroid Build Coastguard Worker         void *mData;
39*ec779b8eSAndroid Build Coastguard Worker         size_t mSize;
40*ec779b8eSAndroid Build Coastguard Worker     };
41*ec779b8eSAndroid Build Coastguard Worker 
42*ec779b8eSAndroid Build Coastguard Worker     Page *acquirePage();
43*ec779b8eSAndroid Build Coastguard Worker     void releasePage(Page *page);
44*ec779b8eSAndroid Build Coastguard Worker 
45*ec779b8eSAndroid Build Coastguard Worker     void appendPage(Page *page);
46*ec779b8eSAndroid Build Coastguard Worker     size_t releaseFromStart(size_t maxBytes);
47*ec779b8eSAndroid Build Coastguard Worker 
totalSizeandroid::PageCache48*ec779b8eSAndroid Build Coastguard Worker     size_t totalSize() const {
49*ec779b8eSAndroid Build Coastguard Worker         return mTotalSize;
50*ec779b8eSAndroid Build Coastguard Worker     }
51*ec779b8eSAndroid Build Coastguard Worker 
52*ec779b8eSAndroid Build Coastguard Worker     void copy(size_t from, void *data, size_t size);
53*ec779b8eSAndroid Build Coastguard Worker 
54*ec779b8eSAndroid Build Coastguard Worker private:
55*ec779b8eSAndroid Build Coastguard Worker     size_t mPageSize;
56*ec779b8eSAndroid Build Coastguard Worker     size_t mTotalSize;
57*ec779b8eSAndroid Build Coastguard Worker 
58*ec779b8eSAndroid Build Coastguard Worker     List<Page *> mActivePages;
59*ec779b8eSAndroid Build Coastguard Worker     List<Page *> mFreePages;
60*ec779b8eSAndroid Build Coastguard Worker 
61*ec779b8eSAndroid Build Coastguard Worker     void freePages(List<Page *> *list);
62*ec779b8eSAndroid Build Coastguard Worker 
63*ec779b8eSAndroid Build Coastguard Worker     DISALLOW_EVIL_CONSTRUCTORS(PageCache);
64*ec779b8eSAndroid Build Coastguard Worker };
65*ec779b8eSAndroid Build Coastguard Worker 
PageCache(size_t pageSize)66*ec779b8eSAndroid Build Coastguard Worker PageCache::PageCache(size_t pageSize)
67*ec779b8eSAndroid Build Coastguard Worker     : mPageSize(pageSize),
68*ec779b8eSAndroid Build Coastguard Worker       mTotalSize(0) {
69*ec779b8eSAndroid Build Coastguard Worker }
70*ec779b8eSAndroid Build Coastguard Worker 
~PageCache()71*ec779b8eSAndroid Build Coastguard Worker PageCache::~PageCache() {
72*ec779b8eSAndroid Build Coastguard Worker     freePages(&mActivePages);
73*ec779b8eSAndroid Build Coastguard Worker     freePages(&mFreePages);
74*ec779b8eSAndroid Build Coastguard Worker }
75*ec779b8eSAndroid Build Coastguard Worker 
freePages(List<Page * > * list)76*ec779b8eSAndroid Build Coastguard Worker void PageCache::freePages(List<Page *> *list) {
77*ec779b8eSAndroid Build Coastguard Worker     List<Page *>::iterator it = list->begin();
78*ec779b8eSAndroid Build Coastguard Worker     while (it != list->end()) {
79*ec779b8eSAndroid Build Coastguard Worker         Page *page = *it;
80*ec779b8eSAndroid Build Coastguard Worker 
81*ec779b8eSAndroid Build Coastguard Worker         free(page->mData);
82*ec779b8eSAndroid Build Coastguard Worker         delete page;
83*ec779b8eSAndroid Build Coastguard Worker         page = NULL;
84*ec779b8eSAndroid Build Coastguard Worker 
85*ec779b8eSAndroid Build Coastguard Worker         ++it;
86*ec779b8eSAndroid Build Coastguard Worker     }
87*ec779b8eSAndroid Build Coastguard Worker }
88*ec779b8eSAndroid Build Coastguard Worker 
acquirePage()89*ec779b8eSAndroid Build Coastguard Worker PageCache::Page *PageCache::acquirePage() {
90*ec779b8eSAndroid Build Coastguard Worker     if (!mFreePages.empty()) {
91*ec779b8eSAndroid Build Coastguard Worker         List<Page *>::iterator it = mFreePages.begin();
92*ec779b8eSAndroid Build Coastguard Worker         Page *page = *it;
93*ec779b8eSAndroid Build Coastguard Worker         mFreePages.erase(it);
94*ec779b8eSAndroid Build Coastguard Worker 
95*ec779b8eSAndroid Build Coastguard Worker         return page;
96*ec779b8eSAndroid Build Coastguard Worker     }
97*ec779b8eSAndroid Build Coastguard Worker 
98*ec779b8eSAndroid Build Coastguard Worker     Page *page = new Page;
99*ec779b8eSAndroid Build Coastguard Worker     page->mData = malloc(mPageSize);
100*ec779b8eSAndroid Build Coastguard Worker     page->mSize = 0;
101*ec779b8eSAndroid Build Coastguard Worker 
102*ec779b8eSAndroid Build Coastguard Worker     return page;
103*ec779b8eSAndroid Build Coastguard Worker }
104*ec779b8eSAndroid Build Coastguard Worker 
releasePage(Page * page)105*ec779b8eSAndroid Build Coastguard Worker void PageCache::releasePage(Page *page) {
106*ec779b8eSAndroid Build Coastguard Worker     page->mSize = 0;
107*ec779b8eSAndroid Build Coastguard Worker     mFreePages.push_back(page);
108*ec779b8eSAndroid Build Coastguard Worker }
109*ec779b8eSAndroid Build Coastguard Worker 
appendPage(Page * page)110*ec779b8eSAndroid Build Coastguard Worker void PageCache::appendPage(Page *page) {
111*ec779b8eSAndroid Build Coastguard Worker     mTotalSize += page->mSize;
112*ec779b8eSAndroid Build Coastguard Worker     mActivePages.push_back(page);
113*ec779b8eSAndroid Build Coastguard Worker }
114*ec779b8eSAndroid Build Coastguard Worker 
releaseFromStart(size_t maxBytes)115*ec779b8eSAndroid Build Coastguard Worker size_t PageCache::releaseFromStart(size_t maxBytes) {
116*ec779b8eSAndroid Build Coastguard Worker     size_t bytesReleased = 0;
117*ec779b8eSAndroid Build Coastguard Worker 
118*ec779b8eSAndroid Build Coastguard Worker     while (maxBytes > 0 && !mActivePages.empty()) {
119*ec779b8eSAndroid Build Coastguard Worker         List<Page *>::iterator it = mActivePages.begin();
120*ec779b8eSAndroid Build Coastguard Worker 
121*ec779b8eSAndroid Build Coastguard Worker         Page *page = *it;
122*ec779b8eSAndroid Build Coastguard Worker 
123*ec779b8eSAndroid Build Coastguard Worker         if (maxBytes < page->mSize) {
124*ec779b8eSAndroid Build Coastguard Worker             break;
125*ec779b8eSAndroid Build Coastguard Worker         }
126*ec779b8eSAndroid Build Coastguard Worker 
127*ec779b8eSAndroid Build Coastguard Worker         mActivePages.erase(it);
128*ec779b8eSAndroid Build Coastguard Worker 
129*ec779b8eSAndroid Build Coastguard Worker         maxBytes -= page->mSize;
130*ec779b8eSAndroid Build Coastguard Worker         bytesReleased += page->mSize;
131*ec779b8eSAndroid Build Coastguard Worker 
132*ec779b8eSAndroid Build Coastguard Worker         releasePage(page);
133*ec779b8eSAndroid Build Coastguard Worker     }
134*ec779b8eSAndroid Build Coastguard Worker 
135*ec779b8eSAndroid Build Coastguard Worker     mTotalSize -= bytesReleased;
136*ec779b8eSAndroid Build Coastguard Worker     return bytesReleased;
137*ec779b8eSAndroid Build Coastguard Worker }
138*ec779b8eSAndroid Build Coastguard Worker 
copy(size_t from,void * data,size_t size)139*ec779b8eSAndroid Build Coastguard Worker void PageCache::copy(size_t from, void *data, size_t size) {
140*ec779b8eSAndroid Build Coastguard Worker     ALOGV("copy from %zu size %zu", from, size);
141*ec779b8eSAndroid Build Coastguard Worker 
142*ec779b8eSAndroid Build Coastguard Worker     if (size == 0) {
143*ec779b8eSAndroid Build Coastguard Worker         return;
144*ec779b8eSAndroid Build Coastguard Worker     }
145*ec779b8eSAndroid Build Coastguard Worker 
146*ec779b8eSAndroid Build Coastguard Worker     CHECK_LE(from + size, mTotalSize);
147*ec779b8eSAndroid Build Coastguard Worker 
148*ec779b8eSAndroid Build Coastguard Worker     size_t offset = 0;
149*ec779b8eSAndroid Build Coastguard Worker     List<Page *>::iterator it = mActivePages.begin();
150*ec779b8eSAndroid Build Coastguard Worker     while (from >= offset + (*it)->mSize) {
151*ec779b8eSAndroid Build Coastguard Worker         offset += (*it)->mSize;
152*ec779b8eSAndroid Build Coastguard Worker         ++it;
153*ec779b8eSAndroid Build Coastguard Worker     }
154*ec779b8eSAndroid Build Coastguard Worker 
155*ec779b8eSAndroid Build Coastguard Worker     size_t delta = from - offset;
156*ec779b8eSAndroid Build Coastguard Worker     size_t avail = (*it)->mSize - delta;
157*ec779b8eSAndroid Build Coastguard Worker 
158*ec779b8eSAndroid Build Coastguard Worker     if (avail >= size) {
159*ec779b8eSAndroid Build Coastguard Worker         memcpy(data, (const uint8_t *)(*it)->mData + delta, size);
160*ec779b8eSAndroid Build Coastguard Worker         return;
161*ec779b8eSAndroid Build Coastguard Worker     }
162*ec779b8eSAndroid Build Coastguard Worker 
163*ec779b8eSAndroid Build Coastguard Worker     memcpy(data, (const uint8_t *)(*it)->mData + delta, avail);
164*ec779b8eSAndroid Build Coastguard Worker     ++it;
165*ec779b8eSAndroid Build Coastguard Worker     data = (uint8_t *)data + avail;
166*ec779b8eSAndroid Build Coastguard Worker     size -= avail;
167*ec779b8eSAndroid Build Coastguard Worker 
168*ec779b8eSAndroid Build Coastguard Worker     while (size > 0) {
169*ec779b8eSAndroid Build Coastguard Worker         size_t copy = (*it)->mSize;
170*ec779b8eSAndroid Build Coastguard Worker         if (copy > size) {
171*ec779b8eSAndroid Build Coastguard Worker             copy = size;
172*ec779b8eSAndroid Build Coastguard Worker         }
173*ec779b8eSAndroid Build Coastguard Worker         memcpy(data, (*it)->mData, copy);
174*ec779b8eSAndroid Build Coastguard Worker         data = (uint8_t *)data + copy;
175*ec779b8eSAndroid Build Coastguard Worker         size -= copy;
176*ec779b8eSAndroid Build Coastguard Worker         ++it;
177*ec779b8eSAndroid Build Coastguard Worker     }
178*ec779b8eSAndroid Build Coastguard Worker }
179*ec779b8eSAndroid Build Coastguard Worker 
180*ec779b8eSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
181*ec779b8eSAndroid Build Coastguard Worker 
NuCachedSource2(const sp<DataSource> & source,const char * cacheConfig,bool disconnectAtHighwatermark)182*ec779b8eSAndroid Build Coastguard Worker NuCachedSource2::NuCachedSource2(
183*ec779b8eSAndroid Build Coastguard Worker         const sp<DataSource> &source,
184*ec779b8eSAndroid Build Coastguard Worker         const char *cacheConfig,
185*ec779b8eSAndroid Build Coastguard Worker         bool disconnectAtHighwatermark)
186*ec779b8eSAndroid Build Coastguard Worker     : mSource(source),
187*ec779b8eSAndroid Build Coastguard Worker       mReflector(new AHandlerReflector<NuCachedSource2>(this)),
188*ec779b8eSAndroid Build Coastguard Worker       mLooper(new ALooper),
189*ec779b8eSAndroid Build Coastguard Worker       mCache(new PageCache(kPageSize)),
190*ec779b8eSAndroid Build Coastguard Worker       mCacheOffset(0),
191*ec779b8eSAndroid Build Coastguard Worker       mFinalStatus(OK),
192*ec779b8eSAndroid Build Coastguard Worker       mLastAccessPos(0),
193*ec779b8eSAndroid Build Coastguard Worker       mFetching(true),
194*ec779b8eSAndroid Build Coastguard Worker       mDisconnecting(false),
195*ec779b8eSAndroid Build Coastguard Worker       mLastFetchTimeUs(-1),
196*ec779b8eSAndroid Build Coastguard Worker       mNumRetriesLeft(kMaxNumRetries),
197*ec779b8eSAndroid Build Coastguard Worker       mHighwaterThresholdBytes(kDefaultHighWaterThreshold),
198*ec779b8eSAndroid Build Coastguard Worker       mLowwaterThresholdBytes(kDefaultLowWaterThreshold),
199*ec779b8eSAndroid Build Coastguard Worker       mKeepAliveIntervalUs(kDefaultKeepAliveIntervalUs),
200*ec779b8eSAndroid Build Coastguard Worker       mDisconnectAtHighwatermark(disconnectAtHighwatermark) {
201*ec779b8eSAndroid Build Coastguard Worker     // We are NOT going to support disconnect-at-highwatermark indefinitely
202*ec779b8eSAndroid Build Coastguard Worker     // and we are not guaranteeing support for client-specified cache
203*ec779b8eSAndroid Build Coastguard Worker     // parameters. Both of these are temporary measures to solve a specific
204*ec779b8eSAndroid Build Coastguard Worker     // problem that will be solved in a better way going forward.
205*ec779b8eSAndroid Build Coastguard Worker 
206*ec779b8eSAndroid Build Coastguard Worker     updateCacheParamsFromSystemProperty();
207*ec779b8eSAndroid Build Coastguard Worker 
208*ec779b8eSAndroid Build Coastguard Worker     if (cacheConfig != NULL) {
209*ec779b8eSAndroid Build Coastguard Worker         updateCacheParamsFromString(cacheConfig);
210*ec779b8eSAndroid Build Coastguard Worker     }
211*ec779b8eSAndroid Build Coastguard Worker 
212*ec779b8eSAndroid Build Coastguard Worker     if (mDisconnectAtHighwatermark) {
213*ec779b8eSAndroid Build Coastguard Worker         // Makes no sense to disconnect and do keep-alives...
214*ec779b8eSAndroid Build Coastguard Worker         mKeepAliveIntervalUs = 0;
215*ec779b8eSAndroid Build Coastguard Worker     }
216*ec779b8eSAndroid Build Coastguard Worker 
217*ec779b8eSAndroid Build Coastguard Worker     mLooper->setName("NuCachedSource2");
218*ec779b8eSAndroid Build Coastguard Worker     mLooper->registerHandler(mReflector);
219*ec779b8eSAndroid Build Coastguard Worker 
220*ec779b8eSAndroid Build Coastguard Worker     // Since it may not be obvious why our looper thread needs to be
221*ec779b8eSAndroid Build Coastguard Worker     // able to call into java since it doesn't appear to do so at all...
222*ec779b8eSAndroid Build Coastguard Worker     // IMediaHTTPConnection may be (and most likely is) implemented in JAVA
223*ec779b8eSAndroid Build Coastguard Worker     // and a local JAVA IBinder will call directly into JNI methods.
224*ec779b8eSAndroid Build Coastguard Worker     // So whenever we call DataSource::readAt it may end up in a call to
225*ec779b8eSAndroid Build Coastguard Worker     // IMediaHTTPConnection::readAt and therefore call back into JAVA.
226*ec779b8eSAndroid Build Coastguard Worker     mLooper->start(false /* runOnCallingThread */, true /* canCallJava */);
227*ec779b8eSAndroid Build Coastguard Worker 
228*ec779b8eSAndroid Build Coastguard Worker     mName = String8::format("NuCachedSource2(%s)", mSource->toString().c_str());
229*ec779b8eSAndroid Build Coastguard Worker }
230*ec779b8eSAndroid Build Coastguard Worker 
~NuCachedSource2()231*ec779b8eSAndroid Build Coastguard Worker NuCachedSource2::~NuCachedSource2() {
232*ec779b8eSAndroid Build Coastguard Worker     mLooper->stop();
233*ec779b8eSAndroid Build Coastguard Worker     mLooper->unregisterHandler(mReflector->id());
234*ec779b8eSAndroid Build Coastguard Worker 
235*ec779b8eSAndroid Build Coastguard Worker     delete mCache;
236*ec779b8eSAndroid Build Coastguard Worker     mCache = NULL;
237*ec779b8eSAndroid Build Coastguard Worker }
238*ec779b8eSAndroid Build Coastguard Worker 
239*ec779b8eSAndroid Build Coastguard Worker // static
Create(const sp<DataSource> & source,const char * cacheConfig,bool disconnectAtHighwatermark)240*ec779b8eSAndroid Build Coastguard Worker sp<NuCachedSource2> NuCachedSource2::Create(
241*ec779b8eSAndroid Build Coastguard Worker         const sp<DataSource> &source,
242*ec779b8eSAndroid Build Coastguard Worker         const char *cacheConfig,
243*ec779b8eSAndroid Build Coastguard Worker         bool disconnectAtHighwatermark) {
244*ec779b8eSAndroid Build Coastguard Worker     sp<NuCachedSource2> instance = new NuCachedSource2(
245*ec779b8eSAndroid Build Coastguard Worker             source, cacheConfig, disconnectAtHighwatermark);
246*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(instance->mLock);
247*ec779b8eSAndroid Build Coastguard Worker     (new AMessage(kWhatFetchMore, instance->mReflector))->post();
248*ec779b8eSAndroid Build Coastguard Worker     return instance;
249*ec779b8eSAndroid Build Coastguard Worker }
250*ec779b8eSAndroid Build Coastguard Worker 
getEstimatedBandwidthKbps(int32_t * kbps)251*ec779b8eSAndroid Build Coastguard Worker status_t NuCachedSource2::getEstimatedBandwidthKbps(int32_t *kbps) {
252*ec779b8eSAndroid Build Coastguard Worker     if (mSource->flags() & kIsHTTPBasedSource) {
253*ec779b8eSAndroid Build Coastguard Worker         HTTPBase* source = static_cast<HTTPBase *>(mSource.get());
254*ec779b8eSAndroid Build Coastguard Worker         return source->getEstimatedBandwidthKbps(kbps);
255*ec779b8eSAndroid Build Coastguard Worker     }
256*ec779b8eSAndroid Build Coastguard Worker     return ERROR_UNSUPPORTED;
257*ec779b8eSAndroid Build Coastguard Worker }
258*ec779b8eSAndroid Build Coastguard Worker 
close()259*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::close() {
260*ec779b8eSAndroid Build Coastguard Worker     disconnect();
261*ec779b8eSAndroid Build Coastguard Worker }
262*ec779b8eSAndroid Build Coastguard Worker 
disconnect()263*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::disconnect() {
264*ec779b8eSAndroid Build Coastguard Worker     if (mSource->flags() & kIsHTTPBasedSource) {
265*ec779b8eSAndroid Build Coastguard Worker         ALOGV("disconnecting HTTPBasedSource");
266*ec779b8eSAndroid Build Coastguard Worker 
267*ec779b8eSAndroid Build Coastguard Worker         {
268*ec779b8eSAndroid Build Coastguard Worker             Mutex::Autolock autoLock(mLock);
269*ec779b8eSAndroid Build Coastguard Worker             // set mDisconnecting to true, if a fetch returns after
270*ec779b8eSAndroid Build Coastguard Worker             // this, the source will be marked as EOS.
271*ec779b8eSAndroid Build Coastguard Worker             mDisconnecting = true;
272*ec779b8eSAndroid Build Coastguard Worker 
273*ec779b8eSAndroid Build Coastguard Worker             // explicitly signal mCondition so that the pending readAt()
274*ec779b8eSAndroid Build Coastguard Worker             // will immediately return
275*ec779b8eSAndroid Build Coastguard Worker             mCondition.signal();
276*ec779b8eSAndroid Build Coastguard Worker         }
277*ec779b8eSAndroid Build Coastguard Worker 
278*ec779b8eSAndroid Build Coastguard Worker         // explicitly disconnect from the source, to allow any
279*ec779b8eSAndroid Build Coastguard Worker         // pending reads to return more promptly
280*ec779b8eSAndroid Build Coastguard Worker         static_cast<HTTPBase *>(mSource.get())->disconnect();
281*ec779b8eSAndroid Build Coastguard Worker     }
282*ec779b8eSAndroid Build Coastguard Worker }
283*ec779b8eSAndroid Build Coastguard Worker 
setCacheStatCollectFreq(int32_t freqMs)284*ec779b8eSAndroid Build Coastguard Worker status_t NuCachedSource2::setCacheStatCollectFreq(int32_t freqMs) {
285*ec779b8eSAndroid Build Coastguard Worker     if (mSource->flags() & kIsHTTPBasedSource) {
286*ec779b8eSAndroid Build Coastguard Worker         HTTPBase *source = static_cast<HTTPBase *>(mSource.get());
287*ec779b8eSAndroid Build Coastguard Worker         return source->setBandwidthStatCollectFreq(freqMs);
288*ec779b8eSAndroid Build Coastguard Worker     }
289*ec779b8eSAndroid Build Coastguard Worker     return ERROR_UNSUPPORTED;
290*ec779b8eSAndroid Build Coastguard Worker }
291*ec779b8eSAndroid Build Coastguard Worker 
initCheck() const292*ec779b8eSAndroid Build Coastguard Worker status_t NuCachedSource2::initCheck() const {
293*ec779b8eSAndroid Build Coastguard Worker     return mSource->initCheck();
294*ec779b8eSAndroid Build Coastguard Worker }
295*ec779b8eSAndroid Build Coastguard Worker 
getSize(off64_t * size)296*ec779b8eSAndroid Build Coastguard Worker status_t NuCachedSource2::getSize(off64_t *size) {
297*ec779b8eSAndroid Build Coastguard Worker     return mSource->getSize(size);
298*ec779b8eSAndroid Build Coastguard Worker }
299*ec779b8eSAndroid Build Coastguard Worker 
flags()300*ec779b8eSAndroid Build Coastguard Worker uint32_t NuCachedSource2::flags() {
301*ec779b8eSAndroid Build Coastguard Worker     // Remove HTTP related flags since NuCachedSource2 is not HTTP-based.
302*ec779b8eSAndroid Build Coastguard Worker     uint32_t flags = mSource->flags() & ~(kWantsPrefetching | kIsHTTPBasedSource);
303*ec779b8eSAndroid Build Coastguard Worker     return (flags | kIsCachingDataSource);
304*ec779b8eSAndroid Build Coastguard Worker }
305*ec779b8eSAndroid Build Coastguard Worker 
onMessageReceived(const sp<AMessage> & msg)306*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) {
307*ec779b8eSAndroid Build Coastguard Worker     switch (msg->what()) {
308*ec779b8eSAndroid Build Coastguard Worker         case kWhatFetchMore:
309*ec779b8eSAndroid Build Coastguard Worker         {
310*ec779b8eSAndroid Build Coastguard Worker             onFetch();
311*ec779b8eSAndroid Build Coastguard Worker             break;
312*ec779b8eSAndroid Build Coastguard Worker         }
313*ec779b8eSAndroid Build Coastguard Worker 
314*ec779b8eSAndroid Build Coastguard Worker         case kWhatRead:
315*ec779b8eSAndroid Build Coastguard Worker         {
316*ec779b8eSAndroid Build Coastguard Worker             onRead(msg);
317*ec779b8eSAndroid Build Coastguard Worker             break;
318*ec779b8eSAndroid Build Coastguard Worker         }
319*ec779b8eSAndroid Build Coastguard Worker 
320*ec779b8eSAndroid Build Coastguard Worker         default:
321*ec779b8eSAndroid Build Coastguard Worker             TRESPASS();
322*ec779b8eSAndroid Build Coastguard Worker     }
323*ec779b8eSAndroid Build Coastguard Worker }
324*ec779b8eSAndroid Build Coastguard Worker 
fetchInternal()325*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::fetchInternal() {
326*ec779b8eSAndroid Build Coastguard Worker     ALOGV("fetchInternal");
327*ec779b8eSAndroid Build Coastguard Worker 
328*ec779b8eSAndroid Build Coastguard Worker     bool reconnect = false;
329*ec779b8eSAndroid Build Coastguard Worker 
330*ec779b8eSAndroid Build Coastguard Worker     {
331*ec779b8eSAndroid Build Coastguard Worker         Mutex::Autolock autoLock(mLock);
332*ec779b8eSAndroid Build Coastguard Worker         CHECK(mFinalStatus == OK || mNumRetriesLeft > 0);
333*ec779b8eSAndroid Build Coastguard Worker 
334*ec779b8eSAndroid Build Coastguard Worker         if (mFinalStatus != OK) {
335*ec779b8eSAndroid Build Coastguard Worker             --mNumRetriesLeft;
336*ec779b8eSAndroid Build Coastguard Worker 
337*ec779b8eSAndroid Build Coastguard Worker             reconnect = true;
338*ec779b8eSAndroid Build Coastguard Worker         }
339*ec779b8eSAndroid Build Coastguard Worker     }
340*ec779b8eSAndroid Build Coastguard Worker 
341*ec779b8eSAndroid Build Coastguard Worker     if (reconnect) {
342*ec779b8eSAndroid Build Coastguard Worker         status_t err =
343*ec779b8eSAndroid Build Coastguard Worker             mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize());
344*ec779b8eSAndroid Build Coastguard Worker 
345*ec779b8eSAndroid Build Coastguard Worker         Mutex::Autolock autoLock(mLock);
346*ec779b8eSAndroid Build Coastguard Worker 
347*ec779b8eSAndroid Build Coastguard Worker         if (mDisconnecting) {
348*ec779b8eSAndroid Build Coastguard Worker             mNumRetriesLeft = 0;
349*ec779b8eSAndroid Build Coastguard Worker             mFinalStatus = ERROR_END_OF_STREAM;
350*ec779b8eSAndroid Build Coastguard Worker             return;
351*ec779b8eSAndroid Build Coastguard Worker         } else if (err == ERROR_UNSUPPORTED || err == -EPIPE) {
352*ec779b8eSAndroid Build Coastguard Worker             // These are errors that are not likely to go away even if we
353*ec779b8eSAndroid Build Coastguard Worker             // retry, i.e. the server doesn't support range requests or similar.
354*ec779b8eSAndroid Build Coastguard Worker             mNumRetriesLeft = 0;
355*ec779b8eSAndroid Build Coastguard Worker             return;
356*ec779b8eSAndroid Build Coastguard Worker         } else if (err != OK) {
357*ec779b8eSAndroid Build Coastguard Worker             ALOGI("The attempt to reconnect failed, %d retries remaining",
358*ec779b8eSAndroid Build Coastguard Worker                  mNumRetriesLeft);
359*ec779b8eSAndroid Build Coastguard Worker 
360*ec779b8eSAndroid Build Coastguard Worker             return;
361*ec779b8eSAndroid Build Coastguard Worker         }
362*ec779b8eSAndroid Build Coastguard Worker     }
363*ec779b8eSAndroid Build Coastguard Worker 
364*ec779b8eSAndroid Build Coastguard Worker     PageCache::Page *page = mCache->acquirePage();
365*ec779b8eSAndroid Build Coastguard Worker 
366*ec779b8eSAndroid Build Coastguard Worker     ssize_t n = mSource->readAt(
367*ec779b8eSAndroid Build Coastguard Worker             mCacheOffset + mCache->totalSize(), page->mData, kPageSize);
368*ec779b8eSAndroid Build Coastguard Worker 
369*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(mLock);
370*ec779b8eSAndroid Build Coastguard Worker 
371*ec779b8eSAndroid Build Coastguard Worker     if (n == 0 || mDisconnecting) {
372*ec779b8eSAndroid Build Coastguard Worker         ALOGI("caching reached eos.");
373*ec779b8eSAndroid Build Coastguard Worker 
374*ec779b8eSAndroid Build Coastguard Worker         mNumRetriesLeft = 0;
375*ec779b8eSAndroid Build Coastguard Worker         mFinalStatus = ERROR_END_OF_STREAM;
376*ec779b8eSAndroid Build Coastguard Worker 
377*ec779b8eSAndroid Build Coastguard Worker         mCache->releasePage(page);
378*ec779b8eSAndroid Build Coastguard Worker     } else if (n < 0) {
379*ec779b8eSAndroid Build Coastguard Worker         mFinalStatus = n;
380*ec779b8eSAndroid Build Coastguard Worker         if (n == ERROR_UNSUPPORTED || n == -EPIPE) {
381*ec779b8eSAndroid Build Coastguard Worker             // These are errors that are not likely to go away even if we
382*ec779b8eSAndroid Build Coastguard Worker             // retry, i.e. the server doesn't support range requests or similar.
383*ec779b8eSAndroid Build Coastguard Worker             mNumRetriesLeft = 0;
384*ec779b8eSAndroid Build Coastguard Worker         }
385*ec779b8eSAndroid Build Coastguard Worker 
386*ec779b8eSAndroid Build Coastguard Worker         ALOGE("source returned error %zd, %d retries left", n, mNumRetriesLeft);
387*ec779b8eSAndroid Build Coastguard Worker         mCache->releasePage(page);
388*ec779b8eSAndroid Build Coastguard Worker     } else {
389*ec779b8eSAndroid Build Coastguard Worker         if (mFinalStatus != OK) {
390*ec779b8eSAndroid Build Coastguard Worker             ALOGI("retrying a previously failed read succeeded.");
391*ec779b8eSAndroid Build Coastguard Worker         }
392*ec779b8eSAndroid Build Coastguard Worker         mNumRetriesLeft = kMaxNumRetries;
393*ec779b8eSAndroid Build Coastguard Worker         mFinalStatus = OK;
394*ec779b8eSAndroid Build Coastguard Worker 
395*ec779b8eSAndroid Build Coastguard Worker         page->mSize = n;
396*ec779b8eSAndroid Build Coastguard Worker         mCache->appendPage(page);
397*ec779b8eSAndroid Build Coastguard Worker     }
398*ec779b8eSAndroid Build Coastguard Worker }
399*ec779b8eSAndroid Build Coastguard Worker 
onFetch()400*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::onFetch() {
401*ec779b8eSAndroid Build Coastguard Worker     ALOGV("onFetch");
402*ec779b8eSAndroid Build Coastguard Worker 
403*ec779b8eSAndroid Build Coastguard Worker     if (mFinalStatus != OK && mNumRetriesLeft == 0) {
404*ec779b8eSAndroid Build Coastguard Worker         ALOGV("EOS reached, done prefetching for now");
405*ec779b8eSAndroid Build Coastguard Worker         mFetching = false;
406*ec779b8eSAndroid Build Coastguard Worker     }
407*ec779b8eSAndroid Build Coastguard Worker 
408*ec779b8eSAndroid Build Coastguard Worker     bool keepAlive =
409*ec779b8eSAndroid Build Coastguard Worker         !mFetching
410*ec779b8eSAndroid Build Coastguard Worker             && mFinalStatus == OK
411*ec779b8eSAndroid Build Coastguard Worker             && mKeepAliveIntervalUs > 0
412*ec779b8eSAndroid Build Coastguard Worker             && ALooper::GetNowUs() >= mLastFetchTimeUs + mKeepAliveIntervalUs;
413*ec779b8eSAndroid Build Coastguard Worker 
414*ec779b8eSAndroid Build Coastguard Worker     if (mFetching || keepAlive) {
415*ec779b8eSAndroid Build Coastguard Worker         if (keepAlive) {
416*ec779b8eSAndroid Build Coastguard Worker             ALOGI("Keep alive");
417*ec779b8eSAndroid Build Coastguard Worker         }
418*ec779b8eSAndroid Build Coastguard Worker 
419*ec779b8eSAndroid Build Coastguard Worker         fetchInternal();
420*ec779b8eSAndroid Build Coastguard Worker 
421*ec779b8eSAndroid Build Coastguard Worker         mLastFetchTimeUs = ALooper::GetNowUs();
422*ec779b8eSAndroid Build Coastguard Worker 
423*ec779b8eSAndroid Build Coastguard Worker         if (mFetching && mCache->totalSize() >= mHighwaterThresholdBytes) {
424*ec779b8eSAndroid Build Coastguard Worker             ALOGI("Cache full, done prefetching for now");
425*ec779b8eSAndroid Build Coastguard Worker             mFetching = false;
426*ec779b8eSAndroid Build Coastguard Worker 
427*ec779b8eSAndroid Build Coastguard Worker             if (mDisconnectAtHighwatermark
428*ec779b8eSAndroid Build Coastguard Worker                     && (mSource->flags() & DataSource::kIsHTTPBasedSource)) {
429*ec779b8eSAndroid Build Coastguard Worker                 ALOGV("Disconnecting at high watermark");
430*ec779b8eSAndroid Build Coastguard Worker                 static_cast<HTTPBase *>(mSource.get())->disconnect();
431*ec779b8eSAndroid Build Coastguard Worker                 mFinalStatus = -EAGAIN;
432*ec779b8eSAndroid Build Coastguard Worker             }
433*ec779b8eSAndroid Build Coastguard Worker         }
434*ec779b8eSAndroid Build Coastguard Worker     } else {
435*ec779b8eSAndroid Build Coastguard Worker         Mutex::Autolock autoLock(mLock);
436*ec779b8eSAndroid Build Coastguard Worker         restartPrefetcherIfNecessary_l();
437*ec779b8eSAndroid Build Coastguard Worker     }
438*ec779b8eSAndroid Build Coastguard Worker 
439*ec779b8eSAndroid Build Coastguard Worker     int64_t delayUs;
440*ec779b8eSAndroid Build Coastguard Worker     if (mFetching) {
441*ec779b8eSAndroid Build Coastguard Worker         if (mFinalStatus != OK && mNumRetriesLeft > 0) {
442*ec779b8eSAndroid Build Coastguard Worker             // We failed this time and will try again in 3 seconds.
443*ec779b8eSAndroid Build Coastguard Worker             delayUs = 3000000LL;
444*ec779b8eSAndroid Build Coastguard Worker         } else {
445*ec779b8eSAndroid Build Coastguard Worker             delayUs = 0;
446*ec779b8eSAndroid Build Coastguard Worker         }
447*ec779b8eSAndroid Build Coastguard Worker     } else {
448*ec779b8eSAndroid Build Coastguard Worker         delayUs = 100000LL;
449*ec779b8eSAndroid Build Coastguard Worker     }
450*ec779b8eSAndroid Build Coastguard Worker 
451*ec779b8eSAndroid Build Coastguard Worker     (new AMessage(kWhatFetchMore, mReflector))->post(delayUs);
452*ec779b8eSAndroid Build Coastguard Worker }
453*ec779b8eSAndroid Build Coastguard Worker 
onRead(const sp<AMessage> & msg)454*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::onRead(const sp<AMessage> &msg) {
455*ec779b8eSAndroid Build Coastguard Worker     ALOGV("onRead");
456*ec779b8eSAndroid Build Coastguard Worker 
457*ec779b8eSAndroid Build Coastguard Worker     int64_t offset;
458*ec779b8eSAndroid Build Coastguard Worker     CHECK(msg->findInt64("offset", &offset));
459*ec779b8eSAndroid Build Coastguard Worker 
460*ec779b8eSAndroid Build Coastguard Worker     void *data;
461*ec779b8eSAndroid Build Coastguard Worker     CHECK(msg->findPointer("data", &data));
462*ec779b8eSAndroid Build Coastguard Worker 
463*ec779b8eSAndroid Build Coastguard Worker     size_t size;
464*ec779b8eSAndroid Build Coastguard Worker     CHECK(msg->findSize("size", &size));
465*ec779b8eSAndroid Build Coastguard Worker 
466*ec779b8eSAndroid Build Coastguard Worker     ssize_t result = readInternal(offset, data, size);
467*ec779b8eSAndroid Build Coastguard Worker 
468*ec779b8eSAndroid Build Coastguard Worker     if (result == -EAGAIN) {
469*ec779b8eSAndroid Build Coastguard Worker         msg->post(50000);
470*ec779b8eSAndroid Build Coastguard Worker         return;
471*ec779b8eSAndroid Build Coastguard Worker     }
472*ec779b8eSAndroid Build Coastguard Worker 
473*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(mLock);
474*ec779b8eSAndroid Build Coastguard Worker     if (mDisconnecting) {
475*ec779b8eSAndroid Build Coastguard Worker         mCondition.signal();
476*ec779b8eSAndroid Build Coastguard Worker         return;
477*ec779b8eSAndroid Build Coastguard Worker     }
478*ec779b8eSAndroid Build Coastguard Worker 
479*ec779b8eSAndroid Build Coastguard Worker     CHECK(mAsyncResult == NULL);
480*ec779b8eSAndroid Build Coastguard Worker 
481*ec779b8eSAndroid Build Coastguard Worker     mAsyncResult = new AMessage;
482*ec779b8eSAndroid Build Coastguard Worker     mAsyncResult->setInt32("result", result);
483*ec779b8eSAndroid Build Coastguard Worker 
484*ec779b8eSAndroid Build Coastguard Worker     mCondition.signal();
485*ec779b8eSAndroid Build Coastguard Worker }
486*ec779b8eSAndroid Build Coastguard Worker 
restartPrefetcherIfNecessary_l(bool ignoreLowWaterThreshold,bool force)487*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::restartPrefetcherIfNecessary_l(
488*ec779b8eSAndroid Build Coastguard Worker         bool ignoreLowWaterThreshold, bool force) {
489*ec779b8eSAndroid Build Coastguard Worker     static const size_t kGrayArea = 1024 * 1024;
490*ec779b8eSAndroid Build Coastguard Worker 
491*ec779b8eSAndroid Build Coastguard Worker     if (mFetching || (mFinalStatus != OK && mNumRetriesLeft == 0)) {
492*ec779b8eSAndroid Build Coastguard Worker         return;
493*ec779b8eSAndroid Build Coastguard Worker     }
494*ec779b8eSAndroid Build Coastguard Worker 
495*ec779b8eSAndroid Build Coastguard Worker     if (!ignoreLowWaterThreshold && !force
496*ec779b8eSAndroid Build Coastguard Worker             && mCacheOffset + mCache->totalSize() - mLastAccessPos
497*ec779b8eSAndroid Build Coastguard Worker                 >= mLowwaterThresholdBytes) {
498*ec779b8eSAndroid Build Coastguard Worker         return;
499*ec779b8eSAndroid Build Coastguard Worker     }
500*ec779b8eSAndroid Build Coastguard Worker 
501*ec779b8eSAndroid Build Coastguard Worker     size_t maxBytes = mLastAccessPos - mCacheOffset;
502*ec779b8eSAndroid Build Coastguard Worker 
503*ec779b8eSAndroid Build Coastguard Worker     if (!force) {
504*ec779b8eSAndroid Build Coastguard Worker         if (maxBytes < kGrayArea) {
505*ec779b8eSAndroid Build Coastguard Worker             return;
506*ec779b8eSAndroid Build Coastguard Worker         }
507*ec779b8eSAndroid Build Coastguard Worker 
508*ec779b8eSAndroid Build Coastguard Worker         maxBytes -= kGrayArea;
509*ec779b8eSAndroid Build Coastguard Worker     }
510*ec779b8eSAndroid Build Coastguard Worker 
511*ec779b8eSAndroid Build Coastguard Worker     size_t actualBytes = mCache->releaseFromStart(maxBytes);
512*ec779b8eSAndroid Build Coastguard Worker     mCacheOffset += actualBytes;
513*ec779b8eSAndroid Build Coastguard Worker 
514*ec779b8eSAndroid Build Coastguard Worker     ALOGI("restarting prefetcher, totalSize = %zu", mCache->totalSize());
515*ec779b8eSAndroid Build Coastguard Worker     mFetching = true;
516*ec779b8eSAndroid Build Coastguard Worker }
517*ec779b8eSAndroid Build Coastguard Worker 
readAt(off64_t offset,void * data,size_t size)518*ec779b8eSAndroid Build Coastguard Worker ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
519*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoSerializer(mSerializer);
520*ec779b8eSAndroid Build Coastguard Worker 
521*ec779b8eSAndroid Build Coastguard Worker     ALOGV("readAt offset %lld, size %zu", (long long)offset, size);
522*ec779b8eSAndroid Build Coastguard Worker 
523*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(mLock);
524*ec779b8eSAndroid Build Coastguard Worker     if (mDisconnecting) {
525*ec779b8eSAndroid Build Coastguard Worker         return ERROR_END_OF_STREAM;
526*ec779b8eSAndroid Build Coastguard Worker     }
527*ec779b8eSAndroid Build Coastguard Worker 
528*ec779b8eSAndroid Build Coastguard Worker     // If the request can be completely satisfied from the cache, do so.
529*ec779b8eSAndroid Build Coastguard Worker 
530*ec779b8eSAndroid Build Coastguard Worker     if (offset >= mCacheOffset
531*ec779b8eSAndroid Build Coastguard Worker             && offset + size <= mCacheOffset + mCache->totalSize()) {
532*ec779b8eSAndroid Build Coastguard Worker         size_t delta = offset - mCacheOffset;
533*ec779b8eSAndroid Build Coastguard Worker         mCache->copy(delta, data, size);
534*ec779b8eSAndroid Build Coastguard Worker 
535*ec779b8eSAndroid Build Coastguard Worker         mLastAccessPos = offset + size;
536*ec779b8eSAndroid Build Coastguard Worker 
537*ec779b8eSAndroid Build Coastguard Worker         return size;
538*ec779b8eSAndroid Build Coastguard Worker     }
539*ec779b8eSAndroid Build Coastguard Worker 
540*ec779b8eSAndroid Build Coastguard Worker     sp<AMessage> msg = new AMessage(kWhatRead, mReflector);
541*ec779b8eSAndroid Build Coastguard Worker     msg->setInt64("offset", offset);
542*ec779b8eSAndroid Build Coastguard Worker     msg->setPointer("data", data);
543*ec779b8eSAndroid Build Coastguard Worker     msg->setSize("size", size);
544*ec779b8eSAndroid Build Coastguard Worker 
545*ec779b8eSAndroid Build Coastguard Worker     CHECK(mAsyncResult == NULL);
546*ec779b8eSAndroid Build Coastguard Worker     msg->post();
547*ec779b8eSAndroid Build Coastguard Worker 
548*ec779b8eSAndroid Build Coastguard Worker     while (mAsyncResult == NULL && !mDisconnecting) {
549*ec779b8eSAndroid Build Coastguard Worker         mCondition.wait(mLock);
550*ec779b8eSAndroid Build Coastguard Worker     }
551*ec779b8eSAndroid Build Coastguard Worker 
552*ec779b8eSAndroid Build Coastguard Worker     if (mDisconnecting) {
553*ec779b8eSAndroid Build Coastguard Worker         mAsyncResult.clear();
554*ec779b8eSAndroid Build Coastguard Worker         return ERROR_END_OF_STREAM;
555*ec779b8eSAndroid Build Coastguard Worker     }
556*ec779b8eSAndroid Build Coastguard Worker 
557*ec779b8eSAndroid Build Coastguard Worker     int32_t result;
558*ec779b8eSAndroid Build Coastguard Worker     CHECK(mAsyncResult->findInt32("result", &result));
559*ec779b8eSAndroid Build Coastguard Worker 
560*ec779b8eSAndroid Build Coastguard Worker     mAsyncResult.clear();
561*ec779b8eSAndroid Build Coastguard Worker 
562*ec779b8eSAndroid Build Coastguard Worker     if (result > 0) {
563*ec779b8eSAndroid Build Coastguard Worker         mLastAccessPos = offset + result;
564*ec779b8eSAndroid Build Coastguard Worker     }
565*ec779b8eSAndroid Build Coastguard Worker 
566*ec779b8eSAndroid Build Coastguard Worker     return (ssize_t)result;
567*ec779b8eSAndroid Build Coastguard Worker }
568*ec779b8eSAndroid Build Coastguard Worker 
cachedSize()569*ec779b8eSAndroid Build Coastguard Worker size_t NuCachedSource2::cachedSize() {
570*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(mLock);
571*ec779b8eSAndroid Build Coastguard Worker     return mCacheOffset + mCache->totalSize();
572*ec779b8eSAndroid Build Coastguard Worker }
573*ec779b8eSAndroid Build Coastguard Worker 
getAvailableSize(off64_t offset,off64_t * size)574*ec779b8eSAndroid Build Coastguard Worker status_t NuCachedSource2::getAvailableSize(off64_t offset, off64_t *size) {
575*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(mLock);
576*ec779b8eSAndroid Build Coastguard Worker     status_t finalStatus = UNKNOWN_ERROR;
577*ec779b8eSAndroid Build Coastguard Worker     *size = approxDataRemaining_l(offset, &finalStatus);
578*ec779b8eSAndroid Build Coastguard Worker     return finalStatus;
579*ec779b8eSAndroid Build Coastguard Worker }
580*ec779b8eSAndroid Build Coastguard Worker 
approxDataRemaining(status_t * finalStatus) const581*ec779b8eSAndroid Build Coastguard Worker size_t NuCachedSource2::approxDataRemaining(status_t *finalStatus) const {
582*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(mLock);
583*ec779b8eSAndroid Build Coastguard Worker     return approxDataRemaining_l(mLastAccessPos, finalStatus);
584*ec779b8eSAndroid Build Coastguard Worker }
585*ec779b8eSAndroid Build Coastguard Worker 
approxDataRemaining_l(off64_t offset,status_t * finalStatus) const586*ec779b8eSAndroid Build Coastguard Worker size_t NuCachedSource2::approxDataRemaining_l(off64_t offset, status_t *finalStatus) const {
587*ec779b8eSAndroid Build Coastguard Worker     *finalStatus = mFinalStatus;
588*ec779b8eSAndroid Build Coastguard Worker 
589*ec779b8eSAndroid Build Coastguard Worker     if (mFinalStatus != OK && mNumRetriesLeft > 0) {
590*ec779b8eSAndroid Build Coastguard Worker         // Pretend that everything is fine until we're out of retries.
591*ec779b8eSAndroid Build Coastguard Worker         *finalStatus = OK;
592*ec779b8eSAndroid Build Coastguard Worker     }
593*ec779b8eSAndroid Build Coastguard Worker 
594*ec779b8eSAndroid Build Coastguard Worker     offset = offset >= 0 ? offset : mLastAccessPos;
595*ec779b8eSAndroid Build Coastguard Worker     off64_t lastBytePosCached = mCacheOffset + mCache->totalSize();
596*ec779b8eSAndroid Build Coastguard Worker     if (offset < lastBytePosCached) {
597*ec779b8eSAndroid Build Coastguard Worker         return lastBytePosCached - offset;
598*ec779b8eSAndroid Build Coastguard Worker     }
599*ec779b8eSAndroid Build Coastguard Worker     return 0;
600*ec779b8eSAndroid Build Coastguard Worker }
601*ec779b8eSAndroid Build Coastguard Worker 
readInternal(off64_t offset,void * data,size_t size)602*ec779b8eSAndroid Build Coastguard Worker ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
603*ec779b8eSAndroid Build Coastguard Worker     CHECK_LE(size, (size_t)mHighwaterThresholdBytes);
604*ec779b8eSAndroid Build Coastguard Worker 
605*ec779b8eSAndroid Build Coastguard Worker     ALOGV("readInternal offset %lld size %zu", (long long)offset, size);
606*ec779b8eSAndroid Build Coastguard Worker 
607*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(mLock);
608*ec779b8eSAndroid Build Coastguard Worker 
609*ec779b8eSAndroid Build Coastguard Worker     // If we're disconnecting, return EOS and don't access *data pointer.
610*ec779b8eSAndroid Build Coastguard Worker     // data could be on the stack of the caller to NuCachedSource2::readAt(),
611*ec779b8eSAndroid Build Coastguard Worker     // which may have exited already.
612*ec779b8eSAndroid Build Coastguard Worker     if (mDisconnecting) {
613*ec779b8eSAndroid Build Coastguard Worker         return ERROR_END_OF_STREAM;
614*ec779b8eSAndroid Build Coastguard Worker     }
615*ec779b8eSAndroid Build Coastguard Worker 
616*ec779b8eSAndroid Build Coastguard Worker     if (!mFetching) {
617*ec779b8eSAndroid Build Coastguard Worker         mLastAccessPos = offset;
618*ec779b8eSAndroid Build Coastguard Worker         restartPrefetcherIfNecessary_l(
619*ec779b8eSAndroid Build Coastguard Worker                 false, // ignoreLowWaterThreshold
620*ec779b8eSAndroid Build Coastguard Worker                 true); // force
621*ec779b8eSAndroid Build Coastguard Worker     }
622*ec779b8eSAndroid Build Coastguard Worker 
623*ec779b8eSAndroid Build Coastguard Worker     if (offset < mCacheOffset
624*ec779b8eSAndroid Build Coastguard Worker             || offset >= (off64_t)(mCacheOffset + mCache->totalSize())) {
625*ec779b8eSAndroid Build Coastguard Worker         static const off64_t kPadding = 256 * 1024;
626*ec779b8eSAndroid Build Coastguard Worker 
627*ec779b8eSAndroid Build Coastguard Worker         // In the presence of multiple decoded streams, once of them will
628*ec779b8eSAndroid Build Coastguard Worker         // trigger this seek request, the other one will request data "nearby"
629*ec779b8eSAndroid Build Coastguard Worker         // soon, adjust the seek position so that that subsequent request
630*ec779b8eSAndroid Build Coastguard Worker         // does not trigger another seek.
631*ec779b8eSAndroid Build Coastguard Worker         off64_t seekOffset = (offset > kPadding) ? offset - kPadding : 0;
632*ec779b8eSAndroid Build Coastguard Worker 
633*ec779b8eSAndroid Build Coastguard Worker         seekInternal_l(seekOffset);
634*ec779b8eSAndroid Build Coastguard Worker     }
635*ec779b8eSAndroid Build Coastguard Worker 
636*ec779b8eSAndroid Build Coastguard Worker     size_t delta = offset - mCacheOffset;
637*ec779b8eSAndroid Build Coastguard Worker 
638*ec779b8eSAndroid Build Coastguard Worker     if (mFinalStatus != OK && mNumRetriesLeft == 0) {
639*ec779b8eSAndroid Build Coastguard Worker         if (delta >= mCache->totalSize()) {
640*ec779b8eSAndroid Build Coastguard Worker             return mFinalStatus;
641*ec779b8eSAndroid Build Coastguard Worker         }
642*ec779b8eSAndroid Build Coastguard Worker 
643*ec779b8eSAndroid Build Coastguard Worker         size_t avail = mCache->totalSize() - delta;
644*ec779b8eSAndroid Build Coastguard Worker 
645*ec779b8eSAndroid Build Coastguard Worker         if (avail > size) {
646*ec779b8eSAndroid Build Coastguard Worker             avail = size;
647*ec779b8eSAndroid Build Coastguard Worker         }
648*ec779b8eSAndroid Build Coastguard Worker 
649*ec779b8eSAndroid Build Coastguard Worker         mCache->copy(delta, data, avail);
650*ec779b8eSAndroid Build Coastguard Worker 
651*ec779b8eSAndroid Build Coastguard Worker         return avail;
652*ec779b8eSAndroid Build Coastguard Worker     }
653*ec779b8eSAndroid Build Coastguard Worker 
654*ec779b8eSAndroid Build Coastguard Worker     if (offset + size <= mCacheOffset + mCache->totalSize()) {
655*ec779b8eSAndroid Build Coastguard Worker         mCache->copy(delta, data, size);
656*ec779b8eSAndroid Build Coastguard Worker 
657*ec779b8eSAndroid Build Coastguard Worker         return size;
658*ec779b8eSAndroid Build Coastguard Worker     }
659*ec779b8eSAndroid Build Coastguard Worker 
660*ec779b8eSAndroid Build Coastguard Worker     ALOGV("deferring read");
661*ec779b8eSAndroid Build Coastguard Worker 
662*ec779b8eSAndroid Build Coastguard Worker     return -EAGAIN;
663*ec779b8eSAndroid Build Coastguard Worker }
664*ec779b8eSAndroid Build Coastguard Worker 
seekInternal_l(off64_t offset)665*ec779b8eSAndroid Build Coastguard Worker status_t NuCachedSource2::seekInternal_l(off64_t offset) {
666*ec779b8eSAndroid Build Coastguard Worker     mLastAccessPos = offset;
667*ec779b8eSAndroid Build Coastguard Worker 
668*ec779b8eSAndroid Build Coastguard Worker     if (offset >= mCacheOffset
669*ec779b8eSAndroid Build Coastguard Worker             && offset <= (off64_t)(mCacheOffset + mCache->totalSize())) {
670*ec779b8eSAndroid Build Coastguard Worker         return OK;
671*ec779b8eSAndroid Build Coastguard Worker     }
672*ec779b8eSAndroid Build Coastguard Worker 
673*ec779b8eSAndroid Build Coastguard Worker     ALOGI("new range: offset= %lld", (long long)offset);
674*ec779b8eSAndroid Build Coastguard Worker 
675*ec779b8eSAndroid Build Coastguard Worker     mCacheOffset = offset;
676*ec779b8eSAndroid Build Coastguard Worker 
677*ec779b8eSAndroid Build Coastguard Worker     size_t totalSize = mCache->totalSize();
678*ec779b8eSAndroid Build Coastguard Worker     CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
679*ec779b8eSAndroid Build Coastguard Worker 
680*ec779b8eSAndroid Build Coastguard Worker     mNumRetriesLeft = kMaxNumRetries;
681*ec779b8eSAndroid Build Coastguard Worker     mFetching = true;
682*ec779b8eSAndroid Build Coastguard Worker 
683*ec779b8eSAndroid Build Coastguard Worker     return OK;
684*ec779b8eSAndroid Build Coastguard Worker }
685*ec779b8eSAndroid Build Coastguard Worker 
resumeFetchingIfNecessary()686*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::resumeFetchingIfNecessary() {
687*ec779b8eSAndroid Build Coastguard Worker     Mutex::Autolock autoLock(mLock);
688*ec779b8eSAndroid Build Coastguard Worker 
689*ec779b8eSAndroid Build Coastguard Worker     restartPrefetcherIfNecessary_l(true /* ignore low water threshold */);
690*ec779b8eSAndroid Build Coastguard Worker }
691*ec779b8eSAndroid Build Coastguard Worker 
getUri()692*ec779b8eSAndroid Build Coastguard Worker String8 NuCachedSource2::getUri() {
693*ec779b8eSAndroid Build Coastguard Worker     return mSource->getUri();
694*ec779b8eSAndroid Build Coastguard Worker }
695*ec779b8eSAndroid Build Coastguard Worker 
getMIMEType() const696*ec779b8eSAndroid Build Coastguard Worker String8 NuCachedSource2::getMIMEType() const {
697*ec779b8eSAndroid Build Coastguard Worker     return mSource->getMIMEType();
698*ec779b8eSAndroid Build Coastguard Worker }
699*ec779b8eSAndroid Build Coastguard Worker 
updateCacheParamsFromSystemProperty()700*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::updateCacheParamsFromSystemProperty() {
701*ec779b8eSAndroid Build Coastguard Worker     char value[PROPERTY_VALUE_MAX];
702*ec779b8eSAndroid Build Coastguard Worker     if (!property_get("media.stagefright.cache-params", value, NULL)) {
703*ec779b8eSAndroid Build Coastguard Worker         return;
704*ec779b8eSAndroid Build Coastguard Worker     }
705*ec779b8eSAndroid Build Coastguard Worker 
706*ec779b8eSAndroid Build Coastguard Worker     updateCacheParamsFromString(value);
707*ec779b8eSAndroid Build Coastguard Worker }
708*ec779b8eSAndroid Build Coastguard Worker 
updateCacheParamsFromString(const char * s)709*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::updateCacheParamsFromString(const char *s) {
710*ec779b8eSAndroid Build Coastguard Worker     ssize_t lowwaterMarkKb, highwaterMarkKb;
711*ec779b8eSAndroid Build Coastguard Worker     int keepAliveSecs;
712*ec779b8eSAndroid Build Coastguard Worker 
713*ec779b8eSAndroid Build Coastguard Worker     if (sscanf(s, "%zd/%zd/%d",
714*ec779b8eSAndroid Build Coastguard Worker                &lowwaterMarkKb, &highwaterMarkKb, &keepAliveSecs) != 3) {
715*ec779b8eSAndroid Build Coastguard Worker         ALOGE("Failed to parse cache parameters from '%s'.", s);
716*ec779b8eSAndroid Build Coastguard Worker         return;
717*ec779b8eSAndroid Build Coastguard Worker     }
718*ec779b8eSAndroid Build Coastguard Worker 
719*ec779b8eSAndroid Build Coastguard Worker     if (lowwaterMarkKb >= 0) {
720*ec779b8eSAndroid Build Coastguard Worker         mLowwaterThresholdBytes = lowwaterMarkKb * 1024;
721*ec779b8eSAndroid Build Coastguard Worker     } else {
722*ec779b8eSAndroid Build Coastguard Worker         mLowwaterThresholdBytes = kDefaultLowWaterThreshold;
723*ec779b8eSAndroid Build Coastguard Worker     }
724*ec779b8eSAndroid Build Coastguard Worker 
725*ec779b8eSAndroid Build Coastguard Worker     if (highwaterMarkKb >= 0) {
726*ec779b8eSAndroid Build Coastguard Worker         mHighwaterThresholdBytes = highwaterMarkKb * 1024;
727*ec779b8eSAndroid Build Coastguard Worker     } else {
728*ec779b8eSAndroid Build Coastguard Worker         mHighwaterThresholdBytes = kDefaultHighWaterThreshold;
729*ec779b8eSAndroid Build Coastguard Worker     }
730*ec779b8eSAndroid Build Coastguard Worker 
731*ec779b8eSAndroid Build Coastguard Worker     if (mLowwaterThresholdBytes >= mHighwaterThresholdBytes) {
732*ec779b8eSAndroid Build Coastguard Worker         ALOGE("Illegal low/highwater marks specified, reverting to defaults.");
733*ec779b8eSAndroid Build Coastguard Worker 
734*ec779b8eSAndroid Build Coastguard Worker         mLowwaterThresholdBytes = kDefaultLowWaterThreshold;
735*ec779b8eSAndroid Build Coastguard Worker         mHighwaterThresholdBytes = kDefaultHighWaterThreshold;
736*ec779b8eSAndroid Build Coastguard Worker     }
737*ec779b8eSAndroid Build Coastguard Worker 
738*ec779b8eSAndroid Build Coastguard Worker     if (keepAliveSecs >= 0) {
739*ec779b8eSAndroid Build Coastguard Worker         mKeepAliveIntervalUs = keepAliveSecs * 1000000LL;
740*ec779b8eSAndroid Build Coastguard Worker     } else {
741*ec779b8eSAndroid Build Coastguard Worker         mKeepAliveIntervalUs = kDefaultKeepAliveIntervalUs;
742*ec779b8eSAndroid Build Coastguard Worker     }
743*ec779b8eSAndroid Build Coastguard Worker 
744*ec779b8eSAndroid Build Coastguard Worker     ALOGV("lowwater = %zu bytes, highwater = %zu bytes, keepalive = %lld us",
745*ec779b8eSAndroid Build Coastguard Worker          mLowwaterThresholdBytes,
746*ec779b8eSAndroid Build Coastguard Worker          mHighwaterThresholdBytes,
747*ec779b8eSAndroid Build Coastguard Worker          (long long)mKeepAliveIntervalUs);
748*ec779b8eSAndroid Build Coastguard Worker }
749*ec779b8eSAndroid Build Coastguard Worker 
750*ec779b8eSAndroid Build Coastguard Worker // static
RemoveCacheSpecificHeaders(KeyedVector<String8,String8> * headers,String8 * cacheConfig,bool * disconnectAtHighwatermark)751*ec779b8eSAndroid Build Coastguard Worker void NuCachedSource2::RemoveCacheSpecificHeaders(
752*ec779b8eSAndroid Build Coastguard Worker         KeyedVector<String8, String8> *headers,
753*ec779b8eSAndroid Build Coastguard Worker         String8 *cacheConfig,
754*ec779b8eSAndroid Build Coastguard Worker         bool *disconnectAtHighwatermark) {
755*ec779b8eSAndroid Build Coastguard Worker     *cacheConfig = String8();
756*ec779b8eSAndroid Build Coastguard Worker     *disconnectAtHighwatermark = false;
757*ec779b8eSAndroid Build Coastguard Worker 
758*ec779b8eSAndroid Build Coastguard Worker     if (headers == NULL) {
759*ec779b8eSAndroid Build Coastguard Worker         return;
760*ec779b8eSAndroid Build Coastguard Worker     }
761*ec779b8eSAndroid Build Coastguard Worker 
762*ec779b8eSAndroid Build Coastguard Worker     ssize_t index;
763*ec779b8eSAndroid Build Coastguard Worker     if ((index = headers->indexOfKey(String8("x-cache-config"))) >= 0) {
764*ec779b8eSAndroid Build Coastguard Worker         *cacheConfig = headers->valueAt(index);
765*ec779b8eSAndroid Build Coastguard Worker 
766*ec779b8eSAndroid Build Coastguard Worker         headers->removeItemsAt(index);
767*ec779b8eSAndroid Build Coastguard Worker 
768*ec779b8eSAndroid Build Coastguard Worker         ALOGV("Using special cache config '%s'", cacheConfig->c_str());
769*ec779b8eSAndroid Build Coastguard Worker     }
770*ec779b8eSAndroid Build Coastguard Worker 
771*ec779b8eSAndroid Build Coastguard Worker     if ((index = headers->indexOfKey(
772*ec779b8eSAndroid Build Coastguard Worker                     String8("x-disconnect-at-highwatermark"))) >= 0) {
773*ec779b8eSAndroid Build Coastguard Worker         *disconnectAtHighwatermark = true;
774*ec779b8eSAndroid Build Coastguard Worker         headers->removeItemsAt(index);
775*ec779b8eSAndroid Build Coastguard Worker 
776*ec779b8eSAndroid Build Coastguard Worker         ALOGV("Client requested disconnection at highwater mark");
777*ec779b8eSAndroid Build Coastguard Worker     }
778*ec779b8eSAndroid Build Coastguard Worker }
779*ec779b8eSAndroid Build Coastguard Worker 
780*ec779b8eSAndroid Build Coastguard Worker }  // namespace android
781