blob: d1f8cd415e14a74c1ce89e909ca6e76d011a015d [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#undef NDEBUG
#include <assert.h>
#include <stdlib.h>
#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/HTTPStream.h>
#include <media/stagefright/string.h>
namespace android {
HTTPDataSource::HTTPDataSource(const char *uri)
: mHost(NULL),
mPort(0),
mPath(NULL),
mBuffer(malloc(kBufferSize)),
mBufferLength(0),
mBufferOffset(0) {
assert(!strncasecmp("http://", uri, 7));
string host;
string path;
int port;
char *slash = strchr(uri + 7, '/');
if (slash == NULL) {
host = uri + 7;
path = "/";
} else {
host = string(uri + 7, slash - (uri + 7));
path = slash;
}
char *colon = strchr(host.c_str(), ':');
if (colon == NULL) {
port = 80;
} else {
char *end;
long tmp = strtol(colon + 1, &end, 10);
assert(end > colon + 1);
assert(tmp > 0 && tmp < 65536);
port = tmp;
host = string(host, 0, colon - host.c_str());
}
LOGI("Connecting to host '%s', port %d, path '%s'",
host.c_str(), port, path.c_str());
mHost = strdup(host.c_str());
mPort = port;
mPath = strdup(path.c_str());
status_t err = mHttp.connect(mHost, mPort);
assert(err == OK);
}
HTTPDataSource::HTTPDataSource(const char *host, int port, const char *path)
: mHost(strdup(host)),
mPort(port),
mPath(strdup(path)),
mBuffer(malloc(kBufferSize)),
mBufferLength(0),
mBufferOffset(0) {
status_t err = mHttp.connect(mHost, mPort);
assert(err == OK);
}
HTTPDataSource::~HTTPDataSource() {
mHttp.disconnect();
free(mBuffer);
mBuffer = NULL;
free(mPath);
mPath = NULL;
}
ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
if (offset >= mBufferOffset && offset < mBufferOffset + mBufferLength) {
size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
size_t copy = num_bytes_available;
if (copy > size) {
copy = size;
}
memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
return copy;
}
mBufferOffset = offset;
mBufferLength = 0;
char host[128];
sprintf(host, "Host: %s\r\n", mHost);
char range[128];
sprintf(range, "Range: bytes=%ld-%ld\r\n\r\n",
mBufferOffset, mBufferOffset + kBufferSize - 1);
int http_status;
status_t err;
int attempt = 1;
for (;;) {
if ((err = mHttp.send("GET ")) != OK
|| (err = mHttp.send(mPath)) != OK
|| (err = mHttp.send(" HTTP/1.1\r\n")) != OK
|| (err = mHttp.send(host)) != OK
|| (err = mHttp.send(range)) != OK
|| (err = mHttp.send("\r\n")) != OK
|| (err = mHttp.receive_header(&http_status)) != OK) {
if (attempt == 3) {
return err;
}
mHttp.connect(mHost, mPort);
++attempt;
} else {
break;
}
}
if ((http_status / 100) != 2) {
return UNKNOWN_ERROR;
}
string value;
if (!mHttp.find_header_value("Content-Length", &value)) {
return UNKNOWN_ERROR;
}
char *end;
unsigned long contentLength = strtoul(value.c_str(), &end, 10);
ssize_t num_bytes_received = mHttp.receive(mBuffer, contentLength);
if (num_bytes_received <= 0) {
return num_bytes_received;
}
mBufferLength = (size_t)num_bytes_received;
size_t copy = mBufferLength;
if (copy > size) {
copy = size;
}
memcpy(data, mBuffer, copy);
return copy;
}
} // namespace android