Loading media/libstagefright/NuHTTPDataSource.cpp 0 → 100644 +265 −0 Original line number Diff line number Diff line //#define LOG_NDEBUG 0 #define LOG_TAG "NuHTTPDataSource" #include <utils/Log.h> #include "include/NuHTTPDataSource.h" #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaErrors.h> namespace android { static bool ParseSingleUnsignedLong( const char *from, unsigned long *x) { char *end; *x = strtoul(from, &end, 10); if (end == from || *end != '\0') { return false; } return true; } static bool ParseURL( const char *url, String8 *host, unsigned *port, String8 *path) { host->setTo(""); *port = 0; path->setTo(""); if (strncasecmp("http://", url, 7)) { return false; } const char *slashPos = strchr(&url[7], '/'); if (slashPos == NULL) { host->setTo(&url[7]); path->setTo("/"); } else { host->setTo(&url[7], slashPos - &url[7]); path->setTo(slashPos); } char *colonPos = strchr(host->string(), ':'); if (colonPos != NULL) { unsigned long x; if (!ParseSingleUnsignedLong(colonPos + 1, &x) || x >= 65536) { return false; } *port = x; size_t colonOffset = colonPos - host->string(); String8 tmp(host->string(), colonOffset); *host = tmp; } else { *port = 80; } return true; } NuHTTPDataSource::NuHTTPDataSource() : mState(DISCONNECTED), mPort(0), mOffset(0), mContentLength(0), mContentLengthValid(false) { } NuHTTPDataSource::~NuHTTPDataSource() { } status_t NuHTTPDataSource::connect(const char *uri, off_t offset) { String8 host, path; unsigned port; if (!ParseURL(uri, &host, &port, &path)) { return ERROR_MALFORMED; } return connect(host, port, path, offset); } status_t NuHTTPDataSource::connect( const char *host, unsigned port, const char *path, off_t offset) { LOGI("connect to %s:%u%s @%ld", host, port, path, offset); bool needsToReconnect = true; if (mState == CONNECTED && host == mHost && port == mPort && offset == mOffset) { if (mContentLengthValid && mOffset == mContentLength) { LOGI("Didn't have to reconnect, old one's still good."); needsToReconnect = false; } } mHost = host; mPort = port; mPath = path; status_t err = OK; mState = CONNECTING; if (needsToReconnect) { mHTTP.disconnect(); err = mHTTP.connect(host, port); } if (err != OK) { mState = DISCONNECTED; } else if (mState != CONNECTING) { err = UNKNOWN_ERROR; } else { mState = CONNECTED; mOffset = offset; mContentLength = 0; mContentLengthValid = false; String8 request("GET "); request.append(mPath); request.append(" HTTP/1.1\r\n"); request.append("Host: "); request.append(mHost); request.append("\r\n"); if (offset != 0) { char rangeHeader[128]; sprintf(rangeHeader, "Range: bytes=%ld-\r\n", offset); request.append(rangeHeader); } request.append("\r\n"); int httpStatus; if ((err = mHTTP.send(request.string(), request.size())) != OK || (err = mHTTP.receive_header(&httpStatus)) != OK) { mHTTP.disconnect(); mState = DISCONNECTED; return err; } if (httpStatus == 302) { string value; CHECK(mHTTP.find_header_value("Location", &value)); mState = DISCONNECTED; mHTTP.disconnect(); return connect(value.c_str()); } CHECK(httpStatus >= 200 && httpStatus < 300); if (offset == 0) { string value; unsigned long x; if (mHTTP.find_header_value(string("Content-Length"), &value) && ParseSingleUnsignedLong(value.c_str(), &x)) { mContentLength = (off_t)x; mContentLengthValid = true; } } else { string value; unsigned long x; if (mHTTP.find_header_value(string("Content-Range"), &value)) { const char *slashPos = strchr(value.c_str(), '/'); if (slashPos != NULL && ParseSingleUnsignedLong(slashPos + 1, &x)) { mContentLength = x; mContentLengthValid = true; } } } } return err; } void NuHTTPDataSource::disconnect() { if (mState == CONNECTING || mState == CONNECTED) { mHTTP.disconnect(); } mState = DISCONNECTED; } status_t NuHTTPDataSource::initCheck() const { return mState == CONNECTED ? OK : NO_INIT; } ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) { LOGV("readAt offset %ld, size %d", offset, size); Mutex::Autolock autoLock(mLock); if (offset != mOffset) { String8 host = mHost; String8 path = mPath; status_t err = connect(host, mPort, path, offset); if (err != OK) { return err; } } if (mContentLengthValid) { size_t avail = (offset >= mContentLength) ? 0 : mContentLength - offset; if (size > avail) { size = avail; } } size_t numBytesRead = 0; while (numBytesRead < size) { ssize_t n = mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead); if (n < 0) { return n; } numBytesRead += (size_t)n; if (n == 0) { if (mContentLengthValid) { // We know the content length and made sure not to read beyond // it and yet the server closed the connection on us. return ERROR_IO; } break; } } mOffset += numBytesRead; return numBytesRead; } status_t NuHTTPDataSource::getSize(off_t *size) { *size = 0; if (mState != CONNECTED) { return ERROR_IO; } if (mContentLengthValid) { *size = mContentLength; return OK; } return ERROR_UNSUPPORTED; } uint32_t NuHTTPDataSource::flags() { return kWantsPrefetching; } } // namespace android media/libstagefright/include/NuHTTPDataSource.h 0 → 100644 +59 −0 Original line number Diff line number Diff line #ifndef NU_HTTP_DATA_SOURCE_H_ #define NU_HTTP_DATA_SOURCE_H_ #include <media/stagefright/DataSource.h> #include <utils/String8.h> #include <utils/threads.h> #include "HTTPStream.h" namespace android { struct NuHTTPDataSource : public DataSource { NuHTTPDataSource(); status_t connect(const char *uri, off_t offset = 0); status_t connect( const char *host, unsigned port, const char *path, off_t offset = 0); void disconnect(); virtual status_t initCheck() const; virtual ssize_t readAt(off_t offset, void *data, size_t size); virtual status_t getSize(off_t *size); virtual uint32_t flags(); protected: virtual ~NuHTTPDataSource(); private: enum State { DISCONNECTED, CONNECTING, CONNECTED }; Mutex mLock; State mState; String8 mHost; unsigned mPort; String8 mPath; HTTPStream mHTTP; off_t mOffset; off_t mContentLength; bool mContentLengthValid; NuHTTPDataSource(const NuHTTPDataSource &); NuHTTPDataSource &operator=(const NuHTTPDataSource &); }; } // namespace android #endif // NU_HTTP_DATA_SOURCE_H_ Loading
media/libstagefright/NuHTTPDataSource.cpp 0 → 100644 +265 −0 Original line number Diff line number Diff line //#define LOG_NDEBUG 0 #define LOG_TAG "NuHTTPDataSource" #include <utils/Log.h> #include "include/NuHTTPDataSource.h" #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaErrors.h> namespace android { static bool ParseSingleUnsignedLong( const char *from, unsigned long *x) { char *end; *x = strtoul(from, &end, 10); if (end == from || *end != '\0') { return false; } return true; } static bool ParseURL( const char *url, String8 *host, unsigned *port, String8 *path) { host->setTo(""); *port = 0; path->setTo(""); if (strncasecmp("http://", url, 7)) { return false; } const char *slashPos = strchr(&url[7], '/'); if (slashPos == NULL) { host->setTo(&url[7]); path->setTo("/"); } else { host->setTo(&url[7], slashPos - &url[7]); path->setTo(slashPos); } char *colonPos = strchr(host->string(), ':'); if (colonPos != NULL) { unsigned long x; if (!ParseSingleUnsignedLong(colonPos + 1, &x) || x >= 65536) { return false; } *port = x; size_t colonOffset = colonPos - host->string(); String8 tmp(host->string(), colonOffset); *host = tmp; } else { *port = 80; } return true; } NuHTTPDataSource::NuHTTPDataSource() : mState(DISCONNECTED), mPort(0), mOffset(0), mContentLength(0), mContentLengthValid(false) { } NuHTTPDataSource::~NuHTTPDataSource() { } status_t NuHTTPDataSource::connect(const char *uri, off_t offset) { String8 host, path; unsigned port; if (!ParseURL(uri, &host, &port, &path)) { return ERROR_MALFORMED; } return connect(host, port, path, offset); } status_t NuHTTPDataSource::connect( const char *host, unsigned port, const char *path, off_t offset) { LOGI("connect to %s:%u%s @%ld", host, port, path, offset); bool needsToReconnect = true; if (mState == CONNECTED && host == mHost && port == mPort && offset == mOffset) { if (mContentLengthValid && mOffset == mContentLength) { LOGI("Didn't have to reconnect, old one's still good."); needsToReconnect = false; } } mHost = host; mPort = port; mPath = path; status_t err = OK; mState = CONNECTING; if (needsToReconnect) { mHTTP.disconnect(); err = mHTTP.connect(host, port); } if (err != OK) { mState = DISCONNECTED; } else if (mState != CONNECTING) { err = UNKNOWN_ERROR; } else { mState = CONNECTED; mOffset = offset; mContentLength = 0; mContentLengthValid = false; String8 request("GET "); request.append(mPath); request.append(" HTTP/1.1\r\n"); request.append("Host: "); request.append(mHost); request.append("\r\n"); if (offset != 0) { char rangeHeader[128]; sprintf(rangeHeader, "Range: bytes=%ld-\r\n", offset); request.append(rangeHeader); } request.append("\r\n"); int httpStatus; if ((err = mHTTP.send(request.string(), request.size())) != OK || (err = mHTTP.receive_header(&httpStatus)) != OK) { mHTTP.disconnect(); mState = DISCONNECTED; return err; } if (httpStatus == 302) { string value; CHECK(mHTTP.find_header_value("Location", &value)); mState = DISCONNECTED; mHTTP.disconnect(); return connect(value.c_str()); } CHECK(httpStatus >= 200 && httpStatus < 300); if (offset == 0) { string value; unsigned long x; if (mHTTP.find_header_value(string("Content-Length"), &value) && ParseSingleUnsignedLong(value.c_str(), &x)) { mContentLength = (off_t)x; mContentLengthValid = true; } } else { string value; unsigned long x; if (mHTTP.find_header_value(string("Content-Range"), &value)) { const char *slashPos = strchr(value.c_str(), '/'); if (slashPos != NULL && ParseSingleUnsignedLong(slashPos + 1, &x)) { mContentLength = x; mContentLengthValid = true; } } } } return err; } void NuHTTPDataSource::disconnect() { if (mState == CONNECTING || mState == CONNECTED) { mHTTP.disconnect(); } mState = DISCONNECTED; } status_t NuHTTPDataSource::initCheck() const { return mState == CONNECTED ? OK : NO_INIT; } ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) { LOGV("readAt offset %ld, size %d", offset, size); Mutex::Autolock autoLock(mLock); if (offset != mOffset) { String8 host = mHost; String8 path = mPath; status_t err = connect(host, mPort, path, offset); if (err != OK) { return err; } } if (mContentLengthValid) { size_t avail = (offset >= mContentLength) ? 0 : mContentLength - offset; if (size > avail) { size = avail; } } size_t numBytesRead = 0; while (numBytesRead < size) { ssize_t n = mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead); if (n < 0) { return n; } numBytesRead += (size_t)n; if (n == 0) { if (mContentLengthValid) { // We know the content length and made sure not to read beyond // it and yet the server closed the connection on us. return ERROR_IO; } break; } } mOffset += numBytesRead; return numBytesRead; } status_t NuHTTPDataSource::getSize(off_t *size) { *size = 0; if (mState != CONNECTED) { return ERROR_IO; } if (mContentLengthValid) { *size = mContentLength; return OK; } return ERROR_UNSUPPORTED; } uint32_t NuHTTPDataSource::flags() { return kWantsPrefetching; } } // namespace android
media/libstagefright/include/NuHTTPDataSource.h 0 → 100644 +59 −0 Original line number Diff line number Diff line #ifndef NU_HTTP_DATA_SOURCE_H_ #define NU_HTTP_DATA_SOURCE_H_ #include <media/stagefright/DataSource.h> #include <utils/String8.h> #include <utils/threads.h> #include "HTTPStream.h" namespace android { struct NuHTTPDataSource : public DataSource { NuHTTPDataSource(); status_t connect(const char *uri, off_t offset = 0); status_t connect( const char *host, unsigned port, const char *path, off_t offset = 0); void disconnect(); virtual status_t initCheck() const; virtual ssize_t readAt(off_t offset, void *data, size_t size); virtual status_t getSize(off_t *size); virtual uint32_t flags(); protected: virtual ~NuHTTPDataSource(); private: enum State { DISCONNECTED, CONNECTING, CONNECTED }; Mutex mLock; State mState; String8 mHost; unsigned mPort; String8 mPath; HTTPStream mHTTP; off_t mOffset; off_t mContentLength; bool mContentLengthValid; NuHTTPDataSource(const NuHTTPDataSource &); NuHTTPDataSource &operator=(const NuHTTPDataSource &); }; } // namespace android #endif // NU_HTTP_DATA_SOURCE_H_