1 use std::convert::{Infallible, TryFrom};
2 use std::os::unix::io::AsRawFd;
3
4 use nix::errno::Errno;
5 use thiserror::Error;
6
7 use crate::bindings;
8 use crate::bindings::v4l2_enc_idx;
9 use crate::bindings::v4l2_encoder_cmd;
10
11 #[doc(hidden)]
12 mod ioctl {
13 use crate::bindings::v4l2_enc_idx;
14 use crate::bindings::v4l2_encoder_cmd;
15
16 nix::ioctl_read!(vidioc_g_enc_index, b'V', 76, v4l2_enc_idx);
17 nix::ioctl_readwrite!(vidioc_encoder_cmd, b'V', 77, v4l2_encoder_cmd);
18 nix::ioctl_readwrite!(vidioc_try_encoder_cmd, b'V', 78, v4l2_encoder_cmd);
19 }
20
21 #[derive(Debug, Error)]
22 pub enum GEncIndexError {
23 #[error("ioctl error: {0}")]
24 IoctlError(Errno),
25 }
26
27 impl From<GEncIndexError> for Errno {
from(err: GEncIndexError) -> Self28 fn from(err: GEncIndexError) -> Self {
29 match err {
30 GEncIndexError::IoctlError(e) => e,
31 }
32 }
33 }
34
35 /// Safe wrapper around the `VIDIOC_G_ENC_INDEX` ioctl.
g_enc_index<O: From<v4l2_enc_idx>>(fd: &impl AsRawFd) -> Result<O, GEncIndexError>36 pub fn g_enc_index<O: From<v4l2_enc_idx>>(fd: &impl AsRawFd) -> Result<O, GEncIndexError> {
37 let mut enc_idx: v4l2_enc_idx = Default::default();
38
39 match unsafe { ioctl::vidioc_g_enc_index(fd.as_raw_fd(), &mut enc_idx) } {
40 Ok(_) => Ok(O::from(enc_idx)),
41 Err(e) => Err(GEncIndexError::IoctlError(e)),
42 }
43 }
44
45 #[derive(Debug, Clone, Copy)]
46 pub enum EncoderCommand {
47 Start,
48 Stop(bool),
49 Pause,
50 Resume,
51 }
52
53 #[derive(Debug, Error)]
54 pub enum EncoderCmdError {
55 #[error("error while converting from v4l2_encoder_cmd")]
56 FromV4L2CommandConversionError,
57 #[error("drain already in progress")]
58 DrainInProgress,
59 #[error("command not supported by device")]
60 UnsupportedCommand,
61 #[error("ioctl error: {0}")]
62 IoctlError(Errno),
63 }
64
65 impl From<EncoderCmdError> for Errno {
from(err: EncoderCmdError) -> Self66 fn from(err: EncoderCmdError) -> Self {
67 match err {
68 EncoderCmdError::FromV4L2CommandConversionError => Errno::EINVAL,
69 EncoderCmdError::DrainInProgress => Errno::EBUSY,
70 EncoderCmdError::UnsupportedCommand => Errno::EINVAL,
71 EncoderCmdError::IoctlError(e) => e,
72 }
73 }
74 }
75
76 impl From<Errno> for EncoderCmdError {
from(error: Errno) -> Self77 fn from(error: Errno) -> Self {
78 match error {
79 Errno::EBUSY => EncoderCmdError::DrainInProgress,
80 Errno::EINVAL => EncoderCmdError::UnsupportedCommand,
81 e => EncoderCmdError::IoctlError(e),
82 }
83 }
84 }
85
86 impl From<&EncoderCommand> for v4l2_encoder_cmd {
from(command: &EncoderCommand) -> Self87 fn from(command: &EncoderCommand) -> Self {
88 v4l2_encoder_cmd {
89 cmd: match command {
90 EncoderCommand::Start => bindings::V4L2_ENC_CMD_START,
91 EncoderCommand::Stop(_) => bindings::V4L2_ENC_CMD_STOP,
92 EncoderCommand::Pause => bindings::V4L2_ENC_CMD_PAUSE,
93 EncoderCommand::Resume => bindings::V4L2_ENC_CMD_RESUME,
94 },
95 flags: match &command {
96 EncoderCommand::Stop(at_gop) if *at_gop => bindings::V4L2_ENC_CMD_STOP_AT_GOP_END,
97 _ => 0,
98 },
99 ..Default::default()
100 }
101 }
102 }
103
104 impl TryFrom<v4l2_encoder_cmd> for () {
105 type Error = Infallible;
106
try_from(_: v4l2_encoder_cmd) -> Result<Self, Self::Error>107 fn try_from(_: v4l2_encoder_cmd) -> Result<Self, Self::Error> {
108 Ok(())
109 }
110 }
111
112 /// Safe wrapper around the `VIDIOC_ENCODER_CMD` ioctl.
encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>( fd: &impl AsRawFd, command: I, ) -> Result<O, EncoderCmdError>113 pub fn encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>(
114 fd: &impl AsRawFd,
115 command: I,
116 ) -> Result<O, EncoderCmdError> {
117 let mut enc_cmd = command.into();
118
119 match unsafe { ioctl::vidioc_encoder_cmd(fd.as_raw_fd(), &mut enc_cmd) } {
120 Ok(_) => Ok(
121 O::try_from(enc_cmd).map_err(|_| EncoderCmdError::FromV4L2CommandConversionError)?
122 ),
123 Err(e) => Err(e.into()),
124 }
125 }
126
127 /// Safe wrapper around the `VIDIOC_TRY_ENCODER_CMD` ioctl.
try_encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>( fd: &impl AsRawFd, command: I, ) -> Result<O, EncoderCmdError>128 pub fn try_encoder_cmd<I: Into<v4l2_encoder_cmd>, O: TryFrom<v4l2_encoder_cmd>>(
129 fd: &impl AsRawFd,
130 command: I,
131 ) -> Result<O, EncoderCmdError> {
132 let mut enc_cmd = command.into();
133
134 match unsafe { ioctl::vidioc_try_encoder_cmd(fd.as_raw_fd(), &mut enc_cmd) } {
135 Ok(_) => Ok(
136 O::try_from(enc_cmd).map_err(|_| EncoderCmdError::FromV4L2CommandConversionError)?
137 ),
138 Err(e) => Err(e.into()),
139 }
140 }
141