Skip to content

Commit

Permalink
Prepare for initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrosland committed Mar 26, 2018
1 parent ca33426 commit abb3c31
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 273 deletions.
Empty file removed .watchmanconfig
Empty file.
19 changes: 10 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
[package]
name = "cam"
version = "0.1.0"
authors = ["Pedro Sland <pedrosland@gmail.com>"]
name = "rascam"
version = "0.0.1"
authors = ["Peter Sutherland <pedrosland+rascam@gmail.com>"]
description = "Library for taking photos and videos with the Raspberry Pi camera"
repository = "https://github.com/pedrosland/rascam"
readme = "README.md"
license = "MIT"
categories = [ "hardware-support" ]
keywords = [ "camera", "raspberry", "pi", "rpi" ]

[dependencies]
mmal-sys = { path = "mmal-sys" }
mmal-sys = "0.1.0-1"
libc = "0.2"
futures = "0.1"
parking_lot = {version = "0.5", features = ["nightly"]}
scopeguard = "0.3.3"

[[bin]]
name = "test"
path = "src/main.rs"

[features]
default = []

Expand Down
21 changes: 21 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017 Peter Sutherland

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
136 changes: 25 additions & 111 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,127 +1,41 @@
http://www.jvcref.com/files/PI/documentation/html/index.html
http://vojtech.kral.hk/en/rust-ffi-wrapping-c-api-in-rust-struct/
http://siciarz.net/ffi-rust-writing-bindings-libcpuid/
# Rascam

TODO:
* decide on API
* see what we can do about using Rust's memory allocator instead of `vcos`? malloc
* match up capture method and complete status from callback function. this is currently very unsafe!
* try to reduce unsafe rust to minimum
* anything else unsafe?
Rust library for interacting with the Raspberry Pi Camera.

