window-basic-main-outputs.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. #include <string>
  2. #include <algorithm>
  3. #include <QMessageBox>
  4. #include "audio-encoders.hpp"
  5. #include "window-basic-main.hpp"
  6. #include "window-basic-main-outputs.hpp"
  7. using namespace std;
  8. static void OBSStreamStarting(void *data, calldata_t *params)
  9. {
  10. BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
  11. obs_output_t *obj = (obs_output_t*)calldata_ptr(params, "output");
  12. int sec = (int)obs_output_get_active_delay(obj);
  13. if (sec == 0)
  14. return;
  15. output->delayActive = true;
  16. QMetaObject::invokeMethod(output->main,
  17. "StreamDelayStarting", Q_ARG(int, sec));
  18. }
  19. static void OBSStreamStopping(void *data, calldata_t *params)
  20. {
  21. BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
  22. obs_output_t *obj = (obs_output_t*)calldata_ptr(params, "output");
  23. int sec = (int)obs_output_get_active_delay(obj);
  24. if (sec == 0)
  25. QMetaObject::invokeMethod(output->main, "StreamStopping");
  26. else
  27. QMetaObject::invokeMethod(output->main,
  28. "StreamDelayStopping", Q_ARG(int, sec));
  29. }
  30. static void OBSStartStreaming(void *data, calldata_t *params)
  31. {
  32. BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
  33. output->streamingActive = true;
  34. QMetaObject::invokeMethod(output->main, "StreamingStart");
  35. UNUSED_PARAMETER(params);
  36. }
  37. static void OBSStopStreaming(void *data, calldata_t *params)
  38. {
  39. BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
  40. int code = (int)calldata_int(params, "code");
  41. output->streamingActive = false;
  42. output->delayActive = false;
  43. QMetaObject::invokeMethod(output->main,
  44. "StreamingStop", Q_ARG(int, code));
  45. }
  46. static void OBSStartRecording(void *data, calldata_t *params)
  47. {
  48. BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
  49. output->recordingActive = true;
  50. QMetaObject::invokeMethod(output->main, "RecordingStart");
  51. UNUSED_PARAMETER(params);
  52. }
  53. static void OBSStopRecording(void *data, calldata_t *params)
  54. {
  55. BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
  56. int code = (int)calldata_int(params, "code");
  57. output->recordingActive = false;
  58. QMetaObject::invokeMethod(output->main,
  59. "RecordingStop", Q_ARG(int, code));
  60. UNUSED_PARAMETER(params);
  61. }
  62. static void OBSRecordStopping(void *data, calldata_t *params)
  63. {
  64. BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
  65. QMetaObject::invokeMethod(output->main, "RecordStopping");
  66. UNUSED_PARAMETER(params);
  67. }
  68. static void FindBestFilename(string &strPath, bool noSpace)
  69. {
  70. int num = 2;
  71. if (!os_file_exists(strPath.c_str()))
  72. return;
  73. const char *ext = strrchr(strPath.c_str(), '.');
  74. if (!ext)
  75. return;
  76. int extStart = int(ext - strPath.c_str());
  77. for (;;) {
  78. string testPath = strPath;
  79. string numStr;
  80. numStr = noSpace ? "_" : " (";
  81. numStr += to_string(num++);
  82. if (!noSpace)
  83. numStr += ")";
  84. testPath.insert(extStart, numStr);
  85. if (!os_file_exists(testPath.c_str())) {
  86. strPath = testPath;
  87. break;
  88. }
  89. }
  90. }
  91. /* ------------------------------------------------------------------------ */
  92. static bool CreateAACEncoder(OBSEncoder &res, string &id, int bitrate,
  93. const char *name, size_t idx)
  94. {
  95. const char *id_ = GetAACEncoderForBitrate(bitrate);
  96. if (!id_) {
  97. id.clear();
  98. res = nullptr;
  99. return false;
  100. }
  101. if (id == id_)
  102. return true;
  103. id = id_;
  104. res = obs_audio_encoder_create(id_, name, nullptr, idx, nullptr);
  105. if (res) {
  106. obs_encoder_release(res);
  107. return true;
  108. }
  109. return false;
  110. }
  111. /* ------------------------------------------------------------------------ */
  112. struct SimpleOutput : BasicOutputHandler {
  113. OBSEncoder aacStreaming;
  114. OBSEncoder h264Streaming;
  115. OBSEncoder aacRecording;
  116. OBSEncoder h264Recording;
  117. string aacRecEncID;
  118. string aacStreamEncID;
  119. string videoEncoder;
  120. string videoQuality;
  121. bool usingRecordingPreset = false;
  122. bool ffmpegOutput = false;
  123. bool lowCPUx264 = false;
  124. SimpleOutput(OBSBasic *main_);
  125. int CalcCRF(int crf);
  126. void UpdateStreamingSettings_amd(obs_data_t *settings, int bitrate);
  127. void UpdateRecordingSettings_x264_crf(int crf);
  128. void UpdateRecordingSettings_qsv11(int crf);
  129. void UpdateRecordingSettings_nvenc(int cqp);
  130. void UpdateRecordingSettings_amd_cqp(int cqp);
  131. void UpdateRecordingSettings();
  132. void UpdateRecordingAudioSettings();
  133. virtual void Update() override;
  134. void SetupOutputs();
  135. int GetAudioBitrate() const;
  136. void LoadRecordingPreset_h264(const char *encoder);
  137. void LoadRecordingPreset_Lossless();
  138. void LoadRecordingPreset();
  139. void LoadStreamingPreset_h264(const char *encoder);
  140. virtual bool StartStreaming(obs_service_t *service) override;
  141. virtual bool StartRecording() override;
  142. virtual void StopStreaming(bool force) override;
  143. virtual void StopRecording(bool force) override;
  144. virtual bool StreamingActive() const override;
  145. virtual bool RecordingActive() const override;
  146. };
  147. void SimpleOutput::LoadRecordingPreset_Lossless()
  148. {
  149. fileOutput = obs_output_create("ffmpeg_output",
  150. "simple_ffmpeg_output", nullptr, nullptr);
  151. if (!fileOutput)
  152. throw "Failed to create recording FFmpeg output "
  153. "(simple output)";
  154. obs_output_release(fileOutput);
  155. obs_data_t *settings = obs_data_create();
  156. obs_data_set_string(settings, "format_name", "avi");
  157. obs_data_set_string(settings, "video_encoder", "utvideo");
  158. obs_data_set_string(settings, "audio_encoder", "pcm_s16le");
  159. obs_output_update(fileOutput, settings);
  160. obs_data_release(settings);
  161. }
  162. void SimpleOutput::LoadRecordingPreset_h264(const char *encoderId)
  163. {
  164. h264Recording = obs_video_encoder_create(encoderId,
  165. "simple_h264_recording", nullptr, nullptr);
  166. if (!h264Recording)
  167. throw "Failed to create h264 recording encoder (simple output)";
  168. obs_encoder_release(h264Recording);
  169. }
  170. void SimpleOutput::LoadStreamingPreset_h264(const char *encoderId)
  171. {
  172. h264Streaming = obs_video_encoder_create(encoderId,
  173. "simple_h264_stream", nullptr, nullptr);
  174. if (!h264Streaming)
  175. throw "Failed to create h264 streaming encoder (simple output)";
  176. obs_encoder_release(h264Streaming);
  177. }
  178. void SimpleOutput::LoadRecordingPreset()
  179. {
  180. const char *quality = config_get_string(main->Config(), "SimpleOutput",
  181. "RecQuality");
  182. const char *encoder = config_get_string(main->Config(), "SimpleOutput",
  183. "RecEncoder");
  184. videoEncoder = encoder;
  185. videoQuality = quality;
  186. ffmpegOutput = false;
  187. if (strcmp(quality, "Stream") == 0) {
  188. h264Recording = h264Streaming;
  189. aacRecording = aacStreaming;
  190. usingRecordingPreset = false;
  191. return;
  192. } else if (strcmp(quality, "Lossless") == 0) {
  193. LoadRecordingPreset_Lossless();
  194. usingRecordingPreset = true;
  195. ffmpegOutput = true;
  196. return;
  197. } else {
  198. lowCPUx264 = false;
  199. if (strcmp(encoder, SIMPLE_ENCODER_X264) == 0) {
  200. LoadRecordingPreset_h264("obs_x264");
  201. } else if (strcmp(encoder, SIMPLE_ENCODER_X264_LOWCPU) == 0) {
  202. LoadRecordingPreset_h264("obs_x264");
  203. lowCPUx264 = true;
  204. } else if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) {
  205. LoadRecordingPreset_h264("obs_qsv11");
  206. } else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) {
  207. LoadRecordingPreset_h264("amd_amf_h264");
  208. } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) {
  209. LoadRecordingPreset_h264("ffmpeg_nvenc");
  210. }
  211. usingRecordingPreset = true;
  212. if (!CreateAACEncoder(aacRecording, aacRecEncID, 192,
  213. "simple_aac_recording", 0))
  214. throw "Failed to create aac recording encoder "
  215. "(simple output)";
  216. }
  217. }
  218. SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_)
  219. {
  220. streamOutput = obs_output_create("rtmp_output", "simple_stream",
  221. nullptr, nullptr);
  222. if (!streamOutput)
  223. throw "Failed to create stream output (simple output)";
  224. obs_output_release(streamOutput);
  225. const char *encoder = config_get_string(main->Config(), "SimpleOutput",
  226. "StreamEncoder");
  227. if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0)
  228. LoadStreamingPreset_h264("obs_qsv11");
  229. else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0)
  230. LoadStreamingPreset_h264("amd_amf_h264");
  231. else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0)
  232. LoadStreamingPreset_h264("ffmpeg_nvenc");
  233. else
  234. LoadStreamingPreset_h264("obs_x264");
  235. if (!CreateAACEncoder(aacStreaming, aacStreamEncID, GetAudioBitrate(),
  236. "simple_aac", 0))
  237. throw "Failed to create aac streaming encoder (simple output)";
  238. streamDelayStarting.Connect(obs_output_get_signal_handler(streamOutput),
  239. "starting", OBSStreamStarting, this);
  240. streamStopping.Connect(obs_output_get_signal_handler(streamOutput),
  241. "stopping", OBSStreamStopping, this);
  242. startStreaming.Connect(obs_output_get_signal_handler(streamOutput),
  243. "start", OBSStartStreaming, this);
  244. stopStreaming.Connect(obs_output_get_signal_handler(streamOutput),
  245. "stop", OBSStopStreaming, this);
  246. LoadRecordingPreset();
  247. if (!ffmpegOutput) {
  248. replayBuffer = config_get_bool(main->Config(),
  249. "SimpleOutput", "RecRB");
  250. if (replayBuffer) {
  251. const char *str = config_get_string(main->Config(),
  252. "Hotkeys", "ReplayBuffer");
  253. obs_data_t *hotkey = obs_data_create_from_json(str);
  254. fileOutput = obs_output_create("replay_buffer",
  255. Str("ReplayBuffer"), nullptr, hotkey);
  256. obs_data_release(hotkey);
  257. } else {
  258. fileOutput = obs_output_create("ffmpeg_muxer",
  259. "simple_file_output", nullptr, nullptr);
  260. }
  261. if (!fileOutput)
  262. throw "Failed to create recording output "
  263. "(simple output)";
  264. obs_output_release(fileOutput);
  265. }
  266. startRecording.Connect(obs_output_get_signal_handler(fileOutput),
  267. "start", OBSStartRecording, this);
  268. stopRecording.Connect(obs_output_get_signal_handler(fileOutput),
  269. "stop", OBSStopRecording, this);
  270. recordStopping.Connect(obs_output_get_signal_handler(fileOutput),
  271. "stopping", OBSRecordStopping, this);
  272. }
  273. int SimpleOutput::GetAudioBitrate() const
  274. {
  275. int bitrate = (int)config_get_uint(main->Config(), "SimpleOutput",
  276. "ABitrate");
  277. return FindClosestAvailableAACBitrate(bitrate);
  278. }
  279. void SimpleOutput::Update()
  280. {
  281. obs_data_t *h264Settings = obs_data_create();
  282. obs_data_t *aacSettings = obs_data_create();
  283. int videoBitrate = config_get_uint(main->Config(), "SimpleOutput",
  284. "VBitrate");
  285. int audioBitrate = GetAudioBitrate();
  286. bool advanced = config_get_bool(main->Config(), "SimpleOutput",
  287. "UseAdvanced");
  288. bool enforceBitrate = config_get_bool(main->Config(), "SimpleOutput",
  289. "EnforceBitrate");
  290. const char *custom = config_get_string(main->Config(),
  291. "SimpleOutput", "x264Settings");
  292. const char *encoder = config_get_string(main->Config(), "SimpleOutput",
  293. "StreamEncoder");
  294. const char *presetType;
  295. const char *preset;
  296. if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) {
  297. presetType = "QSVPreset";
  298. } else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) {
  299. presetType = "AMDPreset";
  300. UpdateStreamingSettings_amd(h264Settings, videoBitrate);
  301. } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) {
  302. presetType = "NVENCPreset";
  303. } else {
  304. presetType = "Preset";
  305. }
  306. preset = config_get_string(main->Config(), "SimpleOutput", presetType);
  307. obs_data_set_string(h264Settings, "rate_control", "CBR");
  308. obs_data_set_int(h264Settings, "bitrate", videoBitrate);
  309. if (advanced) {
  310. obs_data_set_string(h264Settings, "preset", preset);
  311. obs_data_set_string(h264Settings, "x264opts", custom);
  312. }
  313. obs_data_set_string(aacSettings, "rate_control", "CBR");
  314. obs_data_set_int(aacSettings, "bitrate", audioBitrate);
  315. obs_service_apply_encoder_settings(main->GetService(),
  316. h264Settings, aacSettings);
  317. if (advanced && !enforceBitrate) {
  318. obs_data_set_int(h264Settings, "bitrate", videoBitrate);
  319. obs_data_set_int(aacSettings, "bitrate", audioBitrate);
  320. }
  321. video_t *video = obs_get_video();
  322. enum video_format format = video_output_get_format(video);
  323. if (format != VIDEO_FORMAT_NV12 && format != VIDEO_FORMAT_I420)
  324. obs_encoder_set_preferred_video_format(h264Streaming,
  325. VIDEO_FORMAT_NV12);
  326. obs_encoder_update(h264Streaming, h264Settings);
  327. obs_encoder_update(aacStreaming, aacSettings);
  328. obs_data_release(h264Settings);
  329. obs_data_release(aacSettings);
  330. }
  331. void SimpleOutput::UpdateRecordingAudioSettings()
  332. {
  333. obs_data_t *settings = obs_data_create();
  334. obs_data_set_int(settings, "bitrate", 192);
  335. obs_data_set_string(settings, "rate_control", "CBR");
  336. obs_encoder_update(aacRecording, settings);
  337. obs_data_release(settings);
  338. }
  339. #define CROSS_DIST_CUTOFF 2000.0
  340. int SimpleOutput::CalcCRF(int crf)
  341. {
  342. int cx = config_get_uint(main->Config(), "Video", "OutputCX");
  343. int cy = config_get_uint(main->Config(), "Video", "OutputCY");
  344. double fCX = double(cx);
  345. double fCY = double(cy);
  346. if (lowCPUx264)
  347. crf -= 2;
  348. double crossDist = sqrt(fCX * fCX + fCY * fCY);
  349. double crfResReduction =
  350. fmin(CROSS_DIST_CUTOFF, crossDist) / CROSS_DIST_CUTOFF;
  351. crfResReduction = (1.0 - crfResReduction) * 10.0;
  352. return crf - int(crfResReduction);
  353. }
  354. void SimpleOutput::UpdateRecordingSettings_x264_crf(int crf)
  355. {
  356. obs_data_t *settings = obs_data_create();
  357. obs_data_set_int(settings, "crf", crf);
  358. obs_data_set_bool(settings, "use_bufsize", true);
  359. obs_data_set_string(settings, "rate_control", "CRF");
  360. obs_data_set_string(settings, "profile", "high");
  361. obs_data_set_string(settings, "preset",
  362. lowCPUx264 ? "ultrafast" : "veryfast");
  363. obs_encoder_update(h264Recording, settings);
  364. obs_data_release(settings);
  365. }
  366. static bool icq_available(obs_encoder_t *encoder)
  367. {
  368. obs_properties_t *props = obs_encoder_properties(encoder);
  369. obs_property_t *p = obs_properties_get(props, "rate_control");
  370. bool icq_found = false;
  371. size_t num = obs_property_list_item_count(p);
  372. for (size_t i = 0; i < num; i++) {
  373. const char *val = obs_property_list_item_string(p, i);
  374. if (strcmp(val, "ICQ") == 0) {
  375. icq_found = true;
  376. break;
  377. }
  378. }
  379. obs_properties_destroy(props);
  380. return icq_found;
  381. }
  382. void SimpleOutput::UpdateRecordingSettings_qsv11(int crf)
  383. {
  384. bool icq = icq_available(h264Recording);
  385. obs_data_t *settings = obs_data_create();
  386. obs_data_set_string(settings, "profile", "high");
  387. if (icq) {
  388. obs_data_set_string(settings, "rate_control", "ICQ");
  389. obs_data_set_int(settings, "icq_quality", crf);
  390. } else {
  391. obs_data_set_string(settings, "rate_control", "CQP");
  392. obs_data_set_int(settings, "qpi", crf);
  393. obs_data_set_int(settings, "qpp", crf);
  394. obs_data_set_int(settings, "qpb", crf);
  395. }
  396. obs_encoder_update(h264Recording, settings);
  397. obs_data_release(settings);
  398. }
  399. void SimpleOutput::UpdateRecordingSettings_nvenc(int cqp)
  400. {
  401. obs_data_t *settings = obs_data_create();
  402. obs_data_set_string(settings, "rate_control", "CQP");
  403. obs_data_set_string(settings, "profile", "high");
  404. obs_data_set_string(settings, "preset", "hq");
  405. obs_data_set_int(settings, "cqp", cqp);
  406. obs_encoder_update(h264Recording, settings);
  407. obs_data_release(settings);
  408. }
  409. void SimpleOutput::UpdateStreamingSettings_amd(obs_data_t *settings,
  410. int bitrate)
  411. {
  412. // Static Properties
  413. obs_data_set_int(settings, "AMF.H264.Usage", 0);
  414. obs_data_set_int(settings, "AMF.H264.Profile", 100); // High
  415. obs_data_set_string(settings, "profile", "high"); // High
  416. // Rate Control Properties
  417. obs_data_set_int(settings, "AMF.H264.RateControlMethod", 1);
  418. obs_data_set_string(settings, "rate_control", "CBR");
  419. obs_data_set_int(settings, "AMF.H264.Bitrate.Target", bitrate);
  420. obs_data_set_int(settings, "bitrate", bitrate);
  421. obs_data_set_int(settings, "AMF.H264.FillerData", 1);
  422. // Picture Control Properties
  423. obs_data_set_double(settings, "AMF.H264.KeyframeInterval", 2.0);
  424. obs_data_set_int(settings, "keyint_sec", 2);
  425. }
  426. void SimpleOutput::UpdateRecordingSettings_amd_cqp(int cqp)
  427. {
  428. obs_data_t *settings = obs_data_create();
  429. // Static Properties
  430. obs_data_set_int(settings, "AMF.H264.Usage", 0);
  431. obs_data_set_int(settings, "AMF.H264.Profile", 100); // High
  432. obs_data_set_string(settings, "profile", "high"); // High
  433. // Rate Control Properties
  434. obs_data_set_int(settings, "AMF.H264.RateControlMethod", 0);
  435. obs_data_set_string(settings, "rate_control", "CQP");
  436. obs_data_set_int(settings, "AMF.H264.QP.IFrame", cqp);
  437. obs_data_set_int(settings, "AMF.H264.QP.PFrame", cqp);
  438. obs_data_set_int(settings, "AMF.H264.QP.BFrame", cqp);
  439. // Picture Control Properties
  440. obs_data_set_double(settings, "AMF.H264.KeyframeInterval", 2.0);
  441. obs_data_set_int(settings, "keyint_sec", 2);
  442. // Update and release
  443. obs_encoder_update(h264Recording, settings);
  444. obs_data_release(settings);
  445. }
  446. void SimpleOutput::UpdateRecordingSettings()
  447. {
  448. bool ultra_hq = (videoQuality == "HQ");
  449. int crf = CalcCRF(ultra_hq ? 16 : 23);
  450. if (astrcmp_n(videoEncoder.c_str(), "x264", 4) == 0) {
  451. UpdateRecordingSettings_x264_crf(crf);
  452. } else if (videoEncoder == SIMPLE_ENCODER_QSV) {
  453. UpdateRecordingSettings_qsv11(crf);
  454. } else if (videoEncoder == SIMPLE_ENCODER_AMD) {
  455. UpdateRecordingSettings_amd_cqp(crf);
  456. } else if (videoEncoder == SIMPLE_ENCODER_NVENC) {
  457. UpdateRecordingSettings_nvenc(crf);
  458. }
  459. }
  460. inline void SimpleOutput::SetupOutputs()
  461. {
  462. SimpleOutput::Update();
  463. obs_encoder_set_video(h264Streaming, obs_get_video());
  464. obs_encoder_set_audio(aacStreaming, obs_get_audio());
  465. if (usingRecordingPreset) {
  466. if (ffmpegOutput) {
  467. obs_output_set_media(fileOutput, obs_get_video(),
  468. obs_get_audio());
  469. } else {
  470. obs_encoder_set_video(h264Recording, obs_get_video());
  471. obs_encoder_set_audio(aacRecording, obs_get_audio());
  472. }
  473. }
  474. }
  475. bool SimpleOutput::StartStreaming(obs_service_t *service)
  476. {
  477. if (!Active())
  478. SetupOutputs();
  479. obs_output_set_video_encoder(streamOutput, h264Streaming);
  480. obs_output_set_audio_encoder(streamOutput, aacStreaming, 0);
  481. obs_output_set_service(streamOutput, service);
  482. bool reconnect = config_get_bool(main->Config(), "Output",
  483. "Reconnect");
  484. int retryDelay = config_get_uint(main->Config(), "Output",
  485. "RetryDelay");
  486. int maxRetries = config_get_uint(main->Config(), "Output",
  487. "MaxRetries");
  488. bool useDelay = config_get_bool(main->Config(), "Output",
  489. "DelayEnable");
  490. int delaySec = config_get_int(main->Config(), "Output",
  491. "DelaySec");
  492. bool preserveDelay = config_get_bool(main->Config(), "Output",
  493. "DelayPreserve");
  494. const char *bindIP = config_get_string(main->Config(), "Output",
  495. "BindIP");
  496. obs_data_t *settings = obs_data_create();
  497. obs_data_set_string(settings, "bind_ip", bindIP);
  498. obs_output_update(streamOutput, settings);
  499. obs_data_release(settings);
  500. if (!reconnect)
  501. maxRetries = 0;
  502. obs_output_set_delay(streamOutput, useDelay ? delaySec : 0,
  503. preserveDelay ? OBS_OUTPUT_DELAY_PRESERVE : 0);
  504. obs_output_set_reconnect_settings(streamOutput, maxRetries,
  505. retryDelay);
  506. if (obs_output_start(streamOutput)) {
  507. return true;
  508. }
  509. return false;
  510. }
  511. static void ensure_directory_exists(string &path)
  512. {
  513. replace(path.begin(), path.end(), '\\', '/');
  514. size_t last = path.rfind('/');
  515. if (last == string::npos)
  516. return;
  517. string directory = path.substr(0, last);
  518. os_mkdirs(directory.c_str());
  519. }
  520. bool SimpleOutput::StartRecording()
  521. {
  522. if (usingRecordingPreset) {
  523. if (!ffmpegOutput)
  524. UpdateRecordingSettings();
  525. } else if (!obs_output_active(streamOutput)) {
  526. Update();
  527. }
  528. if (!Active())
  529. SetupOutputs();
  530. const char *path = config_get_string(main->Config(),
  531. "SimpleOutput", "FilePath");
  532. const char *format = config_get_string(main->Config(),
  533. "SimpleOutput", "RecFormat");
  534. const char *mux = config_get_string(main->Config(), "SimpleOutput",
  535. "MuxerCustom");
  536. bool noSpace = config_get_bool(main->Config(), "SimpleOutput",
  537. "FileNameWithoutSpace");
  538. const char *filenameFormat = config_get_string(main->Config(), "Output",
  539. "FilenameFormatting");
  540. bool overwriteIfExists = config_get_bool(main->Config(), "Output",
  541. "OverwriteIfExists");
  542. int rbTime = config_get_int(main->Config(), "SimpleOutput",
  543. "RecRBTime");
  544. int rbSize = config_get_int(main->Config(), "SimpleOutput",
  545. "RecRBSize");
  546. os_dir_t *dir = path ? os_opendir(path) : nullptr;
  547. if (!dir) {
  548. if (main->isVisible())
  549. QMessageBox::information(main,
  550. QTStr("Output.BadPath.Title"),
  551. QTStr("Output.BadPath.Text"));
  552. else
  553. main->SysTrayNotify(QTStr("Output.BadPath.Text"),
  554. QSystemTrayIcon::Warning);
  555. return false;
  556. }
  557. os_closedir(dir);
  558. string strPath;
  559. strPath += path;
  560. char lastChar = strPath.back();
  561. if (lastChar != '/' && lastChar != '\\')
  562. strPath += "/";
  563. strPath += GenerateSpecifiedFilename(ffmpegOutput ? "avi" : format,
  564. noSpace, filenameFormat);
  565. ensure_directory_exists(strPath);
  566. if (!overwriteIfExists)
  567. FindBestFilename(strPath, noSpace);
  568. if (!ffmpegOutput) {
  569. obs_output_set_video_encoder(fileOutput, h264Recording);
  570. obs_output_set_audio_encoder(fileOutput, aacRecording, 0);
  571. }
  572. obs_data_t *settings = obs_data_create();
  573. if (replayBuffer) {
  574. obs_data_set_string(settings, "directory", path);
  575. obs_data_set_string(settings, "format", filenameFormat);
  576. obs_data_set_string(settings, "extension", format);
  577. obs_data_set_int(settings, "max_time_sec", rbTime);
  578. obs_data_set_int(settings, "max_size_mb",
  579. usingRecordingPreset ? rbSize : 0);
  580. } else {
  581. obs_data_set_string(settings, ffmpegOutput ? "url" : "path",
  582. strPath.c_str());
  583. }
  584. obs_data_set_string(settings, "muxer_settings", mux);
  585. obs_output_update(fileOutput, settings);
  586. obs_data_release(settings);
  587. if (obs_output_start(fileOutput)) {
  588. return true;
  589. }
  590. return false;
  591. }
  592. void SimpleOutput::StopStreaming(bool force)
  593. {
  594. if (force)
  595. obs_output_force_stop(streamOutput);
  596. else
  597. obs_output_stop(streamOutput);
  598. }
  599. void SimpleOutput::StopRecording(bool force)
  600. {
  601. if (force)
  602. obs_output_force_stop(fileOutput);
  603. else
  604. obs_output_stop(fileOutput);
  605. }
  606. bool SimpleOutput::StreamingActive() const
  607. {
  608. return obs_output_active(streamOutput);
  609. }
  610. bool SimpleOutput::RecordingActive() const
  611. {
  612. return obs_output_active(fileOutput);
  613. }
  614. /* ------------------------------------------------------------------------ */
  615. struct AdvancedOutput : BasicOutputHandler {
  616. OBSEncoder aacTrack[4];
  617. OBSEncoder h264Streaming;
  618. OBSEncoder h264Recording;
  619. bool ffmpegOutput;
  620. bool ffmpegRecording;
  621. bool useStreamEncoder;
  622. string aacEncoderID[4];
  623. AdvancedOutput(OBSBasic *main_);
  624. inline void UpdateStreamSettings();
  625. inline void UpdateRecordingSettings();
  626. inline void UpdateAudioSettings();
  627. virtual void Update() override;
  628. inline void SetupStreaming();
  629. inline void SetupRecording();
  630. inline void SetupFFmpeg();
  631. void SetupOutputs();
  632. int GetAudioBitrate(size_t i) const;
  633. virtual bool StartStreaming(obs_service_t *service) override;
  634. virtual bool StartRecording() override;
  635. virtual void StopStreaming(bool force) override;
  636. virtual void StopRecording(bool force) override;
  637. virtual bool StreamingActive() const override;
  638. virtual bool RecordingActive() const override;
  639. };
  640. static OBSData GetDataFromJsonFile(const char *jsonFile)
  641. {
  642. char fullPath[512];
  643. int ret = GetProfilePath(fullPath, sizeof(fullPath), jsonFile);
  644. if (ret > 0) {
  645. BPtr<char> jsonData = os_quick_read_utf8_file(fullPath);
  646. if (!!jsonData) {
  647. obs_data_t *data = obs_data_create_from_json(jsonData);
  648. OBSData dataRet(data);
  649. obs_data_release(data);
  650. return dataRet;
  651. }
  652. }
  653. return nullptr;
  654. }
  655. AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
  656. {
  657. const char *recType = config_get_string(main->Config(), "AdvOut",
  658. "RecType");
  659. const char *streamEncoder = config_get_string(main->Config(), "AdvOut",
  660. "Encoder");
  661. const char *recordEncoder = config_get_string(main->Config(), "AdvOut",
  662. "RecEncoder");
  663. ffmpegOutput = astrcmpi(recType, "FFmpeg") == 0;
  664. ffmpegRecording = ffmpegOutput &&
  665. config_get_bool(main->Config(), "AdvOut", "FFOutputToFile");
  666. useStreamEncoder = astrcmpi(recordEncoder, "none") == 0;
  667. OBSData streamEncSettings = GetDataFromJsonFile("streamEncoder.json");
  668. OBSData recordEncSettings = GetDataFromJsonFile("recordEncoder.json");
  669. streamOutput = obs_output_create("rtmp_output", "adv_stream",
  670. nullptr, nullptr);
  671. if (!streamOutput)
  672. throw "Failed to create stream output (advanced output)";
  673. obs_output_release(streamOutput);
  674. if (ffmpegOutput) {
  675. fileOutput = obs_output_create("ffmpeg_output",
  676. "adv_ffmpeg_output", nullptr, nullptr);
  677. if (!fileOutput)
  678. throw "Failed to create recording FFmpeg output "
  679. "(advanced output)";
  680. obs_output_release(fileOutput);
  681. } else {
  682. fileOutput = obs_output_create("ffmpeg_muxer",
  683. "adv_file_output", nullptr, nullptr);
  684. if (!fileOutput)
  685. throw "Failed to create recording output "
  686. "(advanced output)";
  687. obs_output_release(fileOutput);
  688. if (!useStreamEncoder) {
  689. h264Recording = obs_video_encoder_create(recordEncoder,
  690. "recording_h264", recordEncSettings,
  691. nullptr);
  692. if (!h264Recording)
  693. throw "Failed to create recording h264 "
  694. "encoder (advanced output)";
  695. obs_encoder_release(h264Recording);
  696. }
  697. }
  698. h264Streaming = obs_video_encoder_create(streamEncoder,
  699. "streaming_h264", streamEncSettings, nullptr);
  700. if (!h264Streaming)
  701. throw "Failed to create streaming h264 encoder "
  702. "(advanced output)";
  703. obs_encoder_release(h264Streaming);
  704. for (int i = 0; i < 4; i++) {
  705. char name[9];
  706. sprintf(name, "adv_aac%d", i);
  707. if (!CreateAACEncoder(aacTrack[i], aacEncoderID[i],
  708. GetAudioBitrate(i), name, i))
  709. throw "Failed to create audio encoder "
  710. "(advanced output)";
  711. }
  712. streamDelayStarting.Connect(obs_output_get_signal_handler(streamOutput),
  713. "starting", OBSStreamStarting, this);
  714. streamStopping.Connect(obs_output_get_signal_handler(streamOutput),
  715. "stopping", OBSStreamStopping, this);
  716. startStreaming.Connect(obs_output_get_signal_handler(streamOutput),
  717. "start", OBSStartStreaming, this);
  718. stopStreaming.Connect(obs_output_get_signal_handler(streamOutput),
  719. "stop", OBSStopStreaming, this);
  720. startRecording.Connect(obs_output_get_signal_handler(fileOutput),
  721. "start", OBSStartRecording, this);
  722. stopRecording.Connect(obs_output_get_signal_handler(fileOutput),
  723. "stop", OBSStopRecording, this);
  724. recordStopping.Connect(obs_output_get_signal_handler(fileOutput),
  725. "stopping", OBSRecordStopping, this);
  726. }
  727. void AdvancedOutput::UpdateStreamSettings()
  728. {
  729. bool applyServiceSettings = config_get_bool(main->Config(), "AdvOut",
  730. "ApplyServiceSettings");
  731. OBSData settings = GetDataFromJsonFile("streamEncoder.json");
  732. if (applyServiceSettings)
  733. obs_service_apply_encoder_settings(main->GetService(),
  734. settings, nullptr);
  735. video_t *video = obs_get_video();
  736. enum video_format format = video_output_get_format(video);
  737. if (format != VIDEO_FORMAT_NV12 && format != VIDEO_FORMAT_I420)
  738. obs_encoder_set_preferred_video_format(h264Streaming,
  739. VIDEO_FORMAT_NV12);
  740. obs_encoder_update(h264Streaming, settings);
  741. }
  742. inline void AdvancedOutput::UpdateRecordingSettings()
  743. {
  744. OBSData settings = GetDataFromJsonFile("recordEncoder.json");
  745. obs_encoder_update(h264Recording, settings);
  746. }
  747. void AdvancedOutput::Update()
  748. {
  749. UpdateStreamSettings();
  750. if (!useStreamEncoder && !ffmpegOutput)
  751. UpdateRecordingSettings();
  752. UpdateAudioSettings();
  753. }
  754. inline void AdvancedOutput::SetupStreaming()
  755. {
  756. bool rescale = config_get_bool(main->Config(), "AdvOut",
  757. "Rescale");
  758. const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
  759. "RescaleRes");
  760. bool multitrack = config_get_bool(main->Config(), "AdvOut",
  761. "Multitrack");
  762. int trackIndex = config_get_int(main->Config(), "AdvOut",
  763. "TrackIndex");
  764. int trackCount = config_get_int(main->Config(), "AdvOut",
  765. "TrackCount");
  766. unsigned int cx = 0;
  767. unsigned int cy = 0;
  768. if (rescale && rescaleRes && *rescaleRes) {
  769. if (sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) {
  770. cx = 0;
  771. cy = 0;
  772. }
  773. }
  774. obs_encoder_set_scaled_size(h264Streaming, cx, cy);
  775. obs_encoder_set_video(h264Streaming, obs_get_video());
  776. obs_output_set_video_encoder(streamOutput, h264Streaming);
  777. if (multitrack) {
  778. int i = 0;
  779. for (; i < trackCount; i++)
  780. obs_output_set_audio_encoder(streamOutput, aacTrack[i],
  781. i);
  782. for (; i < 4; i++)
  783. obs_output_set_audio_encoder(streamOutput, nullptr, i);
  784. } else {
  785. obs_output_set_audio_encoder(streamOutput,
  786. aacTrack[trackIndex - 1], 0);
  787. }
  788. }
  789. inline void AdvancedOutput::SetupRecording()
  790. {
  791. const char *path = config_get_string(main->Config(), "AdvOut",
  792. "RecFilePath");
  793. const char *mux = config_get_string(main->Config(), "AdvOut",
  794. "RecMuxerCustom");
  795. bool rescale = config_get_bool(main->Config(), "AdvOut",
  796. "RecRescale");
  797. const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
  798. "RecRescaleRes");
  799. int tracks = config_get_int(main->Config(), "AdvOut", "RecTracks");
  800. obs_data_t *settings = obs_data_create();
  801. unsigned int cx = 0;
  802. unsigned int cy = 0;
  803. int idx = 0;
  804. if (useStreamEncoder) {
  805. obs_output_set_video_encoder(fileOutput, h264Streaming);
  806. } else {
  807. if (rescale && rescaleRes && *rescaleRes) {
  808. if (sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) {
  809. cx = 0;
  810. cy = 0;
  811. }
  812. }
  813. obs_encoder_set_scaled_size(h264Recording, cx, cy);
  814. obs_encoder_set_video(h264Recording, obs_get_video());
  815. obs_output_set_video_encoder(fileOutput, h264Recording);
  816. }
  817. for (int i = 0; i < MAX_AUDIO_MIXES; i++) {
  818. if ((tracks & (1<<i)) != 0) {
  819. obs_output_set_audio_encoder(fileOutput, aacTrack[i],
  820. idx++);
  821. }
  822. }
  823. obs_data_set_string(settings, "path", path);
  824. obs_data_set_string(settings, "muxer_settings", mux);
  825. obs_output_update(fileOutput, settings);
  826. obs_data_release(settings);
  827. }
  828. inline void AdvancedOutput::SetupFFmpeg()
  829. {
  830. const char *url = config_get_string(main->Config(), "AdvOut", "FFURL");
  831. int vBitrate = config_get_int(main->Config(), "AdvOut",
  832. "FFVBitrate");
  833. bool rescale = config_get_bool(main->Config(), "AdvOut",
  834. "FFRescale");
  835. const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
  836. "FFRescaleRes");
  837. const char *formatName = config_get_string(main->Config(), "AdvOut",
  838. "FFFormat");
  839. const char *mimeType = config_get_string(main->Config(), "AdvOut",
  840. "FFFormatMimeType");
  841. const char *muxCustom = config_get_string(main->Config(), "AdvOut",
  842. "FFMCustom");
  843. const char *vEncoder = config_get_string(main->Config(), "AdvOut",
  844. "FFVEncoder");
  845. int vEncoderId = config_get_int(main->Config(), "AdvOut",
  846. "FFVEncoderId");
  847. const char *vEncCustom = config_get_string(main->Config(), "AdvOut",
  848. "FFVCustom");
  849. int aBitrate = config_get_int(main->Config(), "AdvOut",
  850. "FFABitrate");
  851. int aTrack = config_get_int(main->Config(), "AdvOut",
  852. "FFAudioTrack");
  853. const char *aEncoder = config_get_string(main->Config(), "AdvOut",
  854. "FFAEncoder");
  855. int aEncoderId = config_get_int(main->Config(), "AdvOut",
  856. "FFAEncoderId");
  857. const char *aEncCustom = config_get_string(main->Config(), "AdvOut",
  858. "FFACustom");
  859. obs_data_t *settings = obs_data_create();
  860. obs_data_set_string(settings, "url", url);
  861. obs_data_set_string(settings, "format_name", formatName);
  862. obs_data_set_string(settings, "format_mime_type", mimeType);
  863. obs_data_set_string(settings, "muxer_settings", muxCustom);
  864. obs_data_set_int(settings, "video_bitrate", vBitrate);
  865. obs_data_set_string(settings, "video_encoder", vEncoder);
  866. obs_data_set_int(settings, "video_encoder_id", vEncoderId);
  867. obs_data_set_string(settings, "video_settings", vEncCustom);
  868. obs_data_set_int(settings, "audio_bitrate", aBitrate);
  869. obs_data_set_string(settings, "audio_encoder", aEncoder);
  870. obs_data_set_int(settings, "audio_encoder_id", aEncoderId);
  871. obs_data_set_string(settings, "audio_settings", aEncCustom);
  872. if (rescale && rescaleRes && *rescaleRes) {
  873. int width;
  874. int height;
  875. int val = sscanf(rescaleRes, "%dx%d", &width, &height);
  876. if (val == 2 && width && height) {
  877. obs_data_set_int(settings, "scale_width", width);
  878. obs_data_set_int(settings, "scale_height", height);
  879. }
  880. }
  881. obs_output_set_mixer(fileOutput, aTrack - 1);
  882. obs_output_set_media(fileOutput, obs_get_video(), obs_get_audio());
  883. obs_output_update(fileOutput, settings);
  884. obs_data_release(settings);
  885. }
  886. static inline void SetEncoderName(obs_encoder_t *encoder, const char *name,
  887. const char *defaultName)
  888. {
  889. obs_encoder_set_name(encoder, (name && *name) ? name : defaultName);
  890. }
  891. inline void AdvancedOutput::UpdateAudioSettings()
  892. {
  893. const char *name1 = config_get_string(main->Config(), "AdvOut",
  894. "Track1Name");
  895. const char *name2 = config_get_string(main->Config(), "AdvOut",
  896. "Track2Name");
  897. const char *name3 = config_get_string(main->Config(), "AdvOut",
  898. "Track3Name");
  899. const char *name4 = config_get_string(main->Config(), "AdvOut",
  900. "Track4Name");
  901. bool applyServiceSettings = config_get_bool(main->Config(), "AdvOut",
  902. "ApplyServiceSettings");
  903. int streamTrackIndex = config_get_int(main->Config(), "AdvOut",
  904. "TrackIndex");
  905. obs_data_t *settings[4];
  906. for (size_t i = 0; i < 4; i++) {
  907. settings[i] = obs_data_create();
  908. obs_data_set_int(settings[i], "bitrate", GetAudioBitrate(i));
  909. }
  910. SetEncoderName(aacTrack[0], name1, "Track1");
  911. SetEncoderName(aacTrack[1], name2, "Track2");
  912. SetEncoderName(aacTrack[2], name3, "Track3");
  913. SetEncoderName(aacTrack[3], name4, "Track4");
  914. for (size_t i = 0; i < 4; i++) {
  915. if (applyServiceSettings && (int)(i + 1) == streamTrackIndex)
  916. obs_service_apply_encoder_settings(main->GetService(),
  917. nullptr, settings[i]);
  918. obs_encoder_update(aacTrack[i], settings[i]);
  919. obs_data_release(settings[i]);
  920. }
  921. }
  922. void AdvancedOutput::SetupOutputs()
  923. {
  924. obs_encoder_set_video(h264Streaming, obs_get_video());
  925. if (h264Recording)
  926. obs_encoder_set_video(h264Recording, obs_get_video());
  927. obs_encoder_set_audio(aacTrack[0], obs_get_audio());
  928. obs_encoder_set_audio(aacTrack[1], obs_get_audio());
  929. obs_encoder_set_audio(aacTrack[2], obs_get_audio());
  930. obs_encoder_set_audio(aacTrack[3], obs_get_audio());
  931. SetupStreaming();
  932. if (ffmpegOutput)
  933. SetupFFmpeg();
  934. else
  935. SetupRecording();
  936. }
  937. int AdvancedOutput::GetAudioBitrate(size_t i) const
  938. {
  939. const char *names[] = {
  940. "Track1Bitrate", "Track2Bitrate",
  941. "Track3Bitrate", "Track4Bitrate",
  942. };
  943. int bitrate = (int)config_get_uint(main->Config(), "AdvOut", names[i]);
  944. return FindClosestAvailableAACBitrate(bitrate);
  945. }
  946. bool AdvancedOutput::StartStreaming(obs_service_t *service)
  947. {
  948. if (!useStreamEncoder ||
  949. (!ffmpegOutput && !obs_output_active(fileOutput))) {
  950. UpdateStreamSettings();
  951. }
  952. UpdateAudioSettings();
  953. if (!Active())
  954. SetupOutputs();
  955. obs_output_set_service(streamOutput, service);
  956. bool reconnect = config_get_bool(main->Config(), "Output", "Reconnect");
  957. int retryDelay = config_get_int(main->Config(), "Output", "RetryDelay");
  958. int maxRetries = config_get_int(main->Config(), "Output", "MaxRetries");
  959. bool useDelay = config_get_bool(main->Config(), "Output",
  960. "DelayEnable");
  961. int delaySec = config_get_int(main->Config(), "Output",
  962. "DelaySec");
  963. bool preserveDelay = config_get_bool(main->Config(), "Output",
  964. "DelayPreserve");
  965. const char *bindIP = config_get_string(main->Config(), "Output",
  966. "BindIP");
  967. obs_data_t *settings = obs_data_create();
  968. obs_data_set_string(settings, "bind_ip", bindIP);
  969. obs_output_update(streamOutput, settings);
  970. obs_data_release(settings);
  971. if (!reconnect)
  972. maxRetries = 0;
  973. obs_output_set_delay(streamOutput, useDelay ? delaySec : 0,
  974. preserveDelay ? OBS_OUTPUT_DELAY_PRESERVE : 0);
  975. obs_output_set_reconnect_settings(streamOutput, maxRetries,
  976. retryDelay);
  977. if (obs_output_start(streamOutput)) {
  978. return true;
  979. }
  980. return false;
  981. }
  982. bool AdvancedOutput::StartRecording()
  983. {
  984. const char *path;
  985. const char *recFormat;
  986. const char *filenameFormat;
  987. bool noSpace = false;
  988. bool overwriteIfExists = false;
  989. if (!useStreamEncoder) {
  990. if (!ffmpegOutput) {
  991. UpdateRecordingSettings();
  992. }
  993. } else if (!obs_output_active(streamOutput)) {
  994. UpdateStreamSettings();
  995. }
  996. UpdateAudioSettings();
  997. if (!Active())
  998. SetupOutputs();
  999. if (!ffmpegOutput || ffmpegRecording) {
  1000. path = config_get_string(main->Config(), "AdvOut",
  1001. ffmpegRecording ? "FFFilePath" : "RecFilePath");
  1002. recFormat = config_get_string(main->Config(), "AdvOut",
  1003. ffmpegRecording ? "FFExtension" : "RecFormat");
  1004. filenameFormat = config_get_string(main->Config(), "Output",
  1005. "FilenameFormatting");
  1006. overwriteIfExists = config_get_bool(main->Config(), "Output",
  1007. "OverwriteIfExists");
  1008. noSpace = config_get_bool(main->Config(), "AdvOut",
  1009. ffmpegRecording ?
  1010. "FFFileNameWithoutSpace" :
  1011. "RecFileNameWithoutSpace");
  1012. os_dir_t *dir = path ? os_opendir(path) : nullptr;
  1013. if (!dir) {
  1014. if (main->isVisible())
  1015. QMessageBox::information(main,
  1016. QTStr("Output.BadPath.Title"),
  1017. QTStr("Output.BadPath.Text"));
  1018. else
  1019. main->SysTrayNotify(QTStr("Output.BadPath.Text"),
  1020. QSystemTrayIcon::Warning);
  1021. return false;
  1022. }
  1023. os_closedir(dir);
  1024. string strPath;
  1025. strPath += path;
  1026. char lastChar = strPath.back();
  1027. if (lastChar != '/' && lastChar != '\\')
  1028. strPath += "/";
  1029. strPath += GenerateSpecifiedFilename(recFormat, noSpace,
  1030. filenameFormat);
  1031. ensure_directory_exists(strPath);
  1032. if (!overwriteIfExists)
  1033. FindBestFilename(strPath, noSpace);
  1034. obs_data_t *settings = obs_data_create();
  1035. obs_data_set_string(settings,
  1036. ffmpegRecording ? "url" : "path",
  1037. strPath.c_str());
  1038. obs_output_update(fileOutput, settings);
  1039. obs_data_release(settings);
  1040. }
  1041. if (obs_output_start(fileOutput)) {
  1042. return true;
  1043. }
  1044. return false;
  1045. }
  1046. void AdvancedOutput::StopStreaming(bool force)
  1047. {
  1048. if (force)
  1049. obs_output_force_stop(streamOutput);
  1050. else
  1051. obs_output_stop(streamOutput);
  1052. }
  1053. void AdvancedOutput::StopRecording(bool force)
  1054. {
  1055. if (force)
  1056. obs_output_force_stop(fileOutput);
  1057. else
  1058. obs_output_stop(fileOutput);
  1059. }
  1060. bool AdvancedOutput::StreamingActive() const
  1061. {
  1062. return obs_output_active(streamOutput);
  1063. }
  1064. bool AdvancedOutput::RecordingActive() const
  1065. {
  1066. return obs_output_active(fileOutput);
  1067. }
  1068. /* ------------------------------------------------------------------------ */
  1069. BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main)
  1070. {
  1071. return new SimpleOutput(main);
  1072. }
  1073. BasicOutputHandler *CreateAdvancedOutputHandler(OBSBasic *main)
  1074. {
  1075. return new AdvancedOutput(main);
  1076. }