From edbb4d8f398dfbce7b766d4bd207685dab582847 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 12 Mar 2010 08:59:22 -0800 Subject: [PATCH] HTTPStream and HTTPDataSource now support cancellation of the connection process, AwesomePlayer takes advantage of this in cases where ::reset() or ::suspend() is called while in the preparation phase to bail out early. Also fixes in issue where the audio codec was not properly stopped if no audio player object ever took ownership. Change-Id: I6d73defe6d276693853a469db267bb2668d07af5 related-to-bugs: 2475845,2414536 --- include/media/stagefright/HTTPDataSource.h | 23 +++- media/libstagefright/AwesomePlayer.cpp | 74 +++++++++++- media/libstagefright/DataSource.cpp | 7 +- media/libstagefright/HTTPDataSource.cpp | 110 ++++++++++++++---- media/libstagefright/HTTPStream.cpp | 44 ++++--- .../StagefrightMetadataRetriever.cpp | 1 - media/libstagefright/include/AwesomePlayer.h | 15 ++- media/libstagefright/include/HTTPStream.h | 3 + 8 files changed, 219 insertions(+), 58 deletions(-) diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h index 23522bd0b8828..b6176eb374d53 100644 --- a/include/media/stagefright/HTTPDataSource.h +++ b/include/media/stagefright/HTTPDataSource.h @@ -20,6 +20,7 @@ #include #include +#include namespace android { @@ -35,6 +36,9 @@ public: const char *uri, const KeyedVector *headers = NULL); + status_t connect(); + void disconnect(); + virtual status_t initCheck() const; virtual ssize_t readAt(off_t offset, void *data, size_t size); @@ -53,8 +57,21 @@ private: kBufferSize = 32 * 1024 }; + enum State { + DISCONNECTED, + CONNECTING, + CONNECTED + }; + + State mState; + mutable Mutex mStateLock; + String8 mHeaders; + String8 mStartingHost; + String8 mStartingPath; + int mStartingPort; + HTTPStream *mHttp; char *mHost; int mPort; @@ -67,11 +84,7 @@ private: bool mContentLengthValid; unsigned long long mContentLength; - status_t mInitCheck; - - void init( - const char *_host, int port, const char *_path, - const KeyedVector *headers); + void init(const KeyedVector *headers); ssize_t sendRangeRequest(size_t offset); void initHeaders(const KeyedVector *overrides); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index e00ba47098d96..10b7be349103d 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -318,6 +319,14 @@ void AwesomePlayer::reset() { } void AwesomePlayer::reset_l() { + if (mFlags & PREPARING) { + mFlags |= PREPARE_CANCELLED; + if (mConnectingDataSource != NULL) { + LOGI("interrupting the connection process"); + mConnectingDataSource->disconnect(); + } + } + while (mFlags & PREPARING) { mPreparedCondition.wait(mLock); } @@ -337,6 +346,12 @@ void AwesomePlayer::reset_l() { // If we did this later, audio would continue playing while we // shutdown the video-related resources and the player appear to // not be as responsive to a reset request. + if (mAudioPlayer == NULL && mAudioSource != NULL) { + // If we had an audio player, it would have effectively + // taken possession of the audio source and stopped it when + // _it_ is stopped. Otherwise this is still our responsibility. + mAudioSource->stop(); + } mAudioSource.clear(); if (mTimeSource != mAudioPlayer) { @@ -1039,8 +1054,29 @@ status_t AwesomePlayer::prepareAsync_l() { } status_t AwesomePlayer::finishSetDataSource_l() { - sp dataSource = - DataSource::CreateFromURI(mUri.string(), &mUriHeaders); + sp dataSource; + + if (!strncasecmp("http://", mUri.string(), 7)) { + mConnectingDataSource = new HTTPDataSource(mUri, &mUriHeaders); + + mLock.unlock(); + status_t err = mConnectingDataSource->connect(); + mLock.lock(); + + if (err != OK) { + mConnectingDataSource.clear(); + + LOGI("mConnectingDataSource->connect() returned %d", err); + return err; + } + + dataSource = new CachingDataSource( + mConnectingDataSource, 32 * 1024, 20); + + mConnectingDataSource.clear(); + } else { + dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders); + } if (dataSource == NULL) { return UNKNOWN_ERROR; @@ -1067,7 +1103,7 @@ void AwesomePlayer::abortPrepare(status_t err) { } mPrepareResult = err; - mFlags &= ~PREPARING; + mFlags &= ~(PREPARING|PREPARE_CANCELLED); mAsyncPrepareEvent = NULL; mPreparedCondition.broadcast(); } @@ -1078,6 +1114,12 @@ void AwesomePlayer::onPrepareAsyncEvent() { { Mutex::Autolock autoLock(mLock); + if (mFlags & PREPARE_CANCELLED) { + LOGI("prepare was cancelled before doing anything"); + abortPrepare(UNKNOWN_ERROR); + return; + } + if (mUri.size() > 0) { status_t err = finishSetDataSource_l(); @@ -1109,7 +1151,19 @@ void AwesomePlayer::onPrepareAsyncEvent() { } if (prefetcher != NULL) { + { + Mutex::Autolock autoLock(mLock); + if (mFlags & PREPARE_CANCELLED) { + LOGI("prepare was cancelled before preparing the prefetcher"); + abortPrepare(UNKNOWN_ERROR); + return; + } + } + + LOGI("calling prefetcher->prepare()"); prefetcher->prepare(); + LOGV("prefetcher is done preparing"); + prefetcher.clear(); } @@ -1126,20 +1180,28 @@ void AwesomePlayer::onPrepareAsyncEvent() { } mPrepareResult = OK; - mFlags &= ~PREPARING; + mFlags &= ~(PREPARING|PREPARE_CANCELLED); mFlags |= PREPARED; mAsyncPrepareEvent = NULL; mPreparedCondition.broadcast(); } status_t AwesomePlayer::suspend() { - LOGI("suspend"); + LOGV("suspend"); Mutex::Autolock autoLock(mLock); if (mSuspensionState != NULL) { return INVALID_OPERATION; } + if (mFlags & PREPARING) { + mFlags |= PREPARE_CANCELLED; + if (mConnectingDataSource != NULL) { + LOGI("interrupting the connection process"); + mConnectingDataSource->disconnect(); + } + } + while (mFlags & PREPARING) { mPreparedCondition.wait(mLock); } @@ -1180,7 +1242,7 @@ status_t AwesomePlayer::suspend() { } status_t AwesomePlayer::resume() { - LOGI("resume"); + LOGV("resume"); Mutex::Autolock autoLock(mLock); if (mSuspensionState == NULL) { diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 8468a07dd6d89..284e3bc47020a 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -101,8 +101,11 @@ sp DataSource::CreateFromURI( if (!strncasecmp("file://", uri, 7)) { source = new FileSource(uri + 7); } else if (!strncasecmp("http://", uri, 7)) { - source = new HTTPDataSource(uri, headers); - source = new CachingDataSource(source, 32 * 1024, 20); + sp httpSource = new HTTPDataSource(uri, headers); + if (httpSource->connect() != OK) { + return NULL; + } + source = new CachingDataSource(httpSource, 32 * 1024, 20); } else { // Assume it's a filename. source = new FileSource(uri); diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp index d79c1bd04a786..451fc2639ea97 100644 --- a/media/libstagefright/HTTPDataSource.cpp +++ b/media/libstagefright/HTTPDataSource.cpp @@ -126,41 +126,71 @@ HTTPDataSource::HTTPDataSource( host = string(host, 0, colon - host.c_str()); } - init(host.c_str(), port, path.c_str(), headers); + mStartingHost = host.c_str(); + mStartingPath = path.c_str(); + mStartingPort = port; + + init(headers); } HTTPDataSource::HTTPDataSource( const char *_host, int port, const char *_path, const KeyedVector *headers) { - init(_host, port, _path, headers); + mStartingHost = _host; + mStartingPath = _path; + mStartingPort = port; + + init(headers); } -void HTTPDataSource::init( - const char *_host, int port, const char *_path, - const KeyedVector *headers) { +void HTTPDataSource::init(const KeyedVector *headers) { + mState = DISCONNECTED; mHttp = new HTTPStream; + mHost = NULL; mPort = 0; - mPath = NULL, - mBuffer = malloc(kBufferSize); - mBufferLength = 0; - mBufferOffset = 0; - mContentLengthValid = false; + mPath = NULL; initHeaders(headers); - string host = _host; - string path = _path; + mBuffer = malloc(kBufferSize); + mBufferLength = 0; + mBufferOffset = 0; +} + +status_t HTTPDataSource::connect() { + { + Mutex::Autolock autoLock(mStateLock); + + if (mState != DISCONNECTED) { + return ERROR_ALREADY_CONNECTED; + } + + mState = CONNECTING; + } + + mContentLengthValid = false; + + string host = mStartingHost.string(); + string path = mStartingPath.string(); + int port = mStartingPort; LOGI("Connecting to host '%s', port %d, path '%s'", host.c_str(), port, path.c_str()); int numRedirectsRemaining = 5; do { - mInitCheck = mHttp->connect(host.c_str(), port); + status_t err = mHttp->connect(host.c_str(), port); - if (mInitCheck != OK) { - return; + if (err != OK) { + Mutex::Autolock autoLock(mStateLock); + + if (mState != CONNECTING) { + LOGV("connect() cancelled"); + } + mState = DISCONNECTED; + + return err; } } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port) && numRedirectsRemaining-- > 0); @@ -175,17 +205,44 @@ void HTTPDataSource::init( mHost = strdup(host.c_str()); mPort = port; mPath = strdup(path.c_str()); + + 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 { - return mInitCheck; + Mutex::Autolock autoLock(mStateLock); + + return (mState == CONNECTED) ? (status_t)OK : ERROR_NOT_CONNECTED; } status_t HTTPDataSource::getSize(off_t *size) { *size = 0; - if (mInitCheck != OK) { - return mInitCheck; + { + Mutex::Autolock autoLock(mStateLock); + if (mState != CONNECTED) { + return ERROR_NOT_CONNECTED; + } } if (!mContentLengthValid) { @@ -198,7 +255,10 @@ status_t HTTPDataSource::getSize(off_t *size) { } HTTPDataSource::~HTTPDataSource() { - mHttp->disconnect(); + disconnect(); + + delete mHttp; + mHttp = NULL; free(mBuffer); mBuffer = NULL; @@ -212,9 +272,6 @@ HTTPDataSource::~HTTPDataSource() { free(mHost); mHost = NULL; } - - delete mHttp; - mHttp = NULL; } ssize_t HTTPDataSource::sendRangeRequest(size_t offset) { @@ -271,6 +328,13 @@ ssize_t HTTPDataSource::sendRangeRequest(size_t offset) { ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) { LOGV("readAt %ld, size %d", offset, size); + { + 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); @@ -304,7 +368,7 @@ ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) { mBufferOffset = offset; if (mContentLengthValid - && mBufferOffset + contentLength >= mContentLength) { + && 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. diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index 2c5da686b50ee..66eadf68f6a07 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -44,12 +44,19 @@ HTTPStream::~HTTPStream() { } status_t HTTPStream::connect(const char *server, int port) { + Mutex::Autolock autoLock(mLock); + status_t err = OK; if (mState == CONNECTED) { return ERROR_ALREADY_CONNECTED; } + struct hostent *ent = gethostbyname(server); + if (ent == NULL) { + return ERROR_UNKNOWN_HOST; + } + CHECK_EQ(mSocket, -1); mSocket = socket(AF_INET, SOCK_STREAM, 0); @@ -57,11 +64,11 @@ status_t HTTPStream::connect(const char *server, int port) { return UNKNOWN_ERROR; } - struct hostent *ent = gethostbyname(server); - if (ent == NULL) { - err = ERROR_UNKNOWN_HOST; - goto exit1; - } + mState = CONNECTING; + + int s = mSocket; + + mLock.unlock(); struct sockaddr_in addr; addr.sin_family = AF_INET; @@ -69,24 +76,31 @@ status_t HTTPStream::connect(const char *server, int port) { addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); - if (::connect(mSocket, (const struct sockaddr *)&addr, sizeof(addr)) < 0) { - err = ERROR_CANNOT_CONNECT; - goto exit1; + int res = ::connect(s, (const struct sockaddr *)&addr, sizeof(addr)); + + mLock.lock(); + + if (mState != CONNECTING) { + return UNKNOWN_ERROR; + } + + if (res < 0) { + close(mSocket); + mSocket = -1; + + mState = READY; + return UNKNOWN_ERROR; } mState = CONNECTED; return OK; - -exit1: - close(mSocket); - mSocket = -1; - - return err; } status_t HTTPStream::disconnect() { - if (mState != CONNECTED) { + Mutex::Autolock autoLock(mLock); + + if (mState != CONNECTED && mState != CONNECTING) { return ERROR_NOT_CONNECTED; } diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index c0cf97cd628a9..4679207691f1b 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 7106524afbb55..32c28c1ddab29 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -87,12 +88,13 @@ private: friend struct AwesomeEvent; enum Flags { - PLAYING = 1, - LOOPING = 2, - FIRST_FRAME = 4, - PREPARING = 8, - PREPARED = 16, - AT_EOS = 32, + PLAYING = 1, + LOOPING = 2, + FIRST_FRAME = 4, + PREPARING = 8, + PREPARED = 16, + AT_EOS = 32, + PREPARE_CANCELLED = 64, }; mutable Mutex mLock; @@ -160,6 +162,7 @@ private: MediaBuffer *mVideoBuffer; sp mPrefetcher; + sp mConnectingDataSource; struct SuspensionState { String8 mUri; diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h index 43ef614d71a6e..5d638f386d453 100644 --- a/media/libstagefright/include/HTTPStream.h +++ b/media/libstagefright/include/HTTPStream.h @@ -24,6 +24,7 @@ #include #include +#include namespace android { @@ -54,10 +55,12 @@ public: private: enum State { READY, + CONNECTING, CONNECTED }; State mState; + Mutex mLock; int mSocket; KeyedVector mHeaders;