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{
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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++){
|
||||
|
@ -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){
|
||||
//print the selected tracks
|
||||
std::stringstream selected;
|
||||
|
|
|
@ -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;}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
onHTTP();
|
||||
if (!H.bufferChunks){
|
||||
H.Clean();
|
||||
|
|
|
@ -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(':');
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue