<commit_before>#include <mist/procs.h>
#include <mist/config.h>
#include <mist/timing.h>
#include <mist/stream.h>
#include <mist/dtsc.h>
#include <mist/defines.h>
#include <mist/shared_memory.h>
#include "controller_streams.h"
#include "controller_storage.h"
#include "controller_statistics.h"
#include <sys/stat.h>
#include <map>

///\brief Holds everything unique to the controller.
namespace Controller {

  ///\brief Checks whether two streams are equal.
  ///\param one The first stream for the comparison.
  ///\param two The second stream for the comparison.
  ///\return True if the streams are equal, false otherwise.
  bool streamsEqual(JSON::Value & one, JSON::Value & two){
    if (one.isMember("source") != two.isMember("source") || one["source"] != two["source"]){
      return false;
    }
    if (one.isMember("DVR") != two.isMember("DVR") || (one.isMember("DVR") && one["DVR"] != two["DVR"])){
      return false;
    }
    if (one.isMember("cut") != two.isMember("cut") || (one.isMember("cut") && one["cut"] != two["cut"])){
      return false;
    }
    return true;
  }

  ///\brief Starts a single stream
  ///\param name The name of the stream
  ///\param data The corresponding configuration values.
  void startStream(std::string name, JSON::Value & data){
    data["online"] = (std::string)"Checking...";
    data.removeMember("error");
    std::string URL;
    if (data.isMember("channel") && data["channel"].isMember("URL")){
      URL = data["channel"]["URL"].asString();
    }
    if (data.isMember("source")){
      URL = data["source"].asString();
    }
    if (URL == ""){
      Log("STRM", "Error for stream " + name + "! Source parameter missing.");
      data["error"] = "Stream offline: Missing source parameter!";
      return;
    }
    if (URL.substr(0, 4) == "push"){
      if (hasViewers(name)){
        data["meta"].null();
        IPC::sharedPage streamIndex(name,0,false,false);
        if (!streamIndex.mapped){
          return;
        }
        unsigned int i = 0;
        JSON::fromDTMI((const unsigned char*)streamIndex.mapped + 8, streamIndex.len - 8, i, data["meta"]);
        if (data["meta"].isMember("tracks") && data["meta"]["tracks"].size()){
          for(JSON::ObjIter trackIt = data["meta"]["tracks"].ObjBegin(); trackIt != data["meta"]["tracks"].ObjEnd(); trackIt++){
            trackIt->second.removeMember("fragments");
            trackIt->second.removeMember("keys");
            trackIt->second.removeMember("parts");
          }
        }
      }
    }else{
      if (URL.substr(0, 1) == "/"){
        data.removeMember("error");
        struct stat fileinfo;
        if (stat(URL.c_str(), &fileinfo) != 0 || S_ISDIR(fileinfo.st_mode)){
          Log("BUFF", "Warning for VoD stream " + name + "! File not found: " + URL);
          data["error"] = "Stream offline: Not found: " + URL;
          data["online"] = 0;
          return;
        }
        bool getMeta = false;
        if ( !data.isMember("l_meta") || fileinfo.st_mtime != data["l_meta"].asInt()){
          getMeta = true;
          data["l_meta"] = (long long)fileinfo.st_mtime;
        }
        if (stat((URL+".dtsh").c_str(), &fileinfo) == 0 && !S_ISDIR(fileinfo.st_mode)){
          if ( !data.isMember("h_meta") || fileinfo.st_mtime != data["h_meta"].asInt()){
            getMeta = true;
            data["h_meta"] = (long long)fileinfo.st_mtime;
          }
        }
        if ( !getMeta && data.isMember("meta") && data["meta"].isMember("tracks")){
          for (JSON::ObjIter trIt = data["meta"]["tracks"].ObjBegin(); trIt != data["meta"]["tracks"].ObjEnd(); trIt++){
            if (trIt->second["codec"] == "H264"){
              if ( !trIt->second.isMember("init")){
                getMeta = true;
              }else{
                if (trIt->second["init"].asString().size() < 4){
                  Log("WARN", "Source file "+URL+" does not contain H264 init data that MistServer can interpret.");
                  data["error"] = "Stream offline: Invalid?";
                }else{
                  if (trIt->second["init"].asString().c_str()[1] != 0x42){
                    Log("WARN", "Source file "+URL+" is not H264 Baseline - convert to baseline profile for best compatibility.");
                    data["error"] = "Not optimal (details in log)";
                  }else{
                    if (trIt->second["init"].asString().c_str()[3] > 30){
                      Log("WARN", "Source file "+URL+" is higher than H264 level 3.0 - convert to a level <= 3.0 for best compatibility.");
                      data["error"] = "Not optimal (details in log)";
                    }
                  }
                }
              }
            }
          }
          if ( !data["meta"] || !data["meta"]["tracks"]){
            Log("WARN", "Source file " + URL + " seems to be corrupt.");
            data["error"] = "Stream offline: Corrupt file?";
            data["online"] = 0;
            return;
          }
        }else{
          getMeta = true;
        }
        if (getMeta){
          // if the file isn't dtsc and there's no dtsh file, run getStream on it
          // this guarantees that if the stream is playable, it now has a valid header.
          if ((URL.substr(URL.size() - 5) != ".dtsc") && (stat((URL+".dtsh").c_str(), &fileinfo) != 0)){
            Util::Stream::getVod(URL, name);
          }
          //now, run mistinfo on the source - or on the accompanying dtsh file, if it exists
          if (stat((URL+".dtsh").c_str(), &fileinfo) == 0){
            URL += ".dtsh";
          }
          char * tmp_cmd[3] = {0, 0, 0};
          std::string mistinfo = Util::getMyPath() + "MistInfo";
          tmp_cmd[0] = (char*)mistinfo.c_str();
          tmp_cmd[1] = (char*)URL.c_str();
          data["meta"] = JSON::fromString(Util::Procs::getOutputOf(tmp_cmd));
          if ( !data["meta"] || !data["meta"].isMember("tracks") || !data["meta"]["tracks"]){
            Log("WARN", "Source file " + URL + " seems to be corrupt.");
            data["error"] = "Stream offline: Corrupt file?";
            data["online"] = 0;
            return;
          }
        }
        if ( data["meta"] && data["meta"].isMember("tracks") && data["meta"]["tracks"] && !DTSC::isFixed(data["meta"])){
          char * tmp_cmd[3] = {0, 0, 0};
          std::string mistfix = Util::getMyPath() + "MistDTSCFix";
          tmp_cmd[0] = (char*)mistfix.c_str();
          tmp_cmd[1] = (char*)URL.c_str();
          Util::Procs::getOutputOf(tmp_cmd);
          data.removeMember("meta");
        }
        if (!hasViewers(name)){
          if ( !data.isMember("error")){
            data["error"] = "Available";
          }
          data["online"] = 2;
        }else{
          data["online"] = 1;
        }
        return; //MistPlayer handles VoD
      }else{
        /// \todo Implement ffmpeg pulling again?
        //Util::Procs::Start(name, "ffmpeg -re -async 2 -i " + URL + " -f flv -", Util::getMyPath() + "MistFLV2DTSC", Util::getMyPath() + buffcmd);
        //Log("BUFF", "(re)starting stream buffer " + name + " for ffmpeg data: ffmpeg -re -async 2 -i " + URL + " -f flv -");
      }
    }
  }

  ///\brief Checks all streams, restoring if needed.
  ///\param data The stream configuration for the server.
  void CheckAllStreams(JSON::Value & data){
    long long int currTime = Util::epoch();
    for (JSON::ObjIter jit = data.ObjBegin(); jit != data.ObjEnd(); jit++){
      if ( !Util::Procs::isActive(jit->first)){
        startStream(jit->first, jit->second);
      }
      if (!jit->second.isMember("name")){
        jit->second["name"] = jit->first;
      }
      if (!hasViewers(jit->first)){
        if (jit->second.isMember("source") && jit->second["source"].asString().substr(0, 1) == "/" && jit->second.isMember("error")
            && jit->second["error"].asString().substr(0,15) != "Stream offline:"){
          jit->second["online"] = 2;
        }else{
          if (jit->second.isMember("error") && jit->second["error"].asString() == "Available"){
            jit->second.removeMember("error");
          }
          jit->second["online"] = 0;
        }
      }else{
        // assume all is fine
        jit->second.removeMember("error");
        jit->second["online"] = 1;
        // check if source is valid
        //if (jit->second.isMember("live") && !jit->second.isMember("meta") || !jit->second["meta"]){
        if ( (jit->second.isMember("meta") && !jit->second["meta"].isMember("tracks"))){
          jit->second["online"] = 0;
          jit->second["error"] = "No (valid) source connected ";
        }else{
          // for live streams, keep track of activity
          if (jit->second["meta"].isMember("live")){
            static std::map<std::string, liveCheck> checker;
            //check activity by monitoring the lastms of track 0;
            JSON::ObjIter trackIt = jit->second["meta"]["tracks"].ObjBegin();
            if (trackIt->second["lastms"].asInt() != checker[jit->first].lastms){
              checker[jit->first].lastms = trackIt->second["lastms"].asInt();
              checker[jit->first].last_active = currTime;
            }
            //check H264 tracks for optimality
            if (jit->second.isMember("meta") && jit->second["meta"].isMember("tracks")){
              for (JSON::ObjIter trIt = jit->second["meta"]["tracks"].ObjBegin(); trIt != jit->second["meta"]["tracks"].ObjEnd(); trIt++){
                if (trIt->second["codec"] == "H264"){
                  if (trIt->second.isMember("init")){
                    if (trIt->second["init"].asString().size() < 4){
                      Log("WARN", "Live stream "+jit->first+" does not contain H264 init data that MistServer can interpret.");
                      jit->second["error"] = "Stream offline: Invalid?";
                    }else{
                      if (trIt->second["init"].asString().c_str()[1] != 0x42){
                        Log("WARN", "Live stream "+jit->first+" is not H264 Baseline - convert to baseline profile for best compatibility.");
                        jit->second["error"] = "Not optimal (details in log)";
                      }else{
                        if (trIt->second["init"].asString().c_str()[3] > 30){
                          Log("WARN", "Live stream "+jit->first+" is higher than H264 level 3.0 - convert to a level <= 3.0 for best compatibility.");
                          jit->second["error"] = "Not optimal (details in log)";
                        }
                      }
                    }
                  }
                }
              }
            }
            // mark stream as offline if no activity for 5 seconds
            //if (jit->second.isMember("last_active") && jit->second["last_active"].asInt() < currTime - 5){
            if (checker[jit->first].last_active < currTime - 5){
              jit->second["online"] = 2;
              jit->second["error"] = "Source not active";
            }
          }
        }
      }
    }
    static JSON::Value strlist;
    bool changed = false;
    if (strlist["config"] != Storage["config"]){
      strlist["config"] = Storage["config"];
      changed = true;
    }
    if (strlist["streams"] != Storage["streams"]){
      strlist["streams"] = Storage["streams"];
      changed = true;
    }
    if (changed){
      WriteFile(Util::getTmpFolder() + "streamlist", strlist.toString());
    }
  }

