Removed lots of left-over debug messages, added chunked transfer support to all segmented HTTP streaming methods, fixed several major slowdown problems, fixed HTTP progressive seconds-seeking.

This commit is contained in:
Thulinma 2013-08-23 00:07:02 +02:00
parent 2d9859bbad
commit 2bc9858b66
7 changed files with 460 additions and 481 deletions

View file

@ -403,18 +403,14 @@ namespace Connector_HTTP {
}else{ }else{
long long int ret = Util::getMS(); long long int ret = Util::getMS();
//success, check type of response //success, check type of response
std::cout << "Response headers for " << orig_url << " received...";
if (H.GetHeader("Content-Length") != "" || H.GetHeader("Transfer-Encoding") == "chunked"){ if (H.GetHeader("Content-Length") != "" || H.GetHeader("Transfer-Encoding") == "chunked"){
//known length - simply re-send the request with added headers and continue //known length - simply re-send the request with added headers and continue
H.SetHeader("X-UID", uid); H.SetHeader("X-UID", uid);
H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver);
H.body = ""; H.body = "";
std::cout << "proxying..." << std::endl;
H.Proxy(*(myCConn->conn), *conn); H.Proxy(*(myCConn->conn), *conn);
std::cout << "Proxying " << orig_url << " completed!" << std::endl;
myCConn->inUse.unlock(); myCConn->inUse.unlock();
}else{ }else{
std::cout << "progressin'..." << std::endl;
//unknown length //unknown length
H.SetHeader("X-UID", uid); H.SetHeader("X-UID", uid);
H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver);

View file

@ -150,7 +150,7 @@ namespace Connector_HTTP {
Socket::Connection ss( -1); Socket::Connection ss( -1);
std::string streamname; std::string streamname;
std::string recBuffer = ""; bool handlingRequest = false;
int Quality = 0; int Quality = 0;
int Segment = -1; int Segment = -1;
@ -161,124 +161,125 @@ namespace Connector_HTTP {
conn.setBlocking(false); //do not block on conn.spool() when no data is available conn.setBlocking(false); //do not block on conn.spool() when no data is available
while (conn.connected()){ while (conn.connected()){
if (conn.spool() || conn.Received().size()){ if ( !handlingRequest){
if (HTTP_R.Read(conn)){ if (conn.spool() || conn.Received().size()){
#if DEBUG >= 5 if (HTTP_R.Read(conn)){
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
#endif
conn.setHost(HTTP_R.GetHeader("X-Origin"));
streamname = HTTP_R.GetHeader("X-Stream");
if ( !ss){
ss = Util::Stream::getStream(streamname);
if ( !ss.connected()){
HTTP_S.Clean();
HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
conn.SendNow(HTTP_S.BuildResponse("404", "Not found"));
continue;
}
ss.setBlocking(false);
//make sure metadata is received
while ( !Strm.metadata && ss.connected()){
if (ss.spool()){
while (Strm.parsePacket(ss.Received())){
//do nothing
}
}
}
}
if (HTTP_R.url.find(".abst") != std::string::npos){
std::string streamID = HTTP_R.url.substr(HTTP_R.url.find(streamname) + streamname.size() + 1);
streamID = streamID.substr(0, streamID.find(".abst"));
std::cerr << "Requesting bootstrap for stream " << streamID << std::endl;
HTTP_S.Clean();
HTTP_S.SetBody(dynamicBootstrap(streamname, Strm.getTrackById(atoll(streamID.c_str())),Strm.metadata.isMember("live")));
HTTP_S.SetHeader("Content-Type", "binary/octet");
HTTP_S.SetHeader("Cache-Control", "no-cache");
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
HTTP_R.Clean(); //clean for any possible next requests
continue;
}
if (HTTP_R.url.find("f4m") == std::string::npos){
std::string tmp_qual = HTTP_R.url.substr(HTTP_R.url.find("/", 10) + 1);
Quality = atoi(tmp_qual.substr(0, tmp_qual.find("Seg") - 1).c_str());
int temp;
temp = HTTP_R.url.find("Seg") + 3;
Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("-", temp) - temp).c_str());
temp = HTTP_R.url.find("Frag") + 4;
ReqFragment = atoi(HTTP_R.url.substr(temp).c_str());
#if DEBUG >= 5 #if DEBUG >= 5
printf("Video track %d, segment %d, fragment %d\n", Quality, Segment, ReqFragment); std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
#endif #endif
if (!audioTrack){getTracks(Strm.metadata);} conn.setHost(HTTP_R.GetHeader("X-Origin"));
JSON::Value & vidTrack = Strm.getTrackById(Quality); streamname = HTTP_R.GetHeader("X-Stream");
mstime = 0; if ( !ss){
mslen = 0; ss = Util::Stream::getStream(streamname);
if (vidTrack.isMember("keys")){ if ( !ss.connected()){
for (JSON::ArrIter it = vidTrack["keys"].ArrBegin(); it != vidTrack["keys"].ArrEnd(); it++){
if ((*it)["num"].asInt() >= ReqFragment){
mstime = (*it)["time"].asInt();
mslen = (*it)["len"].asInt();
if (Strm.metadata.isMember("live")){
if (it == vidTrack["keys"].ArrEnd() - 2){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl;
}
}
break;
}
}
}
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
if (Strm.metadata.isMember("live")){
if (mstime == 0 && ReqFragment > 1){
HTTP_S.Clean(); HTTP_S.Clean();
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); HTTP_S.SendResponse("404", "Not found", conn);
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment " << ReqFragment << " too old" << std::endl;
continue; continue;
} }
ss.setBlocking(false);
//make sure metadata is received
while ( !Strm.metadata && ss.connected()){
if (ss.spool()){
while (Strm.parsePacket(ss.Received())){
//do nothing
}
}
}
} }
std::stringstream sstream; if (HTTP_R.url.find(".abst") != std::string::npos){
sstream << "t " << Quality << " " << audioTrack << "\ns " << mstime << "\np " << (mstime + mslen) << "\n"; std::string streamID = HTTP_R.url.substr(HTTP_R.url.find(streamname) + streamname.size() + 1);
ss.SendNow(sstream.str().c_str()); streamID = streamID.substr(0, streamID.find(".abst"));
std::cout << sstream.str() << std::endl; std::cerr << "Requesting bootstrap for stream " << streamID << std::endl;
HTTP_S.Clean();
HTTP_S.Clean(); HTTP_S.SetBody(dynamicBootstrap(streamname, Strm.getTrackById(atoll(streamID.c_str())),Strm.metadata.isMember("live")));
HTTP_S.protocol = "HTTP/1.1"; HTTP_S.SetHeader("Content-Type", "binary/octet");
HTTP_S.SetHeader("Content-Type", "video/mp4"); HTTP_S.SetHeader("Cache-Control", "no-cache");
HTTP_S.SetBody(""); HTTP_S.SendResponse("200", "OK", conn);
std::string new_strap = dynamicBootstrap(streamname, Strm.getTrackById(Quality), Strm.metadata.isMember("live"), ReqFragment); HTTP_R.Clean(); //clean for any possible next requests
HTTP_S.SetHeader("Transfer-Encoding", "chunked"); continue;
HTTP_S.SendResponse("200", "OK", conn);
HTTP_S.Chunkify(new_strap, conn);
HTTP_S.Chunkify("\000\000\000\000mdat", 8, conn);
//fill buffer with init data, if needed.
if (audioTrack > 0 && Strm.getTrackById(audioTrack).isMember("init")){
tmp.DTSCAudioInit(Strm.getTrackById(audioTrack));
tmp.tagTime(mstime);
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
} }
if (Quality > 0 && Strm.getTrackById(Quality).isMember("init")){ if (HTTP_R.url.find("f4m") == std::string::npos){
tmp.DTSCVideoInit(Strm.getTrackById(Quality)); std::string tmp_qual = HTTP_R.url.substr(HTTP_R.url.find("/", 10) + 1);
tmp.tagTime(mstime); Quality = atoi(tmp_qual.substr(0, tmp_qual.find("Seg") - 1).c_str());
HTTP_S.Chunkify(tmp.data, tmp.len, conn); int temp;
temp = HTTP_R.url.find("Seg") + 3;
Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("-", temp) - temp).c_str());
temp = HTTP_R.url.find("Frag") + 4;
ReqFragment = atoi(HTTP_R.url.substr(temp).c_str());
#if DEBUG >= 5
printf("Video track %d, segment %d, fragment %d\n", Quality, Segment, ReqFragment);
#endif
if (!audioTrack){getTracks(Strm.metadata);}
JSON::Value & vidTrack = Strm.getTrackById(Quality);
mstime = 0;
mslen = 0;
if (vidTrack.isMember("keys")){
for (JSON::ArrIter it = vidTrack["keys"].ArrBegin(); it != vidTrack["keys"].ArrEnd(); it++){
if ((*it)["num"].asInt() >= ReqFragment){
mstime = (*it)["time"].asInt();
mslen = (*it)["len"].asInt();
if (Strm.metadata.isMember("live")){
if (it == vidTrack["keys"].ArrEnd() - 2){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
HTTP_S.SendResponse("208", "Ask again later", conn);
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl;
}
}
break;
}
}
}
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
if (Strm.metadata.isMember("live")){
if (mstime == 0 && ReqFragment > 1){
HTTP_S.Clean();
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
HTTP_S.SendResponse("412", "Fragment out of range", conn);
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment " << ReqFragment << " too old" << std::endl;
continue;
}
}
std::stringstream sstream;
sstream << "t " << Quality << " " << audioTrack << "\ns " << mstime << "\np " << (mstime + mslen) << "\n";
ss.SendNow(sstream.str().c_str());
HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", "video/mp4");
HTTP_S.StartResponse(HTTP_R, conn);
//send the bootstrap
std::string bootstrap = dynamicBootstrap(streamname, Strm.getTrackById(Quality), Strm.metadata.isMember("live"), ReqFragment);
HTTP_S.Chunkify(bootstrap, conn);
//send a zero-size mdat, meaning it stretches until end of file.
HTTP_S.Chunkify("\000\000\000\000mdat", 8, conn);
//send init data, if needed.
if (audioTrack > 0 && Strm.getTrackById(audioTrack).isMember("init")){
tmp.DTSCAudioInit(Strm.getTrackById(audioTrack));
tmp.tagTime(mstime);
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
}
if (Quality > 0 && Strm.getTrackById(Quality).isMember("init")){
tmp.DTSCVideoInit(Strm.getTrackById(Quality));
tmp.tagTime(mstime);
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
}
handlingRequest = true;
}else{
HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", "text/xml");
HTTP_S.SetHeader("Cache-Control", "no-cache");
HTTP_S.SetBody(dynamicIndex(streamname, Strm.metadata));
HTTP_S.SendResponse("200", "OK", conn);
} }
}else{ HTTP_R.Clean(); //clean for any possible next requests
HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", "text/xml");
HTTP_S.SetHeader("Cache-Control", "no-cache");
std::string manifest = dynamicIndex(streamname, Strm.metadata);
HTTP_S.SetBody(manifest);
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
} }
HTTP_R.Clean(); //clean for any possible next requests }else{
//sleep for 250ms before next attempt
Util::sleep(250);
} }
}else{
Util::sleep(1);
} }
if (ss.connected()){ if (ss.connected()){
unsigned int now = Util::epoch(); unsigned int now = Util::epoch();
@ -286,13 +287,12 @@ namespace Connector_HTTP {
lastStats = now; lastStats = now;
ss.SendNow(conn.getStats("HTTP_Dynamic").c_str()); ss.SendNow(conn.getStats("HTTP_Dynamic").c_str());
} }
if (ss.spool()){ if (handlingRequest && ss.spool()){
while (Strm.parsePacket(ss.Received())){ while (Strm.parsePacket(ss.Received())){
if (Strm.lastType() == DTSC::PAUSEMARK){ if (Strm.lastType() == DTSC::PAUSEMARK){
//send an empty chunk to signify request is done //send an empty chunk to signify request is done
std::string empty = ""; HTTP_S.Chunkify("", 0, conn);
HTTP_S.Chunkify(empty, conn); handlingRequest = false;
std::cout << "Finito!" << std::endl;
} }
if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){ if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
//send a chunk with the new data //send a chunk with the new data

View file

@ -94,9 +94,6 @@ namespace Connector_HTTP {
///\param conn A socket describing the connection the client. ///\param conn A socket describing the connection the client.
///\return The exit code of the connector. ///\return The exit code of the connector.
int liveConnector(Socket::Connection conn){ int liveConnector(Socket::Connection conn){
std::stringstream TSBuf;
long long int TSBufTime = 0;
DTSC::Stream Strm; //Incoming stream buffer. DTSC::Stream Strm; //Incoming stream buffer.
HTTP::Parser HTTP_R, HTTP_S; //HTTP Receiver en HTTP Sender. HTTP::Parser HTTP_R, HTTP_S; //HTTP Receiver en HTTP Sender.
@ -104,6 +101,7 @@ namespace Connector_HTTP {
bool AppleCompat = false; //Set to true when Apple device detected. bool AppleCompat = false; //Set to true when Apple device detected.
Socket::Connection ss( -1); Socket::Connection ss( -1);
std::string streamname; std::string streamname;
bool handlingRequest = false;
std::string recBuffer = ""; std::string recBuffer = "";
TS::Packet PackData; TS::Packet PackData;
@ -129,100 +127,107 @@ namespace Connector_HTTP {
conn.setBlocking(false); //do not block on conn.spool() when no data is available conn.setBlocking(false); //do not block on conn.spool() when no data is available
while (conn.connected()){ while (conn.connected()){
if (conn.spool() || conn.Received().size()){ if ( !handlingRequest){
if (HTTP_R.Read(conn)){ if (conn.spool() || conn.Received().size()){
#if DEBUG >= 5 if (HTTP_R.Read(conn)){
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; #if DEBUG >= 5
#endif std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
conn.setHost(HTTP_R.GetHeader("X-Origin")); #endif
AppleCompat = (HTTP_R.GetHeader("User-Agent").find("Apple") != std::string::npos); conn.setHost(HTTP_R.GetHeader("X-Origin"));
streamname = HTTP_R.GetHeader("X-Stream"); AppleCompat = (HTTP_R.GetHeader("User-Agent").find("Apple") != std::string::npos);
if ( !ss){ streamname = HTTP_R.GetHeader("X-Stream");
ss = Util::Stream::getStream(streamname); if ( !ss){
if ( !ss.connected()){ ss = Util::Stream::getStream(streamname);
#if DEBUG >= 1 if ( !ss.connected()){
fprintf(stderr, "Could not connect to server!\n"); #if DEBUG >= 1
#endif fprintf(stderr, "Could not connect to server!\n");
HTTP_S.Clean(); #endif
HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); HTTP_S.Clean();
conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
ready4data = false; conn.SendNow(HTTP_S.BuildResponse("404", "Not found"));
continue; ready4data = false;
} continue;
ss.setBlocking(false); }
//make sure metadata is received ss.setBlocking(false);
while ( !Strm.metadata && ss.connected()){ //make sure metadata is received
if (ss.spool()){ while ( !Strm.metadata && ss.connected()){
while (Strm.parsePacket(ss.Received())){ if (ss.spool()){
//do nothing while (Strm.parsePacket(ss.Received())){
//do nothing
}
} }
} }
} }
} if (HTTP_R.url.find(".m3u") == std::string::npos){
if (HTTP_R.url.find(".m3u") == std::string::npos){ temp = HTTP_R.url.find("/", 5) + 1;
temp = HTTP_R.url.find("/", 5) + 1; std::string allTracks = HTTP_R.url.substr(temp, HTTP_R.url.find("/", temp) - temp);
std::string allTracks = HTTP_R.url.substr(temp, HTTP_R.url.find("/", temp) - temp); trackID = atoi(allTracks.c_str());
trackID = atoi(allTracks.c_str()); audioTrackID = atoi(allTracks.substr(allTracks.find("_")+1).c_str());
audioTrackID = atoi(allTracks.substr(allTracks.find("_")+1).c_str()); temp = HTTP_R.url.find("/", temp) + 1;
temp = HTTP_R.url.find("/", temp) + 1; Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("_", temp) - temp).c_str());
Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("_", temp) - temp).c_str()); lastVid = Segment * 90;
lastVid = Segment * 90; temp = HTTP_R.url.find("_", temp) + 1;
temp = HTTP_R.url.find("_", temp) + 1; int frameCount = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str());
int frameCount = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str()); if (Strm.metadata.isMember("live")){
if (Strm.metadata.isMember("live")){ int seekable = Strm.canSeekms(Segment);
int seekable = Strm.canSeekms(Segment); if (seekable < 0){
if (seekable < 0){ HTTP_S.Clean();
HTTP_S.Clean(); HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); HTTP_R.Clean(); //clean for any possible next requests
HTTP_R.Clean(); //clean for any possible next requests std::cout << "Fragment @ " << Segment << " too old" << std::endl;
std::cout << "Fragment @ " << Segment << " too old" << std::endl; continue;
continue; }
if (seekable > 0){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment @ " << Segment << " not available yet" << std::endl;
continue;
}
} }
if (seekable > 0){ for (int i = 0; i < allTracks.size(); i++){
HTTP_S.Clean(); if (allTracks[i] == '_'){
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); allTracks[i] = ' ';
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later")); }
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment @ " << Segment << " not available yet" << std::endl;
continue;
} }
} std::stringstream sstream;
for (int i = 0; i < allTracks.size(); i++){ sstream << "t " << allTracks << "\n";
if (allTracks[i] == '_'){ sstream << "s " << Segment << "\n";
allTracks[i] = ' '; sstream << "p " << frameCount << "\n";
} ss.SendNow(sstream.str().c_str());
}
std::stringstream sstream; HTTP_S.Clean();
sstream << "t " << allTracks << "\n"; HTTP_S.SetHeader("Content-Type", "video/mp2t");
sstream << "s " << Segment << "\n"; HTTP_S.StartResponse(HTTP_R, conn);
sstream << "p " << frameCount << "\n"; handlingRequest = true;
ss.SendNow(sstream.str().c_str());
}else{
std::string request = HTTP_R.url.substr(HTTP_R.url.find("/", 5) + 1);
if (HTTP_R.url.find(".m3u8") != std::string::npos){
manifestType = "audio/x-mpegurl";
}else{ }else{
manifestType = "audio/mpegurl"; std::string request = HTTP_R.url.substr(HTTP_R.url.find("/", 5) + 1);
if (HTTP_R.url.find(".m3u8") != std::string::npos){
manifestType = "audio/x-mpegurl";
}else{
manifestType = "audio/mpegurl";
}
HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", manifestType);
HTTP_S.SetHeader("Cache-Control", "no-cache");
std::string manifest;
if (request.find("/") == std::string::npos){
manifest = liveIndex(Strm.metadata, Strm.metadata.isMember("live"));
}else{
int selectId = atoi(request.substr(0,request.find("/")).c_str());
manifest = liveIndex(Strm.getTrackById(selectId), Strm.metadata.isMember("live"));
}
HTTP_S.SetBody(manifest);
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
} }
HTTP_S.Clean(); ready4data = true;
HTTP_S.SetHeader("Content-Type", manifestType); HTTP_R.Clean(); //clean for any possible next requests
HTTP_S.SetHeader("Cache-Control", "no-cache");
std::string manifest;
if (request.find("/") == std::string::npos){
manifest = liveIndex(Strm.metadata, Strm.metadata.isMember("live"));
}else{
int selectId = atoi(request.substr(0,request.find("/")).c_str());
manifest = liveIndex(Strm.getTrackById(selectId), Strm.metadata.isMember("live"));
}
HTTP_S.SetBody(manifest);
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
} }
ready4data = true; }else{
HTTP_R.Clean(); //clean for any possible next requests Util::sleep(250);
} }
}else{
Util::sleep(1);
} }
if (ready4data){ if (ready4data){
unsigned int now = Util::epoch(); unsigned int now = Util::epoch();
@ -230,23 +235,11 @@ namespace Connector_HTTP {
lastStats = now; lastStats = now;
ss.SendNow(conn.getStats("HTTP_Live").c_str()); ss.SendNow(conn.getStats("HTTP_Live").c_str());
} }
if (ss.spool()){ if (handlingRequest && ss.spool()){
while (Strm.parsePacket(ss.Received())){ while (Strm.parsePacket(ss.Received())){
if (Strm.lastType() == DTSC::PAUSEMARK){ if (Strm.lastType() == DTSC::PAUSEMARK){
TSBuf.flush(); HTTP_S.Chunkify("", 0, conn);
if (TSBuf.str().size()){ handlingRequest = false;
HTTP_S.Clean();
HTTP_S.protocol = "HTTP/1.1";
HTTP_S.SetHeader("Content-Type", "video/mp2t");
HTTP_S.SetHeader("Connection", "keep-alive");
HTTP_S.SetBody("");
HTTP_S.SetHeader("Content-Length", TSBuf.str().size());
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
conn.SendNow(TSBuf.str().c_str(), TSBuf.str().size());
TSBuf.str("");
PacketNumber = 0;
}
TSBuf.str("");
} }
if ( !haveAvcc){ if ( !haveAvcc){
avccbox.setPayload(Strm.getTrackById(trackID)["init"].asString()); avccbox.setPayload(Strm.getTrackById(trackID)["init"].asString());
@ -257,9 +250,9 @@ namespace Connector_HTTP {
//write PAT and PMT TS packets //write PAT and PMT TS packets
if (PacketNumber % 42 == 0){ if (PacketNumber % 42 == 0){
PackData.DefaultPAT(); PackData.DefaultPAT();
TSBuf.write(PackData.ToString(), 188); HTTP_S.Chunkify(PackData.ToString(), 188, conn);
PackData.DefaultPMT(); PackData.DefaultPMT();
TSBuf.write(PackData.ToString(), 188); HTTP_S.Chunkify(PackData.ToString(), 188, conn);
PacketNumber += 2; PacketNumber += 2;
} }
@ -310,7 +303,7 @@ namespace Connector_HTTP {
unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184)); unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184));
std::string gonnaSend = ToPack.remove(toSend); std::string gonnaSend = ToPack.remove(toSend);
PackData.FillFree(gonnaSend); PackData.FillFree(gonnaSend);
TSBuf.write(PackData.ToString(), 188); HTTP_S.Chunkify(PackData.ToString(), 188, conn);
PacketNumber++; PacketNumber++;
//rest of packets //rest of packets
@ -321,7 +314,7 @@ namespace Connector_HTTP {
toSend = PackData.AddStuffing(ToPack.bytes(184)); toSend = PackData.AddStuffing(ToPack.bytes(184));
gonnaSend = ToPack.remove(toSend); gonnaSend = ToPack.remove(toSend);
PackData.FillFree(gonnaSend); PackData.FillFree(gonnaSend);
TSBuf.write(PackData.ToString(), 188); HTTP_S.Chunkify(PackData.ToString(), 188, conn);
PacketNumber++; PacketNumber++;
} }

View file

@ -54,12 +54,7 @@ namespace Connector_HTTP {
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
#endif #endif
conn.setHost(HTTP_R.GetHeader("X-Origin")); conn.setHost(HTTP_R.GetHeader("X-Origin"));
//we assume the URL is the stream name with a 3 letter extension streamname = HTTP_R.GetHeader("X-Stream");
streamname = HTTP_R.getUrl().substr(1);
size_t extDot = streamname.rfind('.');
if (extDot != std::string::npos){
streamname.resize(extDot);
}; //strip the extension
int start = 0; int start = 0;
if ( !HTTP_R.GetVar("start").empty()){ if ( !HTTP_R.GetVar("start").empty()){
start = atoi(HTTP_R.GetVar("start").c_str()); start = atoi(HTTP_R.GetVar("start").c_str());
@ -126,7 +121,9 @@ namespace Connector_HTTP {
byterate += Strm.getTrackById(audioID)["bps"].asInt(); byterate += Strm.getTrackById(audioID)["bps"].asInt();
} }
if ( !byterate){byterate = 1;} if ( !byterate){byterate = 1;}
seek_sec = (seek_byte / byterate) * 1000; if (seek_byte){
seek_sec = (seek_byte / byterate) * 1000;
}
std::stringstream cmd; std::stringstream cmd;
cmd << "t"; cmd << "t";
if (videoID != -1){ if (videoID != -1){

View file

@ -52,12 +52,7 @@ namespace Connector_HTTP {
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
#endif #endif
conn.setHost(HTTP_R.GetHeader("X-Origin")); conn.setHost(HTTP_R.GetHeader("X-Origin"));
//we assume the URL is the stream name with a 3 letter extension streamname = HTTP_R.GetHeader("X-Stream");
streamname = HTTP_R.getUrl().substr(1);
size_t extDot = streamname.rfind('.');
if (extDot != std::string::npos){
streamname.resize(extDot);
}; //strip the extension
int start = 0; int start = 0;
if ( !HTTP_R.GetVar("start").empty()){ if ( !HTTP_R.GetVar("start").empty()){
start = atoi(HTTP_R.GetVar("start").c_str()); start = atoi(HTTP_R.GetVar("start").c_str());
@ -121,7 +116,9 @@ namespace Connector_HTTP {
byterate += Strm.getTrackById(audioID)["bps"].asInt(); byterate += Strm.getTrackById(audioID)["bps"].asInt();
} }
if ( !byterate){byterate = 1;} if ( !byterate){byterate = 1;}
seek_sec = (seek_byte / byterate) * 1000; if (seek_byte){
seek_sec = (seek_byte / byterate) * 1000;
}
std::stringstream cmd; std::stringstream cmd;
cmd << "t"; cmd << "t";
if (videoID != -1){ if (videoID != -1){

View file

@ -60,12 +60,7 @@ namespace Connector_HTTP {
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
#endif #endif
conn.setHost(HTTP_R.GetHeader("X-Origin")); conn.setHost(HTTP_R.GetHeader("X-Origin"));
//we assume the URL is the stream name with a 3 letter extension streamname = HTTP_R.GetHeader("X-Stream");
streamname = HTTP_R.getUrl().substr(1);
size_t extDot = streamname.rfind('.');
if (extDot != std::string::npos){
streamname.resize(extDot);
}; //strip the extension
int start = 0; int start = 0;
if ( !HTTP_R.GetVar("start").empty()){ if ( !HTTP_R.GetVar("start").empty()){
start = atoi(HTTP_R.GetVar("start").c_str()); start = atoi(HTTP_R.GetVar("start").c_str());
@ -132,7 +127,9 @@ namespace Connector_HTTP {
byterate += Strm.getTrackById(audioID)["bps"].asInt(); byterate += Strm.getTrackById(audioID)["bps"].asInt();
} }
if ( !byterate){byterate = 1;} if ( !byterate){byterate = 1;}
seek_sec = (seek_byte / byterate) * 1000; if (seek_byte){
seek_sec = (seek_byte / byterate) * 1000;
}
std::stringstream cmd; std::stringstream cmd;
cmd << "t"; cmd << "t";
if (videoID != -1){ if (videoID != -1){

View file

@ -60,10 +60,10 @@ namespace Connector_HTTP {
} }
if (oIt->second["type"].asString() == "video"){ if (oIt->second["type"].asString() == "video"){
allVideo[oIt->first] = oIt->second; allVideo[oIt->first] = oIt->second;
if (oIt->second["width"].asInt() > maxWidth){ maxWidth = oIt->second["width"].asInt(); } if (oIt->second["width"].asInt() > maxWidth){maxWidth = oIt->second["width"].asInt();}
if (oIt->second["width"].asInt() < minWidth){ minWidth = oIt->second["width"].asInt(); } if (oIt->second["width"].asInt() < minWidth){minWidth = oIt->second["width"].asInt();}
if (oIt->second["height"].asInt() > maxHeight){ maxHeight = oIt->second["height"].asInt(); } if (oIt->second["height"].asInt() > maxHeight){maxHeight = oIt->second["height"].asInt();}
if (oIt->second["height"].asInt() < minHeight){ minHeight = oIt->second["height"].asInt(); } if (oIt->second["height"].asInt() < minHeight){minHeight = oIt->second["height"].asInt();}
} }
} }
@ -164,6 +164,7 @@ namespace Connector_HTTP {
bool ready4data = false;//Set to true when streaming is to begin. bool ready4data = false;//Set to true when streaming is to begin.
Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream. Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream.
std::string streamname;//Will contain the name of the stream. std::string streamname;//Will contain the name of the stream.
bool handlingRequest = false;
bool wantsVideo = false;//Indicates whether this request is a video request. bool wantsVideo = false;//Indicates whether this request is a video request.
bool wantsAudio = false;//Indicates whether this request is an audio request. bool wantsAudio = false;//Indicates whether this request is an audio request.
@ -178,257 +179,253 @@ namespace Connector_HTTP {
JSON::Value allVideo; JSON::Value allVideo;
while (conn.connected()){ while (conn.connected()){
if (conn.spool() || conn.Received().size()){ if ( !handlingRequest){
if (HTTP_R.Read(conn)){ if (conn.spool() || conn.Received().size()){
#if DEBUG >= 5 if (HTTP_R.Read(conn)){
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; #if DEBUG >= 5
#endif std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
//Get data set by the proxy. #endif
conn.setHost(HTTP_R.GetHeader("X-Origin")); //Get data set by the proxy.
streamname = HTTP_R.GetHeader("X-Stream"); conn.setHost(HTTP_R.GetHeader("X-Origin"));
if ( !ss){ streamname = HTTP_R.GetHeader("X-Stream");
//initiate Stream Socket if ( !ss){
ss = Util::Stream::getStream(streamname); //initiate Stream Socket
if ( !ss.connected()){ ss = Util::Stream::getStream(streamname);
#if DEBUG >= 1 if ( !ss.connected()){
fprintf(stderr, "Could not connect to server!\n"); #if DEBUG >= 1
#endif fprintf(stderr, "Could not connect to server!\n");
HTTP_S.Clean(); #endif
HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); HTTP_S.Clean();
conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
ready4data = false; conn.SendNow(HTTP_S.BuildResponse("404", "Not found"));
continue; ready4data = false;
} continue;
ss.setBlocking(false); }
//Do nothing until metadata has been received. ss.setBlocking(false);
while ( !Strm.metadata && ss.connected()){ //Do nothing until metadata has been received.
if (ss.spool()){ while ( !Strm.metadata && ss.connected()){
while (Strm.parsePacket(ss.Received())){ if (ss.spool()){
//do nothing while (Strm.parsePacket(ss.Received())){
//do nothing
}
} }
} }
} for (JSON::ObjIter oIt = Strm.metadata["tracks"].ObjBegin(); oIt != Strm.metadata["tracks"].ObjEnd(); oIt++){
for (JSON::ObjIter oIt = Strm.metadata["tracks"].ObjBegin(); oIt != Strm.metadata["tracks"].ObjEnd(); oIt++){ if (oIt->second["type"].asString() == "audio"){
if (oIt->second["type"].asString() == "audio"){ allAudio[oIt->first] = oIt->second;
allAudio[oIt->first] = oIt->second; }
if (oIt->second["type"].asString() == "video"){
allVideo[oIt->first] = oIt->second;
}
} }
if (oIt->second["type"].asString() == "video"){ };
allVideo[oIt->first] = oIt->second;
}
}
};
if (HTTP_R.url.find("Manifest") == std::string::npos){ if (HTTP_R.url.find("Manifest") == std::string::npos){
//We have a non-manifest request, parse it. //We have a non-manifest request, parse it.
Quality = HTTP_R.url.substr(HTTP_R.url.find("/Q(", 8) + 3); Quality = HTTP_R.url.substr(HTTP_R.url.find("/Q(", 8) + 3);
Quality = Quality.substr(0, Quality.find(")")); Quality = Quality.substr(0, Quality.find(")"));
parseString = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2); parseString = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2);
wantsAudio = false; wantsAudio = false;
wantsVideo = false; wantsVideo = false;
if (parseString[0] == 'A'){ if (parseString[0] == 'A'){
wantsAudio = true; wantsAudio = true;
} }
if (parseString[0] == 'V'){ if (parseString[0] == 'V'){
wantsVideo = true; wantsVideo = true;
} }
parseString = parseString.substr(parseString.find("(") + 1); parseString = parseString.substr(parseString.find("(") + 1);
requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str()); requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str());
if (Strm.metadata.isMember("live")){ if (Strm.metadata.isMember("live")){
///\todo Fix this for live stuff ///\todo Fix this for live stuff
int seekable = Strm.canSeekms(requestedTime / 10000); int seekable = Strm.canSeekms(requestedTime / 10000);
if (seekable == 0){ if (seekable == 0){
// iff the fragment in question is available, check if the next is available too // iff the fragment in question is available, check if the next is available too
for (int i = 0; i < Strm.metadata["keytime"].size(); i++){ for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
if (Strm.metadata["keytime"][i].asInt() >= (requestedTime / 10000)){ if (Strm.metadata["keytime"][i].asInt() >= (requestedTime / 10000)){
if (i + 1 == Strm.metadata["keytime"].size()){ if (i + 1 == Strm.metadata["keytime"].size()){
seekable = 1; seekable = 1;
}
break;
}
}
}
if (seekable < 0){
HTTP_S.Clean();
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment @ " << requestedTime / 10000 << "ms too old (" << Strm.metadata["keytime"][0u].asInt() << " - " << Strm.metadata["keytime"][Strm.metadata["keytime"].size() - 1].asInt() << " ms)" << std::endl;
continue;
}
if (seekable > 0){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment @ " << requestedTime / 10000 << "ms not available yet (" << Strm.metadata["keytime"][0u].asInt() << " - " << Strm.metadata["keytime"][Strm.metadata["keytime"].size() - 1].asInt() << " ms)" << std::endl;
continue;
}
}
//Seek to the right place and send a play-once for a single fragment.
std::stringstream sstream;
JSON::Value myRef;
long long int selectedQuality = atoll(Quality.c_str()) / 8;
if (wantsVideo){
//Select the correct track ID
for (JSON::ObjIter vIt = allVideo.ObjBegin(); vIt != allVideo.ObjEnd(); vIt++){
if (vIt->second["bps"].asInt() == selectedQuality){
myRef = vIt->second;
}
}
}
if (wantsAudio){
//Select the correct track ID
for (JSON::ObjIter aIt = allAudio.ObjBegin(); aIt != allAudio.ObjEnd(); aIt++){
if (aIt->second["bps"].asInt() == selectedQuality){
myRef = aIt->second;
}
}
}
long long mstime = 0;
long long mslen = 0;
if (myRef.isMember("keys")){
for (JSON::ArrIter it = myRef["keys"].ArrBegin(); it != myRef["keys"].ArrEnd(); it++){
if ((*it)["time"].asInt() >= (requestedTime / 10000)){
mstime = (*it)["time"].asInt();
mslen = (*it)["len"].asInt();
if (Strm.metadata.isMember("live")){
if (it == myRef["keys"].ArrEnd() - 2){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment after fragment @ " << (requestedTime / 10000) << " not available yet" << std::endl;
}
} }
break; break;
} }
} }
} }
if (seekable < 0){ if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
HTTP_S.Clean(); if (Strm.metadata.isMember("live")){
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); if (mstime == 0 && (requestedTime / 10000) > 1){
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); HTTP_S.Clean();
HTTP_R.Clean(); //clean for any possible next requests HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
std::cout << "Fragment @ " << requestedTime / 10000 << "ms too old (" << Strm.metadata["keytime"][0u].asInt() << " - " << Strm.metadata["keytime"][Strm.metadata["keytime"].size() - 1].asInt() << " ms)" << std::endl; conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
continue; HTTP_R.Clean(); //clean for any possible next requests
} std::cout << "Fragment @ " << (requestedTime / 10000) << " too old" << std::endl;
if (seekable > 0){ continue;
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment @ " << requestedTime / 10000 << "ms not available yet (" << Strm.metadata["keytime"][0u].asInt() << " - " << Strm.metadata["keytime"][Strm.metadata["keytime"].size() - 1].asInt() << " ms)" << std::endl;
continue;
}
}
//Seek to the right place and send a play-once for a single fragment.
std::stringstream sstream;
JSON::Value myRef;
long long int selectedQuality = atoll(Quality.c_str()) / 8;
if (wantsVideo){
//Select the correct track ID
for (JSON::ObjIter vIt = allVideo.ObjBegin(); vIt != allVideo.ObjEnd(); vIt++){
if (vIt->second["bps"].asInt() == selectedQuality){
myRef = vIt->second;
} }
} }
}
if (wantsAudio){
//Select the correct track ID sstream << "t " << myRef["trackid"].asInt() << "\n";
for (JSON::ObjIter aIt = allAudio.ObjBegin(); aIt != allAudio.ObjEnd(); aIt++){ sstream << "s " << (requestedTime / 10000) << "\np " << (mstime + mslen) <<"\n";
if (aIt->second["bps"].asInt() == selectedQuality){ ss.SendNow(sstream.str().c_str());
myRef = aIt->second;
} unsigned int myDuration;
//Wrap everything in mp4 boxes
MP4::MFHD mfhd_box;
JSON::Value trackRef;
if (wantsVideo){
trackRef = allVideo.ObjBegin()->second;
} }
} if (wantsAudio){
trackRef = allAudio.ObjBegin()->second;
long long mstime = 0; }
long long mslen = 0; //Also obtain the associated keyframe;
if (myRef.isMember("keys")){ JSON::Value keyObj;
for (JSON::ArrIter it = myRef["keys"].ArrBegin(); it != myRef["keys"].ArrEnd(); it++){ for (JSON::ArrIter keyIt = trackRef["keys"].ArrBegin(); keyIt != trackRef["keys"].ArrEnd(); keyIt++){
if ((*it)["time"].asInt() >= (requestedTime / 10000)){ if ((*keyIt)["time"].asInt() >= (requestedTime / 10000)){
mstime = (*it)["time"].asInt(); keyObj = (*keyIt);
mslen = (*it)["len"].asInt(); mfhd_box.setSequenceNumber((*keyIt)["num"].asInt());
if (Strm.metadata.isMember("live")){ myDuration = (*keyIt)["len"].asInt() * 10000;
if (it == myRef["keys"].ArrEnd() - 2){
HTTP_S.Clean();
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
HTTP_R.Clean(); //clean for any possible next requests
std::cout << "Fragment after fragment @ " << (requestedTime / 10000) << " not available yet" << std::endl;
}
}
break; break;
} }
} }
}
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead. MP4::TFHD tfhd_box;
if (Strm.metadata.isMember("live")){ tfhd_box.setFlags(MP4::tfhdSampleFlag);
if (mstime == 0 && (requestedTime / 10000) > 1){ tfhd_box.setTrackID(1);
HTTP_S.Clean(); tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); MP4::TRUN trun_box;
HTTP_R.Clean(); //clean for any possible next requests trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize);
std::cout << "Fragment @ " << (requestedTime / 10000) << " too old" << std::endl; trun_box.setDataOffset(42);
continue; trun_box.setFirstSampleFlags(0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample);
for (int i = 0; i < keyObj["parts"].size(); i++){
MP4::trunSampleInformation trunSample;
trunSample.sampleSize = keyObj["parts"][i].asInt();
//Guesstimate sample duration.
trunSample.sampleDuration = ((double)(keyObj["len"].asInt() * 10000) / keyObj["parts"].size());
trun_box.setSampleInformation(trunSample, i);
} }
}
MP4::SDTP sdtp_box;
sdtp_box.setVersion(0);
sstream << "t " << myRef["trackid"].asInt() << "\n"; sdtp_box.setValue(0x24, 4);
sstream << "s " << (requestedTime / 10000) << "\np " << (mstime + mslen) <<"\n"; for (int i = 1; i < keyObj["parts"].size(); i++){
ss.SendNow(sstream.str().c_str()); sdtp_box.setValue(0x14, 4 + i);
HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", "video/mp4");
HTTP_S.SetBody("");
unsigned int myDuration;
//Wrap everything in mp4 boxes
MP4::MFHD mfhd_box;
JSON::Value trackRef;
if (wantsVideo){
trackRef = allVideo.ObjBegin()->second;
}
if (wantsAudio){
trackRef = allAudio.ObjBegin()->second;
}
//Also obtain the associated keyframe;
JSON::Value keyObj;
for (JSON::ArrIter keyIt = trackRef["keys"].ArrBegin(); keyIt != trackRef["keys"].ArrEnd(); keyIt++){
if ((*keyIt)["time"].asInt() >= (requestedTime / 10000)){
keyObj = (*keyIt);
mfhd_box.setSequenceNumber((*keyIt)["num"].asInt());
myDuration = (*keyIt)["len"].asInt() * 10000;
break;
} }
}
MP4::TRAF traf_box;
MP4::TFHD tfhd_box; traf_box.setContent(tfhd_box, 0);
tfhd_box.setFlags(MP4::tfhdSampleFlag); traf_box.setContent(trun_box, 1);
tfhd_box.setTrackID(1); traf_box.setContent(sdtp_box, 2);
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
//If the stream is live, we want to have a fragref box if possible
MP4::TRUN trun_box; if (Strm.metadata.isMember("live")){
trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize); ///\todo Fix this for live
trun_box.setDataOffset(42); MP4::UUID_TrackFragmentReference fragref_box;
trun_box.setFirstSampleFlags(0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample); fragref_box.setVersion(1);
for (int i = 0; i < keyObj["parts"].size(); i++){ fragref_box.setFragmentCount(0);
MP4::trunSampleInformation trunSample; int fragCount = 0;
trunSample.sampleSize = keyObj["parts"][i].asInt(); for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
//Guesstimate sample duration. if (Strm.metadata["keytime"][i].asInt() > (requestedTime / 10000)){
trunSample.sampleDuration = ((double)(keyObj["len"].asInt() * 10000) / keyObj["parts"].size()); fragref_box.setTime(fragCount, Strm.metadata["keytime"][i].asInt() * 10000);
trun_box.setSampleInformation(trunSample, i); fragref_box.setDuration(fragCount, Strm.metadata["keylen"][i].asInt() * 10000);
} fragref_box.setFragmentCount(++fragCount);
}
MP4::SDTP sdtp_box;
sdtp_box.setVersion(0);
sdtp_box.setValue(0x24, 4);
for (int i = 1; i < keyObj["parts"].size(); i++){
sdtp_box.setValue(0x14, 4 + i);
}
MP4::TRAF traf_box;
traf_box.setContent(tfhd_box, 0);
traf_box.setContent(trun_box, 1);
traf_box.setContent(sdtp_box, 2);
//If the stream is live, we want to have a fragref box if possible
if (Strm.metadata.isMember("live")){
///\todo Fix this for live
MP4::UUID_TrackFragmentReference fragref_box;
fragref_box.setVersion(1);
fragref_box.setFragmentCount(0);
int fragCount = 0;
for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
if (Strm.metadata["keytime"][i].asInt() > (requestedTime / 10000)){
fragref_box.setTime(fragCount, Strm.metadata["keytime"][i].asInt() * 10000);
fragref_box.setDuration(fragCount, Strm.metadata["keylen"][i].asInt() * 10000);
fragref_box.setFragmentCount(++fragCount);
} }
traf_box.setContent(fragref_box, 3);
} }
traf_box.setContent(fragref_box, 3);
MP4::MOOF moof_box;
moof_box.setContent(mfhd_box, 0);
moof_box.setContent(traf_box, 1);
//Setting the correct offsets.
trun_box.setDataOffset(moof_box.boxedSize() + 8);
traf_box.setContent(trun_box, 1);
moof_box.setContent(traf_box, 1);
HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", "video/mp4");
HTTP_S.StartResponse(HTTP_R, conn);
HTTP_S.Chunkify(moof_box.asBox(), moof_box.boxedSize(), conn);
HTTP_S.Chunkify("\000\000\000\000mdat", 8, conn);
handlingRequest = true;
}else{
//We have a request for a Manifest, generate and send it.
HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", "text/xml");
HTTP_S.SetHeader("Cache-Control", "no-cache");
std::string manifest = smoothIndex(Strm.metadata);
HTTP_S.SetBody(manifest);
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
} }
ready4data = true;
MP4::MOOF moof_box; //Clean for any possible next requests
moof_box.setContent(mfhd_box, 0); HTTP_R.Clean();
moof_box.setContent(traf_box, 1);
//Setting the correct offsets.
trun_box.setDataOffset(moof_box.boxedSize() + 8);
traf_box.setContent(trun_box, 1);
moof_box.setContent(traf_box, 1);
//Send the complete message
HTTP_S.SetHeader("Content-Length", keyObj["size"].asInt() + 8 + moof_box.boxedSize());
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
conn.SendNow(moof_box.asBox(), moof_box.boxedSize());
unsigned long size = htonl(keyObj["size"].asInt() + 8);
conn.SendNow((char*) &size, 4);
conn.SendNow("mdat", 4);
}else{
//We have a request for a Manifest, generate and send it.
HTTP_S.Clean();
HTTP_S.SetHeader("Content-Type", "text/xml");
HTTP_S.SetHeader("Cache-Control", "no-cache");
std::string manifest = smoothIndex(Strm.metadata);
HTTP_S.SetBody(manifest);
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
} }
ready4data = true; }else{
//Clean for any possible next requests //Wait 250ms before checking for new data.
HTTP_R.Clean(); Util::sleep(250);
} }
}else{
//Wait 1 second before checking for new data.
Util::sleep(1);
} }
if (ready4data){ if (ready4data){
unsigned int now = Util::epoch(); unsigned int now = Util::epoch();
@ -437,12 +434,14 @@ namespace Connector_HTTP {
lastStats = now; lastStats = now;
ss.SendNow(conn.getStats("HTTP_Smooth").c_str()); ss.SendNow(conn.getStats("HTTP_Smooth").c_str());
} }
if (ss.spool()){ if (handlingRequest && ss.spool()){
while (Strm.parsePacket(ss.Received())){ while (Strm.parsePacket(ss.Received())){
if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){ if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){
//Select only the data that the client has requested. HTTP_S.Chunkify(Strm.lastData(), conn);
int tmp = Util::getMS(); }
conn.SendNow(Strm.lastData()); if (Strm.lastType() == DTSC::PAUSEMARK){
HTTP_S.Chunkify("", 0, conn);
handlingRequest = false;
} }
} }
} }