From 76db36dfed40834c93be411643d763d645855d85 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 22 Sep 2021 14:30:05 +0200 Subject: [PATCH] Added v2 track selectors --- lib/stream.cpp | 127 +++++++++++++++++----------- lib/stream.h | 1 + src/output/output_http_internal.cpp | 2 + 3 files changed, 79 insertions(+), 51 deletions(-) diff --git a/lib/stream.cpp b/lib/stream.cpp index 4c3ef432..76cd64a4 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -688,26 +688,39 @@ DTSC::Scan Util::DTSCShmReader::getScan(){ return DTSC::Scan(rAcc.getPointer("dtsc_data"), rAcc.getSize("dtsc_data")); } -/*LTS-START*/ -/// Selects a specific track or set of tracks of the given trackType, using trackVal to decide. -/// trackVal may be a comma-separated list of numbers, codecs or the word "all" or an asterisk. -/// Does not do any checks if the protocol supports these tracks, just selects blindly. -/// It is necessary to follow up with a selectDefaultTracks() call to strip unsupported -/// codecs/combinations. -std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, const std::string &trackType, const std::string &trackVal, const std::string &UA){ +/// Takes an existing track list, and selects tracks from it according to the given track type and selector +std::set Util::pickTracks(const DTSC::Meta &M, const std::set trackList, const std::string &trackType, const std::string &trackVal){ std::set result; - if (!trackVal.size()){return result;} + if (!trackVal.size()){return result;}//empty selector if (trackVal == "-1" || trackVal == "none"){return result;}// don't select anything in particular + + // Comma-separated list, loop over each in order. if (trackVal.find(',') != std::string::npos){ - // Comma-separated list, recurse. std::stringstream ss(trackVal); std::string item; while (std::getline(ss, item, ',')){ - std::set items = findTracks(M, capa, trackType, item); - result.insert(items.begin(), items.end()); + switch (item[0]){ + case '|':{//Narrow existing selection + result = pickTracks(M, result, trackType, item.substr(1)); + } break; + case '!':{//Remove from existing selection + std::set items = pickTracks(M, result, trackType, item.substr(1)); + if (items.size()){ + for (std::set::const_iterator it = items.begin(); it != items.end(); ++it){ + result.erase(*it); + } + } + } break; + default:{ //Union with existing selection + std::set items = pickTracks(M, trackList, trackType, item); + result.insert(items.begin(), items.end()); + } + } } return result; } + + //Literal track ID, does not check against trackList size_t idx = JSON::Value(trackVal).asInt(); if (trackVal == JSON::Value(idx).asString()){ if (!M.trackValid(idx)){ @@ -722,22 +735,25 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, result.insert(idx); return result; } + + //Convert selector to lower case std::string trackLow = trackVal; Util::stringToLower(trackLow); + + //Select all tracks in trackList of the given type if (trackLow == "all" || trackLow == "*"){ // select all tracks of this type - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){result.insert(*it);} } return result; } + + //Select highest bit rate in trackList of given type if (trackLow == "highbps" || trackLow == "bestbps" || trackLow == "maxbps"){ - // select highest bit rate track of this type - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); size_t currVal = INVALID_TRACK_ID; uint32_t currRate = 0; - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ if (currRate < M.getBps(*it)){ currVal = *it; @@ -748,12 +764,12 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, if (currVal != INVALID_TRACK_ID){result.insert(currVal);} return result; } + + //Select lowest bit rate in trackList of this type if (trackLow == "lowbps" || trackLow == "worstbps" || trackLow == "minbps"){ - // select lowest bit rate track of this type - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); size_t currVal = INVALID_TRACK_ID; - uint32_t currRate = 0xFFFFFFFFul; - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + uint32_t currRate = INVALID_TRACK_ID; + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ if (currRate > M.getBps(*it)){ currVal = *it; @@ -764,6 +780,7 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, if (currVal != INVALID_TRACK_ID){result.insert(currVal);} return result; } + //less-than or greater-than track matching on bit rate or resolution if (trackLow[0] == '<' || trackLow[0] == '>'){ unsigned int bpsVal; @@ -777,8 +794,7 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, if (targetBps){ targetBps /= 8; // select all tracks of this type that match the requirements - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ if (trackLow[0] == '>' && M.getBps(*it) > targetBps){result.insert(*it);} if (trackLow[0] == '<' && M.getBps(*it) < targetBps){result.insert(*it);} @@ -791,8 +807,8 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, if (sscanf(trackLow.c_str(), "<%ux%u", &resX, &resY) == 2){targetArea = resX*resY;} if (sscanf(trackLow.c_str(), ">%ux%u", &resX, &resY) == 2){targetArea = resX*resY;} if (targetArea){ - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + // select all tracks of this type that match the requirements + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ uint64_t trackArea = M.getWidth(*it)*M.getHeight(*it); if (trackLow[0] == '>' && trackArea > targetArea){result.insert(*it);} @@ -802,7 +818,9 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, return result; } } - //approx bitrate matching + + + //Select the single highest bit rate at or under the given bit rate if (trackLow.size() > 7 && trackLow.substr(0, 4) == "max<"){ unsigned int bpsVal; uint64_t targetBps = 0; @@ -812,11 +830,10 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, if (targetBps){ targetBps /= 8; // select nearest bit rate track of this type - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); size_t currVal = INVALID_TRACK_ID; uint32_t currDist = 0; bool foundUnder = false; - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ if (M.getBps(*it) <= targetBps){ if (!foundUnder || currDist > (targetBps-M.getBps(*it))){ @@ -836,7 +853,8 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, return result; } } - //approx bitrate matching + + //Select single track with bit rate closest to the given bit rate { unsigned int bpsVal; uint64_t targetBps = 0; @@ -846,10 +864,9 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, if (targetBps){ targetBps /= 8; // select nearest bit rate track of this type - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); size_t currVal = INVALID_TRACK_ID; uint32_t currDist = 0; - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ if (currVal == INVALID_TRACK_ID || (M.getBps(*it) >= targetBps && currDist > (M.getBps(*it)-targetBps)) || (M.getBps(*it) < targetBps && currDist > (targetBps-M.getBps(*it)))){ currVal = *it; @@ -861,11 +878,11 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, return result; } } - //audio channel matching + + //Match on audio channel count (the only audio-specific match) if (!trackType.size() || trackType == "audio"){ if (trackLow == "surround"){ - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ if (M.getChannels(*it) > 2){result.insert(*it);} } @@ -879,8 +896,7 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, if (trackLow == "stereo"){targetChannel = 2;} if (trackLow.find("ch") != std::string::npos && sscanf(trackLow.c_str(), "%uch", &channelVal) == 1){targetChannel = channelVal;} if (targetChannel){ - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ if (M.getChannels(*it) == targetChannel){result.insert(*it);} } @@ -888,14 +904,15 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, return result; } } - // approx resolution matching + + // Video-specific matches if (!trackType.size() || trackType == "video"){ + //Highest resolution if (trackLow == "highres" || trackLow == "bestres" || trackLow == "maxres"){ - // select highest resolution track of this type - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); + //Select highest resolution track of this type size_t currVal = INVALID_TRACK_ID; uint64_t currRes = 0; - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ uint64_t trackRes = M.getWidth(*it)*M.getHeight(*it); if (currRes < trackRes){ @@ -908,11 +925,10 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, return result; } if (trackLow == "lowres" || trackLow == "worstres" || trackLow == "minres"){ - // select lowest resolution track of this type - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); + //Select lowest resolution track of this type size_t currVal = INVALID_TRACK_ID; - uint64_t currRes = 0xFFFFFFFFFFFFFFFFull; - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + uint64_t currRes = INVALID_TRACK_ID; + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ uint64_t trackRes = M.getWidth(*it)*M.getHeight(*it); if (currRes > trackRes){ @@ -927,12 +943,11 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, { unsigned int resX, resY; if (sscanf(trackLow.c_str(), "~%ux%u", &resX, &resY) == 2){ - // select nearest resolution track of this type - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); + //Select nearest resolution track of this type size_t currVal = INVALID_TRACK_ID; uint64_t currDist = 0; uint64_t targetArea = resX*resY; - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ uint64_t trackArea = M.getWidth(*it)*M.getHeight(*it); if (currVal == INVALID_TRACK_ID || (trackArea >= targetArea && currDist > (trackArea-targetArea)) || (trackArea < targetArea && currDist > (targetArea-trackArea))){ @@ -946,11 +961,11 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, } } }// video track specific + // attempt to do language/codec matching // convert 2-character language codes into 3-character language codes if (trackLow.size() == 2){trackLow = Encodings::ISO639::twoToThree(trackLow);} - std::set validTracks = capa?getSupportedTracks(M, capa):M.getValidTracks(); - for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){ + for (std::set::iterator it = trackList.begin(); it != trackList.end(); it++){ if (!trackType.size() || M.getType(*it) == trackType || M.getCodec(*it) == trackType){ std::string codecLow = M.getCodec(*it); Util::stringToLower(codecLow); @@ -974,6 +989,16 @@ std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, return result; } +/// Selects a specific track or set of tracks of the given trackType, using trackVal to decide. +/// trackVal may be a comma-separated list of numbers, codecs or the word "all" or an asterisk. +/// Does not do any checks if the protocol supports these tracks, just selects blindly. +/// It is necessary to follow up with a selectDefaultTracks() call to strip unsupported +/// codecs/combinations. +std::set Util::findTracks(const DTSC::Meta &M, const JSON::Value &capa, const std::string &trackType, const std::string &trackVal, const std::string &UA){ + std::set validTracks = capa?getSupportedTracks(M, capa, "", UA):M.getValidTracks(); + return pickTracks(M, validTracks, trackType, trackVal); +} + std::set Util::wouldSelect(const DTSC::Meta &M, const std::string &trackSelector, const JSON::Value &capa, const std::string &UA){ std::map parsedVariables; @@ -1070,20 +1095,20 @@ std::set Util::wouldSelect(const DTSC::Meta &M, const std::map tracks = Util::findTracks(M, capa, "audio", targetParams.at("audio")); + std::set tracks = Util::findTracks(M, capa, "audio", targetParams.at("audio"), UA); result.insert(tracks.begin(), tracks.end()); } noSelAudio = true; } if (targetParams.count("video") && targetParams.at("video").size()){ if (targetParams.at("video") != "-1" && targetParams.at("video") != "none"){ - std::set tracks = Util::findTracks(M, capa, "video", targetParams.at("video")); + std::set tracks = Util::findTracks(M, capa, "video", targetParams.at("video"), UA); result.insert(tracks.begin(), tracks.end()); } noSelVideo = true; } if (targetParams.count("subtitle") && targetParams.at("subtitle").size()){ - std::set tracks = Util::findTracks(M, capa, "subtitle", targetParams.at("subtitle")); + std::set tracks = Util::findTracks(M, capa, "subtitle", targetParams.at("subtitle"), UA); result.insert(tracks.begin(), tracks.end()); noSelSub = true; } diff --git a/lib/stream.h b/lib/stream.h index f8ef94a3..3ae17f5f 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -30,6 +30,7 @@ namespace Util{ std::set getSupportedTracks(const DTSC::Meta &M, const JSON::Value &capa, const std::string &type = "", const std::string &UA = ""); + std::set pickTracks(const DTSC::Meta &M, const std::set trackList, const std::string &trackType, const std::string &trackVal); std::set findTracks(const DTSC::Meta &M, const JSON::Value &capa, const std::string &trackType, const std::string &trackVal, const std::string &UA = ""); std::set wouldSelect(const DTSC::Meta &M, const std::string &trackSelector = "", const JSON::Value &capa = empty, const std::string &UA = ""); diff --git a/src/output/output_http_internal.cpp b/src/output/output_http_internal.cpp index 525c995c..3d91b819 100644 --- a/src/output/output_http_internal.cpp +++ b/src/output/output_http_internal.cpp @@ -469,6 +469,8 @@ namespace Mist{ initialize(); if (!myConn){return json_resp;} + json_resp["selver"] = 2; + bool hasVideo = false; std::set validTracks = M.getValidTracks(); for (std::set::iterator it = validTracks.begin(); it != validTracks.end(); it++){