ac_ffmpeg/format/
io.rs

1//! Elementary IO used by the muxer and demuxer.
2
3use std::{
4    convert::{TryFrom, TryInto},
5    io::{self, Read, Seek, SeekFrom, Write},
6    os::raw::{c_int, c_void},
7    slice,
8};
9
10type ReadPacketCallback =
11    extern "C" fn(opaque: *mut c_void, buffer: *mut u8, buffer_size: c_int) -> c_int;
12
13type WritePacketCallback =
14    extern "C" fn(opaque: *mut c_void, buffer: *const u8, buffer_size: c_int) -> c_int;
15
16type SeekCallback = extern "C" fn(opaque: *mut c_void, offset: i64, whence: c_int) -> i64;
17
18extern "C" {
19    fn ffw_io_whence_to_seek_mode(whence: c_int) -> c_int;
20
21    fn ffw_io_context_new(
22        buffer_size: c_int,
23        write_flag: c_int,
24        opaque: *mut c_void,
25        read_packet: Option<ReadPacketCallback>,
26        write_packet: Option<WritePacketCallback>,
27        seek: Option<SeekCallback>,
28    ) -> *mut c_void;
29    fn ffw_io_context_free(context: *mut c_void);
30}
31
32/// IO context.
33#[allow(clippy::upper_case_acronyms)]
34pub(crate) struct IOContext {
35    ptr: *mut c_void,
36}
37
38impl IOContext {
39    /// Create a new IO context from its raw representation.
40    pub unsafe fn from_raw_ptr(ptr: *mut c_void) -> Self {
41        IOContext { ptr }
42    }
43
44    /// Get a mut pointer to the underlying AVIOContext.
45    pub fn as_mut_ptr(&mut self) -> *mut c_void {
46        self.ptr
47    }
48}
49
50impl Drop for IOContext {
51    fn drop(&mut self) {
52        unsafe { ffw_io_context_free(self.ptr) }
53    }
54}
55
56unsafe impl Send for IOContext {}
57unsafe impl Sync for IOContext {}
58
59/// Helper function to get the length of a seekable stream. It will be replaced
60/// by `Seek::stream_len()` once it gets stabilized.
61fn get_seekable_length<T>(seekable: &mut T) -> Result<u64, std::io::Error>
62where
63    T: Seek,
64{
65    let current_position = seekable.stream_position()?;
66    let end_position = seekable.seek(SeekFrom::End(0))?;
67
68    seekable.seek(SeekFrom::Start(current_position))?;
69
70    Ok(end_position)
71}
72
73/// A SeekCallback function for the IO.
74extern "C" fn io_seek<T>(opaque: *mut c_void, offset: i64, whence: c_int) -> i64
75where
76    T: Seek,
77{
78    fn inner<U>(input: &mut U, offset: i64, mode: c_int) -> io::Result<i64>
79    where
80        U: Seek,
81    {
82        // 0 indicates that the AVSEEK_SIZE flag was set and we should just
83        // return the size of the stream.
84        if mode == 0 {
85            return get_seekable_length(input)?
86                .try_into()
87                .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "out of range"));
88        }
89
90        let pos = match mode {
91            // 1 means seek relative to the beginning
92            1 => u64::try_from(offset)
93                .map(SeekFrom::Start)
94                .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid offset"))?,
95
96            // 2 means seek relative to the current position
97            2 => SeekFrom::Current(offset),
98
99            // 3 means seek relative to the end position
100            3 => SeekFrom::End(offset),
101
102            _ => {
103                return Err(io::Error::new(
104                    io::ErrorKind::InvalidInput,
105                    "invalid whence",
106                ))
107            }
108        };
109
110        input
111            .seek(pos)?
112            .try_into()
113            .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "out of range"))
114    }
115
116    let input_ptr = opaque as *mut T;
117
118    let input = unsafe { &mut *input_ptr };
119
120    let mode = unsafe { ffw_io_whence_to_seek_mode(whence) };
121
122    match inner(input, offset, mode) {
123        Ok(len) => len,
124        Err(err) => err
125            .raw_os_error()
126            .map(|code| unsafe { crate::ffw_error_from_posix(code as _) })
127            .unwrap_or_else(|| unsafe { crate::ffw_error_unknown }) as i64,
128    }
129}
130
131/// A ReadPacketCallback function for the IO.
132extern "C" fn io_read_packet<T>(opaque: *mut c_void, buffer: *mut u8, buffer_size: c_int) -> c_int
133where
134    T: Read,
135{
136    let input_ptr = opaque as *mut T;
137
138    let input = unsafe { &mut *input_ptr };
139
140    let buffer = unsafe { slice::from_raw_parts_mut(buffer, buffer_size as usize) };
141
142    match input.read(buffer) {
143        Ok(n) => {
144            if n > 0 {
145                n as c_int
146            } else {
147                unsafe { crate::ffw_error_eof }
148            }
149        }
150        Err(err) => {
151            if let Some(code) = err.raw_os_error() {
152                unsafe { crate::ffw_error_from_posix(code as _) }
153            } else if err.kind() == io::ErrorKind::WouldBlock {
154                unsafe { crate::ffw_error_would_block }
155            } else {
156                unsafe { crate::ffw_error_unknown }
157            }
158        }
159    }
160}
161
162/// A WritePacketCallback function for the IO.
163extern "C" fn io_write_packet<T>(
164    opaque: *mut c_void,
165    buffer: *const u8,
166    buffer_size: c_int,
167) -> c_int
168where
169    T: Write,
170{
171    let output_ptr = opaque as *mut T;
172
173    let output = unsafe { &mut *output_ptr };
174
175    if !buffer.is_null() && buffer_size > 0 {
176        let buffer = unsafe { slice::from_raw_parts(buffer, buffer_size as usize) };
177
178        match output.write(buffer) {
179            Ok(n) => {
180                if n > 0 {
181                    n as c_int
182                } else {
183                    unsafe { crate::ffw_error_eof }
184                }
185            }
186            Err(err) => {
187                if let Some(code) = err.raw_os_error() {
188                    unsafe { crate::ffw_error_from_posix(code as _) }
189                } else if err.kind() == io::ErrorKind::WouldBlock {
190                    unsafe { crate::ffw_error_would_block }
191                } else {
192                    unsafe { crate::ffw_error_unknown }
193                }
194            }
195        }
196    } else if let Err(err) = output.flush() {
197        if let Some(code) = err.raw_os_error() {
198            unsafe { crate::ffw_error_from_posix(code) }
199        } else if err.kind() == io::ErrorKind::WouldBlock {
200            unsafe { crate::ffw_error_would_block }
201        } else {
202            unsafe { crate::ffw_error_unknown }
203        }
204    } else {
205        0
206    }
207}
208
209/// An AVIO IO that connects FFmpeg AVIO context with Rust streams.
210#[allow(clippy::upper_case_acronyms)]
211pub struct IO<T> {
212    io_context: IOContext,
213    stream: Box<T>,
214}
215
216impl<T> IO<T> {
217    /// Create a new IO.
218    fn new(
219        stream: T,
220        read_packet: Option<ReadPacketCallback>,
221        write_packet: Option<WritePacketCallback>,
222        seek: Option<SeekCallback>,
223    ) -> Self {
224        let mut stream = Box::new(stream);
225        let stream_ptr = stream.as_mut() as *mut T;
226        let opaque_ptr = stream_ptr as *mut c_void;
227
228        let write_flag = i32::from(write_packet.is_some());
229
230        let io_context = unsafe {
231            ffw_io_context_new(
232                4096,
233                write_flag,
234                opaque_ptr,
235                read_packet,
236                write_packet,
237                seek,
238            )
239        };
240
241        if io_context.is_null() {
242            panic!("unable to allocate an AVIO context");
243        }
244
245        let io_context = unsafe { IOContext::from_raw_ptr(io_context) };
246
247        Self { io_context, stream }
248    }
249
250    /// Get mutable reference to the underlying IO context.
251    pub(crate) fn io_context_mut(&mut self) -> &mut IOContext {
252        &mut self.io_context
253    }
254
255    /// Get reference to the underlying stream.
256    pub fn stream(&self) -> &T {
257        self.stream.as_ref()
258    }
259
260    /// Get mutable reference to the underlying stream.
261    pub fn stream_mut(&mut self) -> &mut T {
262        self.stream.as_mut()
263    }
264
265    /// Take the underlying stream dropping this IO.
266    pub fn into_stream(self) -> T {
267        *self.stream
268    }
269}
270
271impl<T> IO<T>
272where
273    T: Read,
274{
275    /// Create a new IO from a given stream.
276    pub fn from_read_stream(stream: T) -> Self {
277        Self::new(stream, Some(io_read_packet::<T>), None, None)
278    }
279}
280
281impl<T> IO<T>
282where
283    T: Read + Seek,
284{
285    /// Create a new IO from a given stream.
286    pub fn from_seekable_read_stream(stream: T) -> Self {
287        Self::new(stream, Some(io_read_packet::<T>), None, Some(io_seek::<T>))
288    }
289}
290
291impl<T> IO<T>
292where
293    T: Write,
294{
295    /// Create a new IO from a given stream.
296    pub fn from_write_stream(stream: T) -> Self {
297        Self::new(stream, None, Some(io_write_packet::<T>), None)
298    }
299}
300
301impl<T> IO<T>
302where
303    T: Write + Seek,
304{
305    /// Create a new IO from a given stream.
306    pub fn from_seekable_write_stream(stream: T) -> Self {
307        Self::new(stream, None, Some(io_write_packet::<T>), Some(io_seek::<T>))
308    }
309}
310
311/// Writer that puts everything in memory. It also allows taking the data on
312/// the fly.
313#[derive(Default)]
314pub struct MemWriter {
315    data: Vec<u8>,
316}
317
318impl MemWriter {
319    /// Take data from the writer.
320    pub fn take_data(&mut self) -> Vec<u8> {
321        let res = Vec::from(self.data.as_slice());
322
323        self.data.clear();
324
325        res
326    }
327}
328
329impl Write for MemWriter {
330    fn write(&mut self, buffer: &[u8]) -> Result<usize, io::Error> {
331        self.data.extend_from_slice(buffer);
332
333        Ok(buffer.len())
334    }
335
336    #[inline]
337    fn flush(&mut self) -> Result<(), io::Error> {
338        Ok(())
339    }
340}