  void AddStreams(JSON::Value & in, JSON::Value & out){
    //check for new streams and updates
    for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){
      if (out.isMember(jit->first)){
        if ( !streamsEqual(jit->second, out[jit->first])){
          out[jit->first].null();
          out[jit->first]["name"] = jit->first;
          out[jit->first]["source"] = jit->second["source"];
          out[jit->first]["DVR"] = jit->second["DVR"].asInt();
          out[jit->first]["cut"] = jit->second["cut"].asInt();
          out[jit->first]["updated"] = 1ll;
          Log("STRM", std::string("Updated stream ") + jit->first);
          if (out[jit->first]["source"].asStringRef().substr(0, 7) != "push://"){
            Util::Procs::Stop(jit->first);
            startStream(jit->first, out[jit->first]);
          }else{
            if ( !Util::Procs::isActive(jit->first)){
              startStream(jit->first, out[jit->first]);
            }
          }
        }
      }else{
        out[jit->first]["name"] = jit->first;
        out[jit->first]["source"] = jit->second["source"];
        out[jit->first]["DVR"] = jit->second["DVR"].asInt();
        out[jit->first]["cut"] = jit->second["cut"].asInt();
        Log("STRM", std::string("New stream ") + jit->first);
        startStream(jit->first, out[jit->first]);
      }
    }
  }

  ///\brief Parse a given stream configuration.
  ///\param in The requested configuration.
  ///\param out The new configuration after parsing.
  void CheckStreams(JSON::Value & in, JSON::Value & out){
    //check for new streams and updates
    AddStreams(in, out);

    //check for deleted streams
    std::set<std::string> toDelete;
    for (JSON::ObjIter jit = out.ObjBegin(); jit != out.ObjEnd(); jit++){
      if ( !in.isMember(jit->first)){
        toDelete.insert(jit->first);
        Log("STRM", std::string("Deleted stream ") + jit->first);
        Util::Procs::Stop(jit->first);
      }
    }
    //actually delete the streams
    while (toDelete.size() > 0){
      std::string deleting = *(toDelete.begin());
      out.removeMember(deleting);
      toDelete.erase(deleting);
    }

    //update old-style configurations to new-style
    for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){
      if (jit->second.isMember("channel")){
        if ( !jit->second.isMember("source")){
          jit->second["source"] = jit->second["channel"]["URL"];
        }
        jit->second.removeMember("channel");
      }
      if (jit->second.isMember("preset")){
        jit->second.removeMember("preset");
      }
    }

  }

} //Controller namespace
<commit_msg>Fixed default values for cut and DVR time stream settings.<commit_after>#include <mist/procs.h>
#include <mist/config.h>
#include <mist/timing.h>
#include <mist/stream.h>
#include <mist/dtsc.h>
#include <mist/defines.h>
#include <mist/shared_memory.h>
#include "controller_streams.h"
#include "controller_storage.h"
#include "controller_statistics.h"
#include <sys/stat.h>
#include <map>

///\brief Holds everything unique to the controller.
namespace Controller {

  ///\brief Checks whether two streams are equal.
  ///\param one The first stream for the comparison.
  ///\param two The second stream for the comparison.
  ///\return True if the streams are equal, false otherwise.
  bool streamsEqual(JSON::Value & one, JSON::Value & two){
    if (one.isMember("source") != two.isMember("source") || one["source"] != two["source"]){
      return false;
    }
    if (one.isMember("DVR") != two.isMember("DVR") || (one.isMember("DVR") && one["DVR"] != two["DVR"])){
      return false;
    }
    if (one.isMember("cut") != two.isMember("cut") || (one.isMember("cut") && one["cut"] != two["cut"])){
      return false;
    }
    return true;
  }

  ///\brief Starts a single stream
  ///\param name The name of the stream
  ///\param data The corresponding configuration values.
  void startStream(std::string name, JSON::Value & data){
    data["online"] = (std::string)"Checking...";
    data.removeMember("error");
    std::string URL;
    if (data.isMember("channel") && data["channel"].isMember("URL")){
      URL = data["channel"]["URL"].asString();
    }
    if (data.isMember("source")){
      URL = data["source"].asString();
    }
    if (URL == ""){
      Log("STRM", "Error for stream " + name + "! Source parameter missing.");
      data["error"] = "Stream offline: Missing source parameter!";
      return;
    }
    if (URL.substr(0, 4) == "push"){
      if (hasViewers(name)){
        data["meta"].null();
        IPC::sharedPage streamIndex(name,0,false,false);
        if (!streamIndex.mapped){
          return;
        }
        unsigned int i = 0;
        JSON::fromDTMI((const unsigned char*)streamIndex.mapped + 8, streamIndex.len - 8, i, data["meta"]);
        if (data["meta"].isMember("tracks") && data["meta"]["tracks"].size()){
          for(JSON::ObjIter trackIt = data["meta"]["tracks"].ObjBegin(); trackIt != data["meta"]["tracks"].ObjEnd(); trackIt++){
            trackIt->second.removeMember("fragments");
            trackIt->second.removeMember("keys");
            trackIt->second.removeMember("parts");
          }
        }
      }
    }else{
      if (URL.substr(0, 1) == "/"){
        data.removeMember("error");
        struct stat fileinfo;
        if (stat(URL.c_str(), &fileinfo) != 0 || S_ISDIR(fileinfo.st_mode)){
          Log("BUFF", "Warning for VoD stream " + name + "! File not found: " + URL);
          data["error"] = "Stream offline: Not found: " + URL;
          data["online"] = 0;
          return;
        }
        bool getMeta = false;
        if ( !data.isMember("l_meta") || fileinfo.st_mtime != data["l_meta"].asInt()){
          getMeta = true;
          data["l_meta"] = (long long)fileinfo.st_mtime;
        }
        if (stat((URL+".dtsh").c_str(), &fileinfo) == 0 && !S_ISDIR(fileinfo.st_mode)){
          if ( !data.isMember("h_meta") || fileinfo.st_mtime != data["h_meta"].asInt()){
            getMeta = true;
            data["h_meta"] = (long long)fileinfo.st_mtime;
          }
        }
        if ( !getMeta && data.isMember("meta") && data["meta"].isMember("tracks")){
          for (JSON::ObjIter trIt = data["meta"]["tracks"].ObjBegin(); trIt != data["meta"]["tracks"].ObjEnd(); trIt++){
            if (trIt->second["codec"] == "H264"){
              if ( !trIt->second.isMember("init")){
                getMeta = true;
              }else{
                if (trIt->second["init"].asString().size() < 4){
                  Log("WARN", "Source file "+URL+" does not contain H264 init data that MistServer can interpret.");
                  data["error"] = "Stream offline: Invalid?";
                }else{
                  if (trIt->second["init"].asString().c_str()[1] != 0x42){
                    Log("WARN", "Source file "+URL+" is not H264 Baseline - convert to baseline profile for best compatibility.");
                    data["error"] = "Not optimal (details in log)";
                  }else{
                    if (trIt->second["init"].asString().c_str()[3] > 30){
                      Log("WARN", "Source file "+URL+" is higher than H264 level 3.0 - convert to a level <= 3.0 for best compatibility.");
                      data["error"] = "Not optimal (details in log)";
                    }
                  }
                }
              }
            }
          }
          if ( !data["meta"] || !data["meta"]["tracks"]){
            Log("WARN", "Source file " + URL + " seems to be corrupt.");
            data["error"] = "Stream offline: Corrupt file?";
            data["online"] = 0;
            return;
          }
        }else{
          getMeta = true;
        }
        if (getMeta){
          // if the file isn't dtsc and there's no dtsh file, run getStream on it
          // this guarantees that if the stream is playable, it now has a valid header.
          if ((URL.substr(URL.size() - 5) != ".dtsc") && (stat((URL+".dtsh").c_str(), &fileinfo) != 0)){
            Util::Stream::getVod(URL, name);
          }
          //now, run mistinfo on the source - or on the accompanying dtsh file, if it exists
          if (stat((URL+".dtsh").c_str(), &fileinfo) == 0){
            URL += ".dtsh";
          }
          char * tmp_cmd[3] = {0, 0, 0};
          std::string mistinfo = Util::getMyPath() + "MistInfo";
          tmp_cmd[0] = (char*)mistinfo.c_str();
          tmp_cmd[1] = (char*)URL.c_str();
          data["meta"] = JSON::fromString(Util::Procs::getOutputOf(tmp_cmd));
          if ( !data["meta"] || !data["meta"].isMember("tracks") || !data["meta"]["tracks"]){
            Log("WARN", "Source file " + URL + " seems to be corrupt.");
            data["error"] = "Stream offline: Corrupt file?";
            data["online"] = 0;
            return;
          }
        }
        if ( data["meta"] && data["meta"].isMember("tracks") && data["meta"]["tracks"] && !DTSC::isFixed(data["meta"])){
          char * tmp_cmd[3] = {0, 0, 0};
          std::string mistfix = Util::getMyPath() + "MistDTSCFix";
          tmp_cmd[0] = (char*)mistfix.c_str();
          tmp_cmd[1] = (char*)URL.c_str();
          Util::Procs::getOutputOf(tmp_cmd);
          data.removeMember("meta");
        }
        if (!hasViewers(name)){
          if ( !data.isMember("error")){
            data["error"] = "Available";
          }
          data["online"] = 2;
        }else{
          data["online"] = 1;
        }
        return; //MistPlayer handles VoD
      }else{
        /// \todo Implement ffmpeg pulling again?
        //Util::Procs::Start(name, "ffmpeg -re -async 2 -i " + URL + " -f flv -", Util::getMyPath() + "MistFLV2DTSC", Util::getMyPath() + buffcmd);
        //Log("BUFF", "(re)starting stream buffer " + name + " for ffmpeg data: ffmpeg -re -async 2 -i " + URL + " -f flv -");
      }
    }
  }

