Skip to content

Commit ca305c9

Browse files
committed
Enable recording UI for macOS AVFoundation
1 parent d7a08d4 commit ca305c9

3 files changed

Lines changed: 66 additions & 11 deletions

File tree

installer/Info.plist

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,11 @@
2020
<string>10.15.0</string>
2121
<key>NSHighResolutionCapable</key>
2222
<true/>
23+
<key>NSCameraUsageDescription</key>
24+
<string>OpenShot uses the camera when you choose to record webcam video.</string>
25+
<key>NSMicrophoneUsageDescription</key>
26+
<string>OpenShot uses the microphone when you choose to record audio.</string>
27+
<key>NSScreenCaptureUsageDescription</key>
28+
<string>OpenShot uses screen recording when you choose to capture your display.</string>
2329
</dict>
2430
</plist>

installer/openshot.entitlements

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,11 @@
1111
<!-- For LLVM. -->
1212
<key>com.apple.security.cs.allow-jit</key>
1313
<true/>
14+
<!-- For recording webcam video. -->
15+
<key>com.apple.security.device.camera</key>
16+
<true/>
17+
<!-- For recording microphone audio. -->
18+
<key>com.apple.security.device.audio-input</key>
19+
<true/>
1420
</dict>
1521
</plist>

src/windows/audio_recording.py

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ def screen_capture_backend():
152152
return openshot.SCREEN_CAPTURE_WAYLAND
153153
if sys.platform.startswith("win") and hasattr(openshot, "SCREEN_CAPTURE_WINDOWS_GDI"):
154154
return openshot.SCREEN_CAPTURE_WINDOWS_GDI
155+
if sys.platform == "darwin" and hasattr(openshot, "SCREEN_CAPTURE_MAC_AVFOUNDATION"):
156+
return openshot.SCREEN_CAPTURE_MAC_AVFOUNDATION
155157
if hasattr(openshot, "SCREEN_CAPTURE_X11"):
156158
return openshot.SCREEN_CAPTURE_X11
157159
return auto_backend
@@ -177,6 +179,8 @@ def screen_capture_backend_supported(backend=None):
177179
return False
178180
if sys.platform.startswith("win"):
179181
return selected_backend == getattr(openshot, "SCREEN_CAPTURE_WINDOWS_GDI", object())
182+
if sys.platform == "darwin":
183+
return selected_backend == getattr(openshot, "SCREEN_CAPTURE_MAC_AVFOUNDATION", object())
180184
return selected_backend == getattr(openshot, "SCREEN_CAPTURE_X11", object())
181185

182186

@@ -190,6 +194,11 @@ def screen_capture_backend_is_windows(backend=None):
190194
return selected_backend == getattr(openshot, "SCREEN_CAPTURE_WINDOWS_GDI", object())
191195

192196

197+
def screen_capture_backend_is_mac(backend=None):
198+
selected_backend = screen_capture_backend() if backend is None else backend
199+
return selected_backend == getattr(openshot, "SCREEN_CAPTURE_MAC_AVFOUNDATION", object())
200+
201+
193202
def camera_capture_backend():
194203
default_backend = getattr(getattr(openshot, "CameraCaptureReader", None), "DefaultBackend", None)
195204
if callable(default_backend):
@@ -199,6 +208,8 @@ def camera_capture_backend():
199208
log.debug("Unable to query default camera capture backend", exc_info=True)
200209
if sys.platform.startswith("win") and hasattr(openshot, "CAMERA_CAPTURE_WINDOWS_DSHOW"):
201210
return openshot.CAMERA_CAPTURE_WINDOWS_DSHOW
211+
if sys.platform == "darwin" and hasattr(openshot, "CAMERA_CAPTURE_MAC_AVFOUNDATION"):
212+
return openshot.CAMERA_CAPTURE_MAC_AVFOUNDATION
202213
if hasattr(openshot, "CAMERA_CAPTURE_V4L2"):
203214
return openshot.CAMERA_CAPTURE_V4L2
204215
return getattr(openshot, "CAMERA_CAPTURE_AUTO", None)
@@ -209,6 +220,11 @@ def camera_capture_backend_is_windows(backend=None):
209220
return selected_backend == getattr(openshot, "CAMERA_CAPTURE_WINDOWS_DSHOW", object())
210221

211222

