summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-06-10 11:17:50 -0700
committerAndreas Huber <andih@google.com>2010-06-10 15:16:57 -0700
commit0a5baa9b411fe086013d2a5e9126ed63fbad046c (patch)
treed2f568256c27d816aa5cac3ef365ebe460536a3a /media
parent28b6844dec4297b16777fd45fd77ff33aa495de2 (diff)
downloadframeworks_av-0a5baa9b411fe086013d2a5e9126ed63fbad046c.zip
frameworks_av-0a5baa9b411fe086013d2a5e9126ed63fbad046c.tar.gz
frameworks_av-0a5baa9b411fe086013d2a5e9126ed63fbad046c.tar.bz2
Switch stagefright's approach to prefetching to the new model. The java MediaPlayer is now notified about rebuffering start/end via info messages.
Change-Id: If8185ba329ce8b6663b1ad39a4efb0ad3be81df2
Diffstat (limited to 'media')
-rw-r--r--media/libstagefright/Android.mk5
-rw-r--r--media/libstagefright/AwesomePlayer.cpp158
-rw-r--r--media/libstagefright/CachingDataSource.cpp165
-rw-r--r--media/libstagefright/DataSource.cpp10
-rw-r--r--media/libstagefright/HTTPDataSource.cpp457
-rw-r--r--media/libstagefright/Prefetcher.cpp474
-rw-r--r--media/libstagefright/include/AwesomePlayer.h9
-rw-r--r--media/libstagefright/include/Prefetcher.h70
8 files changed, 88 insertions, 1260 deletions
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 7608ec8..00a6995 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -22,19 +22,18 @@ LOCAL_SRC_FILES += \
AudioPlayer.cpp \
AudioSource.cpp \
AwesomePlayer.cpp \
- CachingDataSource.cpp \
CameraSource.cpp \
DataSource.cpp \
FileSource.cpp \
- HTTPDataSource.cpp \
HTTPStream.cpp \
JPEGSource.cpp \
MP3Extractor.cpp \
MPEG4Extractor.cpp \
MPEG4Writer.cpp \
MediaExtractor.cpp \
+ NuCachedSource2.cpp \
+ NuHTTPDataSource.cpp \
OggExtractor.cpp \
- Prefetcher.cpp \
SampleIterator.cpp \
SampleTable.cpp \
ShoutcastSource.cpp \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 4c9856d..2a8c035 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -23,12 +23,12 @@
#include "include/ARTSPController.h"
#include "include/AwesomePlayer.h"
#include "include/LiveSource.h"
-#include "include/Prefetcher.h"
#include "include/SoftwareRenderer.h"
+#include "include/NuCachedSource2.h"
+#include "include/ThrottledSource.h"
#include <binder/IPCThreadState.h>
#include <media/stagefright/AudioPlayer.h>
-#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
@@ -354,11 +354,7 @@ void AwesomePlayer::reset_l() {
cancelPlayerEvents();
- if (mPrefetcher != NULL) {
- CHECK_EQ(mPrefetcher->getStrongCount(), 1);
- }
- mPrefetcher.clear();
-
+ mCachedSource.clear();
mAudioTrack.clear();
mVideoTrack.clear();
@@ -448,30 +444,45 @@ void AwesomePlayer::onBufferingUpdate() {
}
mBufferingEventPending = false;
- int64_t durationUs;
- {
- Mutex::Autolock autoLock(mMiscStateLock);
- durationUs = mDurationUs;
+ if (mCachedSource == NULL) {
+ return;
}
- int64_t cachedDurationUs = mPrefetcher->getCachedDurationUs();
+ size_t lowWatermark = 400000;
+ size_t highWatermark = 1000000;
- LOGI("cache holds %.2f secs worth of data.", cachedDurationUs / 1E6);
+ off_t size;
+ if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
+ int64_t bitrate = size * 8000000ll / mDurationUs; // in bits/sec
- if (durationUs >= 0) {
- int64_t positionUs;
- getPosition(&positionUs);
+ size_t cachedSize = mCachedSource->cachedSize();
+ int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
- cachedDurationUs += positionUs;
+ double percentage = (double)cachedDurationUs / mDurationUs;
- double percentage = (double)cachedDurationUs / durationUs;
notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage * 100.0);
- postBufferingEvent_l();
- } else {
- // LOGE("Not sending buffering status because duration is unknown.");
- postBufferingEvent_l();
+ lowWatermark = 2 * bitrate / 8; // 2 secs
+ highWatermark = 10 * bitrate / 8; // 10 secs
}
+
+ bool eos;
+ size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
+
+ if ((mFlags & PLAYING) && !eos && (cachedDataRemaining < lowWatermark)) {
+ LOGI("cache is running low (< %d) , pausing.", lowWatermark);
+ mFlags |= CACHE_UNDERRUN;
+ pause_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+ } else if ((mFlags & CACHE_UNDERRUN)
+ && (eos || cachedDataRemaining > highWatermark)) {
+ LOGI("cache has filled up (> %d), resuming.", highWatermark);
+ mFlags &= ~CACHE_UNDERRUN;
+ play_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+ }
+
+ postBufferingEvent_l();
}
void AwesomePlayer::onStreamDone() {
@@ -508,6 +519,9 @@ void AwesomePlayer::onStreamDone() {
status_t AwesomePlayer::play() {
Mutex::Autolock autoLock(mLock);
+
+ mFlags &= ~CACHE_UNDERRUN;
+
return play_l();
}
@@ -632,6 +646,9 @@ void AwesomePlayer::initRenderer_l() {
status_t AwesomePlayer::pause() {
Mutex::Autolock autoLock(mLock);
+
+ mFlags &= ~CACHE_UNDERRUN;
+
return pause_l();
}
@@ -652,7 +669,7 @@ status_t AwesomePlayer::pause_l() {
}
bool AwesomePlayer::isPlaying() const {
- return mFlags & PLAYING;
+ return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN);
}
void AwesomePlayer::setISurface(const sp<ISurface> &isurface) {
@@ -719,6 +736,11 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) {
}
status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
+ if (mFlags & CACHE_UNDERRUN) {
+ mFlags &= ~CACHE_UNDERRUN;
+ play_l();
+ }
+
mSeeking = true;
mSeekNotificationSent = false;
mSeekTimeUs = timeUs;
@@ -764,10 +786,6 @@ status_t AwesomePlayer::getVideoDimensions(
void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
CHECK(source != NULL);
- if (mPrefetcher != NULL) {
- source = mPrefetcher->addSource(source);
- }
-
mAudioTrack = source;
}
@@ -814,10 +832,6 @@ status_t AwesomePlayer::initAudioDecoder() {
void AwesomePlayer::setVideoSource(sp<MediaSource> source) {
CHECK(source != NULL);
- if (mPrefetcher != NULL) {
- source = mPrefetcher->addSource(source);
- }
-
mVideoTrack = source;
}
@@ -869,6 +883,21 @@ void AwesomePlayer::onVideoEvent() {
mVideoBuffer->release();
mVideoBuffer = NULL;
}
+
+ if (mCachedSource != NULL && mAudioSource != NULL) {
+ // We're going to seek the video source first, followed by
+ // the audio source.
+ // In order to avoid jumps in the DataSource offset caused by
+ // the audio codec prefetching data from the old locations
+ // while the video codec is already reading data from the new
+ // locations, we'll "pause" the audio source, causing it to
+ // stop reading input data until a subsequent seek.
+
+ if (mAudioPlayer != NULL) {
+ mAudioPlayer->pause();
+ }
+ mAudioSource->pause();
+ }
}
if (!mVideoBuffer) {
@@ -925,6 +954,7 @@ void AwesomePlayer::onVideoEvent() {
LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
mAudioPlayer->seekTo(timeUs);
+ mAudioPlayer->resume();
mWatchForAudioSeekComplete = true;
mWatchForAudioEOS = true;
} else if (!mSeekNotificationSent) {
@@ -1012,10 +1042,6 @@ void AwesomePlayer::postStreamDoneEvent_l(status_t status) {
}
void AwesomePlayer::postBufferingEvent_l() {
- if (mPrefetcher == NULL) {
- return;
- }
-
if (mBufferingEventPending) {
return;
}
@@ -1123,10 +1149,10 @@ status_t AwesomePlayer::finishSetDataSource_l() {
sp<DataSource> dataSource;
if (!strncasecmp("http://", mUri.string(), 7)) {
- mConnectingDataSource = new HTTPDataSource(mUri, &mUriHeaders);
+ mConnectingDataSource = new NuHTTPDataSource;
mLock.unlock();
- status_t err = mConnectingDataSource->connect();
+ status_t err = mConnectingDataSource->connect(mUri/*, mUriHeaders */);
mLock.lock();
if (err != OK) {
@@ -1136,22 +1162,29 @@ status_t AwesomePlayer::finishSetDataSource_l() {
return err;
}
- dataSource = new CachingDataSource(
- mConnectingDataSource, 64 * 1024, 10);
-
+#if 0
+ mCachedSource = new NuCachedSource2(
+ new ThrottledSource(
+ mConnectingDataSource, 50 * 1024 /* bytes/sec */));
+#else
+ mCachedSource = new NuCachedSource2(mConnectingDataSource);
+#endif
mConnectingDataSource.clear();
+
+ dataSource = mCachedSource;
} else if (!strncasecmp(mUri.string(), "httplive://", 11)) {
String8 uri("http://");
uri.append(mUri.string() + 11);
dataSource = new LiveSource(uri.string());
- if (dataSource->flags() & DataSource::kWantsPrefetching) {
- mPrefetcher = new Prefetcher;
- }
+ mCachedSource = new NuCachedSource2(dataSource);
+ dataSource = mCachedSource;
sp<MediaExtractor> extractor =
MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
+
+ return setDataSource_l(extractor);
} else if (!strncasecmp("rtsp://", mUri.string(), 7)) {
if (mLooper == NULL) {
mLooper = new ALooper;
@@ -1183,10 +1216,6 @@ status_t AwesomePlayer::finishSetDataSource_l() {
return UNKNOWN_ERROR;
}
- if (dataSource->flags() & DataSource::kWantsPrefetching) {
- mPrefetcher = new Prefetcher;
- }
-
return setDataSource_l(extractor);
}
@@ -1211,8 +1240,6 @@ bool AwesomePlayer::ContinuePreparation(void *cookie) {
}
void AwesomePlayer::onPrepareAsyncEvent() {
- sp<Prefetcher> prefetcher;
-
{
Mutex::Autolock autoLock(mLock);
@@ -1248,39 +1275,6 @@ void AwesomePlayer::onPrepareAsyncEvent() {
return;
}
}
-
- prefetcher = mPrefetcher;
- }
-
- if (prefetcher != NULL) {
- {
- Mutex::Autolock autoLock(mLock);
- if (mFlags & PREPARE_CANCELLED) {
- LOGI("prepare was cancelled before preparing the prefetcher");
-
- prefetcher.clear();
- abortPrepare(UNKNOWN_ERROR);
- return;
- }
- }
-
- LOGI("calling prefetcher->prepare()");
- status_t result =
- prefetcher->prepare(&AwesomePlayer::ContinuePreparation, this);
-
- prefetcher.clear();
-
- if (result == OK) {
- LOGI("prefetcher is done preparing");
- } else {
- Mutex::Autolock autoLock(mLock);
-
- CHECK_EQ(result, -EINTR);
-
- LOGI("prefetcher->prepare() was cancelled early.");
- abortPrepare(UNKNOWN_ERROR);
- return;
- }
}
Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
deleted file mode 100644
index 1ca463e..0000000
--- a/media/libstagefright/CachingDataSource.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * 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
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index b569a6b..6e4f9df 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -20,13 +20,13 @@
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include "include/MPEG2TSExtractor.h"
+#include "include/NuCachedSource2.h"
+#include "include/NuHTTPDataSource.h"
#include "matroska/MatroskaExtractor.h"
-#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
-#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/MediaErrors.h>
#include <utils/String8.h>
@@ -108,11 +108,11 @@ sp<DataSource> DataSource::CreateFromURI(
if (!strncasecmp("file://", uri, 7)) {
source = new FileSource(uri + 7);
} else if (!strncasecmp("http://", uri, 7)) {
- sp<HTTPDataSource> httpSource = new HTTPDataSource(uri, headers);
- if (httpSource->connect() != OK) {
+ sp<NuHTTPDataSource> httpSource = new NuHTTPDataSource;
+ if (httpSource->connect(uri /* , headers */) != OK) {
return NULL;
}
- source = new CachingDataSource(httpSource, 64 * 1024, 10);
+ source = new NuCachedSource2(httpSource);
} else {
// Assume it's a filename.
source = new FileSource(uri);
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
deleted file mode 100644
index f72a6cc..0000000
--- a/media/libstagefright/HTTPDataSource.cpp
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * 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 "HTTPDataSource"
-#include <utils/Log.h>
-
-#include "include/stagefright_string.h"
-#include "include/HTTPStream.h"
-
-#include <stdlib.h>
-
-#include <cutils/properties.h>
-#include <media/stagefright/HTTPDataSource.h>
-#include <media/stagefright/MediaDebug.h>
-
-namespace android {
-
-status_t HTTPDataSource::connectWithRedirectsAndRange(off_t rangeStart) {
- string host = mStartingHost.string();
- string path = mStartingPath.string();
- int port = mStartingPort;
-
- LOGV("Connecting to host '%s', port %d, path '%s'",
- host.c_str(), port, path.c_str());
-
- int numRedirectsRemaining = 5;
- while (numRedirectsRemaining-- > 0) {
- {
- Mutex::Autolock autoLock(mStateLock);
- if (mState == DISCONNECTED) {
- return UNKNOWN_ERROR;
- }
- }
-
- status_t err = mHttp->connect(host.c_str(), port);
-
- if (err != OK) {
- return err;
- }
-
- String8 request;
- request.append("GET ");
- request.append(path.c_str());
- request.append(" HTTP/1.1\r\n");
- request.append(mHeaders);
- request.append("Host: ");
- request.append(host.c_str());
- request.append("\r\n");
-
- if (rangeStart > 0) {
- char range[128];
- sprintf(range, "Range: bytes=%ld-\r\n", rangeStart);
-
- request.append(range);
- }
-
- request.append("\r\n");
-
- err = mHttp->send(request.string());
-
- if (err != OK) {
- return err;
- }
-
- int httpStatus;
- err = mHttp->receive_header(&httpStatus);
-
- if (err != OK) {
- return err;
- }
-
- if (httpStatus >= 200 && httpStatus < 300) {
- applyTimeoutResponse();
- return OK;
- }
-
- if (httpStatus != 301 && httpStatus != 302) {
- LOGE("HTTP request failed w/ http status %d", httpStatus);
- return ERROR_IO;
- }
-
- string location;
- CHECK(mHttp->find_header_value("Location", &location));
-
- CHECK(string(location, 0, 7) == "http://");
- location.erase(0, 7);
- string::size_type slashPos = location.find('/');
- if (slashPos == string::npos) {
- slashPos = location.size();
- location += '/';
- }
-
- mHttp->disconnect();
-
- LOGV("Redirecting to %s\n", location.c_str());
-
- host = string(location, 0, slashPos);
-
- string::size_type colonPos = host.find(':');
- if (colonPos != string::npos) {
- const char *start = host.c_str() + colonPos + 1;
- char *end;
- long tmp = strtol(start, &end, 10);
- CHECK(end > start && (*end == '\0'));
-
- port = (tmp >= 0 && tmp < 65536) ? (int)tmp : 80;
-
- host.erase(colonPos, host.size() - colonPos);
- } else {
- port = 80;
- }
-
- path = string(location, slashPos);
-
- mStartingHost = host.c_str();
- mStartingPath = path.c_str();
- mStartingPort = port;
- }
-
- return ERROR_IO;
-}
-
-void HTTPDataSource::applyTimeoutResponse() {
- string timeout;
- if (mHttp->find_header_value("X-SocketTimeout", &timeout)) {
- const char *s = timeout.c_str();
- char *end;
- long tmp = strtol(s, &end, 10);
- if (end == s || *end != '\0') {
- LOGW("Illegal X-SocketTimeout value given.");
- return;
- }
-
- LOGI("overriding default timeout, new timeout is %ld seconds", tmp);
- mHttp->setReceiveTimeout(tmp);
- }
-}
-
-HTTPDataSource::HTTPDataSource(
- const char *uri, const KeyedVector<String8, String8> *headers) {
- CHECK(!strncasecmp("http://", uri, 7));
-
- string host;
- string path;
- int port;
-
- char *slash = strchr(uri + 7, '/');
- if (slash == NULL) {
- host = uri + 7;
- path = "/";
- } else {
- host = string(uri + 7, slash - (uri + 7));
- path = slash;
- }
-
- char *colon = strchr(host.c_str(), ':');
- if (colon == NULL) {
- port = 80;
- } else {
- char *end;
- long tmp = strtol(colon + 1, &end, 10);
- CHECK(end > colon + 1);
- CHECK(tmp > 0 && tmp < 65536);
- port = tmp;
-
- host = string(host, 0, colon - host.c_str());
- }
-
- mStartingHost = host.c_str();
- mStartingPath = path.c_str();
- mStartingPort = port;
-
- init(headers);
-}
-
-HTTPDataSource::HTTPDataSource(
- const char *_host, int port, const char *_path,
- const KeyedVector<String8, String8> *headers) {
- mStartingHost = _host;
- mStartingPath = _path;
- mStartingPort = port;
-
- init(headers);
-}
-
-void HTTPDataSource::init(const KeyedVector<String8, String8> *headers) {
- mState = DISCONNECTED;
- mHttp = new HTTPStream;
-
- initHeaders(headers);
-
- mBuffer = malloc(kBufferSize);
-
- mNumRetriesLeft = kMaxNumRetries;
-}
-
-status_t HTTPDataSource::connect() {
- {
- Mutex::Autolock autoLock(mStateLock);
-
- if (mState != DISCONNECTED) {
- return ERROR_ALREADY_CONNECTED;
- }
-
- mState = CONNECTING;
- }
-
- mBufferLength = 0;
- mBufferOffset = 0;
- mContentLengthValid = false;
-
- status_t err = connectWithRedirectsAndRange(0);
-
- if (err != OK) {
- Mutex::Autolock autoLock(mStateLock);
-
- if (mState != CONNECTING) {
- LOGV("connect() cancelled");
- }
- mState = DISCONNECTED;
-
- return err;
- }
-
- string value;
- if (mHttp->find_header_value("Content-Length", &value)) {
- char *end;
- mContentLength = strtoull(value.c_str(), &end, 10);
- mContentLengthValid = true;
- }
-
- Mutex::Autolock autoLock(mStateLock);
-
- if (mState != CONNECTING) {
- // disconnect was called when we had just successfully connected.
- LOGV("connect() cancelled (we had just succeeded connecting)");
-
- mHttp->disconnect();
- return UNKNOWN_ERROR;
- }
-
- mState = CONNECTED;
-
- return OK;
-}
-
-void HTTPDataSource::disconnect() {
- Mutex::Autolock autoLock(mStateLock);
-
- if (mState == CONNECTING || mState == CONNECTED) {
- mHttp->disconnect();
- mState = DISCONNECTED;
- }
-}
-
-status_t HTTPDataSource::initCheck() const {
- Mutex::Autolock autoLock(mStateLock);
-
- return (mState == CONNECTED) ? (status_t)OK : ERROR_NOT_CONNECTED;
-}
-
-status_t HTTPDataSource::getSize(off_t *size) {
- *size = 0;
-
- {
- Mutex::Autolock autoLock(mStateLock);
- if (mState != CONNECTED) {
- return ERROR_NOT_CONNECTED;
- }
- }
-
- if (!mContentLengthValid) {
- return ERROR_UNSUPPORTED;
- }
-
- *size = mContentLength;
-
- return OK;
-}
-
-HTTPDataSource::~HTTPDataSource() {
- disconnect();
-
- delete mHttp;
- mHttp = NULL;
-
- free(mBuffer);
- mBuffer = NULL;
-}
-
-ssize_t HTTPDataSource::sendRangeRequest(size_t offset) {
- status_t err = connectWithRedirectsAndRange(offset);
-
- if (err != OK) {
- return err;
- }
-
- string value;
- if (!mHttp->find_header_value("Content-Length", &value)) {
- return kBufferSize;
- }
-
- char *end;
- unsigned long contentLength = strtoul(value.c_str(), &end, 10);
-
- return contentLength;
-}
-
-ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) {
- LOGV("readAt %ld, size %d", offset, size);
-
-rinse_repeat:
- {
- Mutex::Autolock autoLock(mStateLock);
- if (mState != CONNECTED) {
- return ERROR_NOT_CONNECTED;
- }
- }
-
- if (offset >= mBufferOffset
- && offset < (off_t)(mBufferOffset + mBufferLength)) {
- size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
-
- size_t copy = num_bytes_available;
- if (copy > size) {
- copy = size;
- }
-
- memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
-
- if (copy < size) {
- LOGV("short read (1), returning %d vs. %d requested", copy, size);
- }
-
- return copy;
- }
-
- ssize_t contentLength = 0;
- if (offset != (off_t)(mBufferOffset + mBufferLength)) {
- LOGV("new range offset=%ld (old=%ld)",
- offset, mBufferOffset + mBufferLength);
-
- mHttp->disconnect();
-
- contentLength = sendRangeRequest(offset);
-
- if (contentLength > kBufferSize) {
- contentLength = kBufferSize;
- }
- } else {
- contentLength = kBufferSize;
- }
-
- mBufferOffset = offset;
-
- if (mContentLengthValid
- && mBufferOffset + contentLength >= (off_t)mContentLength) {
- // If we never triggered a range request but know the content length,
- // make sure to not read more data than there could be, otherwise
- // we'd block indefinitely if the server doesn't close the connection.
-
- contentLength = mContentLength - mBufferOffset;
- }
-
- if (contentLength <= 0) {
- return contentLength;
- }
-
- ssize_t num_bytes_received = mHttp->receive(mBuffer, contentLength);
-
- if (num_bytes_received < 0
- || (mContentLengthValid && num_bytes_received < contentLength)) {
- if (mNumRetriesLeft-- > 0) {
- mHttp->disconnect();
- mBufferLength = 0;
- num_bytes_received = connectWithRedirectsAndRange(mBufferOffset);
- if (num_bytes_received == OK) {
- LOGI("retrying connection succeeded.");
- goto rinse_repeat;
- }
- LOGE("retrying connection failed");
- }
-
- mBufferLength = 0;
-
- return num_bytes_received;
- }
-
- mBufferLength = (size_t)num_bytes_received;
-
- size_t copy = mBufferLength;
- if (copy > size) {
- copy = size;
- }
-
- memcpy(data, mBuffer, copy);
-
- return copy;
-}
-
-void HTTPDataSource::initHeaders(
- const KeyedVector<String8, String8> *overrides) {
- mHeaders = String8();
-
- mHeaders.append("User-Agent: stagefright/1.0 (Linux;Android ");
-
-#if (PROPERTY_VALUE_MAX < 8)
-#error "PROPERTY_VALUE_MAX must be at least 8"
-#endif
-
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.build.version.release", value, "Unknown");
- mHeaders.append(value);
- mHeaders.append(")\r\n");
-
- if (overrides == NULL) {
- return;
- }
-
- for (size_t i = 0; i < overrides->size(); ++i) {
- String8 line;
- line.append(overrides->keyAt(i));
- line.append(": ");
- line.append(overrides->valueAt(i));
- line.append("\r\n");
-
- mHeaders.append(line);
- }
-}
-
-uint32_t HTTPDataSource::flags() {
- uint32_t f = kWantsPrefetching;
-
- if (!strcasecmp(mStartingHost.string(), "localhost")
- || !strcmp(mStartingHost.string(), "127.0.0.1")) {
- f |= kStreamedFromLocalHost;
- }
-
- return f;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
deleted file mode 100644
index b6ed56b..0000000
--- a/media/libstagefright/Prefetcher.cpp
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "Prefetcher"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include "include/Prefetcher.h"
-
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <utils/List.h>
-
-namespace android {
-
-struct PrefetchedSource : public MediaSource {
- PrefetchedSource(
- size_t index,
- const sp<MediaSource> &source);
-
- virtual status_t start(MetaData *params);
- virtual status_t stop();
-
- virtual status_t read(
- MediaBuffer **buffer, const ReadOptions *options);
-
- virtual sp<MetaData> getFormat();
-
-protected:
- virtual ~PrefetchedSource();
-
-private:
- friend struct Prefetcher;
-
- Mutex mLock;
- Condition mCondition;
-
- sp<MediaSource> mSource;
- size_t mIndex;
- bool mStarted;
- bool mReachedEOS;
- status_t mFinalStatus;
- int64_t mSeekTimeUs;
- int64_t mCacheDurationUs;
- size_t mCacheSizeBytes;
- bool mPrefetcherStopped;
- bool mCurrentlyPrefetching;
-
- List<MediaBuffer *> mCachedBuffers;
-
- // Returns true iff source is currently caching.
- bool getCacheDurationUs(int64_t *durationUs, size_t *totalSize = NULL);
-
- void updateCacheDuration_l();
- void clearCache_l();
-
- void cacheMore();
- void onPrefetcherStopped();
-
- PrefetchedSource(const PrefetchedSource &);
- PrefetchedSource &operator=(const PrefetchedSource &);
-};
-
-Prefetcher::Prefetcher()
- : mDone(false),
- mThreadExited(false) {
- startThread();
-}
-
-Prefetcher::~Prefetcher() {
- stopThread();
-}
-
-sp<MediaSource> Prefetcher::addSource(const sp<MediaSource> &source) {
- Mutex::Autolock autoLock(mLock);
-
- sp<PrefetchedSource> psource =
- new PrefetchedSource(mSources.size(), source);
-
- mSources.add(psource);
-
- return psource;
-}
-
-void Prefetcher::startThread() {
- mThreadExited = false;
- mDone = false;
-
- int res = androidCreateThreadEtc(
- ThreadWrapper, this, "Prefetcher",
- ANDROID_PRIORITY_DEFAULT, 0, &mThread);
-
- CHECK_EQ(res, 1);
-}
-
-void Prefetcher::stopThread() {
- Mutex::Autolock autoLock(mLock);
-
- while (!mThreadExited) {
- mDone = true;
- mCondition.signal();
- mCondition.wait(mLock);
- }
-}
-
-// static
-int Prefetcher::ThreadWrapper(void *me) {
- static_cast<Prefetcher *>(me)->threadFunc();
-
- return 0;
-}
-
-// Cache at most 1 min for each source.
-static int64_t kMaxCacheDurationUs = 60 * 1000000ll;
-
-// At the same time cache at most 5MB per source.
-static size_t kMaxCacheSizeBytes = 5 * 1024 * 1024;
-
-// If the amount of cached data drops below this,
-// fill the cache up to the max duration again.
-static int64_t kLowWaterDurationUs = 5000000ll;
-
-void Prefetcher::threadFunc() {
- bool fillingCache = false;
-
- for (;;) {
- sp<PrefetchedSource> minSource;
- int64_t minCacheDurationUs = -1;
-
- {
- Mutex::Autolock autoLock(mLock);
- if (mDone) {
- break;
- }
- mCondition.waitRelative(
- mLock, fillingCache ? 10000000ll : 1000000000ll);
-
- ssize_t minIndex = -1;
- for (size_t i = 0; i < mSources.size(); ++i) {
- sp<PrefetchedSource> source = mSources[i].promote();
-
- if (source == NULL) {
- continue;
- }
-
- int64_t cacheDurationUs;
- size_t cacheSizeBytes;
- if (!source->getCacheDurationUs(&cacheDurationUs, &cacheSizeBytes)) {
- continue;
- }
-
- if (cacheSizeBytes > kMaxCacheSizeBytes) {
- LOGI("max cache size reached");
- continue;
- }
-
- if (mSources.size() > 1 && cacheDurationUs >= kMaxCacheDurationUs) {
- LOGI("max duration reached, size = %d bytes", cacheSizeBytes);
- continue;
- }
-
- if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) {
- minCacheDurationUs = cacheDurationUs;
- minIndex = i;
- minSource = source;
- }
- }
-
- if (minIndex < 0) {
- if (fillingCache) {
- LOGV("[%p] done filling the cache, above high water mark.",
- this);
- fillingCache = false;
- }
- continue;
- }
- }
-
- if (!fillingCache && minCacheDurationUs < kLowWaterDurationUs) {
- LOGI("[%p] cache below low water mark, filling cache.", this);
- fillingCache = true;
- }
-
- if (fillingCache) {
- // Make sure not to hold the lock while calling into the source.
- // The lock guards the list of sources, not the individual sources
- // themselves.
- minSource->cacheMore();
- }
- }
-
- Mutex::Autolock autoLock(mLock);
- for (size_t i = 0; i < mSources.size(); ++i) {
- sp<PrefetchedSource> source = mSources[i].promote();
-
- if (source == NULL) {
- continue;
- }
-
- source->onPrefetcherStopped();
- }
-
- mThreadExited = true;
- mCondition.signal();
-}
-
-int64_t Prefetcher::getCachedDurationUs(bool *noMoreData) {
- Mutex::Autolock autoLock(mLock);
-
- int64_t minCacheDurationUs = -1;
- ssize_t minIndex = -1;
- bool anySourceActive = false;
- for (size_t i = 0; i < mSources.size(); ++i) {
- int64_t cacheDurationUs;
- sp<PrefetchedSource> source = mSources[i].promote();
- if (source == NULL) {
- continue;
- }
-
- if (source->getCacheDurationUs(&cacheDurationUs)) {
- anySourceActive = true;
- }
-
- if (minIndex < 0 || cacheDurationUs < minCacheDurationUs) {
- minCacheDurationUs = cacheDurationUs;
- minIndex = i;
- }
- }
-
- if (noMoreData) {
- *noMoreData = !anySourceActive;
- }
-
- return minCacheDurationUs < 0 ? 0 : minCacheDurationUs;
-}
-
-status_t Prefetcher::prepare(
- bool (*continueFunc)(void *cookie), void *cookie) {
- // Fill the cache.
-
- int64_t duration;
- bool noMoreData;
- do {
- usleep(100000);
-
- if (continueFunc && !(*continueFunc)(cookie)) {
- return -EINTR;
- }
-
- duration = getCachedDurationUs(&noMoreData);
- } while (!noMoreData && duration < 2000000ll);
-
- return OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-PrefetchedSource::PrefetchedSource(
- size_t index,
- const sp<MediaSource> &source)
- : mSource(source),
- mIndex(index),
- mStarted(false),
- mReachedEOS(false),
- mSeekTimeUs(0),
- mCacheDurationUs(0),
- mCacheSizeBytes(0),
- mPrefetcherStopped(false),
- mCurrentlyPrefetching(false) {
-}
-
-PrefetchedSource::~PrefetchedSource() {
- if (mStarted) {
- stop();
- }
-}
-
-status_t PrefetchedSource::start(MetaData *params) {
- CHECK(!mStarted);
-
- Mutex::Autolock autoLock(mLock);
-
- status_t err = mSource->start(params);
-
- if (err != OK) {
- return err;
- }
-
- mStarted = true;
-
- return OK;
-}
-
-status_t PrefetchedSource::stop() {
- CHECK(mStarted);
-
- Mutex::Autolock autoLock(mLock);
-
- while (mCurrentlyPrefetching) {
- mCondition.wait(mLock);
- }
-
- clearCache_l();
-
- status_t err = mSource->stop();
-
- mStarted = false;
-
- return err;
-}
-
-status_t PrefetchedSource::read(
- MediaBuffer **out, const ReadOptions *options) {
- *out = NULL;
-
- Mutex::Autolock autoLock(mLock);
-
- CHECK(mStarted);
-
- int64_t seekTimeUs;
- if (options && options->getSeekTo(&seekTimeUs)) {
- CHECK(seekTimeUs >= 0);
-
- clearCache_l();
-
- mReachedEOS = false;
- mSeekTimeUs = seekTimeUs;
- }
-
- while (!mPrefetcherStopped && !mReachedEOS && mCachedBuffers.empty()) {
- mCondition.wait(mLock);
- }
-
- if (mCachedBuffers.empty()) {
- return mReachedEOS ? mFinalStatus : ERROR_END_OF_STREAM;
- }
-
- *out = *mCachedBuffers.begin();
- mCachedBuffers.erase(mCachedBuffers.begin());
- updateCacheDuration_l();
- mCacheSizeBytes -= (*out)->size();
-
- return OK;
-}
-
-sp<MetaData> PrefetchedSource::getFormat() {
- return mSource->getFormat();
-}
-
-bool PrefetchedSource::getCacheDurationUs(
- int64_t *durationUs, size_t *totalSize) {
- Mutex::Autolock autoLock(mLock);
-
- *durationUs = mCacheDurationUs;
- if (totalSize != NULL) {
- *totalSize = mCacheSizeBytes;
- }
-
- if (!mStarted || mReachedEOS) {
- return false;
- }
-
- return true;
-}
-
-void PrefetchedSource::cacheMore() {
- MediaSource::ReadOptions options;
-
- Mutex::Autolock autoLock(mLock);
-
- if (!mStarted) {
- return;
- }
-
- mCurrentlyPrefetching = true;
-
- if (mSeekTimeUs >= 0) {
- options.setSeekTo(mSeekTimeUs);
- mSeekTimeUs = -1;
- }
-
- // Ensure our object does not go away while we're not holding
- // the lock.
- sp<PrefetchedSource> me = this;
-
- mLock.unlock();
- MediaBuffer *buffer;
- status_t err = mSource->read(&buffer, &options);
- mLock.lock();
-
- if (err != OK) {
- mCurrentlyPrefetching = false;
- mReachedEOS = true;
- mFinalStatus = err;
- mCondition.signal();
-
- return;
- }
-
- CHECK(buffer != NULL);
-
- MediaBuffer *copy = new MediaBuffer(buffer->range_length());
- memcpy(copy->data(),
- (const uint8_t *)buffer->data() + buffer->range_offset(),
- buffer->range_length());
-
- sp<MetaData> from = buffer->meta_data();
- sp<MetaData> to = copy->meta_data();
-
- int64_t timeUs;
- if (from->findInt64(kKeyTime, &timeUs)) {
- to->setInt64(kKeyTime, timeUs);
- }
-
- buffer->release();
- buffer = NULL;
-
- mCachedBuffers.push_back(copy);
- updateCacheDuration_l();
- mCacheSizeBytes += copy->size();
-
- mCurrentlyPrefetching = false;
- mCondition.signal();
-}
-
-void PrefetchedSource::updateCacheDuration_l() {
- if (mCachedBuffers.size() < 2) {
- mCacheDurationUs = 0;
- } else {
- int64_t firstTimeUs, lastTimeUs;
- CHECK((*mCachedBuffers.begin())->meta_data()->findInt64(
- kKeyTime, &firstTimeUs));
- CHECK((*--mCachedBuffers.end())->meta_data()->findInt64(
- kKeyTime, &lastTimeUs));
-
- mCacheDurationUs = lastTimeUs - firstTimeUs;
- }
-}
-
-void PrefetchedSource::clearCache_l() {
- List<MediaBuffer *>::iterator it = mCachedBuffers.begin();
- while (it != mCachedBuffers.end()) {
- (*it)->release();
-
- it = mCachedBuffers.erase(it);
- }
-
- updateCacheDuration_l();
- mCacheSizeBytes = 0;
-}
-
-void PrefetchedSource::onPrefetcherStopped() {
- Mutex::Autolock autoLock(mLock);
- mPrefetcherStopped = true;
- mCondition.signal();
-}
-
-} // namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 182aa06..2a9f21b 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -18,11 +18,11 @@
#define AWESOME_PLAYER_H_
+#include "NuHTTPDataSource.h"
#include "TimedEventQueue.h"
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/DataSource.h>
-#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/OMXClient.h>
#include <utils/threads.h>
@@ -33,8 +33,8 @@ struct DataSource;
struct MediaBuffer;
struct MediaExtractor;
struct MediaSource;
-struct Prefetcher;
struct TimeSource;
+struct NuCachedSource2;
struct ALooper;
struct ARTSPController;
@@ -101,6 +101,7 @@ private:
PREPARED = 16,
AT_EOS = 32,
PREPARE_CANCELLED = 64,
+ CACHE_UNDERRUN = 128,
};
mutable Mutex mLock;
@@ -169,8 +170,8 @@ private:
MediaBuffer *mLastVideoBuffer;
MediaBuffer *mVideoBuffer;
- sp<Prefetcher> mPrefetcher;
- sp<HTTPDataSource> mConnectingDataSource;
+ sp<NuHTTPDataSource> mConnectingDataSource;
+ sp<NuCachedSource2> mCachedSource;
sp<ALooper> mLooper;
sp<ARTSPController> mRTSPController;
diff --git a/media/libstagefright/include/Prefetcher.h b/media/libstagefright/include/Prefetcher.h
deleted file mode 100644
index b411d1b..0000000
--- a/media/libstagefright/include/Prefetcher.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 PREFETCHER_H_
-
-#define PREFETCHER_H_
-
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
-
-namespace android {
-
-struct MediaSource;
-struct PrefetchedSource;
-
-struct Prefetcher : public RefBase {
- Prefetcher();
-
- // Given an existing MediaSource returns a new MediaSource
- // that will benefit from prefetching/caching the original one.
- sp<MediaSource> addSource(const sp<MediaSource> &source);
-
- int64_t getCachedDurationUs(bool *noMoreData = NULL);
-
- // If provided (non-NULL), "continueFunc" will be called repeatedly
- // while preparing and preparation will finish early if it returns
- // false. In this case "-EINTR" is returned as a result.
- status_t prepare(
- bool (*continueFunc)(void *cookie) = NULL,
- void *cookie = NULL);
-
-protected:
- virtual ~Prefetcher();
-
-private:
- Mutex mLock;
- Condition mCondition;
-
- Vector<wp<PrefetchedSource> > mSources;
- android_thread_id_t mThread;
- bool mDone;
- bool mThreadExited;
-
- void startThread();
- void stopThread();
-
- static int ThreadWrapper(void *me);
- void threadFunc();
-
- Prefetcher(const Prefetcher &);
- Prefetcher &operator=(const Prefetcher &);
-};
-
-} // namespace android
-
-#endif // PREFETCHER_H_