Rocc
A Swift framework for remote control of digital Cameras
Install / Use
/learn @simonmitchell/RoccREADME
Rocc (Remote Camera Control) is a Swift framework for interacting with Digital Cameras which support function control or Image/Video transfer via a WiFi connection. It currently only supports control/transfer from Sony's line-up of cameras but will be expanding in the future to support as many manufacturers as possible!
The Sony implementation is a tried and tested codebase which is used by the app Camrote to provide the connectivity with the camera.
Rocc is designed to be as generic as possible, both from a coding point of view and also from an API point of view, meaning support for other manufacturers should be a seamless integration with any existing codebase which is using the framework.
Installation
Swift Package Manager
Swift package manager is swift's de-facto distribution mechanism for code distribution.
Once you have your swift project/package setup, add Rocc as a dependency in your Package.swift file:
dependencies: [
.package(url: "https://github.com/simonmitchell/rocc.git", .upToNextMajor(from: "2.0.0"))
]
Carthage
Carthage is a dependency manager which builds frameworks for you or downloads pre-built binaries from a specific tag on GitHub
- If you haven't already, setup Carthage as outlined here.
- Add Rocc as a dependency in your Cartfile:
github "simonmitchell/rocc" == 2.0.0. - Drag the
Rocc.frameworkinto your project'sFrameworks, Libraries and Embedded Contentsection. - Make sure that Rocc is included in your carthage copy files build phase.
Manual
Manual installation is a bit more involved, and not the suggested approach.
- Clone, download or add the repo as a submodule to your repo.
- Drag the Rocc project file into your main app's project.
- Add
Rocc(Or the platform appropriate equivalent) to theFrameworks, Libraries and Embedded Contentsection of your app's target in the General panel of your project. Making sure you set it toEmbed & Sign. - Import
Roccand you're ready to go!
Examples
Discovering Cameras
To discover cameras you will use the class CameraDiscoverer. You must keep a strong reference to this in order to keep it in memory. It will start all the various tasks necessary for device discovery as well as keeping track of WiFi network changes and re-starting the search e.t.c. in these cases.
It will not start and re-start when your application enters the background and foreground however so you may want to implement this yourself!
init () {
cameraDiscoverer = CameraDiscoverer()
cameraDiscoverer.delegate = self
cameraDiscoverer.start()
}
func cameraDiscoverer(_ discoverer: CameraDiscoverer, didError error: Error) {
// Called with errors, these do happen a lot so you will want to check the error code and type here before displaying!
}
func cameraDiscoverer(_ discoverer: CameraDiscoverer, discovered device: Camera) {
// Connect to the device!
connect(to: device)
}
CameraDiscoverer also maintains a dictionary of devices that have been discovered keyed by the SSID they were discovered on for your convenience, and the current SSID can be accessed using the Reachability class:
let cameras = discoverer.camerasBySSID[Reachability.currentWiFiSSID] ?? []
Connecting to a Camera
Once you have discovered to a camera, you will need to connect to it. Not all, but most Sony cameras require an API call to be made to enable remote functionality, but for the sake of genericness this should be called on all Camera objects.
func connect(to camera: Camera) {
camera.connect { (error, isInTransferMode) in
// isInTransferMode reflects whether the camera was already connected
// to and has been re-connected to whilst in "Contents Transfer" mode.
}
}
You should then progress to performing the functionality you wish to with the connected Camera. You should first check the core capabilities of the camera however as Sony supports two (Really 3) connection modes:
switch camera.connectionMode {
case .contentsTransfer(let preselected):
if preselected {
camera.loadFilesToTransfer(callback: { (fileUrls) in
// Download Files Somehow!
camera.finishTransfer(callback: { (_) in
})
})
} else {
// Show UI for transferring files
}
case .remoteControl:
// Show remote control UI
}
Staying Connected!
Rocc provides a simple delegate based class that will alert you when a Camera has become disconnected.
init(camera: Camera) {
connectivityNotifier = DeviceConnectivityNotifier(camera: camera, delegate: self)
}
func connectivityNotifier(_ notifier: DeviceConnectivityNotifier, didDisconnectFrom device: Camera) {
// If it is appropriate to show some kind of UI to let
// the user know the camera has disconnected!
}
func connectivityNotifier(_ notifier: DeviceConnectivityNotifier, didReconnectTo device: Camera) {
// Let the user carry on as they were!
}
Streaming the Live View
Streaming the live view is as simple as using a LiveViewStream class.
init(camera: Camera) {
liveViewStream = LiveViewStream(camera: camera, delegate: self)
liveViewStream.start()
}
func liveViewStream(_ stream: LiveViewStream, didReceive image: UIImage) {
OperationQueue.main.addOperation {
// Show the next image
}
}
func liveViewStream(_ stream: LiveViewStream, didReceive frames: [FrameInfo]) {
OperationQueue.main.addOperation {
// Show frame information (Focus info)
}
}
func liveViewStreamDidStop(_ stream: LiveViewStream) {
// Live view stopped!
}
func liveViewStream(_ stream: LiveViewStream, didError error: Error) {
// Stream errored, you can try and restart it in this method if
// you want, but be careful not to recurse too much!
}
Receiving Camera "Events"
Because your camera settings can still be adjusted manually on the camera whilst shooting, and some settings may affect others (Changing aperture whilst in aperture priority mode may change shutter speed/ISO e.t.c) it is important that the camera can communicate these changes over WiFi. To get changes you should subscribe to them using CameraEventNotifier:
init(camera: Camera) {
eventNotifier = CameraEventNotifier(camera: camera, delegate: self)
eventNotifier.startNotifying()
}
func eventNotifier(_ notifier: CameraEventNotifier, didError error: Error) {
// If it's important to, show the user an Error
}
func eventNotifier(_ notifier: CameraEventNotifier, receivedEvent event: CameraEvent) {
// Handle the event and update UI! CameraEvent includes all exposure
// info as well as changes to shooting mode, camera status, e.t.c.
}
It is important to note that the information provided by CameraEventNotifier will vary by manufacturer, and even by model of camera for the same manufacturer, so you may not always be able to rely on it solely!
IMPORTANT: The CameraEvent object may have nil values for properties that haven't changed with a given event occuring. For example if only the aperture has changed things like cameraStatus could be nil, which doesn't mean the camera is now idle. This depends on whether the camera is API driven (e.g. a7ii) or PTP/IP model (e.g. a9ii). This behaviour will be bought in line across all models in a future release of ROCC.
Performing Camera Functions
Camera functions are written generically, so there are only 4 methods you need to call on Camera rather than an individual set of methods for each piece of functionality on the camera.
Function Support
Before showing the UI for a function, you should make sure it is supported on your camera. To do this you call a method on your Camera object:
camera.supportsFunction(Focus.Mode.set, callback: { (isSupported, error, supportedValues) in
// Disable/enable features using the returned value
})
The type type of supportedValues is defined on the declaration of Focus.Mode by it's associatedtype SendType
Function Availability
Once you have deemed if a function is supported on your camera, you can then check manually for function availability:
camera.isFunctionAvailable(Focus.Mode.set, callback: { (isAvailable, error, availableValues) in
// Update UI to enable/disable control and show available values
})
Important: Function availability is also provided by the eventing mechanism, which is often a friendlier way to check for function availability and should be used for disabling/enabling controls when things like shutter speed setting become temporarily unavailable as the user takes a picture or changes to "Auto" mode on their camera.
You can also attempt to make a