223+
def camera_capture_backend_is_mac(backend=None):
224+
selected_backend = camera_capture_backend() if backend is None else backend
225+
return selected_backend == getattr(openshot, "CAMERA_CAPTURE_MAC_AVFOUNDATION", object())
226+
227+
212228
class LiveRecordingThumbnailCache:
213229
"""Save coarse thumbnail-grid frames while a live video recording is written."""
214230

@@ -734,8 +750,13 @@ def __init__(self, window, parent=None):
734750
self.screen_status_label.setWordWrap(True)
735751
self.screen_section.body_layout.addWidget(self.screen_status_label)
736752

753+
default_screen_display = os.environ.get("DISPLAY", ":0.0")
754+
if sys.platform.startswith("win"):
755+
default_screen_display = "desktop"
756+
elif sys.platform == "darwin":
757+
default_screen_display = "1:none"
737758
self.screen_display_edit = QLineEdit(
738-
"desktop" if sys.platform.startswith("win") else os.environ.get("DISPLAY", ":0.0"),
759+
default_screen_display,
739760
self.screen_section,
740761
)
741762
self.screen_x_spin = QSpinBox(self.screen_section)
@@ -933,11 +954,14 @@ def _camera_backend_available(self):
933954
) or (
934955
backend == getattr(openshot, "CAMERA_CAPTURE_WINDOWS_DSHOW", object())
935956
and sys.platform.startswith("win")
957+
) or (
958+
backend == getattr(openshot, "CAMERA_CAPTURE_MAC_AVFOUNDATION", object())
959+
and sys.platform == "darwin"
936960
)
937961

938962
def _selected_camera_device(self):
939963
device = self.camera_combo.currentData() if hasattr(self, "camera_combo") else None
940-
if device and (camera_capture_backend_is_windows() or os.path.exists(device)):
964+
if device and (camera_capture_backend_is_windows() or camera_capture_backend_is_mac() or os.path.exists(device)):
941965
return device
942966
return ""
943967

@@ -967,6 +991,7 @@ def _sync_source_availability(self):
967991
def _sync_screen_backend_ui(self):
968992
_ = get_app()._tr
969993
wayland = screen_capture_backend_is_wayland()
994+
mac_screen = screen_capture_backend_is_mac()
970995
self.screen_mode_widget.setVisible(not wayland)
971996
self.screen_display_label.setVisible(not wayland)
972997
self.screen_display_edit.setVisible(not wayland)
@@ -977,12 +1002,16 @@ def _sync_screen_backend_ui(self):
9771002
self.screen_size_label.setVisible(not wayland)
9781003
self.screen_width_spin.setVisible(not wayland)
9791004
self.screen_height_spin.setVisible(not wayland)
980-
self.window_button.setEnabled(not wayland)
1005+
self.window_button.setEnabled(not wayland and not mac_screen)
1006+
self.region_button.setEnabled(not wayland and not mac_screen)
9811007
self.screen_hide_label.setVisible(not wayland)
9821008
self.hide_openshot_combo.setVisible(not wayland)
9831009
if wayland:
9841010
self.screen_status_label.setText(_("Your desktop will ask what to share when recording starts."))
9851011
self._screen_window_id = ""
1012+
elif mac_screen:
1013+
self.screen_status_label.setText(_("macOS screen recording uses the selected AVFoundation display device."))
1014+
self._screen_window_id = ""
9861015
elif screen_capture_backend_is_windows():
9871016
self.screen_status_label.setText(_("Windows screen recording uses full screen or numeric region bounds."))
9881017

