<commit_before>#pragma once

// `krbn::dispatcher::dispatcher` can be used safely in a multi-threaded environment.

#include "dispatcher/object_id.hpp"
#include "thread_utility.hpp"

namespace krbn {
namespace dispatcher {
class dispatcher final {
public:
  dispatcher(const dispatcher&) = delete;

  dispatcher(void) : exit_(false) {
    worker_thread_ = std::thread([this] {
      worker_thread_id_ = std::this_thread::get_id();
      worker_thread_id_wait_.notify();

      while (true) {
        std::function<void(void)> function;

        {
          std::unique_lock<std::mutex> lock(mutex_);

          cv_.wait(lock, [this] {
            return exit_ || !queue_.empty();
          });

          if (exit_ && queue_.empty()) {
            break;
          }

          if (!queue_.empty()) {
            function = queue_.front();
            queue_.pop();
          }
        }

        if (function) {
          std::lock_guard<std::mutex> lock(function_mutex_);

          function();
        }
      }
    });

    worker_thread_id_wait_.wait_notice();
  }

  ~dispatcher(void) {
    if (worker_thread_.joinable()) {
      logger::get_logger().error("Call `dispatcher::terminate` before destroy `dispatcher`");
      terminate();
    }
  }

  void attach(const object_id& object_id) {
    std::lock_guard<std::mutex> lock(object_ids_mutex_);

    object_ids_.insert(object_id.get());
  }

  void detach(const object_id& object_id) {
    {
      std::lock_guard<std::mutex> lock(object_ids_mutex_);

      if (!attached(object_id.get())) {
        return;
      }

      object_ids_.erase(object_id.get());
    }

    if (!is_dispatcher_thread()) {
      // Wait until current running function is finised.
      std::lock_guard<std::mutex> lock(function_mutex_);
    }
  }

  void detach(const object_id& object_id,
              const std::function<void(void)>& function) {
    {
      std::lock_guard<std::mutex> lock(object_ids_mutex_);

      if (!attached(object_id.get())) {
        return;
      }
    }

    if (is_dispatcher_thread()) {
      function();
    } else {
      thread_utility::wait w;

      enqueue(object_id,
              [&w, &function] {
                function();
                w.notify();
              });

      w.wait_notice();
    }

    detach(object_id);
  }

  bool is_dispatcher_thread(void) const {
    return std::this_thread::get_id() == worker_thread_id_;
  }

  void terminate(void) {
    // We should separate `~dispatcher` and `terminate` to ensure dispatcher exists until all jobs are processed.
    //
    // Example:
    // ----------------------------------------
    // class example final {
    // public:
    //   example(void) : object_id_(krbn::dispatcher::make_new_object_id()) {
    //     dispatcher_ = std::make_unique<krbn::dispatcher::dispatcher>();
    //     dispatcher_->attach(object_id_);
    //
    //     dispatcher_->enqueue(
    //         object_id_,
    //         [this] {
    //           // `dispatcher_` might be nullptr if we call `terminate` before `dispatcher_ = nullptr`.
    //           dispatcher_->enqueue(
    //               object_id_,
    //               [] {
    //                 std::cout << "hello" << std::endl;
    //               });
    //         });
    //
    //     dispatcher_->terminate(); // SEGV if comment out this line
    //     dispatcher_ = nullptr;
    //   }
    //
    // private:
    //   krbn::dispatcher::object_id object_id_;
    //   std::unique_ptr<krbn::dispatcher::dispatcher> dispatcher_;
    // };
    // ----------------------------------------

    if (is_dispatcher_thread()) {
      {
        std::lock_guard<std::mutex> lock(mutex_);

        exit_ = true;
        queue_.empty();
      }

      worker_thread_.detach();

    } else {
      if (worker_thread_.joinable()) {
        {
          std::lock_guard<std::mutex> lock(mutex_);

          exit_ = true;
        }

        cv_.notify_one();
        worker_thread_.join();
      }
    }
  }

  void enqueue(const object_id& object_id,
               const std::function<void(void)>& function) {
    {
      std::lock_guard<std::mutex> lock(mutex_);

      auto id = object_id.get();
      queue_.push([this, id, function] {
        {
          std::lock_guard<std::mutex> lock(object_ids_mutex_);

          if (!attached(id)) {
            return;
          }
        }

        function();
      });
    }

    cv_.notify_one();
  }

private:
  bool attached(const uint64_t object_id_value) {
    auto it = object_ids_.find(object_id_value);
    return it != std::end(object_ids_);
  }

  std::thread worker_thread_;
  std::thread::id worker_thread_id_;
  thread_utility::wait worker_thread_id_wait_;

  std::queue<std::function<void(void)>> queue_;
  bool exit_;
  std::mutex mutex_;
  std::condition_variable cv_;

  std::unordered_set<uint64_t> object_ids_;
  std::mutex object_ids_mutex_;

  std::mutex function_mutex_;
};
} // namespace dispatcher
} // namespace krbn
<commit_msg>add dispatcher::entry<commit_after>#pragma once

// `krbn::dispatcher::dispatcher` can be used safely in a multi-threaded environment.

#include "dispatcher/object_id.hpp"
#include "thread_utility.hpp"
#include "types/absolute_time.hpp"
#include <deque>

namespace krbn {
namespace dispatcher {
class dispatcher final {
public:
  dispatcher(const dispatcher&) = delete;

  dispatcher(void) : exit_(false) {
    worker_thread_ = std::thread([this] {
      worker_thread_id_ = std::this_thread::get_id();
      worker_thread_id_wait_.notify();

      while (true) {
        std::shared_ptr<entry> e;

        {
          std::unique_lock<std::mutex> lock(mutex_);

          cv_.wait(lock, [this] {
            return exit_ || !queue_.empty();
          });

          if (exit_ && queue_.empty()) {
            break;
          }

          if (!queue_.empty()) {
            e = queue_.front();
            queue_.pop_front();
          }
        }

        if (e) {
          std::lock_guard<std::mutex> lock(function_mutex_);

          e->call_function();
        }
      }
    });

    worker_thread_id_wait_.wait_notice();
  }

  ~dispatcher(void) {
    if (worker_thread_.joinable()) {
      logger::get_logger().error("Call `dispatcher::terminate` before destroy `dispatcher`");
      terminate();
    }
  }

  void attach(const object_id& object_id) {
    std::lock_guard<std::mutex> lock(object_ids_mutex_);

    object_ids_.insert(object_id.get());
  }

  void detach(const object_id& object_id) {
    {
      std::lock_guard<std::mutex> lock(object_ids_mutex_);

      if (!attached(object_id.get())) {
        return;
      }

      object_ids_.erase(object_id.get());
    }

    if (!is_dispatcher_thread()) {
      // Wait until current running function is finised.
      std::lock_guard<std::mutex> lock(function_mutex_);
    }
  }

  void detach(const object_id& object_id,
              const std::function<void(void)>& function) {
    {
      std::lock_guard<std::mutex> lock(object_ids_mutex_);

      if (!attached(object_id.get())) {
        return;
      }
    }

    if (is_dispatcher_thread()) {
      function();
    } else {
      thread_utility::wait w;

      enqueue(object_id,
              [&w, &function] {
                function();
                w.notify();
              });

      w.wait_notice();
    }

    detach(object_id);
  }

  bool is_dispatcher_thread(void) const {
    return std::this_thread::get_id() == worker_thread_id_;
  }

  void terminate(void) {
    // We should separate `~dispatcher` and `terminate` to ensure dispatcher exists until all jobs are processed.
    //
    // Example:
    // ----------------------------------------
    // class example final {
    // public:
    //   example(void) : object_id_(krbn::dispatcher::make_new_object_id()) {
    //     dispatcher_ = std::make_unique<krbn::dispatcher::dispatcher>();
    //     dispatcher_->attach(object_id_);
    //
    //     dispatcher_->enqueue(
    //         object_id_,
    //         [this] {
    //           // `dispatcher_` might be nullptr if we call `terminate` before `dispatcher_ = nullptr`.
    //           dispatcher_->enqueue(
    //               object_id_,
    //               [] {
    //                 std::cout << "hello" << std::endl;
    //               });
    //         });
    //
    //     dispatcher_->terminate(); // SEGV if comment out this line
    //     dispatcher_ = nullptr;
    //   }
    //
    // private:
    //   krbn::dispatcher::object_id object_id_;
    //   std::unique_ptr<krbn::dispatcher::dispatcher> dispatcher_;
    // };
    // ----------------------------------------

    if (is_dispatcher_thread()) {
      {
        std::lock_guard<std::mutex> lock(mutex_);

        exit_ = true;
        queue_.empty();
      }

      worker_thread_.detach();

    } else {
      if (worker_thread_.joinable()) {
        {
          std::lock_guard<std::mutex> lock(mutex_);

          exit_ = true;
        }

        cv_.notify_one();
        worker_thread_.join();
      }
    }
  }

  void enqueue(const object_id& object_id,
               const std::function<void(void)>& function,
               absolute_time when = absolute_time(0)) {
    {
      std::lock_guard<std::mutex> lock(mutex_);

      auto id = object_id.get();
      queue_.push_back(std::make_shared<entry>(
          [this, id, function] {
            {
              std::lock_guard<std::mutex> lock(object_ids_mutex_);

              if (!attached(id)) {
                return;
              }
            }

            function();
          },
          when));

      std::stable_sort(std::begin(queue_),
                       std::end(queue_),
                       [](auto& a, auto& b) {
                         return a->get_when() < b->get_when();
                       });
    }

    cv_.notify_one();
  }

private:
  class entry final {
  public:
    entry(const std::function<void(void)>& function,
          absolute_time when) : function_(function),
                                when_(when) {
    }

    absolute_time get_when(void) const {
      return when_;
    }

    void call_function(void) const {
      function_();
    }

  private:
    std::function<void(void)> function_;
    absolute_time when_;
  };

  bool attached(const uint64_t object_id_value) {
    auto it = object_ids_.find(object_id_value);
    return it != std::end(object_ids_);
  }

  std::thread worker_thread_;
  std::thread::id worker_thread_id_;
  thread_utility::wait worker_thread_id_wait_;

  std::deque<std::shared_ptr<entry>> queue_;
  bool exit_;
  std::mutex mutex_;
  std::condition_variable cv_;

  std::unordered_set<uint64_t> object_ids_;
  std::mutex object_ids_mutex_;

  std::mutex function_mutex_;
};
} // namespace dispatcher
} // namespace krbn
<|endoftext|>


<commit_before>// Xerus - A General Purpose Tensor Library
// Copyright (C) 2014-2015 Benjamin Huber and Sebastian Wolf.
//
// Xerus is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// Xerus 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Xerus. If not, see <http://www.gnu.org/licenses/>.
//
// For further information on Xerus visit https://libXerus.org
// or contact us at contact@libXerus.org.

#ifdef TEST_

    #include "standard.h"
    #include "exceptions.h"
    #include <iostream>
    #include <iomanip>
    #include <chrono>
    #include <signal.h>
    #include <sstream>

    #include <string.h> // for strsignal

    #include "testManager.h"
    #include "stringUtilities.h"
