rvideo/
lib.rs

1#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "README.md" ) ) ]
2#![deny(missing_docs)]
3use core::fmt;
4use std::{sync::Arc, time::Duration};
5
6use binrw::binrw;
7
8mod client;
9#[cfg(feature = "async")]
10mod client_async;
11mod server;
12pub use client::Client;
13#[cfg(feature = "async")]
14pub use client_async::ClientAsync;
15use once_cell::sync::Lazy;
16use serde::{Deserialize, Serialize};
17pub use server::Server;
18use server::StreamServerInner;
19use std::net::ToSocketAddrs;
20
21#[cfg(feature = "locking-default")]
22use parking_lot::{Condvar, Mutex, RawMutex};
23
24#[cfg(feature = "locking-rt")]
25use parking_lot_rt::{Condvar, Mutex, RawMutex};
26
27#[cfg(feature = "locking-rt-safe")]
28use rtsc::pi::{Condvar, Mutex, RawMutex};
29
30const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
31
32static DEFAULT_SERVER: Lazy<Server> = Lazy::new(|| Server::new(DEFAULT_TIMEOUT));
33
34/// Add a stream to the default server
35pub fn add_stream(format: Format, width: u16, height: u16) -> Result<Stream, Error> {
36    DEFAULT_SERVER.add_stream(format, width, height)
37}
38
39/// Send frame to the default server with stream id
40pub fn send_frame(stream_id: u16, frame: Frame) -> Result<(), Error> {
41    DEFAULT_SERVER.send_frame(stream_id, frame)
42}
43
44/// Serve the default server
45pub fn serve(addr: impl ToSocketAddrs + std::fmt::Debug) -> Result<(), Error> {
46    DEFAULT_SERVER.serve(addr)
47}
48
49/// Video frame
50#[derive(Clone, Debug)]
51pub struct Frame {
52    /// An optional metadata (encoded in a way, known to remotes)
53    pub metadata: Option<Arc<Vec<u8>>>,
54    /// The frame data (encoded/compressed into the stream format)
55    pub data: Arc<Vec<u8>>,
56}
57
58impl From<Vec<u8>> for Frame {
59    fn from(data: Vec<u8>) -> Self {
60        Self {
61            metadata: None,
62            data: data.into(),
63        }
64    }
65}
66
67impl From<Arc<Vec<u8>>> for Frame {
68    fn from(data: Arc<Vec<u8>>) -> Self {
69        Self {
70            metadata: None,
71            data,
72        }
73    }
74}
75
76impl Frame {
77    /// Create a new frame with no metadata. Arc is used to avoid copying the data, as many video
78    /// apps already cover their data with Arc.
79    pub fn new(data: Arc<Vec<u8>>) -> Self {
80        Self {
81            metadata: None,
82            data,
83        }
84    }
85    /// Create a new frame with metadata. Arc is used to avoid copying the data, as many video apps
86    /// already cover their data with Arc. The metadata should be encoded in a way, known to
87    /// remotes
88    pub fn new_with_metadata(metadata: Arc<Vec<u8>>, data: Arc<Vec<u8>>) -> Self {
89        Self {
90            metadata: Some(metadata),
91            data,
92        }
93    }
94}
95
96/// Server API version
97pub const API_VERSION: u8 = 2;
98
99/// Error type
100#[derive(thiserror::Error, Debug)]
101pub enum Error {
102    /// Invalid stream (not known to the server)
103    #[error("Invalid stream")]
104    InvalidStream,
105    /// Too many streams (max supported per server is u16::MAX)
106    #[error("Too many streams")]
107    TooManyStreams,
108    /// IO error
109    #[error("IO error: {0}")]
110    Io(#[from] std::io::Error),
111    #[error("Unsupported API version: {0}")]
112    /// Unsupported API version
113    ApiVersion(u8),
114    /// Invalid data (binrw decode error)
115    #[error("Invalid binary data: {0}")]
116    Decode(#[from] binrw::Error),
117    /// Frame metadata is larger than u32::MAX
118    #[error("Frame metadata too large")]
119    FrameMetaDataTooLarge,
120    /// Frame data is larger than u32::MAX
121    #[error("Frame data too large")]
122    FrameDataTooLarge,
123    /// Invalid TCP/IP address/host name/port
124    #[error("Invalid address")]
125    InvalidAddress,
126    /// Client not ready (not connected/stream not selected)
127    #[error("Not ready")]
128    NotReady,
129    /// Async timeouts
130    #[error("Timed out")]
131    #[cfg(feature = "async")]
132    AsyncTimeout(#[from] tokio::time::error::Elapsed),
133}
134
135/// Video formats. Note: a frame MUST be MANUALLY encoded/compressed with the selected format
136/// BEFORE sending
137#[binrw]
138#[br(repr = u8)]
139#[bw(repr = u8)]
140#[derive(Copy, Clone, Debug, PartialEq, Eq)]
141pub enum Format {
142    /// 8-bit luma
143    Luma8 = 0,
144    /// 16-bit luma
145    Luma16 = 1,
146    /// 8-bit luma with alpha
147    LumaA8 = 2,
148    /// 16-bit luma with alpha
149    LumaA16 = 3,
150    /// 24-bit RGB
151    Rgb8 = 4,
152    /// 48-bit RGB
153    Rgb16 = 5,
154    /// 32-bit RGBA
155    Rgba8 = 6,
156    /// 64-bit RGBA
157    Rgba16 = 7,
158    /// Motion JPEG (JPEG frames can be encoded in any way)
159    MJpeg = 64,
160}
161
162/// The default bounding box which can be used in custom applications. The bounding box format is
163/// also recognized by [rvideo-view](https://crates.io/crates/rvideo-view).
164#[derive(Serialize, Deserialize, Clone, Debug)]
165pub struct BoundingBox {
166    #[serde(rename = "c")]
167    /// The color of the bounding box in RGB format
168    pub color: [u8; 3],
169    /// The x coordinate of the top-left corner
170    pub x: u16,
171    /// The y coordinate of the top-left corner
172    pub y: u16,
173    /// The width of the bounding box
174    #[serde(rename = "w")]
175    pub width: u16,
176    /// The height of the bounding box
177    #[serde(rename = "h")]
178    pub height: u16,
179}
180
181#[binrw]
182#[brw(little, magic = b"R")]
183#[derive(Clone, Debug)]
184struct Greetings {
185    api_version: u8,
186    streams_available: u16,
187}
188
189#[binrw]
190#[brw(little)]
191#[derive(Clone, Debug)]
192struct StreamSelect {
193    stream_id: u16,
194    max_fps: u8,
195}
196
197/// Stream information
198#[binrw]
199#[brw(little)]
200#[derive(Clone, Debug)]
201pub struct StreamInfo {
202    /// Stream id
203    pub id: u16,
204    /// Stream format
205    pub format: Format,
206    /// Picture width
207    pub width: u16,
208    /// Picture height
209    pub height: u16,
210}
211
212impl fmt::Display for StreamInfo {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        write!(
215            f,
216            "#{}, WxH: {}x{}, Fmt: {:?}",
217            self.id, self.width, self.height, self.format
218        )
219    }
220}
221
222/// A stream helper object. Contains a stream id and a reference to the server inner object
223#[derive(Clone)]
224pub struct Stream {
225    id: u16,
226    server_inner: Arc<StreamServerInner>,
227}
228
229impl Stream {
230    /// Get the stream id
231    pub fn id(&self) -> u16 {
232        self.id
233    }
234    /// Send a frame to the stream
235    pub fn send_frame(&self, frame: Frame) -> Result<(), Error> {
236        self.server_inner.send_frame(self.id, frame)
237    }
238}