This project is named after the Imago Camera created by Werner Kraus, which was created to capture images at the life size of a subject.
I created Imago to replace multiple apps that allowed me to use my Canon M50 as a Virtual Camera for apps like Zoom. I was using the wonderful Camera Live by Tom Butterworth (@bangnoise) and CamTwist, both of which have worked extraordinarily well. I encourage you to take a look at both to decide which is a better solution for you.
The DAL plugin is based off of SimpleDALPlugin by Ryohei Ikegami (@seanchas116), who migrated CoreMediaIO Device Abstraction Layer Minimal Example by John Boiles (@johnboiles) into Swift.
The UI is written in SwiftUI for macOS 10.15, so it is missing some of the nicities that were added for macOS 11.
- macOS 10.15
- Compatible Canon Camera
Caveat: This is my first Swift app, so if there are better ways to do something please let me know.
The project is split into two Targets, Imago is the macOS app and ImagoPlugin is the Virtual Camera plugin (aka Device Abstraction Layer).
Beyond the base implementation of the CMIO Plugin, ImagoPlugin is only responsible for communicating with Imago and reconstituing the raw data back into a CVPixelBuffer
. This is accomplished by setting up dedicated inter-process communications using CFMessagePort.
The Imago app is responsible for all communications and data management between the Camera (via the EDSDK.framework), image decompression, and coordinates all the inter-process communications to active instances of ImagoPlugin.
Communications between Imago and the EDSDK can be summarized as:
- Initialize EDSDK
- Get the available list of Cameras
- Connect to a selected Camera
- Get required properties about the Camera
- Set the Camera Output mode
- Get Image Data
All EDSDK calls are in the CanonCamera
subclass of Camera
where they're coordinated using a dedicated DispatchQueue.
For a primer on inter-process communications, check out this NSHipster post by @Mattt.
Imago uses CFMessagePort, which in turn uses Mach Ports under the hood. These provide fast, dedicated communication channels between processes that allow Imago to push image data directly into ImagoPlugin. The main limitation of them is they only provide only 1:1 communications. Given that there can be multiple instances of an ImagoPlugin running if a user has multiple client programs running (for example Zoom and Google Meet in Chrome), Imago implements a Publisher/Subscriber architecture to allow for dedicated 1:1 communication between multiple processes. There are other ways of acheving the same result, either by using lower level Mach Ports or higher level XPC, and passing an IOSurface object between processess; however both those ways are more complicated then using CFMessagePort.
- Start Publisher at known port address (ie.
com.nolanbrown.Imago.conductor.publisher
)- If a Publisher isn't available, the Subscriber will periodically attempt to connect until it's able to establish a connection
- When a Subscriber is ready to connect to the Publisher, it will create a UUID and open a local port address using that ID (ie
com.nolanbrown.Imago.conductor.e02d59bd-16fa-41a0-bf70-70adfc02e877
) - When Publisher receives a
Register
message from a subscriber process , it will open a remote port the Subcriber at that ID - Publisher sends data to all confirmed Subscribers
- A Subscriber will periodically
Register
with the Publisher to confirm that both connections
To get started building Imago, first the required libraries must be downloaded. To simplify this process, you only need to run setup.sh
that's in the root directory of this project. The script will install and setup libturbo-jpeg
and the Canon EOS SDK
, once that's completed you can build the project.
ImagoPlugin is set as a dependecy for Imago, so the plugin will be automatically built every time Imago is.
To use the ImagoPlugin, it needs to be installed in /Library/CoreMediaIO/Plug-Ins/DAL/
. It can be tedious to manually copy the plugin so there is a Run Script build phase that will automatically move the newly built ImagoPlugin.plugin
to that directory.
The Run Script requires a sudoer password to be provided which is done via a query to the Keychain. To set this up for yourself, create a new entry in Keychain Access with both Item Name and Account Name set as ImagoBuild
and enter a password for a Sudo user, or create a new Keychain Item from the command line with the command security add-generic-password -a ImagoBuild -s ImagoBuild -w
and enter a password for Sudo user.
Below is the executed Run Script to automatically install the built plugin.
if [ $CONFIGURATION == "Debug" ];then
echo $(security find-generic-password -ga "ImagoBuild" -w) | sudo -S rm -R /Library/CoreMediaIO/Plug-Ins/DAL/$PRODUCT_NAME.plugin;
sudo cp -R $SYMROOT/$CONFIGURATION/$PRODUCT_NAME.plugin /Library/CoreMediaIO/Plug-Ins/DAL/$PRODUCT_NAME.plugin
fi
To download the EDSDK.framework, register for a developer account with Canon. For US developers, you can do that here and then download the SDK.
Once downloaded, unzip the file and open Macintosh.dmg
. From the mounted disk image Macintosh
copy EDSDK/Framework/EDSDK.framework
to <PROJECT_ROOT>/Frameworks/EDSDK/EDSDK.framework
and the Header
directory to <PROJECT_ROOT>/Frameworks/EDSDK/Header/
You can download the latest version of the libjpeg-turbo binary here or visit their website.
Use Cameo to load and test the ImagoPlugin or use any application that allow for Virtual Cameras.
The error "The file “ic_hevcdec.framework” couldn’t be opened because there is no such file."
is part of EDSDK and can be ignored.
- Connect your camera via USB directly to your computer, not through a USB hub or Dock
- Try a different USB cable
- Turn off any other apps that are communicating with the Camera (EOS Utility, Camera Live, etc)
- Turn off your camera and turn it back on again
- Improve Plugin re-connection
- Test with more Canon Cameras
- Implement an iOS App to act as remote camera
- Add more filter options and ability to configure
- Add a Live Preview mode
- Add preferences pane
- Add support for other DSLR manufacturers
- Calculate and Display frame rate
- Fix UI bugs on macOS 11