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:
		
							parent
							
								
									2d9859bbad
								
							
						
					
					
						commit
						2bc9858b66
					
				
					 7 changed files with 460 additions and 481 deletions
				
			
		|  | @ -403,18 +403,14 @@ namespace Connector_HTTP { | |||
|     }else{ | ||||
|       long long int ret = Util::getMS(); | ||||
|       //success, check type of response
 | ||||
|       std::cout << "Response headers for " << orig_url << " received..."; | ||||
|       if (H.GetHeader("Content-Length") != "" || H.GetHeader("Transfer-Encoding") == "chunked"){ | ||||
|         //known length - simply re-send the request with added headers and continue
 | ||||
|         H.SetHeader("X-UID", uid); | ||||
|         H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); | ||||
|         H.body = ""; | ||||
|         std::cout << "proxying..." << std::endl; | ||||
|         H.Proxy(*(myCConn->conn), *conn); | ||||
|         std::cout << "Proxying " << orig_url << " completed!" << std::endl; | ||||
|         myCConn->inUse.unlock(); | ||||
|       }else{ | ||||
|         std::cout << "progressin'..." << std::endl; | ||||
|         //unknown length
 | ||||
|         H.SetHeader("X-UID", uid); | ||||
|         H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); | ||||
|  |  | |||
|  | @ -150,7 +150,7 @@ namespace Connector_HTTP { | |||
|      | ||||
|     Socket::Connection ss( -1); | ||||
|     std::string streamname; | ||||
|     std::string recBuffer = ""; | ||||
|     bool handlingRequest = false; | ||||
|      | ||||
|     int Quality = 0; | ||||
|     int Segment = -1; | ||||
|  | @ -161,124 +161,125 @@ namespace Connector_HTTP { | |||
|     conn.setBlocking(false); //do not block on conn.spool() when no data is available
 | ||||
|      | ||||
|     while (conn.connected()){ | ||||
|       if (conn.spool() || conn.Received().size()){ | ||||
|         if (HTTP_R.Read(conn)){ | ||||
|           #if DEBUG >= 5 | ||||
|           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 ( !handlingRequest){ | ||||
|         if (conn.spool() || conn.Received().size()){ | ||||
|           if (HTTP_R.Read(conn)){ | ||||
|             #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 | ||||
|             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"); | ||||
|                       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){ | ||||
|             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("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 " << ReqFragment << " too old" << std::endl; | ||||
|                 HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); | ||||
|                 HTTP_S.SendResponse("404", "Not found", conn); | ||||
|                 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; | ||||
|             sstream << "t " << Quality << " " << audioTrack << "\ns " << mstime << "\np " << (mstime + mslen) << "\n"; | ||||
|             ss.SendNow(sstream.str().c_str()); | ||||
|             std::cout << sstream.str() << std::endl; | ||||
|              | ||||
|             HTTP_S.Clean(); | ||||
|             HTTP_S.protocol = "HTTP/1.1"; | ||||
|             HTTP_S.SetHeader("Content-Type", "video/mp4"); | ||||
|             HTTP_S.SetBody(""); | ||||
|             std::string new_strap = dynamicBootstrap(streamname, Strm.getTrackById(Quality), Strm.metadata.isMember("live"), ReqFragment); | ||||
|             HTTP_S.SetHeader("Transfer-Encoding", "chunked"); | ||||
|             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 (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"); | ||||
|               HTTP_S.SendResponse("200", "OK", conn); | ||||
|               HTTP_R.Clean(); //clean for any possible next requests
 | ||||
|               continue; | ||||
|             } | ||||
|             if (Quality > 0 && Strm.getTrackById(Quality).isMember("init")){ | ||||
|               tmp.DTSCVideoInit(Strm.getTrackById(Quality)); | ||||
|               tmp.tagTime(mstime); | ||||
|               HTTP_S.Chunkify(tmp.data, tmp.len, conn); | ||||
|             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 | ||||
|               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_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
 | ||||
|           } | ||||
|           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()){ | ||||
|         unsigned int now = Util::epoch(); | ||||
|  | @ -286,13 +287,12 @@ namespace Connector_HTTP { | |||
|           lastStats = now; | ||||
|           ss.SendNow(conn.getStats("HTTP_Dynamic").c_str()); | ||||
|         } | ||||
|         if (ss.spool()){ | ||||
|         if (handlingRequest && ss.spool()){ | ||||
|           while (Strm.parsePacket(ss.Received())){ | ||||
|             if (Strm.lastType() == DTSC::PAUSEMARK){ | ||||
|               //send an empty chunk to signify request is done
 | ||||
|               std::string empty = ""; | ||||
|               HTTP_S.Chunkify(empty, conn); | ||||
|               std::cout << "Finito!" << std::endl; | ||||
|               HTTP_S.Chunkify("", 0, conn); | ||||
|               handlingRequest = false; | ||||
|             } | ||||
|             if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){ | ||||
|               //send a chunk with the new data
 | ||||
|  |  | |||
|  | @ -94,9 +94,6 @@ namespace Connector_HTTP { | |||
|   ///\param conn A socket describing the connection the client.
 | ||||
|   ///\return The exit code of the connector.
 | ||||
|   int liveConnector(Socket::Connection conn){ | ||||
|     std::stringstream TSBuf; | ||||
|     long long int TSBufTime = 0; | ||||
| 
 | ||||
|     DTSC::Stream Strm; //Incoming stream buffer.
 | ||||
|     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.
 | ||||
|     Socket::Connection ss( -1); | ||||
|     std::string streamname; | ||||
|     bool handlingRequest = false; | ||||
|     std::string recBuffer = ""; | ||||
| 
 | ||||
|     TS::Packet PackData; | ||||
|  | @ -129,100 +127,107 @@ namespace Connector_HTTP { | |||
|     conn.setBlocking(false); //do not block on conn.spool() when no data is available
 | ||||
| 
 | ||||
|     while (conn.connected()){ | ||||
|       if (conn.spool() || conn.Received().size()){ | ||||
|         if (HTTP_R.Read(conn)){ | ||||
| #if DEBUG >= 5 | ||||
|           std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; | ||||
| #endif | ||||
|           conn.setHost(HTTP_R.GetHeader("X-Origin")); | ||||
|           AppleCompat = (HTTP_R.GetHeader("User-Agent").find("Apple") != std::string::npos); | ||||
|           streamname = HTTP_R.GetHeader("X-Stream"); | ||||
|           if ( !ss){ | ||||
|             ss = Util::Stream::getStream(streamname); | ||||
|             if ( !ss.connected()){ | ||||
|               #if DEBUG >= 1 | ||||
|               fprintf(stderr, "Could not connect to server!\n"); | ||||
|               #endif | ||||
|               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")); | ||||
|               ready4data = false; | ||||
|               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 ( !handlingRequest){ | ||||
|         if (conn.spool() || conn.Received().size()){ | ||||
|           if (HTTP_R.Read(conn)){ | ||||
|   #if DEBUG >= 5 | ||||
|             std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; | ||||
|   #endif | ||||
|             conn.setHost(HTTP_R.GetHeader("X-Origin")); | ||||
|             AppleCompat = (HTTP_R.GetHeader("User-Agent").find("Apple") != std::string::npos); | ||||
|             streamname = HTTP_R.GetHeader("X-Stream"); | ||||
|             if ( !ss){ | ||||
|               ss = Util::Stream::getStream(streamname); | ||||
|               if ( !ss.connected()){ | ||||
|                 #if DEBUG >= 1 | ||||
|                 fprintf(stderr, "Could not connect to server!\n"); | ||||
|                 #endif | ||||
|                 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")); | ||||
|                 ready4data = false; | ||||
|                 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(".m3u") == std::string::npos){ | ||||
|             temp = HTTP_R.url.find("/", 5) + 1; | ||||
|             std::string allTracks = HTTP_R.url.substr(temp, HTTP_R.url.find("/", temp) - temp); | ||||
|             trackID = atoi(allTracks.c_str()); | ||||
|             audioTrackID = atoi(allTracks.substr(allTracks.find("_")+1).c_str()); | ||||
|             temp = HTTP_R.url.find("/", temp) + 1; | ||||
|             Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("_", temp) - temp).c_str()); | ||||
|             lastVid = Segment * 90; | ||||
|             temp = HTTP_R.url.find("_", temp) + 1; | ||||
|             int frameCount = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str()); | ||||
|             if (Strm.metadata.isMember("live")){ | ||||
|               int seekable = Strm.canSeekms(Segment); | ||||
|               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 @ " << Segment << " too old" << std::endl; | ||||
|                 continue; | ||||
|             if (HTTP_R.url.find(".m3u") == std::string::npos){ | ||||
|               temp = HTTP_R.url.find("/", 5) + 1; | ||||
|               std::string allTracks = HTTP_R.url.substr(temp, HTTP_R.url.find("/", temp) - temp); | ||||
|               trackID = atoi(allTracks.c_str()); | ||||
|               audioTrackID = atoi(allTracks.substr(allTracks.find("_")+1).c_str()); | ||||
|               temp = HTTP_R.url.find("/", temp) + 1; | ||||
|               Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("_", temp) - temp).c_str()); | ||||
|               lastVid = Segment * 90; | ||||
|               temp = HTTP_R.url.find("_", temp) + 1; | ||||
|               int frameCount = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str()); | ||||
|               if (Strm.metadata.isMember("live")){ | ||||
|                 int seekable = Strm.canSeekms(Segment); | ||||
|                 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 @ " << Segment << " too old" << 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 @ " << Segment << " not available yet" << 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 @ " << Segment << " not available yet" << std::endl; | ||||
|                 continue; | ||||
|               for (int i = 0; i < allTracks.size(); i++){ | ||||
|                 if (allTracks[i] == '_'){ | ||||
|                   allTracks[i] = ' '; | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             for (int i = 0; i < allTracks.size(); i++){ | ||||
|               if (allTracks[i] == '_'){ | ||||
|                 allTracks[i] = ' '; | ||||
|               } | ||||
|             } | ||||
|             std::stringstream sstream; | ||||
|             sstream << "t " << allTracks << "\n"; | ||||
|             sstream << "s " << Segment << "\n"; | ||||
|             sstream << "p " << frameCount << "\n"; | ||||
|             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"; | ||||
|               std::stringstream sstream; | ||||
|               sstream << "t " << allTracks << "\n"; | ||||
|               sstream << "s " << Segment << "\n"; | ||||
|               sstream << "p " << frameCount << "\n"; | ||||
|               ss.SendNow(sstream.str().c_str()); | ||||
|                | ||||
|               HTTP_S.Clean(); | ||||
|               HTTP_S.SetHeader("Content-Type", "video/mp2t"); | ||||
|               HTTP_S.StartResponse(HTTP_R, conn); | ||||
|               handlingRequest = true; | ||||
|             }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(); | ||||
|             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")); | ||||
|             ready4data = true; | ||||
|             HTTP_R.Clean(); //clean for any possible next requests
 | ||||
|           } | ||||
|           ready4data = true; | ||||
|           HTTP_R.Clean(); //clean for any possible next requests
 | ||||
|         }else{ | ||||
|           Util::sleep(250); | ||||
|         } | ||||
|       }else{ | ||||
|         Util::sleep(1); | ||||
|       } | ||||
|       if (ready4data){ | ||||
|         unsigned int now = Util::epoch(); | ||||
|  | @ -230,23 +235,11 @@ namespace Connector_HTTP { | |||
|           lastStats = now; | ||||
|           ss.SendNow(conn.getStats("HTTP_Live").c_str()); | ||||
|         } | ||||
|         if (ss.spool()){ | ||||
|         if (handlingRequest && ss.spool()){ | ||||
|           while (Strm.parsePacket(ss.Received())){ | ||||
|             if (Strm.lastType() == DTSC::PAUSEMARK){ | ||||
|               TSBuf.flush(); | ||||
|               if (TSBuf.str().size()){ | ||||
|                 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(""); | ||||
|               HTTP_S.Chunkify("", 0, conn); | ||||
|               handlingRequest = false; | ||||
|             } | ||||
|             if ( !haveAvcc){ | ||||
|               avccbox.setPayload(Strm.getTrackById(trackID)["init"].asString()); | ||||
|  | @ -257,9 +250,9 @@ namespace Connector_HTTP { | |||
|               //write PAT and PMT TS packets
 | ||||
|               if (PacketNumber % 42 == 0){ | ||||
|                 PackData.DefaultPAT(); | ||||
|                 TSBuf.write(PackData.ToString(), 188); | ||||
|                 HTTP_S.Chunkify(PackData.ToString(), 188, conn); | ||||
|                 PackData.DefaultPMT(); | ||||
|                 TSBuf.write(PackData.ToString(), 188); | ||||
|                 HTTP_S.Chunkify(PackData.ToString(), 188, conn); | ||||
|                 PacketNumber += 2; | ||||
|               } | ||||
| 
 | ||||
|  | @ -310,7 +303,7 @@ namespace Connector_HTTP { | |||
|               unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184)); | ||||
|               std::string gonnaSend = ToPack.remove(toSend); | ||||
|               PackData.FillFree(gonnaSend); | ||||
|               TSBuf.write(PackData.ToString(), 188); | ||||
|               HTTP_S.Chunkify(PackData.ToString(), 188, conn); | ||||
|               PacketNumber++; | ||||
| 
 | ||||
|               //rest of packets
 | ||||
|  | @ -321,7 +314,7 @@ namespace Connector_HTTP { | |||
|                 toSend = PackData.AddStuffing(ToPack.bytes(184)); | ||||
|                 gonnaSend = ToPack.remove(toSend); | ||||
|                 PackData.FillFree(gonnaSend); | ||||
|                 TSBuf.write(PackData.ToString(), 188); | ||||
|                 HTTP_S.Chunkify(PackData.ToString(), 188, conn); | ||||
|                 PacketNumber++; | ||||
|               } | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,12 +54,7 @@ namespace Connector_HTTP { | |||
|             std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; | ||||
| #endif | ||||
|             conn.setHost(HTTP_R.GetHeader("X-Origin")); | ||||
|             //we assume the URL is the stream name with a 3 letter extension
 | ||||
|             streamname = HTTP_R.getUrl().substr(1); | ||||
|             size_t extDot = streamname.rfind('.'); | ||||
|             if (extDot != std::string::npos){ | ||||
|               streamname.resize(extDot); | ||||
|             }; //strip the extension
 | ||||
|             streamname = HTTP_R.GetHeader("X-Stream"); | ||||
|             int start = 0; | ||||
|             if ( !HTTP_R.GetVar("start").empty()){ | ||||
|               start = atoi(HTTP_R.GetVar("start").c_str()); | ||||
|  | @ -126,7 +121,9 @@ namespace Connector_HTTP { | |||
|             byterate += Strm.getTrackById(audioID)["bps"].asInt(); | ||||
|           } | ||||
|           if ( !byterate){byterate = 1;} | ||||
|           seek_sec = (seek_byte / byterate) * 1000; | ||||
|           if (seek_byte){ | ||||
|             seek_sec = (seek_byte / byterate) * 1000; | ||||
|           } | ||||
|           std::stringstream cmd; | ||||
|           cmd << "t"; | ||||
|           if (videoID != -1){ | ||||
|  |  | |||
|  | @ -52,12 +52,7 @@ namespace Connector_HTTP { | |||
|             std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; | ||||
| #endif | ||||
|             conn.setHost(HTTP_R.GetHeader("X-Origin")); | ||||
|             //we assume the URL is the stream name with a 3 letter extension
 | ||||
|             streamname = HTTP_R.getUrl().substr(1); | ||||
|             size_t extDot = streamname.rfind('.'); | ||||
|             if (extDot != std::string::npos){ | ||||
|               streamname.resize(extDot); | ||||
|             }; //strip the extension
 | ||||
|             streamname = HTTP_R.GetHeader("X-Stream"); | ||||
|             int start = 0; | ||||
|             if ( !HTTP_R.GetVar("start").empty()){ | ||||
|               start = atoi(HTTP_R.GetVar("start").c_str()); | ||||
|  | @ -121,7 +116,9 @@ namespace Connector_HTTP { | |||
|             byterate += Strm.getTrackById(audioID)["bps"].asInt(); | ||||
|           } | ||||
|           if ( !byterate){byterate = 1;} | ||||
|           seek_sec = (seek_byte / byterate) * 1000; | ||||
|           if (seek_byte){ | ||||
|             seek_sec = (seek_byte / byterate) * 1000; | ||||
|           } | ||||
|           std::stringstream cmd; | ||||
|           cmd << "t"; | ||||
|           if (videoID != -1){ | ||||
|  |  | |||
|  | @ -60,12 +60,7 @@ namespace Connector_HTTP { | |||
|             std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; | ||||
| #endif | ||||
|             conn.setHost(HTTP_R.GetHeader("X-Origin")); | ||||
|             //we assume the URL is the stream name with a 3 letter extension
 | ||||
|             streamname = HTTP_R.getUrl().substr(1); | ||||
|             size_t extDot = streamname.rfind('.'); | ||||
|             if (extDot != std::string::npos){ | ||||
|               streamname.resize(extDot); | ||||
|             }; //strip the extension
 | ||||
|             streamname = HTTP_R.GetHeader("X-Stream"); | ||||
|             int start = 0; | ||||
|             if ( !HTTP_R.GetVar("start").empty()){ | ||||
|               start = atoi(HTTP_R.GetVar("start").c_str()); | ||||
|  | @ -132,7 +127,9 @@ namespace Connector_HTTP { | |||
|             byterate += Strm.getTrackById(audioID)["bps"].asInt(); | ||||
|           } | ||||
|           if ( !byterate){byterate = 1;} | ||||
|           seek_sec = (seek_byte / byterate) * 1000; | ||||
|           if (seek_byte){ | ||||
|             seek_sec = (seek_byte / byterate) * 1000; | ||||
|           } | ||||
|           std::stringstream cmd; | ||||
|           cmd << "t"; | ||||
|           if (videoID != -1){ | ||||
|  |  | |||
|  | @ -60,10 +60,10 @@ namespace Connector_HTTP { | |||
|       } | ||||
|       if (oIt->second["type"].asString() == "video"){ | ||||
|         allVideo[oIt->first] = oIt->second; | ||||
|         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["height"].asInt() > maxHeight){ maxHeight = oIt->second["height"].asInt(); } | ||||
|         if (oIt->second["height"].asInt() < minHeight){ minHeight = oIt->second["height"].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["height"].asInt() > maxHeight){maxHeight = 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.
 | ||||
|     Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired 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 wantsAudio = false;//Indicates whether this request is an audio request.
 | ||||
|  | @ -178,257 +179,253 @@ namespace Connector_HTTP { | |||
|     JSON::Value allVideo; | ||||
| 
 | ||||
|     while (conn.connected()){ | ||||
|       if (conn.spool() || conn.Received().size()){ | ||||
|         if (HTTP_R.Read(conn)){ | ||||
| #if DEBUG >= 5 | ||||
|           std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; | ||||
| #endif | ||||
|           //Get data set by the proxy.
 | ||||
|           conn.setHost(HTTP_R.GetHeader("X-Origin")); | ||||
|           streamname = HTTP_R.GetHeader("X-Stream"); | ||||
|           if ( !ss){ | ||||
|             //initiate Stream Socket
 | ||||
|             ss = Util::Stream::getStream(streamname); | ||||
|             if ( !ss.connected()){ | ||||
|               #if DEBUG >= 1 | ||||
|               fprintf(stderr, "Could not connect to server!\n"); | ||||
|               #endif | ||||
|               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")); | ||||
|               ready4data = false; | ||||
|               continue; | ||||
|             } | ||||
|             ss.setBlocking(false); | ||||
|             //Do nothing until metadata has been received.
 | ||||
|             while ( !Strm.metadata && ss.connected()){ | ||||
|               if (ss.spool()){ | ||||
|                 while (Strm.parsePacket(ss.Received())){ | ||||
|                   //do nothing
 | ||||
|       if ( !handlingRequest){ | ||||
|         if (conn.spool() || conn.Received().size()){ | ||||
|           if (HTTP_R.Read(conn)){ | ||||
|   #if DEBUG >= 5 | ||||
|             std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; | ||||
|   #endif | ||||
|             //Get data set by the proxy.
 | ||||
|             conn.setHost(HTTP_R.GetHeader("X-Origin")); | ||||
|             streamname = HTTP_R.GetHeader("X-Stream"); | ||||
|             if ( !ss){ | ||||
|               //initiate Stream Socket
 | ||||
|               ss = Util::Stream::getStream(streamname); | ||||
|               if ( !ss.connected()){ | ||||
|                 #if DEBUG >= 1 | ||||
|                 fprintf(stderr, "Could not connect to server!\n"); | ||||
|                 #endif | ||||
|                 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")); | ||||
|                 ready4data = false; | ||||
|                 continue; | ||||
|               } | ||||
|               ss.setBlocking(false); | ||||
|               //Do nothing until metadata has been received.
 | ||||
|               while ( !Strm.metadata && ss.connected()){ | ||||
|                 if (ss.spool()){ | ||||
|                   while (Strm.parsePacket(ss.Received())){ | ||||
|                     //do nothing
 | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             for (JSON::ObjIter oIt = Strm.metadata["tracks"].ObjBegin(); oIt != Strm.metadata["tracks"].ObjEnd(); oIt++){ | ||||
|               if (oIt->second["type"].asString() == "audio"){ | ||||
|                 allAudio[oIt->first] = oIt->second; | ||||
|               for (JSON::ObjIter oIt = Strm.metadata["tracks"].ObjBegin(); oIt != Strm.metadata["tracks"].ObjEnd(); oIt++){ | ||||
|                 if (oIt->second["type"].asString() == "audio"){ | ||||
|                   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){ | ||||
|             //We have a non-manifest request, parse it.
 | ||||
|             Quality = HTTP_R.url.substr(HTTP_R.url.find("/Q(", 8) + 3); | ||||
|             Quality = Quality.substr(0, Quality.find(")")); | ||||
|             parseString = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2); | ||||
|             wantsAudio = false; | ||||
|             wantsVideo = false; | ||||
|             if (parseString[0] == 'A'){ | ||||
|               wantsAudio = true; | ||||
|             } | ||||
|             if (parseString[0] == 'V'){ | ||||
|               wantsVideo = true; | ||||
|             } | ||||
|             parseString = parseString.substr(parseString.find("(") + 1); | ||||
|             requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str()); | ||||
|             if (Strm.metadata.isMember("live")){ | ||||
|               ///\todo Fix this for live stuff
 | ||||
|               int seekable = Strm.canSeekms(requestedTime / 10000); | ||||
|               if (seekable == 0){ | ||||
|                 // iff the fragment in question is available, check if the next is available too
 | ||||
|                 for (int i = 0; i < Strm.metadata["keytime"].size(); i++){ | ||||
|                   if (Strm.metadata["keytime"][i].asInt() >= (requestedTime / 10000)){ | ||||
|                     if (i + 1 == Strm.metadata["keytime"].size()){ | ||||
|                       seekable = 1; | ||||
|             if (HTTP_R.url.find("Manifest") == std::string::npos){ | ||||
|               //We have a non-manifest request, parse it.
 | ||||
|               Quality = HTTP_R.url.substr(HTTP_R.url.find("/Q(", 8) + 3); | ||||
|               Quality = Quality.substr(0, Quality.find(")")); | ||||
|               parseString = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2); | ||||
|               wantsAudio = false; | ||||
|               wantsVideo = false; | ||||
|               if (parseString[0] == 'A'){ | ||||
|                 wantsAudio = true; | ||||
|               } | ||||
|               if (parseString[0] == 'V'){ | ||||
|                 wantsVideo = true; | ||||
|               } | ||||
|               parseString = parseString.substr(parseString.find("(") + 1); | ||||
|               requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str()); | ||||
|               if (Strm.metadata.isMember("live")){ | ||||
|                 ///\todo Fix this for live stuff
 | ||||
|                 int seekable = Strm.canSeekms(requestedTime / 10000); | ||||
|                 if (seekable == 0){ | ||||
|                   // iff the fragment in question is available, check if the next is available too
 | ||||
|                   for (int i = 0; i < Strm.metadata["keytime"].size(); i++){ | ||||
|                     if (Strm.metadata["keytime"][i].asInt() >= (requestedTime / 10000)){ | ||||
|                       if (i + 1 == Strm.metadata["keytime"].size()){ | ||||
|                         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; | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|               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 (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
 | ||||
|               if (Strm.metadata.isMember("live")){ | ||||
|                 if (mstime == 0 && (requestedTime / 10000) > 1){ | ||||
|                   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) << " too old" << std::endl; | ||||
|                   continue; | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             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; | ||||
|                 } | ||||
|                | ||||
|                | ||||
|               sstream << "t " << myRef["trackid"].asInt() << "\n"; | ||||
|               sstream << "s " << (requestedTime / 10000) << "\np " << (mstime + mslen) <<"\n"; | ||||
|               ss.SendNow(sstream.str().c_str()); | ||||
| 
 | ||||
|               unsigned int myDuration; | ||||
|                | ||||
|               //Wrap everything in mp4 boxes
 | ||||
|               MP4::MFHD mfhd_box; | ||||
|               JSON::Value trackRef; | ||||
|               if (wantsVideo){ | ||||
|                 trackRef = allVideo.ObjBegin()->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; | ||||
|                     } | ||||
|                   } | ||||
|               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; | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
 | ||||
|             if (Strm.metadata.isMember("live")){ | ||||
|               if (mstime == 0 && (requestedTime / 10000) > 1){ | ||||
|                 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) << " too old" << std::endl; | ||||
|                 continue; | ||||
|                | ||||
|               MP4::TFHD tfhd_box; | ||||
|               tfhd_box.setFlags(MP4::tfhdSampleFlag); | ||||
|               tfhd_box.setTrackID(1); | ||||
|               tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample); | ||||
|                | ||||
|               MP4::TRUN trun_box; | ||||
|               trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize); | ||||
|               trun_box.setDataOffset(42); | ||||
|               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); | ||||
|               } | ||||
|             } | ||||
|              | ||||
|              | ||||
|             sstream << "t " << myRef["trackid"].asInt() << "\n"; | ||||
|             sstream << "s " << (requestedTime / 10000) << "\np " << (mstime + mslen) <<"\n"; | ||||
|             ss.SendNow(sstream.str().c_str()); | ||||
| 
 | ||||
|             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::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::TFHD tfhd_box; | ||||
|             tfhd_box.setFlags(MP4::tfhdSampleFlag); | ||||
|             tfhd_box.setTrackID(1); | ||||
|             tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample); | ||||
|              | ||||
|             MP4::TRUN trun_box; | ||||
|             trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize); | ||||
|             trun_box.setDataOffset(42); | ||||
|             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); | ||||
|             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); | ||||
|                | ||||
|               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")); | ||||
|             } | ||||
| 
 | ||||
|             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); | ||||
| 
 | ||||
|             //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; | ||||
|             //Clean for any possible next requests
 | ||||
|             HTTP_R.Clean(); | ||||
|           } | ||||
|           ready4data = true; | ||||
|           //Clean for any possible next requests
 | ||||
|           HTTP_R.Clean(); | ||||
|         }else{ | ||||
|           //Wait 250ms before checking for new data.
 | ||||
|           Util::sleep(250); | ||||
|         } | ||||
|       }else{ | ||||
|         //Wait 1 second before checking for new data.
 | ||||
|         Util::sleep(1); | ||||
|       } | ||||
|       if (ready4data){ | ||||
|         unsigned int now = Util::epoch(); | ||||
|  | @ -437,12 +434,14 @@ namespace Connector_HTTP { | |||
|           lastStats = now; | ||||
|           ss.SendNow(conn.getStats("HTTP_Smooth").c_str()); | ||||
|         } | ||||
|         if (ss.spool()){ | ||||
|         if (handlingRequest && ss.spool()){ | ||||
|           while (Strm.parsePacket(ss.Received())){ | ||||
|             if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){ | ||||
|               //Select only the data that the client has requested.
 | ||||
|               int tmp = Util::getMS(); | ||||
|               conn.SendNow(Strm.lastData()); | ||||
|               HTTP_S.Chunkify(Strm.lastData(), conn); | ||||
|             } | ||||
|             if (Strm.lastType() == DTSC::PAUSEMARK){ | ||||
|               HTTP_S.Chunkify("", 0, conn); | ||||
|               handlingRequest = false; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Thulinma
						Thulinma