Added more ISO639 support (2-letter codes and conversions), improved audio/video/subtitle selection through HTTP connectors
This commit is contained in:
parent
e09cd5308e
commit
307d14f901
7 changed files with 1332 additions and 607 deletions
1728
lib/langcodes.cpp
1728
lib/langcodes.cpp
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,8 @@ namespace Encodings {
|
||||||
class ISO639{
|
class ISO639{
|
||||||
public:
|
public:
|
||||||
static std::string decode(const std::string & lang);
|
static std::string decode(const std::string & lang);
|
||||||
|
static std::string twoToThree(const std::string & lang);
|
||||||
|
static std::string encode(const std::string & lang);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <mist/langcodes.h>
|
||||||
/*LTS-END*/
|
/*LTS-END*/
|
||||||
|
|
||||||
namespace Mist{
|
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(){
|
void Output::selectDefaultTracks(){
|
||||||
if (!isInitialized){
|
if (!isInitialized){
|
||||||
initialize();
|
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
|
//check which tracks don't actually exist
|
||||||
std::set<unsigned long> toRemove;
|
std::set<unsigned long> toRemove;
|
||||||
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
|
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
|
||||||
|
@ -495,6 +566,38 @@ 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){
|
if (Util::Config::printDebugLevel >= DLVL_MEDIUM){
|
||||||
//print the selected tracks
|
//print the selected tracks
|
||||||
std::stringstream selected;
|
std::stringstream selected;
|
||||||
|
|
|
@ -56,6 +56,7 @@ namespace Mist {
|
||||||
void setBlocking(bool blocking);
|
void setBlocking(bool blocking);
|
||||||
long unsigned int getMainSelectedTrack();
|
long unsigned int getMainSelectedTrack();
|
||||||
void updateMeta();
|
void updateMeta();
|
||||||
|
void selectTrack(const std::string &trackType, const std::string &trackVal); /*LTS*/
|
||||||
void selectDefaultTracks();
|
void selectDefaultTracks();
|
||||||
bool connectToFile(std::string file);
|
bool connectToFile(std::string file);
|
||||||
static bool listenMode(){return true;}
|
static bool listenMode(){return true;}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#include "output_http.h"
|
#include "output_http.h"
|
||||||
#include <mist/stream.h>
|
#include <mist/stream.h>
|
||||||
#include <mist/checksum.h>
|
#include <mist/checksum.h>
|
||||||
|
#include <mist/util.h>
|
||||||
|
#include <mist/langcodes.h>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace Mist {
|
namespace Mist {
|
||||||
|
@ -227,39 +229,11 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
|
|
||||||
INFO_MSG("Received request %s", H.getUrl().c_str());
|
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();
|
initialize();
|
||||||
if (H.GetVar("audio") != "" || H.GetVar("video") != ""){
|
selectDefaultTracks();
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
onHTTP();
|
onHTTP();
|
||||||
if (!H.bufferChunks){
|
if (!H.bufferChunks){
|
||||||
H.Clean();
|
H.Clean();
|
||||||
|
|
|
@ -44,10 +44,6 @@ namespace Mist{
|
||||||
streamOut = streamName;
|
streamOut = streamName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::string origTarget = config->getOption("target", true)[0u].asStringRef();
|
|
||||||
if (origTarget.rfind('?') != std::string::npos){
|
|
||||||
parseVars(origTarget.substr(origTarget.rfind('?') + 1));
|
|
||||||
}
|
|
||||||
initialize();
|
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());
|
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);
|
myConn = Socket::Connection(host, port, false);
|
||||||
|
@ -172,60 +168,6 @@ namespace Mist{
|
||||||
return false;
|
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){
|
void OutRTMP::init(Util::Config * cfg){
|
||||||
Output::init(cfg);
|
Output::init(cfg);
|
||||||
capa["name"] = "RTMP";
|
capa["name"] = "RTMP";
|
||||||
|
@ -812,7 +754,7 @@ namespace Mist{
|
||||||
if (streamName.find('?') != std::string::npos){
|
if (streamName.find('?') != std::string::npos){
|
||||||
std::string tmpVars = streamName.substr(streamName.find('?') + 1);
|
std::string tmpVars = streamName.substr(streamName.find('?') + 1);
|
||||||
streamName = streamName.substr(0, streamName.find('?'));
|
streamName = streamName.substr(0, streamName.find('?'));
|
||||||
parseVars(tmpVars);
|
HTTP::parseVars(tmpVars, targetParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t colonPos = streamName.find(':');
|
size_t colonPos = streamName.find(':');
|
||||||
|
|
|
@ -21,7 +21,6 @@ namespace Mist {
|
||||||
int64_t rtmpOffset;
|
int64_t rtmpOffset;
|
||||||
uint64_t lastOutTime;
|
uint64_t lastOutTime;
|
||||||
unsigned int maxbps;
|
unsigned int maxbps;
|
||||||
void parseVars(std::string data);
|
|
||||||
std::string app_name;
|
std::string app_name;
|
||||||
void parseChunk(Socket::Buffer & inputBuffer);
|
void parseChunk(Socket::Buffer & inputBuffer);
|
||||||
void parseAMFCommand(AMF::Object & amfData, int messageType, int streamId);
|
void parseAMFCommand(AMF::Object & amfData, int messageType, int streamId);
|
||||||
|
|
Loading…
Add table
Reference in a new issue