blob: 1ca463e7826954c6401a8cc5a87cf4b9105dcdff [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <string.h>
#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/MediaDebug.h>
namespace android {
CachingDataSource::CachingDataSource(
const sp<DataSource> &source, size_t pageSize, int numPages)
: mSource(source),
mData(malloc(pageSize * numPages)),
mPageSize(pageSize),
mFirst(NULL),
mLast(NULL) {
for (int i = 0; i < numPages; ++i) {
Page *page = new Page;
page->mPrev = mLast;
page->mNext = NULL;
if (mLast == NULL) {
mFirst = page;
} else {
mLast->mNext = page;
}
mLast = page;
page->mOffset = -1;
page->mLength = 0;
page->mData = (char *)mData + mPageSize * i;
}
}
CachingDataSource::~CachingDataSource() {
Page *page = mFirst;
while (page != NULL) {
Page *next = page->mNext;
delete page;
page = next;
}
mFirst = mLast = NULL;
free(mData);
mData = NULL;
}
status_t CachingDataSource::initCheck() const {
return mSource->initCheck();
}
status_t CachingDataSource::getSize(off_t *size) {
return mSource->getSize(size);
}
uint32_t CachingDataSource::flags() {
return mSource->flags();
}
ssize_t CachingDataSource::readAt(off_t offset, void *data, size_t size) {
Mutex::Autolock autoLock(mLock);
size_t total = 0;
while (size > 0) {
Page *page = mFirst;
while (page != NULL) {
if (page->mOffset >= 0 && offset >= page->mOffset
&& offset < page->mOffset + (off_t)page->mLength) {
break;
}
page = page->mNext;
}
if (page == NULL) {
page = allocate_page();
page->mOffset = offset - offset % mPageSize;
ssize_t n = mSource->readAt(page->mOffset, page->mData, mPageSize);
if (n < 0) {
page->mLength = 0;
} else {
page->mLength = (size_t)n;
}
mFirst->mPrev = page;
page->mNext = mFirst;
page->mPrev = NULL;
mFirst = page;
if (n < 0) {
return n;
}
if (offset >= page->mOffset + (off_t)page->mLength) {
break;
}
} else {
// Move "page" to the front in LRU order.
if (page->mNext != NULL) {
page->mNext->mPrev = page->mPrev;
} else {
mLast = page->mPrev;
}
if (page->mPrev != NULL) {
page->mPrev->mNext = page->mNext;
} else {
mFirst = page->mNext;
}
mFirst->mPrev = page;
page->mNext = mFirst;
page->mPrev = NULL;
mFirst = page;
}
size_t copy = page->mLength - (offset - page->mOffset);
if (copy > size) {
copy = size;
}
memcpy(data,(const char *)page->mData + (offset - page->mOffset),
copy);
total += copy;
if (page->mLength < mPageSize) {
// This was the final page. There is no more data beyond it.
break;
}
offset += copy;
size -= copy;
data = (char *)data + copy;
}
return total;
}
CachingDataSource::Page *CachingDataSource::allocate_page() {
// The last page is the least recently used, i.e. oldest.
Page *page = mLast;
page->mPrev->mNext = NULL;
mLast = page->mPrev;
page->mPrev = NULL;
return page;
}
} // namespace android