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