meltygui 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. meltygui/__init__.py +107 -0
  2. meltygui/accounts/__init__.py +0 -0
  3. meltygui/accounts/internet_accounts.py +1355 -0
  4. meltygui/chat/__init__.py +91 -0
  5. meltygui/chat/activity.py +75 -0
  6. meltygui/chat/backends.py +36 -0
  7. meltygui/chat/chat_interface.py +732 -0
  8. meltygui/chat/chat_proxy.py +352 -0
  9. meltygui/chat/codex_proxy.py +592 -0
  10. meltygui/chat/codex_settings.py +100 -0
  11. meltygui/chat/codex_transport.py +60 -0
  12. meltygui/chat/command_parser.py +204 -0
  13. meltygui/chat/images.py +227 -0
  14. meltygui/chat/messages.py +417 -0
  15. meltygui/chat/metadata.py +139 -0
  16. meltygui/chat/writer_locks.py +64 -0
  17. meltygui/code/__init__.py +0 -0
  18. meltygui/code/basic_converters.py +533 -0
  19. meltygui/code/chain_converters.py +2111 -0
  20. meltygui/code/code_checks.py +2209 -0
  21. meltygui/code/core_syntax.py +1430 -0
  22. meltygui/code/file_converters.py +1933 -0
  23. meltygui/code/fileref.py +702 -0
  24. meltygui/code/hotswap_guard.py +144 -0
  25. meltygui/code/libcst_conversion.py +9724 -0
  26. meltygui/code/live_instrument.py +392 -0
  27. meltygui/code/live_view.py +2490 -0
  28. meltygui/code/melty_scan.py +2684 -0
  29. meltygui/code/new_codecs.py +1255 -0
  30. meltygui/code/new_converters.py +3017 -0
  31. meltygui/code/project_code.py +278 -0
  32. meltygui/code/source_context.py +63 -0
  33. meltygui/code/symbol_roster.py +1588 -0
  34. meltygui/code/syntax_check.py +34 -0
  35. meltygui/code/syntax_check_worker.py +114 -0
  36. meltygui/completion/__init__.py +0 -0
  37. meltygui/completion/fim.py +1232 -0
  38. meltygui/completion/fim_context.py +481 -0
  39. meltygui/completion/providers/__init__.py +0 -0
  40. meltygui/completion/providers/anthropic_oauth.py +446 -0
  41. meltygui/completion/providers/anthropic_requests.py +69 -0
  42. meltygui/completion/providers/claude.py +203 -0
  43. meltygui/completion/providers/claude_usage.py +499 -0
  44. meltygui/completion/providers/codex_accounts.py +180 -0
  45. meltygui/completion/providers/copilot.py +617 -0
  46. meltygui/completion/providers/oauth_popup.py +220 -0
  47. meltygui/completion/providers/ollama.py +320 -0
  48. meltygui/completion/providers/profiles.py +23 -0
  49. meltygui/core/README.md +88 -0
  50. meltygui/core/__init__.py +1 -0
  51. meltygui/core/automation/__init__.py +1 -0
  52. meltygui/core/automation/action_core.py +153 -0
  53. meltygui/core/automation/collection_action.py +45 -0
  54. meltygui/core/automation/mcp_eval.py +167 -0
  55. meltygui/core/automation/mcp_hotswap.py +107 -0
  56. meltygui/core/automation/mcp_query.py +552 -0
  57. meltygui/core/automation/mcp_server.py +657 -0
  58. meltygui/core/automation/orchestration_core.py +2636 -0
  59. meltygui/core/automation/query_core.py +124 -0
  60. meltygui/core/automation/search_core.py +26 -0
  61. meltygui/core/automation/selector_core.py +340 -0
  62. meltygui/core/automation/value_core.py +1752 -0
  63. meltygui/core/cache/__init__.py +1 -0
  64. meltygui/core/cache/cache_diagnostics.py +0 -0
  65. meltygui/core/cache/invalidation_decoration.py +153 -0
  66. meltygui/core/cache/invalidation_tracker.py +43 -0
  67. meltygui/core/cache/tile_cache.py +5968 -0
  68. meltygui/core/conversion/__init__.py +1 -0
  69. meltygui/core/conversion/bubbling.py +599 -0
  70. meltygui/core/conversion/cache_tree.py +188 -0
  71. meltygui/core/conversion/chain.py +113 -0
  72. meltygui/core/conversion/converter_register.py +145 -0
  73. meltygui/core/conversion/data_decoration.py +67 -0
  74. meltygui/core/conversion/dict_conversion.py +1815 -0
  75. meltygui/core/conversion/dict_conversion_util.py +177 -0
  76. meltygui/core/conversion/dynamic_obj.py +89 -0
  77. meltygui/core/conversion/graph_compare.py +183 -0
  78. meltygui/core/conversion/load_save_v2.py +1032 -0
  79. meltygui/core/conversion/missing_saved_class.py +39 -0
  80. meltygui/core/conversion/path_finder.py +606 -0
  81. meltygui/core/conversion/render_host.py +1083 -0
  82. meltygui/core/core_render.py +6537 -0
  83. meltygui/core/definition_hotswap.py +231 -0
  84. meltygui/core/diagnostics/__init__.py +1 -0
  85. meltygui/core/diagnostics/attribute_churn.py +38 -0
  86. meltygui/core/diagnostics/fps_counter.py +39 -0
  87. meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
  88. meltygui/core/diagnostics/inspection_core.py +169 -0
  89. meltygui/core/diagnostics/monitor_core.py +105 -0
  90. meltygui/core/diagnostics/notifications.py +706 -0
  91. meltygui/core/diagnostics/perf_trace.py +281 -0
  92. meltygui/core/diagnostics/profile_decoration.py +88 -0
  93. meltygui/core/diagnostics/resize_trace.py +62 -0
  94. meltygui/core/diagnostics/screenshot_core.py +247 -0
  95. meltygui/core/diagnostics/session_status.py +98 -0
  96. meltygui/core/diagnostics/trace_core.py +445 -0
  97. meltygui/core/files/__init__.py +1 -0
  98. meltygui/core/files/file_core.py +208 -0
  99. meltygui/core/files/file_explorer_core.py +104 -0
  100. meltygui/core/files/file_tree_core.py +198 -0
  101. meltygui/core/files/file_watch_core.py +43 -0
  102. meltygui/core/files/import_graph_core.py +43 -0
  103. meltygui/core/files/metadata_core.py +51 -0
  104. meltygui/core/graphics/__init__.py +1 -0
  105. meltygui/core/graphics/cuda_context_core.py +166 -0
  106. meltygui/core/graphics/cuda_interop_core.py +136 -0
  107. meltygui/core/graphics/cuda_kernel_core.py +91 -0
  108. meltygui/core/graphics/framebuffer_recorder.py +337 -0
  109. meltygui/core/graphics/gl_state.py +658 -0
  110. meltygui/core/graphics/lut_core.py +52 -0
  111. meltygui/core/graphics/overlay_renderer.py +984 -0
  112. meltygui/core/graphics/scene_target.py +180 -0
  113. meltygui/core/graphics/screenshot.py +439 -0
  114. meltygui/core/graphics/shader_func.py +478 -0
  115. meltygui/core/graphics/tensor_core.py +45 -0
  116. meltygui/core/graphics/text_texture.py +329 -0
  117. meltygui/core/graphics/wayland_color.py +635 -0
  118. meltygui/core/input/__init__.py +1 -0
  119. meltygui/core/input/collision.py +165 -0
  120. meltygui/core/input/drag_drop_core.py +1525 -0
  121. meltygui/core/input/hypr_left_drag.py +323 -0
  122. meltygui/core/input/input_core.py +245 -0
  123. meltygui/core/input/input_handler.py +1101 -0
  124. meltygui/core/input/mouse_cursor.py +355 -0
  125. meltygui/core/input/pynput_backend.py +1054 -0
  126. meltygui/core/input/space_mouse.py +338 -0
  127. meltygui/core/input/touchpad_backend.py +393 -0
  128. meltygui/core/input/view_selection.py +177 -0
  129. meltygui/core/layout/__init__.py +1 -0
  130. meltygui/core/layout/column_core.py +2153 -0
  131. meltygui/core/layout/cursor_core.py +161 -0
  132. meltygui/core/layout/dropdown_core.py +278 -0
  133. meltygui/core/layout/edge_constraints.py +155 -0
  134. meltygui/core/layout/grid_core.py +117 -0
  135. meltygui/core/layout/header_core.py +23 -0
  136. meltygui/core/layout/header_runtime.py +67 -0
  137. meltygui/core/layout/layout_core.py +87 -0
  138. meltygui/core/layout/tile_manager_core.py +591 -0
  139. meltygui/core/melty.py +6726 -0
  140. meltygui/core/module_map.json +896 -0
  141. meltygui/core/module_names.py +19 -0
  142. meltygui/core/rendering/__init__.py +1 -0
  143. meltygui/core/rendering/core_decoration.py +431 -0
  144. meltygui/core/rendering/core_render_helpers.py +328 -0
  145. meltygui/core/rendering/func_metadata.py +398 -0
  146. meltygui/core/rendering/mode.py +818 -0
  147. meltygui/core/rendering/mode_defaults.py +41 -0
  148. meltygui/core/rendering/modes.py +136 -0
  149. meltygui/core/rendering/parameter_core.py +1665 -0
  150. meltygui/core/rendering/render_dispatch.py +1891 -0
  151. meltygui/core/rendering/render_funcs.py +273 -0
  152. meltygui/core/rendering/shaped.py +312 -0
  153. meltygui/core/rendering/window_decoration.py +25 -0
  154. meltygui/core/runtime/__init__.py +1 -0
  155. meltygui/core/runtime/app.py +735 -0
  156. meltygui/core/runtime/app_session.py +140 -0
  157. meltygui/core/runtime/background.py +564 -0
  158. meltygui/core/runtime/extensions.py +53 -0
  159. meltygui/core/runtime/gc_manager.py +1163 -0
  160. meltygui/core/runtime/lifecycle.py +21 -0
  161. meltygui/core/runtime/paths.py +27 -0
  162. meltygui/core/runtime/settings.py +14 -0
  163. meltygui/core/runtime/singleton.py +16 -0
  164. meltygui/core/runtime/thread_safe_bool.py +24 -0
  165. meltygui/core/runtime/thread_signal.py +30 -0
  166. meltygui/core/runtime/toggles.py +3115 -0
  167. meltygui/core/services/__init__.py +1 -0
  168. meltygui/core/services/account_core.py +10 -0
  169. meltygui/core/services/chat_core.py +19 -0
  170. meltygui/core/services/claude_terminal_core.py +346 -0
  171. meltygui/core/services/terminal_core.py +458 -0
  172. meltygui/core/services/terminal_runtime.py +78 -0
  173. meltygui/core/styling/__init__.py +1 -0
  174. meltygui/core/styling/color_core.py +46 -0
  175. meltygui/core/styling/fonts.py +639 -0
  176. meltygui/core/styling/global_style.py +338 -0
  177. meltygui/core/styling/style.py +198 -0
  178. meltygui/core/styling/style_core.py +522 -0
  179. meltygui/core/styling/warm_start.py +149 -0
  180. meltygui/core/windowing/__init__.py +1 -0
  181. meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
  182. meltygui/core/windowing/backends/__init__.py +1 -0
  183. meltygui/core/windowing/backends/imgui_renderer.py +138 -0
  184. meltygui/core/windowing/backends/native_wayland.py +850 -0
  185. meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
  186. meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
  187. meltygui/core/windowing/backends/wayland_protocol.py +101 -0
  188. meltygui/core/windowing/dock_core.py +182 -0
  189. meltygui/core/windowing/frame_geometry.py +42 -0
  190. meltygui/core/windowing/geometry_feed.py +864 -0
  191. meltygui/core/windowing/glfw_utils.py +1343 -0
  192. meltygui/core/windowing/os_frame.py +1552 -0
  193. meltygui/core/windowing/surface.py +643 -0
  194. meltygui/core/windowing/titlebar.py +1560 -0
  195. meltygui/core/windowing/titlebar_buttons.py +281 -0
  196. meltygui/core/windowing/wayland_move.py +932 -0
  197. meltygui/core/windowing/window_api.py +62 -0
  198. meltygui/core/windowing/window_constants.py +339 -0
  199. meltygui/core/windowing/window_visibility.py +162 -0
  200. meltygui/debug/__init__.py +0 -0
  201. meltygui/debug/app_view_utils.py +9 -0
  202. meltygui/editor/__init__.py +0 -0
  203. meltygui/editor/bash_syntax.py +30 -0
  204. meltygui/editor/code_line_fast.py +152 -0
  205. meltygui/editor/diff.py +139 -0
  206. meltygui/editor/external_changes.py +159 -0
  207. meltygui/editor/file_header.py +41 -0
  208. meltygui/editor/live_usage.py +169 -0
  209. meltygui/editor/live_view_views.py +1803 -0
  210. meltygui/editor/pending_save.py +1351 -0
  211. meltygui/editor/roster_tints.py +547 -0
  212. meltygui/editor/source_preview.py +16 -0
  213. meltygui/editor/source_tools.py +10 -0
  214. meltygui/editor/source_ui.py +70 -0
  215. meltygui/editor/spell_check.py +77 -0
  216. meltygui/editor/text_editor.py +9076 -0
  217. meltygui/editor/usage_picker.py +538 -0
  218. meltygui/events/__init__.py +0 -0
  219. meltygui/events/example.py +118 -0
  220. meltygui/examples/__init__.py +0 -0
  221. meltygui/examples/columns_demo.py +33 -0
  222. meltygui/examples/columns_window_demo.py +60 -0
  223. meltygui/examples/context_menu_demo.py +24 -0
  224. meltygui/examples/context_menu_window_demo.py +49 -0
  225. meltygui/examples/gui_playground.py +127 -0
  226. meltygui/examples/live_view_playground.py +256 -0
  227. meltygui/examples/lora.py +43 -0
  228. meltygui/examples/lora_data.py +59 -0
  229. meltygui/examples/lora_policies.py +67 -0
  230. meltygui/examples/mode_demo.py +49 -0
  231. meltygui/examples/modifies_demo.py +119 -0
  232. meltygui/examples/scalar_policies.py +60 -0
  233. meltygui/examples/style_layouts.py +149 -0
  234. meltygui/examples/tile_manager_demo.py +74 -0
  235. meltygui/examples/tint_demo.py +160 -0
  236. meltygui/examples/tint_functions.py +56 -0
  237. meltygui/examples/trace_demo.py +88 -0
  238. meltygui/examples/two_windows.py +36 -0
  239. meltygui/files/__init__.py +0 -0
  240. meltygui/files/fast_file_explorer.py +439 -0
  241. meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
  242. meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
  243. meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
  244. meltygui/graphics/__init__.py +6 -0
  245. meltygui/graphics/base.py +85 -0
  246. meltygui/graphics/examples.py +507 -0
  247. meltygui/graphics/executor.py +520 -0
  248. meltygui/graphics/filter.py +667 -0
  249. meltygui/graphics/filter.pyi +856 -0
  250. meltygui/graphics/generate_stubs.py +22 -0
  251. meltygui/graphics/registry.py +203 -0
  252. meltygui/graphics/shader_compiler.py +155 -0
  253. meltygui/graphics/shaders.py +1302 -0
  254. meltygui/graphics/stub_generator.py +250 -0
  255. meltygui/graphics/texture_manager.py +170 -0
  256. meltygui/graphics/texture_min_max.py +295 -0
  257. meltygui/hdr_color.py +757 -0
  258. meltygui/image_load.py +308 -0
  259. meltygui/model/__init__.py +1 -0
  260. meltygui/model/account_model.py +142 -0
  261. meltygui/model/camera_model.py +159 -0
  262. meltygui/model/chat_model.py +86 -0
  263. meltygui/model/code_model.py +62 -0
  264. meltygui/model/code_proxy_model.py +876 -0
  265. meltygui/model/collection_model.py +41 -0
  266. meltygui/model/color_model.py +127 -0
  267. meltygui/model/cuda_tensor_model.py +36 -0
  268. meltygui/model/cuda_texture_model.py +149 -0
  269. meltygui/model/dropdown_model.py +137 -0
  270. meltygui/model/file_metadata_model.py +73 -0
  271. meltygui/model/file_model.py +289 -0
  272. meltygui/model/format_model.py +390 -0
  273. meltygui/model/graph_model.py +98 -0
  274. meltygui/model/icon_model.py +1024 -0
  275. meltygui/model/import_graph_model.py +468 -0
  276. meltygui/model/layout_model.py +15 -0
  277. meltygui/model/lut_model.py +266 -0
  278. meltygui/model/search_model.py +173 -0
  279. meltygui/model/tensor_model.py +402 -0
  280. meltygui/model/terminal_model.py +329 -0
  281. meltygui/model/texture_model.py +146 -0
  282. meltygui/model/tile_model.py +24 -0
  283. meltygui/model/trace_model.py +198 -0
  284. meltygui/model/trace_report_model.py +146 -0
  285. meltygui/models/__init__.py +0 -0
  286. meltygui/models/file_meta.py +613 -0
  287. meltygui/models/function_console.py +184 -0
  288. meltygui/models/orchestration.py +48 -0
  289. meltygui/pbr.py +1576 -0
  290. meltygui/png_unfilter.c +49 -0
  291. meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
  292. meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
  293. meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
  294. meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
  295. meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
  296. meltygui/resources/dejavu/LICENSE.txt +78 -0
  297. meltygui/resources/fontawesome-LICENSE.txt +121 -0
  298. meltygui/resources/fontawesome-webfont.ttf +0 -0
  299. meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
  300. meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
  301. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
  302. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
  303. meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
  304. meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
  305. meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
  306. meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
  307. meltygui/resources/jetbrains-weights/OFL.txt +93 -0
  308. meltygui/resources/jetbrains-weights/README.md +2 -0
  309. meltygui/state/__init__.py +0 -0
  310. meltygui/state/account_state.py +22 -0
  311. meltygui/state/animation_state.py +97 -0
  312. meltygui/state/annotation_state.py +22 -0
  313. meltygui/state/chat_state.py +36 -0
  314. meltygui/state/code_state.py +10 -0
  315. meltygui/state/core_enums.py +59 -0
  316. meltygui/state/core_markers.py +115 -0
  317. meltygui/state/core_undo.py +1075 -0
  318. meltygui/state/file_state.py +75 -0
  319. meltygui/state/graph_state.py +26 -0
  320. meltygui/state/inspection_state.py +47 -0
  321. meltygui/state/menu_state.py +10 -0
  322. meltygui/state/model_enums.py +11 -0
  323. meltygui/state/new_core_model.py +2394 -0
  324. meltygui/state/orchestration_state.py +14 -0
  325. meltygui/state/query_state.py +24 -0
  326. meltygui/state/tensor_state.py +10 -0
  327. meltygui/state/terminal_state.py +23 -0
  328. meltygui/state/trace_state.py +32 -0
  329. meltygui/state/voxel_state.py +12 -0
  330. meltygui/text_index.py +816 -0
  331. meltygui/utils/__init__.py +0 -0
  332. meltygui/utils/jump_to_code.py +344 -0
  333. meltygui/utils/pkl_inspect.py +90 -0
  334. meltygui/utils/render_utils.py +1419 -0
  335. meltygui/view/__init__.py +1 -0
  336. meltygui/view/account_view.py +524 -0
  337. meltygui/view/action_view.py +73 -0
  338. meltygui/view/chat_decoration_view.py +112 -0
  339. meltygui/view/chat_view.py +1703 -0
  340. meltygui/view/code_view.py +2677 -0
  341. meltygui/view/collection_view.py +1185 -0
  342. meltygui/view/color_view.py +824 -0
  343. meltygui/view/control_view.py +559 -0
  344. meltygui/view/decoration_view.py +439 -0
  345. meltygui/view/diagnostic_view.py +177 -0
  346. meltygui/view/dropdown_view.py +1082 -0
  347. meltygui/view/file_view.py +1599 -0
  348. meltygui/view/graph_cuda_view.py +105 -0
  349. meltygui/view/graph_view.py +882 -0
  350. meltygui/view/header_view.py +848 -0
  351. meltygui/view/input_view.py +238 -0
  352. meltygui/view/inspection_view.py +1783 -0
  353. meltygui/view/layout_view.py +596 -0
  354. meltygui/view/lut_view.py +45 -0
  355. meltygui/view/menu_view.py +212 -0
  356. meltygui/view/orchestration_view.py +658 -0
  357. meltygui/view/query_view.py +118 -0
  358. meltygui/view/search_view.py +342 -0
  359. meltygui/view/tab_view.py +258 -0
  360. meltygui/view/tensor_view.py +430 -0
  361. meltygui/view/terminal_view.py +355 -0
  362. meltygui/view/text_view.py +8031 -0
  363. meltygui/view/texture_view.py +480 -0
  364. meltygui/view/tile_view.py +38 -0
  365. meltygui/view/trace_view.py +923 -0
  366. meltygui/view/voxel_cuda_view.py +824 -0
  367. meltygui/view/voxel_view.py +1860 -0
  368. meltygui/view/window_view.py +111 -0
  369. meltygui-0.1.0.dist-info/METADATA +143 -0
  370. meltygui-0.1.0.dist-info/RECORD +372 -0
  371. meltygui-0.1.0.dist-info/WHEEL +4 -0
  372. meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,337 @@
