use crate::timer::Timer;
use crate::vchiq::{self, ServiceId, Vchiq};
const MSG_MAX_SIZE: usize = 512;
const HEADER_SIZE: usize = 24;
const MAGIC: u32 = fourcc(*b"mmal");
const SERVICE_VERSION: u16 = 15;
const SERVICE_VERSION_MIN: u16 = 10;
const MSG_COMPONENT_CREATE: u32 = 4;
const MSG_COMPONENT_DESTROY: u32 = 5;
const MSG_COMPONENT_ENABLE: u32 = 6;
const MSG_COMPONENT_DISABLE: u32 = 7;
const MSG_PORT_INFO_GET: u32 = 8;
const MSG_PORT_INFO_SET: u32 = 9;
const MSG_PORT_ACTION: u32 = 10;
const MSG_BUFFER_FROM_HOST: u32 = 11;
const MSG_BUFFER_TO_HOST: u32 = 12;
const MSG_PORT_PARAMETER_SET: u32 = 14;
const MSG_PORT_PARAMETER_GET: u32 = 15;
const MSG_EVENT_TO_HOST: u32 = 16;
const NAME_MAX: usize = 128;
const EXTRADATA_MAX: usize = 128;
const SHORT_DATA_MAX: usize = 128;
const PARAMETER_STRING_MAX: usize = 128;
const EVENT_DATA_MAX: usize = 256;
pub const MAX_BUFFERS: usize = 16;
pub const PORT_TYPE_CONTROL: u32 = 1;
pub const PORT_TYPE_INPUT: u32 = 2;
pub const PORT_TYPE_OUTPUT: u32 = 3;
pub const PORT_TYPE_CLOCK: u32 = 4;
pub const ES_TYPE_CONTROL: u32 = 1;
pub const ES_TYPE_AUDIO: u32 = 2;
pub const ES_TYPE_VIDEO: u32 = 3;
pub const ES_TYPE_SUBPICTURE: u32 = 4;
pub const ENCODING_H264: u32 = fourcc(*b"H264");
pub const ENCODING_I420: u32 = fourcc(*b"I420");
pub const ENCODING_NV12: u32 = fourcc(*b"NV12");
pub const ENCODING_PCM_SIGNED_LE: u32 = fourcc(*b"pcms");
pub const BUFFER_FLAG_EOS: u32 = 1 << 0;
pub const BUFFER_FLAG_FRAME_START: u32 = 1 << 1;
pub const BUFFER_FLAG_FRAME_END: u32 = 1 << 2;
pub const BUFFER_FLAG_FRAME: u32 = BUFFER_FLAG_FRAME_START | BUFFER_FLAG_FRAME_END;
pub const BUFFER_FLAG_KEYFRAME: u32 = 1 << 3;
pub const BUFFER_FLAG_DISCONTINUITY: u32 = 1 << 4;
pub const BUFFER_FLAG_CONFIG: u32 = 1 << 5;
pub const BUFFER_FLAG_CORRUPTED: u32 = 1 << 9;
pub const BUFFER_FLAG_TRANSMISSION_FAILED: u32 = 1 << 10;
pub const TIME_UNKNOWN: i64 = i64::MIN;
pub const EVENT_FORMAT_CHANGED: u32 = fourcc(*b"EFCH");
pub const EVENT_EOS: u32 = fourcc(*b"EEOS");
pub const EVENT_ERROR: u32 = fourcc(*b"ERRO");
pub const EVENT_PARAMETER_CHANGED: u32 = fourcc(*b"EPCH");
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum PortAction {
Enable = 1,
Disable = 2,
Flush = 3,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
Vchiq(vchiq::Error),
Status(u32),
UnexpectedReply,
Timeout,
TooManyBuffers,
UnknownBuffer,
TooLarge,
}
impl From<vchiq::Error> for Error {
fn from(error: vchiq::Error) -> Self {
Error::Vchiq(error)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Component {
pub handle: u32,
pub client_handle: u32,
pub inputs: u32,
pub outputs: u32,
pub clocks: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct VideoFormat {
pub width: u32,
pub height: u32,
pub crop_x: i32,
pub crop_y: i32,
pub crop_width: i32,
pub crop_height: i32,
pub frame_rate_num: i32,
pub frame_rate_den: i32,
pub par_num: i32,
pub par_den: i32,
pub color_space: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct AudioFormat {
pub channels: u32,
pub sample_rate: u32,
pub bits_per_sample: u32,
pub block_align: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct PortInfo {
pub component: u32,
pub handle: u32,
pub port_type: u32,
pub index: u32,
pub enabled: bool,
pub buffer_num_min: u32,
pub buffer_size_min: u32,
pub buffer_alignment_min: u32,
pub buffer_num_recommended: u32,
pub buffer_size_recommended: u32,
pub buffer_num: u32,
pub buffer_size: u32,
pub es_type: u32,
pub encoding: u32,
pub encoding_variant: u32,
pub bitrate: u32,
pub flags: u32,
pub video: VideoFormat,
pub audio: AudioFormat,
}
#[derive(Clone, Copy, Debug)]
pub struct FormatChanged {
pub buffer_size_min: u32,
pub buffer_num_min: u32,
pub buffer_size_recommended: u32,
pub buffer_num_recommended: u32,
pub es_type: u32,
pub encoding: u32,
pub video: VideoFormat,
}
#[derive(Debug)]
pub enum Event {
Buffer {
port: u32,
buffer: &'static mut [u8],
length: usize,
flags: u32,
pts: i64,
},
PortEvent {
port_type: u32,
port_index: u32,
cmd: u32,
length: usize,
},
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Stats {
pub buffers_sent: u32,
pub buffers_returned: u32,
pub buffers_failed: u32,
pub last_status: u32,
pub last_returned_flags: u32,
pub last_returned_length: u32,
pub bulk_receives: u32,
pub inline_receives: u32,
pub events: u32,
pub last_event: u32,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum BufferState {
WithFirmware,
Receiving,
Returned,
}
#[derive(Clone, Copy)]
struct BufferSlot {
address: u32,
size: usize,
port: u32,
state: BufferState,
length: usize,
flags: u32,
pts: i64,
}
pub struct Mmal {
vchiq: Vchiq,
service: ServiceId,
tx: [u8; MSG_MAX_SIZE],
rx: [u8; MSG_MAX_SIZE],
rx_len: usize,
event: [u8; MSG_MAX_SIZE],
pending_context: u32,
reply_ready: bool,
pending_event: bool,
next_context: u32,
buffers: [Option<BufferSlot>; MAX_BUFFERS],
returned: [usize; MAX_BUFFERS],
returned_len: usize,
stats: Stats,
}
impl Mmal {
pub fn new(mut vchiq: Vchiq, timer: &Timer) -> Result<Self, Error> {
let service = vchiq.open_service(*b"mmal", SERVICE_VERSION, SERVICE_VERSION_MIN, timer)?;
Ok(Self {
vchiq,
service,
tx: [0; MSG_MAX_SIZE],
rx: [0; MSG_MAX_SIZE],
rx_len: 0,
event: [0; MSG_MAX_SIZE],
pending_context: 0,
reply_ready: false,
pending_event: false,
next_context: 1,
buffers: [None; MAX_BUFFERS],
returned: [0; MAX_BUFFERS],
returned_len: 0,
stats: Stats::default(),
})
}
pub fn component_create(&mut self, name: &str, timer: &Timer) -> Result<Component, Error> {
if name.len() >= NAME_MAX {
return Err(Error::TooLarge);
}
let client_handle = 0;
self.begin(MSG_COMPONENT_CREATE);
self.put_u32(HEADER_SIZE, client_handle);
self.tx[HEADER_SIZE + 4..HEADER_SIZE + 4 + NAME_MAX].fill(0);
self.tx[HEADER_SIZE + 4..HEADER_SIZE + 4 + name.len()].copy_from_slice(name.as_bytes());
self.put_u32(HEADER_SIZE + 4 + NAME_MAX, 0);
let reply = self.request(HEADER_SIZE + 8 + NAME_MAX, MSG_COMPONENT_CREATE, timer)?;
status(get_u32(reply, HEADER_SIZE))?;
Ok(Component {
handle: get_u32(reply, HEADER_SIZE + 4),
client_handle,
inputs: get_u32(reply, HEADER_SIZE + 8),
outputs: get_u32(reply, HEADER_SIZE + 12),
clocks: get_u32(reply, HEADER_SIZE + 16),
})
}
pub fn component_destroy(&mut self, component: &Component, timer: &Timer) -> Result<(), Error> {
self.component_request(MSG_COMPONENT_DESTROY, component, timer)
}
pub fn component_enable(&mut self, component: &Component, timer: &Timer) -> Result<(), Error> {
self.component_request(MSG_COMPONENT_ENABLE, component, timer)
}
pub fn component_disable(&mut self, component: &Component, timer: &Timer) -> Result<(), Error> {
self.component_request(MSG_COMPONENT_DISABLE, component, timer)
}
pub fn port_info_get(
&mut self,
component: &Component,
port_type: u32,
index: u32,
timer: &Timer,
) -> Result<PortInfo, Error> {
self.port_info_get_by_index(component.handle, port_type, index, timer)
}
fn port_info_get_by_index(
&mut self,
component: u32,
port_type: u32,
index: u32,
timer: &Timer,
) -> Result<PortInfo, Error> {
self.begin(MSG_PORT_INFO_GET);
self.put_u32(HEADER_SIZE, component);
self.put_u32(HEADER_SIZE + 4, port_type);
self.put_u32(HEADER_SIZE + 8, index);
let reply = self.request(HEADER_SIZE + 12, MSG_PORT_INFO_GET, timer)?;
status(get_u32(reply, HEADER_SIZE))?;
let port = HEADER_SIZE + 24;
let format = port + PORT_FIELDS_SIZE;
let es = format + FORMAT_FIELDS_SIZE;
Ok(PortInfo {
component,
handle: get_u32(reply, HEADER_SIZE + 20),
port_type: get_u32(reply, HEADER_SIZE + 8),
index: get_u32(reply, HEADER_SIZE + 12),
enabled: get_u32(reply, port + PORT_IS_ENABLED) != 0,
buffer_num_min: get_u32(reply, port + PORT_BUFFER_NUM_MIN),
buffer_size_min: get_u32(reply, port + PORT_BUFFER_SIZE_MIN),
buffer_alignment_min: get_u32(reply, port + PORT_BUFFER_ALIGNMENT_MIN),
buffer_num_recommended: get_u32(reply, port + PORT_BUFFER_NUM_RECOMMENDED),
buffer_size_recommended: get_u32(reply, port + PORT_BUFFER_SIZE_RECOMMENDED),
buffer_num: get_u32(reply, port + PORT_BUFFER_NUM),
buffer_size: get_u32(reply, port + PORT_BUFFER_SIZE),
es_type: get_u32(reply, format),
encoding: get_u32(reply, format + 4),
encoding_variant: get_u32(reply, format + 8),
bitrate: get_u32(reply, format + 16),
flags: get_u32(reply, format + 20),
video: read_video_format(reply, es),
audio: read_audio_format(reply, es),
})
}
pub fn port_info_set(&mut self, port: &mut PortInfo, timer: &Timer) -> Result<(), Error> {
self.begin(MSG_PORT_INFO_SET);
self.put_u32(HEADER_SIZE, port.component);
self.put_u32(HEADER_SIZE + 4, port.port_type);
self.put_u32(HEADER_SIZE + 8, port.index);
let port_offset = HEADER_SIZE + 12;
let format = port_offset + PORT_FIELDS_SIZE;
let es = format + FORMAT_FIELDS_SIZE;
self.tx[port_offset..es + ES_FIELDS_SIZE + EXTRADATA_MAX].fill(0);
self.write_port_fields(port_offset, port);
self.put_u32(format, port.es_type);
self.put_u32(format + 4, port.encoding);
self.put_u32(format + 8, port.encoding_variant);
self.put_u32(format + 16, port.bitrate);
self.put_u32(format + 20, port.flags);
match port.es_type {
ES_TYPE_AUDIO => self.write_audio_format(es, &port.audio),
_ => self.write_video_format(es, &port.video),
}
let reply = self.request(
es + ES_FIELDS_SIZE + EXTRADATA_MAX,
MSG_PORT_INFO_SET,
timer,
)?;
status(get_u32(reply, HEADER_SIZE))?;
let component = port.component;
let port_type = port.port_type;
let index = port.index;
*port = self.port_info_get_by_index(component, port_type, index, timer)?;
Ok(())
}
pub fn port_action(
&mut self,
port: &PortInfo,
action: PortAction,
timer: &Timer,
) -> Result<(), Error> {
self.begin(MSG_PORT_ACTION);
self.put_u32(HEADER_SIZE, port.component);
self.put_u32(HEADER_SIZE + 4, port.handle);
self.put_u32(HEADER_SIZE + 8, action as u32);
let port_offset = HEADER_SIZE + 12;
self.tx[port_offset..port_offset + PORT_FIELDS_SIZE].fill(0);
self.write_port_fields(port_offset, port);
let reply = self.request(port_offset + PORT_FIELDS_SIZE, MSG_PORT_ACTION, timer)?;
status(get_u32(reply, HEADER_SIZE))
}
pub fn parameter_set(
&mut self,
port: &PortInfo,
id: u32,
value: &[u8],
timer: &Timer,
) -> Result<(), Error> {
if HEADER_SIZE + 16 + value.len() > MSG_MAX_SIZE {
return Err(Error::TooLarge);
}
self.begin(MSG_PORT_PARAMETER_SET);
self.put_u32(HEADER_SIZE, port.component);
self.put_u32(HEADER_SIZE + 4, port.handle);
self.put_u32(HEADER_SIZE + 8, id);
self.put_u32(HEADER_SIZE + 12, 8 + value.len() as u32);
self.tx[HEADER_SIZE + 16..HEADER_SIZE + 16 + value.len()].copy_from_slice(value);
let reply = self.request(
HEADER_SIZE + 16 + value.len(),
MSG_PORT_PARAMETER_SET,
timer,
)?;
status(get_u32(reply, HEADER_SIZE))
}
pub fn parameter_set_string(
&mut self,
port: &PortInfo,
id: u32,
value: &str,
timer: &Timer,
) -> Result<(), Error> {
let mut bytes = [0u8; PARAMETER_STRING_MAX];
let Some(terminated) = bytes.get_mut(..value.len() + 1) else {
return Err(Error::TooLarge);
};
terminated[..value.len()].copy_from_slice(value.as_bytes());
let length = terminated.len();
self.parameter_set(port, id, &bytes[..length], timer)
}
pub fn parameter_get(
&mut self,
port: &PortInfo,
id: u32,
value: &mut [u8],
timer: &Timer,
) -> Result<usize, Error> {
self.begin(MSG_PORT_PARAMETER_GET);
self.put_u32(HEADER_SIZE, port.component);
self.put_u32(HEADER_SIZE + 4, port.handle);
self.put_u32(HEADER_SIZE + 8, id);
self.put_u32(HEADER_SIZE + 12, 8 + value.len() as u32);
let reply = self.request(HEADER_SIZE + 16, MSG_PORT_PARAMETER_GET, timer)?;
status(get_u32(reply, HEADER_SIZE))?;
let size = (get_u32(reply, HEADER_SIZE + 8) as usize).saturating_sub(8);
let copied = size.min(value.len());
value[..copied].copy_from_slice(&reply[HEADER_SIZE + 12..HEADER_SIZE + 12 + copied]);
Ok(size)
}
pub fn send_buffer(
&mut self,
port: &PortInfo,
buffer: &'static mut [u8],
length: usize,
flags: u32,
pts: i64,
timer: &Timer,
) -> Result<(), Error> {
if length > buffer.len() {
return Err(Error::TooLarge);
}
let index = self
.buffers
.iter()
.position(|slot| slot.is_none())
.ok_or(Error::TooManyBuffers)?;
let address = buffer.as_ptr() as u32;
let size = buffer.len();
self.buffers[index] = Some(BufferSlot {
address,
size,
port: port.handle,
state: BufferState::WithFirmware,
length: 0,
flags: 0,
pts: TIME_UNKNOWN,
});
let context = buffer_context(index);
self.begin(MSG_BUFFER_FROM_HOST);
self.put_u32(12, context);
let payload = HEADER_SIZE;
self.tx[payload..payload + BUFFER_FROM_HOST_SIZE].fill(0);
self.put_u32(payload, MAGIC);
self.put_u32(payload + 4, port.component);
self.put_u32(payload + 8, port.handle);
self.put_u32(payload + 12, context);
let header = payload + 32;
self.put_u32(header + 12, address);
self.put_u32(header + 16, size as u32);
self.put_u32(header + 20, length as u32);
self.put_u32(header + 24, 0);
self.put_u32(header + 28, flags);
self.put_i64(header + 32, pts);
self.put_i64(header + 40, pts);
self.send(HEADER_SIZE + BUFFER_FROM_HOST_SIZE, timer)?;
self.stats.buffers_sent += 1;
if length > 0 {
unsafe {
self.vchiq
.bulk_transmit(self.service, address, (length + 3) & !3, context)?;
}
}
Ok(())
}
pub fn poll(&mut self) -> Result<Option<Event>, Error> {
if let Some(event) = self.take_returned() {
return Ok(Some(event));
}
self.pump()?;
Ok(self.take_returned())
}
pub fn event_data(&self) -> &[u8] {
let length = (get_u32(&self.event, HEADER_SIZE + 16) as usize).min(EVENT_DATA_MAX);
&self.event[HEADER_SIZE + 20..HEADER_SIZE + 20 + length]
}
pub fn vchiq(&mut self) -> &mut Vchiq {
&mut self.vchiq
}
pub fn stats(&self) -> Stats {
self.stats
}
fn request(&mut self, len: usize, expected_type: u32, timer: &Timer) -> Result<&[u8], Error> {
self.next_context = self.next_context.wrapping_add(1) % BUFFER_CONTEXT_BASE;
self.pending_context = self.next_context.max(1);
self.put_u32(12, self.pending_context);
self.reply_ready = false;
self.send(len, timer)?;
let deadline = timer.now_micros() + REQUEST_TIMEOUT_US;
while !self.reply_ready {
self.pump()?;
if timer.now_micros() > deadline {
self.pending_context = 0;
return Err(Error::Timeout);
}
}
self.reply_ready = false;
self.pending_context = 0;
if get_u32(&self.rx, 4) != expected_type {
return Err(Error::UnexpectedReply);
}
Ok(&self.rx[..self.rx_len])
}
fn send(&mut self, len: usize, timer: &Timer) -> Result<(), Error> {
let deadline = timer.now_micros() + REQUEST_TIMEOUT_US;
loop {
match self.vchiq.send(self.service, &self.tx[..len]) {
Ok(()) => return Ok(()),
Err(vchiq::Error::OutOfSlots) => {}
Err(error) => return Err(error.into()),
}
self.pump()?;
if timer.now_micros() > deadline {
return Err(Error::Timeout);
}
}
}
fn pump(&mut self) -> Result<(), Error> {
let Some(event) = self.vchiq.poll(&mut self.rx)? else {
return Ok(());
};
match event {
vchiq::Event::Message { len, .. } => {
self.rx_len = len.min(MSG_MAX_SIZE);
self.on_message()?;
}
vchiq::Event::BulkReceiveDone { context, actual } => {
if let Some(index) = buffer_index(context) {
if let Some(slot) = &mut self.buffers[index] {
slot.length = slot.length.min(actual);
slot.state = BufferState::Returned;
}
self.push_returned(index);
}
}
vchiq::Event::BulkTransmitDone { .. } => {}
vchiq::Event::Connected
| vchiq::Event::ServiceOpened(_)
| vchiq::Event::ServiceClosed(_) => {}
}
Ok(())
}
fn on_message(&mut self) -> Result<(), Error> {
match get_u32(&self.rx, 4) {
MSG_BUFFER_TO_HOST => self.on_buffer_to_host(),
MSG_BUFFER_FROM_HOST => Ok(()),
MSG_EVENT_TO_HOST => {
self.event.copy_from_slice(&self.rx);
self.pending_event = true;
self.stats.events += 1;
self.stats.last_event = get_u32(&self.rx, HEADER_SIZE + 12);
Ok(())
}
_ => {
if self.pending_context != 0 && get_u32(&self.rx, 12) == self.pending_context {
self.reply_ready = true;
}
Ok(())
}
}
}
fn on_buffer_to_host(&mut self) -> Result<(), Error> {
let payload = HEADER_SIZE;
if get_u32(&self.rx, payload) != MAGIC {
return Err(Error::UnknownBuffer);
}
let context = get_u32(&self.rx, payload + 12);
let index = buffer_index(context).ok_or(Error::UnknownBuffer)?;
let Some(slot) = self.buffers[index] else {
return Err(Error::UnknownBuffer);
};
let header = payload + 32;
let length = (get_u32(&self.rx, header + 20) as usize).min(slot.size);
let flags = get_u32(&self.rx, header + 28);
let pts = get_i64(&self.rx, header + 32);
let payload_in_message = get_u32(&self.rx, payload + 136) as usize;
let message_status = get_u32(&self.rx, 16);
if let Some(slot) = &mut self.buffers[index] {
slot.length = length;
slot.flags = flags;
slot.pts = pts;
}
self.stats.buffers_returned += 1;
self.stats.last_returned_flags = flags;
self.stats.last_returned_length = length as u32;
if message_status != 0 {
self.stats.buffers_failed += 1;
self.stats.last_status = message_status;
self.finish_buffer(index, 0);
return Ok(());
}
if payload_in_message > 0 {
self.stats.inline_receives += 1;
let copied = payload_in_message.min(slot.size);
let destination =
unsafe { core::slice::from_raw_parts_mut(slot.address as *mut u8, copied) };
destination.copy_from_slice(&self.rx[payload + 140..payload + 140 + copied]);
self.finish_buffer(index, copied);
return Ok(());
}
let transfer = if length == 0 {
if flags & BUFFER_FLAG_EOS == 0 {
self.finish_buffer(index, 0);
return Ok(());
}
8
} else {
(length + 3) & !3
};
if let Some(slot) = &mut self.buffers[index] {
slot.state = BufferState::Receiving;
}
self.stats.bulk_receives += 1;
unsafe {
self.vchiq
.bulk_receive(self.service, slot.address, transfer, context)?;
}
Ok(())
}
fn finish_buffer(&mut self, index: usize, length: usize) {
if let Some(slot) = &mut self.buffers[index] {
slot.length = length;
slot.state = BufferState::Returned;
}
self.push_returned(index);
}
fn take_returned(&mut self) -> Option<Event> {
if self.returned_len > 0 {
let index = self.returned[0];
self.returned.rotate_left(1);
self.returned_len -= 1;
let slot = self.buffers[index].take()?;
let buffer =
unsafe { core::slice::from_raw_parts_mut(slot.address as *mut u8, slot.size) };
return Some(Event::Buffer {
port: slot.port,
buffer,
length: slot.length,
flags: slot.flags,
pts: slot.pts,
});
}
if self.pending_event {
self.pending_event = false;
return Some(Event::PortEvent {
port_type: get_u32(&self.event, HEADER_SIZE + 4),
port_index: get_u32(&self.event, HEADER_SIZE + 8),
cmd: get_u32(&self.event, HEADER_SIZE + 12),
length: (get_u32(&self.event, HEADER_SIZE + 16) as usize).min(EVENT_DATA_MAX),
});
}
None
}
fn push_returned(&mut self, index: usize) {
if self.returned_len < MAX_BUFFERS {
self.returned[self.returned_len] = index;
self.returned_len += 1;
}
}
fn begin(&mut self, message_type: u32) {
self.tx[..HEADER_SIZE].fill(0);
self.put_u32(0, MAGIC);
self.put_u32(4, message_type);
}
fn write_port_fields(&mut self, offset: usize, port: &PortInfo) {
self.put_u32(offset + PORT_TYPE, port.port_type);
self.tx[offset + PORT_INDEX..offset + PORT_INDEX + 2]
.copy_from_slice(&(port.index as u16).to_le_bytes());
self.put_u32(offset + PORT_IS_ENABLED, u32::from(port.enabled));
self.put_u32(offset + PORT_BUFFER_NUM_MIN, port.buffer_num_min);
self.put_u32(offset + PORT_BUFFER_SIZE_MIN, port.buffer_size_min);
self.put_u32(
offset + PORT_BUFFER_ALIGNMENT_MIN,
port.buffer_alignment_min,
);
self.put_u32(
offset + PORT_BUFFER_NUM_RECOMMENDED,
port.buffer_num_recommended,
);
self.put_u32(
offset + PORT_BUFFER_SIZE_RECOMMENDED,
port.buffer_size_recommended,
);
self.put_u32(offset + PORT_BUFFER_NUM, port.buffer_num);
self.put_u32(offset + PORT_BUFFER_SIZE, port.buffer_size);
}
fn write_video_format(&mut self, offset: usize, video: &VideoFormat) {
self.put_u32(offset, video.width);
self.put_u32(offset + 4, video.height);
self.put_u32(offset + 8, video.crop_x as u32);
self.put_u32(offset + 12, video.crop_y as u32);
self.put_u32(offset + 16, video.crop_width as u32);
self.put_u32(offset + 20, video.crop_height as u32);
self.put_u32(offset + 24, video.frame_rate_num as u32);
self.put_u32(offset + 28, video.frame_rate_den as u32);
self.put_u32(offset + 32, video.par_num as u32);
self.put_u32(offset + 36, video.par_den as u32);
self.put_u32(offset + 40, video.color_space);
}
fn write_audio_format(&mut self, offset: usize, audio: &AudioFormat) {
self.put_u32(offset, audio.channels);
self.put_u32(offset + 4, audio.sample_rate);
self.put_u32(offset + 8, audio.bits_per_sample);
self.put_u32(offset + 12, audio.block_align);
}
fn component_request(
&mut self,
message_type: u32,
component: &Component,
timer: &Timer,
) -> Result<(), Error> {
self.begin(message_type);
self.put_u32(HEADER_SIZE, component.handle);
let reply = self.request(HEADER_SIZE + 4, message_type, timer)?;
status(get_u32(reply, HEADER_SIZE))
}
fn put_u32(&mut self, offset: usize, value: u32) {
self.tx[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
}
fn put_i64(&mut self, offset: usize, value: i64) {
self.tx[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
}
}
pub(crate) const POOL_CAPACITY: usize = 6;
pub(crate) struct Pool {
buffers: [Option<&'static mut [u8]>; POOL_CAPACITY],
total: usize,
}
impl Pool {
pub(crate) const fn new() -> Self {
Self {
buffers: [const { None }; POOL_CAPACITY],
total: 0,
}
}
pub(crate) fn add(&mut self, buffer: &'static mut [u8]) -> bool {
let Some(slot) = self.buffers.iter_mut().find(|slot| slot.is_none()) else {
return false;
};
*slot = Some(buffer);
self.total += 1;
true
}
pub(crate) fn put(&mut self, buffer: &'static mut [u8]) {
if let Some(slot) = self.buffers.iter_mut().find(|slot| slot.is_none()) {
*slot = Some(buffer);
}
}
pub(crate) fn take(&mut self) -> Option<&'static mut [u8]> {
self.buffers.iter_mut().find_map(|slot| slot.take())
}
pub(crate) fn total(&self) -> usize {
self.total
}
pub(crate) fn smallest(&self) -> usize {
self.buffers
.iter()
.flatten()
.map(|buffer| buffer.len())
.min()
.unwrap_or(0)
}
}
const REQUEST_TIMEOUT_US: u64 = 2_000_000;
const BUFFER_CONTEXT_BASE: u32 = 0x1000_0000;
const PORT_FIELDS_SIZE: usize = 64;
#[allow(dead_code)]
const PORT_PRIV: usize = 0;
#[allow(dead_code)]
const PORT_NAME: usize = 4;
const PORT_TYPE: usize = 8;
const PORT_INDEX: usize = 12;
const PORT_IS_ENABLED: usize = 16;
#[allow(dead_code)]
const PORT_FORMAT: usize = 20;
const PORT_BUFFER_NUM_MIN: usize = 24;
const PORT_BUFFER_SIZE_MIN: usize = 28;
const PORT_BUFFER_ALIGNMENT_MIN: usize = 32;
const PORT_BUFFER_NUM_RECOMMENDED: usize = 36;
const PORT_BUFFER_SIZE_RECOMMENDED: usize = 40;
const PORT_BUFFER_NUM: usize = 44;
const PORT_BUFFER_SIZE: usize = 48;
#[allow(dead_code)]
const PORT_COMPONENT: usize = 52;
#[allow(dead_code)]
const PORT_USERDATA: usize = 56;
const PORT_CAPABILITIES: usize = 60;
const _: () = assert!(PORT_CAPABILITIES + 4 == PORT_FIELDS_SIZE);
const FORMAT_FIELDS_SIZE: usize = 32;
const ES_FIELDS_SIZE: usize = 44;
const BUFFER_FROM_HOST_SIZE: usize = (32 + 56 + 40 + 12 + SHORT_DATA_MAX).next_multiple_of(8);
pub fn parse_format_changed(data: &[u8]) -> Option<FormatChanged> {
let format = 20;
let es = format + FORMAT_FIELDS_SIZE;
if data.len() < es + ES_FIELDS_SIZE {
return None;
}
Some(FormatChanged {
buffer_size_min: get_u32(data, 0),
buffer_num_min: get_u32(data, 4),
buffer_size_recommended: get_u32(data, 8),
buffer_num_recommended: get_u32(data, 12),
es_type: get_u32(data, format),
encoding: get_u32(data, format + 4),
video: read_video_format(data, es),
})
}
fn buffer_context(index: usize) -> u32 {
BUFFER_CONTEXT_BASE + index as u32
}
fn buffer_index(context: u32) -> Option<usize> {
let index = context.checked_sub(BUFFER_CONTEXT_BASE)? as usize;
(index < MAX_BUFFERS).then_some(index)
}
fn status(value: u32) -> Result<(), Error> {
if value == 0 {
Ok(())
} else {
Err(Error::Status(value))
}
}
fn read_video_format(data: &[u8], offset: usize) -> VideoFormat {
VideoFormat {
width: get_u32(data, offset),
height: get_u32(data, offset + 4),
crop_x: get_u32(data, offset + 8) as i32,
crop_y: get_u32(data, offset + 12) as i32,
crop_width: get_u32(data, offset + 16) as i32,
crop_height: get_u32(data, offset + 20) as i32,
frame_rate_num: get_u32(data, offset + 24) as i32,
frame_rate_den: get_u32(data, offset + 28) as i32,
par_num: get_u32(data, offset + 32) as i32,
par_den: get_u32(data, offset + 36) as i32,
color_space: get_u32(data, offset + 40),
}
}
fn read_audio_format(data: &[u8], offset: usize) -> AudioFormat {
AudioFormat {
channels: get_u32(data, offset),
sample_rate: get_u32(data, offset + 4),
bits_per_sample: get_u32(data, offset + 8),
block_align: get_u32(data, offset + 12),
}
}
fn get_u32(data: &[u8], offset: usize) -> u32 {
match data.get(offset..offset + 4) {
Some(bytes) => u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
None => 0,
}
}
fn get_i64(data: &[u8], offset: usize) -> i64 {
(u64::from(get_u32(data, offset)) | (u64::from(get_u32(data, offset + 4)) << 32)) as i64
}
const fn fourcc(code: [u8; 4]) -> u32 {
u32::from_le_bytes(code)
}