#include <sys/stat.h>

    std::map<std::string, std::map<std::string, std::function<bool ()>>> *___UnitTest::tests;
	std::map<___RequiredTest::identifier, size_t> *___RequiredTest::tests;

    bool ___test(const std::pair<std::string, std::function<bool ()>> &_t) {
        bool passed = false;

        std::cout << "| " << _t.first << " starting: "  << std::flush;

        std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
        try {
            passed = _t.second(); // executes the test
        } catch (const MISC::generic_error &e) {
            std::cout << u8"\033[1;31m\u2717 \033[0m" << std::endl;
            std::cerr << "| Test has thrown an uncaught xerus::generic_error():" << std::endl;
            std::cerr << e.what() << std::endl;
            passed = false;
        } catch (const std::exception &e) {
            std::cout << u8"\033[1;31m\u2717 \033[0m" << std::endl;
            std::cerr << "| Test has thrown an uncaught std::exception:" << std::endl;
            std::cerr << e.what() << std::endl;
            passed = false;
        } catch (...) {
            std::cout << u8"\033[1;31m\u2717 \033[0m" << std::endl;
            std::cerr << "| Test has thrown an uncaught unknown exception..." << std::endl;
            passed = false;
        }
        std::chrono::microseconds::rep time = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start).count();

        if (passed) {
            std::cout << std::endl << "| " << _t.first << ":\033[1;32m passed!\033[0m (" << std::fixed << std::setprecision(3) << (double)time/1000.0 << " ms)" << std::endl << "| " << std::endl;
        } else {
            std::cout << std::endl << "| " << _t.first << ":\033[1;31m FAILED!\033[0m (" << std::fixed << std::setprecision(3) << (double)time/1000.0 << " ms)" << std::endl << "| " << std::endl;
        }

        return passed;
    }

    void ___print_group_name(std::string _g) {
        int a = (77-(int)_g.size())/2; if (a<0) a=0;
        int b = 77-(77+(int)_g.size())/2; if (b<0) b=0;
        std::cout << "-------------------------------------------------------------------------------" << std::endl;
        std::cout << "|" << std::string((size_t)a, ' ') << "\033[1m" << _g << "\033[0m" << std::string((size_t)b, ' ')  << ' ' << std::endl;
        std::cout << "|" << std::endl;
        //std::cout << "-------------------------------------------------------------------------------" << std::endl;
    }

    void ___print_group_summary(std::string _g, unsigned _passes, unsigned _total) {
        std::stringstream ts;
        ts.str(""); ts.clear();
        ts << _g << " summary " << (_passes == _total?"\033[1;32m":"\033[1;31m") << _passes << " of " << _total << " passed\033[0m";
        int a = (77-((int)ts.str().size()-11))/2; if (a<0) a=0;
        int b = 77-(77+((int)ts.str().size()-11))/2; if (b<0) b=0;
        //std::cout << "-------------------------------------------------------------------------------" << std::endl;
        std::cout << "|" << std::endl;
        std::cout << "|" << std::string((size_t)a, ' ') << ts.str() << std::string((size_t)b, ' ')  << ' ' << std::endl;
        std::cout << "-------------------------------------------------------------------------------" << std::endl;
    }

    _noreturn_ void ___catch_signals(int _sig)  {
        XERUS_THROW(MISC::generic_error() << "signal " << _sig << " = " << strsignal(_sig) << "callstack:\n" << MISC::get_call_stack());
    }

    #undef main
    int main(int argc, char* argv[]) {
    // 	signal(SIGINT, ___catch_signals); // users ctrl+c should actually terminate the program
    // 	signal(SIGTERM, ___catch_signals);
    // 	signal(SIGHUP, ___catch_signals);
        //signal(SIGABRT,___catch_signals); // common source of abort is "double free or corurption" in which case we cannot continue
        signal(SIGFPE,___catch_signals);
        signal(SIGILL,___catch_signals);
        signal(SIGSEGV,___catch_signals);

        //Calculate complete time
        std::chrono::high_resolution_clock::time_point startTime = std::chrono::high_resolution_clock::now();

        std::cout << "###############################################################################" << std::endl;
        std::cout << "#                                unit-testing                                 #" << std::endl;
        std::cout << "###############################################################################" << std::endl;
        if (argc < 2) {
            std::cout << "usage:" << std::endl;
            std::cout << "  " << MISC_NAMESPACE::explode(argv[0],'/').back() << " [groupname] ..." << std::endl;
            std::cout << "  " << MISC_NAMESPACE::explode(argv[0],'/').back() << " [groupname]:[testname] ..." << std::endl;
            std::cout << "  " << MISC_NAMESPACE::explode(argv[0],'/').back() << " all" << std::endl << std::endl;
            std::cout << "available groups:" << std::endl;
            for (const auto &p : *___UnitTest::tests) {
                std::cout << "# " <<  p.first << std::endl;
            }
            return 0;
        }

        unsigned passCount=0;
        unsigned totalPassCount=0;
        unsigned totalCount=0;

        for (int currArg = 1; currArg < argc; ++currArg) {
            std::string grp = argv[currArg];
            // do all unit tests
            if (grp == "all") {
                for (const auto &p : *___UnitTest::tests) {
                    ___print_group_name(p.first);
                    passCount=0;
                    for (const auto &t : p.second) {
                        totalCount += 1;
                        if (___test(t)) {
                            passCount += 1;
                            totalPassCount += 1;
                        }
                    }
                    ___print_group_summary(p.first, passCount, (unsigned)p.second.size());
                }
                break;
            }
            // explicit test inside a group?
            std::vector<std::string> cmd = MISC_NAMESPACE::explode(grp,':');
            if (cmd.size()>1) {
                if ((*___UnitTest::tests)[cmd[0]].count(cmd[1]) == 0) {
                    std::cout << "########## \033[1;31munknown unittest '" << cmd[0] << ":" << cmd[1] << "'\033[0m" << std::endl;
                    continue;
                }
                totalCount += 1;
                if (___test({grp, (*___UnitTest::tests)[cmd[0]][cmd[1]]}) ) {
                    totalPassCount += 1;
                }
            } else {
                // unknown group
                if (___UnitTest::tests->count(grp) == 0) {
                    std::cout << "########## \033[1;31munknown group or unittest '" << grp << "'\033[0m" << std::endl;
                    continue;
                }
                // one (whole) group
                ___print_group_name(grp);
                passCount=0;
                for (const auto &t : (*___UnitTest::tests)[grp]) {
                    totalCount += 1;
                    if (___test(t)) {
                        passCount += 1;
                        totalPassCount += 1;
                    }
                }
                ___print_group_summary(grp, passCount, (unsigned)(*___UnitTest::tests)[grp].size());
            }
        }

        //Calc total elapsed time
        std::chrono::microseconds::rep totalTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - startTime).count();

        ___print_group_summary("total", totalPassCount, totalCount);

        std::cout << "|" << std::endl;
        std::cout << "|" << std::string(23, ' ') << "Total time elapsed: " << (double)totalTime/1000.0 << " ms" << std::string(50, ' ')  << ' ' << std::endl;
        std::cout << "-------------------------------------------------------------------------------" << std::endl;

		// check whether all REQUIRED_TESTs were tested
		std::map<std::string, std::pair<size_t, size_t>> perFile;

		for (auto &t : (*___RequiredTest::tests)) {
// 			std::cout << MISC::demangle_cxa(t.first.functionName) << " (" << t.first.filename << ":" << t.first.lineNumber << ")" << t.second << std::endl;
			std::pair<size_t, size_t> &pf = perFile[t.first.filename];
			pf.second += 1;
			if (t.second == 0) {
				std::cout << "\033[1;31m missing test for function \033[0m"
					<< MISC::demangle_cxa(t.first.functionName) << " (" << t.first.filename << ":" << t.first.lineNumber << ")" << std::endl;
			} else {
				pf.first += 1;
			}
		}

		for (auto &f : perFile) {
			std::pair<size_t, size_t> &fstats = f.second;
			if (fstats.first == fstats.second) {
				std::cout << "file " << f.first << " :\033[1;32m " << fstats.first << " of " << fstats.second << " tests performed\033[0m" << std::endl;
			} else {
				std::cout << "file " << f.first << " :\033[1;31m " << fstats.first << " of " << fstats.second << " tests performed\033[0m" << std::endl;
			}
		}

		// destroy all stored tests to make memory-leak detection simpler
		delete ___UnitTest::tests;
		delete ___RequiredTest::tests;

        return totalPassCount != totalCount;
    }

    #define main(...) ___horst_main_will_not_be_called( __VA_ARGS__ )
#endif
<commit_msg>closes #30 - test.cpp correctly detects if the map of unittests was never created (ie. no unit tests defined)<commit_after>// Xerus - A General Purpose Tensor Library
// Copyright (C) 2014-2015 Benjamin Huber and Sebastian Wolf.
//
// Xerus is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// Xerus 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Xerus. If not, see <http://www.gnu.org/licenses/>.
//
// For further information on Xerus visit https://libXerus.org
// or contact us at contact@libXerus.org.

#ifdef TEST_

    #include "standard.h"
    #include "exceptions.h"
    #include <iostream>
    #include <iomanip>
    #include <chrono>
    #include <signal.h>
    #include <sstream>

    #include <string.h> // for strsignal

    #include "testManager.h"
    #include "stringUtilities.h"