# API ideas
This provides a friendly, high level API over the [mmal-sys](https://crates.io/crates/mmal-sys) library.

## New SimpleCamera
There are three main components in this library:

Do we want to force users to use futures? Should we provide a callback-style API? Should `capture_still()` just block?
* Info - Describe the attached camera.
* SimpleCamera - Aims to provide a simple, easy to use API.
* SeriousCamera - This API is very unstable and will likely change! Aims to expose the power of the `mmal-sys`'s camera while providing a safe Rust API.

Note that these C types should actually be wrappers, not raw types or they are likely to cause memory management fun.
## Documentation and examples

There should be settings for brightness, exposure, burst mode, image format etc etc.
Please see the [documentation](https://docs.rs/crate/mmal/0.0.0) and [examples](https://github.com/pedrosland/mmal/tree/master/examples)

Should we provide a preview API?
## Usage

What API should be used for getting camera info? Note that this lists both cameras and "flashes".
Add the following to your Cargo.toml, changing `0.0.1` for the latest release:

### `SimpleCamera::new() -> SimpleCamera`

### `set_camera_num(u8) -> Result<MMAL_PARAMETER_CAMERA_INFO_CAMERA_T, MMAL_STATUS_T>`

### `set_camera_info(MMAL_PARAMETER_CAMERA_INFO_CAMERA_T) -> Result<(), MMAL_STATUS_T>`

This is a companion for the above. Useful if the user already has a `MMAL_PARAMETER_CAMERA_INFO_CAMERA_T`. Does it provide enough value?

### `activate() -> Result<(), MMAL_STATUS_T>`

Start the camera. Useful for metering etc.

### `capture_still() -> Future<[u8], MMAL_STATUS_T>`

Take a still picture.

What about burst mode? `capture_burst() -> Stream<[u8], MMAL_STATUS_T>`?

### `record_video() -> Stream<[u8], MMAL_STATUS_T>`

Record a video and return a stream of frames.

### `stop_video()`

Should this return a `Result<(), ?>` so that we can error if not already recording a video?

## Old SimpleCamera

Is there much need for `Result` when we have `MMAL_SUCCESS`? Not really but `Result` is Rust-like and `MMAL_SUCCESS` is C-like.

How should we represent errors? Is just `MMAL_STATUS_T` ok?
Is this informative? Should we have a `CameraError` type with a code property? Is this better? Is it enough?

What happens when there are multiple libmmal calls inside a method? Is it clear which the error comes from or what it means?

### `SimpleCamera::new() -> Result<SimpleCamera, MMAL_STATUS_T>`

Should this actually create camera objects? (it does now)
Should this take the camera number?
Should this take a `MMAL_PARAMETER_CAMERA_INFO_CAMERA_T`?

### `set_camera_num(u8) -> Result<(), MMAL_STATUS_T>`

If constructor doesn't take a camera number or camera info, we
should get one here.

Users or SimpleCamera shouldn't care about any of the following APIs except `take()`.

### `create_encoder() -> Result<(), MMAL_STATUS_T>`

### `enable_control_port() -> Result<(), MMAL_STATUS_T>`

### `set_camera_params(MMAL_PARAMETER_CAMERA_INFO_CAMERA_T) -> Result<(), MMAL_STATUS_T>`

Users shouldn't have to pass this in.

### `set_camera_format(MMAL_PARAMETER_CAMERA_INFO_CAMERA_T) -> Result<(), MMAL_STATUS_T>`

Users shouldn't have to pass this in and certainly not twice.

### `enable() -> Result<(), MMAL_STATUS_T>`

### `create_pool() -> Result<(), MMAL_STATUS_T>`

### `create_preview() -> Result<(), MMAL_STATUS_T>`

### `enable_preview() -> Result<(), MMAL_STATUS_T>`

### `connect_ports() -> Result<(), MMAL_STATUS_T>`

### `enable_still_port() -> Result<(), MMAL_STATUS_T>`

### `take() -> Result<(), MMAL_STATUS_T>`

Rename to capture?

## SeriousCamera (or just Camera?)

## CameraInfo

`CameraInfo::info() -> Result<CameraInfo, MMAL_STATUS_T>`
```toml
[dependencies]
rascam = "0.0.1"
```

# Debugging
Import this crate into your lib.rs or main.rs file:

```
$ convert --version
Version: ImageMagick 6.9.9-27 Q16 x86_64 2017-12-23 http://www.imagemagick.org
Copyright: © 1999-2018 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC Modules OpenMP
Delegates (built-in): bzlib cairo djvu fftw fontconfig freetype gslib jbig jng jp2 jpeg lcms ltdl lzma openexr pangocairo png ps rsvg tiff webp wmf x xml zlib
```rust
extern crate rascam;
```

To test rgb output:
```
convert -size 96x96 -depth 8 -colorspace RGB rgb:test.rgb out.png
If things are crashing or producing unexpected results there is a feature flag which enables some print statements which may help to debug an issue:

```toml
[dependencies]
libc = { version = "0.0.1", features = ["debug"] }
```

Note that `mmal_port_parameter_get` and `mmal_port_parameter_set` use memcpy into our struct so `params` should be owned by rust.
https://github.com/raspberrypi/userland/blob/a1b89e91f393c7134b4cdc36431f863bb3333163/interface/mmal/vc/mmal_vc_api.c#L1222
## License

TODO: why does `cargo test` require a `build.rs` in the top level package when `cargo build` does not?
Released under the MIT license.
117 changes: 117 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
TODO:
* see what we can do about using Rust's memory allocator instead of `vcos`? malloc
* try to reduce unsafe rust to minimum
* anything else unsafe?

# API ideas

## New SimpleCamera

Note that these C types should actually be wrappers, not raw types or they are likely to cause memory management fun.

There should be settings for brightness, exposure, burst mode, image format etc etc.

Should we provide a preview API?

What API should be used for getting camera info? Note that this lists both cameras and "flashes".

### `SimpleCamera::new() -> SimpleCamera`

### `set_camera_num(u8) -> Result<MMAL_PARAMETER_CAMERA_INFO_CAMERA_T, MMAL_STATUS_T>`

### `set_camera_info(MMAL_PARAMETER_CAMERA_INFO_CAMERA_T) -> Result<(), MMAL_STATUS_T>`

This is a companion for the above. Useful if the user already has a `MMAL_PARAMETER_CAMERA_INFO_CAMERA_T`. Does it provide enough value?

### `activate() -> Result<(), MMAL_STATUS_T>`

Start the camera. Useful for metering etc.

### `capture_still() -> Future<[u8], MMAL_STATUS_T>`

Take a still picture.

What about burst mode? `capture_burst() -> Stream<[u8], MMAL_STATUS_T>`?

### `record_video() -> Stream<[u8], MMAL_STATUS_T>`

Record a video and return a stream of frames.

### `stop_video()`

Should this return a `Result<(), ?>` so that we can error if not already recording a video?

## Old SimpleCamera

Is there much need for `Result` when we have `MMAL_SUCCESS`? Not really but `Result` is Rust-like and `MMAL_SUCCESS` is C-like.

How should we represent errors? Is just `MMAL_STATUS_T` ok?
Is this informative? Should we have a `CameraError` type with a code property? Is this better? Is it enough?

What happens when there are multiple libmmal calls inside a method? Is it clear which the error comes from or what it means?

### `SimpleCamera::new() -> Result<SimpleCamera, MMAL_STATUS_T>`

Should this actually create camera objects? (it does now)
Should this take the camera number?
Should this take a `MMAL_PARAMETER_CAMERA_INFO_CAMERA_T`?

### `set_camera_num(u8) -> Result<(), MMAL_STATUS_T>`

If constructor doesn't take a camera number or camera info, we
should get one here.

Users or SimpleCamera shouldn't care about any of the following APIs except `take()`.

### `create_encoder() -> Result<(), MMAL_STATUS_T>`

### `enable_control_port() -> Result<(), MMAL_STATUS_T>`

### `set_camera_params(MMAL_PARAMETER_CAMERA_INFO_CAMERA_T) -> Result<(), MMAL_STATUS_T>`

Users shouldn't have to pass this in.

### `set_camera_format(MMAL_PARAMETER_CAMERA_INFO_CAMERA_T) -> Result<(), MMAL_STATUS_T>`

Users shouldn't have to pass this in and certainly not twice.

### `enable() -> Result<(), MMAL_STATUS_T>`

### `create_pool() -> Result<(), MMAL_STATUS_T>`

### `create_preview() -> Result<(), MMAL_STATUS_T>`

### `enable_preview() -> Result<(), MMAL_STATUS_T>`

### `connect_ports() -> Result<(), MMAL_STATUS_T>`

### `enable_still_port() -> Result<(), MMAL_STATUS_T>`

### `take() -> Result<(), MMAL_STATUS_T>`

Rename to capture?

## SeriousCamera (or just Camera?)

## CameraInfo

`CameraInfo::info() -> Result<CameraInfo, MMAL_STATUS_T>`

# Debugging

```
convert --version
Version: ImageMagick 6.9.7-4 Q16 arm 20170114 http://www.imagemagick.org
Copyright: © 1999-2017 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC Modules OpenMP
Delegates (built-in): bzlib djvu fftw fontconfig freetype jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png tiff wmf x xml zlib
```

To test rgb output:
```
convert -size 96x96 -depth 8 -colorspace RGB rgb:test.rgb out.png
```

Note that `mmal_port_parameter_get` and `mmal_port_parameter_set` use memcpy into our struct so `params` should be owned by rust.
https://github.com/raspberrypi/userland/blob/a1b89e91f393c7134b4cdc36431f863bb3333163/interface/mmal/vc/mmal_vc_api.c#L1222
73 changes: 73 additions & 0 deletions examples/serious.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
extern crate rascam;

use std::fs::File;
use std::io::Write;
use std::{thread, time};
use rascam::*;

fn main() {
let info = info().unwrap();
if info.cameras.len() < 1 {
println!("Found 0 cameras. Exiting");
// note that this doesn't run destructors
::std::process::exit(1);
}
println!("{}", info);

serious(&info.cameras[0]);
}

fn serious(info: &CameraInfo) {
let mut camera = SeriousCamera::new().unwrap();
println!("camera created");
camera.set_camera_num(0).unwrap();
println!("camera number set");
camera.create_encoder().unwrap();
println!("encoder created");
camera.enable_control_port(true).unwrap();
println!("camera control port enabled");
camera.set_camera_params(info).unwrap();
println!("camera params set");

let settings = CameraSettings {
encoding: MMAL_ENCODING_RGB24,
width: 96, // 96px will not require padding
height: 96,
zero_copy: true,
use_encoder: false,
};

camera.set_camera_format(&settings).unwrap();
println!("set camera format");
camera.enable().unwrap();
println!("camera enabled");
camera.create_pool().unwrap();
println!("pool created");

camera.create_preview().unwrap();
println!("preview created");
camera.connect_preview().unwrap();
println!("preview connected");
camera.enable_preview().unwrap();
println!("preview enabled");

println!("taking photo");

let sleep_duration = time::Duration::from_millis(2000);
thread::sleep(sleep_duration);

let receiver = camera.take().unwrap();

let buffer = receiver.recv().unwrap().unwrap();

File::create("image.rgb")
.unwrap()
.write_all(&buffer.get_bytes())
.unwrap();

println!("Raw rgb bytes written to image.rgb");
println!("Try: convert -size 96x96 -depth 8 -colorspace RGB rgb:image.rgb image.png");
// If imagemagick gives something like:
// convert-im6.q16: unexpected end-of-file `image.rgb': No such file or directory @ error/rgb.c/ReadRGBImage/239.
// There is probably padding in the image. Check the width.
}
Loading

0 comments on commit abb3c31

Please sign in to comment.