  ///\brief Checks all streams, restoring if needed.
  ///\param data The stream configuration for the server.
  void CheckAllStreams(JSON::Value & data){
    long long int currTime = Util::epoch();
    for (JSON::ObjIter jit = data.ObjBegin(); jit != data.ObjEnd(); jit++){
      if ( !Util::Procs::isActive(jit->first)){
        startStream(jit->first, jit->second);
      }
      if (!jit->second.isMember("name")){
        jit->second["name"] = jit->first;
      }
      if (!hasViewers(jit->first)){
        if (jit->second.isMember("source") && jit->second["source"].asString().substr(0, 1) == "/" && jit->second.isMember("error")
            && jit->second["error"].asString().substr(0,15) != "Stream offline:"){
          jit->second["online"] = 2;
        }else{
          if (jit->second.isMember("error") && jit->second["error"].asString() == "Available"){
            jit->second.removeMember("error");
          }
          jit->second["online"] = 0;
        }
      }else{
        // assume all is fine
        jit->second.removeMember("error");
        jit->second["online"] = 1;
        // check if source is valid
        //if (jit->second.isMember("live") && !jit->second.isMember("meta") || !jit->second["meta"]){
        if ( (jit->second.isMember("meta") && !jit->second["meta"].isMember("tracks"))){
          jit->second["online"] = 0;
          jit->second["error"] = "No (valid) source connected ";
        }else{
          // for live streams, keep track of activity
          if (jit->second["meta"].isMember("live")){
            static std::map<std::string, liveCheck> checker;
            //check activity by monitoring the lastms of track 0;
            JSON::ObjIter trackIt = jit->second["meta"]["tracks"].ObjBegin();
            if (trackIt->second["lastms"].asInt() != checker[jit->first].lastms){
              checker[jit->first].lastms = trackIt->second["lastms"].asInt();
              checker[jit->first].last_active = currTime;
            }
            //check H264 tracks for optimality
            if (jit->second.isMember("meta") && jit->second["meta"].isMember("tracks")){
              for (JSON::ObjIter trIt = jit->second["meta"]["tracks"].ObjBegin(); trIt != jit->second["meta"]["tracks"].ObjEnd(); trIt++){
                if (trIt->second["codec"] == "H264"){
                  if (trIt->second.isMember("init")){
                    if (trIt->second["init"].asString().size() < 4){
                      Log("WARN", "Live stream "+jit->first+" does not contain H264 init data that MistServer can interpret.");
                      jit->second["error"] = "Stream offline: Invalid?";
                    }else{
                      if (trIt->second["init"].asString().c_str()[1] != 0x42){
                        Log("WARN", "Live stream "+jit->first+" is not H264 Baseline - convert to baseline profile for best compatibility.");
                        jit->second["error"] = "Not optimal (details in log)";
                      }else{
                        if (trIt->second["init"].asString().c_str()[3] > 30){
                          Log("WARN", "Live stream "+jit->first+" is higher than H264 level 3.0 - convert to a level <= 3.0 for best compatibility.");
                          jit->second["error"] = "Not optimal (details in log)";
                        }
                      }
                    }
                  }
                }
              }
            }
            // mark stream as offline if no activity for 5 seconds
            //if (jit->second.isMember("last_active") && jit->second["last_active"].asInt() < currTime - 5){
            if (checker[jit->first].last_active < currTime - 5){
              jit->second["online"] = 2;
              jit->second["error"] = "Source not active";
            }
          }
        }
      }
    }
    static JSON::Value strlist;
    bool changed = false;
    if (strlist["config"] != Storage["config"]){
      strlist["config"] = Storage["config"];
      changed = true;
    }
    if (strlist["streams"] != Storage["streams"]){
      strlist["streams"] = Storage["streams"];
      changed = true;
    }
    if (changed){
      WriteFile(Util::getTmpFolder() + "streamlist", strlist.toString());
    }
  }

  void AddStreams(JSON::Value & in, JSON::Value & out){
    //check for new streams and updates
    for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){
      if (out.isMember(jit->first)){
        if ( !streamsEqual(jit->second, out[jit->first])){
          out[jit->first].null();
          out[jit->first]["name"] = jit->first;
          out[jit->first]["source"] = jit->second["source"];
          if (jit->second.isMember("DVR")){
            out[jit->first]["DVR"] = jit->second["DVR"].asInt();
          }
          if (jit->second.isMember("cut")){
            out[jit->first]["cut"] = jit->second["cut"].asInt();
          }
          out[jit->first]["updated"] = 1ll;
          Log("STRM", std::string("Updated stream ") + jit->first);
          if (out[jit->first]["source"].asStringRef().substr(0, 7) != "push://"){
            Util::Procs::Stop(jit->first);
            startStream(jit->first, out[jit->first]);
          }else{
            if ( !Util::Procs::isActive(jit->first)){
              startStream(jit->first, out[jit->first]);
            }
          }
        }
      }else{
        out[jit->first]["name"] = jit->first;
        out[jit->first]["source"] = jit->second["source"];
        if (jit->second.isMember("DVR")){
          out[jit->first]["DVR"] = jit->second["DVR"].asInt();
        }
        if (jit->second.isMember("cut")){
          out[jit->first]["cut"] = jit->second["cut"].asInt();
        }
        Log("STRM", std::string("New stream ") + jit->first);
        startStream(jit->first, out[jit->first]);
      }
    }
  }

  ///\brief Parse a given stream configuration.
  ///\param in The requested configuration.
  ///\param out The new configuration after parsing.
  void CheckStreams(JSON::Value & in, JSON::Value & out){
    //check for new streams and updates
    AddStreams(in, out);

    //check for deleted streams
    std::set<std::string> toDelete;
    for (JSON::ObjIter jit = out.ObjBegin(); jit != out.ObjEnd(); jit++){
      if ( !in.isMember(jit->first)){
        toDelete.insert(jit->first);
        Log("STRM", std::string("Deleted stream ") + jit->first);
        Util::Procs::Stop(jit->first);
      }
    }
    //actually delete the streams
    while (toDelete.size() > 0){
      std::string deleting = *(toDelete.begin());
      out.removeMember(deleting);
      toDelete.erase(deleting);
    }

    //update old-style configurations to new-style
    for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){
      if (jit->second.isMember("channel")){
        if ( !jit->second.isMember("source")){
          jit->second["source"] = jit->second["channel"]["URL"];
        }
        jit->second.removeMember("channel");
      }
      if (jit->second.isMember("preset")){
        jit->second.removeMember("preset");
      }
    }

  }

} //Controller namespace
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <config.h>

#include <glibmm.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
#include "version.hpp"
#include <glib/gstdio.h>
#include <ftw.h>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

#include "TransportFactory.hpp"

#include <SignalHandler.hpp>
#include <ServerMethods.hpp>
#include <gst/gst.h>

#include <boost/program_options.hpp>
#include <boost/exception/diagnostic_information.hpp>

#include "logging.hpp"

#define GST_CAT_DEFAULT kurento_media_server
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoMediaServer"

const std::string DEFAULT_CONFIG_FILE = "/etc/kurento/kurento.conf.json";

using namespace ::kurento;

static std::shared_ptr<Transport> transport;

__pid_t pid;

Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create ();

static gchar *tmp_dir;

Glib::RefPtr<Glib::IOChannel> channel;

static void
load_config (boost::property_tree::ptree &config, const std::string &file_name)
{
  boost::filesystem::path configFilePath (file_name);
  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  boost::property_tree::read_json (file_name, config);

  config.add ("configPath", configFilePath.parent_path().string() );
  pid = getpid();

  GST_INFO ("Configuration loaded successfully");

  std::shared_ptr<ServerMethods> serverMethods (new ServerMethods (config) );

  try {
    transport = TransportFactory::create_transport (config, serverMethods);
  } catch (std::exception &e) {
    GST_ERROR ("Error creating transport: %s", e.what() );
    exit (1);
  }
}

static void
signal_handler (uint32_t signo)
{
  static unsigned int __terminated = 0;

  switch (signo) {
  case SIGINT:
  case SIGTERM:
    if (__terminated == 0) {
      GST_DEBUG ("Terminating.");
      loop->quit ();
    }

    __terminated = 1;
    break;

  case SIGPIPE:
    GST_DEBUG ("Ignore sigpipe signal");
    break;

  case SIGSEGV:
    GST_DEBUG ("Segmentation fault. Aborting process execution");
    abort ();

  default:
    break;
  }
}

static int
delete_file (const char *fpath, const struct stat *sb, int typeflag,
             struct FTW *ftwbuf)
{
  int rv = g_remove (fpath);

  if (rv) {
    GST_WARNING ("Error deleting file: %s. %s", fpath, strerror (errno) );
  }

  return rv;
}
static void
remove_recursive (const gchar *path)
{
  nftw (path, delete_file, 64, FTW_DEPTH | FTW_PHYS);
}

static void
deleteCertificate ()
{
  // Only parent process can delete certificate
  if (pid != getpid() ) {
    return;
  }

  if (tmp_dir != NULL) {
    remove_recursive (tmp_dir);
    g_free (tmp_dir);
  }
}

int
main (int argc, char **argv)
{
  sigset_t mask;
  std::shared_ptr <SignalHandler> signalHandler;
  boost::property_tree::ptree config;
  std::string confFile;

  Glib::init();

  gst_init (&argc, &argv);
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);
  gst_debug_remove_log_function_by_data (NULL);
  gst_debug_add_log_function (log_function, NULL, NULL);

  try {
    boost::program_options::options_description desc ("kurento-media-server usage");

    desc.add_options()
    ("help,h", "Display this help message")
    ("version,v", "Display the version number")
    ("conf-file,f", boost::program_options::value<std::string>
     (&confFile)->default_value (DEFAULT_CONFIG_FILE),
     "Configuration file location");

    boost::program_options::variables_map vm;
    boost::program_options::store (boost::program_options::parse_command_line (argc,
                                   argv, desc), vm);
    boost::program_options::notify (vm);

    if (vm.count ("help") ) {
      std::cout << desc << "\n";
      exit (0);
    }

  } catch (boost::program_options::error &e) {
    std::cerr <<  "Error : " << e.what() << std::endl;
    exit (1);
  }

  /* Install our signal handler */
  sigemptyset (&mask);
  sigaddset (&mask, SIGINT);
  sigaddset (&mask, SIGTERM);
  sigaddset (&mask, SIGSEGV);
  sigaddset (&mask, SIGPIPE);
  signalHandler = std::shared_ptr <SignalHandler> (new SignalHandler (mask,
                  signal_handler) );

  GST_INFO ("Kmsc version: %s", get_version () );

  load_config (config, confFile);

  /* Start transport */
  transport->start ();

  GST_INFO ("Mediaserver started");

  loop->run ();

  signalHandler.reset();

  deleteCertificate ();

  transport->stop();

  transport.reset();

  return 0;
}
<commit_msg>main: Parse commanline options also from environment variables<commit_after>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <config.h>

