Generic HTTP handler
# Conflicts: # src/output/output_mp4.cpp # src/output/output_mp4.h
This commit is contained in:
parent
6f6827607d
commit
9953cd6ee2
10 changed files with 129 additions and 191 deletions
|
@ -274,7 +274,7 @@ void HTTP::Parser::SendResponse(std::string code, std::string message, Socket::C
|
|||
/// a zero-content-length HTTP/1.0 response. \param code The HTTP response code. Usually you want
|
||||
/// 200. \param message The HTTP response message. Usually you want "OK". \param request The HTTP
|
||||
/// request to respond to. \param conn The connection to send over.
|
||||
void HTTP::Parser::StartResponse(std::string code, std::string message, HTTP::Parser &request,
|
||||
void HTTP::Parser::StartResponse(std::string code, std::string message, const HTTP::Parser &request,
|
||||
Socket::Connection &conn, bool bufferAllChunks){
|
||||
std::string prot = request.protocol;
|
||||
sendingChunks =
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace HTTP{
|
|||
void sendRequest(Socket::Connection &conn, const void *body = 0, const size_t bodyLen = 0,
|
||||
bool allAtOnce = false);
|
||||
void SendResponse(std::string code, std::string message, Socket::Connection &conn);
|
||||
void StartResponse(std::string code, std::string message, Parser &request,
|
||||
void StartResponse(std::string code, std::string message, const Parser &request,
|
||||
Socket::Connection &conn, bool bufferAllChunks = false);
|
||||
void StartResponse(Parser &request, Socket::Connection &conn, bool bufferAllChunks = false);
|
||||
void Chunkify(const std::string &bodypart, Socket::Connection &conn);
|
||||
|
|
|
@ -24,8 +24,6 @@ namespace Mist{
|
|||
cfg->addOption("target", opt);
|
||||
}
|
||||
|
||||
bool OutAAC::isRecording(){return config->getString("target").size();}
|
||||
|
||||
void OutAAC::initialSeek(){
|
||||
if (!meta){return;}
|
||||
maxSkipAhead = 30000;
|
||||
|
@ -42,40 +40,20 @@ namespace Mist{
|
|||
void OutAAC::sendNext(){
|
||||
char *dataPointer = 0;
|
||||
size_t len = 0;
|
||||
|
||||
thisPacket.getString("data", dataPointer, len);
|
||||
std::string head = TS::getAudioHeader(len, M.getInit(thisIdx));
|
||||
myConn.SendNow(head);
|
||||
myConn.SendNow(dataPointer, len);
|
||||
}
|
||||
|
||||
void OutAAC::sendHeader(){
|
||||
if (!isRecording()){
|
||||
H.Clean();
|
||||
H.SetHeader("Content-Type", "audio/aac");
|
||||
H.SetHeader("Accept-Ranges", "none");
|
||||
H.protocol = "HTTP/1.0";
|
||||
H.setCORSHeaders();
|
||||
H.SendResponse("200", "OK", myConn);
|
||||
void OutAAC::respondHTTP(const HTTP::Parser & req, bool headersOnly){
|
||||
HTTPOutput::respondHTTP(req, headersOnly);
|
||||
H.protocol = "HTTP/1.0";
|
||||
H.SendResponse("200", "OK", myConn);
|
||||
if (!headersOnly){
|
||||
parseData = true;
|
||||
wantRequest = false;
|
||||
}
|
||||
sentHeader = true;
|
||||
}
|
||||
|
||||
void OutAAC::onHTTP(){
|
||||
std::string method = H.method;
|
||||
if (method == "OPTIONS" || method == "HEAD"){
|
||||
H.Clean();
|
||||
H.SetHeader("Content-Type", "audio/aac");
|
||||
H.SetHeader("Accept-Ranges", "none");
|
||||
H.protocol = "HTTP/1.0";
|
||||
H.setCORSHeaders();
|
||||
H.SendResponse("200", "OK", myConn);
|
||||
H.Clean();
|
||||
return;
|
||||
}
|
||||
|
||||
parseData = true;
|
||||
wantRequest = false;
|
||||
}
|
||||
|
||||
}// namespace Mist
|
||||
|
|
|
@ -5,13 +5,11 @@ namespace Mist{
|
|||
public:
|
||||
OutAAC(Socket::Connection &conn);
|
||||
static void init(Util::Config *cfg);
|
||||
void onHTTP();
|
||||
void respondHTTP(const HTTP::Parser & req, bool headersOnly);
|
||||
void sendNext();
|
||||
void sendHeader();
|
||||
void initialSeek();
|
||||
|
||||
private:
|
||||
bool isRecording();
|
||||
bool isFileTarget(){return isRecording();}
|
||||
};
|
||||
}// namespace Mist
|
||||
|
|
|
@ -391,17 +391,15 @@ namespace Mist{
|
|||
// End of file. This probably won't work right, but who cares, it's the end of the file.
|
||||
}
|
||||
|
||||
void OutEBML::onHTTP(){
|
||||
std::string method = H.method;
|
||||
if (method == "OPTIONS" || method == "HEAD"){
|
||||
H.Clean();
|
||||
H.setCORSHeaders();
|
||||
H.SetHeader("Content-Type", "video/MP4");
|
||||
H.SetHeader("Accept-Ranges", "bytes, parsec");
|
||||
H.SendResponse("200", "OK", myConn);
|
||||
return;
|
||||
}
|
||||
if (H.url.find(".webm") != std::string::npos){
|
||||
void OutEBML::respondHTTP(const HTTP::Parser & req, bool headersOnly){
|
||||
//Set global defaults, first
|
||||
HTTPOutput::respondHTTP(req, headersOnly);
|
||||
|
||||
//If non-live, we accept range requests
|
||||
if (!M.getLive()){H.SetHeader("Accept-Ranges", "bytes, parsec");}
|
||||
|
||||
//We change the header's document type based on file extension
|
||||
if (req.url.find(".webm") != std::string::npos){
|
||||
doctype = "webm";
|
||||
}else{
|
||||
doctype = "matroska";
|
||||
|
@ -417,72 +415,46 @@ namespace Mist{
|
|||
|
||||
size_t byteEnd = totalSize - 1;
|
||||
size_t byteStart = 0;
|
||||
|
||||
/*LTS-START*/
|
||||
// allow setting of max lead time through buffer variable.
|
||||
// max lead time is set in MS, but the variable is in integer seconds for simplicity.
|
||||
if (H.GetVar("buffer") != ""){maxSkipAhead = JSON::Value(H.GetVar("buffer")).asInt() * 1000;}
|
||||
// allow setting of play back rate through buffer variable.
|
||||
// play back rate is set in MS per second, but the variable is a simple multiplier.
|
||||
if (H.GetVar("rate") != ""){
|
||||
long long int multiplier = JSON::Value(H.GetVar("rate")).asInt();
|
||||
if (multiplier){
|
||||
realTime = 1000 / multiplier;
|
||||
}else{
|
||||
realTime = 0;
|
||||
}
|
||||
}
|
||||
if (H.GetHeader("X-Mist-Rate") != ""){
|
||||
long long int multiplier = JSON::Value(H.GetHeader("X-Mist-Rate")).asInt();
|
||||
if (multiplier){
|
||||
realTime = 1000 / multiplier;
|
||||
}else{
|
||||
realTime = 0;
|
||||
}
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
char rangeType = ' ';
|
||||
if (M.getVod()){
|
||||
if (H.GetHeader("Range") != ""){
|
||||
if (parseRange(byteStart, byteEnd)){
|
||||
if (H.GetVar("buffer") == ""){
|
||||
size_t idx = getMainSelectedTrack();
|
||||
maxSkipAhead = (M.getLastms(idx) - M.getFirstms(idx)) / 20 + 7500;
|
||||
}
|
||||
if (!M.getLive() && req.GetHeader("Range") != ""){
|
||||
//Range request
|
||||
if (parseRange(req.GetHeader("Range"), byteStart, byteEnd)){
|
||||
if (!req.GetVar("buffer").size()){
|
||||
size_t idx = getMainSelectedTrack();
|
||||
maxSkipAhead = (M.getLastms(idx) - M.getFirstms(idx)) / 20 + 7500;
|
||||
}
|
||||
rangeType = H.GetHeader("Range")[0];
|
||||
}
|
||||
}
|
||||
H.Clean(); // make sure no parts of old requests are left in any buffers
|
||||
H.setCORSHeaders();
|
||||
H.SetHeader("Content-Type", "video/webm");
|
||||
if (M.getVod()){H.SetHeader("Accept-Ranges", "bytes, parsec");}
|
||||
if (rangeType != ' '){
|
||||
//Failed range request
|
||||
if (!byteEnd){
|
||||
if (rangeType == 'p'){
|
||||
H.SetBody("Starsystem not in communications range");
|
||||
if (req.GetHeader("Range")[0] == 'p'){
|
||||
if (!headersOnly){H.SetBody("Starsystem not in communications range");}
|
||||
H.SendResponse("416", "Starsystem not in communications range", myConn);
|
||||
return;
|
||||
}
|
||||
H.SetBody("Requested Range Not Satisfiable");
|
||||
if (!headersOnly){H.SetBody("Requested Range Not Satisfiable");}
|
||||
H.SendResponse("416", "Requested Range Not Satisfiable", myConn);
|
||||
return;
|
||||
}
|
||||
//Successful range request
|
||||
std::stringstream rangeReply;
|
||||
rangeReply << "bytes " << byteStart << "-" << byteEnd << "/" << totalSize;
|
||||
H.SetHeader("Content-Length", byteEnd - byteStart + 1);
|
||||
H.SetHeader("Content-Range", rangeReply.str());
|
||||
/// \todo Switch to chunked?
|
||||
H.SendResponse("206", "Partial content", myConn);
|
||||
byteSeek(byteStart);
|
||||
if (!headersOnly){
|
||||
byteSeek(byteStart);
|
||||
}
|
||||
}else{
|
||||
if (M.getVod()){H.SetHeader("Content-Length", byteEnd - byteStart + 1);}
|
||||
//Non-range request
|
||||
if (!M.getLive()){H.SetHeader("Content-Length", byteEnd - byteStart + 1);}
|
||||
/// \todo Switch to chunked?
|
||||
H.SendResponse("200", "OK", myConn);
|
||||
}
|
||||
parseData = true;
|
||||
wantRequest = false;
|
||||
//Start outputting data
|
||||
if (!headersOnly){
|
||||
parseData = true;
|
||||
wantRequest = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OutEBML::calcVodSizes(){
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Mist{
|
|||
public:
|
||||
OutEBML(Socket::Connection &conn);
|
||||
static void init(Util::Config *cfg);
|
||||
void onHTTP();
|
||||
void respondHTTP(const HTTP::Parser & req, bool headersOnly);
|
||||
void sendNext();
|
||||
void sendHeader();
|
||||
size_t clusterSize(uint64_t start, uint64_t end);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "output_http.h"
|
||||
#include <mist/checksum.h>
|
||||
#include <mist/encode.h>
|
||||
#include <mist/langcodes.h>
|
||||
#include <mist/stream.h>
|
||||
#include <mist/util.h>
|
||||
|
@ -221,8 +222,6 @@ namespace Mist{
|
|||
myConn.close();
|
||||
return;
|
||||
}
|
||||
std::string connHeader = H.GetHeader("Connection");
|
||||
Util::stringToLower(connHeader);
|
||||
if (handler != capa["name"].asStringRef() || H.GetVar("stream") != streamName){
|
||||
MEDIUM_MSG("Switching from %s (%s) to %s (%s)", capa["name"].asStringRef().c_str(),
|
||||
streamName.c_str(), handler.c_str(), H.GetVar("stream").c_str());
|
||||
|
@ -263,8 +262,12 @@ namespace Mist{
|
|||
if (H.GetVar("stop") != ""){targetParams["stop"] = H.GetVar("stop");}
|
||||
if (H.GetVar("startunix") != ""){targetParams["startunix"] = H.GetVar("startunix");}
|
||||
if (H.GetVar("stopunix") != ""){targetParams["stopunix"] = H.GetVar("stopunix");}
|
||||
if (H.GetVar("buffer") != ""){targetParams["buffer"] = H.GetVar("buffer");}
|
||||
// allow setting of play back rate through buffer variable.
|
||||
// allow setting of max lead time through buffer variable.
|
||||
// max lead time is set in MS, but the variable is in integer seconds for simplicity.
|
||||
if (H.GetVar("buffer") != ""){
|
||||
maxSkipAhead = JSON::Value(H.GetVar("buffer")).asInt() * 1000;
|
||||
}
|
||||
// allow setting of play back rate through rate variable or custom HTTP header.
|
||||
// play back rate is set in MS per second, but the variable is a simple multiplier.
|
||||
if (H.GetVar("rate") != ""){
|
||||
long long int multiplier = JSON::Value(H.GetVar("rate")).asInt();
|
||||
|
@ -301,18 +304,58 @@ namespace Mist{
|
|||
}
|
||||
responded = false;
|
||||
preHTTP();
|
||||
if (!myConn){return;}
|
||||
onHTTP();
|
||||
idleLast = Util::bootMS();
|
||||
if (!H.bufferChunks){H.Clean();}
|
||||
}
|
||||
}
|
||||
|
||||
/// Default HTTP handler.
|
||||
/// Only takes care of OPTIONS and HEAD, saving the original request, and calling respondHTTP
|
||||
void HTTPOutput::onHTTP(){
|
||||
const HTTP::Parser reqH = H;
|
||||
bool headersOnly = (reqH.method == "OPTIONS" || reqH.method == "HEAD");
|
||||
H.Clean();
|
||||
respondHTTP(reqH, headersOnly);
|
||||
}
|
||||
|
||||
/// Default implementation of preHTTP simply calls initialize and selectDefaultTracks.
|
||||
void HTTPOutput::preHTTP(){
|
||||
initialize();
|
||||
selectDefaultTracks();
|
||||
}
|
||||
|
||||
/// Sets common HTTP headers. Virtual, so child classes can/will implement further behaviour if needed.
|
||||
/// Child classes are suggested to call the parent implementation and then add their own logic afterwards.
|
||||
void HTTPOutput::respondHTTP(const HTTP::Parser & req, bool headersOnly){
|
||||
//We generally want the CORS headers to be set for all responses
|
||||
H.setCORSHeaders();
|
||||
//Set attachment header to force download, if applicable
|
||||
if (req.GetVar("dl").size()){
|
||||
//If we want to download, and the string contains a dot, use as-is.
|
||||
std::string dl = req.GetVar("dl");
|
||||
//Without a dot, we use the last segment of the bare URL
|
||||
if (dl.find('.') == std::string::npos){
|
||||
dl = HTTP::URL(req.url).getFilePath();
|
||||
size_t lSlash = dl.rfind('/');
|
||||
if (lSlash != std::string::npos){dl = dl.substr(lSlash+1);}
|
||||
}
|
||||
H.SetHeader("Content-Disposition", "attachment; filename=" + Encodings::URL::encode(dl) + ";");
|
||||
//Force max download speed when downloading
|
||||
realTime = 0;
|
||||
}
|
||||
//If there is a method defined, and the first method has a type with two slashes in it,
|
||||
//assume the last two sections are the content type.
|
||||
if (capa.isMember("methods") && capa["methods"].isArray() && capa["methods"].size() && capa["methods"][0u].isMember("type")){
|
||||
const std::string & cType = capa["methods"][0u]["type"].asStringRef();
|
||||
size_t fSlash = cType.find('/');
|
||||
if (fSlash != std::string::npos && cType.rfind('/') != fSlash){
|
||||
H.SetHeader("Content-Type", cType.substr(fSlash+1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void builPipedPart(JSON::Value &p, char *argarr[], int &argnum, JSON::Value &argset){
|
||||
jsonForEach(argset, it){
|
||||
if (it->isMember("option") && p.isMember(it.key())){
|
||||
|
@ -488,8 +531,7 @@ namespace Mist{
|
|||
/// Parses a "Range: " header, setting byteStart and byteEnd.
|
||||
/// Assumes byteStart and byteEnd are initialized to their minimum respectively maximum values
|
||||
/// when the function is called. On error, byteEnd is set to zero and the function return false.
|
||||
bool HTTPOutput::parseRange(uint64_t &byteStart, uint64_t &byteEnd){
|
||||
std::string header = H.GetHeader("Range");
|
||||
bool HTTPOutput::parseRange(std::string header, uint64_t &byteStart, uint64_t &byteEnd){
|
||||
if (header.size() < 6 || header.substr(0, 6) != "bytes="){
|
||||
byteEnd = 0;
|
||||
WARN_MSG("Invalid range header: %s", header.c_str());
|
||||
|
|
|
@ -12,7 +12,8 @@ namespace Mist{
|
|||
virtual ~HTTPOutput();
|
||||
static void init(Util::Config *cfg);
|
||||
virtual void onFail(const std::string &msg, bool critical = false);
|
||||
virtual void onHTTP(){};
|
||||
virtual void onHTTP();
|
||||
virtual void respondHTTP(const HTTP::Parser & req, bool headersOnly);
|
||||
virtual void onIdle(){};
|
||||
virtual void onWebsocketFrame(){};
|
||||
virtual void onWebsocketConnect(){};
|
||||
|
@ -23,7 +24,7 @@ namespace Mist{
|
|||
virtual bool doesWebsockets(){return false;}
|
||||
void reConnector(std::string &connector);
|
||||
std::string getHandler();
|
||||
bool parseRange(uint64_t &byteStart, uint64_t &byteEnd);
|
||||
bool parseRange(std::string header, uint64_t &byteStart, uint64_t &byteEnd);
|
||||
|
||||
protected:
|
||||
bool firstRun;
|
||||
|
|
|
@ -1060,60 +1060,17 @@ namespace Mist{
|
|||
H.Chunkify(mdatHeader, 8, myConn);
|
||||
}
|
||||
|
||||
void OutMP4::onHTTP(){
|
||||
void OutMP4::respondHTTP(const HTTP::Parser & req, bool headersOnly){
|
||||
//Set global defaults, first
|
||||
HTTPOutput::respondHTTP(req, headersOnly);
|
||||
|
||||
if (webSock) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string dl;
|
||||
if (H.GetVar("dl").size()){
|
||||
dl = H.GetVar("dl");
|
||||
if (dl.find('.') == std::string::npos){dl = streamName + ".mp4";}
|
||||
}
|
||||
if (H.method == "OPTIONS" || H.method == "HEAD"){
|
||||
H.Clean();
|
||||
H.setCORSHeaders();
|
||||
if (dl.size()){
|
||||
H.SetHeader("Content-Disposition", "attachment; filename=" + Encodings::URL::encode(dl) + ";");
|
||||
}
|
||||
H.SetHeader("Content-Type", "video/MP4");
|
||||
H.SetHeader("Accept-Ranges", "bytes, parsec");
|
||||
H.StartResponse(H, myConn);
|
||||
return;
|
||||
}
|
||||
H.SetHeader("Content-Type", "video/MP4");
|
||||
if (!M.getLive()){H.SetHeader("Accept-Ranges", "bytes, parsec");}
|
||||
|
||||
chromeWorkaround = (H.GetHeader("User-Agent").find("Chrome") != std::string::npos &&
|
||||
H.GetHeader("User-Agent").find("Edge") == std::string::npos &&
|
||||
H.GetHeader("User-Agent").find("OPR/") == std::string::npos);
|
||||
chromeWorkaround = (req.GetHeader("User-Agent").find("Chrome") != std::string::npos &&
|
||||
req.GetHeader("User-Agent").find("Edge") == std::string::npos &&
|
||||
req.GetHeader("User-Agent").find("OPR/") == std::string::npos);
|
||||
|
||||
/*LTS-START*/
|
||||
// allow setting of max lead time through buffer variable.
|
||||
// max lead time is set in MS, but the variable is in integer seconds for simplicity.
|
||||
if (H.GetVar("buffer") != ""){maxSkipAhead = JSON::Value(H.GetVar("buffer")).asInt() * 1000;}
|
||||
// allow setting of play back rate through buffer variable.
|
||||
// play back rate is set in MS per second, but the variable is a simple multiplier.
|
||||
if (H.GetVar("rate") != ""){
|
||||
long long int multiplier = JSON::Value(H.GetVar("rate")).asInt();
|
||||
if (multiplier){
|
||||
realTime = 1000 / multiplier;
|
||||
}else{
|
||||
realTime = 0;
|
||||
}
|
||||
}
|
||||
if (H.GetHeader("X-Mist-Rate") != ""){
|
||||
long long int multiplier = JSON::Value(H.GetHeader("X-Mist-Rate")).asInt();
|
||||
if (multiplier){
|
||||
realTime = 1000 / multiplier;
|
||||
}else{
|
||||
realTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*LTS-END*/
|
||||
// Always initialize before anything else, and break if this failed.
|
||||
initialize();
|
||||
if (!myConn){return;}
|
||||
uint32_t mainTrack = M.mainTrack();
|
||||
if (mainTrack == INVALID_TRACK_ID){
|
||||
onFail("No main track found", true);
|
||||
|
@ -1122,9 +1079,9 @@ namespace Mist{
|
|||
|
||||
DTSC::Fragments fragments(M.fragments(mainTrack));
|
||||
|
||||
if (H.GetVar("startfrag") != ""){
|
||||
if (req.GetVar("startfrag") != ""){
|
||||
realTime = 0;
|
||||
size_t startFrag = JSON::Value(H.GetVar("startfrag")).asInt();
|
||||
size_t startFrag = JSON::Value(req.GetVar("startfrag")).asInt();
|
||||
if (startFrag >= fragments.getFirstValid() && startFrag < fragments.getEndValid()){
|
||||
startTime = M.getTimeForFragmentIndex(mainTrack, startFrag);
|
||||
|
||||
|
@ -1140,8 +1097,8 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
if (H.GetVar("endfrag") != ""){
|
||||
size_t endFrag = JSON::Value(H.GetVar("endfrag")).asInt();
|
||||
if (req.GetVar("endfrag") != ""){
|
||||
size_t endFrag = JSON::Value(req.GetVar("endfrag")).asInt();
|
||||
if (endFrag < fragments.getEndValid()){
|
||||
endTime = M.getTimeForFragmentIndex(mainTrack, endFrag);
|
||||
}else{
|
||||
|
@ -1149,19 +1106,14 @@ namespace Mist{
|
|||
}
|
||||
}
|
||||
|
||||
if (H.GetVar("starttime") != ""){
|
||||
startTime = std::max((uint64_t)JSON::Value(H.GetVar("starttime")).asInt(), M.getFirstms(mainTrack));
|
||||
if (req.GetVar("starttime") != ""){
|
||||
startTime = std::max((uint64_t)JSON::Value(req.GetVar("starttime")).asInt(), M.getFirstms(mainTrack));
|
||||
}
|
||||
|
||||
if (H.GetVar("endtime") != ""){
|
||||
endTime = std::min((uint64_t)JSON::Value(H.GetVar("endtime")).asInt(), M.getLastms(mainTrack));
|
||||
if (req.GetVar("endtime") != ""){
|
||||
endTime = std::min((uint64_t)JSON::Value(req.GetVar("endtime")).asInt(), M.getLastms(mainTrack));
|
||||
}
|
||||
|
||||
// Make sure we start receiving data after this function
|
||||
parseData = true;
|
||||
wantRequest = false;
|
||||
sentHeader = false;
|
||||
|
||||
// Check if the url contains .3gp --> if yes, we will send a 3gp header
|
||||
sending3GP = (H.url.find(".3gp") != std::string::npos);
|
||||
|
||||
|
@ -1169,14 +1121,9 @@ namespace Mist{
|
|||
headerSize = mp4HeaderSize(fileSize, M.getLive());
|
||||
|
||||
seekPoint = 0;
|
||||
if (M.getLive()){
|
||||
// for live we use fragmented mode
|
||||
fragSeqNum = 0;
|
||||
}
|
||||
byteStart = 0;
|
||||
byteEnd = fileSize - 1;
|
||||
char rangeType = ' ';
|
||||
currPos = 0;
|
||||
// for live we use fragmented mode
|
||||
if (M.getLive()){fragSeqNum = 0;}
|
||||
|
||||
sortSet.clear();
|
||||
for (std::map<size_t, Comms::Users>::const_iterator subIt = userSelect.begin();
|
||||
subIt != userSelect.end(); subIt++){
|
||||
|
@ -1186,23 +1133,14 @@ namespace Mist{
|
|||
temp.index = 0;
|
||||
sortSet.insert(temp);
|
||||
}
|
||||
if (M.getVod() && H.GetHeader("Range") != ""){
|
||||
if (parseRange(byteStart, byteEnd)){findSeekPoint(byteStart, seekPoint, headerSize);}
|
||||
rangeType = H.GetHeader("Range")[0];
|
||||
}
|
||||
HTTP::Parser request = H;
|
||||
H.Clean(); // make sure no parts of old requests are left in any buffers
|
||||
H.setCORSHeaders();
|
||||
if (dl.size()){
|
||||
H.SetHeader("Content-Disposition", "attachment; filename=" + Encodings::URL::encode(dl) + ";");
|
||||
realTime = 0; // force max download speed when downloading
|
||||
}
|
||||
H.SetHeader("Content-Type", "video/MP4"); // Send the correct content-type for MP4 files
|
||||
if (M.getVod()){H.SetHeader("Accept-Ranges", "bytes, parsec");}
|
||||
|
||||
if (rangeType != ' '){
|
||||
byteStart = 0;
|
||||
byteEnd = fileSize - 1;
|
||||
currPos = 0;
|
||||
if (!M.getLive() && req.GetHeader("Range") != ""){
|
||||
if (parseRange(req.GetHeader("Range"), byteStart, byteEnd)){findSeekPoint(byteStart, seekPoint, headerSize);}
|
||||
if (!byteEnd){
|
||||
if (rangeType == 'p'){
|
||||
if (req.GetHeader("Range")[0] == 'p'){
|
||||
H.SetBody("Starsystem not in communications range");
|
||||
H.SendResponse("416", "Starsystem not in communications range", myConn);
|
||||
parseData = false;
|
||||
|
@ -1220,12 +1158,21 @@ namespace Mist{
|
|||
rangeReply << "bytes " << byteStart << "-" << byteEnd << "/" << fileSize;
|
||||
H.SetHeader("Content-Length", byteEnd - byteStart + 1);
|
||||
H.SetHeader("Content-Range", rangeReply.str());
|
||||
H.StartResponse("206", "Partial content", request, myConn);
|
||||
H.StartResponse("206", "Partial content", req, myConn);
|
||||
}
|
||||
}else{
|
||||
if (M.getVod()){H.SetHeader("Content-Length", byteEnd - byteStart + 1);}
|
||||
H.StartResponse("200", "OK", request, myConn);
|
||||
H.StartResponse("200", "OK", req, myConn);
|
||||
}
|
||||
|
||||
if (headersOnly){return;}
|
||||
|
||||
//Start sending data
|
||||
parseData = true;
|
||||
wantRequest = false;
|
||||
sentHeader = false;
|
||||
|
||||
//Send MP4 header if needed
|
||||
leftOver = byteEnd - byteStart + 1; // add one byte, because range "0-0" = 1 byte of data
|
||||
byteEnd++;
|
||||
if (byteStart < headerSize){
|
||||
|
|
|
@ -100,7 +100,7 @@ namespace Mist{
|
|||
void findSeekPoint(uint64_t byteStart, uint64_t &seekPoint, uint64_t headerSize);
|
||||
void appendSinglePacketMoof(Util::ResizeablePointer& moofOut, size_t extraBytes = 0);
|
||||
size_t fragmentHeaderSize(std::deque<size_t>& sortedTracks, std::set<keyPart>& trunOrder, uint64_t startFragmentTime, uint64_t endFragmentTime);
|
||||
void onHTTP();
|
||||
void respondHTTP(const HTTP::Parser & req, bool headersOnly);
|
||||
void sendNext();
|
||||
void sendHeader();
|
||||
bool doesWebsockets() { return true; }
|
||||
|
|
Loading…
Add table
Reference in a new issue