-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathioctl_api.rs
284 lines (243 loc) · 10.3 KB
/
ioctl_api.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
use nix::fcntl::{open, OFlag};
use nix::sys::stat::Mode;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::io::FromRawFd;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Instant;
use utils::framegen::FrameGenerator;
use v4l2r::memory::{MemoryType, MmapHandle};
use v4l2r::{ioctl::*, memory::UserPtrHandle};
use v4l2r::{Format, QueueType::*};
/// Run a sample encoder on device `device_path`, which must be a `vicodec`
/// encoder instance. `lets_quit` will turn to true when Ctrl+C is pressed.
pub fn run<F: FnMut(&[u8])>(
device_path: &Path,
output_mem: MemoryType,
capture_mem: MemoryType,
lets_quit: Arc<AtomicBool>,
stop_after: Option<usize>,
mut save_output: F,
) {
let mut fd = unsafe {
File::from_raw_fd(
open(device_path, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
.unwrap_or_else(|_| panic!("Cannot open {}", device_path.display())),
)
};
// Check that we are dealing with vicodec.
let caps: Capability = querycap(&fd).expect("Failed to get device capacities");
println!(
"Opened device: {}\n\tdriver: {}\n\tbus: {}\n\tcapabilities: {}",
caps.card, caps.driver, caps.bus_info, caps.capabilities
);
if caps.driver != "vicodec" {
panic!(
"This device is {}, but this test is designed to work with the vicodec driver.",
caps.driver
);
}
// Check whether the driver uses the single or multi-planar API by
// requesting 0 MMAP buffers on the OUTPUT queue. The working queue will
// return a success.
let (output_queue_type, _capture_queue_type, use_multi_planar) =
if reqbufs::<(), _>(&fd, VideoOutput, MemoryType::Mmap, 0).is_ok() {
(VideoOutput, VideoCapture, false)
} else if reqbufs::<(), _>(&fd, VideoOutputMplane, MemoryType::Mmap, 0).is_ok() {
(VideoOutputMplane, VideoCaptureMplane, true)
} else {
panic!("Both single-planar and multi-planar queues are unusable.");
};
println!(
"Multi-planar: {}",
if use_multi_planar { "yes" } else { "no" }
);
let (output_queue, capture_queue) = match use_multi_planar {
false => (VideoOutput, VideoCapture),
true => (VideoOutputMplane, VideoCaptureMplane),
};
// List the output formats.
let out_formats = FormatIterator::new(&fd, output_queue)
.map(|f| (f.pixelformat, f))
.collect::<BTreeMap<_, _>>();
println!("Output formats:");
for (_, fmtdesc) in out_formats.iter() {
println!("\t{}", fmtdesc);
}
// List the capture formats.
let cap_formats = FormatIterator::new(&fd, capture_queue)
.map(|f| (f.pixelformat, f))
.collect::<BTreeMap<_, _>>();
println!("Capture formats:");
for (_, fmtdesc) in cap_formats.iter() {
println!("\t{}", fmtdesc);
}
// We will encode from RGB3 to FWHT.
if !out_formats.contains_key(&b"RGB3".into()) {
panic!("RGB3 format not supported on OUTPUT queue.");
}
if !cap_formats.contains_key(&b"FWHT".into()) {
panic!("FWHT format not supported on CAPTURE queue.");
}
let mut capture_format: Format =
g_fmt(&fd, capture_queue).expect("Failed getting capture format");
// Let's just make sure the encoding format on the CAPTURE queue is FWHT.
capture_format.pixelformat = b"FWHT".into();
println!("Setting capture format: {:?}", capture_format);
let _capture_format: Format =
s_fmt(&mut fd, capture_queue, capture_format).expect("Failed setting capture format");
// We will be happy with 640x480 resolution.
let output_format = Format {
width: 640,
height: 480,
pixelformat: b"RGB3".into(),
..Default::default()
};
println!("Setting output format: {:?}", output_format);
let output_format: Format =
s_fmt(&mut fd, output_queue, output_format).expect("Failed setting output format");
let capture_format: Format = g_fmt(&fd, capture_queue).expect("Failed getting capture format");
println!("Adjusted output format: {:?}", output_format);
println!("Adjusted capture format: {:?}", capture_format);
match output_mem {
MemoryType::Mmap => (),
MemoryType::UserPtr => (),
m => panic!("Unsupported output memory type {:?}", m),
}
match capture_mem {
MemoryType::Mmap => (),
m => panic!("Unsupported capture memory type {:?}", m),
}
// We could run this with as little as one buffer, but let's cycle between
// two for the sake of it.
// For simplicity the OUTPUT buffers will use user memory.
let num_output_buffers: usize =
reqbufs(&fd, output_queue, output_mem, 2).expect("Failed to allocate output buffers");
let num_capture_buffers: usize =
reqbufs(&fd, capture_queue, capture_mem, 2).expect("Failed to allocate capture buffers");
println!(
"Using {} output and {} capture buffers.",
num_output_buffers, num_capture_buffers
);
let mut capture_mappings = Vec::new();
for i in 0..num_capture_buffers {
let query_buf: QueryBuffer =
querybuf(&fd, capture_queue, i).expect("Failed to query buffer");
println!(
"Capture buffer {} at offset 0x{:0x}, length 0x{:0x}",
i, query_buf.planes[0].mem_offset, query_buf.planes[0].length
);
capture_mappings.push(
mmap(
&fd,
query_buf.planes[0].mem_offset,
query_buf.planes[0].length,
)
.expect("Failed to map buffer"),
);
}
let output_image_size = output_format.plane_fmt[0].sizeimage as usize;
let mut output_buffers: Vec<UserPtrHandle<Vec<u8>>> = match output_mem {
MemoryType::Mmap => Default::default(),
MemoryType::UserPtr => std::iter::repeat(vec![0u8; output_image_size])
.take(num_output_buffers)
.map(UserPtrHandle::from)
.collect(),
_ => unreachable!(),
};
// Start streaming.
streamon(&fd, output_queue).expect("Failed to start output queue");
streamon(&fd, capture_queue).expect("Failed to start capture queue");
let mut frame_gen = FrameGenerator::new(
output_format.width as usize,
output_format.height as usize,
output_format.plane_fmt[0].bytesperline as usize,
)
.expect("Failed to create frame generator");
let mut cpt = 0usize;
let mut total_size = 0usize;
let start_time = Instant::now();
// Encode generated frames until Ctrl+c is pressed.
while !lets_quit.load(Ordering::SeqCst) {
if let Some(max_cpt) = stop_after {
if cpt >= max_cpt {
break;
}
}
let output_buffer_index = cpt % num_output_buffers;
let capture_buffer_index = cpt % num_output_buffers;
// Generate the frame data and buffer to queue.
match output_mem {
MemoryType::Mmap => {
let buffer_info: QueryBuffer =
querybuf(&fd, output_queue_type, output_buffer_index)
.expect("Failed to query output buffer");
let plane = &buffer_info.planes[0];
let mut mapping =
mmap(&fd, plane.mem_offset, plane.length).expect("Failed to map output buffer");
frame_gen
.next_frame(&mut mapping)
.expect("Failed to generate frame");
let out_qbuf = QBuffer::<MmapHandle> {
planes: vec![QBufPlane::new(frame_gen.frame_size())],
..Default::default()
};
qbuf(&fd, output_queue, output_buffer_index, out_qbuf)
}
MemoryType::UserPtr => {
let output_buffer = &mut output_buffers[output_buffer_index];
frame_gen
.next_frame(&mut output_buffer.0)
.expect("Failed to generate frame");
let out_qbuf = QBuffer::<UserPtrHandle<Vec<u8>>> {
planes: vec![QBufPlane::new_from_handle(
output_buffer,
output_buffer.0.len(),
)],
..Default::default()
};
qbuf(&fd, output_queue, output_buffer_index, out_qbuf)
}
_ => unreachable!(),
}
.expect("Error queueing output buffer");
let cap_qbuf = QBuffer::<MmapHandle> {
planes: vec![QBufPlane::new(0)],
..Default::default()
};
qbuf(&fd, capture_queue, capture_buffer_index, cap_qbuf)
.expect("Error queueing capture buffer");
// Now dequeue the work that we just scheduled.
// We can disregard the OUTPUT buffer since it does not contain any
// useful data for us.
dqbuf::<(), _>(&fd, output_queue).expect("Failed to dequeue output buffer");
// The CAPTURE buffer, on the other hand, we want to examine more closely.
let cap_dqbuf: DqBuffer =
dqbuf(&fd, capture_queue).expect("Failed to dequeue capture buffer");
let bytes_used = cap_dqbuf.get_first_plane().bytesused() as usize;
total_size = total_size.wrapping_add(bytes_used);
let elapsed = start_time.elapsed();
let fps = cpt as f64 / elapsed.as_millis() as f64 * 1000.0;
print!(
"\rEncoded buffer {:#5}, index: {:#2}), bytes used:{:#6} total encoded size:{:#8} fps: {:#5.2}",
cap_dqbuf.sequence(), cap_dqbuf.index(), bytes_used, total_size, fps
);
io::stdout().flush().unwrap();
save_output(&capture_mappings[cap_dqbuf.index() as usize].as_ref()[0..bytes_used]);
cpt = cpt.wrapping_add(1);
}
// Stop streaming.
streamoff(&fd, capture_queue).expect("Failed to stop capture queue");
streamoff(&fd, output_queue).expect("Failed to stop output queue");
// Clear the mappings
drop(capture_mappings);
// Free the buffers.
reqbufs::<(), _>(&fd, capture_queue, MemoryType::Mmap, 0)
.expect("Failed to release capture buffers");
reqbufs::<(), _>(&fd, output_queue, MemoryType::UserPtr, 0)
.expect("Failed to release output buffers");
// The fd will be closed as the File instance gets out of scope.
}