#include <glibmm.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
#include "version.hpp"
#include <glib/gstdio.h>
#include <ftw.h>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

#include "TransportFactory.hpp"

#include <SignalHandler.hpp>
#include <ServerMethods.hpp>
#include <gst/gst.h>

#include <boost/program_options.hpp>
#include <boost/exception/diagnostic_information.hpp>

#include "logging.hpp"

#define GST_CAT_DEFAULT kurento_media_server
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoMediaServer"

const std::string DEFAULT_CONFIG_FILE = "/etc/kurento/kurento.conf.json";
const std::string ENV_PREFIX = "KURENTO_";

using namespace ::kurento;

static std::shared_ptr<Transport> transport;

__pid_t pid;

Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create ();

static gchar *tmp_dir;

Glib::RefPtr<Glib::IOChannel> channel;

static void
load_config (boost::property_tree::ptree &config, const std::string &file_name)
{
  boost::filesystem::path configFilePath (file_name);
  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  boost::property_tree::read_json (file_name, config);

  config.add ("configPath", configFilePath.parent_path().string() );
  pid = getpid();

  GST_INFO ("Configuration loaded successfully");

  std::shared_ptr<ServerMethods> serverMethods (new ServerMethods (config) );

  try {
    transport = TransportFactory::create_transport (config, serverMethods);
  } catch (std::exception &e) {
    GST_ERROR ("Error creating transport: %s", e.what() );
    exit (1);
  }
}

static void
signal_handler (uint32_t signo)
{
  static unsigned int __terminated = 0;

  switch (signo) {
  case SIGINT:
  case SIGTERM:
    if (__terminated == 0) {
      GST_DEBUG ("Terminating.");
      loop->quit ();
    }

    __terminated = 1;
    break;

  case SIGPIPE:
    GST_DEBUG ("Ignore sigpipe signal");
    break;

  case SIGSEGV:
    GST_DEBUG ("Segmentation fault. Aborting process execution");
    abort ();

  default:
    break;
  }
}

static int
delete_file (const char *fpath, const struct stat *sb, int typeflag,
             struct FTW *ftwbuf)
{
  int rv = g_remove (fpath);

  if (rv) {
    GST_WARNING ("Error deleting file: %s. %s", fpath, strerror (errno) );
  }

  return rv;
}
static void
remove_recursive (const gchar *path)
{
  nftw (path, delete_file, 64, FTW_DEPTH | FTW_PHYS);
}

static void
deleteCertificate ()
{
  // Only parent process can delete certificate
  if (pid != getpid() ) {
    return;
  }

  if (tmp_dir != NULL) {
    remove_recursive (tmp_dir);
    g_free (tmp_dir);
  }
}

static std::string
environment_adaptor (std::string &input)
{
  /* Look for KMS_ prefix and change to lower case */
  if (input.find (ENV_PREFIX) == 0) {
    std::string aux = input.substr (ENV_PREFIX.size() );
    std::transform (aux.begin(), aux.end(), aux.begin(), [] (int c) -> int {
      return (c == '_') ? '-' : tolower (c);
    });
    return aux;
  }

  return "";
}

int
main (int argc, char **argv)
{
  sigset_t mask;
  std::shared_ptr <SignalHandler> signalHandler;
  boost::property_tree::ptree config;
  std::string confFile;

  Glib::init();

  gst_init (&argc, &argv);
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);
  gst_debug_remove_log_function_by_data (NULL);
  gst_debug_add_log_function (log_function, NULL, NULL);

  try {
    boost::program_options::options_description desc ("kurento-media-server usage");

    desc.add_options()
    ("help,h", "Display this help message")
    ("version,v", "Display the version number")
    ("conf-file,f", boost::program_options::value<std::string>
     (&confFile)->default_value (DEFAULT_CONFIG_FILE),
     "Configuration file location");

    boost::program_options::variables_map vm;
    boost::program_options::store (boost::program_options::parse_command_line (argc,
                                   argv, desc), vm);
    boost::program_options::store (boost::program_options::parse_environment (desc,
                                   &environment_adaptor), vm);
    boost::program_options::notify (vm);

    if (vm.count ("help") ) {
      std::cout << desc << "\n";
      exit (0);
    }

  } catch (boost::program_options::error &e) {
    std::cerr <<  "Error : " << e.what() << std::endl;
    exit (1);
  }

  /* Install our signal handler */
  sigemptyset (&mask);
  sigaddset (&mask, SIGINT);
  sigaddset (&mask, SIGTERM);
  sigaddset (&mask, SIGSEGV);
  sigaddset (&mask, SIGPIPE);
  signalHandler = std::shared_ptr <SignalHandler> (new SignalHandler (mask,
                  signal_handler) );

  GST_INFO ("Kmsc version: %s", get_version () );

  load_config (config, confFile);

  /* Start transport */
  transport->start ();

  GST_INFO ("Mediaserver started");

  loop->run ();

  signalHandler.reset();

  deleteCertificate ();

  transport->stop();

  transport.reset();

  return 0;
}
<|endoftext|>


<commit_before>#include "RGTopFrame.h"

#include "RGBrowser.h"
#include "RGEditor.h"

#include <Reve/EventBase.h>
#include "VSDSelector.h"

#include <TGMenu.h>
#include <TGTab.h>
#include <TGToolBar.h>
#include <TGLabel.h>
#include <TGTextEntry.h>
#include <TGSplitter.h>
#include <TRootEmbeddedCanvas.h>
#include <TGMimeTypes.h>

#include <TGLSAViewer.h>
#include <TH1F.h>
#include <TView.h>

#include <TROOT.h>
#include <TFile.h>
#include <TMacro.h>
#include <TFolder.h>
#include <TStyle.h>
#include <TPad.h>
#include <TCanvas.h>
#include <TSystem.h>
#include <TRint.h>
#include <TVirtualX.h>
#include <TPolyLine3D.h>
#include <TPolyMarker3D.h>
#include <TEnv.h>
#include <TStyle.h>
#include <KeySymbols.h>
#include "TVirtualGL.h"
#include "TPluginManager.h"

#include <iostream>

using namespace Reve;
using namespace Reve;

Reve::RGTopFrame* gReve = 0;

/**************************************************************************/

RGTopFrame::RGTopFrame(const TGWindow *p, UInt_t w, UInt_t h, LookType_e look) :
  TGMainFrame(p, w, h),

  fMasterFrame (0),
  fMasterTab   (0),
  fGLCanvas    (0),
  fSelector    (0),
  fBrowser     (0),
  fStatusBar   (0),
  fVSDFile     (""),

  fMacroFolder(0),
  fEditor (0),

  fCurrentEvent   (0),
  fGlobalStore    (0),

  fRedrawDisabled (0),
  fResetCameras   (kFALSE),
  fTimerActive    (kFALSE),
  fRedrawTimer    (),

  fLook           (LT_Editor),
  fGeometries     ()
{
  gReve = this;
  fRedrawTimer.Connect("Timeout()", "Reve::RGTopFrame", this, "DoRedraw3D()");
  fMacroFolder = new TFolder("EVE", "Visualization macros");
  gROOT->GetListOfBrowsables()->Add(fMacroFolder);

  fClient->GetMimeTypeList()->AddType("root/tmacro", "Reve::RMacro",
                                      "tmacro_s.xpm", "tmacro_t.xpm", "");

  // Build GUI

  TGLayoutHints *lay0 = new TGLayoutHints(kLHintsCenterX | kLHintsCenterY | kLHintsExpandY | kLHintsExpandX);
  TGLayoutHints *lay1 = new TGLayoutHints(kLHintsCenterX | kLHintsCenterY | kLHintsExpandY | kLHintsExpandX, 2, 0, 2, 2);
  TGLayoutHints *lay2 = new TGLayoutHints(kLHintsTop | kLHintsLeft | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2);

  fMasterFrame = new TGCompositeFrame(this, w, h, kHorizontalFrame | kRaisedFrame);
  TGVerticalFrame* fV2 = new TGVerticalFrame(fMasterFrame, GetWidth()-40, GetHeight()-40, kSunkenFrame);

  fMasterFrame->AddFrame(fV2, lay1);

  fMasterTab = new TGTab(fV2, GetWidth(), GetHeight());

  // browser tab
  TGCompositeFrame* tframe1 = fMasterTab->AddTab("Object Browser");
  fBrowser = new RGBrowser(tframe1, w, h);
  tframe1->AddFrame(fBrowser, lay2);

  // tree selection tab
  TGCompositeFrame* tframe2 = fMasterTab->AddTab("Tree Selections");
  fSelector = new VSDSelector(tframe2);

  // gl-canvas
  Reve::PushPad();
  TGCompositeFrame* tframe3 = fMasterTab->AddTab("GLCanvas");
  TRootEmbeddedCanvas* ecanvas3 = new TRootEmbeddedCanvas("GLCanvas", tframe3, 580, 360);
  tframe3->AddFrame(ecanvas3, lay2);
  fGLCanvas = ecanvas3->GetCanvas();
  fGLCanvas->SetEditable(kFALSE);
  Reve::PopPad();

  fV2->AddFrame(fMasterTab, lay0);
  AddFrame(fMasterFrame, lay0);

  // Create status bar
  Int_t parts[] = {45, 45, 10};
  fStatusBar = new TGStatusBar(this, 50, 10, kHorizontalFrame);
  fStatusBar->SetParts(parts, 3);
  TGLayoutHints* lay6 = new TGLayoutHints(kLHintsBottom| kLHintsExpandX, 0, 0, 0, 0);
  AddFrame(fStatusBar, lay6);
  fStatusBar->SetText("GUI created", 0);

  MapSubwindows();

  /**************************************************************************/
  /**************************************************************************/

  switch(look) {
  case LT_Classic: {
    fBrowser->SetupClassicLook(fEditor, fGLCanvas);
    fGLCanvas->GetViewer3D("ogl");
    break;
  }

  case LT_Editor: {
    fBrowser->SetupEditorLook(fEditor, fGLCanvas);
    fGLCanvas->GetViewer3D("ogl");
    break;
  }

  case LT_GLViewer: {
    fBrowser->SetupGLViewerLook(fEditor, fGLCanvas);
    break;
  }

  default: {
    printf("RGTopFrame unknown look-type, ignoring.\n");
    break;
  }
  }

  TGLViewer* glv = dynamic_cast<TGLViewer*>(fGLCanvas->GetViewer3D());
  if(glv) {
    glv->SetSmartRefresh(kTRUE);
    glv->SetResetCamerasOnUpdate(kFALSE);
    glv->SetResetCameraOnDoubleClick(kFALSE);
    TGLSAViewer* glsav = dynamic_cast<TGLSAViewer*>(glv);
    if(glsav) {
      TGedEditor* e = glsav->GetGedEditor();
      e->SetModel(e->GetPad(), glsav, kButton1Down);
    }
  }

  /**************************************************************************/
  /**************************************************************************/

  fGlobalStore = new RenderElementList("Geometry", "");
  fGlobalStore->SetDenyDestroy(kTRUE);
  TGListTreeItem* glti = fGlobalStore->AddIntoListTree(GetListTree(), (TGListTreeItem*)0);
  GetListTree()->OpenItem(glti);
  DrawRenderElement(fGlobalStore);

  Resize(GetDefaultSize()); // this is used here to init layout algorithm
  SetWindowName("Reve");
  MapWindow();

  fEditor->DisplayObject(0);

  gSystem->ProcessEvents();
}

