Split datasource out of libstagefright
into libdatasource, which contains:
DataSourceFactory
(Clear)FileSource
(Clear)MediaHTTP
DataURISource
HTTPBase
NuCachedSource2
This is needed to break a circular dependency in an upcoming CL.
Test: build, boot
Change-Id: I34d9937235c78f18f51b18945342a0743e209577
diff --git a/media/libdatasource/Android.bp b/media/libdatasource/Android.bp
new file mode 100644
index 0000000..dd8ef74
--- /dev/null
+++ b/media/libdatasource/Android.bp
@@ -0,0 +1,66 @@
+cc_library {
+ name: "libdatasource",
+
+ srcs: [
+ "ClearFileSource.cpp",
+ "ClearMediaHTTP.cpp",
+ "DataSourceFactory.cpp",
+ "DataURISource.cpp",
+ "FileSource.cpp",
+ "HTTPBase.cpp",
+ "MediaHTTP.cpp",
+ "NuCachedSource2.cpp",
+ ],
+
+ aidl: {
+ local_include_dirs: ["aidl"],
+ export_aidl_headers: true,
+ },
+
+ header_libs: [
+ "libstagefright_headers",
+ "media_ndk_headers",
+ "libmedia_headers",
+ ],
+
+ export_header_lib_headers: [
+ "libstagefright_headers",
+ "media_ndk_headers",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ "libdrmframework",
+ "libutils",
+ "libstagefright_foundation",
+ "libdl",
+ ],
+
+ static_libs: [
+ "libc_malloc_debug_backtrace", // for memory heap analysis
+ "libmedia_midiiowrapper",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libdatasource/ClearFileSource.cpp b/media/libdatasource/ClearFileSource.cpp
new file mode 100644
index 0000000..afafa23
--- /dev/null
+++ b/media/libdatasource/ClearFileSource.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ClearFileSource"
+#include <utils/Log.h>
+
+#include <datasource/ClearFileSource.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/FoundationUtils.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace android {
+
+ClearFileSource::ClearFileSource(const char *filename)
+ : mFd(-1),
+ mOffset(0),
+ mLength(-1),
+ mName("<null>") {
+
+ if (filename) {
+ mName = String8::format("FileSource(%s)", filename);
+ }
+ ALOGV("%s", filename);
+ mFd = open(filename, O_LARGEFILE | O_RDONLY);
+
+ if (mFd >= 0) {
+ mLength = lseek64(mFd, 0, SEEK_END);
+ } else {
+ ALOGE("Failed to open file '%s'. (%s)", filename, strerror(errno));
+ }
+}
+
+ClearFileSource::ClearFileSource(int fd, int64_t offset, int64_t length)
+ : mFd(fd),
+ mOffset(offset),
+ mLength(length),
+ mName("<null>") {
+ ALOGV("fd=%d (%s), offset=%lld, length=%lld",
+ fd, nameForFd(fd).c_str(), (long long) offset, (long long) length);
+
+ if (mOffset < 0) {
+ mOffset = 0;
+ }
+ if (mLength < 0) {
+ mLength = 0;
+ }
+ if (mLength > INT64_MAX - mOffset) {
+ mLength = INT64_MAX - mOffset;
+ }
+ struct stat s;
+ if (fstat(fd, &s) == 0) {
+ if (mOffset > s.st_size) {
+ mOffset = s.st_size;
+ mLength = 0;
+ }
+ if (mOffset + mLength > s.st_size) {
+ mLength = s.st_size - mOffset;
+ }
+ }
+ if (mOffset != offset || mLength != length) {
+ ALOGW("offset/length adjusted from %lld/%lld to %lld/%lld",
+ (long long) offset, (long long) length,
+ (long long) mOffset, (long long) mLength);
+ }
+
+ mName = String8::format(
+ "FileSource(fd(%s), %lld, %lld)",
+ nameForFd(fd).c_str(),
+ (long long) mOffset,
+ (long long) mLength);
+
+}
+
+ClearFileSource::~ClearFileSource() {
+ if (mFd >= 0) {
+ ::close(mFd);
+ mFd = -1;
+ }
+}
+
+status_t ClearFileSource::initCheck() const {
+ return mFd >= 0 ? OK : NO_INIT;
+}
+
+ssize_t ClearFileSource::readAt(off64_t offset, void *data, size_t size) {
+ if (mFd < 0) {
+ return NO_INIT;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ if (mLength >= 0) {
+ if (offset >= mLength) {
+ return 0; // read beyond EOF.
+ }
+ uint64_t numAvailable = mLength - offset;
+ if ((uint64_t)size > numAvailable) {
+ size = numAvailable;
+ }
+ }
+ return readAt_l(offset, data, size);
+}
+
+ssize_t ClearFileSource::readAt_l(off64_t offset, void *data, size_t size) {
+ off64_t result = lseek64(mFd, offset + mOffset, SEEK_SET);
+ if (result == -1) {
+ ALOGE("seek to %lld failed", (long long)(offset + mOffset));
+ return UNKNOWN_ERROR;
+ }
+
+ return ::read(mFd, data, size);
+}
+
+status_t ClearFileSource::getSize(off64_t *size) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mFd < 0) {
+ return NO_INIT;
+ }
+
+ *size = mLength;
+
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libdatasource/ClearMediaHTTP.cpp b/media/libdatasource/ClearMediaHTTP.cpp
new file mode 100644
index 0000000..7249c84
--- /dev/null
+++ b/media/libdatasource/ClearMediaHTTP.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ClearMediaHTTP"
+#include <utils/Log.h>
+
+#include <datasource/ClearMediaHTTP.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/FoundationUtils.h>
+
+#include <media/MediaHTTPConnection.h>
+
+namespace android {
+
+ClearMediaHTTP::ClearMediaHTTP(const sp<MediaHTTPConnection> &conn)
+ : mInitCheck((conn != NULL) ? OK : NO_INIT),
+ mHTTPConnection(conn),
+ mCachedSizeValid(false),
+ mCachedSize(0ll) {
+}
+
+ClearMediaHTTP::~ClearMediaHTTP() {
+}
+
+status_t ClearMediaHTTP::connect(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t /* offset */) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ KeyedVector<String8, String8> extHeaders;
+ if (headers != NULL) {
+ extHeaders = *headers;
+ }
+
+ if (extHeaders.indexOfKey(String8("User-Agent")) < 0) {
+ extHeaders.add(String8("User-Agent"), String8(MakeUserAgent().c_str()));
+ }
+
+ mLastURI = uri;
+ // reconnect() calls with uri == old mLastURI.c_str(), which gets zapped
+ // as part of the above assignment. Ensure no accidental later use.
+ uri = NULL;
+
+ bool success = mHTTPConnection->connect(mLastURI.c_str(), &extHeaders);
+
+ mLastHeaders = extHeaders;
+
+ mCachedSizeValid = false;
+
+ if (success) {
+ AString sanitized = uriDebugString(mLastURI);
+ mName = String8::format("ClearMediaHTTP(%s)", sanitized.c_str());
+ }
+
+ return success ? OK : UNKNOWN_ERROR;
+}
+
+void ClearMediaHTTP::close() {
+ disconnect();
+}
+
+void ClearMediaHTTP::disconnect() {
+ mName = String8("ClearMediaHTTP(<disconnected>)");
+ if (mInitCheck != OK) {
+ return;
+ }
+
+ mHTTPConnection->disconnect();
+}
+
+status_t ClearMediaHTTP::initCheck() const {
+ return mInitCheck;
+}
+
+ssize_t ClearMediaHTTP::readAt(off64_t offset, void *data, size_t size) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ int64_t startTimeUs = ALooper::GetNowUs();
+
+ size_t numBytesRead = 0;
+ while (numBytesRead < size) {
+ size_t copy = size - numBytesRead;
+
+ if (copy > 64 * 1024) {
+ // limit the buffer sizes transferred across binder boundaries
+ // to avoid spurious transaction failures.
+ copy = 64 * 1024;
+ }
+
+ ssize_t n = mHTTPConnection->readAt(
+ offset + numBytesRead, (uint8_t *)data + numBytesRead, copy);
+
+ if (n < 0) {
+ return n;
+ } else if (n == 0) {
+ break;
+ }
+
+ numBytesRead += n;
+ }
+
+ int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
+
+ addBandwidthMeasurement(numBytesRead, delayUs);
+
+ return numBytesRead;
+}
+
+status_t ClearMediaHTTP::getSize(off64_t *size) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ // Caching the returned size so that it stays valid even after a
+ // disconnect. NuCachedSource2 relies on this.
+
+ if (!mCachedSizeValid) {
+ mCachedSize = mHTTPConnection->getSize();
+ mCachedSizeValid = true;
+ }
+
+ *size = mCachedSize;
+
+ return *size < 0 ? *size : static_cast<status_t>(OK);
+}
+
+uint32_t ClearMediaHTTP::flags() {
+ return kWantsPrefetching | kIsHTTPBasedSource;
+}
+
+status_t ClearMediaHTTP::reconnectAtOffset(off64_t offset) {
+ return connect(mLastURI.c_str(), &mLastHeaders, offset);
+}
+
+
+String8 ClearMediaHTTP::getUri() {
+ if (mInitCheck != OK) {
+ return String8::empty();
+ }
+
+ String8 uri;
+ if (OK == mHTTPConnection->getUri(&uri)) {
+ return uri;
+ }
+ return String8(mLastURI.c_str());
+}
+
+String8 ClearMediaHTTP::getMIMEType() const {
+ if (mInitCheck != OK) {
+ return String8("application/octet-stream");
+ }
+
+ String8 mimeType;
+ status_t err = mHTTPConnection->getMIMEType(&mimeType);
+
+ if (err != OK) {
+ return String8("application/octet-stream");
+ }
+
+ return mimeType;
+}
+
+} // namespace android
diff --git a/media/libdatasource/DataSourceFactory.cpp b/media/libdatasource/DataSourceFactory.cpp
new file mode 100644
index 0000000..8c772dd
--- /dev/null
+++ b/media/libdatasource/DataSourceFactory.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DataSource"
+
+
+#include <datasource/DataSourceFactory.h>
+#include <datasource/DataURISource.h>
+#include <datasource/HTTPBase.h>
+#include <datasource/FileSource.h>
+#include <datasource/MediaHTTP.h>
+#include <datasource/NuCachedSource2.h>
+#include <media/MediaHTTPConnection.h>
+#include <media/MediaHTTPService.h>
+#include <utils/String8.h>
+
+namespace android {
+
+// static
+sp<DataSource> DataSourceFactory::CreateFromURI(
+ const sp<MediaHTTPService> &httpService,
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ String8 *contentType,
+ HTTPBase *httpSource) {
+ if (contentType != NULL) {
+ *contentType = "";
+ }
+
+ sp<DataSource> source;
+ if (!strncasecmp("file://", uri, 7)) {
+ source = new FileSource(uri + 7);
+ } else if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {
+ if (httpService == NULL) {
+ ALOGE("Invalid http service!");
+ return NULL;
+ }
+
+ if (httpSource == NULL) {
+ sp<MediaHTTPConnection> conn = httpService->makeHTTPConnection();
+ if (conn == NULL) {
+ ALOGE("Failed to make http connection from http service!");
+ return NULL;
+ }
+ httpSource = new MediaHTTP(conn);
+ }
+
+ String8 cacheConfig;
+ bool disconnectAtHighwatermark = false;
+ KeyedVector<String8, String8> nonCacheSpecificHeaders;
+ if (headers != NULL) {
+ nonCacheSpecificHeaders = *headers;
+ NuCachedSource2::RemoveCacheSpecificHeaders(
+ &nonCacheSpecificHeaders,
+ &cacheConfig,
+ &disconnectAtHighwatermark);
+ }
+
+ if (httpSource->connect(uri, &nonCacheSpecificHeaders) != OK) {
+ ALOGE("Failed to connect http source!");
+ return NULL;
+ }
+
+ if (contentType != NULL) {
+ *contentType = httpSource->getMIMEType();
+ }
+
+ source = NuCachedSource2::Create(
+ httpSource,
+ cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
+ disconnectAtHighwatermark);
+ } else if (!strncasecmp("data:", uri, 5)) {
+ source = DataURISource::Create(uri);
+ } else {
+ // Assume it's a filename.
+ source = new FileSource(uri);
+ }
+
+ if (source == NULL || source->initCheck() != OK) {
+ return NULL;
+ }
+
+ return source;
+}
+
+sp<DataSource> DataSourceFactory::CreateFromFd(int fd, int64_t offset, int64_t length) {
+ sp<FileSource> source = new FileSource(fd, offset, length);
+ return source->initCheck() != OK ? nullptr : source;
+}
+
+sp<DataSource> DataSourceFactory::CreateMediaHTTP(const sp<MediaHTTPService> &httpService) {
+ if (httpService == NULL) {
+ return NULL;
+ }
+
+ sp<MediaHTTPConnection> conn = httpService->makeHTTPConnection();
+ if (conn == NULL) {
+ return NULL;
+ } else {
+ return new MediaHTTP(conn);
+ }
+}
+
+} // namespace android
diff --git a/media/libdatasource/DataURISource.cpp b/media/libdatasource/DataURISource.cpp
new file mode 100644
index 0000000..216f3d0
--- /dev/null
+++ b/media/libdatasource/DataURISource.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 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 <datasource/DataURISource.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+
+namespace android {
+
+// static
+sp<DataURISource> DataURISource::Create(const char *uri) {
+ if (strncasecmp("data:", uri, 5)) {
+ return NULL;
+ }
+
+ const char *commaPos = strrchr(uri, ',');
+
+ if (commaPos == NULL) {
+ return NULL;
+ }
+
+ sp<ABuffer> buffer;
+
+ AString tmp(&uri[5], commaPos - &uri[5]);
+
+ if (tmp.endsWith(";base64")) {
+
+ // strip all CR and LF characters.
+ const char *src = commaPos+1;
+ int len = strlen(src) + 1;
+ char *cleansed = (char *) malloc(len);
+ if (cleansed == NULL) return NULL;
+ char *keeping = cleansed;
+ int left = len;
+ for (int i = 0; i < len ; i++)
+ {
+ const char c = *src++;
+ if (c == '\r' || c == '\n') {
+ continue;
+ }
+ *keeping++ = c;
+ left--;
+ }
+ memset(keeping, 0, left);
+
+ AString encoded(cleansed);
+ free(cleansed);
+
+ buffer = decodeBase64(encoded);
+
+ if (buffer == NULL) {
+ ALOGE("Malformed base64 encoded content found.");
+ return NULL;
+ }
+ } else {
+#if 0
+ size_t dataLen = strlen(uri) - tmp.size() - 6;
+ buffer = new ABuffer(dataLen);
+ memcpy(buffer->data(), commaPos + 1, dataLen);
+
+ // unescape
+#else
+ // MediaPlayer doesn't care for this right now as we don't
+ // play any text-based media.
+ return NULL;
+#endif
+ }
+
+ // We don't really care about charset or mime type.
+
+ return new DataURISource(buffer);
+}
+
+DataURISource::DataURISource(const sp<ABuffer> &buffer)
+ : mBuffer(buffer) {
+}
+
+DataURISource::~DataURISource() {
+}
+
+status_t DataURISource::initCheck() const {
+ return OK;
+}
+
+ssize_t DataURISource::readAt(off64_t offset, void *data, size_t size) {
+ if ((offset < 0) || (offset >= (off64_t)mBuffer->size())) {
+ return 0;
+ }
+
+ size_t copy = mBuffer->size() - offset;
+ if (copy > size) {
+ copy = size;
+ }
+
+ memcpy(data, mBuffer->data() + offset, copy);
+
+ return copy;
+}
+
+status_t DataURISource::getSize(off64_t *size) {
+ *size = mBuffer->size();
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libdatasource/FileSource.cpp b/media/libdatasource/FileSource.cpp
new file mode 100644
index 0000000..65780e3
--- /dev/null
+++ b/media/libdatasource/FileSource.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "FileSource"
+#include <utils/Log.h>
+
+#include <datasource/FileSource.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <private/android_filesystem_config.h>
+
+namespace android {
+
+FileSource::FileSource(const char *filename)
+ : ClearFileSource(filename),
+ mDecryptHandle(NULL),
+ mDrmManagerClient(NULL),
+ mDrmBufOffset(0),
+ mDrmBufSize(0),
+ mDrmBuf(NULL){
+}
+
+FileSource::FileSource(int fd, int64_t offset, int64_t length)
+ : ClearFileSource(fd, offset, length),
+ mDecryptHandle(NULL),
+ mDrmManagerClient(NULL),
+ mDrmBufOffset(0),
+ mDrmBufSize(0),
+ mDrmBuf(NULL) {
+}
+
+FileSource::~FileSource() {
+ if (mDrmBuf != NULL) {
+ delete[] mDrmBuf;
+ mDrmBuf = NULL;
+ }
+
+ if (mDecryptHandle != NULL) {
+ // To release mDecryptHandle
+ CHECK(mDrmManagerClient);
+ mDrmManagerClient->closeDecryptSession(mDecryptHandle);
+ mDecryptHandle = NULL;
+ }
+
+ if (mDrmManagerClient != NULL) {
+ delete mDrmManagerClient;
+ mDrmManagerClient = NULL;
+ }
+}
+
+ssize_t FileSource::readAt(off64_t offset, void *data, size_t size) {
+ if (mFd < 0) {
+ return NO_INIT;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mLength >= 0) {
+ if (offset >= mLength) {
+ return 0; // read beyond EOF.
+ }
+ uint64_t numAvailable = mLength - offset;
+ if ((uint64_t)size > numAvailable) {
+ size = numAvailable;
+ }
+ }
+
+ if (mDecryptHandle != NULL && DecryptApiType::CONTAINER_BASED
+ == mDecryptHandle->decryptApiType) {
+ return readAtDRM_l(offset, data, size);
+ } else {
+ return readAt_l(offset, data, size);
+ }
+}
+
+sp<DecryptHandle> FileSource::DrmInitialization(const char *mime) {
+ if (getuid() == AID_MEDIA_EX) return nullptr; // no DRM in media extractor
+ if (mDrmManagerClient == NULL) {
+ mDrmManagerClient = new DrmManagerClient();
+ }
+
+ if (mDrmManagerClient == NULL) {
+ return NULL;
+ }
+
+ if (mDecryptHandle == NULL) {
+ mDecryptHandle = mDrmManagerClient->openDecryptSession(
+ mFd, mOffset, mLength, mime);
+ }
+
+ if (mDecryptHandle == NULL) {
+ delete mDrmManagerClient;
+ mDrmManagerClient = NULL;
+ }
+
+ return mDecryptHandle;
+}
+
+ssize_t FileSource::readAtDRM_l(off64_t offset, void *data, size_t size) {
+ size_t DRM_CACHE_SIZE = 1024;
+ if (mDrmBuf == NULL) {
+ mDrmBuf = new unsigned char[DRM_CACHE_SIZE];
+ }
+
+ if (mDrmBuf != NULL && mDrmBufSize > 0 && (offset + mOffset) >= mDrmBufOffset
+ && (offset + mOffset + size) <= static_cast<size_t>(mDrmBufOffset + mDrmBufSize)) {
+ /* Use buffered data */
+ memcpy(data, (void*)(mDrmBuf+(offset+mOffset-mDrmBufOffset)), size);
+ return size;
+ } else if (size <= DRM_CACHE_SIZE) {
+ /* Buffer new data */
+ mDrmBufOffset = offset + mOffset;
+ mDrmBufSize = mDrmManagerClient->pread(mDecryptHandle, mDrmBuf,
+ DRM_CACHE_SIZE, offset + mOffset);
+ if (mDrmBufSize > 0) {
+ int64_t dataRead = 0;
+ dataRead = size > static_cast<size_t>(mDrmBufSize) ? mDrmBufSize : size;
+ memcpy(data, (void*)mDrmBuf, dataRead);
+ return dataRead;
+ } else {
+ return mDrmBufSize;
+ }
+ } else {
+ /* Too big chunk to cache. Call DRM directly */
+ return mDrmManagerClient->pread(mDecryptHandle, data, size, offset + mOffset);
+ }
+}
+
+/* static */
+bool FileSource::requiresDrm(int fd, int64_t offset, int64_t length, const char *mime) {
+ std::unique_ptr<DrmManagerClient> drmClient(new DrmManagerClient());
+ sp<DecryptHandle> decryptHandle =
+ drmClient->openDecryptSession(fd, offset, length, mime);
+ bool requiresDrm = false;
+ if (decryptHandle != nullptr) {
+ requiresDrm = decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED;
+ drmClient->closeDecryptSession(decryptHandle);
+ }
+ return requiresDrm;
+}
+
+} // namespace android
diff --git a/media/libdatasource/HTTPBase.cpp b/media/libdatasource/HTTPBase.cpp
new file mode 100644
index 0000000..ef29c48
--- /dev/null
+++ b/media/libdatasource/HTTPBase.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "HTTPBase"
+#include <utils/Log.h>
+
+#include <datasource/HTTPBase.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+
+#include <cutils/properties.h>
+#include <cutils/qtaguid.h>
+
+namespace android {
+
+HTTPBase::HTTPBase()
+ : mNumBandwidthHistoryItems(0),
+ mTotalTransferTimeUs(0),
+ mTotalTransferBytes(0),
+ mMaxBandwidthHistoryItems(100),
+ mPrevBandwidthMeasureTimeUs(0),
+ mPrevEstimatedBandWidthKbps(0),
+ mBandWidthCollectFreqMs(5000) {
+ mName = String8("HTTPBase(<disconnected>)");
+}
+
+void HTTPBase::addBandwidthMeasurement(
+ size_t numBytes, int64_t delayUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ BandwidthEntry entry;
+ entry.mDelayUs = delayUs;
+ entry.mNumBytes = numBytes;
+ mTotalTransferTimeUs += delayUs;
+ mTotalTransferBytes += numBytes;
+
+ mBandwidthHistory.push_back(entry);
+ if (++mNumBandwidthHistoryItems > mMaxBandwidthHistoryItems) {
+ BandwidthEntry *entry = &*mBandwidthHistory.begin();
+ mTotalTransferTimeUs -= entry->mDelayUs;
+ mTotalTransferBytes -= entry->mNumBytes;
+ mBandwidthHistory.erase(mBandwidthHistory.begin());
+ --mNumBandwidthHistoryItems;
+
+ int64_t timeNowUs = ALooper::GetNowUs();
+ if (timeNowUs - mPrevBandwidthMeasureTimeUs >=
+ mBandWidthCollectFreqMs * 1000LL) {
+
+ if (mPrevBandwidthMeasureTimeUs != 0) {
+ mPrevEstimatedBandWidthKbps =
+ (mTotalTransferBytes * 8E3 / mTotalTransferTimeUs);
+ }
+ mPrevBandwidthMeasureTimeUs = timeNowUs;
+ }
+ }
+
+}
+
+bool HTTPBase::estimateBandwidth(int32_t *bandwidth_bps) {
+ Mutex::Autolock autoLock(mLock);
+
+ // Do not do bandwidth estimation if we don't have enough samples, or
+ // total bytes download are too small (<64K).
+ // Bandwidth estimation from these samples can often shoot up and cause
+ // unwanted bw adaption behaviors.
+ if (mNumBandwidthHistoryItems < 2 || mTotalTransferBytes < 65536) {
+ return false;
+ }
+
+ *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
+
+ return true;
+}
+
+status_t HTTPBase::getEstimatedBandwidthKbps(int32_t *kbps) {
+ Mutex::Autolock autoLock(mLock);
+ *kbps = mPrevEstimatedBandWidthKbps;
+ return OK;
+}
+
+status_t HTTPBase::setBandwidthStatCollectFreq(int32_t freqMs) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (freqMs < kMinBandwidthCollectFreqMs
+ || freqMs > kMaxBandwidthCollectFreqMs) {
+
+ ALOGE("frequency (%d ms) is out of range [1000, 60000]", freqMs);
+ return BAD_VALUE;
+ }
+
+ ALOGI("frequency set to %d ms", freqMs);
+ mBandWidthCollectFreqMs = freqMs;
+ return OK;
+}
+
+void HTTPBase::setBandwidthHistorySize(size_t numHistoryItems) {
+ mMaxBandwidthHistoryItems = numHistoryItems;
+}
+
+} // namespace android
diff --git a/media/libdatasource/MediaHTTP.cpp b/media/libdatasource/MediaHTTP.cpp
new file mode 100644
index 0000000..e57510d
--- /dev/null
+++ b/media/libdatasource/MediaHTTP.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaHTTP"
+#include <utils/Log.h>
+
+#include <datasource/MediaHTTP.h>
+
+#include <binder/IServiceManager.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/FoundationUtils.h>
+
+#include <media/MediaHTTPConnection.h>
+
+namespace android {
+
+MediaHTTP::MediaHTTP(const sp<MediaHTTPConnection> &conn)
+ : ClearMediaHTTP(conn),
+ mDrmManagerClient(NULL) {
+}
+
+MediaHTTP::~MediaHTTP() {
+ clearDRMState_l();
+}
+
+// DRM...
+
+sp<DecryptHandle> MediaHTTP::DrmInitialization(const char* mime) {
+ if (mDrmManagerClient == NULL) {
+ mDrmManagerClient = new DrmManagerClient();
+ }
+
+ if (mDrmManagerClient == NULL) {
+ return NULL;
+ }
+
+ if (mDecryptHandle == NULL) {
+ mDecryptHandle = mDrmManagerClient->openDecryptSession(
+ String8(mLastURI.c_str()), mime);
+ }
+
+ if (mDecryptHandle == NULL) {
+ delete mDrmManagerClient;
+ mDrmManagerClient = NULL;
+ }
+
+ return mDecryptHandle;
+}
+
+void MediaHTTP::clearDRMState_l() {
+ if (mDecryptHandle != NULL) {
+ // To release mDecryptHandle
+ CHECK(mDrmManagerClient);
+ mDrmManagerClient->closeDecryptSession(mDecryptHandle);
+ mDecryptHandle = NULL;
+ }
+}
+
+} // namespace android
diff --git a/media/libdatasource/NuCachedSource2.cpp b/media/libdatasource/NuCachedSource2.cpp
new file mode 100644
index 0000000..7f5ae61
--- /dev/null
+++ b/media/libdatasource/NuCachedSource2.cpp
@@ -0,0 +1,784 @@
+/*
+ * Copyright (C) 2010 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 <inttypes.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuCachedSource2"
+#include <utils/Log.h>
+
+#include <datasource/NuCachedSource2.h>
+#include <datasource/HTTPBase.h>
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+struct PageCache {
+ explicit PageCache(size_t pageSize);
+ ~PageCache();
+
+ struct Page {
+ void *mData;
+ size_t mSize;
+ };
+
+ Page *acquirePage();
+ void releasePage(Page *page);
+
+ void appendPage(Page *page);
+ size_t releaseFromStart(size_t maxBytes);
+
+ size_t totalSize() const {
+ return mTotalSize;
+ }
+
+ void copy(size_t from, void *data, size_t size);
+
+private:
+ size_t mPageSize;
+ size_t mTotalSize;
+
+ List<Page *> mActivePages;
+ List<Page *> mFreePages;
+
+ void freePages(List<Page *> *list);
+
+ DISALLOW_EVIL_CONSTRUCTORS(PageCache);
+};
+
+PageCache::PageCache(size_t pageSize)
+ : mPageSize(pageSize),
+ mTotalSize(0) {
+}
+
+PageCache::~PageCache() {
+ freePages(&mActivePages);
+ freePages(&mFreePages);
+}
+
+void PageCache::freePages(List<Page *> *list) {
+ List<Page *>::iterator it = list->begin();
+ while (it != list->end()) {
+ Page *page = *it;
+
+ free(page->mData);
+ delete page;
+ page = NULL;
+
+ ++it;
+ }
+}
+
+PageCache::Page *PageCache::acquirePage() {
+ if (!mFreePages.empty()) {
+ List<Page *>::iterator it = mFreePages.begin();
+ Page *page = *it;
+ mFreePages.erase(it);
+
+ return page;
+ }
+
+ Page *page = new Page;
+ page->mData = malloc(mPageSize);
+ page->mSize = 0;
+
+ return page;
+}
+
+void PageCache::releasePage(Page *page) {
+ page->mSize = 0;
+ mFreePages.push_back(page);
+}
+
+void PageCache::appendPage(Page *page) {
+ mTotalSize += page->mSize;
+ mActivePages.push_back(page);
+}
+
+size_t PageCache::releaseFromStart(size_t maxBytes) {
+ size_t bytesReleased = 0;
+
+ while (maxBytes > 0 && !mActivePages.empty()) {
+ List<Page *>::iterator it = mActivePages.begin();
+
+ Page *page = *it;
+
+ if (maxBytes < page->mSize) {
+ break;
+ }
+
+ mActivePages.erase(it);
+
+ maxBytes -= page->mSize;
+ bytesReleased += page->mSize;
+
+ releasePage(page);
+ }
+
+ mTotalSize -= bytesReleased;
+ return bytesReleased;
+}
+
+void PageCache::copy(size_t from, void *data, size_t size) {
+ ALOGV("copy from %zu size %zu", from, size);
+
+ if (size == 0) {
+ return;
+ }
+
+ CHECK_LE(from + size, mTotalSize);
+
+ size_t offset = 0;
+ List<Page *>::iterator it = mActivePages.begin();
+ while (from >= offset + (*it)->mSize) {
+ offset += (*it)->mSize;
+ ++it;
+ }
+
+ size_t delta = from - offset;
+ size_t avail = (*it)->mSize - delta;
+
+ if (avail >= size) {
+ memcpy(data, (const uint8_t *)(*it)->mData + delta, size);
+ return;
+ }
+
+ memcpy(data, (const uint8_t *)(*it)->mData + delta, avail);
+ ++it;
+ data = (uint8_t *)data + avail;
+ size -= avail;
+
+ while (size > 0) {
+ size_t copy = (*it)->mSize;
+ if (copy > size) {
+ copy = size;
+ }
+ memcpy(data, (*it)->mData, copy);
+ data = (uint8_t *)data + copy;
+ size -= copy;
+ ++it;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+NuCachedSource2::NuCachedSource2(
+ const sp<DataSource> &source,
+ const char *cacheConfig,
+ bool disconnectAtHighwatermark)
+ : mSource(source),
+ mReflector(new AHandlerReflector<NuCachedSource2>(this)),
+ mLooper(new ALooper),
+ mCache(new PageCache(kPageSize)),
+ mCacheOffset(0),
+ mFinalStatus(OK),
+ mLastAccessPos(0),
+ mFetching(true),
+ mDisconnecting(false),
+ mLastFetchTimeUs(-1),
+ mNumRetriesLeft(kMaxNumRetries),
+ mHighwaterThresholdBytes(kDefaultHighWaterThreshold),
+ mLowwaterThresholdBytes(kDefaultLowWaterThreshold),
+ mKeepAliveIntervalUs(kDefaultKeepAliveIntervalUs),
+ mDisconnectAtHighwatermark(disconnectAtHighwatermark) {
+ // We are NOT going to support disconnect-at-highwatermark indefinitely
+ // and we are not guaranteeing support for client-specified cache
+ // parameters. Both of these are temporary measures to solve a specific
+ // problem that will be solved in a better way going forward.
+
+ updateCacheParamsFromSystemProperty();
+
+ if (cacheConfig != NULL) {
+ updateCacheParamsFromString(cacheConfig);
+ }
+
+ if (mDisconnectAtHighwatermark) {
+ // Makes no sense to disconnect and do keep-alives...
+ mKeepAliveIntervalUs = 0;
+ }
+
+ mLooper->setName("NuCachedSource2");
+ mLooper->registerHandler(mReflector);
+
+ // Since it may not be obvious why our looper thread needs to be
+ // able to call into java since it doesn't appear to do so at all...
+ // IMediaHTTPConnection may be (and most likely is) implemented in JAVA
+ // and a local JAVA IBinder will call directly into JNI methods.
+ // So whenever we call DataSource::readAt it may end up in a call to
+ // IMediaHTTPConnection::readAt and therefore call back into JAVA.
+ mLooper->start(false /* runOnCallingThread */, true /* canCallJava */);
+
+ mName = String8::format("NuCachedSource2(%s)", mSource->toString().string());
+}
+
+NuCachedSource2::~NuCachedSource2() {
+ mLooper->stop();
+ mLooper->unregisterHandler(mReflector->id());
+
+ delete mCache;
+ mCache = NULL;
+}
+
+// static
+sp<NuCachedSource2> NuCachedSource2::Create(
+ const sp<DataSource> &source,
+ const char *cacheConfig,
+ bool disconnectAtHighwatermark) {
+ sp<NuCachedSource2> instance = new NuCachedSource2(
+ source, cacheConfig, disconnectAtHighwatermark);
+ Mutex::Autolock autoLock(instance->mLock);
+ (new AMessage(kWhatFetchMore, instance->mReflector))->post();
+ return instance;
+}
+
+status_t NuCachedSource2::getEstimatedBandwidthKbps(int32_t *kbps) {
+ if (mSource->flags() & kIsHTTPBasedSource) {
+ HTTPBase* source = static_cast<HTTPBase *>(mSource.get());
+ return source->getEstimatedBandwidthKbps(kbps);
+ }
+ return ERROR_UNSUPPORTED;
+}
+
+void NuCachedSource2::close() {
+ disconnect();
+}
+
+void NuCachedSource2::disconnect() {
+ if (mSource->flags() & kIsHTTPBasedSource) {
+ ALOGV("disconnecting HTTPBasedSource");
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ // set mDisconnecting to true, if a fetch returns after
+ // this, the source will be marked as EOS.
+ mDisconnecting = true;
+
+ // explicitly signal mCondition so that the pending readAt()
+ // will immediately return
+ mCondition.signal();
+ }
+
+ // explicitly disconnect from the source, to allow any
+ // pending reads to return more promptly
+ static_cast<HTTPBase *>(mSource.get())->disconnect();
+ }
+}
+
+status_t NuCachedSource2::setCacheStatCollectFreq(int32_t freqMs) {
+ if (mSource->flags() & kIsHTTPBasedSource) {
+ HTTPBase *source = static_cast<HTTPBase *>(mSource.get());
+ return source->setBandwidthStatCollectFreq(freqMs);
+ }
+ return ERROR_UNSUPPORTED;
+}
+
+status_t NuCachedSource2::initCheck() const {
+ return mSource->initCheck();
+}
+
+status_t NuCachedSource2::getSize(off64_t *size) {
+ return mSource->getSize(size);
+}
+
+uint32_t NuCachedSource2::flags() {
+ // Remove HTTP related flags since NuCachedSource2 is not HTTP-based.
+ uint32_t flags = mSource->flags() & ~(kWantsPrefetching | kIsHTTPBasedSource);
+ return (flags | kIsCachingDataSource);
+}
+
+void NuCachedSource2::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatFetchMore:
+ {
+ onFetch();
+ break;
+ }
+
+ case kWhatRead:
+ {
+ onRead(msg);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+void NuCachedSource2::fetchInternal() {
+ ALOGV("fetchInternal");
+
+ bool reconnect = false;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ CHECK(mFinalStatus == OK || mNumRetriesLeft > 0);
+
+ if (mFinalStatus != OK) {
+ --mNumRetriesLeft;
+
+ reconnect = true;
+ }
+ }
+
+ if (reconnect) {
+ status_t err =
+ mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize());
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mDisconnecting) {
+ mNumRetriesLeft = 0;
+ mFinalStatus = ERROR_END_OF_STREAM;
+ return;
+ } else if (err == ERROR_UNSUPPORTED || err == -EPIPE) {
+ // These are errors that are not likely to go away even if we
+ // retry, i.e. the server doesn't support range requests or similar.
+ mNumRetriesLeft = 0;
+ return;
+ } else if (err != OK) {
+ ALOGI("The attempt to reconnect failed, %d retries remaining",
+ mNumRetriesLeft);
+
+ return;
+ }
+ }
+
+ PageCache::Page *page = mCache->acquirePage();
+
+ ssize_t n = mSource->readAt(
+ mCacheOffset + mCache->totalSize(), page->mData, kPageSize);
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (n == 0 || mDisconnecting) {
+ ALOGI("caching reached eos.");
+
+ mNumRetriesLeft = 0;
+ mFinalStatus = ERROR_END_OF_STREAM;
+
+ mCache->releasePage(page);
+ } else if (n < 0) {
+ mFinalStatus = n;
+ if (n == ERROR_UNSUPPORTED || n == -EPIPE) {
+ // These are errors that are not likely to go away even if we
+ // retry, i.e. the server doesn't support range requests or similar.
+ mNumRetriesLeft = 0;
+ }
+
+ ALOGE("source returned error %zd, %d retries left", n, mNumRetriesLeft);
+ mCache->releasePage(page);
+ } else {
+ if (mFinalStatus != OK) {
+ ALOGI("retrying a previously failed read succeeded.");
+ }
+ mNumRetriesLeft = kMaxNumRetries;
+ mFinalStatus = OK;
+
+ page->mSize = n;
+ mCache->appendPage(page);
+ }
+}
+
+void NuCachedSource2::onFetch() {
+ ALOGV("onFetch");
+
+ if (mFinalStatus != OK && mNumRetriesLeft == 0) {
+ ALOGV("EOS reached, done prefetching for now");
+ mFetching = false;
+ }
+
+ bool keepAlive =
+ !mFetching
+ && mFinalStatus == OK
+ && mKeepAliveIntervalUs > 0
+ && ALooper::GetNowUs() >= mLastFetchTimeUs + mKeepAliveIntervalUs;
+
+ if (mFetching || keepAlive) {
+ if (keepAlive) {
+ ALOGI("Keep alive");
+ }
+
+ fetchInternal();
+
+ mLastFetchTimeUs = ALooper::GetNowUs();
+
+ if (mFetching && mCache->totalSize() >= mHighwaterThresholdBytes) {
+ ALOGI("Cache full, done prefetching for now");
+ mFetching = false;
+
+ if (mDisconnectAtHighwatermark
+ && (mSource->flags() & DataSource::kIsHTTPBasedSource)) {
+ ALOGV("Disconnecting at high watermark");
+ static_cast<HTTPBase *>(mSource.get())->disconnect();
+ mFinalStatus = -EAGAIN;
+ }
+ }
+ } else {
+ Mutex::Autolock autoLock(mLock);
+ restartPrefetcherIfNecessary_l();
+ }
+
+ int64_t delayUs;
+ if (mFetching) {
+ if (mFinalStatus != OK && mNumRetriesLeft > 0) {
+ // We failed this time and will try again in 3 seconds.
+ delayUs = 3000000LL;
+ } else {
+ delayUs = 0;
+ }
+ } else {
+ delayUs = 100000LL;
+ }
+
+ (new AMessage(kWhatFetchMore, mReflector))->post(delayUs);
+}
+
+void NuCachedSource2::onRead(const sp<AMessage> &msg) {
+ ALOGV("onRead");
+
+ int64_t offset;
+ CHECK(msg->findInt64("offset", &offset));
+
+ void *data;
+ CHECK(msg->findPointer("data", &data));
+
+ size_t size;
+ CHECK(msg->findSize("size", &size));
+
+ ssize_t result = readInternal(offset, data, size);
+
+ if (result == -EAGAIN) {
+ msg->post(50000);
+ return;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ if (mDisconnecting) {
+ mCondition.signal();
+ return;
+ }
+
+ CHECK(mAsyncResult == NULL);
+
+ mAsyncResult = new AMessage;
+ mAsyncResult->setInt32("result", result);
+
+ mCondition.signal();
+}
+
+void NuCachedSource2::restartPrefetcherIfNecessary_l(
+ bool ignoreLowWaterThreshold, bool force) {
+ static const size_t kGrayArea = 1024 * 1024;
+
+ if (mFetching || (mFinalStatus != OK && mNumRetriesLeft == 0)) {
+ return;
+ }
+
+ if (!ignoreLowWaterThreshold && !force
+ && mCacheOffset + mCache->totalSize() - mLastAccessPos
+ >= mLowwaterThresholdBytes) {
+ return;
+ }
+
+ size_t maxBytes = mLastAccessPos - mCacheOffset;
+
+ if (!force) {
+ if (maxBytes < kGrayArea) {
+ return;
+ }
+
+ maxBytes -= kGrayArea;
+ }
+
+ size_t actualBytes = mCache->releaseFromStart(maxBytes);
+ mCacheOffset += actualBytes;
+
+ ALOGI("restarting prefetcher, totalSize = %zu", mCache->totalSize());
+ mFetching = true;
+}
+
+ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
+ Mutex::Autolock autoSerializer(mSerializer);
+
+ ALOGV("readAt offset %lld, size %zu", (long long)offset, size);
+
+ Mutex::Autolock autoLock(mLock);
+ if (mDisconnecting) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ // If the request can be completely satisfied from the cache, do so.
+
+ if (offset >= mCacheOffset
+ && offset + size <= mCacheOffset + mCache->totalSize()) {
+ size_t delta = offset - mCacheOffset;
+ mCache->copy(delta, data, size);
+
+ mLastAccessPos = offset + size;
+
+ return size;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatRead, mReflector);
+ msg->setInt64("offset", offset);
+ msg->setPointer("data", data);
+ msg->setSize("size", size);
+
+ CHECK(mAsyncResult == NULL);
+ msg->post();
+
+ while (mAsyncResult == NULL && !mDisconnecting) {
+ mCondition.wait(mLock);
+ }
+
+ if (mDisconnecting) {
+ mAsyncResult.clear();
+ return ERROR_END_OF_STREAM;
+ }
+
+ int32_t result;
+ CHECK(mAsyncResult->findInt32("result", &result));
+
+ mAsyncResult.clear();
+
+ if (result > 0) {
+ mLastAccessPos = offset + result;
+ }
+
+ return (ssize_t)result;
+}
+
+size_t NuCachedSource2::cachedSize() {
+ Mutex::Autolock autoLock(mLock);
+ return mCacheOffset + mCache->totalSize();
+}
+
+status_t NuCachedSource2::getAvailableSize(off64_t offset, off64_t *size) {
+ Mutex::Autolock autoLock(mLock);
+ status_t finalStatus = UNKNOWN_ERROR;
+ *size = approxDataRemaining_l(offset, &finalStatus);
+ return finalStatus;
+}
+
+size_t NuCachedSource2::approxDataRemaining(status_t *finalStatus) const {
+ Mutex::Autolock autoLock(mLock);
+ return approxDataRemaining_l(mLastAccessPos, finalStatus);
+}
+
+size_t NuCachedSource2::approxDataRemaining_l(off64_t offset, status_t *finalStatus) const {
+ *finalStatus = mFinalStatus;
+
+ if (mFinalStatus != OK && mNumRetriesLeft > 0) {
+ // Pretend that everything is fine until we're out of retries.
+ *finalStatus = OK;
+ }
+
+ offset = offset >= 0 ? offset : mLastAccessPos;
+ off64_t lastBytePosCached = mCacheOffset + mCache->totalSize();
+ if (offset < lastBytePosCached) {
+ return lastBytePosCached - offset;
+ }
+ return 0;
+}
+
+ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
+ CHECK_LE(size, (size_t)mHighwaterThresholdBytes);
+
+ ALOGV("readInternal offset %lld size %zu", (long long)offset, size);
+
+ Mutex::Autolock autoLock(mLock);
+
+ // If we're disconnecting, return EOS and don't access *data pointer.
+ // data could be on the stack of the caller to NuCachedSource2::readAt(),
+ // which may have exited already.
+ if (mDisconnecting) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ if (!mFetching) {
+ mLastAccessPos = offset;
+ restartPrefetcherIfNecessary_l(
+ false, // ignoreLowWaterThreshold
+ true); // force
+ }
+
+ if (offset < mCacheOffset
+ || offset >= (off64_t)(mCacheOffset + mCache->totalSize())) {
+ static const off64_t kPadding = 256 * 1024;
+
+ // In the presence of multiple decoded streams, once of them will
+ // trigger this seek request, the other one will request data "nearby"
+ // soon, adjust the seek position so that that subsequent request
+ // does not trigger another seek.
+ off64_t seekOffset = (offset > kPadding) ? offset - kPadding : 0;
+
+ seekInternal_l(seekOffset);
+ }
+
+ size_t delta = offset - mCacheOffset;
+
+ if (mFinalStatus != OK && mNumRetriesLeft == 0) {
+ if (delta >= mCache->totalSize()) {
+ return mFinalStatus;
+ }
+
+ size_t avail = mCache->totalSize() - delta;
+
+ if (avail > size) {
+ avail = size;
+ }
+
+ mCache->copy(delta, data, avail);
+
+ return avail;
+ }
+
+ if (offset + size <= mCacheOffset + mCache->totalSize()) {
+ mCache->copy(delta, data, size);
+
+ return size;
+ }
+
+ ALOGV("deferring read");
+
+ return -EAGAIN;
+}
+
+status_t NuCachedSource2::seekInternal_l(off64_t offset) {
+ mLastAccessPos = offset;
+
+ if (offset >= mCacheOffset
+ && offset <= (off64_t)(mCacheOffset + mCache->totalSize())) {
+ return OK;
+ }
+
+ ALOGI("new range: offset= %lld", (long long)offset);
+
+ mCacheOffset = offset;
+
+ size_t totalSize = mCache->totalSize();
+ CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
+
+ mNumRetriesLeft = kMaxNumRetries;
+ mFetching = true;
+
+ return OK;
+}
+
+void NuCachedSource2::resumeFetchingIfNecessary() {
+ Mutex::Autolock autoLock(mLock);
+
+ restartPrefetcherIfNecessary_l(true /* ignore low water threshold */);
+}
+
+sp<DecryptHandle> NuCachedSource2::DrmInitialization(const char* mime) {
+ return mSource->DrmInitialization(mime);
+}
+
+String8 NuCachedSource2::getUri() {
+ return mSource->getUri();
+}
+
+String8 NuCachedSource2::getMIMEType() const {
+ return mSource->getMIMEType();
+}
+
+void NuCachedSource2::updateCacheParamsFromSystemProperty() {
+ char value[PROPERTY_VALUE_MAX];
+ if (!property_get("media.stagefright.cache-params", value, NULL)) {
+ return;
+ }
+
+ updateCacheParamsFromString(value);
+}
+
+void NuCachedSource2::updateCacheParamsFromString(const char *s) {
+ ssize_t lowwaterMarkKb, highwaterMarkKb;
+ int keepAliveSecs;
+
+ if (sscanf(s, "%zd/%zd/%d",
+ &lowwaterMarkKb, &highwaterMarkKb, &keepAliveSecs) != 3) {
+ ALOGE("Failed to parse cache parameters from '%s'.", s);
+ return;
+ }
+
+ if (lowwaterMarkKb >= 0) {
+ mLowwaterThresholdBytes = lowwaterMarkKb * 1024;
+ } else {
+ mLowwaterThresholdBytes = kDefaultLowWaterThreshold;
+ }
+
+ if (highwaterMarkKb >= 0) {
+ mHighwaterThresholdBytes = highwaterMarkKb * 1024;
+ } else {
+ mHighwaterThresholdBytes = kDefaultHighWaterThreshold;
+ }
+
+ if (mLowwaterThresholdBytes >= mHighwaterThresholdBytes) {
+ ALOGE("Illegal low/highwater marks specified, reverting to defaults.");
+
+ mLowwaterThresholdBytes = kDefaultLowWaterThreshold;
+ mHighwaterThresholdBytes = kDefaultHighWaterThreshold;
+ }
+
+ if (keepAliveSecs >= 0) {
+ mKeepAliveIntervalUs = keepAliveSecs * 1000000LL;
+ } else {
+ mKeepAliveIntervalUs = kDefaultKeepAliveIntervalUs;
+ }
+
+ ALOGV("lowwater = %zu bytes, highwater = %zu bytes, keepalive = %lld us",
+ mLowwaterThresholdBytes,
+ mHighwaterThresholdBytes,
+ (long long)mKeepAliveIntervalUs);
+}
+
+// static
+void NuCachedSource2::RemoveCacheSpecificHeaders(
+ KeyedVector<String8, String8> *headers,
+ String8 *cacheConfig,
+ bool *disconnectAtHighwatermark) {
+ *cacheConfig = String8();
+ *disconnectAtHighwatermark = false;
+
+ if (headers == NULL) {
+ return;
+ }
+
+ ssize_t index;
+ if ((index = headers->indexOfKey(String8("x-cache-config"))) >= 0) {
+ *cacheConfig = headers->valueAt(index);
+
+ headers->removeItemsAt(index);
+
+ ALOGV("Using special cache config '%s'", cacheConfig->string());
+ }
+
+ if ((index = headers->indexOfKey(
+ String8("x-disconnect-at-highwatermark"))) >= 0) {
+ *disconnectAtHighwatermark = true;
+ headers->removeItemsAt(index);
+
+ ALOGV("Client requested disconnection at highwater mark");
+ }
+}
+
+} // namespace android
diff --git a/media/libdatasource/include/datasource/ClearFileSource.h b/media/libdatasource/include/datasource/ClearFileSource.h
new file mode 100644
index 0000000..be83748
--- /dev/null
+++ b/media/libdatasource/include/datasource/ClearFileSource.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef CLEAR_FILE_SOURCE_H_
+
+#define CLEAR_FILE_SOURCE_H_
+
+#include <stdio.h>
+
+#include <media/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class ClearFileSource : public DataSource {
+public:
+ ClearFileSource(const char *filename);
+ // ClearFileSource takes ownership and will close the fd
+ ClearFileSource(int fd, int64_t offset, int64_t length);
+
+ virtual status_t initCheck() const;
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+
+ virtual status_t getSize(off64_t *size);
+
+ virtual uint32_t flags() {
+ return kIsLocalFileSource;
+ }
+
+ virtual String8 toString() {
+ return mName;
+ }
+
+protected:
+ virtual ~ClearFileSource();
+ virtual ssize_t readAt_l(off64_t offset, void *data, size_t size);
+
+ int mFd;
+ int64_t mOffset;
+ int64_t mLength;
+ Mutex mLock;
+
+private:
+ String8 mName;
+
+ ClearFileSource(const ClearFileSource &);
+ ClearFileSource &operator=(const ClearFileSource &);
+};
+
+} // namespace android
+
+#endif // CLEAR_FILE_SOURCE_H_
+
diff --git a/media/libdatasource/include/datasource/ClearMediaHTTP.h b/media/libdatasource/include/datasource/ClearMediaHTTP.h
new file mode 100644
index 0000000..5440a3a
--- /dev/null
+++ b/media/libdatasource/include/datasource/ClearMediaHTTP.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef CLEAR_MEDIA_HTTP_H_
+
+#define CLEAR_MEDIA_HTTP_H_
+
+#include <media/stagefright/foundation/AString.h>
+
+#include "HTTPBase.h"
+
+namespace android {
+
+struct MediaHTTPConnection;
+
+struct ClearMediaHTTP : public HTTPBase {
+ ClearMediaHTTP(const sp<MediaHTTPConnection> &conn);
+
+ virtual status_t connect(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers,
+ off64_t offset);
+
+ virtual void close();
+
+ virtual void disconnect();
+
+ virtual status_t initCheck() const;
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+
+ virtual status_t getSize(off64_t *size);
+
+ virtual uint32_t flags();
+
+ virtual status_t reconnectAtOffset(off64_t offset);
+
+protected:
+ virtual ~ClearMediaHTTP();
+
+ virtual String8 getUri();
+ virtual String8 getMIMEType() const;
+
+ AString mLastURI;
+
+private:
+ status_t mInitCheck;
+ sp<MediaHTTPConnection> mHTTPConnection;
+
+ KeyedVector<String8, String8> mLastHeaders;
+
+ bool mCachedSizeValid;
+ off64_t mCachedSize;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ClearMediaHTTP);
+};
+
+} // namespace android
+
+#endif // CLEAR_MEDIA_HTTP_H_
diff --git a/media/libdatasource/include/datasource/DataSourceFactory.h b/media/libdatasource/include/datasource/DataSourceFactory.h
new file mode 100644
index 0000000..6e313d3
--- /dev/null
+++ b/media/libdatasource/include/datasource/DataSourceFactory.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef DATA_SOURCE_FACTORY_H_
+
+#define DATA_SOURCE_FACTORY_H_
+
+#include <media/DataSource.h>
+#include <sys/types.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct MediaHTTPService;
+class String8;
+struct HTTPBase;
+
+class DataSourceFactory {
+public:
+ static sp<DataSource> CreateFromURI(
+ const sp<MediaHTTPService> &httpService,
+ const char *uri,
+ const KeyedVector<String8, String8> *headers = NULL,
+ String8 *contentType = NULL,
+ HTTPBase *httpSource = NULL);
+
+ static sp<DataSource> CreateMediaHTTP(const sp<MediaHTTPService> &httpService);
+ static sp<DataSource> CreateFromFd(int fd, int64_t offset, int64_t length);
+};
+
+} // namespace android
+
+#endif // DATA_SOURCE_FACTORY_H_
diff --git a/media/libdatasource/include/datasource/DataURISource.h b/media/libdatasource/include/datasource/DataURISource.h
new file mode 100644
index 0000000..cf8d68e
--- /dev/null
+++ b/media/libdatasource/include/datasource/DataURISource.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef DATA_URI_SOURCE_H_
+
+#define DATA_URI_SOURCE_H_
+
+#include <media/DataSource.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct DataURISource : public DataSource {
+ static sp<DataURISource> Create(const char *uri);
+
+ virtual status_t initCheck() const;
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+ virtual status_t getSize(off64_t *size);
+
+protected:
+ virtual ~DataURISource();
+
+private:
+ sp<ABuffer> mBuffer;
+
+ DataURISource(const sp<ABuffer> &buffer);
+
+ DISALLOW_EVIL_CONSTRUCTORS(DataURISource);
+};
+
+} // namespace android
+
+#endif // DATA_URI_SOURCE_H_
+
diff --git a/media/libdatasource/include/datasource/FileSource.h b/media/libdatasource/include/datasource/FileSource.h
new file mode 100644
index 0000000..9249842
--- /dev/null
+++ b/media/libdatasource/include/datasource/FileSource.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef FILE_SOURCE_H_
+
+#define FILE_SOURCE_H_
+
+#include <stdio.h>
+
+#include <datasource/ClearFileSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
+
+namespace android {
+
+class FileSource : public ClearFileSource {
+public:
+ FileSource(const char *filename);
+ // FileSource takes ownership and will close the fd
+ FileSource(int fd, int64_t offset, int64_t length);
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime);
+
+ static bool requiresDrm(int fd, int64_t offset, int64_t length, const char *mime);
+
+protected:
+ virtual ~FileSource();
+
+private:
+ /*for DRM*/
+ sp<DecryptHandle> mDecryptHandle;
+ DrmManagerClient *mDrmManagerClient;
+ int64_t mDrmBufOffset;
+ ssize_t mDrmBufSize;
+ unsigned char *mDrmBuf;
+
+ ssize_t readAtDRM_l(off64_t offset, void *data, size_t size);
+
+ FileSource(const FileSource &);
+ FileSource &operator=(const FileSource &);
+};
+
+} // namespace android
+
+#endif // FILE_SOURCE_H_
+
diff --git a/media/libdatasource/include/datasource/HTTPBase.h b/media/libdatasource/include/datasource/HTTPBase.h
new file mode 100644
index 0000000..8b20187
--- /dev/null
+++ b/media/libdatasource/include/datasource/HTTPBase.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef HTTP_BASE_H_
+
+#define HTTP_BASE_H_
+
+#include <media/DataSource.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct HTTPBase : public DataSource {
+ enum Flags {
+ // Don't log any URLs.
+ kFlagIncognito = 1
+ };
+
+ HTTPBase();
+
+ virtual status_t connect(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers = NULL,
+ off64_t offset = 0) = 0;
+
+ virtual void disconnect() = 0;
+
+ // Returns true if bandwidth could successfully be estimated,
+ // false otherwise.
+ virtual bool estimateBandwidth(int32_t *bandwidth_bps);
+
+ virtual status_t getEstimatedBandwidthKbps(int32_t *kbps);
+
+ virtual status_t setBandwidthStatCollectFreq(int32_t freqMs);
+
+ virtual void setBandwidthHistorySize(size_t numHistoryItems);
+
+ virtual String8 toString() {
+ return mName;
+ }
+
+protected:
+ virtual void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
+ String8 mName;
+
+private:
+ struct BandwidthEntry {
+ int64_t mDelayUs;
+ size_t mNumBytes;
+ };
+
+ Mutex mLock;
+
+ List<BandwidthEntry> mBandwidthHistory;
+ size_t mNumBandwidthHistoryItems;
+ int64_t mTotalTransferTimeUs;
+ size_t mTotalTransferBytes;
+ size_t mMaxBandwidthHistoryItems;
+
+ enum {
+ kMinBandwidthCollectFreqMs = 1000, // 1 second
+ kMaxBandwidthCollectFreqMs = 60000, // one minute
+ };
+
+ int64_t mPrevBandwidthMeasureTimeUs;
+ int32_t mPrevEstimatedBandWidthKbps;
+ int32_t mBandWidthCollectFreqMs;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HTTPBase);
+};
+
+} // namespace android
+
+#endif // HTTP_BASE_H_
diff --git a/media/libdatasource/include/datasource/MediaHTTP.h b/media/libdatasource/include/datasource/MediaHTTP.h
new file mode 100644
index 0000000..60252ce
--- /dev/null
+++ b/media/libdatasource/include/datasource/MediaHTTP.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef MEDIA_HTTP_H_
+
+#define MEDIA_HTTP_H_
+
+#include <datasource/ClearMediaHTTP.h>
+#include <media/stagefright/foundation/AString.h>
+
+namespace android {
+
+struct MediaHTTPConnection;
+
+struct MediaHTTP : public ClearMediaHTTP {
+ MediaHTTP(const sp<MediaHTTPConnection> &conn);
+
+protected:
+ virtual ~MediaHTTP();
+
+ virtual sp<DecryptHandle> DrmInitialization(const char* mime);
+
+private:
+ sp<DecryptHandle> mDecryptHandle;
+ DrmManagerClient *mDrmManagerClient;
+
+ void clearDRMState_l();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaHTTP);
+};
+
+} // namespace android
+
+#endif // MEDIA_HTTP_H_
diff --git a/media/libdatasource/include/datasource/NuCachedSource2.h b/media/libdatasource/include/datasource/NuCachedSource2.h
new file mode 100644
index 0000000..596efb8
--- /dev/null
+++ b/media/libdatasource/include/datasource/NuCachedSource2.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef NU_CACHED_SOURCE_2_H_
+
+#define NU_CACHED_SOURCE_2_H_
+
+#include <media/DataSource.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+
+namespace android {
+
+struct ALooper;
+struct PageCache;
+
+struct NuCachedSource2 : public DataSource {
+ static sp<NuCachedSource2> Create(
+ const sp<DataSource> &source,
+ const char *cacheConfig = NULL,
+ bool disconnectAtHighwatermark = false);
+
+ virtual status_t initCheck() const;
+
+ virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+
+ virtual void close();
+
+ virtual void disconnect();
+
+ virtual status_t getSize(off64_t *size);
+ virtual uint32_t flags();
+
+ virtual sp<DecryptHandle> DrmInitialization(const char* mime);
+ virtual String8 getUri();
+
+ virtual String8 getMIMEType() const;
+
+ virtual String8 toString() {
+ return mName;
+ }
+
+ status_t getAvailableSize(off64_t offset, off64_t *size);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ size_t cachedSize();
+ size_t approxDataRemaining(status_t *finalStatus) const;
+
+ void resumeFetchingIfNecessary();
+
+ // The following methods are supported only if the
+ // data source is HTTP-based; otherwise, ERROR_UNSUPPORTED
+ // is returned.
+ status_t getEstimatedBandwidthKbps(int32_t *kbps);
+ status_t setCacheStatCollectFreq(int32_t freqMs);
+
+ static void RemoveCacheSpecificHeaders(
+ KeyedVector<String8, String8> *headers,
+ String8 *cacheConfig,
+ bool *disconnectAtHighwatermark);
+
+protected:
+ virtual ~NuCachedSource2();
+
+private:
+ friend struct AHandlerReflector<NuCachedSource2>;
+
+ NuCachedSource2(
+ const sp<DataSource> &source,
+ const char *cacheConfig,
+ bool disconnectAtHighwatermark);
+
+ enum {
+ kPageSize = 65536,
+ kDefaultHighWaterThreshold = 20 * 1024 * 1024,
+ kDefaultLowWaterThreshold = 4 * 1024 * 1024,
+
+ // Read data after a 15 sec timeout whether we're actively
+ // fetching or not.
+ kDefaultKeepAliveIntervalUs = 15000000,
+ };
+
+ enum {
+ kWhatFetchMore = 'fetc',
+ kWhatRead = 'read',
+ };
+
+ enum {
+ kMaxNumRetries = 10,
+ };
+
+ sp<DataSource> mSource;
+ sp<AHandlerReflector<NuCachedSource2> > mReflector;
+ sp<ALooper> mLooper;
+ String8 mName;
+
+ Mutex mSerializer;
+ mutable Mutex mLock;
+ Condition mCondition;
+
+ PageCache *mCache;
+ off64_t mCacheOffset;
+ status_t mFinalStatus;
+ off64_t mLastAccessPos;
+ sp<AMessage> mAsyncResult;
+ bool mFetching;
+ bool mDisconnecting;
+ int64_t mLastFetchTimeUs;
+
+ int32_t mNumRetriesLeft;
+
+ size_t mHighwaterThresholdBytes;
+ size_t mLowwaterThresholdBytes;
+
+ // If the keep-alive interval is 0, keep-alives are disabled.
+ int64_t mKeepAliveIntervalUs;
+
+ bool mDisconnectAtHighwatermark;
+
+ void onMessageReceived(const sp<AMessage> &msg);
+ void onFetch();
+ void onRead(const sp<AMessage> &msg);
+
+ void fetchInternal();
+ ssize_t readInternal(off64_t offset, void *data, size_t size);
+ status_t seekInternal_l(off64_t offset);
+
+ size_t approxDataRemaining_l(off64_t offset, status_t *finalStatus) const;
+
+ void restartPrefetcherIfNecessary_l(
+ bool ignoreLowWaterThreshold = false, bool force = false);
+
+ void updateCacheParamsFromSystemProperty();
+ void updateCacheParamsFromString(const char *s);
+
+ DISALLOW_EVIL_CONSTRUCTORS(NuCachedSource2);
+};
+
+} // namespace android
+
+#endif // NU_CACHED_SOURCE_2_H_