Added more ISO639 support (2-letter codes and conversions), improved audio/video/subtitle selection through HTTP connectors

This commit is contained in:
Thulinma 2017-11-17 11:26:03 +01:00
parent e09cd5308e
commit 307d14f901
7 changed files with 1332 additions and 607 deletions

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,8 @@ namespace Encodings {
class ISO639{
public:
static std::string decode(const std::string & lang);
static std::string twoToThree(const std::string & lang);
static std::string encode(const std::string & lang);
};

View file

@ -20,6 +20,7 @@
#include <sys/socket.h>
#include <netdb.h>
#include <sys/stat.h>
#include <mist/langcodes.h>
/*LTS-END*/
namespace Mist{
@ -392,11 +393,81 @@ namespace Mist{
}
}
/*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.
void Output::selectTrack(const std::string &trackType, const std::string &trackVal){
if (!trackVal.size() || trackVal == "0"){return;}//don't select anything in particular
if (trackVal.find(',') != std::string::npos){
//Comma-separated list, recurse.
std::stringstream ss(trackVal);
std::string item;
while (std::getline(ss, item, ',')){selectTrack(trackType, item);}
return;
}
long long trackNo = JSON::Value(trackVal).asInt();
if (trackVal == JSON::Value(trackNo).asString()){
//It's an integer number
if (!myMeta.tracks.count(trackNo)){
WARN_MSG("Track %lld does not exist in stream, cannot select", trackNo);
return;
}
const DTSC::Track & Trk = myMeta.tracks[trackNo];
if (Trk.type != trackType && Trk.codec != trackType){
WARN_MSG("Track %lld is not %s (%s/%s), cannot select", trackNo, trackType.c_str(), Trk.type.c_str(), Trk.codec.c_str());
return;
}
INFO_MSG("Selecting %s track %lld (%s/%s)", trackType.c_str(), trackNo, Trk.type.c_str(), Trk.codec.c_str());
selectedTracks.insert(trackNo);
return;
}
std::string trackLow = trackVal;
Util::stringToLower(trackLow);
if (trackLow == "all" || trackLow == "*"){
//select all tracks of this type
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
const DTSC::Track & Trk = it->second;
if (Trk.type == trackType || Trk.codec == trackType){
selectedTracks.insert(it->first);
INFO_MSG("Selecting %s track %lu (%s/%s)", trackType.c_str(), it->first, Trk.type.c_str(), Trk.codec.c_str());
}
}
return;
}
//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);}
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
const DTSC::Track & Trk = it->second;
if (Trk.type == trackType || Trk.codec == trackType){
std::string codecLow = Trk.codec;
Util::stringToLower(codecLow);
if (Trk.lang == trackLow || trackLow == codecLow){
selectedTracks.insert(it->first);
INFO_MSG("Selecting %s track %lu (%s/%s)", trackType.c_str(), it->first, Trk.type.c_str(), Trk.codec.c_str());
}
}
}
}
/*LTS-END*/
void Output::selectDefaultTracks(){
if (!isInitialized){
initialize();
return;
if (!isInitialized){return;}
}
//First, wipe the existing selections, if any.
selectedTracks.clear();
/*LTS-START*/
//Then, select the tracks we've been asked to select.
if (targetParams.count("audio")){selectTrack("audio", targetParams["audio"]);}
if (targetParams.count("video")){selectTrack("video", targetParams["video"]);}
if (targetParams.count("subtitle")){selectTrack("subtitle", targetParams["subtitle"]);}
/*LTS-END*/
//check which tracks don't actually exist
std::set<unsigned long> toRemove;
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
@ -494,7 +565,39 @@ namespace Mist{
}
}
}
/*LTS-START*/
//Finally, we strip anything unwanted that the above may have auto-selected.
toRemove.clear();
if (targetParams.count("subtitle") && targetParams["subtitle"] == "0"){
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks.at(*it).codec=="subtitle"){
toRemove.insert(*it);
}
}
}
if (targetParams.count("video") && targetParams["video"] == "0"){
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks.at(*it).type=="video"){
toRemove.insert(*it);
}
}
}
if (targetParams.count("audio") && targetParams["audio"] == "0"){
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks.at(*it).type=="audio"){
toRemove.insert(*it);
}
}
}
//remove those from selectedtracks
for (std::set<unsigned long>::iterator it = toRemove.begin(); it != toRemove.end(); it++){
selectedTracks.erase(*it);
}
/*LTS-END*/
if (Util::Config::printDebugLevel >= DLVL_MEDIUM){
//print the selected tracks
std::stringstream selected;

View file

@ -56,6 +56,7 @@ namespace Mist {
void setBlocking(bool blocking);
long unsigned int getMainSelectedTrack();
void updateMeta();
void selectTrack(const std::string &trackType, const std::string &trackVal); /*LTS*/
void selectDefaultTracks();
bool connectToFile(std::string file);
static bool listenMode(){return true;}

View file

@ -2,6 +2,8 @@
#include "output_http.h"
#include <mist/stream.h>
#include <mist/checksum.h>
#include <mist/util.h>
#include <mist/langcodes.h>
#include <set>
namespace Mist {
@ -207,7 +209,7 @@ namespace Mist {
}
}
}
void HTTPOutput::onRequest(){
while (H.Read(myConn)){
if (H.hasHeader("User-Agent")){
@ -227,39 +229,11 @@ namespace Mist {
}
INFO_MSG("Received request %s", H.getUrl().c_str());
if (H.GetVar("audio") != ""){targetParams["audio"] = H.GetVar("audio");}
if (H.GetVar("video") != ""){targetParams["video"] = H.GetVar("video");}
if (H.GetVar("subtitle") != ""){targetParams["subtitle"] = H.GetVar("subtitle");}
initialize();
if (H.GetVar("audio") != "" || H.GetVar("video") != ""){
selectedTracks.clear();
if (H.GetVar("audio") != ""){
selectedTracks.insert(JSON::Value(H.GetVar("audio")).asInt());
}
if (H.GetVar("video") != ""){
selectedTracks.insert(JSON::Value(H.GetVar("video")).asInt());
}
selectDefaultTracks();
std::set<unsigned long> toRemove;
if (H.GetVar("video") == "0"){
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks.at(*it).type=="video"){
toRemove.insert(*it);
}
}
}
if (H.GetVar("audio") == "0"){
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks.at(*it).type=="audio"){
toRemove.insert(*it);
}
}
}
//remove those from selectedtracks
for (std::set<unsigned long>::iterator it = toRemove.begin(); it != toRemove.end(); it++){
selectedTracks.erase(*it);
}
}else{
selectDefaultTracks();
}
selectDefaultTracks();
onHTTP();
if (!H.bufferChunks){
H.Clean();

View file

@ -44,10 +44,6 @@ namespace Mist{
streamOut = streamName;
}
}
std::string origTarget = config->getOption("target", true)[0u].asStringRef();
if (origTarget.rfind('?') != std::string::npos){
parseVars(origTarget.substr(origTarget.rfind('?') + 1));
}
initialize();
INFO_MSG("About to push stream %s out. Host: %s, port: %d, app: %s, stream: %s", streamName.c_str(), host.c_str(), port, app.c_str(), streamOut.c_str());
myConn = Socket::Connection(host, port, false);
@ -172,60 +168,6 @@ namespace Mist{
return false;
}
void OutRTMP::parseVars(std::string data){
std::string varname;
std::string varval;
bool trackSwitch = false;
// position where a part start (e.g. after &)
size_t pos = 0;
while (pos < data.length()){
size_t nextpos = data.find('&', pos);
if (nextpos == std::string::npos){
nextpos = data.length();
}
size_t eq_pos = data.find('=', pos);
if (eq_pos < nextpos){
// there is a key and value
varname = data.substr(pos, eq_pos - pos);
varval = data.substr(eq_pos + 1, nextpos - eq_pos - 1);
}else{
// no value, only a key
varname = data.substr(pos, nextpos - pos);
varval.clear();
}
if (varname == "track" || varname == "audio" || varname == "video"){
long long int selTrack = JSON::Value(varval).asInt();
if (myMeta){
if (myMeta.tracks.count(selTrack)){
std::string & delThis = myMeta.tracks[selTrack].type;
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks[*it].type == delThis){
selectedTracks.erase(it);
trackSwitch = true;
break;
}
}
selectedTracks.insert(selTrack);
}
}else{
selectedTracks.insert(selTrack);
}
}
if (nextpos == std::string::npos){
// in case the string is gigantic
break;
}
// erase &
pos = nextpos + 1;
}
if (trackSwitch && thisPacket){
seek(thisPacket.getTime());
}
}
void OutRTMP::init(Util::Config * cfg){
Output::init(cfg);
capa["name"] = "RTMP";
@ -812,7 +754,7 @@ namespace Mist{
if (streamName.find('?') != std::string::npos){
std::string tmpVars = streamName.substr(streamName.find('?') + 1);
streamName = streamName.substr(0, streamName.find('?'));
parseVars(tmpVars);
HTTP::parseVars(tmpVars, targetParams);
}
size_t colonPos = streamName.find(':');

View file

@ -21,7 +21,6 @@ namespace Mist {
int64_t rtmpOffset;
uint64_t lastOutTime;
unsigned int maxbps;
void parseVars(std::string data);
std::string app_name;
void parseChunk(Socket::Buffer & inputBuffer);
void parseAMFCommand(AMF::Object & amfData, int messageType, int streamId);