From d457046420c718ede8ad7a52f1908e5520a2b382 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Oct 2014 00:38:25 +0100 Subject: [PATCH] Converted HTTP based outputs to new and improved mechanism, increasing robustness and efficiency. --- Makefile | 66 +- src/connectors/conn_http.cpp | 744 ----------------------- src/controller/controller_connectors.cpp | 2 +- src/{connectors => }/embed.js | 0 src/{connectors => }/icon.h | 0 src/output/mist_out.cpp | 8 +- src/output/mist_out_http.cpp | 32 - src/output/output.cpp | 26 +- src/output/output.h | 6 +- src/output/output_hds.cpp | 199 +++--- src/output/output_hds.h | 11 +- src/output/output_hls.cpp | 189 +++--- src/output/output_hls.h | 10 +- src/output/output_hss.cpp | 119 ++-- src/output/output_hss.h | 10 +- src/output/output_http.cpp | 295 +++++++++ src/output/output_http.h | 22 + src/output/output_http_internal.cpp | 356 +++++++++++ src/output/output_http_internal.h | 15 + src/output/output_httpts.cpp | 46 +- src/output/output_httpts.h | 9 +- src/output/output_json.cpp | 59 +- src/output/output_json.h | 6 +- src/output/output_progressive_flv.cpp | 72 +-- src/output/output_progressive_flv.h | 10 +- src/output/output_progressive_mp3.cpp | 50 +- src/output/output_progressive_mp3.h | 8 +- src/output/output_progressive_mp4.cpp | 182 +++--- src/output/output_progressive_mp4.h | 11 +- src/output/output_srt.cpp | 53 +- src/output/output_srt.h | 7 +- src/{connectors => }/xap.h | 0 32 files changed, 1113 insertions(+), 1510 deletions(-) delete mode 100644 src/connectors/conn_http.cpp rename src/{connectors => }/embed.js (100%) rename src/{connectors => }/icon.h (100%) delete mode 100644 src/output/mist_out_http.cpp create mode 100644 src/output/output_http.cpp create mode 100644 src/output/output_http.h create mode 100644 src/output/output_http_internal.cpp create mode 100644 src/output/output_http_internal.h rename src/{connectors => }/xap.h (100%) diff --git a/Makefile b/Makefile index 1df5176e..a17d4133 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ LDLIBS = -lmist -lrt .DEFAULT_GOAL := all -all: MistConnHTTP controller analysers inputs outputs +all: controller analysers inputs outputs DOXYGEN := $(shell doxygen -v 2> /dev/null) ifdef DOXYGEN @@ -37,11 +37,6 @@ MistController: override LDLIBS += $(THREADLIB) MistController: src/controller/server.html.h src/controller/* $(CXX) $(LDFLAGS) $(CPPFLAGS) src/controller/*.cpp $(LDLIBS) -o $@ -connectors: MistConnHTTP -MistConnHTTP: override LDLIBS += $(THREADLIB) -MistConnHTTP: src/connectors/conn_http.cpp src/connectors/embed.js.h src/connectors/icon.h - $(CXX) $(LDFLAGS) $(CPPFLAGS) $< $(LDLIBS) -o $@ - analysers: MistAnalyserRTMP MistAnalyserRTMP: src/analysers/rtmp_analyser.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ @@ -70,34 +65,6 @@ analysers: MistInfo MistInfo: src/analysers/info.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ -converters: MistDTSC2FLV -MistDTSC2FLV: src/converters/dtsc2flv.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -converters: MistFLV2DTSC -MistFLV2DTSC: src/converters/flv2dtsc.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -converters: MistDTSCFix -MistDTSCFix: src/converters/dtscfix.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -converters: MistDTSCMerge -MistDTSCMerge: src/converters/dtscmerge.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -converters: MistDTSC2TS -MistDTSC2TS: src/converters/dtsc2ts.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -converters: MistSRT2DTSC -MistSRT2DTSC: src/converters/srt2dtsc.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -converters: MistDTSC2SRT -MistDTSC2SRT: src/converters/dtsc2srt.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - inputs: MistInDTSC MistInDTSC: override LDLIBS += $(THREADLIB) MistInDTSC: override CPPFLAGS += "-DINPUTTYPE=\"input_dtsc.h\"" @@ -125,19 +92,19 @@ MistInBuffer: src/input/mist_in.cpp src/input/input.cpp src/input/input_buffer.c outputs: MistOutFLV MistOutFLV: override LDLIBS += $(THREADLIB) MistOutFLV: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_flv.h\"" -MistOutFLV: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_progressive_flv.cpp +MistOutFLV: src/output/mist_out.cpp src/output/output_http.cpp src/output/output.cpp src/output/output_progressive_flv.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ outputs: MistOutMP4 MistOutMP4: override LDLIBS += $(THREADLIB) MistOutMP4: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_mp4.h\"" -MistOutMP4: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_progressive_mp4.cpp +MistOutMP4: src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_progressive_mp4.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ outputs: MistOutMP3 MistOutMP3: override LDLIBS += $(THREADLIB) MistOutMP3: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_mp3.h\"" -MistOutMP3: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_progressive_mp3.cpp +MistOutMP3: src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_progressive_mp3.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ outputs: MistOutRTMP @@ -155,7 +122,7 @@ MistOutRaw: src/output/mist_out.cpp src/output/output.cpp src/output/output_raw. outputs: MistOutHTTPTS MistOutHTTPTS: override LDLIBS += $(THREADLIB) MistOutHTTPTS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_httpts.h\"" -MistOutHTTPTS: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_httpts.cpp +MistOutHTTPTS: src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_httpts.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ outputs: MistOutTS @@ -164,37 +131,42 @@ MistOutTS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_ts.h\"" MistOutTS: src/output/mist_out.cpp src/output/output.cpp src/output/output_ts.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ +outputs: MistOutHTTP +MistOutHTTP: override LDLIBS += $(THREADLIB) +MistOutHTTP: override CPPFLAGS += "-DOUTPUTTYPE=\"output_http_internal.h\"" +MistOutHTTP: src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_http_internal.cpp src/embed.js.h + $(CXX) $(LDFLAGS) $(CPPFLAGS) src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_http_internal.cpp $(LDLIBS) -o $@ + outputs: MistOutHSS MistOutHSS: override LDLIBS += $(THREADLIB) MistOutHSS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_hss.h\"" -MistOutHSS: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_hss.cpp +MistOutHSS: src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_hss.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ outputs: MistOutHLS MistOutHLS: override LDLIBS += $(THREADLIB) MistOutHLS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_hls.h\"" -MistOutHLS: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_hls.cpp +MistOutHLS: src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_hls.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ outputs: MistOutHDS MistOutHDS: override LDLIBS += $(THREADLIB) MistOutHDS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_hds.h\"" -MistOutHDS: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_hds.cpp +MistOutHDS: src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_hds.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ outputs: MistOutSRT MistOutSRT: override LDLIBS += $(THREADLIB) MistOutSRT: override CPPFLAGS += "-DOUTPUTTYPE=\"output_srt.h\"" -MistOutSRT: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_srt.cpp +MistOutSRT: src/output/mist_out.cpp src/output/output_http.cpp src/output/output.cpp src/output/output_srt.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ outputs: MistOutJSON MistOutJSON: override LDLIBS += $(THREADLIB) MistOutJSON: override CPPFLAGS += "-DOUTPUTTYPE=\"output_json.h\"" -MistOutJSON: src/output/mist_out_http.cpp src/output/output.cpp src/output/output_json.cpp +MistOutJSON: src/output/mist_out.cpp src/output/output.cpp src/output/output_http.cpp src/output/output_json.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ -BUILT_SOURCES=controller/server.html.h connectors/embed.js.h lspSOURCES=lsp/plugins/jquery.js lsp/plugins/placeholder.js lsp/plugins/md5.js lsp/main.js lsp/pages.js lsp/plugins/tablesort.js lsp/plugins/jquery.flot.min.js lsp/plugins/jquery.flot.time.min.js lsp/plugins/jquery.flot.crosshair.min.js lspDATA=lsp/header.html lsp/main.css lsp/footer.html @@ -209,9 +181,9 @@ endif sourcery: src/sourcery.cpp $(CXX) -o $@ $(CPPFLAGS) $^ -src/connectors/embed.js.h: src/connectors/embed.js sourcery - $(CLOSURE) src/connectors/embed.js > embed.min.js - ./sourcery embed.min.js embed_js > src/connectors/embed.js.h +src/embed.js.h: src/embed.js sourcery + $(CLOSURE) src/embed.js > embed.min.js + ./sourcery embed.min.js embed_js > src/embed.js.h rm embed.min.js src/controller/server.html: $(lspDATA) $(lspSOURCES) diff --git a/src/connectors/conn_http.cpp b/src/connectors/conn_http.cpp deleted file mode 100644 index bba983d8..00000000 --- a/src/connectors/conn_http.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/// \file conn_http.cpp -/// Contains the main code for the HTTP Connector - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include // -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "embed.js.h" - - -/// Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - - - - - static inline void builPipedPart(JSON::Value & p, char * argarr[], int & argnum, JSON::Value & argset){ - for (JSON::ObjIter it = argset.ObjBegin(); it != argset.ObjEnd(); ++it){ - if (it->second.isMember("option") && p.isMember(it->first)){ - if (it->second.isMember("type")){ - if (it->second["type"].asStringRef() == "str" && !p[it->first].isString()){ - p[it->first] = p[it->first].asString(); - } - if ((it->second["type"].asStringRef() == "uint" || it->second["type"].asStringRef() == "int") && !p[it->first].isInt()){ - p[it->first] = JSON::Value(p[it->first].asInt()).asString(); - } - } - if (p[it->first].asStringRef().size() > 0){ - argarr[argnum++] = (char*)(it->second["option"].c_str()); - argarr[argnum++] = (char*)(p[it->first].c_str()); - } - } - } - } - - /// Class for keeping track of connections to connectors. - class ConnConn{ - public: - Socket::Connection * conn; ///< The socket of this connection - unsigned int lastUse; ///< Seconds since last use of this connection. - tthread::mutex inUse; ///< Mutex for this connection. - /// Constructor that sets the socket and lastUse to 0. - ConnConn(){ - conn = 0; - lastUse = 0; - } - /// Constructor that sets lastUse to 0, but socket to s. - ConnConn(Socket::Connection * s){ - conn = s; - lastUse = 0; - } - /// Destructor that deletes the socket if non-null. - ~ConnConn(){ - if (conn){ - conn->close(); - delete conn; - } - conn = 0; - } - }; - - std::map connectorConnections; ///< Connections to connectors - tthread::mutex connMutex; ///< Mutex for adding/removing connector connections. - bool timeoutThreadStarted = false; - tthread::mutex timeoutStartMutex; ///< Mutex for starting timeout thread. - tthread::mutex timeoutMutex; ///< Mutex for timeout thread. - tthread::thread * timeouter = 0; ///< Thread that times out connections to connectors. - IPC::sharedPage serverCfg; ///< Contains server configuration and capabilities - - ///\brief Function run as a thread to timeout requests on the proxy. - ///\param n A NULL-pointer - void proxyTimeoutThread(void * n){ - n = 0; //prevent unused variable warning - tthread::lock_guard guard(timeoutMutex); - timeoutThreadStarted = true; - while (true){ - { - tthread::lock_guard guard(connMutex); - if (connectorConnections.empty()){ - return; - } - std::map::iterator it; - for (it = connectorConnections.begin(); it != connectorConnections.end(); it++){ - ConnConn* ccPointer = it->second; - if ( !ccPointer->conn->connected() || ccPointer->lastUse++ > 15){ - if (ccPointer->inUse.try_lock()){ - connectorConnections.erase(it); - ccPointer->inUse.unlock(); - delete ccPointer; - it = connectorConnections.begin(); //get a valid iterator - if (it == connectorConnections.end()){ - return; - } - } - } - } - } - usleep(1000000); //sleep 1 second and re-check - } - } - - ///\brief Handles requests without associated handler. - /// - ///Displays a friendly error message. - ///\param H The request to be handled. - ///\param conn The connection to the client that issued the request. - ///\return A timestamp indicating when the request was parsed. - long long int proxyHandleUnsupported(HTTP::Parser & H, Socket::Connection & conn){ - H.Clean(); - H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody( - "Unsupported Media Type

Unsupported Media Type

The server isn't quite sure what you wanted to receive from it."); - long long int ret = Util::getMS(); - conn.SendNow(H.BuildResponse("415", "Unsupported Media Type")); - return ret; - } - - ///\brief Handles requests that have timed out. - /// - ///Displays a friendly error message. - ///\param H The request that was being handled upon timeout. - ///\param conn The connection to the client that issued the request. - ///\param msg The message to print to the client. - ///\return A timestamp indicating when the request was parsed. - long long int proxyHandleTimeout(HTTP::Parser & H, Socket::Connection & conn, std::string msg){ - H.Clean(); - H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody( - ""+msg+"

"+msg+"

Though the server understood your request and attempted to handle it, somehow handling it took longer than it should. Your request has been cancelled - please try again later."); - long long int ret = Util::getMS(); - conn.SendNow(H.BuildResponse("504", msg)); - return ret; - } - - /// Sorts the JSON::Value objects that hold source information by preference. - struct sourceCompare { - bool operator() (const JSON::Value& lhs, const JSON::Value& rhs) const { - //first compare simultaneous tracks - if (lhs["simul_tracks"].asInt() > rhs["simul_tracks"].asInt()){ - //more tracks = higher priority = true. - return true; - } - if (lhs["simul_tracks"].asInt() < rhs["simul_tracks"].asInt()){ - //less tracks = lower priority = false - return false; - } - //same amount of tracks - compare "hardcoded" priorities - if (lhs["priority"].asInt() > rhs["priority"].asInt()){ - //higher priority = true. - return true; - } - if (lhs["priority"].asInt() < rhs["priority"].asInt()){ - //lower priority = false - return false; - } - //same priority - compare total matches - if (lhs["total_matches"].asInt() > rhs["total_matches"].asInt()){ - //more matches = higher priority = true. - return true; - } - if (lhs["total_matches"].asInt() < rhs["total_matches"].asInt()){ - //less matches = lower priority = false - return false; - } - //also same amount of matches? just compare the URL then. - return lhs["url"].asStringRef() < rhs["url"].asStringRef(); - } - }; - - void addSource(const std::string & rel, std::set & sources, std::string & host, const std::string & port, JSON::Value & conncapa, unsigned int most_simul, unsigned int total_matches){ - JSON::Value tmp; - tmp["type"] = conncapa["type"]; - tmp["relurl"] = rel; - tmp["priority"] = conncapa["priority"]; - tmp["simul_tracks"] = most_simul; - tmp["total_matches"] = total_matches; - tmp["url"] = conncapa["handler"].asStringRef() + "://" + host + ":" + port + rel; - sources.insert(tmp); - } - - void addSources(std::string & streamname, const std::string & rel, std::set & sources, std::string & host, const std::string & port, JSON::Value & conncapa, JSON::Value & strmMeta){ - unsigned int most_simul = 0; - unsigned int total_matches = 0; - if (conncapa.isMember("codecs") && conncapa["codecs"].size() > 0){ - for (JSON::ArrIter it = conncapa["codecs"].ArrBegin(); it != conncapa["codecs"].ArrEnd(); it++){ - unsigned int simul = 0; - if ((*it).size() > 0){ - for (JSON::ArrIter itb = (*it).ArrBegin(); itb != (*it).ArrEnd(); itb++){ - unsigned int matches = 0; - if ((*itb).size() > 0){ - for (JSON::ArrIter itc = (*itb).ArrBegin(); itc != (*itb).ArrEnd(); itc++){ - for (JSON::ObjIter trit = strmMeta["tracks"].ObjBegin(); trit != strmMeta["tracks"].ObjEnd(); trit++){ - if (trit->second["codec"].asStringRef() == (*itc).asStringRef()){ - matches++; - total_matches++; - } - } - } - } - if (matches){ - simul++; - } - } - } - if (simul > most_simul){ - most_simul = simul; - } - } - } - if (conncapa.isMember("methods") && conncapa["methods"].size() > 0){ - std::string relurl; - size_t found = rel.find('$'); - if (found != std::string::npos){ - relurl = rel.substr(0, found) + streamname + rel.substr(found+1); - }else{ - relurl = "/"; - } - for (JSON::ArrIter it = conncapa["methods"].ArrBegin(); it != conncapa["methods"].ArrEnd(); it++){ - if (!strmMeta.isMember("live") || !it->isMember("nolive")){ - addSource(relurl, sources, host, port, *it, most_simul, total_matches); - } - } - } - } - - - ///\brief Handles requests within the proxy. - /// - ///Currently supported urls: - /// - /crossdomain.xml - /// - /clientaccesspolicy.xml - /// - *.ico (for favicon) - /// - /info_[streamname].js (stream info) - /// - /embed_[streamname].js (embed info) - /// - ///Unsupported urls default to proxyHandleUnsupported( ). - ///\param H The request to be handled. - ///\param conn The connection to the client that issued the request. - ///\return A timestamp indicating when the request was parsed. - long long int proxyHandleInternal(HTTP::Parser & H, Socket::Connection & conn){ - std::string url = H.getUrl(); - - if (url == "/crossdomain.xml"){ - H.Clean(); - H.SetHeader("Content-Type", "text/xml"); - H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody( - ""); - long long int ret = Util::getMS(); - conn.SendNow(H.BuildResponse("200", "OK")); - return ret; - } //crossdomain.xml - - if (url == "/clientaccesspolicy.xml"){ - H.Clean(); - H.SetHeader("Content-Type", "text/xml"); - H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody( - ""); - long long int ret = Util::getMS(); - conn.SendNow(H.BuildResponse("200", "OK")); - return ret; - } //clientaccesspolicy.xml - - // send logo icon - if (url.length() > 4 && url.substr(url.length() - 4, 4) == ".ico"){ - H.Clean(); -#include "icon.h" - H.SetHeader("Content-Type", "image/x-icon"); - H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetHeader("Content-Length", icon_len); - long long int ret = Util::getMS(); - H.SendResponse("200", "OK", conn); - conn.SendNow((const char*)icon_data, icon_len); - return ret; - } - - // send logo icon - if (url.length() > 6 && url.substr(url.length() - 5, 5) == ".html"){ - std::string streamname = url.substr(1, url.length() - 6); - Util::sanitizeName(streamname); - H.Clean(); - H.SetHeader("Content-Type", "text/html"); - H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody("Stream "+streamname+""); - long long int ret = Util::getMS(); - H.SendResponse("200", "OK", conn); - return ret; - } - - // send smil MBR index - if (url.length() > 6 && url.substr(url.length() - 5, 5) == ".smil"){ - std::string streamname = url.substr(1, url.length() - 6); - Util::sanitizeName(streamname); - - std::string host = H.GetHeader("Host"); - if (host.find(':')){ - host.resize(host.find(':')); - } - - std::string port, url_rel; - - IPC::semaphore configLock("!mistConfLock", O_CREAT | O_RDWR, ACCESSPERMS, 1); - configLock.wait(); - DTSC::Scan prtcls = DTSC::Scan(serverCfg.mapped, serverCfg.len).getMember("config").getMember("protocols"); - DTSC::Scan capa = DTSC::Scan(serverCfg.mapped, serverCfg.len).getMember("capabilities").getMember("connectors").getMember("RTMP"); - unsigned int pro_cnt = prtcls.getSize(); - for (unsigned int i = 0; i < pro_cnt; ++i){ - if (prtcls.getIndice(i).getMember("connector").asString() != "RTMP"){ - continue; - } - port = prtcls.getIndice(i).getMember("port").asString(); - //get the default port if none is set - if (!port.size()){ - port = capa.getMember("optional").getMember("port").getMember("default").asString(); - } - //extract url - url_rel = capa.getMember("url_rel").asString(); - if (url_rel.find('$')){ - url_rel.resize(url_rel.find('$')); - } - } - - std::string trackSources;//this string contains all track sources for MBR smil - DTSC::Scan tracks = DTSC::Scan(serverCfg.mapped, serverCfg.len).getMember("streams").getMember(streamname).getMember("meta").getMember("tracks"); - unsigned int track_ctr = tracks.getSize(); - for (unsigned int i = 0; i < track_ctr; ++i){//for all video tracks - DTSC::Scan trk = tracks.getIndice(i); - if (trk.getMember("type").asString() == "video"){ - trackSources += "