#include <sys/stat.h>

    std::map<std::string, std::map<std::string, std::function<bool ()>>> *___UnitTest::tests;
	std::map<___RequiredTest::identifier, size_t> *___RequiredTest::tests;

    bool ___test(const std::pair<std::string, std::function<bool ()>> &_t) {
        bool passed = false;

        std::cout << "| " << _t.first << " starting: "  << std::flush;

        std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
        try {
            passed = _t.second(); // executes the test
        } catch (const MISC::generic_error &e) {
            std::cout << u8"\033[1;31m\u2717 \033[0m" << std::endl;
            std::cerr << "| Test has thrown an uncaught xerus::generic_error():" << std::endl;
            std::cerr << e.what() << std::endl;
            passed = false;
        } catch (const std::exception &e) {
            std::cout << u8"\033[1;31m\u2717 \033[0m" << std::endl;
            std::cerr << "| Test has thrown an uncaught std::exception:" << std::endl;
            std::cerr << e.what() << std::endl;
            passed = false;
        } catch (...) {
            std::cout << u8"\033[1;31m\u2717 \033[0m" << std::endl;
            std::cerr << "| Test has thrown an uncaught unknown exception..." << std::endl;
            passed = false;
        }
        std::chrono::microseconds::rep time = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start).count();

        if (passed) {
            std::cout << std::endl << "| " << _t.first << ":\033[1;32m passed!\033[0m (" << std::fixed << std::setprecision(3) << (double)time/1000.0 << " ms)" << std::endl << "| " << std::endl;
        } else {
            std::cout << std::endl << "| " << _t.first << ":\033[1;31m FAILED!\033[0m (" << std::fixed << std::setprecision(3) << (double)time/1000.0 << " ms)" << std::endl << "| " << std::endl;
        }

        return passed;
    }

    void ___print_group_name(std::string _g) {
        int a = (77-(int)_g.size())/2; if (a<0) a=0;
        int b = 77-(77+(int)_g.size())/2; if (b<0) b=0;
        std::cout << "-------------------------------------------------------------------------------" << std::endl;
        std::cout << "|" << std::string((size_t)a, ' ') << "\033[1m" << _g << "\033[0m" << std::string((size_t)b, ' ')  << ' ' << std::endl;
        std::cout << "|" << std::endl;
        //std::cout << "-------------------------------------------------------------------------------" << std::endl;
    }

    void ___print_group_summary(std::string _g, unsigned _passes, unsigned _total) {
        std::stringstream ts;
        ts.str(""); ts.clear();
        ts << _g << " summary " << (_passes == _total?"\033[1;32m":"\033[1;31m") << _passes << " of " << _total << " passed\033[0m";
        int a = (77-((int)ts.str().size()-11))/2; if (a<0) a=0;
        int b = 77-(77+((int)ts.str().size()-11))/2; if (b<0) b=0;
        //std::cout << "-------------------------------------------------------------------------------" << std::endl;
        std::cout << "|" << std::endl;
        std::cout << "|" << std::string((size_t)a, ' ') << ts.str() << std::string((size_t)b, ' ')  << ' ' << std::endl;
        std::cout << "-------------------------------------------------------------------------------" << std::endl;
    }

    _noreturn_ void ___catch_signals(int _sig)  {
        XERUS_THROW(MISC::generic_error() << "signal " << _sig << " = " << strsignal(_sig) << "callstack:\n" << MISC::get_call_stack());
    }

    #undef main
    int main(int argc, char* argv[]) {
    // 	signal(SIGINT, ___catch_signals); // users ctrl+c should actually terminate the program
    // 	signal(SIGTERM, ___catch_signals);
    // 	signal(SIGHUP, ___catch_signals);
        //signal(SIGABRT,___catch_signals); // common source of abort is "double free or corurption" in which case we cannot continue
        signal(SIGFPE,___catch_signals);
        signal(SIGILL,___catch_signals);
        signal(SIGSEGV,___catch_signals);

        //Calculate complete time
        std::chrono::high_resolution_clock::time_point startTime = std::chrono::high_resolution_clock::now();

        std::cout << "###############################################################################" << std::endl;
        std::cout << "#                                unit-testing                                 #" << std::endl;
        std::cout << "###############################################################################" << std::endl;
		// no unittests defined (ie. the map tests does not exist!)
		if (!___UnitTest::tests) {
			std::cout << "no unittests defined." << std::endl;
			std::cout << "use the macro UNIT_TEST(group, testname, ...) to define unittests inside the sourcecode." << std::endl;
			return 0;
		}

        if (argc < 2) {
            std::cout << "usage:" << std::endl;
            std::cout << "  " << MISC_NAMESPACE::explode(argv[0],'/').back() << " [groupname] ..." << std::endl;
            std::cout << "  " << MISC_NAMESPACE::explode(argv[0],'/').back() << " [groupname]:[testname] ..." << std::endl;
            std::cout << "  " << MISC_NAMESPACE::explode(argv[0],'/').back() << " all" << std::endl << std::endl;
            std::cout << "available groups:" << std::endl;
            for (const auto &p : *___UnitTest::tests) {
                std::cout << "# " <<  p.first << std::endl;
            }
            return 0;
        }

        unsigned passCount=0;
        unsigned totalPassCount=0;
        unsigned totalCount=0;

        for (int currArg = 1; currArg < argc; ++currArg) {
            std::string grp = argv[currArg];
            // do all unit tests
            if (grp == "all") {
                for (const auto &p : *___UnitTest::tests) {
                    ___print_group_name(p.first);
                    passCount=0;
                    for (const auto &t : p.second) {
                        totalCount += 1;
                        if (___test(t)) {
                            passCount += 1;
                            totalPassCount += 1;
                        }
                    }
                    ___print_group_summary(p.first, passCount, (unsigned)p.second.size());
                }
                break;
            }
            // explicit test inside a group?
            std::vector<std::string> cmd = MISC_NAMESPACE::explode(grp,':');
            if (cmd.size()>1) {
				if (cmd.size()>2) {
                    std::cout << "########## \033[1;31munknown syntax '" << grp << "'\033[0m" << std::endl;
                    continue;
                }
                if (!___UnitTest::tests->count(cmd[0]) || (*___UnitTest::tests)[cmd[0]].count(cmd[1]) == 0) {
                    std::cout << "########## \033[1;31munknown unittest '" << cmd[0] << ":" << cmd[1] << "'\033[0m" << std::endl;
                    continue;
                }
                totalCount += 1;
                if (___test({grp, (*___UnitTest::tests)[cmd[0]][cmd[1]]}) ) {
                    totalPassCount += 1;
                }
            } else {
                // unknown group
                if (___UnitTest::tests->count(grp) == 0) {
                    std::cout << "########## \033[1;31munknown group or unittest '" << grp << "'\033[0m" << std::endl;
                    continue;
                }
                // one (whole) group
                ___print_group_name(grp);
                passCount=0;
                for (const auto &t : (*___UnitTest::tests)[grp]) {
                    totalCount += 1;
                    if (___test(t)) {
                        passCount += 1;
                        totalPassCount += 1;
                    }
                }
                ___print_group_summary(grp, passCount, (unsigned)(*___UnitTest::tests)[grp].size());
            }
        }

        //Calc total elapsed time
        std::chrono::microseconds::rep totalTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - startTime).count();

        ___print_group_summary("total", totalPassCount, totalCount);

        std::cout << "|" << std::endl;
        std::cout << "|" << std::string(23, ' ') << "Total time elapsed: " << (double)totalTime/1000.0 << " ms" << std::string(50, ' ')  << ' ' << std::endl;
        std::cout << "-------------------------------------------------------------------------------" << std::endl;

		// check whether all REQUIRED_TESTs were tested
		std::map<std::string, std::pair<size_t, size_t>> perFile;

		for (auto &t : (*___RequiredTest::tests)) {
// 			std::cout << MISC::demangle_cxa(t.first.functionName) << " (" << t.first.filename << ":" << t.first.lineNumber << ")" << t.second << std::endl;
			std::pair<size_t, size_t> &pf = perFile[t.first.filename];
			pf.second += 1;
			if (t.second == 0) {
				std::cout << "\033[1;31m missing test for function \033[0m"
					<< MISC::demangle_cxa(t.first.functionName) << " (" << t.first.filename << ":" << t.first.lineNumber << ")" << std::endl;
			} else {
				pf.first += 1;
			}
		}

		for (auto &f : perFile) {
			std::pair<size_t, size_t> &fstats = f.second;
			if (fstats.first == fstats.second) {
				std::cout << "file " << f.first << " :\033[1;32m " << fstats.first << " of " << fstats.second << " tests performed\033[0m" << std::endl;
			} else {
				std::cout << "file " << f.first << " :\033[1;31m " << fstats.first << " of " << fstats.second << " tests performed\033[0m" << std::endl;
			}
		}

		// destroy all stored tests to make memory-leak detection simpler
		delete ___UnitTest::tests;
		delete ___RequiredTest::tests;

        return totalPassCount != totalCount;
    }

    #define main(...) ___horst_main_will_not_be_called( __VA_ARGS__ )
#endif
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008-2010 The QXmpp developers
 *
 * Author:
 *  Jeremy Lainé
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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 <QDataStream>

#include "QXmppCodec.h"
#include "QXmppJingleIq.h"
#include "QXmppRtpChannel.h"

//#define QXMPP_DEBUG_RTP
#define SAMPLE_BYTES 2

const quint8 RTP_VERSION = 0x02;

class QXmppRtpChannelPrivate
{
public:
    QXmppRtpChannelPrivate();

    // signals
    bool signalsEmitted;
    qint64 writtenSinceLastEmit;

    // RTP
    QXmppCodec *codec;
    QHostAddress remoteHost;
    quint16 remotePort;

    QByteArray incomingBuffer;
    bool incomingBuffering;
    int incomingMinimum;
    int incomingMaximum;
    quint16 incomingSequence;
    quint32 incomingStamp;

    quint16 outgoingChunk;
    QByteArray outgoingBuffer;
    bool outgoingMarker;
    quint16 outgoingSequence;
    quint32 outgoingStamp;

    quint32 ssrc;
    QXmppJinglePayloadType payloadType;
};

QXmppRtpChannelPrivate::QXmppRtpChannelPrivate()
    : signalsEmitted(false),
    writtenSinceLastEmit(0),
    codec(0),
    incomingBuffering(true),
    incomingMinimum(0),
    incomingMaximum(0),
    incomingSequence(0),
    incomingStamp(0),
    outgoingMarker(true),
    outgoingSequence(0),
    outgoingStamp(0),
    ssrc(0)
{
    ssrc = qrand();
}

/// Creates a new RTP channel.
///
/// \param parent

QXmppRtpChannel::QXmppRtpChannel(QObject *parent)
    : QIODevice(parent),
    d(new QXmppRtpChannelPrivate)
{
    QXmppLoggable *logParent = qobject_cast<QXmppLoggable*>(parent);
    if (logParent) {
        connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
                logParent, SIGNAL(logMessage(QXmppLogger::MessageType,QString)));
    }
}

/// Destroys an RTP channel.
///

QXmppRtpChannel::~QXmppRtpChannel()
{
    delete d;
}

/// Returns the number of bytes that are available for reading.
///

qint64 QXmppRtpChannel::bytesAvailable() const
{
    return d->incomingBuffer.size();
}

/// Processes an incoming RTP packet.
///
/// \param ba
void QXmppRtpChannel::datagramReceived(const QByteArray &ba)
{
    if (!d->codec)
    {
        warning("QXmppRtpChannel::datagramReceived before codec selection");
        return;
    }

    if (ba.size() < 12 || (quint8(ba.at(0)) >> 6) != RTP_VERSION)
    {
        warning("QXmppRtpChannel::datagramReceived got an invalid RTP packet");
        return;
    }

    // parse RTP header
    QDataStream stream(ba);
    quint8 version, marker_type;
    quint32 ssrc;
    quint16 sequence;
    quint32 stamp;
    stream >> version;
    stream >> marker_type;
    stream >> sequence;
    stream >> stamp;
    stream >> ssrc;
    const bool marker = marker_type & 0x80;
    const quint8 type = marker_type & 0x7f;
    const qint64 packetLength = ba.size() - 12;

#ifdef QXMPP_DEBUG_RTP
    logReceived(QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
            QString::number(sequence),
            QString::number(stamp),
            QString::number(marker),
            QString::number(type),
            QString::number(packetLength)));
#endif

    // check type
    if (type != d->payloadType.id())
    {
        warning(QString("RTP packet seq %1 has unknown type %2")
                .arg(QString::number(sequence))
                .arg(QString::number(type)));
        return;
    }

    // check sequence number
    if (!marker && sequence != d->incomingSequence + 1)
        warning(QString("RTP packet seq %1 is out of order, previous was %2")
                .arg(QString::number(sequence))
                .arg(QString::number(d->incomingSequence)));
    d->incomingSequence = sequence;

    // determine packet's position in the buffer (in bytes)
    qint64 packetOffset = 0;
    if (!d->incomingBuffer.isEmpty())
    {
        packetOffset = (stamp - d->incomingStamp) * SAMPLE_BYTES;
        if (packetOffset < 0)
        {
            warning(QString("RTP packet stamp %1 is too old, buffer start is %2")
                    .arg(QString::number(stamp))
                    .arg(QString::number(d->incomingStamp)));
            return;
        }
    } else {
        d->incomingStamp = stamp;
    }

    // allocate space for new packet
    if (packetOffset + packetLength > d->incomingBuffer.size())
        d->incomingBuffer += QByteArray(packetOffset + packetLength - d->incomingBuffer.size(), 0);
    QDataStream output(&d->incomingBuffer, QIODevice::WriteOnly);
    output.device()->seek(packetOffset);
    output.setByteOrder(QDataStream::LittleEndian);
    d->codec->decode(stream, output);

    // check whether we are running late
    if (d->incomingBuffer.size() > d->incomingMaximum)
    {
        const qint64 droppedSize = d->incomingBuffer.size() - d->incomingMinimum;
        debug(QString("RTP buffer is too full, dropping %1 bytes")
                .arg(QString::number(droppedSize)));
        d->incomingBuffer = d->incomingBuffer.right(d->incomingMinimum);
        d->incomingStamp += droppedSize / SAMPLE_BYTES;
    }
    // check whether we have filled the initial buffer
    if (d->incomingBuffer.size() >= d->incomingMinimum)
        d->incomingBuffering = false;
    if (!d->incomingBuffering)
        emit readyRead();
}

