blob: 7f5ae616335db3b87210ef1e4605a34349ba4c3f [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
Mark Salyzyna5750e02014-06-18 16:34:45 -070017#include <inttypes.h>
18
Andreas Huberac05c312011-01-19 15:07:19 -080019//#define LOG_NDEBUG 0
Andreas Huber5994b472010-06-10 11:09:36 -070020#define LOG_TAG "NuCachedSource2"
21#include <utils/Log.h>
22
Marco Nelissen42057ce2019-09-23 12:15:57 -070023#include <datasource/NuCachedSource2.h>
24#include <datasource/HTTPBase.h>
Andreas Huber5994b472010-06-10 11:09:36 -070025
Andreas Hubera045cb02011-10-05 14:32:17 -070026#include <cutils/properties.h>
Andreas Huber5994b472010-06-10 11:09:36 -070027#include <media/stagefright/foundation/ADebug.h>
28#include <media/stagefright/foundation/AMessage.h>
29#include <media/stagefright/MediaErrors.h>
30
31namespace android {
32
33struct PageCache {
Chih-Hung Hsieh090ef602016-04-27 10:39:54 -070034 explicit PageCache(size_t pageSize);
Andreas Huber5994b472010-06-10 11:09:36 -070035 ~PageCache();
36
37 struct Page {
38 void *mData;
39 size_t mSize;
40 };
41
42 Page *acquirePage();
43 void releasePage(Page *page);
44
45 void appendPage(Page *page);
46 size_t releaseFromStart(size_t maxBytes);
47
48 size_t totalSize() const {
49 return mTotalSize;
50 }
51
52 void copy(size_t from, void *data, size_t size);
53
54private:
55 size_t mPageSize;
56 size_t mTotalSize;
57
58 List<Page *> mActivePages;
59 List<Page *> mFreePages;
60
61 void freePages(List<Page *> *list);
62
63 DISALLOW_EVIL_CONSTRUCTORS(PageCache);
64};
65
66PageCache::PageCache(size_t pageSize)
67 : mPageSize(pageSize),
68 mTotalSize(0) {
69}
70
71PageCache::~PageCache() {
72 freePages(&mActivePages);
73 freePages(&mFreePages);
74}
75
76void PageCache::freePages(List<Page *> *list) {
77 List<Page *>::iterator it = list->begin();
78 while (it != list->end()) {
79 Page *page = *it;
80
81 free(page->mData);
82 delete page;
83 page = NULL;
84
85 ++it;
86 }
87}
88
89PageCache::Page *PageCache::acquirePage() {
90 if (!mFreePages.empty()) {
91 List<Page *>::iterator it = mFreePages.begin();
92 Page *page = *it;
93 mFreePages.erase(it);
94
95 return page;
96 }
97
98 Page *page = new Page;
99 page->mData = malloc(mPageSize);
100 page->mSize = 0;
101
102 return page;
103}
104
105void PageCache::releasePage(Page *page) {
106 page->mSize = 0;
107 mFreePages.push_back(page);
108}
109
110void PageCache::appendPage(Page *page) {
111 mTotalSize += page->mSize;
112 mActivePages.push_back(page);
113}
114
115size_t PageCache::releaseFromStart(size_t maxBytes) {
116 size_t bytesReleased = 0;
117
118 while (maxBytes > 0 && !mActivePages.empty()) {
119 List<Page *>::iterator it = mActivePages.begin();
120
121 Page *page = *it;
122
123 if (maxBytes < page->mSize) {
124 break;
125 }
126
127 mActivePages.erase(it);
128
129 maxBytes -= page->mSize;
130 bytesReleased += page->mSize;
131
132 releasePage(page);
133 }
134
135 mTotalSize -= bytesReleased;
136 return bytesReleased;
137}
138
139void PageCache::copy(size_t from, void *data, size_t size) {
Mark Salyzyna5750e02014-06-18 16:34:45 -0700140 ALOGV("copy from %zu size %zu", from, size);
Andreas Huber5994b472010-06-10 11:09:36 -0700141
Andreas Huber31096292011-03-21 14:16:03 -0700142 if (size == 0) {
143 return;
144 }
145
Andreas Huber5994b472010-06-10 11:09:36 -0700146 CHECK_LE(from + size, mTotalSize);
147
148 size_t offset = 0;
149 List<Page *>::iterator it = mActivePages.begin();
150 while (from >= offset + (*it)->mSize) {
151 offset += (*it)->mSize;
152 ++it;
153 }
154
155 size_t delta = from - offset;
156 size_t avail = (*it)->mSize - delta;
157
158 if (avail >= size) {
159 memcpy(data, (const uint8_t *)(*it)->mData + delta, size);
160 return;
161 }
162
163 memcpy(data, (const uint8_t *)(*it)->mData + delta, avail);
164 ++it;
165 data = (uint8_t *)data + avail;
166 size -= avail;
167
168 while (size > 0) {
169 size_t copy = (*it)->mSize;
170 if (copy > size) {
171 copy = size;
172 }
173 memcpy(data, (*it)->mData, copy);
174 data = (uint8_t *)data + copy;
175 size -= copy;
176 ++it;
177 }
178}
179
180////////////////////////////////////////////////////////////////////////////////
181
Andreas Huber49c59812011-10-07 13:40:45 -0700182NuCachedSource2::NuCachedSource2(
183 const sp<DataSource> &source,
184 const char *cacheConfig,
185 bool disconnectAtHighwatermark)
Andreas Huber5994b472010-06-10 11:09:36 -0700186 : mSource(source),
187 mReflector(new AHandlerReflector<NuCachedSource2>(this)),
188 mLooper(new ALooper),
189 mCache(new PageCache(kPageSize)),
190 mCacheOffset(0),
191 mFinalStatus(OK),
192 mLastAccessPos(0),
Andreas Hubera5273eb2010-06-22 08:57:34 -0700193 mFetching(true),
Chong Zhang48296b72014-09-14 14:28:45 -0700194 mDisconnecting(false),
Andreas Huber0683eba2011-07-18 13:47:55 -0700195 mLastFetchTimeUs(-1),
Andreas Hubera045cb02011-10-05 14:32:17 -0700196 mNumRetriesLeft(kMaxNumRetries),
197 mHighwaterThresholdBytes(kDefaultHighWaterThreshold),
198 mLowwaterThresholdBytes(kDefaultLowWaterThreshold),
Andreas Huber49c59812011-10-07 13:40:45 -0700199 mKeepAliveIntervalUs(kDefaultKeepAliveIntervalUs),
200 mDisconnectAtHighwatermark(disconnectAtHighwatermark) {
201 // We are NOT going to support disconnect-at-highwatermark indefinitely
202 // and we are not guaranteeing support for client-specified cache
203 // parameters. Both of these are temporary measures to solve a specific
204 // problem that will be solved in a better way going forward.
205
Andreas Hubera045cb02011-10-05 14:32:17 -0700206 updateCacheParamsFromSystemProperty();
207
Andreas Huber49c59812011-10-07 13:40:45 -0700208 if (cacheConfig != NULL) {
209 updateCacheParamsFromString(cacheConfig);
210 }
211
212 if (mDisconnectAtHighwatermark) {
213 // Makes no sense to disconnect and do keep-alives...
214 mKeepAliveIntervalUs = 0;
215 }
216
Andreas Hubera814c1f2010-08-27 15:21:07 -0700217 mLooper->setName("NuCachedSource2");
Andreas Huber5994b472010-06-10 11:09:36 -0700218 mLooper->registerHandler(mReflector);
Andreas Huber1b86fe02014-01-29 11:13:26 -0800219
220 // Since it may not be obvious why our looper thread needs to be
221 // able to call into java since it doesn't appear to do so at all...
222 // IMediaHTTPConnection may be (and most likely is) implemented in JAVA
223 // and a local JAVA IBinder will call directly into JNI methods.
224 // So whenever we call DataSource::readAt it may end up in a call to
225 // IMediaHTTPConnection::readAt and therefore call back into JAVA.
226 mLooper->start(false /* runOnCallingThread */, true /* canCallJava */);
Marco Nelissen69d3d8a2016-03-07 13:20:01 -0800227
228 mName = String8::format("NuCachedSource2(%s)", mSource->toString().string());
Andreas Huber5994b472010-06-10 11:09:36 -0700229}
230
231NuCachedSource2::~NuCachedSource2() {
232 mLooper->stop();
233 mLooper->unregisterHandler(mReflector->id());
234
235 delete mCache;
236 mCache = NULL;
237}
238
Wonsik Kim316c3d92015-09-08 17:32:28 +0900239// static
240sp<NuCachedSource2> NuCachedSource2::Create(
241 const sp<DataSource> &source,
242 const char *cacheConfig,
243 bool disconnectAtHighwatermark) {
244 sp<NuCachedSource2> instance = new NuCachedSource2(
245 source, cacheConfig, disconnectAtHighwatermark);
246 Mutex::Autolock autoLock(instance->mLock);
247 (new AMessage(kWhatFetchMore, instance->mReflector))->post();
248 return instance;
249}
250
James Dong5b1b8a92011-05-25 19:37:03 -0700251status_t NuCachedSource2::getEstimatedBandwidthKbps(int32_t *kbps) {
James Dongb33d2ac2011-06-01 15:27:20 -0700252 if (mSource->flags() & kIsHTTPBasedSource) {
253 HTTPBase* source = static_cast<HTTPBase *>(mSource.get());
254 return source->getEstimatedBandwidthKbps(kbps);
255 }
256 return ERROR_UNSUPPORTED;
James Dong5b1b8a92011-05-25 19:37:03 -0700257}
258
Robert Shih03244032018-10-02 14:36:32 -0700259void NuCachedSource2::close() {
260 disconnect();
261}
262
Chong Zhang48296b72014-09-14 14:28:45 -0700263void NuCachedSource2::disconnect() {
264 if (mSource->flags() & kIsHTTPBasedSource) {
265 ALOGV("disconnecting HTTPBasedSource");
266
267 {
268 Mutex::Autolock autoLock(mLock);
269 // set mDisconnecting to true, if a fetch returns after
270 // this, the source will be marked as EOS.
271 mDisconnecting = true;
Chong Zhang9f3d1cf2014-09-23 22:22:30 -0700272
273 // explicitly signal mCondition so that the pending readAt()
274 // will immediately return
275 mCondition.signal();
Chong Zhang48296b72014-09-14 14:28:45 -0700276 }
277
278 // explicitly disconnect from the source, to allow any
279 // pending reads to return more promptly
280 static_cast<HTTPBase *>(mSource.get())->disconnect();
281 }
282}
283
James Dong5b1b8a92011-05-25 19:37:03 -0700284status_t NuCachedSource2::setCacheStatCollectFreq(int32_t freqMs) {
James Dongb33d2ac2011-06-01 15:27:20 -0700285 if (mSource->flags() & kIsHTTPBasedSource) {
286 HTTPBase *source = static_cast<HTTPBase *>(mSource.get());
287 return source->setBandwidthStatCollectFreq(freqMs);
288 }
289 return ERROR_UNSUPPORTED;
James Dong5b1b8a92011-05-25 19:37:03 -0700290}
291
Andreas Huber5994b472010-06-10 11:09:36 -0700292status_t NuCachedSource2::initCheck() const {
293 return mSource->initCheck();
294}
295
James Dongc7fc37a2010-11-16 14:04:54 -0800296status_t NuCachedSource2::getSize(off64_t *size) {
Andreas Huber5994b472010-06-10 11:09:36 -0700297 return mSource->getSize(size);
298}
299
300uint32_t NuCachedSource2::flags() {
James Dongb33d2ac2011-06-01 15:27:20 -0700301 // Remove HTTP related flags since NuCachedSource2 is not HTTP-based.
302 uint32_t flags = mSource->flags() & ~(kWantsPrefetching | kIsHTTPBasedSource);
303 return (flags | kIsCachingDataSource);
Andreas Huber5994b472010-06-10 11:09:36 -0700304}
305
306void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) {
307 switch (msg->what()) {
308 case kWhatFetchMore:
309 {
310 onFetch();
311 break;
312 }
313
314 case kWhatRead:
315 {
316 onRead(msg);
317 break;
318 }
319
320 default:
321 TRESPASS();
322 }
323}
324
325void NuCachedSource2::fetchInternal() {
Steve Block3856b092011-10-20 11:56:00 +0100326 ALOGV("fetchInternal");
Andreas Huber5994b472010-06-10 11:09:36 -0700327
Andreas Huber95c4d602011-10-17 15:49:01 -0700328 bool reconnect = false;
329
Andreas Huber0683eba2011-07-18 13:47:55 -0700330 {
331 Mutex::Autolock autoLock(mLock);
332 CHECK(mFinalStatus == OK || mNumRetriesLeft > 0);
333
334 if (mFinalStatus != OK) {
335 --mNumRetriesLeft;
336
Andreas Huber95c4d602011-10-17 15:49:01 -0700337 reconnect = true;
338 }
339 }
Andreas Huber0683eba2011-07-18 13:47:55 -0700340
Andreas Huber95c4d602011-10-17 15:49:01 -0700341 if (reconnect) {
342 status_t err =
343 mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize());
Andreas Huber0683eba2011-07-18 13:47:55 -0700344
Andreas Huber95c4d602011-10-17 15:49:01 -0700345 Mutex::Autolock autoLock(mLock);
346
Chong Zhang9f3d1cf2014-09-23 22:22:30 -0700347 if (mDisconnecting) {
348 mNumRetriesLeft = 0;
349 mFinalStatus = ERROR_END_OF_STREAM;
350 return;
351 } else if (err == ERROR_UNSUPPORTED || err == -EPIPE) {
Andreas Hubera7607a72012-08-28 09:48:40 -0700352 // These are errors that are not likely to go away even if we
353 // retry, i.e. the server doesn't support range requests or similar.
Andreas Huber95c4d602011-10-17 15:49:01 -0700354 mNumRetriesLeft = 0;
355 return;
356 } else if (err != OK) {
Steve Blockdf64d152012-01-04 20:05:49 +0000357 ALOGI("The attempt to reconnect failed, %d retries remaining",
Andreas Huber95c4d602011-10-17 15:49:01 -0700358 mNumRetriesLeft);
359
360 return;
Andreas Huber0683eba2011-07-18 13:47:55 -0700361 }
362 }
Andreas Huber5994b472010-06-10 11:09:36 -0700363
364 PageCache::Page *page = mCache->acquirePage();
365
366 ssize_t n = mSource->readAt(
367 mCacheOffset + mCache->totalSize(), page->mData, kPageSize);
368
369 Mutex::Autolock autoLock(mLock);
370
Chong Zhang48296b72014-09-14 14:28:45 -0700371 if (n == 0 || mDisconnecting) {
Chong Zhangefbb6192015-01-30 17:13:27 -0800372 ALOGI("caching reached eos.");
Chong Zhang48296b72014-09-14 14:28:45 -0700373
374 mNumRetriesLeft = 0;
375 mFinalStatus = ERROR_END_OF_STREAM;
376
377 mCache->releasePage(page);
378 } else if (n < 0) {
Andreas Huber5994b472010-06-10 11:09:36 -0700379 mFinalStatus = n;
Andreas Hubera7607a72012-08-28 09:48:40 -0700380 if (n == ERROR_UNSUPPORTED || n == -EPIPE) {
381 // These are errors that are not likely to go away even if we
382 // retry, i.e. the server doesn't support range requests or similar.
383 mNumRetriesLeft = 0;
384 }
385
Mark Salyzyna5750e02014-06-18 16:34:45 -0700386 ALOGE("source returned error %zd, %d retries left", n, mNumRetriesLeft);
Andreas Huber5994b472010-06-10 11:09:36 -0700387 mCache->releasePage(page);
Andreas Huber5994b472010-06-10 11:09:36 -0700388 } else {
Andreas Huber0683eba2011-07-18 13:47:55 -0700389 if (mFinalStatus != OK) {
Steve Blockdf64d152012-01-04 20:05:49 +0000390 ALOGI("retrying a previously failed read succeeded.");
Andreas Huber0683eba2011-07-18 13:47:55 -0700391 }
392 mNumRetriesLeft = kMaxNumRetries;
393 mFinalStatus = OK;
394
Andreas Huber5994b472010-06-10 11:09:36 -0700395 page->mSize = n;
396 mCache->appendPage(page);
397 }
398}
399
400void NuCachedSource2::onFetch() {
Steve Block3856b092011-10-20 11:56:00 +0100401 ALOGV("onFetch");
Andreas Huber5994b472010-06-10 11:09:36 -0700402
Andreas Huber0683eba2011-07-18 13:47:55 -0700403 if (mFinalStatus != OK && mNumRetriesLeft == 0) {
Steve Block3856b092011-10-20 11:56:00 +0100404 ALOGV("EOS reached, done prefetching for now");
Andreas Huber5994b472010-06-10 11:09:36 -0700405 mFetching = false;
406 }
407
Andreas Hubera5273eb2010-06-22 08:57:34 -0700408 bool keepAlive =
409 !mFetching
410 && mFinalStatus == OK
Andreas Hubera045cb02011-10-05 14:32:17 -0700411 && mKeepAliveIntervalUs > 0
412 && ALooper::GetNowUs() >= mLastFetchTimeUs + mKeepAliveIntervalUs;
Andreas Hubera5273eb2010-06-22 08:57:34 -0700413
414 if (mFetching || keepAlive) {
415 if (keepAlive) {
Steve Blockdf64d152012-01-04 20:05:49 +0000416 ALOGI("Keep alive");
Andreas Hubera5273eb2010-06-22 08:57:34 -0700417 }
418
Andreas Huber5994b472010-06-10 11:09:36 -0700419 fetchInternal();
420
Andreas Hubera5273eb2010-06-22 08:57:34 -0700421 mLastFetchTimeUs = ALooper::GetNowUs();
422
Andreas Hubera045cb02011-10-05 14:32:17 -0700423 if (mFetching && mCache->totalSize() >= mHighwaterThresholdBytes) {
Steve Blockdf64d152012-01-04 20:05:49 +0000424 ALOGI("Cache full, done prefetching for now");
Andreas Huber5994b472010-06-10 11:09:36 -0700425 mFetching = false;
Andreas Huber49c59812011-10-07 13:40:45 -0700426
427 if (mDisconnectAtHighwatermark
428 && (mSource->flags() & DataSource::kIsHTTPBasedSource)) {
Steve Block3856b092011-10-20 11:56:00 +0100429 ALOGV("Disconnecting at high watermark");
Andreas Huber49c59812011-10-07 13:40:45 -0700430 static_cast<HTTPBase *>(mSource.get())->disconnect();
Bryan Mawhinney40a4e142012-01-18 13:40:07 +0000431 mFinalStatus = -EAGAIN;
Andreas Huber49c59812011-10-07 13:40:45 -0700432 }
Andreas Huber5994b472010-06-10 11:09:36 -0700433 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800434 } else {
Andreas Huberd17875a2010-06-11 14:14:52 -0700435 Mutex::Autolock autoLock(mLock);
Andreas Huber5994b472010-06-10 11:09:36 -0700436 restartPrefetcherIfNecessary_l();
437 }
438
Andreas Huber0683eba2011-07-18 13:47:55 -0700439 int64_t delayUs;
440 if (mFetching) {
441 if (mFinalStatus != OK && mNumRetriesLeft > 0) {
442 // We failed this time and will try again in 3 seconds.
Chih-Hung Hsieh3794b242018-12-11 13:55:06 -0800443 delayUs = 3000000LL;
Andreas Huber0683eba2011-07-18 13:47:55 -0700444 } else {
445 delayUs = 0;
446 }
447 } else {
Chih-Hung Hsieh3794b242018-12-11 13:55:06 -0800448 delayUs = 100000LL;
Andreas Huber0683eba2011-07-18 13:47:55 -0700449 }
450
Lajos Molnar1d15ab52015-03-04 16:46:34 -0800451 (new AMessage(kWhatFetchMore, mReflector))->post(delayUs);
Andreas Huber5994b472010-06-10 11:09:36 -0700452}
453
454void NuCachedSource2::onRead(const sp<AMessage> &msg) {
Steve Block3856b092011-10-20 11:56:00 +0100455 ALOGV("onRead");
Andreas Huber5994b472010-06-10 11:09:36 -0700456
457 int64_t offset;
458 CHECK(msg->findInt64("offset", &offset));
459
460 void *data;
461 CHECK(msg->findPointer("data", &data));
462
463 size_t size;
464 CHECK(msg->findSize("size", &size));
465
466 ssize_t result = readInternal(offset, data, size);
467
468 if (result == -EAGAIN) {
469 msg->post(50000);
470 return;
471 }
472
473 Mutex::Autolock autoLock(mLock);
Robert Shih4f17dad2014-09-30 14:17:38 -0700474 if (mDisconnecting) {
475 mCondition.signal();
476 return;
477 }
Andreas Huber5994b472010-06-10 11:09:36 -0700478
479 CHECK(mAsyncResult == NULL);
480
481 mAsyncResult = new AMessage;
482 mAsyncResult->setInt32("result", result);
483
484 mCondition.signal();
485}
486
Andreas Huber34ef0f32010-11-11 15:37:17 -0800487void NuCachedSource2::restartPrefetcherIfNecessary_l(
Andreas Huber7bf84132011-04-19 10:04:08 -0700488 bool ignoreLowWaterThreshold, bool force) {
James Dong6ee94582011-01-14 15:15:12 -0800489 static const size_t kGrayArea = 1024 * 1024;
Andreas Huber5994b472010-06-10 11:09:36 -0700490
Andreas Huber0683eba2011-07-18 13:47:55 -0700491 if (mFetching || (mFinalStatus != OK && mNumRetriesLeft == 0)) {
Andreas Huber5994b472010-06-10 11:09:36 -0700492 return;
493 }
494
Andreas Huber7bf84132011-04-19 10:04:08 -0700495 if (!ignoreLowWaterThreshold && !force
Andreas Huber34ef0f32010-11-11 15:37:17 -0800496 && mCacheOffset + mCache->totalSize() - mLastAccessPos
Andreas Hubera045cb02011-10-05 14:32:17 -0700497 >= mLowwaterThresholdBytes) {
Andreas Huber5994b472010-06-10 11:09:36 -0700498 return;
499 }
500
501 size_t maxBytes = mLastAccessPos - mCacheOffset;
Andreas Huber5994b472010-06-10 11:09:36 -0700502
Andreas Huber7bf84132011-04-19 10:04:08 -0700503 if (!force) {
504 if (maxBytes < kGrayArea) {
505 return;
506 }
507
508 maxBytes -= kGrayArea;
509 }
Andreas Huber5994b472010-06-10 11:09:36 -0700510
511 size_t actualBytes = mCache->releaseFromStart(maxBytes);
512 mCacheOffset += actualBytes;
513
Mark Salyzyna5750e02014-06-18 16:34:45 -0700514 ALOGI("restarting prefetcher, totalSize = %zu", mCache->totalSize());
Andreas Huber5994b472010-06-10 11:09:36 -0700515 mFetching = true;
516}
517
James Dongc7fc37a2010-11-16 14:04:54 -0800518ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
Andreas Huber5994b472010-06-10 11:09:36 -0700519 Mutex::Autolock autoSerializer(mSerializer);
520
Lajos Molnaree4e1b12015-04-17 13:46:19 -0700521 ALOGV("readAt offset %lld, size %zu", (long long)offset, size);
Andreas Huber5994b472010-06-10 11:09:36 -0700522
523 Mutex::Autolock autoLock(mLock);
Robert Shih4f17dad2014-09-30 14:17:38 -0700524 if (mDisconnecting) {
525 return ERROR_END_OF_STREAM;
526 }
Andreas Huber5994b472010-06-10 11:09:36 -0700527
528 // If the request can be completely satisfied from the cache, do so.
529
530 if (offset >= mCacheOffset
531 && offset + size <= mCacheOffset + mCache->totalSize()) {
532 size_t delta = offset - mCacheOffset;
533 mCache->copy(delta, data, size);
534
535 mLastAccessPos = offset + size;
536
537 return size;
538 }
539
Lajos Molnar1d15ab52015-03-04 16:46:34 -0800540 sp<AMessage> msg = new AMessage(kWhatRead, mReflector);
Andreas Huber5994b472010-06-10 11:09:36 -0700541 msg->setInt64("offset", offset);
542 msg->setPointer("data", data);
543 msg->setSize("size", size);
544
545 CHECK(mAsyncResult == NULL);
546 msg->post();
547
Chong Zhang9f3d1cf2014-09-23 22:22:30 -0700548 while (mAsyncResult == NULL && !mDisconnecting) {
Andreas Huber5994b472010-06-10 11:09:36 -0700549 mCondition.wait(mLock);
550 }
551
Chong Zhang9f3d1cf2014-09-23 22:22:30 -0700552 if (mDisconnecting) {
Robert Shih4f17dad2014-09-30 14:17:38 -0700553 mAsyncResult.clear();
Chong Zhang9f3d1cf2014-09-23 22:22:30 -0700554 return ERROR_END_OF_STREAM;
555 }
556
Andreas Huber5994b472010-06-10 11:09:36 -0700557 int32_t result;
558 CHECK(mAsyncResult->findInt32("result", &result));
559
560 mAsyncResult.clear();
561
562 if (result > 0) {
563 mLastAccessPos = offset + result;
564 }
565
566 return (ssize_t)result;
567}
568
569size_t NuCachedSource2::cachedSize() {
570 Mutex::Autolock autoLock(mLock);
571 return mCacheOffset + mCache->totalSize();
572}
573
Robert Shih3f657642018-09-27 12:22:23 -0700574status_t NuCachedSource2::getAvailableSize(off64_t offset, off64_t *size) {
Andreas Huber5994b472010-06-10 11:09:36 -0700575 Mutex::Autolock autoLock(mLock);
Robert Shih3f657642018-09-27 12:22:23 -0700576 status_t finalStatus = UNKNOWN_ERROR;
577 *size = approxDataRemaining_l(offset, &finalStatus);
578 return finalStatus;
Andreas Huber5994b472010-06-10 11:09:36 -0700579}
580
Robert Shih3f657642018-09-27 12:22:23 -0700581size_t NuCachedSource2::approxDataRemaining(status_t *finalStatus) const {
582 Mutex::Autolock autoLock(mLock);
583 return approxDataRemaining_l(mLastAccessPos, finalStatus);
584}
585
586size_t NuCachedSource2::approxDataRemaining_l(off64_t offset, status_t *finalStatus) const {
Bryan Mawhinney1bd233c2011-01-18 19:12:21 +0000587 *finalStatus = mFinalStatus;
Andreas Huber0683eba2011-07-18 13:47:55 -0700588
589 if (mFinalStatus != OK && mNumRetriesLeft > 0) {
590 // Pretend that everything is fine until we're out of retries.
591 *finalStatus = OK;
592 }
593
Robert Shih3f657642018-09-27 12:22:23 -0700594 offset = offset >= 0 ? offset : mLastAccessPos;
James Dongc7fc37a2010-11-16 14:04:54 -0800595 off64_t lastBytePosCached = mCacheOffset + mCache->totalSize();
Robert Shih3f657642018-09-27 12:22:23 -0700596 if (offset < lastBytePosCached) {
597 return lastBytePosCached - offset;
Andreas Huber5994b472010-06-10 11:09:36 -0700598 }
599 return 0;
600}
601
James Dongc7fc37a2010-11-16 14:04:54 -0800602ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
Andreas Hubera045cb02011-10-05 14:32:17 -0700603 CHECK_LE(size, (size_t)mHighwaterThresholdBytes);
Andreas Huber7bf84132011-04-19 10:04:08 -0700604
Lajos Molnaree4e1b12015-04-17 13:46:19 -0700605 ALOGV("readInternal offset %lld size %zu", (long long)offset, size);
Andreas Huber5994b472010-06-10 11:09:36 -0700606
607 Mutex::Autolock autoLock(mLock);
608
Chong Zhang2c878cf2015-05-19 10:56:40 -0700609 // If we're disconnecting, return EOS and don't access *data pointer.
610 // data could be on the stack of the caller to NuCachedSource2::readAt(),
611 // which may have exited already.
612 if (mDisconnecting) {
613 return ERROR_END_OF_STREAM;
614 }
615
Andreas Huber7bf84132011-04-19 10:04:08 -0700616 if (!mFetching) {
617 mLastAccessPos = offset;
618 restartPrefetcherIfNecessary_l(
619 false, // ignoreLowWaterThreshold
620 true); // force
621 }
622
Andreas Huber5994b472010-06-10 11:09:36 -0700623 if (offset < mCacheOffset
James Dongc7fc37a2010-11-16 14:04:54 -0800624 || offset >= (off64_t)(mCacheOffset + mCache->totalSize())) {
James Dong6ee94582011-01-14 15:15:12 -0800625 static const off64_t kPadding = 256 * 1024;
Andreas Huber5994b472010-06-10 11:09:36 -0700626
627 // In the presence of multiple decoded streams, once of them will
628 // trigger this seek request, the other one will request data "nearby"
629 // soon, adjust the seek position so that that subsequent request
630 // does not trigger another seek.
James Dongc7fc37a2010-11-16 14:04:54 -0800631 off64_t seekOffset = (offset > kPadding) ? offset - kPadding : 0;
Andreas Huber5994b472010-06-10 11:09:36 -0700632
633 seekInternal_l(seekOffset);
634 }
635
636 size_t delta = offset - mCacheOffset;
637
Bryan Mawhinney40a4e142012-01-18 13:40:07 +0000638 if (mFinalStatus != OK && mNumRetriesLeft == 0) {
Andreas Huber5994b472010-06-10 11:09:36 -0700639 if (delta >= mCache->totalSize()) {
640 return mFinalStatus;
641 }
642
Andreas Huber6f5aae12010-06-11 09:22:48 -0700643 size_t avail = mCache->totalSize() - delta;
Andreas Huber67802972011-05-04 11:43:43 -0700644
645 if (avail > size) {
646 avail = size;
647 }
648
Andreas Huber5994b472010-06-10 11:09:36 -0700649 mCache->copy(delta, data, avail);
650
651 return avail;
652 }
653
654 if (offset + size <= mCacheOffset + mCache->totalSize()) {
655 mCache->copy(delta, data, size);
656
657 return size;
658 }
659
Steve Block3856b092011-10-20 11:56:00 +0100660 ALOGV("deferring read");
Andreas Huber5994b472010-06-10 11:09:36 -0700661
662 return -EAGAIN;
663}
664
James Dongc7fc37a2010-11-16 14:04:54 -0800665status_t NuCachedSource2::seekInternal_l(off64_t offset) {
Andreas Huber5994b472010-06-10 11:09:36 -0700666 mLastAccessPos = offset;
667
668 if (offset >= mCacheOffset
James Dongc7fc37a2010-11-16 14:04:54 -0800669 && offset <= (off64_t)(mCacheOffset + mCache->totalSize())) {
Andreas Huber5994b472010-06-10 11:09:36 -0700670 return OK;
671 }
672
Lajos Molnaree4e1b12015-04-17 13:46:19 -0700673 ALOGI("new range: offset= %lld", (long long)offset);
Andreas Huber5994b472010-06-10 11:09:36 -0700674
675 mCacheOffset = offset;
676
677 size_t totalSize = mCache->totalSize();
678 CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
679
Bryan Mawhinney40a4e142012-01-18 13:40:07 +0000680 mNumRetriesLeft = kMaxNumRetries;
Andreas Huber5994b472010-06-10 11:09:36 -0700681 mFetching = true;
682
683 return OK;
684}
685
Andreas Huber34ef0f32010-11-11 15:37:17 -0800686void NuCachedSource2::resumeFetchingIfNecessary() {
687 Mutex::Autolock autoLock(mLock);
688
689 restartPrefetcherIfNecessary_l(true /* ignore low water threshold */);
690}
691
James Dong9d2f3862012-01-10 08:24:37 -0800692sp<DecryptHandle> NuCachedSource2::DrmInitialization(const char* mime) {
693 return mSource->DrmInitialization(mime);
Gloria Wangb3714262010-11-01 15:53:16 -0700694}
695
Gloria Wang771b85d2010-11-09 15:06:51 -0800696String8 NuCachedSource2::getUri() {
697 return mSource->getUri();
698}
Andreas Huberac05c312011-01-19 15:07:19 -0800699
Andreas Huber6511c972011-03-30 11:15:27 -0700700String8 NuCachedSource2::getMIMEType() const {
701 return mSource->getMIMEType();
702}
703
Andreas Hubera045cb02011-10-05 14:32:17 -0700704void NuCachedSource2::updateCacheParamsFromSystemProperty() {
705 char value[PROPERTY_VALUE_MAX];
706 if (!property_get("media.stagefright.cache-params", value, NULL)) {
707 return;
708 }
709
710 updateCacheParamsFromString(value);
711}
712
713void NuCachedSource2::updateCacheParamsFromString(const char *s) {
714 ssize_t lowwaterMarkKb, highwaterMarkKb;
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700715 int keepAliveSecs;
Andreas Hubera045cb02011-10-05 14:32:17 -0700716
Mark Salyzyndb43b342014-04-04 14:47:28 -0700717 if (sscanf(s, "%zd/%zd/%d",
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700718 &lowwaterMarkKb, &highwaterMarkKb, &keepAliveSecs) != 3) {
Steve Block29357bc2012-01-06 19:20:56 +0000719 ALOGE("Failed to parse cache parameters from '%s'.", s);
Andreas Hubera045cb02011-10-05 14:32:17 -0700720 return;
721 }
722
723 if (lowwaterMarkKb >= 0) {
724 mLowwaterThresholdBytes = lowwaterMarkKb * 1024;
725 } else {
726 mLowwaterThresholdBytes = kDefaultLowWaterThreshold;
727 }
728
729 if (highwaterMarkKb >= 0) {
730 mHighwaterThresholdBytes = highwaterMarkKb * 1024;
731 } else {
732 mHighwaterThresholdBytes = kDefaultHighWaterThreshold;
733 }
734
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700735 if (mLowwaterThresholdBytes >= mHighwaterThresholdBytes) {
Steve Block29357bc2012-01-06 19:20:56 +0000736 ALOGE("Illegal low/highwater marks specified, reverting to defaults.");
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700737
738 mLowwaterThresholdBytes = kDefaultLowWaterThreshold;
739 mHighwaterThresholdBytes = kDefaultHighWaterThreshold;
740 }
741
742 if (keepAliveSecs >= 0) {
Chih-Hung Hsieh3794b242018-12-11 13:55:06 -0800743 mKeepAliveIntervalUs = keepAliveSecs * 1000000LL;
Andreas Huber0b8cd8b2011-10-07 10:00:38 -0700744 } else {
745 mKeepAliveIntervalUs = kDefaultKeepAliveIntervalUs;
746 }
Andreas Hubera045cb02011-10-05 14:32:17 -0700747
Lajos Molnaree4e1b12015-04-17 13:46:19 -0700748 ALOGV("lowwater = %zu bytes, highwater = %zu bytes, keepalive = %lld us",
Andreas Hubera045cb02011-10-05 14:32:17 -0700749 mLowwaterThresholdBytes,
750 mHighwaterThresholdBytes,
Lajos Molnaree4e1b12015-04-17 13:46:19 -0700751 (long long)mKeepAliveIntervalUs);
Andreas Hubera045cb02011-10-05 14:32:17 -0700752}
753
Andreas Huber49c59812011-10-07 13:40:45 -0700754// static
755void NuCachedSource2::RemoveCacheSpecificHeaders(
756 KeyedVector<String8, String8> *headers,
757 String8 *cacheConfig,
758 bool *disconnectAtHighwatermark) {
759 *cacheConfig = String8();
760 *disconnectAtHighwatermark = false;
761
762 if (headers == NULL) {
763 return;
764 }
765
766 ssize_t index;
767 if ((index = headers->indexOfKey(String8("x-cache-config"))) >= 0) {
768 *cacheConfig = headers->valueAt(index);
769
770 headers->removeItemsAt(index);
771
Steve Block3856b092011-10-20 11:56:00 +0100772 ALOGV("Using special cache config '%s'", cacheConfig->string());
Andreas Huber49c59812011-10-07 13:40:45 -0700773 }
774
775 if ((index = headers->indexOfKey(
776 String8("x-disconnect-at-highwatermark"))) >= 0) {
777 *disconnectAtHighwatermark = true;
778 headers->removeItemsAt(index);
779
Steve Block3856b092011-10-20 11:56:00 +0100780 ALOGV("Client requested disconnection at highwater mark");
Andreas Huber49c59812011-10-07 13:40:45 -0700781 }
782}
783
Andreas Huber5994b472010-06-10 11:09:36 -0700784} // namespace android