blob: 05e599b321f83ead721555b2e6d94054d34865d0 [file] [log] [blame]
Andreas Huber5994b472010-06-10 11:09:36 -07001/*
2 * Copyright (C) 2010 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
Andreas Huberac05c312011-01-19 15:07:19 -080017//#define LOG_NDEBUG 0
Andreas Huber5994b472010-06-10 11:09:36 -070018#define LOG_TAG "NuCachedSource2"
19#include <utils/Log.h>
20
21#include "include/NuCachedSource2.h"
James Dong5b1b8a92011-05-25 19:37:03 -070022#include "include/HTTPBase.h"
Andreas Huber5994b472010-06-10 11:09:36 -070023
Andreas Hubera045cb02011-10-05 14:32:17 -070024#include <cutils/properties.h>
Andreas Huber5994b472010-06-10 11:09:36 -070025#include <media/stagefright/foundation/ADebug.h>
26#include <media/stagefright/foundation/AMessage.h>
27#include <media/stagefright/MediaErrors.h>
28
29namespace android {
30
31struct PageCache {
32 PageCache(size_t pageSize);
33 ~PageCache();
34
35 struct Page {
36 void *mData;
37 size_t mSize;
38 };
39
40 Page *acquirePage();
41 void releasePage(Page *page);
42
43 void appendPage(Page *page);
44 size_t releaseFromStart(size_t maxBytes);
45
46 size_t totalSize() const {
47 return mTotalSize;
48 }
49
50 void copy(size_t from, void *data, size_t size);
51
52private:
53 size_t mPageSize;
54 size_t mTotalSize;
55
56 List<Page *> mActivePages;
57 List<Page *> mFreePages;
58
59 void freePages(List<Page *> *list);
60
61 DISALLOW_EVIL_CONSTRUCTORS(PageCache);
62};
63
64PageCache::PageCache(size_t pageSize)
65 : mPageSize(pageSize),
66 mTotalSize(0) {
67}
68
69PageCache::~PageCache() {
70 freePages(&mActivePages);
71 freePages(&mFreePages);
72}
73
74void PageCache::freePages(List<Page *> *list) {
75 List<Page *>::iterator it = list->begin();
76 while (it != list->end()) {
77 Page *page = *it;
78
79 free(page->mData);
80 delete page;
81 page = NULL;
82
83 ++it;
84 }
85}
86
87PageCache::Page *PageCache::acquirePage() {
88 if (!mFreePages.empty()) {
89 List<Page *>::iterator it = mFreePages.begin();
90 Page *page = *it;
91 mFreePages.erase(it);
92
93 return page;
94 }
95
96 Page *page = new Page;
97 page->mData = malloc(mPageSize);
98 page->mSize = 0;
99
100 return page;
101}
102
103void PageCache::releasePage(Page *page) {
104 page->mSize = 0;
105 mFreePages.push_back(page);
106}
107
108void PageCache::appendPage(Page *page) {
109 mTotalSize += page->mSize;
110 mActivePages.push_back(page);
111}
112
113size_t PageCache::releaseFromStart(size_t maxBytes) {
114 size_t bytesReleased = 0;
115
116 while (maxBytes > 0 && !mActivePages.empty()) {
117 List<Page *>::iterator it = mActivePages.begin();
118
119 Page *page = *it;
120
121 if (maxBytes < page->mSize) {
122 break;
123 }
124
125 mActivePages.erase(it);
126
127 maxBytes -= page->mSize;
128 bytesReleased += page->mSize;
129
130 releasePage(page);
131 }
132
133 mTotalSize -= bytesReleased;
134 return bytesReleased;
135}
136
137void PageCache::copy(size_t from, void *data, size_t size) {
Steve Block3856b092011-10-20 11:56:00 +0100138 ALOGV("copy from %d size %d", from, size);
Andreas Huber5994b472010-06-10 11:09:36 -0700139
Andreas Huber31096292011-03-21 14:16:03 -0700140 if (size == 0) {
141 return;
142 }
143
Andreas Huber5994b472010-06-10 11:09:36 -0700144 CHECK_LE(from + size, mTotalSize);
145
146 size_t offset = 0;
147 List<Page *>::iterator it = mActivePages.begin();
148 while (from >= offset + (*it)->mSize) {
149 offset += (*it)->mSize;
150 ++it;
151 }
152
153 size_t delta = from - offset;
154 size_t avail = (*it)->mSize - delta;
155
156 if (avail >= size) {
157 memcpy(data, (const uint8_t *)(*it)->mData + delta, size);
158 return;
159 }
160
161 memcpy(data, (const uint8_t *)(*it)->mData + delta, avail);
162 ++it;
163 data = (uint8_t *)data + avail;
164 size -= avail;
165
166 while (size > 0) {
167 size_t copy = (*it)->mSize;
168 if (copy > size) {
169 copy = size;
170 }
171 memcpy(data, (*it)->mData, copy);
172 data = (uint8_t *)data + copy;
173 size -= copy;
174 ++it;
175 }
176}
177
178////////////////////////////////////////////////////////////////////////////////
179
Andreas Huber49c59812011-10-07 13:40:45 -0700180NuCachedSource2::NuCachedSource2(
181 const sp<DataSource> &source,
182 const char *cacheConfig,
183 bool disconnectAtHighwatermark)
Andreas Huber5994b472010-06-10 11:09:36 -0700184 : mSource(source),
185 mReflector(new AHandlerReflector<NuCachedSource2>(this)),
186 mLooper(new ALooper),
187 mCache(new PageCache(kPageSize)),
188 mCacheOffset(0),
189 mFinalStatus(OK),
190 mLastAccessPos(0),
Andreas Hubera5273eb2010-06-22 08:57:34 -0700191 mFetching(true),
Andreas Huber0683eba2011-07-18 13:47:55 -0700192 mLastFetchTimeUs(-1),
Andreas Hubera045cb02011-10-05 14:32:17 -0700193 mNumRetriesLeft(kMaxNumRetries),
194 mHighwaterThresholdBytes(kDefaultHighWaterThreshold),
195 mLowwaterThresholdBytes(kDefaultLowWaterThreshold),
Andreas Huber49c59812011-10-07 13:40:45 -0700196 mKeepAliveIntervalUs(kDefaultKeepAliveIntervalUs),
197 mDisconnectAtHighwatermark(disconnectAtHighwatermark) {
198 // We are NOT going to support disconnect-at-highwatermark indefinitely
199 // and we are not guaranteeing support for client-specified cache
200 // parameters. Both of these are temporary measures to solve a specific
201 // problem that will be solved in a better way going forward.
202
Andreas Hubera045cb02011-10-05 14:32:17 -0700203 updateCacheParamsFromSystemProperty();
204
Andreas Huber49c59812011-10-07 13:40:45 -0700205 if (cacheConfig != NULL) {
206 updateCacheParamsFromString(cacheConfig);
207 }
208
209 if (mDisconnectAtHighwatermark) {
210 // Makes no sense to disconnect and do keep-alives...
211 mKeepAliveIntervalUs = 0;
212 }
213
Andreas Hubera814c1f2010-08-27 15:21:07 -0700214 mLooper->setName("NuCachedSource2");
Andreas Huber5994b472010-06-10 11:09:36 -0700215 mLooper->registerHandler(mReflector);
216 mLooper->start();
217
218 Mutex::Autolock autoLock(mLock);
219 (new AMessage(kWhatFetchMore, mReflector->id()))->post();
220}
221
222NuCachedSource2::~NuCachedSource2() {
223 mLooper->stop();
224 mLooper->unregisterHandler(mReflector->id());
225
226 delete mCache;
227 mCache = NULL;
228}
229
James Dong5b1b8a92011-05-25 19:37:03 -0700230status_t NuCachedSource2::getEstimatedBandwidthKbps(int32_t *kbps) {
James Dongb33d2ac2011-06-01 15:27:20 -0700231 if (mSource->flags() & kIsHTTPBasedSource) {
232 HTTPBase* source = static_cast<HTTPBase *>(mSource.get());
233 return source->getEstimatedBandwidthKbps(kbps);
234 }
235 return ERROR_UNSUPPORTED;
James Dong5b1b8a92011-05-25 19:37:03 -0700236}
237
238status_t NuCachedSource2::setCacheStatCollectFreq(int32_t freqMs) {
James Dongb33d2ac2011-06-01 15:27:20 -0700239 if (mSource->flags() & kIsHTTPBasedSource) {
240 HTTPBase *source = static_cast<HTTPBase *>(mSource.get());
241 return source->setBandwidthStatCollectFreq(freqMs);
242 }
243 return ERROR_UNSUPPORTED;
James Dong5b1b8a92011-05-25 19:37:03 -0700244}
245
Andreas Huber5994b472010-06-10 11:09:36 -0700246status_t NuCachedSource2::initCheck() const {
247 return mSource->initCheck();
248}
249
James Dongc7fc37a2010-11-16 14:04:54 -0800250status_t NuCachedSource2::getSize(off64_t *size) {
Andreas Huber5994b472010-06-10 11:09:36 -0700251 return mSource->getSize(size);
252}
253
254uint32_t NuCachedSource2::flags() {
James Dongb33d2ac2011-06-01 15:27:20 -0700255 // Remove HTTP related flags since NuCachedSource2 is not HTTP-based.
256 uint32_t flags = mSource->flags() & ~(kWantsPrefetching | kIsHTTPBasedSource);
257 return (flags | kIsCachingDataSource);
Andreas Huber5994b472010-06-10 11:09:36 -0700258}
259
260void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) {
261 switch (msg->what()) {
262 case kWhatFetchMore:
263 {
264 onFetch();
265 break;
266 }
267
268 case kWhatRead:
269 {
270 onRead(msg);
271 break;
272 }
273
274 default:
275 TRESPASS();
276 }
277}
278
279void NuCachedSource2::fetchInternal() {
Steve Block3856b092011-10-20 11:56:00 +0100280 ALOGV("fetchInternal");
Andreas Huber5994b472010-06-10 11:09:36 -0700281
Andreas Huber95c4d602011-10-17 15:49:01 -0700282 bool reconnect = false;
283
Andreas Huber0683eba2011-07-18 13:47:55 -0700284 {
285 Mutex::Autolock autoLock(mLock);
286 CHECK(mFinalStatus == OK || mNumRetriesLeft > 0);
287
288 if (mFinalStatus != OK) {
289 --mNumRetriesLeft;
290
Andreas Huber95c4d602011-10-17 15:49:01 -0700291 reconnect = true;
292 }
293 }
Andreas Huber0683eba2011-07-18 13:47:55 -0700294
Andreas Huber95c4d602011-10-17 15:49:01 -0700295 if (reconnect) {
296 status_t err =
297 mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize());
Andreas Huber0683eba2011-07-18 13:47:55 -0700298
Andreas Huber95c4d602011-10-17 15:49:01 -0700299 Mutex::Autolock autoLock(mLock);
300
Andreas Hubera7607a72012-08-28 09:48:40 -0700301 if (err == ERROR_UNSUPPORTED || err == -EPIPE) {
302 // These are errors that are not likely to go away even if we
303 // retry, i.e. the server doesn't support range requests or similar.
Andreas Huber95c4d602011-10-17 15:49:01 -0700304 mNumRetriesLeft = 0;
305 return;
306 } else if (err != OK) {
Steve Blockdf64d152012-01-04 20:05:49 +0000307 ALOGI("The attempt to reconnect failed, %d retries remaining",
Andreas Huber95c4d602011-10-17 15:49:01 -0700308 mNumRetriesLeft);
309
310 return;
Andreas Huber0683eba2011-07-18 13:47:55 -0700311 }
312 }
Andreas Huber5994b472010-06-10 11:09:36 -0700313
314 PageCache::Page *page = mCache->acquirePage();
315
316 ssize_t n = mSource->readAt(
317 mCacheOffset + mCache->totalSize(), page->mData, kPageSize);
318
319 Mutex::Autolock autoLock(mLock);
320
321 if (n < 0) {
Andreas Huber5994b472010-06-10 11:09:36 -0700322 mFinalStatus = n;
Andreas Hubera7607a72012-08-28 09:48:40 -0700323 if (n == ERROR_UNSUPPORTED || n == -EPIPE) {
324 // These are errors that are not likely to go away even if we
325 // retry, i.e. the server doesn't support range requests or similar.
326 mNumRetriesLeft = 0;
327 }
328
329 ALOGE("source returned error %ld, %d retries left", n, mNumRetriesLeft);
Andreas Huber5994b472010-06-10 11:09:36 -0700330 mCache->releasePage(page);
331 } else if (n == 0) {
Steve Blockdf64d152012-01-04 20:05:49 +0000332 ALOGI("ERROR_END_OF_STREAM");
Andreas Huber0683eba2011-07-18 13:47:55 -0700333
334 mNumRetriesLeft = 0;
Andreas Huber5994b472010-06-10 11:09:36 -0700335 mFinalStatus = ERROR_END_OF_STREAM;
Andreas Huber0683eba2011-07-18 13:47:55 -0700336
Andreas Huber5994b472010-06-10 11:09:36 -0700337 mCache->releasePage(page);
338 } else {
Andreas Huber0683eba2011-07-18 13:47:55 -0700339 if (mFinalStatus != OK) {
Steve Blockdf64d152012-01-04 20:05:49 +0000340 ALOGI("retrying a previously failed read succeeded.");
Andreas Huber0683eba2011-07-18 13:47:55 -0700341 }
342 mNumRetriesLeft = kMaxNumRetries;
343 mFinalStatus = OK;
344
Andreas Huber5994b472010-06-10 11:09:36 -0700345 page->mSize = n;
346 mCache->appendPage(page);
347 }
348}
349
350void NuCachedSource2::onFetch() {
Steve Block3856b092011-10-20 11:56:00 +0100351 ALOGV("onFetch");
Andreas Huber5994b472010-06-10 11:09:36 -0700352
Andreas Huber0683eba2011-07-18 13:47:55 -0700353 if (mFinalStatus != OK && mNumRetriesLeft == 0) {
Steve Block3856b092011-10-20 11:56:00 +0100354 ALOGV("EOS reached, done prefetching for now");
Andreas Huber5994b472010-06-10 11:09:36 -0700355 mFetching = false;
356 }
357
Andreas Hubera5273eb2010-06-22 08:57:34 -0700358 bool keepAlive =
359 !mFetching
360 && mFinalStatus == OK
Andreas Hubera045cb02011-10-05 14:32:17 -0700361 && mKeepAliveIntervalUs > 0
362 && ALooper::GetNowUs() >= mLastFetchTimeUs + mKeepAliveIntervalUs;
Andreas Hubera5273eb2010-06-22 08:57:34 -0700363
364 if (mFetching || keepAlive) {
365 if (keepAlive) {
Steve Blockdf64d152012-01-04 20:05:49 +0000366 ALOGI("Keep alive");
Andreas Hubera5273eb2010-06-22 08:57:34 -0700367 }
368
Andreas Huber5994b472010-06-10 11:09:36 -0700369 fetchInternal();
370
Andreas Hubera5273eb2010-06-22 08:57:34 -0700371 mLastFetchTimeUs = ALooper::GetNowUs();
372
Andreas Hubera045cb02011-10-05 14:32:17 -0700373 if (mFetching && mCache->totalSize() >= mHighwaterThresholdBytes) {
Steve Blockdf64d152012-01-04 20:05:49 +0000374 ALOGI("Cache full, done prefetching for now");
Andreas Huber5994b472010-06-10 11:09:36 -0700375 mFetching = false;
Andreas Huber49c59812011-10-07 13:40:45 -0700376
377 if (mDisconnectAtHighwatermark
378 && (mSource->flags() & DataSource::kIsHTTPBasedSource)) {
Steve Block3856b092011-10-20 11:56:00 +0100379 ALOGV("Disconnecting at high watermark");
Andreas Huber49c59812011-10-07 13:40:45 -0700380 static_cast<HTTPBase *>(mSource.get())->disconnect();
Bryan Mawhinney40a4e142012-01-18 13:40:07 +0000381 mFinalStatus = -EAGAIN;
Andreas Huber49c59812011-10-07 13:40:45 -0700382 }
Andreas Huber5994b472010-06-10 11:09:36 -0700383 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800384 } else {
Andreas Huberd17875a2010-06-11 14:14:52 -0700385 Mutex::Autolock autoLock(mLock);
Andreas Huber5994b472010-06-10 11:09:36 -0700386 restartPrefetcherIfNecessary_l();
387 }
388
Andreas Huber0683eba2011-07-18 13:47:55 -0700389 int64_t delayUs;
390 if (mFetching) {
391 if (mFinalStatus != OK && mNumRetriesLeft > 0) {
392 // We failed this time and will try again in 3 seconds.
393 delayUs = 3000000ll;
394 } else {
395 delayUs = 0;
396 }
397 } else {
398 delayUs = 100000ll;
399 }
400
401 (new AMessage(kWhatFetchMore, mReflector->id()))->post(delayUs);
Andreas Huber5994b472010-06-10 11:09:36 -0700402}
403
404void NuCachedSource2::onRead(const sp<AMessage> &msg) {
Steve Block3856b092011-10-20 11:56:00 +0100405 ALOGV("onRead");
Andreas Huber5994b472010-06-10 11:09:36 -0700406
407 int64_t offset;
408 CHECK(msg->findInt64("offset", &offset));
409
410 void *data;
411 CHECK(msg->findPointer("data", &data));
412
413 size_t size;
414 CHECK(msg->findSize("size", &size));
415
416 ssize_t result = readInternal(offset, data, size);
417
418 if (result == -EAGAIN) {
419 msg->post(50000);
420 return;
421 }
422
423 Mutex::Autolock autoLock(mLock);
424
425 CHECK(mAsyncResult == NULL);
426
427 mAsyncResult = new AMessage;
428 mAsyncResult->setInt32("result", result);
429
430 mCondition.signal();
431}
432
Andreas Huber34ef0f32010-11-11 15:37:17 -0800433void NuCachedSource2::restartPrefetcherIfNecessary_l(
Andreas Huber7bf84132011-04-19 10:04:08 -0700434 bool ignoreLowWaterThreshold, bool force) {
James Dong6ee94582011-01-14 15:15:12 -0800435 static const size_t kGrayArea = 1024 * 1024;
Andreas Huber5994b472010-06-10 11:09:36 -0700436
Andreas Huber0683eba2011-07-18 13:47:55 -0700437 if (mFetching || (mFinalStatus != OK && mNumRetriesLeft == 0)) {
Andreas Huber5994b472010-06-10 11:09:36 -0700438 return;
439 }
440
Andreas Huber7bf84132011-04-19 10:04:08 -0700441 if (!ignoreLowWaterThreshold && !force
Andreas Huber34ef0f32010-11-11 15:37:17 -0800442 && mCacheOffset + mCache->totalSize() - mLastAccessPos
Andreas Hubera045cb02011-10-05 14:32:17 -0700443 >= mLowwaterThresholdBytes) {
Andreas Huber5994b472010-06-10 11:09:36 -0700444 return;
445 }
446
447 size_t maxBytes = mLastAccessPos - mCacheOffset;
Andreas Huber5994b472010-06-10 11:09:36 -0700448
Andreas Huber7bf84132011-04-19 10:04:08 -0700449 if (!force) {
450 if (maxBytes < kGrayArea) {
451 return;
452 }
453
454 maxBytes -= kGrayArea;
455 }
Andreas Huber5994b472010-06-10 11:09:36 -0700456
457 size_t actualBytes = mCache->releaseFromStart(maxBytes);
458 mCacheOffset += actualBytes;
459
Steve Blockdf64d152012-01-04 20:05:49 +0000460 ALOGI("restarting prefetcher, totalSize = %d", mCache->totalSize());
Andreas Huber5994b472010-06-10 11:09:36 -0700461 mFetching = true;
462}
463
James Dongc7fc37a2010-11-16 14:04:54 -0800464ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
Andreas Huber5994b472010-06-10 11:09:36 -0700465 Mutex::Autolock autoSerializer(mSerializer);
466
Steve Block3856b092011-10-20 11:56:00 +0100467 ALOGV("readAt offset %lld, size %d", offset, size);
Andreas Huber5994b472010-06-10 11:09:36 -0700468
469 Mutex::Autolock autoLock(mLock);
470
471 // If the request can be completely satisfied from the cache, do so.
472
473 if (offset >= mCacheOffset
474 && offset + size <= mCacheOffset + mCache->totalSize()) {
475 size_t delta = offset - mCacheOffset;
476 mCache->copy(delta, data, size);
477
478 mLastAccessPos = offset + size;
479
480 return size;
481 }
482
483 sp<AMessage> msg = new AMessage(kWhatRead, mReflector->id());
484 msg->setInt64("offset", offset);
485 msg->setPointer("data", data);
486 msg->setSize("size", size);
487
488 CHECK(mAsyncResult == NULL);
489 msg->post();
490
491 while (mAsyncResult == NULL) {
492 mCondition.wait(mLock);
493 }
494
495 int32_t result;
496 CHECK(mAsyncResult->findInt32("result", &result));
497
498 mAsyncResult.clear();
499
500 if (result > 0) {
501 mLastAccessPos = offset + result;
502 }
503
504 return (ssize_t)result;
505}
506
507size_t NuCachedSource2::cachedSize() {
508 Mutex::Autolock autoLock(mLock);
509 return mCacheOffset + mCache->totalSize();
510}
511
Andreas Hubera53d87c2012-04-19 16:25:20 -0700512size_t NuCachedSource2::approxDataRemaining(status_t *finalStatus) const {
Andreas Huber5994b472010-06-10 11:09:36 -0700513 Mutex::Autolock autoLock(mLock);
Bryan Mawhinney1bd233c2011-01-18 19:12:21 +0000514 return approxDataRemaining_l(finalStatus);
Andreas Huber5994b472010-06-10 11:09:36 -0700515}
516
Andreas Hubera53d87c2012-04-19 16:25:20 -0700517size_t NuCachedSource2::approxDataRemaining_l(status_t *finalStatus) const {
Bryan Mawhinney1bd233c2011-01-18 19:12:21 +0000518 *finalStatus = mFinalStatus;
Andreas Huber0683eba2011-07-18 13:47:55 -0700519
520 if (mFinalStatus != OK && mNumRetriesLeft > 0) {
521 // Pretend that everything is fine until we're out of retries.
522 *finalStatus = OK;
523 }
524
James Dongc7fc37a2010-11-16 14:04:54 -0800525 off64_t lastBytePosCached = mCacheOffset + mCache->totalSize();
Andreas Huber5994b472010-06-10 11:09:36 -0700526 if (mLastAccessPos < lastBytePosCached) {
527 return lastBytePosCached - mLastAccessPos;
528 }
529 return 0;
530}
531
James Dongc7fc37a2010-11-16 14:04:54 -0800532ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
Andreas Hubera045cb02011-10-05 14:32:17 -0700533 CHECK_LE(size, (size_t)mHighwaterThresholdBytes);
Andreas Huber7bf84132011-04-19 10:04:08 -0700534
Steve Block3856b092011-10-20 11:56:00 +0100535 ALOGV("readInternal offset %lld size %d", offset, size);
Andreas Huber5994b472010-06-10 11:09:36 -0700536
537 Mutex::Autolock autoLock(mLock);
538
Andreas Huber7bf84132011-04-19 10:04:08 -0700539 if (!mFetching) {
540 mLastAccessPos = offset;
541 restartPrefetcherIfNecessary_l(
542 false, // ignoreLowWaterThreshold
543 true); // force
544 }
545
Andreas Huber5994b472010-06-10 11:09:36 -0700546 if (offset < mCacheOffset
James Dongc7fc37a2010-11-16 14:04:54 -0800547 || offset >= (off64_t)(mCacheOffset + mCache->totalSize())) {
James Dong6ee94582011-01-14 15:15:12 -0800548 static const off64_t kPadding = 256 * 1024;
Andreas Huber5994b472010-06-10 11:09:36 -0700549
550 // In the presence of multiple decoded streams, once of them will
551 // trigger this seek request, the other one will request data "nearby"
552 // soon, adjust the seek position so that that subsequent request
553 // does not trigger another seek.
James Dongc7fc37a2010-11-16 14:04:54 -0800554 off64_t seekOffset = (offset > kPadding) ? offset - kPadding : 0;
Andreas Huber5994b472010-06-10 11:09:36 -0700555
556 seekInternal_l(seekOffset);
557 }
558
559 size_t delta = offset - mCacheOffset;
560
Bryan Mawhinney40a4e142012-01-18 13:40:07 +0000561 if (mFinalStatus != OK && mNumRetriesLeft == 0) {
Andreas Huber5994b472010-06-10 11:09:36 -0700562 if (delta >= mCache->totalSize()) {
563 return mFinalStatus;
564 }
565
Andreas Huber6f5aae12010-06-11 09:22:48 -0700566 size_t avail = mCache->totalSize() - delta;
Andreas Huber67802972011-05-04 11:43:43 -0700567
568 if (avail > size) {
569 avail = size;
570 }
571
Andreas Huber5994b472010-06-10 11:09:36 -0700572 mCache->copy(delta, data, avail);
573
574 return avail;
575 }
576
577 if (offset + size <= mCacheOffset + mCache->totalSize()) {
578 mCache->copy(delta, data, size);
579
580 return size;
581 }
582
Steve Block3856b092011-10-20 11:56:00 +0100583 ALOGV("deferring read");
Andreas Huber5994b472010-06-10 11:09:36 -0700584
585 return -EAGAIN;
586}
587
James Dongc7fc37a2010-11-16 14:04:54 -0800588status_t NuCachedSource2::seekInternal_l(off64_t offset) {
Andreas Huber5994b472010-06-10 11:09:36 -0700589 mLastAccessPos = offset;
590
591 if (offset >= mCacheOffset
James Dongc7fc37a2010-11-16 14:04:54 -0800592 && offset <= (off64_t)(mCacheOffset + mCache->totalSize())) {
Andreas Huber5994b472010-06-10 11:09:36 -0700593 return OK;
594 }
595
Steve Blockdf64d152012-01-04 20:05:49 +0000596 ALOGI("new range: offset= %lld", offset);
Andreas Huber5994b472010-06-10 11:09:36 -0700597
598 mCacheOffset = offset;
599
600 size_t totalSize = mCache->totalSize();
601 CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
602
Bryan Mawhinney40a4e142012-01-18 13:40:07 +0000603 mNumRetriesLeft = kMaxNumRetries;
Andreas Huber5994b472010-06-10 11:09:36 -0700604 mFetching = true;
605
606 return OK;
607}
608
Andreas Huber34ef0f32010-11-11 15:37:17 -0800609void NuCachedSource2::resumeFetchingIfNecessary() {
610 Mutex::Autolock autoLock(mLock);
611
612 restartPrefetcherIfNecessary_l(true /* ignore low water threshold */);
613}
614
James Dong9d2f3862012-01-10 08:24:37 -0800615sp<DecryptHandle> NuCachedSource2::DrmInitialization(const char* mime) {
616 return mSource->DrmInitialization(mime);
Gloria Wangb3714262010-11-01 15:53:16 -0700617}
618
Gloria Wangb5ce3612011-02-24 16:40:57 -0800619void NuCachedSource2::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {
Gloria Wangb3714262010-11-01 15:53:16 -0700620 mSource->getDrmInfo(handle, client);
621}
622
Gloria Wang771b85d2010-11-09 15:06:51 -0800623String8 NuCachedSource2::getUri() {
624 return mSource->getUri();
625}
Andreas Huberac05c312011-01-19 15:07:19 -0800626
Andreas Huber6511c972011-03-30 11:15:27 -0700627String8 NuCachedSource2::getMIMEType() const {
628 return mSource->getMIMEType();
629}
630
Andreas Hubera045cb02011-10-05 14:32:17 -0700631void NuCachedSource2::updateCacheParamsFromSystemProperty() {
632 char value[PROPERTY_VALUE_MAX];
633 if (!property_get("media.stagefright.cache-params", value, NULL)) {
634 return;
635 }
636
637 updateCacheParamsFromString(value);
638}
639
640void NuCachedSource2::updateCacheParamsFromString(const char *s) {
641 ssize_t lowwaterMarkKb, highwaterMarkKb;
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700642 int keepAliveSecs;
Andreas Hubera045cb02011-10-05 14:32:17 -0700643
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700644 if (sscanf(s, "%ld/%ld/%d",
645 &lowwaterMarkKb, &highwaterMarkKb, &keepAliveSecs) != 3) {
Steve Block29357bc2012-01-06 19:20:56 +0000646 ALOGE("Failed to parse cache parameters from '%s'.", s);
Andreas Hubera045cb02011-10-05 14:32:17 -0700647 return;
648 }
649
650 if (lowwaterMarkKb >= 0) {
651 mLowwaterThresholdBytes = lowwaterMarkKb * 1024;
652 } else {
653 mLowwaterThresholdBytes = kDefaultLowWaterThreshold;
654 }
655
656 if (highwaterMarkKb >= 0) {
657 mHighwaterThresholdBytes = highwaterMarkKb * 1024;
658 } else {
659 mHighwaterThresholdBytes = kDefaultHighWaterThreshold;
660 }
661
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700662 if (mLowwaterThresholdBytes >= mHighwaterThresholdBytes) {
Steve Block29357bc2012-01-06 19:20:56 +0000663 ALOGE("Illegal low/highwater marks specified, reverting to defaults.");
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700664
665 mLowwaterThresholdBytes = kDefaultLowWaterThreshold;
666 mHighwaterThresholdBytes = kDefaultHighWaterThreshold;
667 }
668
669 if (keepAliveSecs >= 0) {
670 mKeepAliveIntervalUs = keepAliveSecs * 1000000ll;
671 } else {
672 mKeepAliveIntervalUs = kDefaultKeepAliveIntervalUs;
673 }
Andreas Hubera045cb02011-10-05 14:32:17 -0700674
Steve Block3856b092011-10-20 11:56:00 +0100675 ALOGV("lowwater = %d bytes, highwater = %d bytes, keepalive = %lld us",
Andreas Hubera045cb02011-10-05 14:32:17 -0700676 mLowwaterThresholdBytes,
677 mHighwaterThresholdBytes,
678 mKeepAliveIntervalUs);
679}
680
Andreas Huber49c59812011-10-07 13:40:45 -0700681// static
682void NuCachedSource2::RemoveCacheSpecificHeaders(
683 KeyedVector<String8, String8> *headers,
684 String8 *cacheConfig,
685 bool *disconnectAtHighwatermark) {
686 *cacheConfig = String8();
687 *disconnectAtHighwatermark = false;
688
689 if (headers == NULL) {
690 return;
691 }
692
693 ssize_t index;
694 if ((index = headers->indexOfKey(String8("x-cache-config"))) >= 0) {
695 *cacheConfig = headers->valueAt(index);
696
697 headers->removeItemsAt(index);
698
Steve Block3856b092011-10-20 11:56:00 +0100699 ALOGV("Using special cache config '%s'", cacheConfig->string());
Andreas Huber49c59812011-10-07 13:40:45 -0700700 }
701
702 if ((index = headers->indexOfKey(
703 String8("x-disconnect-at-highwatermark"))) >= 0) {
704 *disconnectAtHighwatermark = true;
705 headers->removeItemsAt(index);
706
Steve Block3856b092011-10-20 11:56:00 +0100707 ALOGV("Client requested disconnection at highwater mark");
Andreas Huber49c59812011-10-07 13:40:45 -0700708 }
709}
710
Andreas Huber5994b472010-06-10 11:09:36 -0700711} // namespace android