Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unity Video Capture backend #56

Merged
merged 19 commits into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/scripts/install-windows-unity-capture.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
$ErrorActionPreference = 'Stop'

. $PSScriptRoot\download-file.ps1

$VERSION = "fe461e8f6e1cd1e6a0dfa9891147c8e393a20a2c"

$FILENAME = "UnityCaptureFilter64bit.dll"
$URL = "https://github.com/schellingb/UnityCapture/raw/$VERSION/Install/$FILENAME"

$path = DownloadFile $URL $FILENAME

$args = "/i:UnityCaptureDevices=2 /s $path"
Write-Host "regsvr32" $args
Start-Process -FilePath "regsvr32" -ArgumentList $args -Wait -Passthru -Verb "runAs"
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ jobs:
if: matrix.config.os-name == 'windows'
run: |
.github/scripts/install-windows-obs-virtual-cam.ps1
.github/scripts/install-windows-unity-capture.ps1
.github/scripts/build-windows.ps1
shell: pwsh
env:
Expand Down
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ with pyvirtualcam.Camera(width=1280, height=720, fps=20) as cam:
cam.sleep_until_next_frame()
```

For more examples, including using different pixel formats like BGR, or selecting a specific camera device on Linux, check out the [`samples/`](samples) folder.
pyvirtualcam uses the first available virtual camera it finds (see later section).

For more examples, including using different pixel formats like BGR, or selecting a specific camera device, check out the [`samples/`](samples) folder.

See also the [API Documentation](https://letmaik.github.io/pyvirtualcam).

Expand All @@ -39,7 +41,13 @@ pyvirtualcam relies on existing virtual cameras which have to be installed first

To use the OBS virtual camera, simply [install OBS](https://obsproject.com/).

Note that OBS provides a single camera instance only, so it is *not* possible to send frames from Python, capture the camera in OBS, mix it with other content, and output it again as virtual camera.
Note that OBS provides a single camera instance only, so it is *not* possible to send frames from Python to the built-in OBS virtual camera, capture the camera in OBS, mix it with other content, and output it again to OBS' built-in virtual camera. To achieve such a workflow, use another virtual camera from Python (like Unity Capture) so that OBS' built-in virtual camera is free for use in OBS.

### Windows: Unity Capture

[Unity Capture](https://github.com/schellingb/UnityCapture) provides a virtual camera originally meant for streaming Unity games. Compared to most other virtual cameras it supports RGBA frames (frames with transparency) which in turn can be captured in [OBS](https://obsproject.com/) for further processing.

To use the Unity Capture virtual camera, follow the [installation instructions](https://github.com/schellingb/UnityCapture#installation) on the project site.

### macOS: OBS

Expand All @@ -66,9 +74,6 @@ sudo modprobe v4l2loopback devices=1

For further information, see the [v4l2loopback documentation](https://github.com/umlaeute/v4l2loopback).

If the `device` keyword argument is not given, then pyvirtualcam uses the first available v4l2loopback virtual camera it finds.
The camera device name can be accessed with `cam.device`.

## Build from source

### Linux/macOS
Expand Down
9 changes: 8 additions & 1 deletion pyvirtualcam/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ def register_backend(name: str, clazz):
BACKENDS[name] = clazz

if platform.system() == 'Windows':
from pyvirtualcam import _native_windows_obs
from pyvirtualcam import _native_windows_obs, _native_windows_unity_capture
register_backend('obs', _native_windows_obs.Camera)
register_backend('unitycapture', _native_windows_unity_capture.Camera)
elif platform.system() == 'Darwin':
from pyvirtualcam import _native_macos_obs
register_backend('obs', _native_macos_obs.Camera)
Expand All @@ -128,6 +129,9 @@ class PixelFormat(Enum):
BGR = '24BG'
""" Shape: ``(h,w,3)`` """

RGBA = 'ABGR'
""" Shape: ``(h,w,4)`` """

GRAY = 'J400'
""" Shape: ``(h,w)`` """

Expand All @@ -152,6 +156,7 @@ def __repr__(self):
FrameShapes = {
PixelFormat.RGB: lambda w, h: (h, w, 3),
PixelFormat.BGR: lambda w, h: (h, w, 3),
PixelFormat.RGBA: lambda w, h: (h, w, 4),
PixelFormat.GRAY: lambda w, h: (h, w),
PixelFormat.I420: lambda w, h: w * h * 3 // 2,
PixelFormat.NV12: lambda w, h: w * h * 3 // 2,
Expand All @@ -172,13 +177,15 @@ class Camera:

- ``v4l2loopback`` (Linux): ``/dev/video<n>``
- ``obs`` (macOS/Windows): ``OBS Virtual Camera``
- ``unitycapture`` (Windows): ``Unity Video Capture``, or the name you gave to the device
:param backend: The virtual camera backend to use.
If ``None``, all available backends are tried.

Built-in backends:

- ``v4l2loopback`` (Linux)
- ``obs`` (macOS/Windows)
- ``unitycapture`` (Windows)
:param print_fps: Print frame rate every second.
:param kw: Extra keyword arguments forwarded to the backend.
Should only be given if a backend is specified.
Expand Down
16 changes: 8 additions & 8 deletions pyvirtualcam/native_macos_obs/virtual_output.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ class VirtualOutput {
case libyuv::FOURCC_RAW:
case libyuv::FOURCC_24BG:
case libyuv::FOURCC_J400:
// RGB|BGR|GRAY -> ARGB -> UYVY
_buffer_tmp.resize(argb_frame_size(width, height));
// RGB|BGR|GRAY -> BGRA -> UYVY
_buffer_tmp.resize(bgra_frame_size(width, height));
_buffer_output.resize(out_frame_size);
break;
case libyuv::FOURCC_I420:
Expand Down Expand Up @@ -136,18 +136,18 @@ class VirtualOutput {
switch (_frame_fourcc) {
case libyuv::FOURCC_RAW:
out_frame = _buffer_output.data();
rgb_to_argb(frame, tmp, _frame_width, _frame_height);
argb_to_uyvy(tmp, out_frame, _frame_width, _frame_height);
rgb_to_bgra(frame, tmp, _frame_width, _frame_height);
bgra_to_uyvy(tmp, out_frame, _frame_width, _frame_height);
break;
case libyuv::FOURCC_24BG:
out_frame = _buffer_output.data();
bgr_to_argb(frame, tmp, _frame_width, _frame_height);
argb_to_uyvy(tmp, out_frame, _frame_width, _frame_height);
bgr_to_bgra(frame, tmp, _frame_width, _frame_height);
bgra_to_uyvy(tmp, out_frame, _frame_width, _frame_height);
break;
case libyuv::FOURCC_J400:
out_frame = _buffer_output.data();
gray_to_argb(frame, tmp, _frame_width, _frame_height);
argb_to_uyvy(tmp, out_frame, _frame_width, _frame_height);
gray_to_bgra(frame, tmp, _frame_width, _frame_height);
bgra_to_uyvy(tmp, out_frame, _frame_width, _frame_height);
break;
case libyuv::FOURCC_I420:
out_frame = _buffer_output.data();
Expand Down
Loading