| /* |
| * 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 <media/stagefright/MediaHTTP.h> |
| |
| #include <binder/IServiceManager.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/ALooper.h> |
| #include <media/stagefright/Utils.h> |
| |
| #include <media/IMediaHTTPConnection.h> |
| |
| namespace android { |
| |
| MediaHTTP::MediaHTTP(const sp<IMediaHTTPConnection> &conn) |
| : mInitCheck((conn != NULL) ? OK : NO_INIT), |
| mHTTPConnection(conn), |
| mCachedSizeValid(false), |
| mCachedSize(0ll), |
| mDrmManagerClient(NULL) { |
| } |
| |
| MediaHTTP::~MediaHTTP() { |
| clearDRMState_l(); |
| } |
| |
| status_t MediaHTTP::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("MediaHTTP(%s)", sanitized.c_str()); |
| } |
| |
| return success ? OK : UNKNOWN_ERROR; |
| } |
| |
| void MediaHTTP::disconnect() { |
| mName = String8("MediaHTTP(<disconnected>)"); |
| if (mInitCheck != OK) { |
| return; |
| } |
| |
| mHTTPConnection->disconnect(); |
| } |
| |
| status_t MediaHTTP::initCheck() const { |
| return mInitCheck; |
| } |
| |
| ssize_t MediaHTTP::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 MediaHTTP::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 MediaHTTP::flags() { |
| return kWantsPrefetching | kIsHTTPBasedSource; |
| } |
| |
| status_t MediaHTTP::reconnectAtOffset(off64_t offset) { |
| return connect(mLastURI.c_str(), &mLastHeaders, offset); |
| } |
| |
| // 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::getDrmInfo( |
| sp<DecryptHandle> &handle, DrmManagerClient **client) { |
| handle = mDecryptHandle; |
| *client = mDrmManagerClient; |
| } |
| |
| String8 MediaHTTP::getUri() { |
| if (mInitCheck != OK) { |
| return String8::empty(); |
| } |
| |
| String8 uri; |
| if (OK == mHTTPConnection->getUri(&uri)) { |
| return uri; |
| } |
| return String8(mLastURI.c_str()); |
| } |
| |
| String8 MediaHTTP::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; |
| } |
| |
| void MediaHTTP::clearDRMState_l() { |
| if (mDecryptHandle != NULL) { |
| // To release mDecryptHandle |
| CHECK(mDrmManagerClient); |
| mDrmManagerClient->closeDecryptSession(mDecryptHandle); |
| mDecryptHandle = NULL; |
| } |
| } |
| |
| } // namespace android |