void QXmppRtpChannel::emitSignals()
{
    emit bytesWritten(d->writtenSinceLastEmit);
    d->writtenSinceLastEmit = 0;
    d->signalsEmitted = false;
}

/// Returns true, as the RTP channel is a sequential device.
///

bool QXmppRtpChannel::isSequential() const
{
    return true;
}

qint64 QXmppRtpChannel::readData(char * data, qint64 maxSize)
{
    // if we are filling the buffer, return empty samples
    if (d->incomingBuffering)
    {
        memset(data, 0, maxSize);
        return maxSize;
    }

    qint64 readSize = qMin(maxSize, qint64(d->incomingBuffer.size()));
    memcpy(data, d->incomingBuffer.constData(), readSize);
    d->incomingBuffer.remove(0, readSize);
    if (readSize < maxSize)
    {
#ifdef QXMPP_DEBUG_RTP
        debug(QString("QXmppRtpChannel::readData missing %1 bytes").arg(QString::number(maxSize - readSize)));
#endif
        memset(data + readSize, 0, maxSize - readSize);
    }
    d->incomingStamp += readSize / SAMPLE_BYTES;
    return maxSize;
}

/// Returns the RTP channel's payload type.
///
/// You can use this to determine the QAudioFormat to use with your
/// QAudioInput/QAudioOutput.

QXmppJinglePayloadType QXmppRtpChannel::payloadType() const
{
    return d->payloadType;
}

/// Sets the RTP channel's payload type.
///
/// \param payloadType

void QXmppRtpChannel::setPayloadType(const QXmppJinglePayloadType &payloadType)
{
    d->payloadType = payloadType;
    if (payloadType.id() == G711u)
        d->codec = new QXmppG711uCodec(payloadType.clockrate());
    else if (payloadType.id() == G711a)
        d->codec = new QXmppG711aCodec(payloadType.clockrate());
#ifdef QXMPP_USE_SPEEX
    else if (payloadType.name().toLower() == "speex")
        d->codec = new QXmppSpeexCodec(payloadType.clockrate());
#endif
    else
    {
        warning(QString("QXmppCall got an unknown codec : %1 (%2)")
                .arg(QString::number(payloadType.id()))
                .arg(payloadType.name()));
        return;
    }

    // size in bytes of an unencoded packet
    d->outgoingChunk = SAMPLE_BYTES * payloadType.ptime() * payloadType.clockrate() / 1000;

    // initial number of bytes to buffer
    d->incomingMinimum = d->outgoingChunk * 5;
    d->incomingMaximum = d->outgoingChunk * 8;

    open(QIODevice::ReadWrite | QIODevice::Unbuffered);
}

/// Returns the list of supported payload types.
///

QList<QXmppJinglePayloadType> QXmppRtpChannel::supportedPayloadTypes() const
{
    QList<QXmppJinglePayloadType> payloads;
    QXmppJinglePayloadType payload;

#ifdef QXMPP_USE_SPEEX
    payload.setId(96);
    payload.setChannels(1);
    payload.setName("SPEEX");
    payload.setClockrate(16000);
    payloads << payload;

    payload.setId(97);
    payload.setChannels(1);
    payload.setName("SPEEX");
    payload.setClockrate(8000);
    payloads << payload;
#endif

    payload.setId(QXmppRtpChannel::G711u);
    payload.setChannels(1);
    payload.setName("PCMU");
    payload.setClockrate(8000);
    payloads << payload;

    payload.setId(QXmppRtpChannel::G711a);
    payload.setChannels(1);
    payload.setName("PCMA");
    payload.setClockrate(8000);
    payloads << payload;

    return payloads;
}