/**************************************************************************/

TCanvas* RGTopFrame::AddCanvasTab(const char* name)
{
  TGCompositeFrame    *f  = fMasterTab->AddTab(name);
  TRootEmbeddedCanvas *ec = new TRootEmbeddedCanvas(name, f, 580, 360);
  f->AddFrame(ec, new TGLayoutHints(kLHintsTop | kLHintsLeft | kLHintsExpandX | kLHintsExpandY,
				    2, 2, 2, 2));

  f->MapSubwindows();
  f->MapWindow();
  fMasterTab->GetTabTab(name)->MapWindow();
  fMasterTab->Layout();

  return ec->GetCanvas();
}

/**************************************************************************/

TGListTree* RGTopFrame::GetListTree() const
{
  return fBrowser->GetListTree();
}

/**************************************************************************/
// Macro management
/**************************************************************************/

TMacro* RGTopFrame::GetMacro(const Text_t* name) const
{
  return dynamic_cast<TMacro*>(fMacroFolder->FindObject(name));
}

/**************************************************************************/
// Editor
/**************************************************************************/

void RGTopFrame::EditRenderElement(RenderElement* rnr_element)
{
  static const Exc_t eH("RGTopFrame::EditRenderElement ");

  fEditor->DisplayRenderElement(rnr_element);
}

/**************************************************************************/
// 3D Pad management
/**************************************************************************/

void RGTopFrame::RegisterRedraw3D()
{
  fRedrawTimer.Start(0, kTRUE);
  fTimerActive = true;
}

void RGTopFrame::DoRedraw3D()
{
  // printf("RGTopFrame::DoRedraw3D redraw triggered\n");
  if (fResetCameras) {
    fGLCanvas->GetViewer3D()->ResetCamerasAfterNextUpdate();
    fResetCameras = kFALSE;
  }

  fGLCanvas->Modified();
  fGLCanvas->Update();
  fTimerActive = kFALSE;
}

/**************************************************************************/

int RGTopFrame::SpawnGuiAndRun(int argc, char **argv)
{
  LookType_e revemode = LT_Editor;
  Int_t w = 540;
  Int_t h = 500;
  if(argc >= 3 && (strcmp(argv[1], "-revemode")==0 || strcmp(argv[1], "-mode")==0)) {
    LookType_e m = LookType_e(atoi(argv[2]));
    if(m >= LT_Classic && m <= LT_GLViewer)
      revemode = m;
    printf("revemode = %d\n", revemode);
    if(revemode == LT_GLViewer) {
      w = 1024; h = 768;
    }
  }

  TRint theApp("App", &argc, argv);

  /* gReve = */ new RGTopFrame(gClient ? gClient->GetRoot() : 0, w, h, revemode);

 run_loop:
  try {
    theApp.Run();
  }
  catch(Exc_t& exc) {
    gReve->GetStatusBar()->SetText(exc.Data());
    fprintf(stderr, "Exception: %s\n", exc.Data());
    goto run_loop;
  }
  return 0;
}

void RGTopFrame::SpawnGui(LookType_e revemode)
{
  Int_t w = 540;
  Int_t h = 500;
  if(revemode == LT_GLViewer) {
    w = 1024; h = 768;
  }

  /* gReve = */ new RGTopFrame(gClient->GetRoot(), w, h, revemode);
}

/**************************************************************************/
/**************************************************************************/

TGListTreeItem* RGTopFrame::AddEvent(EventBase* event)
{
  fCurrentEvent = event;
  fCurrentEvent->SetDenyDestroy(kTRUE);
  TGListTreeItem* elti = event->AddIntoListTree(GetListTree(), (TGListTreeItem*)0);
  GetListTree()->OpenItem(elti);
  DrawRenderElement(event);
  return elti;
}

TGListTreeItem* RGTopFrame::AddRenderElement(RenderElement* rnr_element)
{
  return AddRenderElement(fCurrentEvent, rnr_element);
}

TGListTreeItem* RGTopFrame::AddRenderElement(RenderElement* parent,
					     RenderElement* rnr_element)
{
  static const Exc_t eH("RGTopFrame::AddRenderElement ");

  // Here could route rnr-element to several browsers/pads.

  RenderElementListBase* rel = dynamic_cast<RenderElementListBase*>(parent);
  if(rel)
    rel->AddElement(rnr_element);

  TGListTreeItem* newitem =
    rnr_element->AddIntoListTree(GetListTree(), parent);

  return newitem;
}

TGListTreeItem* RGTopFrame::AddGlobalRenderElement(RenderElement* rnr_element)
{
  return AddGlobalRenderElement(fGlobalStore, rnr_element);
}

TGListTreeItem* RGTopFrame::AddGlobalRenderElement(RenderElement* parent,
						   RenderElement* rnr_element)
{
  static const Exc_t eH("RGTopFrame::AddGlobalRenderElement ");

  // Here could route rnr-element to several browsers/pads.

  RenderElementListBase* rel = dynamic_cast<RenderElementListBase*>(parent);
  if(rel)
    rel->AddElement(rnr_element);

  TGListTreeItem* newitem =
    rnr_element->AddIntoListTree(GetListTree(), parent);

  return newitem;
}

/**************************************************************************/

void RGTopFrame::RemoveRenderElement(RenderElement* parent,
				     RenderElement* rnr_element)
{
  rnr_element->RemoveFromListTree(GetListTree());

  RenderElementListBase* rel = dynamic_cast<RenderElementListBase*>(parent);
  if(rel)
    rel->RemoveElement(rnr_element);
}

void RGTopFrame::PreDeleteRenderElement(RenderElement* rnr_element)
{
  if (fEditor->GetRnrElement() == rnr_element)
    fEditor->DisplayObject(0);
}

/**************************************************************************/

void RGTopFrame::DrawRenderElement(RenderElement* rnr_element, TVirtualPad* pad)
{
  if(pad == 0) pad = fGLCanvas;

  { Reve::PadHolder pHolder(false, pad);
    if (pad == fGLCanvas) fGLCanvas->SetEditable(kTRUE);
    rnr_element->GetObject()->Draw();
    if (pad == fGLCanvas) fGLCanvas->SetEditable(kFALSE);
  }
  Redraw3D();
}

void RGTopFrame::UndrawRenderElement(RenderElement* rnr_element, TVirtualPad* pad)
{
  if(pad == 0) pad = fGLCanvas;
  { Reve::PadHolder pHolder(false, pad);
    pad->GetListOfPrimitives()->Remove(rnr_element->GetObject());
  }
  Redraw3D();
}

/**************************************************************************/

void RGTopFrame::RenderElementChecked(TObject* obj, Bool_t state)
{
  // Item's user-data is blindly casted into TObject.
  // We recast it blindly back into the render element.

  RenderElement* rnrEl = (RenderElement*) obj;
  rnrEl->SetRnrElement(state);
  Redraw3D();
}

/**************************************************************************/

void RGTopFrame::NotifyBrowser(TGListTreeItem* parent_lti)
{
  TGListTree* l_tree = GetListTree();
  if(parent_lti)
    l_tree->OpenItem(parent_lti);
  gClient->NeedRedraw(l_tree);
}

void RGTopFrame::NotifyBrowser(RenderElement* parent)
{
  TGListTreeItem* parent_lti = parent ? parent->FindListTreeItem(GetListTree()) : 0;
  NotifyBrowser(parent_lti);
}

/**************************************************************************/
// GeoManager registration
/**************************************************************************/

TGeoManager* RGTopFrame::GetGeometry(const TString& filename)
{
  static const Exc_t eH("RGTopFrame::GetGeometry ");

  TString exp_filename = filename;
  gSystem->ExpandPathName(exp_filename);
  printf("%s loading: '%s' -> '%s'.\n", eH.Data(),
	 filename.Data(), exp_filename.Data());

  std::map<TString, TGeoManager*>::iterator g = fGeometries.find(filename);
  if(g != fGeometries.end()) {
    return g->second;
  } else {
    if(gSystem->AccessPathName(exp_filename, kReadPermission))
      throw(eH + "file '" + exp_filename + "' not readable.");
    gGeoManager = 0;
    TGeoManager::Import(filename);
    if(gGeoManager == 0)
      throw(eH + "GeoManager import failed.");
    gGeoManager->GetTopVolume()->VisibleDaughters(1);

    // Import colors exported by Gled, if they exist.
    {
      TFile f(exp_filename, "READ");
      TObjArray* collist = (TObjArray*) f.Get("ColorList");
      f.Close();
      if(collist != 0) {
	TSeqCollection* glist = gROOT->GetListOfColors();
	glist->Clear();
	glist->AddAll(collist);
      }
    }

    fGeometries[filename] = gGeoManager;
    return gGeoManager;
  }
}

/**************************************************************************/
// Testing exceptions
/**************************************************************************/

void RGTopFrame::ThrowException(const char* text)
{
  static const Exc_t eH("RGTopFrame::ThrowException ");

  throw(eH + text);
}
<commit_msg>Create a dummy event directory if none exists on AddRenderElement.<commit_after>#include "RGTopFrame.h"

