window-basic-status-bar.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. #include <QLabel>
  2. #include "obs-app.hpp"
  3. #include "window-basic-main.hpp"
  4. #include "window-basic-status-bar.hpp"
  5. #include "window-basic-main-outputs.hpp"
  6. OBSBasicStatusBar::OBSBasicStatusBar(QWidget *parent)
  7. : QStatusBar (parent),
  8. delayInfo (new QLabel),
  9. droppedFrames (new QLabel),
  10. sessionTime (new QLabel),
  11. cpuUsage (new QLabel),
  12. kbps (new QLabel)
  13. {
  14. sessionTime->setText(QString("00:00:00"));
  15. cpuUsage->setText(QString("CPU: 0.0%"));
  16. delayInfo->setAlignment(Qt::AlignRight);
  17. droppedFrames->setAlignment(Qt::AlignRight);
  18. sessionTime->setAlignment(Qt::AlignRight);
  19. cpuUsage->setAlignment(Qt::AlignRight);
  20. kbps->setAlignment(Qt::AlignRight);
  21. delayInfo->setIndent(20);
  22. droppedFrames->setIndent(20);
  23. sessionTime->setIndent(20);
  24. cpuUsage->setIndent(20);
  25. kbps->setIndent(10);
  26. addPermanentWidget(droppedFrames);
  27. addPermanentWidget(sessionTime);
  28. addPermanentWidget(cpuUsage);
  29. addPermanentWidget(delayInfo);
  30. addPermanentWidget(kbps);
  31. }
  32. void OBSBasicStatusBar::Activate()
  33. {
  34. if (!active) {
  35. refreshTimer = new QTimer(this);
  36. connect(refreshTimer, SIGNAL(timeout()),
  37. this, SLOT(UpdateStatusBar()));
  38. int skipped = video_output_get_skipped_frames(obs_get_video());
  39. int total = video_output_get_total_frames(obs_get_video());
  40. totalSeconds = 0;
  41. lastSkippedFrameCount = 0;
  42. startSkippedFrameCount = skipped;
  43. startTotalFrameCount = total;
  44. refreshTimer->start(1000);
  45. active = true;
  46. }
  47. }
  48. void OBSBasicStatusBar::Deactivate()
  49. {
  50. OBSBasic *main = qobject_cast<OBSBasic*>(parent());
  51. if (!main)
  52. return;
  53. if (!main->outputHandler->Active()) {
  54. delete refreshTimer;
  55. sessionTime->setText(QString("00:00:00"));
  56. delayInfo->setText("");
  57. droppedFrames->setText("");
  58. kbps->setText("");
  59. delaySecTotal = 0;
  60. delaySecStarting = 0;
  61. delaySecStopping = 0;
  62. reconnectTimeout = 0;
  63. active = false;
  64. overloadedNotify = true;
  65. }
  66. }
  67. void OBSBasicStatusBar::UpdateDelayMsg()
  68. {
  69. QString msg;
  70. if (delaySecTotal) {
  71. if (delaySecStarting && !delaySecStopping) {
  72. msg = QTStr("Basic.StatusBar.DelayStartingIn");
  73. msg = msg.arg(QString::number(delaySecStarting));
  74. } else if (!delaySecStarting && delaySecStopping) {
  75. msg = QTStr("Basic.StatusBar.DelayStoppingIn");
  76. msg = msg.arg(QString::number(delaySecStopping));
  77. } else if (delaySecStarting && delaySecStopping) {
  78. msg = QTStr("Basic.StatusBar.DelayStartingStoppingIn");
  79. msg = msg.arg(QString::number(delaySecStopping),
  80. QString::number(delaySecStarting));
  81. } else {
  82. msg = QTStr("Basic.StatusBar.Delay");
  83. msg = msg.arg(QString::number(delaySecTotal));
  84. }
  85. }
  86. delayInfo->setText(msg);
  87. }
  88. #define BITRATE_UPDATE_SECONDS 2
  89. void OBSBasicStatusBar::UpdateBandwidth()
  90. {
  91. if (!streamOutput)
  92. return;
  93. if (++bitrateUpdateSeconds < BITRATE_UPDATE_SECONDS)
  94. return;
  95. uint64_t bytesSent = obs_output_get_total_bytes(streamOutput);
  96. uint64_t bytesSentTime = os_gettime_ns();
  97. if (bytesSent < lastBytesSent)
  98. bytesSent = 0;
  99. if (bytesSent == 0)
  100. lastBytesSent = 0;
  101. uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;
  102. double timePassed = double(bytesSentTime - lastBytesSentTime) /
  103. 1000000000.0;
  104. double kbitsPerSec = double(bitsBetween) / timePassed / 1000.0;
  105. QString text;
  106. text += QString("kb/s: ") +
  107. QString::number(kbitsPerSec, 'f', 0);
  108. kbps->setText(text);
  109. kbps->setMinimumWidth(kbps->width());
  110. lastBytesSent = bytesSent;
  111. lastBytesSentTime = bytesSentTime;
  112. bitrateUpdateSeconds = 0;
  113. }
  114. void OBSBasicStatusBar::UpdateCPUUsage()
  115. {
  116. OBSBasic *main = qobject_cast<OBSBasic*>(parent());
  117. if (!main)
  118. return;
  119. QString text;
  120. text += QString("CPU: ") +
  121. QString::number(main->GetCPUUsage(), 'f', 1) + QString("%, ") +
  122. QString::number(obs_get_active_fps(), 'f', 2) + QString(" fps");
  123. cpuUsage->setText(text);
  124. cpuUsage->setMinimumWidth(cpuUsage->width());
  125. }
  126. void OBSBasicStatusBar::UpdateSessionTime()
  127. {
  128. totalSeconds++;
  129. int seconds = totalSeconds % 60;
  130. int totalMinutes = totalSeconds / 60;
  131. int minutes = totalMinutes % 60;
  132. int hours = totalMinutes / 60;
  133. QString text;
  134. text.sprintf("%02d:%02d:%02d", hours, minutes, seconds);
  135. sessionTime->setText(text);
  136. sessionTime->setMinimumWidth(sessionTime->width());
  137. if (reconnectTimeout > 0) {
  138. QString msg = QTStr("Basic.StatusBar.Reconnecting")
  139. .arg(QString::number(retries),
  140. QString::number(reconnectTimeout));
  141. showMessage(msg);
  142. reconnectTimeout--;
  143. } else if (retries > 0) {
  144. QString msg = QTStr("Basic.StatusBar.AttemptingReconnect");
  145. showMessage(msg.arg(QString::number(retries)));
  146. }
  147. if (delaySecStopping > 0 || delaySecStarting > 0) {
  148. if (delaySecStopping > 0)
  149. --delaySecStopping;
  150. if (delaySecStarting > 0)
  151. --delaySecStarting;
  152. UpdateDelayMsg();
  153. }
  154. }
  155. void OBSBasicStatusBar::UpdateDroppedFrames()
  156. {
  157. if (!streamOutput)
  158. return;
  159. int totalDropped = obs_output_get_frames_dropped(streamOutput);
  160. int totalFrames = obs_output_get_total_frames(streamOutput);
  161. double percent = (double)totalDropped / (double)totalFrames * 100.0;
  162. if (!totalFrames)
  163. return;
  164. QString text = QTStr("DroppedFrames");
  165. text = text.arg(QString::number(totalDropped),
  166. QString::number(percent, 'f', 1));
  167. droppedFrames->setText(text);
  168. droppedFrames->setMinimumWidth(droppedFrames->width());
  169. }
  170. void OBSBasicStatusBar::OBSOutputReconnect(void *data, calldata_t *params)
  171. {
  172. OBSBasicStatusBar *statusBar =
  173. reinterpret_cast<OBSBasicStatusBar*>(data);
  174. int seconds = (int)calldata_int(params, "timeout_sec");
  175. QMetaObject::invokeMethod(statusBar, "Reconnect", Q_ARG(int, seconds));
  176. UNUSED_PARAMETER(params);
  177. }
  178. void OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data, calldata_t *params)
  179. {
  180. OBSBasicStatusBar *statusBar =
  181. reinterpret_cast<OBSBasicStatusBar*>(data);
  182. QMetaObject::invokeMethod(statusBar, "ReconnectSuccess");
  183. UNUSED_PARAMETER(params);
  184. }
  185. void OBSBasicStatusBar::Reconnect(int seconds)
  186. {
  187. OBSBasic *main = qobject_cast<OBSBasic*>(parent());
  188. if (!retries)
  189. main->SysTrayNotify(
  190. QTStr("Basic.SystemTray.Message.Reconnecting"),
  191. QSystemTrayIcon::Warning);
  192. reconnectTimeout = seconds;
  193. if (streamOutput) {
  194. delaySecTotal = obs_output_get_active_delay(streamOutput);
  195. UpdateDelayMsg();
  196. retries++;
  197. }
  198. }
  199. void OBSBasicStatusBar::ReconnectClear()
  200. {
  201. retries = 0;
  202. reconnectTimeout = 0;
  203. bitrateUpdateSeconds = -1;
  204. lastBytesSent = 0;
  205. lastBytesSentTime = os_gettime_ns();
  206. delaySecTotal = 0;
  207. UpdateDelayMsg();
  208. }
  209. void OBSBasicStatusBar::ReconnectSuccess()
  210. {
  211. OBSBasic *main = qobject_cast<OBSBasic*>(parent());
  212. QString msg = QTStr("Basic.StatusBar.ReconnectSuccessful");
  213. showMessage(msg, 4000);
  214. main->SysTrayNotify(msg, QSystemTrayIcon::Information);
  215. ReconnectClear();
  216. if (streamOutput) {
  217. delaySecTotal = obs_output_get_active_delay(streamOutput);
  218. UpdateDelayMsg();
  219. }
  220. }
  221. void OBSBasicStatusBar::UpdateStatusBar()
  222. {
  223. OBSBasic *main = qobject_cast<OBSBasic*>(parent());
  224. UpdateBandwidth();
  225. UpdateSessionTime();
  226. UpdateDroppedFrames();
  227. int skipped = video_output_get_skipped_frames(obs_get_video());
  228. int total = video_output_get_total_frames(obs_get_video());
  229. skipped -= startSkippedFrameCount;
  230. total -= startTotalFrameCount;
  231. int diff = skipped - lastSkippedFrameCount;
  232. double percentage = double(skipped) / double(total) * 100.0;
  233. if (diff > 10 && percentage >= 0.1f) {
  234. showMessage(QTStr("HighResourceUsage"), 4000);
  235. if (!main->isVisible() && overloadedNotify) {
  236. main->SysTrayNotify(QTStr("HighResourceUsage"),
  237. QSystemTrayIcon::Warning);
  238. overloadedNotify = false;
  239. }
  240. }
  241. lastSkippedFrameCount = skipped;
  242. }
  243. void OBSBasicStatusBar::StreamDelayStarting(int sec)
  244. {
  245. OBSBasic *main = qobject_cast<OBSBasic*>(parent());
  246. if (!main || !main->outputHandler)
  247. return;
  248. streamOutput = main->outputHandler->streamOutput;
  249. delaySecTotal = delaySecStarting = sec;
  250. UpdateDelayMsg();
  251. Activate();
  252. }
  253. void OBSBasicStatusBar::StreamDelayStopping(int sec)
  254. {
  255. delaySecTotal = delaySecStopping = sec;
  256. UpdateDelayMsg();
  257. }
  258. void OBSBasicStatusBar::StreamStarted(obs_output_t *output)
  259. {
  260. streamOutput = output;
  261. signal_handler_connect(obs_output_get_signal_handler(streamOutput),
  262. "reconnect", OBSOutputReconnect, this);
  263. signal_handler_connect(obs_output_get_signal_handler(streamOutput),
  264. "reconnect_success", OBSOutputReconnectSuccess, this);
  265. retries = 0;
  266. lastBytesSent = 0;
  267. lastBytesSentTime = os_gettime_ns();
  268. Activate();
  269. }
  270. void OBSBasicStatusBar::StreamStopped()
  271. {
  272. if (streamOutput) {
  273. signal_handler_disconnect(
  274. obs_output_get_signal_handler(streamOutput),
  275. "reconnect", OBSOutputReconnect, this);
  276. signal_handler_disconnect(
  277. obs_output_get_signal_handler(streamOutput),
  278. "reconnect_success", OBSOutputReconnectSuccess,
  279. this);
  280. ReconnectClear();
  281. streamOutput = nullptr;
  282. clearMessage();
  283. Deactivate();
  284. }
  285. }
  286. void OBSBasicStatusBar::RecordingStarted(obs_output_t *output)
  287. {
  288. recordOutput = output;
  289. Activate();
  290. }
  291. void OBSBasicStatusBar::RecordingStopped()
  292. {
  293. recordOutput = nullptr;
  294. Deactivate();
  295. }