diff options
author | Andreas Huber <andih@google.com> | 2010-11-18 13:19:07 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-11-18 13:19:07 -0800 |
commit | 17468ee8dbfe5eb0b9a4f72be80cbb92a5134800 (patch) | |
tree | 2df8c45b5ee7d67a0fd57739823a250738dc9de9 | |
parent | 22c86bdeabc3e7e65913136e44e30aceb1685f4f (diff) | |
parent | 7d23aa2a8354046ae0390eb5ad492346af5bce0f (diff) | |
download | frameworks_av-17468ee8dbfe5eb0b9a4f72be80cbb92a5134800.zip frameworks_av-17468ee8dbfe5eb0b9a4f72be80cbb92a5134800.tar.gz frameworks_av-17468ee8dbfe5eb0b9a4f72be80cbb92a5134800.tar.bz2 |
Merge "Support for "chunked" HTTP transfer encoding."
-rw-r--r-- | media/libstagefright/NuHTTPDataSource.cpp | 106 | ||||
-rw-r--r-- | media/libstagefright/httplive/LiveSource.cpp | 35 | ||||
-rw-r--r-- | media/libstagefright/include/HTTPStream.h | 8 | ||||
-rw-r--r-- | media/libstagefright/include/NuHTTPDataSource.h | 8 |
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); |