#include "RGBrowser.h"
#include "RGEditor.h"

#include <Reve/EventBase.h>
#include "VSDSelector.h"

#include <TGMenu.h>
#include <TGTab.h>
#include <TGToolBar.h>
#include <TGLabel.h>
#include <TGTextEntry.h>
#include <TGSplitter.h>
#include <TRootEmbeddedCanvas.h>
#include <TGMimeTypes.h>

#include <TGLSAViewer.h>
#include <TH1F.h>
#include <TView.h>

#include <TROOT.h>
#include <TFile.h>
#include <TMacro.h>
#include <TFolder.h>
#include <TStyle.h>
#include <TPad.h>
#include <TCanvas.h>
#include <TSystem.h>
#include <TRint.h>
#include <TVirtualX.h>
#include <TPolyLine3D.h>
#include <TPolyMarker3D.h>
#include <TEnv.h>
#include <TStyle.h>
#include <KeySymbols.h>
#include "TVirtualGL.h"
#include "TPluginManager.h"

#include <iostream>

using namespace Reve;
using namespace Reve;

Reve::RGTopFrame* gReve = 0;

/**************************************************************************/

RGTopFrame::RGTopFrame(const TGWindow *p, UInt_t w, UInt_t h, LookType_e look) :
  TGMainFrame(p, w, h),

  fMasterFrame (0),
  fMasterTab   (0),
  fGLCanvas    (0),
  fSelector    (0),
  fBrowser     (0),
  fStatusBar   (0),
  fVSDFile     (""),

  fMacroFolder(0),
  fEditor (0),

  fCurrentEvent   (0),
  fGlobalStore    (0),

  fRedrawDisabled (0),
  fResetCameras   (kFALSE),
  fTimerActive    (kFALSE),
  fRedrawTimer    (),

  fLook           (LT_Editor),
  fGeometries     ()
{
  gReve = this;
  fRedrawTimer.Connect("Timeout()", "Reve::RGTopFrame", this, "DoRedraw3D()");
  fMacroFolder = new TFolder("EVE", "Visualization macros");
  gROOT->GetListOfBrowsables()->Add(fMacroFolder);

  fClient->GetMimeTypeList()->AddType("root/tmacro", "Reve::RMacro",
                                      "tmacro_s.xpm", "tmacro_t.xpm", "");

  // Build GUI

  TGLayoutHints *lay0 = new TGLayoutHints(kLHintsCenterX | kLHintsCenterY | kLHintsExpandY | kLHintsExpandX);
  TGLayoutHints *lay1 = new TGLayoutHints(kLHintsCenterX | kLHintsCenterY | kLHintsExpandY | kLHintsExpandX, 2, 0, 2, 2);
  TGLayoutHints *lay2 = new TGLayoutHints(kLHintsTop | kLHintsLeft | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2);

  fMasterFrame = new TGCompositeFrame(this, w, h, kHorizontalFrame | kRaisedFrame);
  TGVerticalFrame* fV2 = new TGVerticalFrame(fMasterFrame, GetWidth()-40, GetHeight()-40, kSunkenFrame);

  fMasterFrame->AddFrame(fV2, lay1);

  fMasterTab = new TGTab(fV2, GetWidth(), GetHeight());

  // browser tab
  TGCompositeFrame* tframe1 = fMasterTab->AddTab("Object Browser");
  fBrowser = new RGBrowser(tframe1, w, h);
  tframe1->AddFrame(fBrowser, lay2);

  // tree selection tab
  TGCompositeFrame* tframe2 = fMasterTab->AddTab("Tree Selections");
  fSelector = new VSDSelector(tframe2);

  // gl-canvas
  Reve::PushPad();
  TGCompositeFrame* tframe3 = fMasterTab->AddTab("GLCanvas");
  TRootEmbeddedCanvas* ecanvas3 = new TRootEmbeddedCanvas("GLCanvas", tframe3, 580, 360);
  tframe3->AddFrame(ecanvas3, lay2);
  fGLCanvas = ecanvas3->GetCanvas();
  fGLCanvas->SetEditable(kFALSE);
  Reve::PopPad();

  fV2->AddFrame(fMasterTab, lay0);
  AddFrame(fMasterFrame, lay0);

  // Create status bar
  Int_t parts[] = {45, 45, 10};
  fStatusBar = new TGStatusBar(this, 50, 10, kHorizontalFrame);
  fStatusBar->SetParts(parts, 3);
  TGLayoutHints* lay6 = new TGLayoutHints(kLHintsBottom| kLHintsExpandX, 0, 0, 0, 0);
  AddFrame(fStatusBar, lay6);
  fStatusBar->SetText("GUI created", 0);

  MapSubwindows();

  /**************************************************************************/
  /**************************************************************************/

  switch(look) {
  case LT_Classic: {
    fBrowser->SetupClassicLook(fEditor, fGLCanvas);
    fGLCanvas->GetViewer3D("ogl");
    break;
  }

  case LT_Editor: {
    fBrowser->SetupEditorLook(fEditor, fGLCanvas);
    fGLCanvas->GetViewer3D("ogl");
    break;
  }

  case LT_GLViewer: {
    fBrowser->SetupGLViewerLook(fEditor, fGLCanvas);
    break;
  }

  default: {
    printf("RGTopFrame unknown look-type, ignoring.\n");
    break;
  }
  }

  TGLViewer* glv = dynamic_cast<TGLViewer*>(fGLCanvas->GetViewer3D());
  if(glv) {
    glv->SetSmartRefresh(kTRUE);
    glv->SetResetCamerasOnUpdate(kFALSE);
    glv->SetResetCameraOnDoubleClick(kFALSE);
    TGLSAViewer* glsav = dynamic_cast<TGLSAViewer*>(glv);
    if(glsav) {
      TGedEditor* e = glsav->GetGedEditor();
      e->SetModel(e->GetPad(), glsav, kButton1Down);
    }
  }

  /**************************************************************************/
  /**************************************************************************/

  fGlobalStore = new RenderElementList("Geometry", "");
  fGlobalStore->SetDenyDestroy(kTRUE);
  TGListTreeItem* glti = fGlobalStore->AddIntoListTree(GetListTree(), (TGListTreeItem*)0);
  GetListTree()->OpenItem(glti);
  DrawRenderElement(fGlobalStore);

  Resize(GetDefaultSize()); // this is used here to init layout algorithm
  SetWindowName("Reve");
  MapWindow();

  fEditor->DisplayObject(0);

  gSystem->ProcessEvents();
}

/**************************************************************************/

TCanvas* RGTopFrame::AddCanvasTab(const char* name)
{
  TGCompositeFrame    *f  = fMasterTab->AddTab(name);
  TRootEmbeddedCanvas *ec = new TRootEmbeddedCanvas(name, f, 580, 360);
  f->AddFrame(ec, new TGLayoutHints(kLHintsTop | kLHintsLeft | kLHintsExpandX | kLHintsExpandY,
				    2, 2, 2, 2));

  f->MapSubwindows();
  f->MapWindow();
  fMasterTab->GetTabTab(name)->MapWindow();
  fMasterTab->Layout();

  return ec->GetCanvas();
}

/**************************************************************************/

TGListTree* RGTopFrame::GetListTree() const
{
  return fBrowser->GetListTree();
}

/**************************************************************************/
// Macro management
/**************************************************************************/

TMacro* RGTopFrame::GetMacro(const Text_t* name) const
{
  return dynamic_cast<TMacro*>(fMacroFolder->FindObject(name));
}

/**************************************************************************/
// Editor
/**************************************************************************/

void RGTopFrame::EditRenderElement(RenderElement* rnr_element)
{
  static const Exc_t eH("RGTopFrame::EditRenderElement ");

  fEditor->DisplayRenderElement(rnr_element);
}

/**************************************************************************/
// 3D Pad management
/**************************************************************************/

void RGTopFrame::RegisterRedraw3D()
{
  fRedrawTimer.Start(0, kTRUE);
  fTimerActive = true;
}

void RGTopFrame::DoRedraw3D()
{
  // printf("RGTopFrame::DoRedraw3D redraw triggered\n");
  if (fResetCameras) {
    fGLCanvas->GetViewer3D()->ResetCamerasAfterNextUpdate();
    fResetCameras = kFALSE;
  }

  fGLCanvas->Modified();
  fGLCanvas->Update();
  fTimerActive = kFALSE;
}

/**************************************************************************/

int RGTopFrame::SpawnGuiAndRun(int argc, char **argv)
{
  LookType_e revemode = LT_Editor;
  Int_t w = 540;
  Int_t h = 500;
  if(argc >= 3 && (strcmp(argv[1], "-revemode")==0 || strcmp(argv[1], "-mode")==0)) {
    LookType_e m = LookType_e(atoi(argv[2]));
    if(m >= LT_Classic && m <= LT_GLViewer)
      revemode = m;
    printf("revemode = %d\n", revemode);
    if(revemode == LT_GLViewer) {
      w = 1024; h = 768;
    }
  }

  TRint theApp("App", &argc, argv);

  /* gReve = */ new RGTopFrame(gClient ? gClient->GetRoot() : 0, w, h, revemode);

 run_loop:
  try {
    theApp.Run();
  }
  catch(Exc_t& exc) {
    gReve->GetStatusBar()->SetText(exc.Data());
    fprintf(stderr, "Exception: %s\n", exc.Data());
    goto run_loop;
  }
  return 0;
}

void RGTopFrame::SpawnGui(LookType_e revemode)
{
  Int_t w = 540;
  Int_t h = 500;
  if(revemode == LT_GLViewer) {
    w = 1024; h = 768;
  }

  /* gReve = */ new RGTopFrame(gClient->GetRoot(), w, h, revemode);
}

/**************************************************************************/
/**************************************************************************/

TGListTreeItem* RGTopFrame::AddEvent(EventBase* event)
{
  fCurrentEvent = event;
  fCurrentEvent->SetDenyDestroy(kTRUE);
  TGListTreeItem* elti = event->AddIntoListTree(GetListTree(), (TGListTreeItem*)0);
  GetListTree()->OpenItem(elti);
  DrawRenderElement(event);
  return elti;
}

TGListTreeItem* RGTopFrame::AddRenderElement(RenderElement* rnr_element)
{
  if (fCurrentEvent == 0)
    AddEvent(new EventBase("Event", "Auto-created event directory"));
  return AddRenderElement(fCurrentEvent, rnr_element);
}

