You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
9.9KB

  1. /*
  2. * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
  3. *
  4. * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
  5. *
  6. * Use of this source code is governed by MIT license that can be found in the
  7. * LICENSE file in the root of the source tree. All contributing project authors
  8. * may be found in the AUTHORS file in the root of the source tree.
  9. */
  10. #include <signal.h>
  11. #include <atomic>
  12. #include <iostream>
  13. #include "Util/logger.h"
  14. #include "Util/onceToken.h"
  15. #include "Util/CMD.h"
  16. #include "Common/config.h"
  17. #include "Common/Parser.h"
  18. #include "Rtsp/Rtsp.h"
  19. #include "Thread/WorkThreadPool.h"
  20. #include "Pusher/MediaPusher.h"
  21. #include "Player/PlayerProxy.h"
  22. using namespace std;
  23. using namespace toolkit;
  24. using namespace mediakit;
  25. class CMD_main : public CMD {
  26. public:
  27. CMD_main() {
  28. _parser.reset(new OptionParser(nullptr));
  29. (*_parser) << Option('l',/*该选项简称,如果是\x00则说明无简称*/
  30. "level",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  31. Option::ArgRequired,/*该选项后面必须跟值*/
  32. to_string(LTrace).data(),/*该选项默认值*/
  33. false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  34. "日志等级,LTrace~LError(0~4)",/*该选项说明文字*/
  35. nullptr);
  36. (*_parser) << Option('t',/*该选项简称,如果是\x00则说明无简称*/
  37. "threads",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  38. Option::ArgRequired,/*该选项后面必须跟值*/
  39. to_string(thread::hardware_concurrency()).data(),/*该选项默认值*/
  40. false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  41. "启动事件触发线程数",/*该选项说明文字*/
  42. nullptr);
  43. (*_parser) << Option('i',/*该选项简称,如果是\x00则说明无简称*/
  44. "in",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  45. Option::ArgRequired,/*该选项后面必须跟值*/
  46. nullptr,/*该选项默认值*/
  47. true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  48. "拉流url,支持rtsp/rtmp/hls",/*该选项说明文字*/
  49. nullptr);
  50. (*_parser) << Option('o',/*该选项简称,如果是\x00则说明无简称*/
  51. "out",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  52. Option::ArgRequired,/*该选项后面必须跟值*/
  53. nullptr,/*该选项默认值*/
  54. true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  55. "推流url,支持rtsp/rtmp",/*该选项说明文字*/
  56. nullptr);
  57. (*_parser) << Option('c',/*该选项简称,如果是\x00则说明无简称*/
  58. "count",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  59. Option::ArgRequired,/*该选项后面必须跟值*/
  60. "1000",/*该选项默认值*/
  61. true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  62. "推流客户端个数",/*该选项说明文字*/
  63. nullptr);
  64. (*_parser) << Option('d',/*该选项简称,如果是\x00则说明无简称*/
  65. "delay",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  66. Option::ArgRequired,/*该选项后面必须跟值*/
  67. "50",/*该选项默认值*/
  68. true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  69. "启动推流客户端间隔,单位毫秒",/*该选项说明文字*/
  70. nullptr);
  71. (*_parser) << Option('m',/*该选项简称,如果是\x00则说明无简称*/
  72. "merge",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  73. Option::ArgRequired,/*该选项后面必须跟值*/
  74. "300",/*该选项默认值*/
  75. true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  76. "推流合并写毫秒,合并写能提高性能",/*该选项说明文字*/
  77. nullptr);
  78. (*_parser) << Option('T',/*该选项简称,如果是\x00则说明无简称*/
  79. "rtp",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  80. Option::ArgRequired,/*该选项后面必须跟值*/
  81. to_string((int) (Rtsp::RTP_TCP)).data(),/*该选项默认值*/
  82. true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  83. "rtsp拉流和推流方式,支持tcp/udp:0/1",/*该选项说明文字*/
  84. nullptr);
  85. }
  86. ~CMD_main() override {}
  87. const char *description() const override {
  88. return "主程序命令参数";
  89. }
  90. };
  91. //此程序用于推流性能测试
  92. int main(int argc, char *argv[]) {
  93. CMD_main cmd_main;
  94. try {
  95. cmd_main.operator()(argc, argv);
  96. } catch (ExitException &) {
  97. return 0;
  98. } catch (std::exception &ex) {
  99. cout << ex.what() << endl;
  100. return -1;
  101. }
  102. int threads = cmd_main["threads"];
  103. LogLevel logLevel = (LogLevel) cmd_main["level"].as<int>();
  104. logLevel = MIN(MAX(logLevel, LTrace), LError);
  105. auto in_url = cmd_main["in"];
  106. auto out_url = cmd_main["out"];
  107. auto rtp_type = cmd_main["rtp"].as<int>();
  108. auto delay_ms = cmd_main["delay"].as<int>();
  109. auto pusher_count = cmd_main["count"].as<int>();
  110. auto merge_ms = cmd_main["merge"].as<int>();
  111. auto schema = FindField(out_url.data(), nullptr, "://");
  112. if (schema != RTSP_SCHEMA && schema != RTMP_SCHEMA) {
  113. cout << "推流协议只支持rtsp或rtmp!" << endl;
  114. return -1;
  115. }
  116. //设置日志
  117. Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
  118. //启动异步日志线程
  119. Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
  120. //设置线程数
  121. EventPollerPool::setPoolSize(threads);
  122. WorkThreadPool::setPoolSize(threads);
  123. //设置合并写
  124. mINI::Instance()[General::kMergeWriteMS] = merge_ms;
  125. ProtocolOption option;
  126. option.enable_hls = false;
  127. option.enable_mp4 = false;
  128. //添加拉流代理
  129. auto proxy = std::make_shared<PlayerProxy>(DEFAULT_VHOST, "app", "test", option);
  130. //rtsp拉流代理方式
  131. (*proxy)[Client::kRtpType] = rtp_type;
  132. //开始拉流代理
  133. proxy->play(in_url);
  134. auto get_src = [schema]() {
  135. return MediaSource::find(schema, DEFAULT_VHOST, "app", "test", false);
  136. };
  137. //推流器map
  138. recursive_mutex mtx;
  139. unordered_map<void *, MediaPusher::Ptr> pusher_map;
  140. auto add_pusher = [&](const MediaSource::Ptr &src, const string &rand_str, size_t index) {
  141. auto pusher = std::make_shared<MediaPusher>(src);
  142. auto tag = pusher.get();
  143. pusher->setOnCreateSocket([](const EventPoller::Ptr &poller) {
  144. //socket关闭互斥锁,提高性能
  145. return std::make_shared<Socket>(poller, false);
  146. });
  147. //设置推流失败监听
  148. pusher->setOnPublished([&mtx, &pusher_map, tag](const SockException &ex) {
  149. if (ex) {
  150. //推流失败,移除之
  151. lock_guard<recursive_mutex> lck(mtx);
  152. pusher_map.erase(tag);
  153. }
  154. });
  155. //设置推流中途断开监听
  156. pusher->setOnShutdown([&mtx, &pusher_map, tag](const SockException &ex) {
  157. //推流中途失败,移除之
  158. lock_guard<recursive_mutex> lck(mtx);
  159. pusher_map.erase(tag);
  160. });
  161. //设置rtsp推流方式(在rtsp推流时有效)
  162. (*pusher)[Client::kRtpType] = rtp_type;
  163. //发起推流请求,每个推流端的stream_id都不一样
  164. string url = StrPrinter << out_url << "_" << rand_str << "_" << index;
  165. pusher->publish(url);
  166. //保持对象不销毁
  167. lock_guard<recursive_mutex> lck(mtx);
  168. pusher_map.emplace(tag, std::move(pusher));
  169. //休眠后再启动下一个推流,防止短时间海量链接
  170. if (delay_ms > 0) {
  171. usleep(1000 * delay_ms);
  172. }
  173. };
  174. // 设置退出信号
  175. static bool exit_flag = false;
  176. signal(SIGINT, [](int) { exit_flag = true; });
  177. while (!exit_flag) {
  178. //休眠一秒打印
  179. sleep(1);
  180. size_t alive_pusher = 0;
  181. {
  182. lock_guard<recursive_mutex> lck(mtx);
  183. alive_pusher = pusher_map.size();
  184. }
  185. InfoL << "在线推流器个数:" << alive_pusher;
  186. auto src = get_src();
  187. for(size_t i = 0; i < pusher_count - alive_pusher && src && !exit_flag; ++i){
  188. //有些推流器失败了,那么我们重试添加
  189. add_pusher(get_src(), makeRandStr(8), i);
  190. }
  191. }
  192. return 0;
  193. }