remote-camera-control-rs is a Rust-first port of the portable Sony camera control core from the original Swift-based rocc project.
The long-term goal is a reusable library for Android, Windows, and Linux frontends, with a small CLI kept as a debug and integration tool rather than the main product surface.
This is currently a Sony-first project. The public library direction is broader, but the real-world validation and protocol coverage today are focused on Sony Alpha cameras.
This is an unofficial community project and is not affiliated with or endorsed by Sony.
cargo fmt --check
cargo testcargo run -p rocc-cli -- discover
cargo run -p rocc-cli -- ptp-status 5- Sony camera discovery over SSDP,
dd.xml, andDigitalImagingDesc.xml - backend routing between:
ScalarWebAPIfor older Sony bodiesDigitalImagingas metadata/capability discoveryPTP/IPfor newer Sony bodies
- generic library reads for Sony parameters:
discover()connect()get_parameter()set_parameter()get_parameter_options()
- high-level live view entrypoint for
ScalarWebAPIcameras:SonyCameraConnection::start_live_view()
- Sony PTP/IP session setup:
- command channel
- event channel
OpenSessionGetDeviceInfo- Sony SDIO extension handshake
getAllDevicePropData (0x9209)
- decoded Sony parameter values such as:
- aperture
- ISO
- shutter speed
- white balance
- exposure mode
- exposure compensation
- focus mode
- focus status
The following cameras have been exercised against the current Rust port:
- Sony
ILCE-7M3(a7 III)- discovered through the classic
ScalarWebAPIpath
- discovered through the classic
- Sony
ILCE-6700(a6700)- discovered through
DigitalImaging - controlled through
PTP/IP - confirmed parameter reads
- confirmed parameter writes for:
- ISO
- aperture
- shutter speed
- exposure compensation
- white balance
- discovered through
remote-camera-control-rs treats Sony support as one library with multiple transport/control backends:
ScalarWebAPI- legacy Sony HTTP/JSON-RPC path
- still important for older bodies like the
a7 III
DigitalImaging- not treated as the primary control API
- used as a capability and routing layer for modern cameras
- especially useful for discovering
PTPsupport
PTP/IP- primary control path for newer cameras like the
a6700 - the main focus for modern Sony support
- primary control path for newer cameras like the
When you query parameter options, remote-camera-control-rs keeps two Sony-specific concepts separate:
available- values the camera reports as currently selectable in the present mode/state
supported- values the camera reports as part of the wider capability set
Some Sony bodies do not report a useful available list for every parameter even when they do report supported. In those cases the CLI shows available: none reported by the camera and the library still returns the supported set.
| Backend | Discovery | Read | Write | Notes |
|---|---|---|---|---|
ScalarWebAPI |
Good | Good for mapped getters | Initial generic setter path | Best for older cameras |
DigitalImaging |
Good | Metadata/capabilities | Not a primary write path | Used for routing and PTP capability detection |
PTP/IP |
Good | Good for mapped parameters | Good for validated parameters | Main modern Sony path |
The following parameters are currently marked as Stable for the PTP write path:
- ISO
- aperture
- shutter speed
- exposure compensation
- white balance
- the public API is intentionally still small
- event-driven live synchronization is not complete yet
- live view is still more diagnostic than productized
- not every Sony-specific property has a friendly name or value mapping yet
ScalarWebAPIcoverage is solid for the basics but still narrower than the modern PTP path
- current scope is intentionally Sony-first
a7 IIIanda6700are the primary real hardware validation targets so far- older Sony bodies are expected to favor
ScalarWebAPI - newer Sony bodies are expected to favor
PTP/IP - multi-vendor support is a future direction, not part of the current release target
crates/rocc-cli- debug/example frontend for discovery, probing, and end-to-end camera testing
crates/rocc-core- shared camera models, values, descriptors, and error types
crates/rocc-discovery- SSDP/UPnP discovery and Sony device-description parsing
crates/rocc-net- small networking primitives used by the higher layers
crates/rocc-ptp- PTP/IP transport, Sony SDIO handshake, property parsing, and status decoding
crates/rocc-sony- Sony-specific routing plus the high-level library API
use std::time::Duration;
use rocc_core::SonyParameterId;
use rocc_sony::{connect, discover};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let timeout = Duration::from_secs(5);
let devices = discover(timeout)?;
let device = devices
.into_iter()
.find(|device| device.connection_mode().is_some())
.expect("no Sony camera found");
let mut camera = connect(&device, timeout)?;
let iso = camera.get_parameter(SonyParameterId::Iso)?;
let options = camera.get_parameter_options(SonyParameterId::Iso)?;
println!("ISO: {:?}", iso);
println!("ISO options: {}", options.supported.len());
Ok(())
}The library exposes a high-level live view entrypoint on the connection itself. It chooses the backend-specific setup path internally:
- older bodies such as the
a7 IIIuse the classicScalarWebAPIstart/stop flow - newer bodies such as the
a6700can resolve the stream URL throughPTP/IPand then reuse the same HTTP live view reader
use std::time::Duration;
use rocc_sony::{connect, discover};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let timeout = Duration::from_secs(5);
let devices = discover(timeout)?;
let device = devices
.into_iter()
.find(|device| device.connection_mode().is_some())
.expect("no Sony camera found");
let connection = connect(&device, timeout)?;
let mut live_view = connection.start_live_view(Some("L"), true)?;
let report = live_view.read_payloads(262_144)?;
println!("stream bytes: {}", report.bytes.len());
println!("payloads: {}", report.payloads.len());
live_view.stop()?;
Ok(())
}For the current modern Sony path, stop() is intentionally treated as a successful no-op on PTP/IP until a camera-specific teardown step proves necessary.
If you do not need manual session handling, there is also a top-level helper on the connection:
use std::time::Duration;
use rocc_sony::{connect, discover};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let timeout = Duration::from_secs(5);
let device = discover(timeout)?
.into_iter()
.find(|device| device.connection_mode().is_some())
.expect("no Sony camera found");
let mut connection = connect(&device, timeout)?;
let report = connection.capture_live_view_frames(Some("L"), false, 262_144)?;
println!("payloads: {}", report.payloads.len());
println!("images: {}", report.image_count());
println!("jpeg bytes: {}", report.total_jpeg_bytes());
println!("jpeg frames: {}", report.jpeg_frames().len());
Ok(())
}For longer reads there are now two ergonomic layers:
- library:
connection.start_live_view(...)for manual session controlconnection.capture_live_view_frames(...)for one bounded read windowconnection.capture_live_view_jpeg_frames(...)when only JPEG payloads matterconnection.capture_live_view_chunks(...)for repeated chunk readsconnection.for_each_live_view_chunk(...)for callback-style chunk handlingconnection.for_each_live_view_jpeg_frame(...)for callback-style JPEG frame handling
- CLI:
liveview-roccfor one bounded windowliveview-streamfor repeated chunk reads over one open session
Here is the intended callback-style library path for continuous JPEG frame handling:
use std::time::Duration;
use rocc_sony::{connect, discover};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let timeout = Duration::from_secs(5);
let device = discover(timeout)?
.into_iter()
.find(|device| device.connection_mode().is_some())
.expect("no Sony camera found");
let mut connection = connect(&device, timeout)?;
let processed = connection.for_each_live_view_jpeg_frame(
Some("L"),
false,
5,
131_072,
Some(8),
|jpeg| {
println!("frame: {} bytes", jpeg.len());
Ok(())
},
)?;
println!("processed {processed} jpeg frame(s)");
Ok(())
}The same example also lives in:
cargo run -p rocc-sony --example liveview_callback
The CLI is intentionally kept in the repo because it is extremely useful for:
- validating new cameras
- comparing
ScalarWebAPIandPTP/IPbehavior - investigating Sony-specific properties
- reproducing real hardware issues quickly
It should be thought of as a companion debug tool and example consumer of the library, not the final public surface.