summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/NuHTTPDataSource.cpp
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-06-10 11:08:43 -0700
committerAndreas Huber <andih@google.com>2010-06-10 11:22:56 -0700
commit163c493b50bb8473319a942c8feb4528cdc56c11 (patch)
tree6908595fcd377b556ad457ceec9cb5802fb501d1 /media/libstagefright/NuHTTPDataSource.cpp
parent66b89a62aa6d5599ad4d11d891b5028587d4c6be (diff)
downloadframeworks_base-163c493b50bb8473319a942c8feb4528cdc56c11.zip
frameworks_base-163c493b50bb8473319a942c8feb4528cdc56c11.tar.gz
frameworks_base-163c493b50bb8473319a942c8feb4528cdc56c11.tar.bz2
Initial check in of a new http data source implementation.
Change-Id: I17c358288ffe9ef690d702c58723c766d0a0cf21
Diffstat (limited to 'media/libstagefright/NuHTTPDataSource.cpp')
-rw-r--r--media/libstagefright/NuHTTPDataSource.cpp265
1 files changed, 265 insertions, 0 deletions
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
new file mode 100644
index 0000000..8587c1b
--- /dev/null
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -0,0 +1,265 @@
+//#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