qint64 QXmppRtpChannel::writeData(const char * data, qint64 maxSize)
{
    if (!d->codec)
    {
        warning("QXmppRtpChannel::writeData before codec was set");
        return -1;
    }

    d->outgoingBuffer += QByteArray::fromRawData(data, maxSize);
    while (d->outgoingBuffer.size() >= d->outgoingChunk)
    {
        QByteArray header;
        QDataStream stream(&header, QIODevice::WriteOnly);
        quint8 version = RTP_VERSION << 6;
        stream << version;
        quint8 marker_type = d->payloadType.id();
        if (d->outgoingMarker)
        {
            marker_type |= 0x80;
            d->outgoingMarker= false;
        }
        stream << marker_type;
        stream << ++d->outgoingSequence;
        stream << d->outgoingStamp;
        stream << d->ssrc;

        QByteArray chunk = d->outgoingBuffer.left(d->outgoingChunk);
        QDataStream input(chunk);
        input.setByteOrder(QDataStream::LittleEndian);
        d->outgoingStamp += d->codec->encode(input, stream);

#ifdef QXMPP_DEBUG_RTP
        logSent(QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
                    QString::number(d->outgoingSequence),
                    QString::number(d->outgoingStamp),
                    QString::number(marker_type & 0x80 != 0),
                    QString::number(marker_type & 0x7f),
                    QString::number(header.size() - 12)));
#endif
        emit sendDatagram(header);

        d->outgoingBuffer.remove(0, chunk.size());
    }

    d->writtenSinceLastEmit += maxSize;
    if (!d->signalsEmitted && !signalsBlocked()) {
        d->signalsEmitted = true;
        QMetaObject::invokeMethod(this, "emitSignals", Qt::QueuedConnection);
    }

    return maxSize;
}
<commit_msg>fix a spurious RTP warning<commit_after>/*
 * Copyright (C) 2008-2010 The QXmpp developers
 *
 * Author:
 *  Jeremy Lainé
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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 <QDataStream>

#include "QXmppCodec.h"
#include "QXmppJingleIq.h"
#include "QXmppRtpChannel.h"

//#define QXMPP_DEBUG_RTP
#define SAMPLE_BYTES 2

const quint8 RTP_VERSION = 0x02;

class QXmppRtpChannelPrivate
{
public:
    QXmppRtpChannelPrivate();

    // signals
    bool signalsEmitted;
    qint64 writtenSinceLastEmit;

    // RTP
    QXmppCodec *codec;
    QHostAddress remoteHost;
    quint16 remotePort;

    QByteArray incomingBuffer;
    bool incomingBuffering;
    int incomingMinimum;
    int incomingMaximum;
    quint16 incomingSequence;
    quint32 incomingStamp;

    quint16 outgoingChunk;
    QByteArray outgoingBuffer;
    bool outgoingMarker;
    quint16 outgoingSequence;
    quint32 outgoingStamp;

    quint32 ssrc;
    QXmppJinglePayloadType payloadType;
};

QXmppRtpChannelPrivate::QXmppRtpChannelPrivate()
    : signalsEmitted(false),
    writtenSinceLastEmit(0),
    codec(0),
    incomingBuffering(true),
    incomingMinimum(0),
    incomingMaximum(0),
    incomingSequence(0),
    incomingStamp(0),
    outgoingMarker(true),
    outgoingSequence(0),
    outgoingStamp(0),
    ssrc(0)
{
    ssrc = qrand();
}

/// Creates a new RTP channel.
///
/// \param parent

QXmppRtpChannel::QXmppRtpChannel(QObject *parent)
    : QIODevice(parent),
    d(new QXmppRtpChannelPrivate)
{
    QXmppLoggable *logParent = qobject_cast<QXmppLoggable*>(parent);
    if (logParent) {
        connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
                logParent, SIGNAL(logMessage(QXmppLogger::MessageType,QString)));
    }
}

/// Destroys an RTP channel.
///

QXmppRtpChannel::~QXmppRtpChannel()
{
    delete d;
}

/// Returns the number of bytes that are available for reading.
///

qint64 QXmppRtpChannel::bytesAvailable() const
{
    return d->incomingBuffer.size();
}

/// Processes an incoming RTP packet.
///
/// \param ba
void QXmppRtpChannel::datagramReceived(const QByteArray &ba)
{
    if (!d->codec)
    {
        warning("QXmppRtpChannel::datagramReceived before codec selection");
        return;
    }

    if (ba.size() < 12 || (quint8(ba.at(0)) >> 6) != RTP_VERSION)
    {
        warning("QXmppRtpChannel::datagramReceived got an invalid RTP packet");
        return;
    }

    // parse RTP header
    QDataStream stream(ba);
    quint8 version, marker_type;
    quint32 ssrc;
    quint16 sequence;
    quint32 stamp;
    stream >> version;
    stream >> marker_type;
    stream >> sequence;
    stream >> stamp;
    stream >> ssrc;
    const bool marker = marker_type & 0x80;
    const quint8 type = marker_type & 0x7f;
    const qint64 packetLength = ba.size() - 12;

#ifdef QXMPP_DEBUG_RTP
    logReceived(QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
            QString::number(sequence),
            QString::number(stamp),
            QString::number(marker),
            QString::number(type),
            QString::number(packetLength)));
#endif

    // check type
    if (type != d->payloadType.id())
    {
        warning(QString("RTP packet seq %1 has unknown type %2")
                .arg(QString::number(sequence))
                .arg(QString::number(type)));
        return;
    }

    // check sequence number
    if (d->incomingSequence && sequence != d->incomingSequence + 1)
        warning(QString("RTP packet seq %1 is out of order, previous was %2")
                .arg(QString::number(sequence))
                .arg(QString::number(d->incomingSequence)));
    d->incomingSequence = sequence;

    // determine packet's position in the buffer (in bytes)
    qint64 packetOffset = 0;
    if (!d->incomingBuffer.isEmpty())
    {
        packetOffset = (stamp - d->incomingStamp) * SAMPLE_BYTES;
        if (packetOffset < 0)
        {
            warning(QString("RTP packet stamp %1 is too old, buffer start is %2")
                    .arg(QString::number(stamp))
                    .arg(QString::number(d->incomingStamp)));
            return;
        }
    } else {
        d->incomingStamp = stamp;
    }

    // allocate space for new packet
    if (packetOffset + packetLength > d->incomingBuffer.size())
        d->incomingBuffer += QByteArray(packetOffset + packetLength - d->incomingBuffer.size(), 0);
    QDataStream output(&d->incomingBuffer, QIODevice::WriteOnly);
    output.device()->seek(packetOffset);
    output.setByteOrder(QDataStream::LittleEndian);
    d->codec->decode(stream, output);

    // check whether we are running late
    if (d->incomingBuffer.size() > d->incomingMaximum)
    {
        const qint64 droppedSize = d->incomingBuffer.size() - d->incomingMinimum;
        debug(QString("RTP buffer is too full, dropping %1 bytes")
                .arg(QString::number(droppedSize)));
        d->incomingBuffer = d->incomingBuffer.right(d->incomingMinimum);
        d->incomingStamp += droppedSize / SAMPLE_BYTES;
    }
    // check whether we have filled the initial buffer
    if (d->incomingBuffer.size() >= d->incomingMinimum)
        d->incomingBuffering = false;
    if (!d->incomingBuffering)
        emit readyRead();
}

void QXmppRtpChannel::emitSignals()
{
    emit bytesWritten(d->writtenSinceLastEmit);
    d->writtenSinceLastEmit = 0;
    d->signalsEmitted = false;
}

/// Returns true, as the RTP channel is a sequential device.
///

bool QXmppRtpChannel::isSequential() const
{
    return true;
}

qint64 QXmppRtpChannel::readData(char * data, qint64 maxSize)
{
    // if we are filling the buffer, return empty samples
    if (d->incomingBuffering)
    {
        memset(data, 0, maxSize);
        return maxSize;
    }

    qint64 readSize = qMin(maxSize, qint64(d->incomingBuffer.size()));
    memcpy(data, d->incomingBuffer.constData(), readSize);
    d->incomingBuffer.remove(0, readSize);
    if (readSize < maxSize)
    {
#ifdef QXMPP_DEBUG_RTP
        debug(QString("QXmppRtpChannel::readData missing %1 bytes").arg(QString::number(maxSize - readSize)));
#endif
        memset(data + readSize, 0, maxSize - readSize);
    }
    d->incomingStamp += readSize / SAMPLE_BYTES;
    return maxSize;
}

/// Returns the RTP channel's payload type.
///
/// You can use this to determine the QAudioFormat to use with your
/// QAudioInput/QAudioOutput.

QXmppJinglePayloadType QXmppRtpChannel::payloadType() const
{
    return d->payloadType;
}

/// Sets the RTP channel's payload type.
///
/// \param payloadType

void QXmppRtpChannel::setPayloadType(const QXmppJinglePayloadType &payloadType)
{
    d->payloadType = payloadType;
    if (payloadType.id() == G711u)
        d->codec = new QXmppG711uCodec(payloadType.clockrate());
    else if (payloadType.id() == G711a)
        d->codec = new QXmppG711aCodec(payloadType.clockrate());
#ifdef QXMPP_USE_SPEEX
    else if (payloadType.name().toLower() == "speex")
        d->codec = new QXmppSpeexCodec(payloadType.clockrate());
#endif
    else
    {
        warning(QString("QXmppCall got an unknown codec : %1 (%2)")
                .arg(QString::number(payloadType.id()))
                .arg(payloadType.name()));
        return;
    }

    // size in bytes of an unencoded packet
    d->outgoingChunk = SAMPLE_BYTES * payloadType.ptime() * payloadType.clockrate() / 1000;

    // initial number of bytes to buffer
    d->incomingMinimum = d->outgoingChunk * 5;
    d->incomingMaximum = d->outgoingChunk * 8;

    open(QIODevice::ReadWrite | QIODevice::Unbuffered);
}

/// Returns the list of supported payload types.
///

QList<QXmppJinglePayloadType> QXmppRtpChannel::supportedPayloadTypes() const
{
    QList<QXmppJinglePayloadType> payloads;
    QXmppJinglePayloadType payload;

#ifdef QXMPP_USE_SPEEX
    payload.setId(96);
    payload.setChannels(1);
    payload.setName("SPEEX");
    payload.setClockrate(16000);
    payloads << payload;

    payload.setId(97);
    payload.setChannels(1);
    payload.setName("SPEEX");
    payload.setClockrate(8000);
    payloads << payload;
#endif

    payload.setId(QXmppRtpChannel::G711u);
    payload.setChannels(1);
    payload.setName("PCMU");
    payload.setClockrate(8000);
    payloads << payload;

    payload.setId(QXmppRtpChannel::G711a);
    payload.setChannels(1);
    payload.setName("PCMA");
    payload.setClockrate(8000);
    payloads << payload;

    return payloads;
}

qint64 QXmppRtpChannel::writeData(const char * data, qint64 maxSize)
{
    if (!d->codec)
    {
        warning("QXmppRtpChannel::writeData before codec was set");
        return -1;
    }

    d->outgoingBuffer += QByteArray::fromRawData(data, maxSize);
    while (d->outgoingBuffer.size() >= d->outgoingChunk)
    {
        QByteArray header;
        QDataStream stream(&header, QIODevice::WriteOnly);
        quint8 version = RTP_VERSION << 6;
        stream << version;
        quint8 marker_type = d->payloadType.id();
        if (d->outgoingMarker)
        {
            marker_type |= 0x80;
            d->outgoingMarker= false;
        }
        stream << marker_type;
        stream << ++d->outgoingSequence;
        stream << d->outgoingStamp;
        stream << d->ssrc;

        QByteArray chunk = d->outgoingBuffer.left(d->outgoingChunk);
        QDataStream input(chunk);
        input.setByteOrder(QDataStream::LittleEndian);
        d->outgoingStamp += d->codec->encode(input, stream);

#ifdef QXMPP_DEBUG_RTP
        logSent(QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
                    QString::number(d->outgoingSequence),
                    QString::number(d->outgoingStamp),
                    QString::number(marker_type & 0x80 != 0),
                    QString::number(marker_type & 0x7f),
                    QString::number(header.size() - 12)));
#endif
        emit sendDatagram(header);

        d->outgoingBuffer.remove(0, chunk.size());
    }

    d->writtenSinceLastEmit += maxSize;
    if (!d->signalsEmitted && !signalsBlocked()) {
        d->signalsEmitted = true;
        QMetaObject::invokeMethod(this, "emitSignals", Qt::QueuedConnection);
    }

    return maxSize;
}
<|endoftext|>


<commit_before>/*
* MIT License
*
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* 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 "Recorder.h"
#include "Common/config.h"
#include "MP4Recorder.h"
#include "HlsRecorder.h"

using namespace toolkit;

namespace mediakit {

MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string &strApp, const string &strId) {
#if defined(ENABLE_HLS)
    GET_CONFIG(bool, enableVhost, General::kEnableVhost);
    GET_CONFIG(string, hlsPath, Hls::kFilePath);

    string strVhost = strVhost_tmp;
    if (trim(strVhost).empty()) {
        //如果strVhost为空，则强制为默认虚拟主机
        strVhost = DEFAULT_VHOST;
    }

    string m3u8FilePath;
    string params;
    if (enableVhost) {
        m3u8FilePath = strVhost + "/" + strApp + "/" + strId + "/hls.m3u8";
        params = string(VHOST_KEY) + "=" + strVhost;
    } else {
        m3u8FilePath = strApp + "/" + strId + "/hls.m3u8";
    }
    m3u8FilePath = File::absolutePath(m3u8FilePath, hlsPath);
    return new HlsRecorder(m3u8FilePath, params);
#else
    return nullptr;
#endif //defined(ENABLE_HLS)
}

MediaSinkInterface *createMP4Recorder(const string &strVhost_tmp, const string &strApp, const string &strId) {
#if defined(ENABLE_MP4RECORD)
    GET_CONFIG(bool, enableVhost, General::kEnableVhost);
    GET_CONFIG(string, recordPath, Record::kFilePath);
    GET_CONFIG(string, recordAppName, Record::kAppName);

    string strVhost = strVhost_tmp;
    if (trim(strVhost).empty()) {
        //如果strVhost为空，则强制为默认虚拟主机
        strVhost = DEFAULT_VHOST;
    }

    string mp4FilePath;
    if (enableVhost) {
        mp4FilePath = strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
    } else {
        mp4FilePath = recordAppName + "/" + strApp + "/" + strId + "/";
    }
    mp4FilePath = File::absolutePath(mp4FilePath, recordPath);
    return new MP4Recorder(mp4FilePath, strVhost, strApp, strId);
#else
    return nullptr;
#endif //defined(ENABLE_MP4RECORD)
}

////////////////////////////////////////////////////////////////////////////////////////

class RecorderHelper {
public:
    typedef std::shared_ptr<RecorderHelper> Ptr;

    /**
     * 构建函数
     * @param bContinueRecord false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除)
     */
    RecorderHelper(const MediaSinkInterface::Ptr &recorder, vector<Track::Ptr> &&tracks , bool bContinueRecord, const string &schema) {
        _recorder = recorder;
        _continueRecord = bContinueRecord;
        _schema = schema;
        attachTracks(std::move(tracks));
    }

    ~RecorderHelper() {
        resetTracks();
    }

    // 附则于track上
    void attachTracks(vector<Track::Ptr> &&tracks){
        if(isTracksSame(tracks)){
            return;
        }
        resetTracks();
        _tracks = std::move(tracks);
        for (auto &track : _tracks) {
            _recorder->addTrack(track);
            track->addDelegate(_recorder);
        }
    }

    // 判断新的tracks是否与之前的一致
    bool isTracksSame(const vector<Track::Ptr> &tracks){
        if(tracks.size() != _tracks.size()) {
            return false;
        }
        int i = 0;
        for(auto &track : tracks){
            if(track != _tracks[i++]){
                return false;
            }
        }
        return true;
    }

    // 重置所有track
    void resetTracks(){
        if(_tracks.empty()){
            return;
        }
        for (auto &track : _tracks) {
            track->delDelegate(_recorder.get());
        }
        _tracks.clear();
        _recorder->resetTracks();
    }

    // 返回false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除)
    bool continueRecord(){
        return _continueRecord;
    }

    bool isRecording() {
        return !_tracks.empty();
    }

    const string &getSchema() const{
        return _schema;
    }
private:
    MediaSinkInterface::Ptr _recorder;
    vector<Track::Ptr> _tracks;
    bool _continueRecord;
    string _schema;
};

template<Recorder::type type>
class MediaSourceWatcher {
public:
    static MediaSourceWatcher& Instance(){
        static MediaSourceWatcher instance;
        return instance;
    }

    Recorder::status getRecordStatus(const string &vhost, const string &app, const string &stream_id) {
        return getRecordStatus_l(getRecorderKey(vhost, app, stream_id));
    }

    int startRecord(const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) {
        auto key = getRecorderKey(vhost, app, stream_id);
        lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
        if (getRecordStatus_l(key) != Recorder::status_not_record) {
            // 已经在录制了
            return 0;
        }

        string schema;
        auto tracks = findTracks(vhost, app, stream_id,schema);
        if (!waitForRecord && tracks.empty()) {
            // 暂时无法开启录制
            return -1;
        }

        auto recorder = MediaSinkInterface::Ptr(createRecorder(vhost, app, stream_id));
        if (!recorder) {
            // 创建录制器失败
            return -2;
        }
        _recorder_map[key] = std::make_shared<RecorderHelper>(recorder, std::move(tracks), continueRecord, schema);
        return 0;
    }

