blob: cc3ea796d6641c020cdbcc356301f1a548b9d2db [file] [log] [blame]
use std::convert::{Infallible, TryFrom};
use std::mem;
use std::os::unix::io::AsRawFd;
use nix::errno::Errno;
use thiserror::Error;
use crate::bindings;
use crate::bindings::v4l2_enc_idx;
use crate::bindings::v4l2_encoder_cmd;
#[doc(hidden)]
mod ioctl {
use crate::bindings::v4l2_enc_idx;
use crate::bindings::v4l2_encoder_cmd;
nix::ioctl_read!(vidioc_g_enc_index, b'V', 76, v4l2_enc_idx);
nix::ioctl_readwrite!(vidioc_encoder_cmd, b'V', 77, v4l2_encoder_cmd);
nix::ioctl_readwrite!(vidioc_try_encoder_cmd, b'V', 78, v4l2_encoder_cmd);
}
#[derive(Debug, Error)]
pub enum GEncIndexError {
#[error("ioctl error: {0}")]
IoctlError(Errno),
}
impl From<GEncIndexError> for Errno {
fn from(err: GEncIndexError) -> Self {
match err {
GEncIndexError::IoctlError(e) => e,
}
}
}
/// Safe wrapper around the `VIDIOC_G_ENC_INDEX` ioctl.
pub fn g_enc_index<O: From<v4l2_enc_idx>>(fd: &impl AsRawFd) -> Result<O, GEncIndexError> {
let mut enc_idx: v4l2_enc_idx = unsafe { std::mem::zeroed() };
match unsafe { ioctl::vidioc_g_enc_index(fd.as_raw_fd(), &mut enc_idx) } {
Ok(_) => Ok(O::from(enc_idx)),
Err(e) => Err(GEncIndexError::IoctlError(e)),
}
}
#[derive(Debug, Clone, Copy)]
pub enum EncoderCommand {
Start,
Stop(bool),
Pause,
Resume,
}
#[derive(Debug, Error)]
pub enum EncoderCmdError {
#[error("error while converting from v4l2_encoder_cmd")]
FromV4L2CommandConversionError,
#[error("drain already in progress")]
DrainInProgress,
#[error("command not supported by device")]
UnsupportedCommand,
#[error("ioctl error: {0}")]
IoctlError(Errno),
}
impl From<EncoderCmdError> for Errno {
fn from(err: EncoderCmdError) -> Self {
match err {
EncoderCmdError::FromV4L2CommandConversionError => Errno::EINVAL,
EncoderCmdError::DrainInProgress => Errno::EBUSY,
EncoderCmdError::UnsupportedCommand => Errno::EINVAL,
EncoderCmdError::IoctlError(e) => e,
}
}
}
impl From<Errno> for EncoderCmdError {
fn from(error: Errno) -> Self {
match error {
Errno::EBUSY => EncoderCmdError::DrainInProgress,
Errno::EINVAL => EncoderCmdError::UnsupportedCommand,
e => EncoderCmdError::IoctlError(e),
}
}
}
impl From<&EncoderCommand> for v4l2_encoder_cmd {
fn from(command: &EncoderCommand) -> Self {
v4l2_encoder_cmd {
cmd: match command {
EncoderCommand::Start => bindings::V4L2_ENC_CMD_START,
EncoderCommand::Stop(_) => bindings::V4L2_ENC_CMD_STOP,
EncoderCommand::Pause => bindings::V4L2_ENC_CMD_PAUSE,
EncoderCommand::Resume => bindings::V4L2_ENC_CMD_RESUME,
},
flags: match &command {
EncoderCommand::Stop(at_gop) if *at_gop => bindings::V4L2_ENC_CMD_STOP_AT_GOP_END,
_ => 0,
},
..unsafe { mem::zeroed() }
}
}
}
impl TryFrom<v4l2_encoder_cmd> for () {
type Error = Infallible;
fn try_from(_: v4l2_encoder_cmd) -> Result<Self, Self::Error> {
Ok(())
}
}
/// Safe wrapper around the `VIDIOC_ENCODER_CMD` ioctl.
pub fn encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>(
fd: &impl AsRawFd,
command: I,
) -> Result<O, EncoderCmdError> {
let mut enc_cmd = command.into();
match unsafe { ioctl::vidioc_encoder_cmd(fd.as_raw_fd(), &mut enc_cmd) } {
Ok(_) => Ok(
O::try_from(enc_cmd).map_err(|_| EncoderCmdError::FromV4L2CommandConversionError)?
),
Err(e) => Err(e.into()),
}
}
/// Safe wrapper around the `VIDIOC_TRY_ENCODER_CMD` ioctl.
pub fn try_encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>(
fd: &impl AsRawFd,
command: I,
) -> Result<O, EncoderCmdError> {
let mut enc_cmd = command.into();
match unsafe { ioctl::vidioc_try_encoder_cmd(fd.as_raw_fd(), &mut enc_cmd) } {
Ok(_) => Ok(
O::try_from(enc_cmd).map_err(|_| EncoderCmdError::FromV4L2CommandConversionError)?
),
Err(e) => Err(e.into()),
}
}