@@ -1047,6 +1076,11 @@ def _select_window(self):
10471076
if screen_capture_backend_is_wayland():
10481077
self.screen_status_label.setText(get_app()._tr("Your desktop will ask what to share when recording starts."))
10491078
return
1079+
if screen_capture_backend_is_mac():
1080+
self.full_screen_button.setChecked(True)
1081+
self.window_button.setChecked(False)
1082+
self.screen_status_label.setText(get_app()._tr("Window selection is not available for macOS AVFoundation recording."))
1083+
return
10501084
self.window_button.setChecked(True)
10511085
self.full_screen_button.setChecked(False)
10521086
self.region_button.setChecked(False)
@@ -1068,6 +1102,11 @@ def _select_region(self):
10681102
if screen_capture_backend_is_wayland():
10691103
self.screen_status_label.setText(get_app()._tr("Region selection is not available for Wayland screen recording."))
10701104
return
1105+
if screen_capture_backend_is_mac():
1106+
self.full_screen_button.setChecked(True)
1107+
self.region_button.setChecked(False)
1108+
self.screen_status_label.setText(get_app()._tr("Region selection is not available for macOS AVFoundation recording."))
1109+
return
10711110
self.region_button.setChecked(True)
10721111
self.full_screen_button.setChecked(False)
10731112
self.window_button.setChecked(False)
@@ -1250,7 +1289,7 @@ def refresh_cameras(self):
12501289
self.camera_combo.blockSignals(True)
12511290
self.camera_combo.clear()
12521291
devices = []
1253-
if camera_capture_backend_is_windows():
1292+
if camera_capture_backend_is_windows() or camera_capture_backend_is_mac():
12541293
try:
12551294
get_devices = getattr(openshot.CameraCaptureReader, "GetDeviceNames", None)
12561295
if callable(get_devices):
@@ -1369,7 +1408,7 @@ def _probe_camera_modes(self, device):
13691408
(640, 360): {30},
13701409
}
13711410
self._camera_mode_formats = {}
1372-
if camera_capture_backend_is_windows():
1411+
if camera_capture_backend_is_windows() or camera_capture_backend_is_mac():
13731412
return fallback
13741413
try:
13751414
result = subprocess.run(
@@ -1545,9 +1584,9 @@ def _recording_start_validation_error(self):
15451584
device = self.camera_combo.currentData()
15461585
if not device:
15471586
return _("No webcam device was found.")
1548-
if not camera_capture_backend_is_windows() and not os.path.exists(device):
1587+
if not (camera_capture_backend_is_windows() or camera_capture_backend_is_mac()) and not os.path.exists(device):
15491588
return _("Webcam device was not found: %s") % device
1550-
if not camera_capture_backend_is_windows() and not os.access(device, os.R_OK):
1589+
if not (camera_capture_backend_is_windows() or camera_capture_backend_is_mac()) and not os.access(device, os.R_OK):
15511590
return _("Webcam device is not accessible: %s") % device
15521591
return ""
15531592

@@ -1737,6 +1776,7 @@ def _build_video_jobs(self):
17371776
screen_width = self._safe_even_dimension(self.screen_width_spin.value())
17381777
screen_height = self._safe_even_dimension(self.screen_height_spin.value())
17391778
windows_screen = screen_capture_backend_is_windows(screen_backend)
1779+
mac_screen = screen_capture_backend_is_mac(screen_backend)
17401780
if wayland_screen:
17411781
root_x, root_y, root_width, root_height = 0, 0, None, None
17421782
else:
@@ -1756,9 +1796,12 @@ def _build_video_jobs(self):
17561796
screen_height = self._safe_even_dimension(min(screen_height, root_bottom - screen_y))
17571797
settings = openshot.ScreenCaptureSettings()
17581798
settings.backend = screen_backend
1759-
settings.display = (
1760-
"desktop" if windows_screen else self.screen_display_edit.text().strip() or os.environ.get("DISPLAY", ":0.0")
1761-
)
1799+
if windows_screen:
1800+
settings.display = "desktop"
1801+
elif mac_screen:
1802+
settings.display = self.screen_display_edit.text().strip() or "1:none"
1803+
else:
1804+
settings.display = self.screen_display_edit.text().strip() or os.environ.get("DISPLAY", ":0.0")
17621805
settings.x = screen_x
17631806
settings.y = screen_y
17641807
settings.width = screen_width
@@ -1801,7 +1844,7 @@ def _build_video_jobs(self):
18011844
settings.height = self._safe_even_dimension(camera_size[1])
18021845
settings.fps = camera_fps
18031846
input_format = self._camera_mode_formats.get((settings.width, settings.height, int(camera_fps.num)))
1804-
if camera_capture_backend_is_windows(settings.backend):
1847+
if camera_capture_backend_is_windows(settings.backend) or camera_capture_backend_is_mac(settings.backend):
18051848
settings.options["use_device_defaults"] = "1"
18061849
elif input_format:
18071850
settings.options["input_format"] = input_format

0 commit comments

Comments
 (0)