    void stopRecord(const string &vhost, const string &app, const string &stream_id) {
        lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
        _recorder_map.erase(getRecorderKey(vhost, app, stream_id));
    }

private:
    MediaSourceWatcher(){
        NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){
            if(bRegist){
                onRegist(schema,vhost,app,stream,sender);
            }else{
                onUnRegist(schema,vhost,app,stream,sender);
            }
        });
        NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){
            onRegist(schema,vhost,app,stream,sender);
        });
    }

    ~MediaSourceWatcher(){
        NoticeCenter::Instance().delListener(this,Broadcast::kBroadcastMediaChanged);
        NoticeCenter::Instance().delListener(this,Broadcast::kBroadcastMediaResetTracks);
    }

    void onRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){
        auto key = getRecorderKey(vhost,app,stream);
        lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
        auto it = _recorder_map.find(key);
        if(it == _recorder_map.end()){
            // 录像记录不存在
            return;
        }

        auto tracks = sender.getTracks(true);
        if (tracks.empty()) {
            // 无有效的tracks
            return;
        }

        if(it->second->getSchema() == schema){
            // 绑定的协议一致,替换tracks
            it->second->attachTracks(std::move(tracks));
        }

    }

    void onUnRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){
        auto key = getRecorderKey(vhost,app,stream);
        lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
        auto it = _recorder_map.find(key);
        if(it == _recorder_map.end()){
            // 录像记录不存在
            return;
        }

        if(it->second->getSchema() != schema){
            // 绑定的协议不一致
            return;
        }
        if(it->second->continueRecord()){
            // 如果可以继续录制，那么只重置tracks,不删除对象
            it->second->resetTracks();
        }else{
            // 删除对象(意味着可能删除hls临时文件)
            _recorder_map.erase(it);
        }
    }

    Recorder::status getRecordStatus_l(const string &key) {
        auto it = _recorder_map.find(key);
        if (it == _recorder_map.end()) {
            return Recorder::status_not_record;
        }
        return it->second->isRecording() ? Recorder::status_recording : Recorder::status_wait_record;
    }

    // 查找MediaSource以便录制
    vector<Track::Ptr> findTracks(const string &vhost, const string &app, const string &stream_id,string &schema) {
        auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id);
        if (src) {
            auto ret = src->getTracks(true);
            if (!ret.empty()) {
                schema = RTMP_SCHEMA;
                return std::move(ret);
            }
        }

        src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id);
        if (src) {
            schema = RTSP_SCHEMA;
            return src->getTracks(true);
        }
        return vector<Track::Ptr>();
    }

    string getRecorderKey(const string &vhost, const string &app, const string &stream_id) {
        return vhost + "/" + app + "/" + stream_id;
    }

    MediaSinkInterface *createRecorder(const string &vhost, const string &app, const string &stream_id) {
        MediaSinkInterface *ret = nullptr;
        switch (type) {
            case Recorder::type_hls:
                ret = createHlsRecorder(vhost, app, stream_id);
                break;
            case Recorder::type_mp4:
                ret = createMP4Recorder(vhost, app, stream_id);
                break;
            default:
                break;
        }
        if(!ret){
            WarnL << "can not recorder of: " << type;
        }
        return ret;
    }
private:
    recursive_mutex _recorder_mtx;
    unordered_map<string, RecorderHelper::Ptr> _recorder_map;
};

Recorder::status Recorder::getRecordStatus(Recorder::type type, const string &vhost, const string &app, const string &stream_id) {
    switch (type){
        case type_mp4:
            return MediaSourceWatcher<type_mp4>::Instance().getRecordStatus(vhost,app,stream_id);
        case type_hls:
            return MediaSourceWatcher<type_hls>::Instance().getRecordStatus(vhost,app,stream_id);
    }
    return status_not_record;
}

int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) {
    switch (type){
        case type_mp4:
            return MediaSourceWatcher<type_mp4>::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord);
        case type_hls:
            return MediaSourceWatcher<type_hls>::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord);
    }
    return -3;
}

void Recorder::stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id) {
    switch (type){
        case type_mp4:
            return MediaSourceWatcher<type_mp4>::Instance().stopRecord(vhost,app,stream_id);
        case type_hls:
            return MediaSourceWatcher<type_hls>::Instance().stopRecord(vhost,app,stream_id);
    }
}

} /* namespace mediakit */
<commit_msg>修复无法延后录制的bug<commit_after>/*
* MIT License
*
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* 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 "Recorder.h"
#include "Common/config.h"
#include "MP4Recorder.h"
#include "HlsRecorder.h"

using namespace toolkit;

namespace mediakit {

MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string &strApp, const string &strId) {
#if defined(ENABLE_HLS)
    GET_CONFIG(bool, enableVhost, General::kEnableVhost);
    GET_CONFIG(string, hlsPath, Hls::kFilePath);

    string strVhost = strVhost_tmp;
    if (trim(strVhost).empty()) {
        //如果strVhost为空，则强制为默认虚拟主机
        strVhost = DEFAULT_VHOST;
    }

    string m3u8FilePath;
    string params;
    if (enableVhost) {
        m3u8FilePath = strVhost + "/" + strApp + "/" + strId + "/hls.m3u8";
        params = string(VHOST_KEY) + "=" + strVhost;
    } else {
        m3u8FilePath = strApp + "/" + strId + "/hls.m3u8";
    }
    m3u8FilePath = File::absolutePath(m3u8FilePath, hlsPath);
    return new HlsRecorder(m3u8FilePath, params);
#else
    return nullptr;
#endif //defined(ENABLE_HLS)
}

MediaSinkInterface *createMP4Recorder(const string &strVhost_tmp, const string &strApp, const string &strId) {
#if defined(ENABLE_MP4RECORD)
    GET_CONFIG(bool, enableVhost, General::kEnableVhost);
    GET_CONFIG(string, recordPath, Record::kFilePath);
    GET_CONFIG(string, recordAppName, Record::kAppName);

    string strVhost = strVhost_tmp;
    if (trim(strVhost).empty()) {
        //如果strVhost为空，则强制为默认虚拟主机
        strVhost = DEFAULT_VHOST;
    }

    string mp4FilePath;
    if (enableVhost) {
        mp4FilePath = strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/";
    } else {
        mp4FilePath = recordAppName + "/" + strApp + "/" + strId + "/";
    }
    mp4FilePath = File::absolutePath(mp4FilePath, recordPath);
    return new MP4Recorder(mp4FilePath, strVhost, strApp, strId);
#else
    return nullptr;
#endif //defined(ENABLE_MP4RECORD)
}

////////////////////////////////////////////////////////////////////////////////////////

class RecorderHelper {
public:
    typedef std::shared_ptr<RecorderHelper> Ptr;

    /**
     * 构建函数
     * @param bContinueRecord false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除)
     */
    RecorderHelper(const MediaSinkInterface::Ptr &recorder, vector<Track::Ptr> &&tracks , bool bContinueRecord, const string &schema) {
        _recorder = recorder;
        _continueRecord = bContinueRecord;
        attachTracks(std::move(tracks),schema);
    }

    ~RecorderHelper() {
        resetTracks();
    }

    // 附则于track上
    void attachTracks(vector<Track::Ptr> &&tracks, const string &schema){
        if(isTracksSame(tracks)){
            return;
        }
        resetTracks();
        _tracks = std::move(tracks);
        _schema = schema;
        for (auto &track : _tracks) {
            _recorder->addTrack(track);
            track->addDelegate(_recorder);
        }
    }

    // 判断新的tracks是否与之前的一致
    bool isTracksSame(const vector<Track::Ptr> &tracks){
        if(tracks.size() != _tracks.size()) {
            return false;
        }
        int i = 0;
        for(auto &track : tracks){
            if(track != _tracks[i++]){
                return false;
            }
        }
        return true;
    }

    // 重置所有track
    void resetTracks(){
        if(_tracks.empty()){
            return;
        }
        for (auto &track : _tracks) {
            track->delDelegate(_recorder.get());
        }
        _tracks.clear();
        _recorder->resetTracks();
    }

    // 返回false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除)
    bool continueRecord(){
        return _continueRecord;
    }

    bool isRecording() {
        return !_tracks.empty();
    }

    const string &getSchema() const{
        return _schema;
    }
private:
    MediaSinkInterface::Ptr _recorder;
    vector<Track::Ptr> _tracks;
    bool _continueRecord;
    string _schema;
};

template<Recorder::type type>
class MediaSourceWatcher {
public:
    static MediaSourceWatcher& Instance(){
        static MediaSourceWatcher instance;
        return instance;
    }

    Recorder::status getRecordStatus(const string &vhost, const string &app, const string &stream_id) {
        return getRecordStatus_l(getRecorderKey(vhost, app, stream_id));
    }

    int startRecord(const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) {
        auto key = getRecorderKey(vhost, app, stream_id);
        lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
        if (getRecordStatus_l(key) != Recorder::status_not_record) {
            // 已经在录制了
            return 0;
        }

        string schema;
        auto tracks = findTracks(vhost, app, stream_id,schema);
        if (!waitForRecord && tracks.empty()) {
            // 暂时无法开启录制
            return -1;
        }

        auto recorder = MediaSinkInterface::Ptr(createRecorder(vhost, app, stream_id));
        if (!recorder) {
            // 创建录制器失败
            return -2;
        }
        _recorder_map[key] = std::make_shared<RecorderHelper>(recorder, std::move(tracks), continueRecord, schema);
        return 0;
    }

    void stopRecord(const string &vhost, const string &app, const string &stream_id) {
        lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
        _recorder_map.erase(getRecorderKey(vhost, app, stream_id));
    }

private:
    MediaSourceWatcher(){
        NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){
            if(bRegist){
                onRegist(schema,vhost,app,stream,sender);
            }else{
                onUnRegist(schema,vhost,app,stream,sender);
            }
        });
        NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){
            onRegist(schema,vhost,app,stream,sender);
        });
    }

    ~MediaSourceWatcher(){
        NoticeCenter::Instance().delListener(this,Broadcast::kBroadcastMediaChanged);
        NoticeCenter::Instance().delListener(this,Broadcast::kBroadcastMediaResetTracks);
    }

    void onRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){
        auto key = getRecorderKey(vhost,app,stream);
        lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
        auto it = _recorder_map.find(key);
        if(it == _recorder_map.end()){
            // 录像记录不存在
            return;
        }

        auto tracks = sender.getTracks(true);
        if (tracks.empty()) {
            // 无有效的tracks
            return;
        }

        if(!it->second->isRecording() || it->second->getSchema() == schema){
            // 绑定的协议一致,替换tracks
            it->second->attachTracks(std::move(tracks),schema);
        }

    }

    void onUnRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){
        auto key = getRecorderKey(vhost,app,stream);
        lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
        auto it = _recorder_map.find(key);
        if(it == _recorder_map.end()){
            // 录像记录不存在
            return;
        }

        if(it->second->getSchema() != schema){
            // 绑定的协议不一致
            return;
        }
        if(it->second->continueRecord()){
            // 如果可以继续录制，那么只重置tracks,不删除对象
            it->second->resetTracks();
        }else{
            // 删除对象(意味着可能删除hls临时文件)
            _recorder_map.erase(it);
        }
    }

    Recorder::status getRecordStatus_l(const string &key) {
        auto it = _recorder_map.find(key);
        if (it == _recorder_map.end()) {
            return Recorder::status_not_record;
        }
        return it->second->isRecording() ? Recorder::status_recording : Recorder::status_wait_record;
    }

    // 查找MediaSource以便录制
    vector<Track::Ptr> findTracks(const string &vhost, const string &app, const string &stream_id,string &schema) {
        auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id);
        if (src) {
            auto ret = src->getTracks(true);
            if (!ret.empty()) {
                schema = RTMP_SCHEMA;
                return std::move(ret);
            }
        }

        src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id);
        if (src) {
            schema = RTSP_SCHEMA;
            return src->getTracks(true);
        }
        return vector<Track::Ptr>();
    }

    string getRecorderKey(const string &vhost, const string &app, const string &stream_id) {
        return vhost + "/" + app + "/" + stream_id;
    }

    MediaSinkInterface *createRecorder(const string &vhost, const string &app, const string &stream_id) {
        MediaSinkInterface *ret = nullptr;
        switch (type) {
            case Recorder::type_hls:
                ret = createHlsRecorder(vhost, app, stream_id);
                break;
            case Recorder::type_mp4:
                ret = createMP4Recorder(vhost, app, stream_id);
                break;
            default:
                break;
        }
        if(!ret){
            WarnL << "can not recorder of: " << type;
        }
        return ret;
    }