TGListTreeItem* RGTopFrame::AddRenderElement(RenderElement* parent,
					     RenderElement* rnr_element)
{
  static const Exc_t eH("RGTopFrame::AddRenderElement ");

  // Here could route rnr-element to several browsers/pads.

  RenderElementListBase* rel = dynamic_cast<RenderElementListBase*>(parent);
  if(rel)
    rel->AddElement(rnr_element);

  TGListTreeItem* newitem =
    rnr_element->AddIntoListTree(GetListTree(), parent);

  return newitem;
}

TGListTreeItem* RGTopFrame::AddGlobalRenderElement(RenderElement* rnr_element)
{
  return AddGlobalRenderElement(fGlobalStore, rnr_element);
}

TGListTreeItem* RGTopFrame::AddGlobalRenderElement(RenderElement* parent,
						   RenderElement* rnr_element)
{
  static const Exc_t eH("RGTopFrame::AddGlobalRenderElement ");

  // Here could route rnr-element to several browsers/pads.

  RenderElementListBase* rel = dynamic_cast<RenderElementListBase*>(parent);
  if(rel)
    rel->AddElement(rnr_element);

  TGListTreeItem* newitem =
    rnr_element->AddIntoListTree(GetListTree(), parent);

  return newitem;
}

/**************************************************************************/

void RGTopFrame::RemoveRenderElement(RenderElement* parent,
				     RenderElement* rnr_element)
{
  rnr_element->RemoveFromListTree(GetListTree());

  RenderElementListBase* rel = dynamic_cast<RenderElementListBase*>(parent);
  if(rel)
    rel->RemoveElement(rnr_element);
}

void RGTopFrame::PreDeleteRenderElement(RenderElement* rnr_element)
{
  if (fEditor->GetRnrElement() == rnr_element)
    fEditor->DisplayObject(0);
}

/**************************************************************************/

void RGTopFrame::DrawRenderElement(RenderElement* rnr_element, TVirtualPad* pad)
{
  if(pad == 0) pad = fGLCanvas;

  { Reve::PadHolder pHolder(false, pad);
    if (pad == fGLCanvas) fGLCanvas->SetEditable(kTRUE);
    rnr_element->GetObject()->Draw();
    if (pad == fGLCanvas) fGLCanvas->SetEditable(kFALSE);
  }
  Redraw3D();
}

void RGTopFrame::UndrawRenderElement(RenderElement* rnr_element, TVirtualPad* pad)
{
  if(pad == 0) pad = fGLCanvas;
  { Reve::PadHolder pHolder(false, pad);
    pad->GetListOfPrimitives()->Remove(rnr_element->GetObject());
  }
  Redraw3D();
}

/**************************************************************************/

void RGTopFrame::RenderElementChecked(TObject* obj, Bool_t state)
{
  // Item's user-data is blindly casted into TObject.
  // We recast it blindly back into the render element.

  RenderElement* rnrEl = (RenderElement*) obj;
  rnrEl->SetRnrElement(state);
  Redraw3D();
}

/**************************************************************************/

void RGTopFrame::NotifyBrowser(TGListTreeItem* parent_lti)
{
  TGListTree* l_tree = GetListTree();
  if(parent_lti)
    l_tree->OpenItem(parent_lti);
  gClient->NeedRedraw(l_tree);
}

void RGTopFrame::NotifyBrowser(RenderElement* parent)
{
  TGListTreeItem* parent_lti = parent ? parent->FindListTreeItem(GetListTree()) : 0;
  NotifyBrowser(parent_lti);
}

/**************************************************************************/
// GeoManager registration
/**************************************************************************/

TGeoManager* RGTopFrame::GetGeometry(const TString& filename)
{
  static const Exc_t eH("RGTopFrame::GetGeometry ");

  TString exp_filename = filename;
  gSystem->ExpandPathName(exp_filename);
  printf("%s loading: '%s' -> '%s'.\n", eH.Data(),
	 filename.Data(), exp_filename.Data());

  std::map<TString, TGeoManager*>::iterator g = fGeometries.find(filename);
  if(g != fGeometries.end()) {
    return g->second;
  } else {
    if(gSystem->AccessPathName(exp_filename, kReadPermission))
      throw(eH + "file '" + exp_filename + "' not readable.");
    gGeoManager = 0;
    TGeoManager::Import(filename);
    if(gGeoManager == 0)
      throw(eH + "GeoManager import failed.");
    gGeoManager->GetTopVolume()->VisibleDaughters(1);

    // Import colors exported by Gled, if they exist.
    {
      TFile f(exp_filename, "READ");
      TObjArray* collist = (TObjArray*) f.Get("ColorList");
      f.Close();
      if(collist != 0) {
	TSeqCollection* glist = gROOT->GetListOfColors();
	glist->Clear();
	glist->AddAll(collist);
      }
    }

    fGeometries[filename] = gGeoManager;
    return gGeoManager;
  }
}

/**************************************************************************/
// Testing exceptions
/**************************************************************************/

void RGTopFrame::ThrowException(const char* text)
{
  static const Exc_t eH("RGTopFrame::ThrowException ");

  throw(eH + text);
}
<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2012-2015 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include "imagemixer.h"
#include <modules/opengl/texture/textureunit.h>
#include <modules/opengl/texture/textureutils.h>
#include <modules/opengl/shader/shaderutils.h>

namespace inviwo {

const ProcessorInfo ImageMixer::processorInfo_{
    "org.inviwo.ImageMixer",  // Class identifier
    "Image Mixer",            // Display name
    "Image Operation",        // Category
    CodeState::Stable,        // Code state
    Tags::GL,                 // Tags
};
const ProcessorInfo ImageMixer::getProcessorInfo() const {
    return processorInfo_;
}

ImageMixer::ImageMixer()
    : Processor()
    , inport0_("inport0")
    , inport1_("inport1")
    , outport_("outport")
    , blendingMode_("blendMode", "Blend Mode", InvalidationLevel::InvalidResources)
    , weight_("weight", "Weight", 0.5f, 0.0f, 1.0f)
    , shader_("img_mix.frag", false) {

    shader_.onReload([this]() { invalidate(InvalidationLevel::InvalidResources); });

    addPort(inport0_);
    addPort(inport1_);
    addPort(outport_);

    blendingMode_.addOption("mix", "Mix", BlendModes::Mix);
    blendingMode_.addOption("over", "Over", BlendModes::Over);
    blendingMode_.addOption("multiply", "Multiply", BlendModes::Multiply);
    blendingMode_.addOption("screen", "Screen", BlendModes::Screen);
    blendingMode_.addOption("overlay", "Overlay", BlendModes::Overlay);
    blendingMode_.addOption("hardlight", "Hard Light", BlendModes::HardLight);
    blendingMode_.addOption("divide", "Divide", BlendModes::Divide);
    blendingMode_.addOption("addition", "Addition", BlendModes::Addition);
    blendingMode_.addOption("subtraction", "Subtraction", BlendModes::Subtraction);
    blendingMode_.addOption("difference", "Difference", BlendModes::Difference);
    blendingMode_.addOption("darkenonly", "DarkenOnly (min)", BlendModes::DarkenOnly);
    blendingMode_.addOption("brightenonly", "BrightenOnly (max)", BlendModes::BrightenOnly);
    blendingMode_.setSelectedValue(BlendModes::Mix);
    blendingMode_.setCurrentStateAsDefault();

    addProperty(blendingMode_);
    addProperty(weight_);
}

ImageMixer::~ImageMixer() {}

void ImageMixer::process() {
    if (inport0_.isChanged()) {
        const DataFormatBase* format = inport0_.getData()->getDataFormat();
        size2_t dimensions = inport0_.getData()->getDimensions();
        if (!outport_.hasData() || format != outport_.getData()->getDataFormat() ||
            dimensions != outport_.getData()->getDimensions()) {
            Image* img = new Image(dimensions, format);
            img->copyMetaDataFrom(*inport0_.getData());
            outport_.setData(img);
        }
    }

    utilgl::activateAndClearTarget(outport_);
    shader_.activate();
    TextureUnitContainer units;
    utilgl::bindAndSetUniforms(shader_, units, inport0_, ImageType::ColorDepthPicking);
    utilgl::bindAndSetUniforms(shader_, units, inport1_, ImageType::ColorDepthPicking);
    utilgl::setUniforms(shader_, outport_, weight_);
    utilgl::singleDrawImagePlaneRect();
    shader_.deactivate();
    utilgl::deactivateCurrentTarget();
}

void ImageMixer::initializeResources() {
    // compositing defines
    std::string compositingKey = "COLOR_BLENDING(colorA, colorB)";
    std::string compositingValue = "";

    switch (blendingMode_.get()) {
        case BlendModes::Over:  //<! f(a,b) = b, b over a, regular front-to-back blending
            compositingValue = "over(colorA, colorB)";
            break;
        case BlendModes::Multiply:  //!< f(a,b) = a * b
            compositingValue = "multiply(colorA, colorB)";
            break;
        case BlendModes::Screen:  //!< f(a,b) = 1 - (1 - a) * (1 - b)
            compositingValue = "screen(colorA, colorB)";
            break;
        case BlendModes::Overlay:  //!< f(a,b) = 2 * a *b, if a < 0.5,   f(a,b) = 1 - 2(1 - a)(1 -
                                   //b), otherwise (combination of Multiply and Screen)
            compositingValue = "overlay(colorA, colorB)";
            break;
        case BlendModes::HardLight:  //!< Overlay where a and b are swapped
            compositingValue = "overlay(colorB, colorA)";
            break;
        case BlendModes::Divide:  //!< f(a,b) = a/b
            compositingValue = "divide(colorA, colorB)";
            break;
        case BlendModes::Addition:  //!< f(a,b) = a + b, clamped to [0,1]
            compositingValue = "addition(colorA, colorB)";
            break;
        case BlendModes::Subtraction:  //!< f(a,b) = a - b, clamped to [0,1]
            compositingValue = "subtraction(colorA, colorB)";
            break;
        case BlendModes::Difference:  //!< f(a,b) = |a - b|
            compositingValue = "difference(colorA, colorB)";
            break;
        case BlendModes::DarkenOnly:  //!< f(a,b) = min(a, b), per component
            compositingValue = "darkenOnly(colorA, colorB)";
            break;
        case BlendModes::BrightenOnly:  //!< f(a,b) = max(a, b), per component
            compositingValue = "brightenOnly(colorA, colorB)";
            break;
        case BlendModes::Mix:  //!< f(a,b) = a * (1 - alpha) + b * alpha
        default:
            compositingValue = "colorB";
            break;
    }

    shader_.getFragmentShaderObject()->addShaderDefine(compositingKey, compositingValue);
    shader_.build();
}

}  // namespace

