summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-11-18 11:03:48 -0800
committerAndreas Huber <andih@google.com>2010-11-18 12:45:12 -0800
commit7d23aa2a8354046ae0390eb5ad492346af5bce0f (patch)
treebca61c738bda9d21f15ab9a22e49388210e5a6b2
parent14d32754d3a9c1ccf49188c489f224800cd747a7 (diff)
downloadframeworks_av-7d23aa2a8354046ae0390eb5ad492346af5bce0f.zip
frameworks_av-7d23aa2a8354046ae0390eb5ad492346af5bce0f.tar.gz
frameworks_av-7d23aa2a8354046ae0390eb5ad492346af5bce0f.tar.bz2
Support for "chunked" HTTP transfer encoding.
Change-Id: I2f20d2d9ec0fa0c840b429049b0385289a30e774 related-to-bug: 3205131
-rw-r--r--media/libstagefright/NuHTTPDataSource.cpp106
-rw-r--r--media/libstagefright/httplive/LiveSource.cpp35
-rw-r--r--media/libstagefright/include/HTTPStream.h8
-rw-r--r--media/libstagefright/include/NuHTTPDataSource.h8
4 files changed, 136 insertions, 21 deletions
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 40f501a..c3c0d81 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -69,6 +69,8 @@ NuHTTPDataSource::NuHTTPDataSource()
mOffset(0),
mContentLength(0),
mContentLengthValid(false),
+ mHasChunkedTransferEncoding(false),
+ mChunkDataBytesLeft(0),
mNumBandwidthHistoryItems(0),
mTotalTransferTimeUs(0),
mTotalTransferBytes(0),
@@ -193,17 +195,27 @@ status_t NuHTTPDataSource::connect(
return ERROR_IO;
}
+ mHasChunkedTransferEncoding = false;
+
{
string value;
- if (mHTTP.find_header_value("Transfer-Encoding", &value)) {
- // We don't currently support any transfer encodings.
-
- mState = DISCONNECTED;
- mHTTP.disconnect();
-
- LOGE("We don't support '%s' transfer encoding.", value.c_str());
-
- return ERROR_UNSUPPORTED;
+ if (mHTTP.find_header_value("Transfer-Encoding", &value)
+ || mHTTP.find_header_value("Transfer-encoding", &value)) {
+ // We don't currently support any transfer encodings but
+ // chunked.
+
+ if (!strcasecmp(value.c_str(), "chunked")) {
+ LOGI("Chunked transfer encoding applied.");
+ mHasChunkedTransferEncoding = true;
+ mChunkDataBytesLeft = 0;
+ } else {
+ mState = DISCONNECTED;
+ mHTTP.disconnect();
+
+ LOGE("We don't support '%s' transfer encoding.", value.c_str());
+
+ return ERROR_UNSUPPORTED;
+ }
}
}
@@ -216,8 +228,17 @@ status_t NuHTTPDataSource::connect(
&& ParseSingleUnsignedLong(value.c_str(), &x)) {
mContentLength = (off_t)x;
mContentLengthValid = true;
+ } else {
+ LOGW("Server did not give us the content length!");
}
} else {
+ if (httpStatus != 206 /* Partial Content */) {
+ // We requested a range but the server didn't support that.
+ LOGE("We requested a range but the server didn't "
+ "support that.");
+ return ERROR_UNSUPPORTED;
+ }
+
string value;
unsigned long x;
if (mHTTP.find_header_value(string("Content-Range"), &value)) {
@@ -245,6 +266,71 @@ status_t NuHTTPDataSource::initCheck() const {
return mState == CONNECTED ? OK : NO_INIT;
}
+ssize_t NuHTTPDataSource::internalRead(void *data, size_t size) {
+ if (!mHasChunkedTransferEncoding) {
+ return mHTTP.receive(data, size);
+ }
+
+ if (mChunkDataBytesLeft < 0) {
+ return 0;
+ } else if (mChunkDataBytesLeft == 0) {
+ char line[1024];
+ status_t err = mHTTP.receive_line(line, sizeof(line));
+
+ if (err != OK) {
+ return err;
+ }
+
+ LOGV("line = '%s'", line);
+
+ char *end;
+ unsigned long n = strtoul(line, &end, 16);
+
+ if (end == line || (*end != ';' && *end != '\0')) {
+ LOGE("malformed HTTP chunk '%s'", line);
+ return ERROR_MALFORMED;
+ }
+
+ mChunkDataBytesLeft = n;
+ LOGV("chunk data size = %lu", n);
+
+ if (mChunkDataBytesLeft == 0) {
+ mChunkDataBytesLeft = -1;
+ return 0;
+ }
+
+ // fall through
+ }
+
+ if (size > (size_t)mChunkDataBytesLeft) {
+ size = mChunkDataBytesLeft;
+ }
+
+ ssize_t n = mHTTP.receive(data, size);
+
+ if (n < 0) {
+ return n;
+ }
+
+ mChunkDataBytesLeft -= (size_t)n;
+
+ if (mChunkDataBytesLeft == 0) {
+ char line[1024];
+ status_t err = mHTTP.receive_line(line, sizeof(line));
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (line[0] != '\0') {
+ LOGE("missing HTTP chunk terminator.");
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return n;
+}
+
ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) {
LOGV("readAt offset %ld, size %d", offset, size);
@@ -275,7 +361,7 @@ ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) {
int64_t startTimeUs = ALooper::GetNowUs();
ssize_t n =
- mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead);
+ internalRead((uint8_t *)data + numBytesRead, size - numBytesRead);
if (n < 0) {
return n;
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
index f9d27eb..c19b6f7 100644
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ b/media/libstagefright/httplive/LiveSource.cpp
@@ -555,20 +555,41 @@ status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
status_t err = source->getSize(&size);
if (err != OK) {
- return err;
+ size = 65536;
}
sp<ABuffer> buffer = new ABuffer(size);
- size_t offset = 0;
- while (offset < (size_t)size) {
+ buffer->setRange(0, 0);
+
+ for (;;) {
+ size_t bufferRemaining = buffer->capacity() - buffer->size();
+
+ if (bufferRemaining == 0) {
+ bufferRemaining = 32768;
+
+ LOGV("increasing download buffer to %d bytes",
+ buffer->size() + bufferRemaining);
+
+ sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
+ memcpy(copy->data(), buffer->data(), buffer->size());
+ copy->setRange(0, buffer->size());
+
+ buffer = copy;
+ }
+
ssize_t n = source->readAt(
- offset, buffer->data() + offset, size - offset);
+ buffer->size(), buffer->data() + buffer->size(),
+ bufferRemaining);
- if (n <= 0) {
- return ERROR_IO;
+ if (n < 0) {
+ return err;
+ }
+
+ if (n == 0) {
+ break;
}
- offset += n;
+ buffer->setRange(0, buffer->size() + (size_t)n);
}
*out = buffer;
diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h
index 35b0865..793798f 100644
--- a/media/libstagefright/include/HTTPStream.h
+++ b/media/libstagefright/include/HTTPStream.h
@@ -55,6 +55,10 @@ public:
// Pass a negative value to disable the timeout.
void setReceiveTimeout(int seconds);
+ // Receive a line of data terminated by CRLF, line will be '\0' terminated
+ // _excluding_ the termianting CRLF.
+ status_t receive_line(char *line, size_t size);
+
private:
enum State {
READY,
@@ -68,10 +72,6 @@ private:
KeyedVector<string, string> mHeaders;
- // Receive a line of data terminated by CRLF, line will be '\0' terminated
- // _excluding_ the termianting CRLF.
- status_t receive_line(char *line, size_t size);
-
HTTPStream(const HTTPStream &);
HTTPStream &operator=(const HTTPStream &);
};
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
index 0b840bd..c42691f 100644
--- a/media/libstagefright/include/NuHTTPDataSource.h
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -64,6 +64,11 @@ private:
off_t mOffset;
off_t mContentLength;
bool mContentLengthValid;
+ bool mHasChunkedTransferEncoding;
+
+ // The number of data bytes in the current chunk before any subsequent
+ // chunk header (or -1 if no more chunks).
+ ssize_t mChunkDataBytesLeft;
List<BandwidthEntry> mBandwidthHistory;
size_t mNumBandwidthHistoryItems;
@@ -81,6 +86,9 @@ private:
const String8 &headers,
off_t offset);
+ // Read up to "size" bytes of data, respect transfer encoding.
+ ssize_t internalRead(void *data, size_t size);
+
void applyTimeoutResponse();
void addBandwidthMeasurement_l(size_t numBytes, int64_t delayUs);