private:
    recursive_mutex _recorder_mtx;
    unordered_map<string, RecorderHelper::Ptr> _recorder_map;
};

Recorder::status Recorder::getRecordStatus(Recorder::type type, const string &vhost, const string &app, const string &stream_id) {
    switch (type){
        case type_mp4:
            return MediaSourceWatcher<type_mp4>::Instance().getRecordStatus(vhost,app,stream_id);
        case type_hls:
            return MediaSourceWatcher<type_hls>::Instance().getRecordStatus(vhost,app,stream_id);
    }
    return status_not_record;
}

int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) {
    switch (type){
        case type_mp4:
            return MediaSourceWatcher<type_mp4>::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord);
        case type_hls:
            return MediaSourceWatcher<type_hls>::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord);
    }
    return -3;
}

void Recorder::stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id) {
    switch (type){
        case type_mp4:
            return MediaSourceWatcher<type_mp4>::Instance().stopRecord(vhost,app,stream_id);
        case type_hls:
            return MediaSourceWatcher<type_hls>::Instance().stopRecord(vhost,app,stream_id);
    }
}

} /* namespace mediakit */
<|endoftext|>


<commit_before>/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <geos/geom/util/CoordinateOperation.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/LineString.h>
#include <geos/geom/Point.h>
#include <geos/geom/Geometry.h>
#include <geos/geom/GeometryFactory.h>

namespace geos {
namespace geom { // geos.geom
namespace util { // geos.geom.util

Geometry*
CoordinateOperation::edit(const Geometry *geometry,
		const GeometryFactory *factory)
{
		const LinearRing *ring = dynamic_cast<const LinearRing *>(geometry);
		if (ring) {
			const CoordinateSequence *coords = ring->getCoordinatesRO();
			CoordinateSequence *newCoords = edit(coords,geometry);
            // LinearRing instance takes over ownership of newCoords instance
			return factory->createLinearRing(newCoords);
		}
		const LineString *line = dynamic_cast<const LineString *>(geometry);
		if (line) {
			const CoordinateSequence *coords = line->getCoordinatesRO();
			CoordinateSequence *newCoords = edit(coords,geometry);
			return factory->createLineString(newCoords);
		}
		if (typeid(*geometry)==typeid(Point)) {
			CoordinateSequence *coords = geometry->getCoordinates();
			CoordinateSequence *newCoords = edit(coords,geometry);
			delete coords;
			return factory->createPoint(newCoords);
		}

		return geometry->clone();
}

} // namespace geos.geom.util
} // namespace geos.geom
} // namespace geos
<commit_msg>Fixes for build on both windows and Linux<commit_after>/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#ifndef _MSC_VER
#include <typeinfo>
#endif

#include <geos/geom/util/CoordinateOperation.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/LineString.h>
#include <geos/geom/Point.h>
#include <geos/geom/Geometry.h>
#include <geos/geom/GeometryFactory.h>

namespace geos {
namespace geom { // geos.geom
namespace util { // geos.geom.util

Geometry*
CoordinateOperation::edit(const Geometry *geometry,
		const GeometryFactory *factory)
{
		const LinearRing *ring = dynamic_cast<const LinearRing *>(geometry);
		if (ring) {
			const CoordinateSequence *coords = ring->getCoordinatesRO();
			CoordinateSequence *newCoords = edit(coords,geometry);
            // LinearRing instance takes over ownership of newCoords instance
			return factory->createLinearRing(newCoords);
		}
		const LineString *line = dynamic_cast<const LineString *>(geometry);
		if (line) {
			const CoordinateSequence *coords = line->getCoordinatesRO();
			CoordinateSequence *newCoords = edit(coords,geometry);
			return factory->createLineString(newCoords);
		}
		if (typeid(*geometry)==typeid(Point)) {
			CoordinateSequence *coords = geometry->getCoordinates();
			CoordinateSequence *newCoords = edit(coords,geometry);
			delete coords;
			return factory->createPoint(newCoords);
		}

		return geometry->clone();
}

} // namespace geos.geom.util
} // namespace geos.geom
} // namespace geos
<|endoftext|>


<commit_before>#include "openMVG/sfm/sfm.hpp"
#include "openMVG/sfm/sfm_data_io.hpp"

#include "software/SfM/io_readGT.hpp"
#include "software/SfM/tools_precisionEvaluationToGt.hpp"

#include "third_party/cmdLine/cmdLine.h"

#include <vector>
#include <iostream>

using namespace openMVG;
using namespace openMVG::sfm;

int main(int argc, char **argv)
{
	// Command line configuration
	//
	CmdLine cmd;

  std::string
    sSfmFile,
    sGTDirectory,
    sOutFile = "";
  bool mayaTransform = false;

  cmd.add( make_option('i', sSfmFile, "sfm") );
  cmd.add( make_option('g', sGTDirectory, "gt") );
  cmd.add( make_option('o', sOutFile, "output") );
  cmd.add( make_option('m', mayaTransform, "maya") );

  try {
    if (argc == 1) throw std::string("Invalid command line parameter.");
    cmd.process(argc, argv);
  } catch(const std::string& s) {
    std::cerr << "Usage: " << argv[0] << '\n'
      << "[-i|--sfm] path (OpenMVG Json file, ouput from SfMInit_ImageListing)\n"
      << "[-g|--gt] path (where ground truth camera trajectory are saved)\n"
      << "[-o|--output] path (where export file, .json or .abc, will be save)\n"
      << "[-m|--maya] enable [1,-1,-1] transformation if ground truth comes from Maya\n"
      << std::endl;

    std::cerr << s << std::endl;
    return EXIT_FAILURE;
  }

  // Setup the camera type and the appropriate camera reader
  //
  bool (*fcnReadCamPtr)(const std::string&, Pinhole_Intrinsic&, geometry::Pose3&);
  int camType;
  std::string suffix;

  if (!stlplus::folder_wildcard(sGTDirectory, "*.bin", false, true).empty())
    camType = 1;
  else if (!stlplus::folder_wildcard(sGTDirectory, "*.png.camera", false, true).empty())
    camType = 2;
  else if (!stlplus::folder_wildcard(sGTDirectory, "*.jpg.camera", false, true).empty())
    camType = 3;
  else if (!stlplus::folder_wildcard(sGTDirectory, "*.PNG.camera", false, true).empty())
    camType = 4;
  else if (!stlplus::folder_wildcard(sGTDirectory, "*.JPG.camera", false, true).empty())
    camType = 5;
  else
    camType = std::numeric_limits<int>::infinity();
  switch (camType)
  {
    case 1:
      std::cout << "\nusing openMVG Camera";
      fcnReadCamPtr = &read_openMVG_Camera;
      suffix = "bin";
      break;
    case 2:
      std::cout << "\nusing Strechas Camera (png)";
      fcnReadCamPtr = &read_Strecha_Camera;
      suffix = "png.camera";
      break;
    case 3:
      std::cout << "\nusing Strechas Camera (jpg)";
      fcnReadCamPtr = &read_Strecha_Camera;
      suffix = "jpg.camera";
      break;
    case 4:
      std::cout << "\nusing Strechas Camera (PNG)";
      fcnReadCamPtr = &read_Strecha_Camera;
      suffix = "PNG.camera";
      break;
    case 5:
      std::cout << "\nusing Strechas Camera (JPG)";
      fcnReadCamPtr = &read_Strecha_Camera;
      suffix = "JPG.camera";
      break;
    default:
      std::cerr << "Unsupported camera type. Please write your camera reader." << std::endl;
      return EXIT_FAILURE;
  }

  // Load JSON
  //
  SfM_Data sfm_data_in;
  if (!Load(sfm_data_in, sSfmFile, ESfM_Data(VIEWS|INTRINSICS|EXTRINSICS)))
  {
  	std::cerr << "ERROR" << std::endl;
  	std::cerr << "The input SfM_Data file \"" << sSfmFile << "\" cannot be read." << std::endl;
  	return EXIT_FAILURE;
  }

  // Load GT
  //
  SfM_Data sfm_data_gt;
  std::vector<std::string> vec_fileNames;
  readGt(fcnReadCamPtr, sGTDirectory, suffix, vec_fileNames, sfm_data_gt.poses, sfm_data_gt.intrinsics);
  std:cout << sfm_data_gt.poses.size() << " gt cameras have been found" << std::endl;

  // Fill sfm_data_in with poses from sfm_data_gt
  //
  for(const auto &iter : sfm_data_in.GetViews())
  {
  	const auto &view = iter.second;
  	const std::string sImageName = stlplus::filename_part(view->s_Img_path);
  	int idGT = findIdGT(sImageName, vec_fileNames);
  	if(idGT == -1)
  	{
  		std::cerr << "No ground truth for file: " << sImageName << std::endl;
  		continue;
  	}
  	else
  	{
  		geometry::Pose3 poseGT = sfm_data_gt.GetPoses().at(idGT);
  		Vec3 vecMaya;
  		if(mayaTransform)
  			vecMaya = {1,-1,-1};
  		else
  			vecMaya = {1,1,1};
  		geometry::Pose3 poseIN(vecMaya.asDiagonal() * poseGT.rotation(), poseGT.center());

  		sfm_data_in.poses.emplace(view->id_pose, poseIN);
  		sfm_data_in.intrinsics[view->id_intrinsic] = sfm_data_gt.intrinsics.at(idGT);
  	}
  }

  std::cout << "Saved: " << Save(sfm_data_in, sOutFile, ESfM_Data(VIEWS|INTRINSICS|EXTRINSICS)) << std::endl;

	return 0;
}

<commit_msg>change tabs for spaces<commit_after>#include "openMVG/sfm/sfm.hpp"
#include "openMVG/sfm/sfm_data_io.hpp"

#include "software/SfM/io_readGT.hpp"
#include "software/SfM/tools_precisionEvaluationToGt.hpp"

#include "third_party/cmdLine/cmdLine.h"

#include <vector>
#include <iostream>

using namespace openMVG;
using namespace openMVG::sfm;