1
+ import ctypes
2
+ import subprocess
3
+ import tempfile
4
+
5
+ import OpenGL.GL as gl
6
+ import numpy as np
7
+
8
+
9
+ class FramebufferRecorder:
10
+ def __init__(self, width, height, fps=30, codec='h264', bitrate='20M', gpu_encoding=True):
11
+ """
12
+ Initialize recorder for OpenGL framebuffers
13
+
14
+ Args:
15
+ width (int): Width of the framebuffer
16
+ height (int): Height of the framebuffer
17
+ fps (int): Frames per second for recording
18
+ codec (str): Codec to use for video ('h264', 'hevc', etc.)
19
+ bitrate (str): Target bitrate for video
20
+ gpu_encoding (bool): Whether to use GPU acceleration if available
21
+ """
22
+ self.width = width
23
+ self.height = height
24
+ self.fps = fps
25
+ self.codec = codec
26
+ self.bitrate = bitrate
27
+ self.gpu_encoding = gpu_encoding
28
+
29
+ # Configuration
30
+ self.recording = False
31
+ self.frames = []
32
+ self.frame_count = 0
33
+
34
+ # Create a pixel buffer object (PBO) for faster GPU->CPU transfers
35
+ self.pbo = gl.glGenBuffers(1)
36
+ gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, self.pbo)
37
+ gl.glBufferData(gl.GL_PIXEL_PACK_BUFFER, width * height * 4, None, gl.GL_STREAM_READ)
38
+ gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, 0)
39
+
40
+ def start_recording(self):
41
+ """Start recording frames"""
42
+ if self.recording:
43
+ return
44
+
45
+ self.recording = True
46
+ self.frames = []
47
+ self.frame_count = 0
48
+ print("Recording started...")
49
+
50
+ def stop_recording(self):
51
+ """Stop recording"""
52
+ if not self.recording:
53
+ return
54
+
55
+ self.recording = False
56
+ print(f"Recording stopped. Captured {self.frame_count} frames.")
57
+
58
+ def capture_frame(self, framebuffer=0, read_buffer=gl.GL_BACK):
59
+ """
60
+ Capture a frame from the specified framebuffer
61
+
62
+ Args:
63
+ framebuffer (int): OpenGL framebuffer object ID (0 for default framebuffer)
64
+ read_buffer (GL enum): Buffer to read from (GL_BACK, GL_COLOR_ATTACHMENT0, etc.)
65
+
66
+ Returns:
67
+ bool: True if frame was captured successfully
68
+ """
69
+ if not self.recording:
70
+ return False
71
+
72
+ # Bind the framebuffer we want to read from
73
+ current_fbo = gl.glGetIntegerv(gl.GL_READ_FRAMEBUFFER_BINDING)
74
+ gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, framebuffer)
75
+
76
+ # Set the read buffer
77
+ if framebuffer == 0: # Default framebuffer
78
+ gl.glReadBuffer(read_buffer)
79
+ else: # Custom framebuffer
80
+ gl.glReadBuffer(read_buffer)
81
+
82
+ # Use PBO for faster transfers
83
+ gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, self.pbo)
84
+
85
+ # Read pixels into the PBO (this is asynchronous)
86
+ gl.glReadPixels(0, 0, self.width, self.height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)
87
+
88
+ # Map the PBO and get a pointer to the data
89
+ ptr = gl.glMapBuffer(gl.GL_PIXEL_PACK_BUFFER, gl.GL_READ_ONLY)
90
+
91
+ result = False
92
+ if ptr:
93
+ # Copy data from the mapped buffer
94
+ data = np.ctypeslib.as_array(ctypes.cast(ptr, ctypes.POINTER(ctypes.c_ubyte)),
95
+ shape=(self.height, self.width, 4))
96
+ # We need to copy because the PBO will be unmapped
97
+ frame_copy = np.copy(data)
98
+ # Flip the image vertically (OpenGL has origin at bottom-left)
99
+ frame_copy = np.flipud(frame_copy)
100
+
101
+ # Unmap the buffer
102
+ gl.glUnmapBuffer(gl.GL_PIXEL_PACK_BUFFER)
103
+
104
+ # Store the frame (removing alpha channel)
105
+ self.frames.append(frame_copy[:, :, :3])
106
+ self.frame_count += 1
107
+ result = True
108
+
109
+ # Unbind the PBO
110
+ gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, 0)
111
+
112
+ # Restore the original framebuffer
113
+ gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, current_fbo)
114
+
115
+ return result
116
+
117
+ def save_video(self, output_file="output.mp4"):
118
+ """
119
+ Save the captured frames as a video file using FFmpeg
120
+
121
+ Args:
122
+ output_file (str): Path to the output video file
123
+
124
+ Returns:
125
+ bool: True if video was saved successfully
126
+ """
127
+ if not self.frames:
128
+ print("No frames to save")
129
+ return False
130
+
131
+ print(f"Saving video to {output_file}...")
132
+
133
+ if self.gpu_encoding and self._is_gpu_encoding_available():
134
+ return self._save_video_gpu(output_file)
135
+ else:
136
+ return self._save_video_cpu(output_file)
137
+
138
+ def _save_video_cpu(self, output_file):
139
+ """Save video using CPU-based encoding with FFmpeg"""
140
+ # Create a temporary directory for the output
141
+ with tempfile.TemporaryDirectory() as temp_dir:
142
+ # Set up FFmpeg command
143
+ ffmpeg_cmd = [
144
+ 'ffmpeg',
145
+ '-y', # Overwrite output file if it exists
146
+ '-f', 'rawvideo',
147
+ '-vcodec', 'rawvideo',
148
+ '-s', f'{self.width}x{self.height}',
149
+ '-pix_fmt', 'rgb24',
150
+ '-r', str(self.fps),
151
+ '-i', '-', # Read from stdin
152
+ '-c:v', self.codec,
153
+ '-b:v', self.bitrate,
154
+ '-pix_fmt', 'yuv420p', # Standard pixel format for compatibility
155
+ output_file
156
+ ]
157
+
158
+ # Start FFmpeg process
159
+ process = subprocess.Popen(
160
+ ffmpeg_cmd,
161
+ stdin=subprocess.PIPE,
162
+ stdout=subprocess.PIPE,
163
+ stderr=subprocess.PIPE
164
+ )
165
+
166
+ # Feed frames to FFmpeg
167
+ for frame in self.frames:
168
+ process.stdin.write(frame.tobytes())
169
+
170
+ # Close stdin and wait for FFmpeg to finish
171
+ process.stdin.close()
172
+ process.wait()
173
+
174
+ if process.returncode != 0:
175
+ stderr = process.stderr.read()
176
+ print(f"FFmpeg error: {stderr.decode()}")
177
+ return False
178
+
179
+ print(f"Video saved successfully to {output_file}")
180
+ return True
181
+
182
+ def _save_video_gpu(self, output_file):
183
+ """Save video using GPU-based encoding with FFmpeg (if available)"""
184
+ # Determine which GPU encoder to use
185
+ encoder = self._get_gpu_encoder()
186
+ if not encoder:
187
+ print("No suitable GPU encoder found, falling back to CPU encoding")
188
+ return self._save_video_cpu(output_file)
189
+
190
+ # Set up FFmpeg command with GPU acceleration
191
+ ffmpeg_cmd = [
192
+ 'ffmpeg',
193
+ '-y',
194
+ '-f', 'rawvideo',
195
+ '-vcodec', 'rawvideo',
196
+ '-s', f'{self.width}x{self.height}',
197
+ '-pix_fmt', 'rgb24',
198
+ '-r', str(self.fps),
199
+ '-i', '-',
200
+ '-c:v', encoder,
201
+ '-b:v', self.bitrate,
202
+ '-preset', 'fast',
203
+ output_file
204
+ ]
205
+
206
+ # Additional options for specific encoders
207
+ if 'nvenc' in encoder:
208
+ ffmpeg_cmd.insert(-1, '-gpu', '0')
209
+ elif 'qsv' in encoder:
210
+ ffmpeg_cmd.insert(-1, '-hwaccel', 'qsv')
211
+ elif 'amf' in encoder:
212
+ ffmpeg_cmd.insert(-1, '-hwaccel', 'amf')
213
+
214
+ # Start FFmpeg process
215
+ process = subprocess.Popen(
216
+ ffmpeg_cmd,
217
+ stdin=subprocess.PIPE,
218
+ stdout=subprocess.PIPE,
219
+ stderr=subprocess.PIPE
220
+ )
221
+
222
+ # Feed frames to FFmpeg
223
+ for frame in self.frames:
224
+ process.stdin.write(frame.tobytes())
225
+
226
+ # Close stdin and wait for FFmpeg to finish
227
+ process.stdin.close()
228
+ process.wait()
229
+
230
+ if process.returncode != 0:
231
+ stderr = process.stderr.read()
232
+ print(f"FFmpeg error: {stderr.decode()}")
233
+ return False
234
+
235
+ print(f"Video saved successfully to {output_file} using GPU acceleration")
236
+ return True
237
+
238
+ def _is_gpu_encoding_available(self):
239
+ """Check if GPU encoding is available"""
240
+ try:
241
+ # Run FFmpeg to get available encoders
242
+ result = subprocess.run(
243
+ ['ffmpeg', '-encoders'],
244
+ stdout=subprocess.PIPE,
245
+ stderr=subprocess.PIPE,
246
+ text=True
247
+ )
248
+
249
+ # Check for GPU encoders
250
+ output = result.stdout
251
+ return any(encoder in output for encoder in ['nvenc', 'qsv', 'amf', 'videotoolbox'])
252
+ except Exception as e:
253
+ print(f"Error checking GPU encoders: {e}")
254
+ return False
255
+
256
+ def _get_gpu_encoder(self):
257
+ """Get the appropriate GPU encoder based on the system"""
258
+ try:
259
+ # Run FFmpeg to get available encoders
260
+ result = subprocess.run(
261
+ ['ffmpeg', '-encoders'],
262
+ stdout=subprocess.PIPE,
263
+ stderr=subprocess.PIPE,
264
+ text=True
265
+ )
266
+
267
+ output = result.stdout
268
+
269
+ # Check for NVIDIA encoders (NVENC)
270
+ if 'h264_nvenc' in output:
271
+ return 'h264_nvenc'
272
+ elif 'hevc_nvenc' in output:
273
+ return 'hevc_nvenc'
274
+
275
+ # Check for Intel QuickSync
276
+ if 'h264_qsv' in output:
277
+ return 'h264_qsv'
278
+ elif 'hevc_qsv' in output:
279
+ return 'hevc_qsv'
280
+
281
+ # Check for AMD encoders
282
+ if 'h264_amf' in output:
283
+ return 'h264_amf'
284
+ elif 'hevc_amf' in output:
285
+ return 'hevc_amf'
286
+
287
+ # Check for Apple VideoToolbox (macOS)
288
+ if 'h264_videotoolbox' in output:
289
+ return 'h264_videotoolbox'
290
+
291
+ return None
292
+ except Exception as e:
293
+ print(f"Error determining GPU encoder: {e}")
294
+ return None
295
+
296
+ def clear_frames(self):
297
+ """Clear all captured frames without saving"""
298
+ self.frames = []
299
+ self.frame_count = 0
300
+ print("Frames cleared.")
301
+
302
+ def get_frame_count(self):
303
+ """Get the number of frames captured"""
304
+ return self.frame_count
305
+
306
+
307
+ # Example usage:
308
+ """
309
+ # Initialize the recorder (typically in your setup code)
310
+ recorder = FramebufferRecorder(width=1920, height=1080, fps=30)
311
+
312
+ # Start recording
313
+ recorder.start_recording()
314
+
315
+ # In your render loop:
316
+ def render():
317
+ # Render to your custom framebuffer
318
+ gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, my_fbo)
319
+ # ... render your scene ...
320
+
321
+ # Capture the frame from your custom framebuffer
322
+ if recorder.recording:
323
+ recorder.capture_frame(
324
+ framebuffer=my_fbo,
325
+ read_buffer=gl.GL_COLOR_ATTACHMENT0
326
+ )
327
+
328
+ # Continue with your rendering...
329
+ gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
330
+ # ... render other things ...
331
+
332
+ # When done recording:
333
+ recorder.stop_recording()
334
+
335
+ # Save the video at any point after stopping recording
336
+ recorder.save_video("my_video.mp4")
337
+ """