<commit_msg>BaseGL: ImageMixer, make sure the outport format is the "larger" one of the inputs<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2012-2015 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include "imagemixer.h"
#include <modules/opengl/texture/textureunit.h>
#include <modules/opengl/texture/textureutils.h>
#include <modules/opengl/shader/shaderutils.h>

namespace inviwo {

const ProcessorInfo ImageMixer::processorInfo_{
    "org.inviwo.ImageMixer",  // Class identifier
    "Image Mixer",            // Display name
    "Image Operation",        // Category
    CodeState::Stable,        // Code state
    Tags::GL,                 // Tags
};
const ProcessorInfo ImageMixer::getProcessorInfo() const {
    return processorInfo_;
}

ImageMixer::ImageMixer()
    : Processor()
    , inport0_("inport0")
    , inport1_("inport1")
    , outport_("outport")
    , blendingMode_("blendMode", "Blend Mode", InvalidationLevel::InvalidResources)
    , weight_("weight", "Weight", 0.5f, 0.0f, 1.0f)
    , shader_("img_mix.frag", false) {

    shader_.onReload([this]() { invalidate(InvalidationLevel::InvalidResources); });

    addPort(inport0_);
    addPort(inport1_);
    addPort(outport_);

    blendingMode_.addOption("mix", "Mix", BlendModes::Mix);
    blendingMode_.addOption("over", "Over", BlendModes::Over);
    blendingMode_.addOption("multiply", "Multiply", BlendModes::Multiply);
    blendingMode_.addOption("screen", "Screen", BlendModes::Screen);
    blendingMode_.addOption("overlay", "Overlay", BlendModes::Overlay);
    blendingMode_.addOption("hardlight", "Hard Light", BlendModes::HardLight);
    blendingMode_.addOption("divide", "Divide", BlendModes::Divide);
    blendingMode_.addOption("addition", "Addition", BlendModes::Addition);
    blendingMode_.addOption("subtraction", "Subtraction", BlendModes::Subtraction);
    blendingMode_.addOption("difference", "Difference", BlendModes::Difference);
    blendingMode_.addOption("darkenonly", "DarkenOnly (min)", BlendModes::DarkenOnly);
    blendingMode_.addOption("brightenonly", "BrightenOnly (max)", BlendModes::BrightenOnly);
    blendingMode_.setSelectedValue(BlendModes::Mix);
    blendingMode_.setCurrentStateAsDefault();

    addProperty(blendingMode_);
    addProperty(weight_);
}

ImageMixer::~ImageMixer() {}

void ImageMixer::process() {
    if (inport0_.isChanged() || inport1_.isChanged()) {
        auto format0 = inport0_.getData()->getDataFormat();
        auto format1 = inport1_.getData()->getDataFormat();
        auto format = format0->getSize() > format1->getSize() ? format0 : format1;
        if (format != outport_.getData()->getDataFormat()) {
            auto dimensions = outport_.getData()->getDimensions();
            auto img = std::make_shared<Image>(dimensions, format);
            img->copyMetaDataFrom(*inport0_.getData());
            outport_.setData(img);
        }
    }

    utilgl::activateAndClearTarget(outport_);
    shader_.activate();
    TextureUnitContainer units;
    utilgl::bindAndSetUniforms(shader_, units, inport0_, ImageType::ColorDepthPicking);
    utilgl::bindAndSetUniforms(shader_, units, inport1_, ImageType::ColorDepthPicking);
    utilgl::setUniforms(shader_, outport_, weight_);
    utilgl::singleDrawImagePlaneRect();
    shader_.deactivate();
    utilgl::deactivateCurrentTarget();
}

void ImageMixer::initializeResources() {
    // compositing defines
    std::string compositingKey = "COLOR_BLENDING(colorA, colorB)";
    std::string compositingValue = "";

    switch (blendingMode_.get()) {
        case BlendModes::Over:  //<! f(a,b) = b, b over a, regular front-to-back blending
            compositingValue = "over(colorA, colorB)";
            break;
        case BlendModes::Multiply:  //!< f(a,b) = a * b
            compositingValue = "multiply(colorA, colorB)";
            break;
        case BlendModes::Screen:  //!< f(a,b) = 1 - (1 - a) * (1 - b)
            compositingValue = "screen(colorA, colorB)";
            break;
        case BlendModes::Overlay:  //!< f(a,b) = 2 * a *b, if a < 0.5,   f(a,b) = 1 - 2(1 - a)(1 -
                                   //b), otherwise (combination of Multiply and Screen)
            compositingValue = "overlay(colorA, colorB)";
            break;
        case BlendModes::HardLight:  //!< Overlay where a and b are swapped
            compositingValue = "overlay(colorB, colorA)";
            break;
        case BlendModes::Divide:  //!< f(a,b) = a/b
            compositingValue = "divide(colorA, colorB)";
            break;
        case BlendModes::Addition:  //!< f(a,b) = a + b, clamped to [0,1]
            compositingValue = "addition(colorA, colorB)";
            break;
        case BlendModes::Subtraction:  //!< f(a,b) = a - b, clamped to [0,1]
            compositingValue = "subtraction(colorA, colorB)";
            break;
        case BlendModes::Difference:  //!< f(a,b) = |a - b|
            compositingValue = "difference(colorA, colorB)";
            break;
        case BlendModes::DarkenOnly:  //!< f(a,b) = min(a, b), per component
            compositingValue = "darkenOnly(colorA, colorB)";
            break;
        case BlendModes::BrightenOnly:  //!< f(a,b) = max(a, b), per component
            compositingValue = "brightenOnly(colorA, colorB)";
            break;
        case BlendModes::Mix:  //!< f(a,b) = a * (1 - alpha) + b * alpha
        default:
            compositingValue = "colorB";
            break;
    }

    shader_.getFragmentShaderObject()->addShaderDefine(compositingKey, compositingValue);
    shader_.build();
}

}  // namespace

<|endoftext|>


<commit_before>/*************************************************************************/
/*  image_compress_squish.cpp                                            */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "image_compress_squish.h"

#include "print_string.h"

#include <squish.h>

void image_compress_squish(Image *p_image) {

	int w = p_image->get_width();
	int h = p_image->get_height();

	if (!p_image->has_mipmaps()) {
		ERR_FAIL_COND(!w || w % 4 != 0);
		ERR_FAIL_COND(!h || h % 4 != 0);
	} else {
		ERR_FAIL_COND(!w || w != nearest_power_of_2(w));
		ERR_FAIL_COND(!h || h != nearest_power_of_2(h));
	};

	if (p_image->get_format() >= Image::FORMAT_DXT1)
		return; //do not compress, already compressed

	int shift = 0;
	int squish_comp = squish::kColourRangeFit;
	Image::Format target_format;

	if (p_image->get_format() == Image::FORMAT_LA8) {
		//compressed normalmap
		target_format = Image::FORMAT_DXT5;
		squish_comp |= squish::kDxt5;
	} else if (p_image->detect_alpha() != Image::ALPHA_NONE) {

		target_format = Image::FORMAT_DXT3;
		squish_comp |= squish::kDxt3;
	} else {
		target_format = Image::FORMAT_DXT1;
		shift = 1;
		squish_comp |= squish::kDxt1;
	}

	p_image->convert(Image::FORMAT_RGBA8); //always expects rgba

	PoolVector<uint8_t> data;
	int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0);
	int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0;
	data.resize(target_size);

	PoolVector<uint8_t>::Read rb = p_image->get_data().read();
	PoolVector<uint8_t>::Write wb = data.write();

	int dst_ofs = 0;

	for (int i = 0; i <= mm_count; i++) {

		int src_ofs = p_image->get_mipmap_offset(i);
		squish::CompressImage(&rb[src_ofs], w, h, &wb[dst_ofs], squish_comp);
		dst_ofs += (MAX(4, w) * MAX(4, h)) >> shift;
		w >>= 1;
		h >>= 1;
	}

	rb = PoolVector<uint8_t>::Read();
	wb = PoolVector<uint8_t>::Write();

	p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
<commit_msg>Detect SSE/SSE2 for libsquish.<commit_after>/*************************************************************************/
/*  image_compress_squish.cpp                                            */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "image_compress_squish.h"

#include "print_string.h"

#if defined(__SSE2__)
#define SQUISH_USE_SSE 2
#elif defined(__SSE__)
#define SQUISH_USE_SSE 1
#endif

#include <squish.h>

void image_compress_squish(Image *p_image) {

	int w = p_image->get_width();
	int h = p_image->get_height();

	if (!p_image->has_mipmaps()) {
		ERR_FAIL_COND(!w || w % 4 != 0);
		ERR_FAIL_COND(!h || h % 4 != 0);
	} else {
		ERR_FAIL_COND(!w || w != nearest_power_of_2(w));
		ERR_FAIL_COND(!h || h != nearest_power_of_2(h));
	};

	if (p_image->get_format() >= Image::FORMAT_DXT1)
		return; //do not compress, already compressed

	int shift = 0;
	int squish_comp = squish::kColourRangeFit;
	Image::Format target_format;

	if (p_image->get_format() == Image::FORMAT_LA8) {
		//compressed normalmap
		target_format = Image::FORMAT_DXT5;
		squish_comp |= squish::kDxt5;
	} else if (p_image->detect_alpha() != Image::ALPHA_NONE) {

		target_format = Image::FORMAT_DXT3;
		squish_comp |= squish::kDxt3;
	} else {
		target_format = Image::FORMAT_DXT1;
		shift = 1;
		squish_comp |= squish::kDxt1;
	}

	p_image->convert(Image::FORMAT_RGBA8); //always expects rgba

	PoolVector<uint8_t> data;
	int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0);
	int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0;
	data.resize(target_size);

	PoolVector<uint8_t>::Read rb = p_image->get_data().read();
	PoolVector<uint8_t>::Write wb = data.write();

	int dst_ofs = 0;

	for (int i = 0; i <= mm_count; i++) {

		int src_ofs = p_image->get_mipmap_offset(i);
		squish::CompressImage(&rb[src_ofs], w, h, &wb[dst_ofs], squish_comp);
		dst_ofs += (MAX(4, w) * MAX(4, h)) >> shift;
		w >>= 1;
		h >>= 1;
	}

	rb = PoolVector<uint8_t>::Read();
	wb = PoolVector<uint8_t>::Write();

	p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
<|endoftext|>