From dcd66ce4ff689c52f6caf076cd3db8f6277e1b0e Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 27 Feb 2013 09:47:09 +0100 Subject: [PATCH] Updates to all connectors for live support --- src/buffer.cpp | 12 ++++- src/conn_http.cpp | 5 +- src/conn_http_dynamic.cpp | 100 ++++++++++++++++++++++---------------- src/conn_http_live.cpp | 59 +++++++++++++--------- src/conn_http_smooth.cpp | 13 +++-- 5 files changed, 118 insertions(+), 71 deletions(-) diff --git a/src/buffer.cpp b/src/buffer.cpp index 6d75a2d0..a008a91f 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -114,7 +114,14 @@ namespace Buffer { break; } case 'f': { //frame-seek - //ignored for now + fprintf( stderr, "Received a frame-seek\n" ); + unsigned int frameno = JSON::Value(usr->S.Received().get().substr(2)).asInt(); + usr->myRing->waiting = false; + usr->myRing->starved = false; + usr->myRing->b = thisStream->getStream()->frameSeek(frameno); + if (usr->myRing->playCount > 0 ) { + usr->myRing->playCount = 0; + } break; } case 'p': { //play @@ -122,6 +129,7 @@ namespace Buffer { break; } case 'o': { //once-play + fprintf( stderr, "Received a play-once\n" ); if (usr->myRing->playCount >= 0 ) { usr->myRing->playCount++; } @@ -156,7 +164,7 @@ namespace Buffer { while (std::cin.good() && buffer_running){ //slow down packet receiving to real-time now = getNowMS(); - if ((now - timeDiff >= lastPacket) || (lastPacket - (now - timeDiff) > 15000)){ + if (((now - timeDiff) >= lastPacket) || (lastPacket - (now - timeDiff) > 15000)){ thisStream->getWriteLock(); if (thisStream->getStream()->parsePacket(inBuffer)){ thisStream->getStream()->outPacket(0); diff --git a/src/conn_http.cpp b/src/conn_http.cpp index 3c7f9c76..c7d6b4f3 100644 --- a/src/conn_http.cpp +++ b/src/conn_http.cpp @@ -203,6 +203,7 @@ namespace Connector_HTTP { void Handle_Through_Connector(HTTP::Parser & H, Socket::Connection * conn, std::string & connector){ //create a unique ID based on a hash of the user agent and host, followed by the stream name and connector std::string uid = Secure::md5(H.GetHeader("User-Agent") + conn->getHost()) + "_" + H.GetVar("stream") + "_" + connector; + H.SetHeader("X-Stream", H.GetVar("stream")); H.SetHeader("X-UID", uid); //add the UID to the headers before copying H.SetHeader("X-Origin", conn->getHost()); //add the UID to the headers before copying std::string request = H.BuildRequest(); //copy the request for later forwarding to the connector @@ -321,8 +322,8 @@ namespace Connector_HTTP { /// - progressive (request fed from http_progressive connector) std::string getHTTPType(HTTP::Parser & H){ std::string url = H.getUrl(); - if ((url.find("f4m") != std::string::npos) || ((url.find("Seg") != std::string::npos) && (url.find("Frag") != std::string::npos))){ - std::string streamname = url.substr(1, url.find("/", 1) - 1); + if (url.find("/dynamic/") != std::string::npos){ + std::string streamname = url.substr(9, url.find("/", 9) - 9); Util::Stream::sanitizeName(streamname); H.SetVar("stream", streamname); return "dynamic"; diff --git a/src/conn_http_dynamic.cpp b/src/conn_http_dynamic.cpp index 7d3ea274..eb8cfd39 100644 --- a/src/conn_http_dynamic.cpp +++ b/src/conn_http_dynamic.cpp @@ -35,10 +35,10 @@ namespace Connector_HTTP { }else{ asrt.setUpdate(true); } - asrt.setVersion(1); - asrt.setQualityEntry(empty, 0); - if ( !metadata.isMember("keytime") || metadata["keytime"].size() == 0){ - asrt.setSegmentRun(1, 20000, 0); + asrt.setVersion(0);//1 + //asrt.setQualityEntry(empty, 0); + if (metadata.isMember("keynum")){ + asrt.setSegmentRun(1, -1, 0); }else{ asrt.setSegmentRun(1, metadata["keytime"].size(), 0); } @@ -49,19 +49,18 @@ namespace Connector_HTTP { }else{ afrt.setUpdate(true); } - afrt.setVersion(1); + afrt.setVersion(0);//1 afrt.setTimeScale(1000); - afrt.setQualityEntry(empty, 0); + //afrt.setQualityEntry(empty, 0); MP4::afrt_runtable afrtrun; - if ( !metadata.isMember("keytime") || metadata["keytime"].size() == 0){ - afrtrun.firstFragment = 1; - afrtrun.firstTimestamp = 0; - if ( !metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){ - afrtrun.duration = 2000; - }else{ - afrtrun.duration = metadata["video"]["keyms"].asInt(); + if (metadata.isMember("keynum")){ + unsigned long long int firstAvail = metadata["keynum"].size() / 2; + for (int i = firstAvail; i < metadata["keynum"].size() -2; i++ ) { + afrtrun.firstFragment = metadata["keynum"][i].asInt(); + afrtrun.firstTimestamp = metadata["keytime"][i].asInt(); + afrtrun.duration = metadata["keytime"][i+1].asInt() - metadata["keytime"][i].asInt(); + afrt.setFragmentRun(afrtrun, i - firstAvail); } - afrt.setFragmentRun(afrtrun, 0); }else{ for (int i = 0; i < metadata["keytime"].size(); i++){ afrtrun.firstFragment = i + 1; @@ -80,8 +79,12 @@ namespace Connector_HTTP { } MP4::ABST abst; - abst.setVersion(1); - abst.setBootstrapinfoVersion(1); + abst.setVersion(0); + if( metadata.isMember("keynum") ) { + abst.setBootstrapinfoVersion(metadata["keynum"][0u].asInt()); + }else{ + abst.setBootstrapinfoVersion(1); + } abst.setProfile(0); if (starttime == 0){ abst.setUpdate(false); @@ -98,14 +101,14 @@ namespace Connector_HTTP { } }else{ abst.setLive(true); - abst.setCurrentMediaTime(0xFFFFFFFF); + abst.setCurrentMediaTime(metadata["lastms"].asInt()); } abst.setSmpteTimeCodeOffset(0); abst.setMovieIdentifier(MovieId); - abst.setServerEntry(empty, 0); - abst.setQualityEntry(empty, 0); - abst.setDrmData(empty); - abst.setMetaData(empty); + //abst.setServerEntry(empty, 0); + //abst.setQualityEntry(empty, 0); + //abst.setDrmData(empty); + //abst.setMetaData(empty); abst.setSegmentRunTable(asrt, 0); abst.setFragmentRunTable(afrt, 0); @@ -118,7 +121,7 @@ namespace Connector_HTTP { /// Returns a F4M-format manifest file std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){ std::string Result; - if (metadata.isMember("length") && metadata["length"].asInt() > 0){ + if ( !metadata.isMember("keynum")){ Result = "\n" "\n" @@ -139,11 +142,14 @@ namespace Connector_HTTP { Result = "\n" "\n" "" + MovieId + "\n" + "0.00\n" "video/mp4\n" "live\n" "streaming\n" - "" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + "\n" - "\n" + "\n" + "" + "AgAKb25NZXRhRGF0YQgAAAAAAA9tZXRhZGF0YWNyZWF0b3ICABBBbmV2aWEgVmlhTW90aW9uAAhoYXNBdWRpbwEBAAhoYXNWaWRlbwEBAAhkdXJhdGlvbgBBIWWYAAAAAAAPYXVkaW9zYW1wbGVyYXRlAEBIAAAAAAAAAA1hdWRpb2RhdGFyYXRlAEBgAAAAAAAAAAxhdWRpb2NvZGVjaWQCAARtcDRhAAZhYWNhb3QAQAAAAAAAAAAABXdpZHRoAECQAAAAAAAAAAZoZWlnaHQAQIIAAAAAAAAADXZpZGVvZGF0YXJhdGUAQJ9AAAAAAAAADHZpZGVvY29kZWNpZAIABEFWQzEACmF2Y3Byb2ZpbGUAQFNAAAAAAAAACGF2Y2xldmVsAEA/AAAAAAAAAAAJ\n" + "\n" "\n"; } #if DEBUG >= 8 @@ -193,27 +199,38 @@ namespace Connector_HTTP { if (HTTP_R.Read(conn.Received().get())){ #if DEBUG >= 4 std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; + std::cout << "Received request: " << HTTP_R.BuildRequest() << std::endl; #endif conn.setHost(HTTP_R.GetHeader("X-Origin")); - if (HTTP_R.url.find("f4m") == std::string::npos){ - streamname = HTTP_R.url.substr(1, HTTP_R.url.find("/", 1) - 1); - if ( !ss){ - ss = Util::Stream::getStream(streamname); - if ( !ss.connected()){ + 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"); + fprintf(stderr, "Could not connect to server!\n"); #endif - ss.close(); - 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); - inited = true; + ss.close(); + 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; + HTTP_R.Clean(); //clean for any possible next requests + continue; } - Quality = HTTP_R.url.substr(HTTP_R.url.find("/", 1) + 1); + ss.setBlocking(false); + inited = true; + while ( !ss.spool()){} + Strm.parsePacket(ss.Received()); + } + if (HTTP_R.url.find(".bootstrap") != std::string::npos){ + HTTP_S.Clean(); + HTTP_S.SetBody(GenerateBootstrap(streamname, Strm.metadata, 1, 0, 0)); + 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){ + Quality = HTTP_R.url.substr(HTTP_R.url.find("/", 10) + 1); Quality = Quality.substr(0, Quality.find("Seg")); temp = HTTP_R.url.find("Seg") + 3; Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("-", temp) - temp).c_str()); @@ -227,7 +244,6 @@ namespace Connector_HTTP { ss.SendNow(sstream.str().c_str()); Flash_RequestPending++; }else{ - streamname = HTTP_R.url.substr(1, HTTP_R.url.find("/", 1) - 1); if ( !Strm.metadata.isNull()){ HTTP_S.Clean(); HTTP_S.SetHeader("Content-Type", "text/xml"); @@ -284,6 +300,7 @@ namespace Connector_HTTP { } if (ss.spool()){ while (Strm.parsePacket(ss.Received())){ + /* if (Strm.getPacket(0).isMember("time")){ if ( !Strm.metadata.isMember("firsttime")){ Strm.metadata["firsttime"] = Strm.getPacket(0)["time"]; @@ -294,6 +311,7 @@ namespace Connector_HTTP { } Strm.metadata["lasttime"] = Strm.getPacket(0)["time"]; } + */ if (pending_manifest){ HTTP_S.Clean(); HTTP_S.SetHeader("Content-Type", "text/xml"); diff --git a/src/conn_http_live.cpp b/src/conn_http_live.cpp index ecbad952..7ff89940 100644 --- a/src/conn_http_live.cpp +++ b/src/conn_http_live.cpp @@ -31,12 +31,18 @@ namespace Connector_HTTP { if (metadata.isNull()){ return result; } - result.push_back(0); - int currentBase = metadata["keytime"][0u].asInt(); - for (int i = 0; i < metadata["keytime"].size(); i++){ - if ((metadata["keytime"][i].asInt() - currentBase) > 10000){ - currentBase = metadata["keytime"][i].asInt(); - result.push_back(i); + if( metadata.isMember( "keynum" ) ) { + for (int i = 0; i < metadata["keynum"].size(); i++){ + result.push_back(metadata["keynum"][i].asInt()); + } + }else{ + result.push_back(0); + int currentBase = metadata["keytime"][0u].asInt(); + for (int i = 0; i < metadata["keytime"].size(); i++){ + if ((metadata["keytime"][i].asInt() - currentBase) > 10000){ + currentBase = metadata["keytime"][i].asInt(); + result.push_back(i); + } } } return result; @@ -61,7 +67,6 @@ namespace Connector_HTTP { "#EXT-X-MEDIA-SEQUENCE:0\r\n"; //"#EXT-X-PLAYLIST-TYPE:VOD\r\n"; int lastDuration = 0; - bool writeOffset = true; for (int i = 0; i < fragIndices.size() - 1; i++){ Result << "#EXTINF:" << (metadata["keytime"][fragIndices[i + 1]].asInt() - lastDuration) / 1000 << ", no desc\r\n" << fragIndices[i] + 1 << "_" << fragIndices[i + 1] - fragIndices[i] << ".ts\r\n"; @@ -72,6 +77,12 @@ namespace Connector_HTTP { Result << "#EXTM3U\r\n" "#EXT-X-MEDIA-SEQUENCE:0\r\n" "#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n"; + int lastDuration = 0; + for (int i = 0; i < fragIndices.size() - 1; i++){ + Result << "#EXTINF:" << (metadata["keytime"][fragIndices[i + 1]].asInt() - lastDuration) / 1000 << ", no desc\r\n" << fragIndices[i] + 1 + << "_" << fragIndices[i + 1] - fragIndices[i] << ".ts\r\n"; + lastDuration = metadata["keytime"][fragIndices[i + 1]].asInt(); + } } #if DEBUG >= 8 std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl; @@ -133,23 +144,25 @@ namespace Connector_HTTP { std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; #endif conn.setHost(HTTP_R.GetHeader("X-Origin")); - if (HTTP_R.url.find(".m3u") == std::string::npos){ - streamname = HTTP_R.url.substr(5, HTTP_R.url.find("/", 5) - 5); - if ( !ss){ - ss = Util::Stream::getStream(streamname); - if ( !ss.connected()){ + 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"); + 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); - inited = true; + 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); + inited = true; + while ( !ss.spool()){} + Strm.parsePacket(ss.Received()); + } + if (HTTP_R.url.find(".m3u") == std::string::npos){ temp = HTTP_R.url.find("/", 5) + 1; Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("_", temp) - temp).c_str()); temp = HTTP_R.url.find("_", temp) + 1; @@ -163,7 +176,9 @@ namespace Connector_HTTP { ss.SendNow(sstream.str().c_str()); Flash_RequestPending++; }else{ - streamname = HTTP_R.url.substr(5, HTTP_R.url.find("/", 5) - 5); + if ( ss.spool()){ + Strm.parsePacket(ss.Received()); + } if (HTTP_R.url.find(".m3u8") != std::string::npos){ manifestType = "audio/x-mpegurl"; }else{ diff --git a/src/conn_http_smooth.cpp b/src/conn_http_smooth.cpp index 5a674809..8fbbe171 100644 --- a/src/conn_http_smooth.cpp +++ b/src/conn_http_smooth.cpp @@ -29,8 +29,13 @@ namespace Connector_HTTP { std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){ std::stringstream Result; Result << "\n"; - Result << "\n"; + Result << " 0){ + Result << "Duration=\"" << metadata["lastms"].asInt() << "\""; + } else { + Result << "Duration=\"0\" IsLive=\"TRUE\" LookAheadFragmentCount=\"2\" "; + } + Result << ">\n"; if (metadata.isMember("audio")){ Result << " \n"; @@ -45,7 +50,7 @@ namespace Connector_HTTP { for (int i = 0; i < metadata["keytime"].size() - 1; i++){ Result << " \n"; } @@ -71,7 +76,7 @@ namespace Connector_HTTP { for (int i = 0; i < metadata["keytime"].size() - 1; i++){ Result << " \n"; }