am 3ee447e4: am 75c1c8e3: Merge "Initial check in of a new http data source implementation." into kraken
This commit is contained in:
265
media/libstagefright/NuHTTPDataSource.cpp
Normal file
265
media/libstagefright/NuHTTPDataSource.cpp
Normal file
@@ -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
|
||||
59
media/libstagefright/include/NuHTTPDataSource.h
Normal file
59
media/libstagefright/include/NuHTTPDataSource.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#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_
|
||||
Reference in New Issue
Block a user