int main(int argc, char **argv)
{
  // Command line configuration
  //
  CmdLine cmd;

  std::string
    sSfmFile,
    sGTDirectory,
    sOutFile = "";
  bool mayaTransform = false;

  cmd.add( make_option('i', sSfmFile, "sfm") );
  cmd.add( make_option('g', sGTDirectory, "gt") );
  cmd.add( make_option('o', sOutFile, "output") );
  cmd.add( make_option('m', mayaTransform, "maya") );

  try {
    if (argc == 1) throw std::string("Invalid command line parameter.");
    cmd.process(argc, argv);
  } catch(const std::string& s) {
    std::cerr << "Usage: " << argv[0] << '\n'
      << "[-i|--sfm] path (OpenMVG Json file, ouput from SfMInit_ImageListing)\n"
      << "[-g|--gt] path (where ground truth camera trajectory are saved)\n"
      << "[-o|--output] path (where export file, .json or .abc, will be save)\n"
      << "[-m|--maya] enable [1,-1,-1] transformation if ground truth comes from Maya\n"
      << std::endl;

    std::cerr << s << std::endl;
    return EXIT_FAILURE;
  }

  // Setup the camera type and the appropriate camera reader
  //
  bool (*fcnReadCamPtr)(const std::string&, Pinhole_Intrinsic&, geometry::Pose3&);
  int camType;
  std::string suffix;

  if (!stlplus::folder_wildcard(sGTDirectory, "*.bin", false, true).empty())
    camType = 1;
  else if (!stlplus::folder_wildcard(sGTDirectory, "*.png.camera", false, true).empty())
    camType = 2;
  else if (!stlplus::folder_wildcard(sGTDirectory, "*.jpg.camera", false, true).empty())
    camType = 3;
  else if (!stlplus::folder_wildcard(sGTDirectory, "*.PNG.camera", false, true).empty())
    camType = 4;
  else if (!stlplus::folder_wildcard(sGTDirectory, "*.JPG.camera", false, true).empty())
    camType = 5;
  else
    camType = std::numeric_limits<int>::infinity();
  switch (camType)
  {
    case 1:
      std::cout << "\nusing openMVG Camera";
      fcnReadCamPtr = &read_openMVG_Camera;
      suffix = "bin";
      break;
    case 2:
      std::cout << "\nusing Strechas Camera (png)";
      fcnReadCamPtr = &read_Strecha_Camera;
      suffix = "png.camera";
      break;
    case 3:
      std::cout << "\nusing Strechas Camera (jpg)";
      fcnReadCamPtr = &read_Strecha_Camera;
      suffix = "jpg.camera";
      break;
    case 4:
      std::cout << "\nusing Strechas Camera (PNG)";
      fcnReadCamPtr = &read_Strecha_Camera;
      suffix = "PNG.camera";
      break;
    case 5:
      std::cout << "\nusing Strechas Camera (JPG)";
      fcnReadCamPtr = &read_Strecha_Camera;
      suffix = "JPG.camera";
      break;
    default:
      std::cerr << "Unsupported camera type. Please write your camera reader." << std::endl;
      return EXIT_FAILURE;
  }

  // Load JSON
  //
  SfM_Data sfm_data_in;
  if (!Load(sfm_data_in, sSfmFile, ESfM_Data(VIEWS|INTRINSICS|EXTRINSICS)))
  {
    std::cerr << "ERROR" << std::endl;
    std::cerr << "The input SfM_Data file \"" << sSfmFile << "\" cannot be read." << std::endl;
    return EXIT_FAILURE;
  }

  // Load GT
  //
  SfM_Data sfm_data_gt;
  std::vector<std::string> vec_fileNames;
  readGt(fcnReadCamPtr, sGTDirectory, suffix, vec_fileNames, sfm_data_gt.poses, sfm_data_gt.intrinsics);
  std:cout << sfm_data_gt.poses.size() << " gt cameras have been found" << std::endl;

  // Fill sfm_data_in with poses from sfm_data_gt
  //
  for(const auto &iter : sfm_data_in.GetViews())
  {
    const auto &view = iter.second;
    const std::string sImageName = stlplus::filename_part(view->s_Img_path);
    int idGT = findIdGT(sImageName, vec_fileNames);
    if(idGT == -1)
    {
      std::cerr << "No ground truth for file: " << sImageName << std::endl;
      continue;
    }
    else
    {
      geometry::Pose3 poseGT = sfm_data_gt.GetPoses().at(idGT);
      Vec3 vecMaya;
      if(mayaTransform)
        vecMaya = {1,-1,-1};
      else
        vecMaya = {1,1,1};
      geometry::Pose3 poseIN(vecMaya.asDiagonal() * poseGT.rotation(), poseGT.center());

      sfm_data_in.poses.emplace(view->id_pose, poseIN);
      sfm_data_in.intrinsics[view->id_intrinsic] = sfm_data_gt.intrinsics.at(idGT);
    }
  }

  std::cout << "Saved: " << Save(sfm_data_in, sOutFile, ESfM_Data(VIEWS|INTRINSICS|EXTRINSICS)) << std::endl;

  return 0;
}

<|endoftext|>


<commit_before>// The libMesh Finite Element Library.
// Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// 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.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh/petsc_auto_fieldsplit.h"

#ifdef LIBMESH_HAVE_PETSC

#if !PETSC_VERSION_LESS_THAN(3,2,0)

// Local includes
#include "libmesh/dof_map.h"
#include "libmesh/system.h"

EXTERN_C_FOR_PETSC_BEGIN
#  include <petscksp.h>
EXTERN_C_FOR_PETSC_END

// C++ includes

namespace {

void indices_to_fieldsplit (const libMesh::Parallel::Communicator& comm,
                            const std::vector<dof_id_type>& indices,
                            PC my_pc,
                            const std::string& field_name)
{
  const PetscInt *idx = PETSC_NULL;
  if (!indices.empty())
  idx = reinterpret_cast<const PetscInt*>(&indices[0]);

  IS is;
  int ierr = ISCreateLibMesh(comm.get(), indices.size(),
  idx, PETSC_COPY_VALUES, &is);
  CHKERRABORT(comm.get(), ierr);

  ierr = PCFieldSplitSetIS(my_pc, field_name.c_str(), is);
  CHKERRABORT(comm.get(), ierr);
}

}

namespace libMesh
{

void petsc_auto_fieldsplit (PC my_pc,
                            const System &sys)
{
  std::string sys_prefix = "--solver_group_";

  if (libMesh::on_command_line("--solver_system_names"))
    {
      sys_prefix = sys_prefix + sys.name() + "_";
    }

  std::map<std::string, std::vector<dof_id_type> > group_indices;

  if (libMesh::on_command_line("--solver_variable_names"))
    {
      for (unsigned int v = 0; v != sys.n_vars(); ++v)
        {
          const std::string& var_name = sys.variable_name(v);

          std::vector<dof_id_type> var_idx;
          sys.get_dof_map().local_variable_indices
            (var_idx, sys.get_mesh(), v);

          std::string group_command = sys_prefix + var_name;

          if (libMesh::on_command_line (group_command))
            {
              std::string group_name = libMesh::command_line_value
                (group_command, std::string());

              std::vector<dof_id_type> &indices =
                      group_indices[group_name];
              const bool prior_indices = !indices.empty();
              indices.insert(indices.end(), var_idx.begin(),
                             var_idx.end());
              if (prior_indices)
                std::sort(indices.begin(), indices.end());
            }
          else
            {
              indices_to_fieldsplit (sys.comm(), var_idx, my_pc, var_name);
            }
        }
    }

  for (std::map<std::string, std::vector<dof_id_type> >::const_iterator
       i = group_indices.begin(); i != group_indices.end(); ++i)
    {
      indices_to_fieldsplit(sys.comm(), i->second, my_pc, i->first);
    }
}

} // namespace libMesh

#else  // #PETSC_VERSION < 3.2.0
void assign_solver_fieldsplit_names (PC my_pc,
                                     const System &sys)
{
  if (libMesh::on_command_line("--solver_variable_names"))
    {
      libmesh_do_once(
        libMesh::out <<
          "WARNING: libMesh does not support setting field splits" <<
          std::endl << "with PETSc " <<
          LIBMESH_DETECTED_PETSC_VERSION_MAJOR << '.'
          LIBMESH_DETECTED_PETSC_VERSION_MINOR << '.'
          LIBMESH_DETECTED_PETSC_VERSION_SUBMINOR << std::endl;);
    }
}
#endif // #PETSC_VERSION > 3.2.0
#endif // #ifdef LIBMESH_HAVE_PETSC
<commit_msg>Use consistent name for old PETSc case<commit_after>// The libMesh Finite Element Library.
// Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// 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.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh/petsc_auto_fieldsplit.h"

#ifdef LIBMESH_HAVE_PETSC

#if !PETSC_VERSION_LESS_THAN(3,2,0)

// Local includes
#include "libmesh/dof_map.h"
#include "libmesh/system.h"

EXTERN_C_FOR_PETSC_BEGIN
#  include <petscksp.h>
EXTERN_C_FOR_PETSC_END

// C++ includes

namespace {

void indices_to_fieldsplit (const libMesh::Parallel::Communicator& comm,
                            const std::vector<dof_id_type>& indices,
                            PC my_pc,
                            const std::string& field_name)
{
  const PetscInt *idx = PETSC_NULL;
  if (!indices.empty())
  idx = reinterpret_cast<const PetscInt*>(&indices[0]);

  IS is;
  int ierr = ISCreateLibMesh(comm.get(), indices.size(),
  idx, PETSC_COPY_VALUES, &is);
  CHKERRABORT(comm.get(), ierr);

  ierr = PCFieldSplitSetIS(my_pc, field_name.c_str(), is);
  CHKERRABORT(comm.get(), ierr);
}

}

namespace libMesh
{

void petsc_auto_fieldsplit (PC my_pc,
                            const System &sys)
{
  std::string sys_prefix = "--solver_group_";

  if (libMesh::on_command_line("--solver_system_names"))
    {
      sys_prefix = sys_prefix + sys.name() + "_";
    }

  std::map<std::string, std::vector<dof_id_type> > group_indices;

  if (libMesh::on_command_line("--solver_variable_names"))
    {
      for (unsigned int v = 0; v != sys.n_vars(); ++v)
        {
          const std::string& var_name = sys.variable_name(v);

          std::vector<dof_id_type> var_idx;
          sys.get_dof_map().local_variable_indices
            (var_idx, sys.get_mesh(), v);

          std::string group_command = sys_prefix + var_name;

          if (libMesh::on_command_line (group_command))
            {
              std::string group_name = libMesh::command_line_value
                (group_command, std::string());

              std::vector<dof_id_type> &indices =
                      group_indices[group_name];
              const bool prior_indices = !indices.empty();
              indices.insert(indices.end(), var_idx.begin(),
                             var_idx.end());
              if (prior_indices)
                std::sort(indices.begin(), indices.end());
            }
          else
            {
              indices_to_fieldsplit (sys.comm(), var_idx, my_pc, var_name);
            }
        }
    }

  for (std::map<std::string, std::vector<dof_id_type> >::const_iterator
       i = group_indices.begin(); i != group_indices.end(); ++i)
    {
      indices_to_fieldsplit(sys.comm(), i->second, my_pc, i->first);
    }
}

} // namespace libMesh

#else  // #PETSC_VERSION < 3.2.0
void petsc_auto_fieldsplit (PC my_pc,
                            const System &sys)
{
  if (libMesh::on_command_line("--solver_variable_names"))
    {
      libmesh_do_once(
        libMesh::out <<
          "WARNING: libMesh does not support setting field splits" <<
          std::endl << "with PETSc " <<
          LIBMESH_DETECTED_PETSC_VERSION_MAJOR << '.'
          LIBMESH_DETECTED_PETSC_VERSION_MINOR << '.'
          LIBMESH_DETECTED_PETSC_VERSION_SUBMINOR << std::endl;);
    }
}
#endif // #PETSC_VERSION > 3.2.0
#endif // #ifdef LIBMESH_HAVE_PETSC
<|endoftext|>