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.

340 lines
8.2 KiB

2 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
2 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
  1. #include "obs-manager.hpp"
  2. #include "settings-manager.hpp"
  3. #include <ctime>
  4. #include <list>
  5. #include <filesystem>
  6. #include <stdexcept>
  7. using namespace std;
  8. //
  9. // Static Functions
  10. //
  11. static void OBSRender(void *param, uint32_t cx, uint32_t cy)
  12. {
  13. obs_render_main_texture();
  14. }
  15. static void OBSStartRecording(void *data, calldata_t *params)
  16. {
  17. OBSManager *o = static_cast<OBSManager*>(data);
  18. o->sigStartRecording.emit();
  19. }
  20. static void OBSStopRecording(void *data, calldata_t *params)
  21. {
  22. OBSManager *o = static_cast<OBSManager*>(data);
  23. o->sigStopRecording.emit();
  24. }
  25. //
  26. // OBSManager
  27. //
  28. OBSManager::OBSManager()
  29. {
  30. mPlugins = {
  31. "obs-ffmpeg.so",
  32. "obs-outputs.so",
  33. "obs-x264.so",
  34. "linux-alsa.so",
  35. "linux-pulseaudio.so",
  36. "linux-v4l2.so",
  37. "linux-capture.so",
  38. };
  39. }
  40. OBSManager::~OBSManager()
  41. {
  42. Cleanup();
  43. }
  44. string OBSManager::GetVersion()
  45. {
  46. return string(obs_get_version_string());
  47. }
  48. void OBSManager::LoadSettings(SettingsManager *settings)
  49. {
  50. mPluginDir = settings->GetWithDefault(SETTINGS_KEY_PLUGIN_DIR, SETTINGS_DEFAULT_PLUGIN_DIR);
  51. mOutputDir = settings->GetWithDefault(SETTINGS_KEY_OUTPUT_DIR, std::filesystem::current_path());
  52. mWebcamDeviceID = settings->Get(SETTINGS_KEY_VIDEO_DEVICE_ID);
  53. mAudioDeviceID = settings->Get(SETTINGS_KEY_AUDIO_DEVICE_ID);
  54. mScreenEnabled = settings->GetBool(SETTINGS_KEY_SCREEN_ENABLED);
  55. mWebcamEnabled = settings->GetBool(SETTINGS_KEY_WEBCAM_ENABLED);
  56. mAudioEnabled = settings->GetBool(SETTINGS_KEY_AUDIO_ENABLED);
  57. }
  58. void OBSManager::Initialize(Gdk::Rectangle rect)
  59. {
  60. if (isInitialized)
  61. return;
  62. mScreenWidth = rect.get_width();
  63. mScreenHeight = rect.get_height();
  64. auto settings = new SettingsManager();
  65. LoadSettings(settings);
  66. if (!obs_startup("en-US", NULL, NULL))
  67. throw runtime_error("Failed to initialize OBS");
  68. loadPlugins();
  69. obs_video_info v = {};
  70. v.graphics_module = "libobs-opengl.so.0";
  71. v.fps_num = 30000;
  72. v.fps_den = 1001;
  73. v.base_width = mScreenWidth;
  74. v.base_height = mScreenHeight;
  75. v.output_width = mScreenWidth;
  76. v.output_height = mScreenHeight;
  77. v.output_format = VIDEO_FORMAT_NV12;
  78. v.adapter = 0;
  79. v.gpu_conversion = true;
  80. v.colorspace = VIDEO_CS_601;
  81. v.range = VIDEO_RANGE_PARTIAL;
  82. v.scale_type = OBS_SCALE_BICUBIC;
  83. obs_audio_info a = {};
  84. a.samples_per_sec = 44100;
  85. a.speakers = SPEAKERS_STEREO;
  86. obs_reset_video(&v);
  87. obs_reset_audio(&a);
  88. isInitialized = true;
  89. //printTypes();
  90. }
  91. void OBSManager::StartPreview(XID wid, Display *wdisplay)
  92. {
  93. gs_init_data init = {};
  94. init.cx = mScreenWidth;
  95. init.cy = mScreenHeight;
  96. init.format = GS_BGRA;
  97. init.zsformat = GS_ZS_NONE;
  98. init.window.id = wid;
  99. init.window.display = (void*)wdisplay;
  100. mDisplay = obs_display_create(&init, 0);
  101. if (mDisplay == nullptr)
  102. throw runtime_error("Failed to create display");
  103. //obs_display_resize(mDisplay, PreviewWidth, PreviewHeight);
  104. obs_display_add_draw_callback(mDisplay, OBSRender, nullptr);
  105. auto sources = new list<OBSSource>();
  106. OBSScene scene = obs_scene_create("Main");
  107. if (scene == NULL)
  108. throw runtime_error("Couldn't create scene\n");
  109. if (mScreenEnabled)
  110. {
  111. auto source = CreateScreenSource();
  112. obs_scene_add(scene, source);
  113. sources->push_back(source);
  114. }
  115. if (mWebcamEnabled)
  116. {
  117. vec2 scale;
  118. vec2_set(&scale, 0.5f, 0.5f);
  119. auto source = CreateWebcamSource();
  120. auto item = obs_scene_add(scene, source);
  121. obs_sceneitem_set_scale(item, &scale);
  122. sources->push_back(source);
  123. }
  124. if (mAudioEnabled)
  125. {
  126. auto source = CreateAudioSource();
  127. obs_scene_add(scene, source);
  128. sources->push_back(source);
  129. }
  130. obs_set_output_source(0, obs_scene_get_source(scene));
  131. obs_scene_release(scene);
  132. for (auto source : *sources)
  133. {
  134. obs_source_release(source);
  135. }
  136. sigStartPreview.emit();
  137. }
  138. void OBSManager::StopPreview()
  139. {
  140. if (mDisplay != nullptr)
  141. obs_display_destroy(mDisplay);
  142. sigStopPreview.emit();
  143. }
  144. void OBSManager::StartRecording()
  145. {
  146. if (isRecording)
  147. return;
  148. auto s = new SettingsManager();
  149. obs_encoder_t *venc = obs_video_encoder_create("obs_x264", "x264", nullptr, nullptr);
  150. obs_encoder_t *aenc = obs_audio_encoder_create("ffmpeg_aac", "aac", nullptr, 0, nullptr);
  151. obs_encoder_set_video(venc, obs_get_video());
  152. obs_encoder_set_audio(aenc, obs_get_audio());
  153. string path = s->GetWithDefault(SETTINGS_KEY_OUTPUT_DIR, filesystem::current_path());
  154. if (path.back() != '/')
  155. path += "/";
  156. string fileName = path + "recording_" + to_string(time(0)) + ".mp4";
  157. obs_data_t *settings = obs_data_create();
  158. obs_data_set_string(settings, "directory", path.c_str());
  159. obs_data_set_string(settings, "url", fileName.c_str());
  160. mOutput = obs_output_create("ffmpeg_output", "output", nullptr, nullptr);
  161. obs_output_set_video_encoder(mOutput, venc);
  162. obs_output_set_audio_encoder(mOutput, aenc, 0);
  163. obs_output_update(mOutput, settings);
  164. obs_output_set_media(mOutput, obs_get_video(), obs_get_audio());
  165. obs_data_release(settings);
  166. if (!obs_output_start(mOutput))
  167. throw runtime_error("Failed to start recording");
  168. obsStartRecording.Connect(obs_output_get_signal_handler(mOutput), "start", OBSStartRecording, this);
  169. obsStopRecording.Connect(obs_output_get_signal_handler(mOutput), "stop", OBSStopRecording, this);
  170. obs_encoder_release(venc);
  171. obs_encoder_release(aenc);
  172. obs_output_release(mOutput);
  173. isRecording = true;
  174. }
  175. void OBSManager::StopRecording()
  176. {
  177. if (isRecording && mOutput != nullptr)
  178. obs_output_stop(mOutput);
  179. isRecording = false;
  180. }
  181. bool OBSManager::IsRecording()
  182. {
  183. return isRecording;
  184. }
  185. void OBSManager::Cleanup()
  186. {
  187. if (isInitialized)
  188. {
  189. StopRecording();
  190. StopPreview();
  191. if (obs_initialized())
  192. obs_shutdown();
  193. isInitialized = false;
  194. }
  195. sigCleanup.emit();
  196. }
  197. //
  198. // Sources
  199. //
  200. OBSSource OBSManager::CreateScreenSource()
  201. {
  202. obs_data_t *settings = obs_data_create();
  203. OBSSource source = obs_source_create("xshm_input", "Screen Source", settings, NULL);
  204. if (source == NULL)
  205. throw runtime_error("Couldn't create screen source");
  206. obs_data_release(settings);
  207. return source;
  208. }
  209. OBSSource OBSManager::CreateWebcamSource()
  210. {
  211. obs_data_t *settings = obs_data_create();
  212. obs_data_set_string(settings, "device_id", mWebcamDeviceID.c_str());
  213. OBSSource source = obs_source_create("v4l2_input", "Webcam Source", settings, NULL);
  214. if (source == NULL)
  215. throw runtime_error("Couldn't create webcam source");
  216. obs_data_release(settings);
  217. return source;
  218. }
  219. OBSSource OBSManager::CreateAudioSource()
  220. {
  221. obs_data_t *settings = obs_data_create();
  222. obs_data_set_string(settings, "device_id", mAudioDeviceID.c_str());
  223. OBSSource source = obs_source_create("pulse_input_capture", "Audio Source", settings, NULL);
  224. if (source == NULL)
  225. throw runtime_error("Couldn't create screen source");
  226. obs_data_release(settings);
  227. return source;
  228. }
  229. //
  230. // Private (Helpers)
  231. //
  232. void OBSManager::loadPlugin(string name)
  233. {
  234. obs_module_t *module;
  235. string path;
  236. path.append(mPluginDir);
  237. path.append(name);
  238. int res = obs_open_module(&module, path.c_str(), nullptr);
  239. if (res != MODULE_SUCCESS)
  240. throw runtime_error("Failed to open plugin");
  241. obs_init_module(module);
  242. }
  243. void OBSManager::loadPlugins()
  244. {
  245. for (string plugin : mPlugins)
  246. {
  247. loadPlugin(plugin);
  248. }
  249. obs_post_load_modules();
  250. }
  251. void OBSManager::printTypes()
  252. {
  253. const char *t;
  254. for (int i = 0; obs_enum_input_types(i, &t); ++i)
  255. {
  256. cout << "input type: " << t << endl;
  257. }
  258. for (int i = 0; obs_enum_output_types(i, &t); ++i)
  259. {
  260. cout << "output type: " << t << endl;
  261. }
  262. for (int i = 0; obs_enum_encoder_types(i, &t); ++i)
  263. {
  264. cout << "encoder type: " << t << endl;
  265. }
  266. }