Commit 9227c871 authored by Francois Cartegnie's avatar Francois Cartegnie

stream_filter: dash: rewrite the http connection and remove double queuing

Ahead chunks now equals network-caching
parent 1afd4f37
...@@ -63,10 +63,10 @@ void* DASHDownloader::download (void *thread_sys) ...@@ -63,10 +63,10 @@ void* DASHDownloader::download (void *thread_sys)
do do
{ {
block_t *block = NULL; block_t *block = NULL;
ret = conManager->read(&block, BLOCKSIZE); ret = conManager->read(&block);
if(ret > 0) if(ret > 0)
buffer->put(block); buffer->put(block);
}while(ret && !buffer->getEOF()); }while(ret > 0 && !buffer->getEOF());
buffer->setEOF(true); buffer->setEOF(true);
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include "adaptationlogic/IAdaptationLogic.h" #include "adaptationlogic/IAdaptationLogic.h"
#include "buffer/BlockBuffer.h" #include "buffer/BlockBuffer.h"
#define BLOCKSIZE 32768
#define CHUNKDEFAULTBITRATE 1 #define CHUNKDEFAULTBITRATE 1
#include <iostream> #include <iostream>
......
...@@ -32,6 +32,7 @@ using namespace dash::http; ...@@ -32,6 +32,7 @@ using namespace dash::http;
Chunk::Chunk () : Chunk::Chunk () :
startByte (0), startByte (0),
endByte (0), endByte (0),
bitrate (1),
port (0), port (0),
isHostname (false), isHostname (false),
length (0), length (0),
...@@ -149,11 +150,11 @@ size_t Chunk::getPercentDownloaded () const ...@@ -149,11 +150,11 @@ size_t Chunk::getPercentDownloaded () const
{ {
return (size_t)(((float)this->bytesRead / this->length) * 100); return (size_t)(((float)this->bytesRead / this->length) * 100);
} }
IHTTPConnection* Chunk::getConnection () const HTTPConnection* Chunk::getConnection () const
{ {
return this->connection; return this->connection;
} }
void Chunk::setConnection (IHTTPConnection *connection) void Chunk::setConnection (HTTPConnection *connection)
{ {
this->connection = connection; this->connection = connection;
} }
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <vlc_common.h> #include <vlc_common.h>
#include <vlc_url.h> #include <vlc_url.h>
#include "IHTTPConnection.h" #include "HTTPConnection.h"
#include <vector> #include <vector>
#include <string> #include <string>
...@@ -59,9 +59,9 @@ namespace dash ...@@ -59,9 +59,9 @@ namespace dash
uint64_t getBytesRead () const; uint64_t getBytesRead () const;
uint64_t getBytesToRead () const; uint64_t getBytesToRead () const;
size_t getPercentDownloaded () const; size_t getPercentDownloaded () const;
IHTTPConnection* getConnection () const; HTTPConnection* getConnection () const;
void setConnection (IHTTPConnection *connection); void setConnection (HTTPConnection *connection);
void setBytesRead (uint64_t bytes); void setBytesRead (uint64_t bytes);
void setBytesToRead (uint64_t bytes); void setBytesToRead (uint64_t bytes);
void setLength (uint64_t length); void setLength (uint64_t length);
...@@ -88,7 +88,7 @@ namespace dash ...@@ -88,7 +88,7 @@ namespace dash
uint64_t length; uint64_t length;
uint64_t bytesRead; uint64_t bytesRead;
uint64_t bytesToRead; uint64_t bytesToRead;
IHTTPConnection *connection; HTTPConnection *connection;
}; };
} }
} }
......
...@@ -26,128 +26,88 @@ ...@@ -26,128 +26,88 @@
#endif #endif
#include "HTTPConnection.h" #include "HTTPConnection.h"
#include <vlc_network.h> #include "Chunk.h"
#include <sstream> #include <sstream>
#include <vlc_stream.h>
using namespace dash::http; using namespace dash::http;
HTTPConnection::HTTPConnection (stream_t *stream) : HTTPConnection::HTTPConnection (stream_t *stream, Chunk *chunk_) :
IHTTPConnection (stream), IHTTPConnection (stream)
peekBufferLen (0)
{ {
this->peekBuffer = new uint8_t[PEEKBUFFER]; toRead = 0;
chunk = NULL;
bindChunk(chunk_);
} }
HTTPConnection::~HTTPConnection ()
std::string HTTPConnection::buildRequestHeader(const std::string &path) const
{ {
delete[] this->peekBuffer; std::string req = IHTTPConnection::buildRequestHeader(path);
this->closeSocket(); return req.append("Connection: close\r\n");
} }
int HTTPConnection::read (void *p_buffer, size_t len) void HTTPConnection::bindChunk(Chunk *chunk_)
{ {
if(this->peekBufferLen == 0) if(chunk_ == chunk)
return;
if (chunk_)
{ {
ssize_t size = net_Read(stream, httpSocket, NULL, p_buffer, len, false); chunk_->setConnection(this);
if(!chunk->hasHostname())
if(size <= 0) chunk->setUrl(getUrlRelative(chunk));
return 0;
return size;
} }
chunk = chunk_;
memcpy(p_buffer, this->peekBuffer, this->peekBufferLen);
int ret = this->peekBufferLen;
this->peekBufferLen = 0;
return ret;
}
int HTTPConnection::peek (const uint8_t **pp_peek, size_t i_peek)
{
if(this->peekBufferLen == 0)
this->peekBufferLen = this->read(this->peekBuffer, PEEKBUFFER);
int size = i_peek > this->peekBufferLen ? this->peekBufferLen : i_peek;
uint8_t *peek = new uint8_t [size];
memcpy(peek, this->peekBuffer, size);
*pp_peek = peek;
return size;
} }
std::string HTTPConnection::getRequestHeader (const Chunk *chunk) const void HTTPConnection::releaseChunk()
{ {
return IHTTPConnection::getRequestHeader(chunk) if(chunk)
.append("Connection: close\r\n");
}
bool HTTPConnection::init (Chunk *chunk)
{
if (IHTTPConnection::init(chunk))
{ {
HeaderReply reply; chunk->setConnection(NULL);
return parseHeader(&reply); chunk = NULL;
} }
else
return false;
} }
bool HTTPConnection::parseHeader (HeaderReply *reply) void HTTPConnection::onHeader(const std::string &key,
const std::string &value)
{ {
std::string line = this->readLine(); if(key == "Content-Length")
if(line.size() == 0)
return false;
while(line.compare("\r\n"))
{ {
if(!strncasecmp(line.c_str(), "Content-Length", 14)) std::istringstream ss(value);
reply->contentLength = atoi(line.substr(15,line.size()).c_str()); size_t length;
ss >> length;
line = this->readLine(); chunk->setLength(length);
toRead = length;
if(line.size() == 0)
return false;
} }
return true;
} }
std::string HTTPConnection::readLine ()
std::string HTTPConnection::extraRequestHeaders() const
{ {
std::stringstream ss; std::stringstream ss;
char c[1]; if(chunk->usesByteRange())
ssize_t size = net_Read(stream, httpSocket, NULL, c, 1, false);
while(size >= 0)
{ {
ss << c[0]; ss << "Range: bytes=" << chunk->getStartByte() << "-";
if(c[0] == '\n') if(chunk->getEndByte())
break; ss << chunk->getEndByte();
ss << "\r\n";
size = net_Read(stream, httpSocket, NULL, c, 1, false);
} }
if(size > 0)
return ss.str(); return ss.str();
return "";
} }
bool HTTPConnection::send (const std::string& data)
{
ssize_t size = net_Write(this->stream, this->httpSocket, NULL, data.c_str(), data.size());
if (size == -1)
{
return false;
}
if ((size_t)size != data.length())
{
this->send(data.substr(size, data.size()));
}
return true; std::string HTTPConnection::getUrlRelative(const Chunk *chunk) const
{
std::stringstream ss;
ss << stream->psz_access << "://" << Helper::combinePaths(Helper::getDirectoryPath(stream->psz_path), chunk->getUrl());
return ss.str();
} }
void HTTPConnection::closeSocket ()
bool HTTPConnection::isAvailable() const
{ {
if (httpSocket >= 0) return chunk == NULL;
net_Close(httpSocket);
} }
void HTTPConnection::disconnect()
{
toRead = 0;
}
...@@ -28,41 +28,31 @@ ...@@ -28,41 +28,31 @@
#include <string> #include <string>
#include "http/IHTTPConnection.h" #include "http/IHTTPConnection.h"
#include "http/Chunk.h"
#include "Helper.h" #include "Helper.h"
#define PEEKBUFFER 4096
namespace dash namespace dash
{ {
namespace http namespace http
{ {
class Chunk;
class HTTPConnection : public IHTTPConnection class HTTPConnection : public IHTTPConnection
{ {
public: public:
HTTPConnection (stream_t *stream); HTTPConnection (stream_t *stream, Chunk *chunk = NULL);
virtual ~HTTPConnection (); virtual void bindChunk (Chunk *chunk);
virtual void onHeader (const std::string &line,
virtual bool init (Chunk *chunk); const std::string &value);
void closeSocket (); virtual bool isAvailable () const;
virtual int read (void *p_buffer, size_t len); virtual void disconnect ();
virtual int peek (const uint8_t **pp_peek, size_t i_peek); virtual void releaseChunk();
protected: protected:
size_t toRead;
Chunk *chunk;
virtual std::string extraRequestHeaders() const;
virtual std::string buildRequestHeader(const std::string &path) const;
class HeaderReply std::string getUrlRelative(const Chunk *chunk) const;
{
public:
int contentLength;
};
uint8_t *peekBuffer;
size_t peekBufferLen;
virtual bool send (const std::string& data);
bool parseHeader (HeaderReply *);
std::string readLine ();
virtual std::string getRequestHeader(const Chunk *chunk) const; /* reimpl */
}; };
} }
} }
......
...@@ -28,11 +28,13 @@ ...@@ -28,11 +28,13 @@
#include "HTTPConnectionManager.h" #include "HTTPConnectionManager.h"
#include "mpd/Segment.h" #include "mpd/Segment.h"
#include <vlc_block.h>
#include <vlc_stream.h>
using namespace dash::http; using namespace dash::http;
using namespace dash::logic; using namespace dash::logic;
const size_t HTTPConnectionManager::PIPELINE = 80;
const size_t HTTPConnectionManager::PIPELINELENGTH = 2;
const uint64_t HTTPConnectionManager::CHUNKDEFAULTBITRATE = 1; const uint64_t HTTPConnectionManager::CHUNKDEFAULTBITRATE = 1;
HTTPConnectionManager::HTTPConnectionManager (IAdaptationLogic *adaptationLogic, stream_t *stream) : HTTPConnectionManager::HTTPConnectionManager (IAdaptationLogic *adaptationLogic, stream_t *stream) :
...@@ -58,52 +60,73 @@ void HTTPConnectionManager::closeAllConnections ...@@ -58,52 +60,73 @@ void HTTPConnectionManager::closeAllConnections
vlc_delete_all(this->connectionPool); vlc_delete_all(this->connectionPool);
vlc_delete_all(this->downloadQueue); vlc_delete_all(this->downloadQueue);
} }
int HTTPConnectionManager::read(block_t **pp_block, size_t len)
ssize_t HTTPConnectionManager::read(block_t **pp_block)
{ {
if(this->downloadQueue.size() == 0) Chunk *chunk;
if(!this->addChunk(this->adaptationLogic->getNextChunk()))
return 0; if(downloadQueue.empty())
{
chunk = adaptationLogic->getNextChunk();
if(!connectChunk(chunk))
return -1;
else
downloadQueue.push_back(chunk);
}
if(this->downloadQueue.front()->getPercentDownloaded() > HTTPConnectionManager::PIPELINE && chunk = downloadQueue.front();
this->downloadQueue.size() < HTTPConnectionManager::PIPELINELENGTH)
this->addChunk(this->adaptationLogic->getNextChunk());
int ret = 0; if(chunk->getBytesRead() == 0)
{
if (!chunk->getConnection()->query(chunk->getPath()))
return -1;
}
block_t *block = block_Alloc(len); /* chunk length should be set at connect/query reply time */
size_t readsize = chunk->getBytesToRead();
if (readsize > 128000)
readsize = 32768;
block_t *block = block_Alloc(readsize);
if(!block) if(!block)
return -1; return -1;
mtime_t start = mdate(); mtime_t time = mdate();
ret = this->downloadQueue.front()->getConnection()->read(block->p_buffer, block->i_buffer); ssize_t ret = chunk->getConnection()->read(block->p_buffer, readsize);
mtime_t end = mdate(); time = mdate() - time;
block->i_length = (mtime_t)((ret * 8) / ((float)this->downloadQueue.front()->getBitrate() / 1000000));
double time = ((double)(end - start)) / 1000000; block->i_length = (mtime_t)((ret * 8) / ((float)chunk->getBitrate() / CLOCK_FREQ));
if(ret <= 0) if(ret <= 0)
{ {
block_Release(block); block_Release(block);
*pp_block = NULL;
this->bpsLastChunk = this->bpsCurrentChunk; this->bpsLastChunk = this->bpsCurrentChunk;
this->bytesReadChunk = 0; this->bytesReadChunk = 0;
this->timeChunk = 0; this->timeChunk = 0;
delete(this->downloadQueue.front()); delete(chunk);
this->downloadQueue.pop_front(); downloadQueue.pop_front();
return this->read(pp_block, len); return read(pp_block);
} }
else else
{ {
this->updateStatistics(ret, time); updateStatistics((size_t)ret, ((double)time) / CLOCK_FREQ);
block->i_buffer = ret; block->i_buffer = ret;
if (chunk->getBytesToRead() == 0)
{
chunk->onDownload(block->p_buffer, block->i_buffer);
delete chunk;
downloadQueue.pop_front();
}
} }
*pp_block = block; *pp_block = block;
return ret; return ret;
} }
void HTTPConnectionManager::attach (IDownloadRateObserver *observer) void HTTPConnectionManager::attach (IDownloadRateObserver *observer)
{ {
this->rateObservers.push_back(observer); this->rateObservers.push_back(observer);
...@@ -115,17 +138,19 @@ void HTTPConnectionManager::notify ...@@ -115,17 +138,19 @@ void HTTPConnectionManager::notify
for(size_t i = 0; i < this->rateObservers.size(); i++) for(size_t i = 0; i < this->rateObservers.size(); i++)
this->rateObservers.at(i)->downloadRateChanged(this->bpsAvg, this->bpsLastChunk); this->rateObservers.at(i)->downloadRateChanged(this->bpsAvg, this->bpsLastChunk);
} }
std::vector<PersistentConnection *> HTTPConnectionManager::getConnectionsForHost (const std::string &hostname)
{
std::vector<PersistentConnection *> cons;
for(size_t i = 0; i < this->connectionPool.size(); i++)
if(!this->connectionPool.at(i)->getHostname().compare(hostname) || !this->connectionPool.at(i)->isConnected())
cons.push_back(this->connectionPool.at(i));
return cons; PersistentConnection * HTTPConnectionManager::getConnectionForHost(const std::string &hostname)
{
std::vector<PersistentConnection *>::const_iterator it;
for(it = connectionPool.begin(); it != connectionPool.end(); it++)
{
if(!(*it)->getHostname().compare(hostname))
return *it;
}
return NULL;
} }
void HTTPConnectionManager::updateStatistics (int bytes, double time)
void HTTPConnectionManager::updateStatistics(size_t bytes, double time)
{ {
this->bytesReadSession += bytes; this->bytesReadSession += bytes;
this->bytesReadChunk += bytes; this->bytesReadChunk += bytes;
...@@ -143,29 +168,28 @@ void HTTPConnectionManager::updateStatistics ...@@ -143,29 +168,28 @@ void HTTPConnectionManager::updateStatistics
this->notify(); this->notify();
} }
bool HTTPConnectionManager::addChunk (Chunk *chunk)
bool HTTPConnectionManager::connectChunk(Chunk *chunk)
{ {
if(chunk == NULL) if(chunk == NULL)
return false; return false;
this->downloadQueue.push_back(chunk); msg_Dbg(stream, "Retrieving %s", chunk->getUrl().c_str());
std::vector<PersistentConnection *> cons = this->getConnectionsForHost(chunk->getHostname()); PersistentConnection *conn = getConnectionForHost(chunk->getHostname());
if(!conn)
if(cons.size() == 0)
{ {
PersistentConnection *con = new PersistentConnection(this->stream); conn = new PersistentConnection(stream, chunk);
this->connectionPool.push_back(con); if(!conn)
cons.push_back(con); return false;
if (!chunk->getConnection()->connect(chunk->getHostname(), chunk->getPort()))
return false;
connectionPool.push_back(conn);
} }
size_t pos = this->chunkCount % cons.size(); conn->bindChunk(chunk);
cons.at(pos)->addChunk(chunk);
chunk->setConnection(cons.at(pos));
this->chunkCount++; chunkCount++;
if(chunk->getBitrate() <= 0) if(chunk->getBitrate() <= 0)
chunk->setBitrate(HTTPConnectionManager::CHUNKDEFAULTBITRATE); chunk->setBitrate(HTTPConnectionManager::CHUNKDEFAULTBITRATE);
......
...@@ -48,8 +48,7 @@ namespace dash ...@@ -48,8 +48,7 @@ namespace dash
virtual ~HTTPConnectionManager (); virtual ~HTTPConnectionManager ();
void closeAllConnections (); void closeAllConnections ();
bool addChunk (Chunk *chunk); ssize_t read (block_t **);
int read (block_t **, size_t);
void attach (dash::logic::IDownloadRateObserver *observer); void attach (dash::logic::IDownloadRateObserver *observer);
void notify (); void notify ();
...@@ -68,12 +67,11 @@ namespace dash ...@@ -68,12 +67,11 @@ namespace dash
double timeSession; double timeSession;
double timeChunk; double timeChunk;
static const size_t PIPELINE;
static const size_t PIPELINELENGTH;
static const uint64_t CHUNKDEFAULTBITRATE; static const uint64_t CHUNKDEFAULTBITRATE;
std::vector<PersistentConnection *> getConnectionsForHost (const std::string &hostname); bool connectChunk (Chunk *chunk);
void updateStatistics (int bytes, double time); PersistentConnection * getConnectionForHost (const std::string &hostname);
void updateStatistics (size_t bytes, double time);
}; };
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "dash.hpp" #include "dash.hpp"
#include <vlc_network.h> #include <vlc_network.h>
#include <vlc_stream.h>
#include <sstream> #include <sstream>
...@@ -37,45 +38,135 @@ IHTTPConnection::IHTTPConnection(stream_t *stream_) ...@@ -37,45 +38,135 @@ IHTTPConnection::IHTTPConnection(stream_t *stream_)
IHTTPConnection::~IHTTPConnection() IHTTPConnection::~IHTTPConnection()
{ {
disconnect();
} }
bool IHTTPConnection::init(Chunk *chunk) bool IHTTPConnection::connect(const std::string &hostname, int port)
{ {
if(chunk == NULL) httpSocket = net_ConnectTCP(stream, hostname.c_str(), port);
this->hostname = hostname;
if(httpSocket == -1)
return false; return false;
if(!chunk->hasHostname()) return true;
}
bool IHTTPConnection::connected() const
{
return (httpSocket != -1);
}
void IHTTPConnection::disconnect()
{
if (httpSocket >= 0)
{ {
chunk->setUrl(getUrlRelative(chunk)); net_Close(httpSocket);
if(!chunk->hasHostname()) httpSocket = -1;
return false;
} }
}
httpSocket = net_ConnectTCP(stream, chunk->getHostname().c_str(), chunk->getPort()); bool IHTTPConnection::query(const std::string &path)
{
std::string header = buildRequestHeader(path);
header.append("\r\n");
if (!send( header ) || !parseReply())
return false;
return true;
}
if(httpSocket == -1) ssize_t IHTTPConnection::read(void *p_buffer, size_t len)
{
ssize_t size = net_Read(stream, httpSocket, NULL, p_buffer, len, true);
if(size <= 0)
return -1;
else
return size;
}
bool IHTTPConnection::send(const std::string &data)
{
return send(data.c_str(), data.length());
}
bool IHTTPConnection::send(const void *buf, size_t size)
{
if (size == 0)
return true;
if (httpSocket == -1)
return false; return false;
return send(getRequestHeader(chunk).append("\r\n")); ssize_t ret = net_Write(stream, httpSocket, NULL, buf, size);
if (ret <= 0)
return false;
if ( (size_t)ret < size )
send( ((uint8_t*)buf) + ret, size - ret );
return true;
} }
std::string IHTTPConnection::getRequestHeader(const Chunk *chunk) const bool IHTTPConnection::parseReply()
{ {
std::stringstream req; std::string line = readLine();
req << "GET " << chunk->getPath() << " HTTP/1.1\r\n" <<
"Host: " << chunk->getHostname() << "\r\n" <<
"User-Agent: " << std::string(stream->p_sys->psz_useragent) << "\r\n";
if(chunk->usesByteRange()) if(line.empty())
req << "Range: bytes=" << chunk->getStartByte() << "-" << chunk->getEndByte() << "\r\n"; return false;
return req.str(); if (line.compare(0, 9, "HTTP/1.1 ")!=0)
return false;
std::istringstream ss(line.substr(9));
int replycode;
ss >> replycode;
if (replycode != 200 && replycode != 206)
return false;
readLine();
while(!line.empty() && line.compare("\r\n"))
{
size_t split = line.find_first_of(':');
size_t value = split + 1;
while(line.at(value) == ' ')
value++;
onHeader(line.substr(0, split), line.substr(value));
line = readLine();
}
return true;
} }
std::string IHTTPConnection::getUrlRelative(const Chunk *chunk) const std::string IHTTPConnection::readLine()
{ {
std::stringstream ss; std::stringstream ss;
ss << stream->psz_access << "://" << Helper::combinePaths(Helper::getDirectoryPath(stream->psz_path), chunk->getUrl()); char c[1];
ssize_t size = net_Read(stream, httpSocket, NULL, c, 1, false);
while(size >= 0)
{
ss << c[0];
if(c[0] == '\n')
break;
size = net_Read(stream, httpSocket, NULL, c, 1, false);
}
if(size > 0)
return ss.str(); return ss.str();
return "";
}
std::string IHTTPConnection::buildRequestHeader(const std::string &path) const
{
std::stringstream req;
req << "GET " << path << " HTTP/1.1\r\n" <<
"Host: " << hostname << "\r\n" <<
"User-Agent: " << std::string(stream->p_sys->psz_useragent) << "\r\n";
req << extraRequestHeaders();
return req.str();
} }
...@@ -30,27 +30,39 @@ ...@@ -30,27 +30,39 @@
#endif #endif
#include <vlc_common.h> #include <vlc_common.h>
#include <vlc_stream.h>
#include <string> #include <string>
namespace dash namespace dash
{ {
namespace http namespace http
{ {
class Chunk;
class IHTTPConnection class IHTTPConnection
{ {
public: public:
IHTTPConnection(stream_t *stream); IHTTPConnection(stream_t *stream);
virtual ~IHTTPConnection(); virtual ~IHTTPConnection();
virtual bool init (Chunk *chunk);
virtual bool send (const std::string& data) = 0; virtual bool connect (const std::string& hostname, int port = 80);
virtual int read (void *p_buffer, size_t len) = 0; virtual bool connected () const;
virtual int peek (const uint8_t **pp_peek, size_t i_peek) = 0; virtual bool query (const std::string& path);
virtual bool send (const void *buf, size_t size);
virtual ssize_t read (void *p_buffer, size_t len);
virtual void disconnect ();
virtual bool send (const std::string &data);
protected: protected:
virtual std::string getRequestHeader(const Chunk *chunk) const;
virtual std::string getUrlRelative (const Chunk *chunk) const; virtual void onHeader (const std::string &key,
const std::string &value) = 0;
virtual std::string extraRequestHeaders() const = 0;
virtual std::string buildRequestHeader(const std::string &path) const;
bool parseReply();
std::string readLine();
std::string hostname;
stream_t *stream; stream_t *stream;
private:
int httpSocket; int httpSocket;
}; };
} }
......
...@@ -26,164 +26,97 @@ ...@@ -26,164 +26,97 @@
#endif #endif
#include "PersistentConnection.h" #include "PersistentConnection.h"
#include "Chunk.h"
#include <vlc_network.h> #include <vlc_network.h>
using namespace dash::http; using namespace dash::http;
const int PersistentConnection::RETRY = 5; PersistentConnection::PersistentConnection (stream_t *stream, Chunk *chunk) :
HTTPConnection (stream, chunk)
PersistentConnection::PersistentConnection (stream_t *stream) :
HTTPConnection (stream),
isInit (false)
{
}
PersistentConnection::~PersistentConnection ()
{ {
queryOk = false;
retries = 0;
} }
#include <cassert>
int PersistentConnection::read (void *p_buffer, size_t len) ssize_t PersistentConnection::read(void *p_buffer, size_t len)
{ {
if(this->chunkQueue.size() == 0) if(!chunk)
return -1; return -1;
Chunk *readChunk = this->chunkQueue.front(); if(len == 0)
return 0;
if(readChunk->getBytesRead() == 0) if(chunk->getBytesRead() == 0 && !queryOk)
{
if(!this->initChunk(readChunk))
{ {
this->chunkQueue.pop_front(); if(!query(chunk->getPath()))
return -1; return -1;
} }
}
if(readChunk->getBytesToRead() == 0) assert(connected() && queryOk);
{
this->chunkQueue.pop_front(); if(chunk->getBytesToRead() == 0)
return 0; return 0;
}
int ret = 0; if(len > chunk->getBytesToRead())
if(len > readChunk->getBytesToRead()) len = chunk->getBytesToRead();
ret = HTTPConnection::read(p_buffer, readChunk->getBytesToRead());
else
ret = HTTPConnection::read(p_buffer, len);
ssize_t ret = IHTTPConnection::read(p_buffer, len);
if(ret <= 0) if(ret <= 0)
{ {
readChunk->setStartByte(readChunk->getStartByte() + readChunk->getBytesRead()); chunk->setStartByte(chunk->getStartByte() + chunk->getBytesRead());
readChunk->setBytesRead(0); chunk->setBytesRead(0);
if(!this->reconnect(readChunk)) disconnect();
{ if(retries++ == retryCount || !query(chunk->getPath()))
this->chunkQueue.pop_front();
return -1; return -1;
}
return this->read(p_buffer, len); return read(p_buffer, len);
} }
readChunk->setBytesRead(readChunk->getBytesRead() + ret); retries = 0;
chunk->setBytesRead(chunk->getBytesRead() + ret);
return ret; return ret;
} }
bool PersistentConnection::init (Chunk *chunk) bool PersistentConnection::query(const std::string &path)
{ {
if(isInit) if(!connected() &&
return true; !connect(chunk->getHostname(), chunk->getPort()))
if (IHTTPConnection::init(chunk))
{
isInit = true;
chunkQueue.push_back(chunk);
hostname = chunk->getHostname();
}
return isInit;
}
bool PersistentConnection::addChunk (Chunk *chunk)
{
if(chunk == NULL)
return false;
if(!this->isInit)
return this->init(chunk);
if(!chunk->hasHostname())
{
chunk->setUrl(getUrlRelative(chunk));
if(!chunk->hasHostname())
return false;
}
if(chunk->getHostname().compare(this->hostname))
return false; return false;
if(send(getRequestHeader(chunk).append("\r\n"))) queryOk = IHTTPConnection::query(path);
{ return queryOk;
this->chunkQueue.push_back(chunk);
return true;
}
return false;
} }
bool PersistentConnection::initChunk (Chunk *chunk)
{
HeaderReply reply;
if(parseHeader(&reply))
{
chunk->setLength(reply.contentLength);
return true;
}
if(!reconnect(chunk)) bool PersistentConnection::connect(const std::string &hostname, int port)
return false;
if(parseHeader(&reply))
{
chunk->setLength(reply.contentLength);
return true;
}
return false;
}
bool PersistentConnection::reconnect (Chunk *chunk)
{ {
int count = 0; assert(!connected());
std::string request = getRequestHeader(chunk).append("\r\n"); assert(!queryOk);
return IHTTPConnection::connect(hostname, port);
while(count < this->RETRY)
{
this->httpSocket = net_ConnectTCP(this->stream, chunk->getHostname().c_str(), chunk->getPort());
if(this->httpSocket != -1)
if(this->resendAllRequests())
return true;
count++;
}
return false;
} }
const std::string& PersistentConnection::getHostname () const
void PersistentConnection::releaseChunk()
{ {
return this->hostname; if(!chunk)
return;
if(toRead > 0 && connected()) /* We can't resend request if we haven't finished reading */
disconnect();
HTTPConnection::releaseChunk();
} }
bool PersistentConnection::isConnected () const
void PersistentConnection::disconnect()
{ {
return this->isInit; queryOk = false;
IHTTPConnection::disconnect();
} }
bool PersistentConnection::resendAllRequests ()
{
for(size_t i = 0; i < chunkQueue.size(); i++)
if(!send(getRequestHeader(chunkQueue.at(i)).append("\r\n")))
return false;
return true; const std::string& PersistentConnection::getHostname() const
{
return hostname;
} }
std::string PersistentConnection::getRequestHeader(const Chunk *chunk) const std::string PersistentConnection::buildRequestHeader(const std::string &path) const
{ {
/* can clearly see here that inheritance is reversed :/ */ return IHTTPConnection::buildRequestHeader(path);
return IHTTPConnection::getRequestHeader(chunk);
} }
...@@ -35,27 +35,23 @@ namespace dash ...@@ -35,27 +35,23 @@ namespace dash
class PersistentConnection : public HTTPConnection class PersistentConnection : public HTTPConnection
{ {
public: public:
PersistentConnection (stream_t *stream); PersistentConnection(stream_t *stream, Chunk *chunk = NULL);
virtual ~PersistentConnection ();
virtual bool connect (const std::string &hostname, int port = 80);
virtual bool query (const std::string& path);
virtual ssize_t read (void *p_buffer, size_t len);
virtual void disconnect ();
virtual void releaseChunk();
virtual int read (void *p_buffer, size_t len);
virtual bool init (Chunk *chunk);
bool addChunk (Chunk *chunk);
const std::string& getHostname () const; const std::string& getHostname () const;
bool isConnected () const;
private: private:
std::deque<Chunk *> chunkQueue; bool queryOk;
bool isInit; int retries;
std::string hostname;
static const int RETRY;
protected: protected:
bool initChunk (Chunk *chunk); static const int retryCount = 5;
bool reconnect (Chunk *chunk); virtual std::string buildRequestHeader(const std::string &path) const;
bool resendAllRequests ();
virtual std::string getRequestHeader (const Chunk *chunk) const; /* reimpl */
}; };
} }
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <sstream> #include <sstream>
#include <vlc_strings.h> #include <vlc_strings.h>
#include <vlc_stream.h>
using namespace dash::mpd; using namespace dash::mpd;
using namespace dash::xml; using namespace dash::xml;
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <vector> #include <vector>
#include <vlc_xml.h> #include <vlc_xml.h>
#include <vlc_stream.h>
using namespace dash::xml; using namespace dash::xml;
using namespace dash::mpd; using namespace dash::mpd;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment