diff --git a/lib/stream.cpp b/lib/stream.cpp index 76cd64a4..821734b2 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -20,6 +20,8 @@ #include #include +enum Util::trackSortOrder Util::defaultTrackSortOrder = TRKSORT_DEFAULT; + /// Calls strftime using the current local time, returning empty string on any error. static std::string strftime_now(const std::string &format){ time_t rawtime; @@ -1222,6 +1224,10 @@ std::set Util::wouldSelect(const DTSC::Meta &M, const std::map srtTrks; + Util::sortTracks(validTracks, M, Util::defaultTrackSortOrder, srtTrks); + // try to fill as many codecs simultaneously as possible if (capa["codecs"][bestSoFar].size() > 0){ jsonForEachConst(capa["codecs"][bestSoFar], itb){ @@ -1274,66 +1280,35 @@ std::set Util::wouldSelect(const DTSC::Meta &M, const std::map::reverse_iterator trit = validTracks.rbegin(); - trit != validTracks.rend(); trit++){ - if ((!byType && M.getCodec(*trit) == strRef.substr(shift)) || - (byType && M.getType(*trit) == strRef.substr(shift)) || strRef.substr(shift) == "*"){ - // user-agent-check - bool problems = false; - if (capa.isMember("exceptions") && capa["exceptions"].isObject() && - capa["exceptions"].size()){ - jsonForEachConst(capa["exceptions"], ex){ - if (ex.key() == "codec:" + strRef.substr(shift)){ - problems = !Util::checkException(*ex, UA); - break; - } + + for (std::list::iterator trit = srtTrks.begin(); + trit != srtTrks.end(); trit++){ + if ((!byType && M.getCodec(*trit) == strRef.substr(shift)) || + (byType && M.getType(*trit) == strRef.substr(shift)) || strRef.substr(shift) == "*"){ + // user-agent-check + bool problems = false; + if (capa.isMember("exceptions") && capa["exceptions"].isObject() && + capa["exceptions"].size()){ + jsonForEachConst(capa["exceptions"], ex){ + if (ex.key() == "codec:" + strRef.substr(shift)){ + problems = !Util::checkException(*ex, UA); + break; } } - if (!allowBFrames && M.hasBFrames(*trit)){problems = true;} - if (problems){break;} - /*LTS-START*/ - if (noSelAudio && M.getType(*trit) == "audio"){continue;} - if (noSelVideo && M.getType(*trit) == "video"){continue;} - if (noSelSub && - (M.getType(*trit) == "subtitle" || M.getCodec(*trit) == "subtitle")){ - continue; - } - /*LTS-END*/ - result.insert(*trit); - found = true; - if (!multiSel){break;} } - } - }else{ - for (std::set::iterator trit = validTracks.begin(); trit != validTracks.end(); trit++){ - if ((!byType && M.getCodec(*trit) == strRef.substr(shift)) || - (byType && M.getType(*trit) == strRef.substr(shift)) || strRef.substr(shift) == "*"){ - // user-agent-check - bool problems = false; - if (capa.isMember("exceptions") && capa["exceptions"].isObject() && - capa["exceptions"].size()){ - jsonForEachConst(capa["exceptions"], ex){ - if (ex.key() == "codec:" + strRef.substr(shift)){ - problems = !Util::checkException(*ex, UA); - break; - } - } - } - if (!allowBFrames && M.hasBFrames(*trit)){problems = true;} - if (problems){break;} - /*LTS-START*/ - if (noSelAudio && M.getType(*trit) == "audio"){continue;} - if (noSelVideo && M.getType(*trit) == "video"){continue;} - if (noSelSub && - (M.getType(*trit) == "subtitle" || M.getCodec(*trit) == "subtitle")){ - continue; - } - /*LTS-END*/ - result.insert(*trit); - found = true; - if (!multiSel){break;} + if (!allowBFrames && M.hasBFrames(*trit)){problems = true;} + if (problems){break;} + /*LTS-START*/ + if (noSelAudio && M.getType(*trit) == "audio"){continue;} + if (noSelVideo && M.getType(*trit) == "video"){continue;} + if (noSelSub && + (M.getType(*trit) == "subtitle" || M.getCodec(*trit) == "subtitle")){ + continue; } + /*LTS-END*/ + result.insert(*trit); + found = true; + if (!multiSel){break;} } } } @@ -1357,3 +1332,65 @@ std::set Util::wouldSelect(const DTSC::Meta &M, const std::map & validTracks, const DTSC::Meta & M, Util::trackSortOrder sorting, std::list & srtTrks){ + srtTrks.clear(); + if (sorting == TRKSORT_DEFAULT){ + if (M.getLive()){ + sorting = TRKSORT_ID_HTL; + }else{ + sorting = TRKSORT_ID_LTH; + } + } + if (!validTracks.size()){return;} + for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); ++it){ + //The first element is always at the beginning of the list. Yeah. That makes sense. + if (!srtTrks.size()){ + srtTrks.push_front(*it); + continue; + } + if (sorting == TRKSORT_ID_LTH){ + //ID low to high needs no comparison, already sorted + srtTrks.push_back(*it); + continue; + }else if (sorting == TRKSORT_ID_HTL){ + //ID high to low needs no comparison either, already sorted in reverse + srtTrks.push_front(*it); + continue; + } + bool inserted = false; + for (std::list::iterator lt = srtTrks.begin(); lt != srtTrks.end(); ++lt){ + if (sorting == TRKSORT_BPS_LTH){ + if (M.getBps(*it) <= M.getBps(*lt)){ + srtTrks.insert(lt, *it); + inserted = true; + break; + } + }else if (sorting == TRKSORT_BPS_HTL){ + if (M.getBps(*it) >= M.getBps(*lt)){ + srtTrks.insert(lt, *it); + inserted = true; + break; + } + }else if (sorting == TRKSORT_RES_LTH){ + if (M.getWidth(*it) * M.getHeight(*it) < M.getWidth(*lt) * M.getHeight(*lt) || M.getRate(*it) < M.getRate(*lt)){ + srtTrks.insert(lt, *it); + inserted = true; + break; + } + }else if (sorting == TRKSORT_RES_HTL){ + if (M.getWidth(*it) * M.getHeight(*it) > M.getWidth(*lt) * M.getHeight(*lt) || M.getRate(*it) > M.getRate(*lt)){ + srtTrks.insert(lt, *it); + inserted = true; + break; + } + } + } + //Insert at end of list if not inserted yet + if (!inserted){srtTrks.push_back(*it);} + } + +} + diff --git a/lib/stream.h b/lib/stream.h index 3ae17f5f..15b3cadb 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -8,6 +8,7 @@ #include "socket.h" #include "util.h" #include +#include const JSON::Value empty; @@ -37,6 +38,19 @@ namespace Util{ std::set wouldSelect(const DTSC::Meta &M, const std::map &targetParams, const JSON::Value &capa = empty, const std::string &UA = "", uint64_t seekTarget = 0); + enum trackSortOrder{ + TRKSORT_DEFAULT = 0, + TRKSORT_BPS_LTH, + TRKSORT_BPS_HTL, + TRKSORT_ID_LTH, + TRKSORT_ID_HTL, + TRKSORT_RES_LTH, + TRKSORT_RES_HTL + }; + extern trackSortOrder defaultTrackSortOrder; + void sortTracks(std::set & validTracks, const DTSC::Meta & M, trackSortOrder sorting, std::list & srtTrks); + + class DTSCShmReader{ public: DTSCShmReader(const std::string &pageName); diff --git a/src/output/mist_out.cpp b/src/output/mist_out.cpp index ae3dfd98..9f3a3302 100644 --- a/src/output/mist_out.cpp +++ b/src/output/mist_out.cpp @@ -3,6 +3,7 @@ #include #include #include +#include int spawnForked(Socket::Connection &S){ mistOut tmp(S); @@ -27,6 +28,20 @@ int main(int argc, char *argv[]){ std::cout << mistOut::capa.toString() << std::endl; return -1; } + { + std::string defTrkSrt = conf.getString("default_track_sorting"); + if (!defTrkSrt.size()){ + //defTrkSrt = Util::getGlobalConfig("default_track_sorting").asString(); + } + if (defTrkSrt.size()){ + if (defTrkSrt == "bps_lth"){Util::defaultTrackSortOrder = Util::TRKSORT_BPS_LTH;} + if (defTrkSrt == "bps_htl"){Util::defaultTrackSortOrder = Util::TRKSORT_BPS_HTL;} + if (defTrkSrt == "id_lth"){Util::defaultTrackSortOrder = Util::TRKSORT_ID_LTH;} + if (defTrkSrt == "id_htl"){Util::defaultTrackSortOrder = Util::TRKSORT_ID_HTL;} + if (defTrkSrt == "res_lth"){Util::defaultTrackSortOrder = Util::TRKSORT_RES_LTH;} + if (defTrkSrt == "res_htl"){Util::defaultTrackSortOrder = Util::TRKSORT_RES_HTL;} + } + } conf.activate(); if (mistOut::listenMode()){ { diff --git a/src/output/output.cpp b/src/output/output.cpp index 7bbb010b..148b77b5 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -45,6 +45,43 @@ namespace Mist{ option["help"] = "Do not start input if not already started"; option["value"].append(0); cfg->addOption("noinput", option); + option.null(); + + + capa["optional"]["default_track_sorting"]["name"] = "Default track sorting"; + capa["optional"]["default_track_sorting"]["help"] = "What tracks are selected first when no specific track selector is used for playback."; + capa["optional"]["default_track_sorting"]["default"] = ""; + capa["optional"]["default_track_sorting"]["type"] = "select"; + capa["optional"]["default_track_sorting"]["option"] = "--default_track_sorting"; + capa["optional"]["default_track_sorting"]["short"] = "S"; + option.append(""); + option.append("Default (last added for live, first added for VoD)"); + capa["optional"]["default_track_sorting"]["select"].append(option); + option.null(); + option.append("bps_lth"); + option.append("Bit rate, low to high"); + capa["optional"]["default_track_sorting"]["select"].append(option); + option.null(); + option.append("bps_htl"); + option.append("Bit rate, high to low"); + capa["optional"]["default_track_sorting"]["select"].append(option); + option.null(); + option.append("id_lth"); + option.append("Track ID, low to high"); + capa["optional"]["default_track_sorting"]["select"].append(option); + option.null(); + option.append("id_htl"); + option.append("Track ID, high to low"); + capa["optional"]["default_track_sorting"]["select"].append(option); + option.null(); + option.append("res_lth"); + option.append("Resolution, low to high"); + capa["optional"]["default_track_sorting"]["select"].append(option); + option.null(); + option.append("res_htl"); + option.append("Resolution, high to low"); + capa["optional"]["default_track_sorting"]["select"].append(option); + config = cfg; }