opus_codec/
repacketizer.rs

1//! Safe wrapper for `OpusRepacketizer` utilities
2
3use crate::bindings::{
4    OpusRepacketizer, opus_repacketizer_cat, opus_repacketizer_create, opus_repacketizer_destroy,
5    opus_repacketizer_get_nb_frames, opus_repacketizer_get_size, opus_repacketizer_init,
6    opus_repacketizer_out, opus_repacketizer_out_range,
7};
8use crate::error::{Error, Result};
9use crate::{AlignedBuffer, Ownership, RawHandle};
10use std::marker::PhantomData;
11use std::ops::{Deref, DerefMut};
12use std::ptr::NonNull;
13
14/// Repackages Opus frames into packets.
15pub struct Repacketizer {
16    rp: RawHandle<OpusRepacketizer>,
17    packets: Vec<Vec<u8>>,
18}
19
20unsafe impl Send for Repacketizer {}
21unsafe impl Sync for Repacketizer {}
22
23/// Borrowed wrapper around a repacketizer state.
24pub struct RepacketizerRef<'a> {
25    inner: Repacketizer,
26    _marker: PhantomData<&'a mut OpusRepacketizer>,
27}
28
29unsafe impl Send for RepacketizerRef<'_> {}
30unsafe impl Sync for RepacketizerRef<'_> {}
31
32impl Repacketizer {
33    fn from_raw(ptr: NonNull<OpusRepacketizer>, ownership: Ownership) -> Self {
34        Self {
35            rp: RawHandle::new(ptr, ownership, opus_repacketizer_destroy),
36            packets: Vec::new(),
37        }
38    }
39
40    /// Create a new repacketizer.
41    ///
42    /// # Errors
43    /// Returns `AllocFail` if allocation fails.
44    pub fn new() -> Result<Self> {
45        let rp = unsafe { opus_repacketizer_create() };
46        let rp = NonNull::new(rp).ok_or(Error::AllocFail)?;
47        Ok(Self::from_raw(rp, Ownership::Owned))
48    }
49
50    /// Reset internal state.
51    pub fn reset(&mut self) {
52        unsafe { opus_repacketizer_init(self.rp.as_ptr()) };
53        self.packets.clear();
54    }
55
56    /// Add a packet to the current state.
57    ///
58    /// The packet data is copied and retained until the next call to [`Self::reset`].
59    ///
60    /// # Errors
61    /// Returns an error if the packet is invalid for the current state.
62    pub fn push(&mut self, packet: &[u8]) -> Result<()> {
63        if packet.is_empty() {
64            return Err(Error::BadArg);
65        }
66        let len_i32 = i32::try_from(packet.len()).map_err(|_| Error::BadArg)?;
67        self.packets.push(packet.to_vec());
68        let idx = self.packets.len() - 1;
69        let r =
70            unsafe { opus_repacketizer_cat(self.rp.as_ptr(), self.packets[idx].as_ptr(), len_i32) };
71        if r != 0 {
72            self.packets.pop();
73            return Err(Error::from_code(r));
74        }
75        // libopus stores pointers into packet data; keep owned buffers alive.
76        Ok(())
77    }
78
79    /// Number of frames currently queued.
80    #[must_use]
81    pub fn frame_count(&self) -> i32 {
82        unsafe { opus_repacketizer_get_nb_frames(self.rp.as_ptr()) }
83    }
84
85    /// Number of frames currently queued as a `usize`.
86    #[must_use]
87    pub fn len(&self) -> usize {
88        let frames = self.frame_count();
89        debug_assert!(
90            frames >= 0,
91            "repacketizer frame count should be non-negative"
92        );
93        usize::try_from(frames).unwrap_or(0)
94    }
95
96    /// Returns true when there are no queued frames.
97    #[must_use]
98    pub fn is_empty(&self) -> bool {
99        self.len() == 0
100    }
101
102    /// Emit a packet containing frames in range [begin, end).
103    ///
104    /// # Errors
105    /// Returns an error if range is invalid or output buffer is too small.
106    pub fn emit_range(&mut self, begin: i32, end: i32, out: &mut [u8]) -> Result<usize> {
107        if out.is_empty() {
108            return Err(Error::BadArg);
109        }
110        if begin < 0 || end <= begin {
111            return Err(Error::BadArg);
112        }
113        let out_len_i32 = i32::try_from(out.len()).map_err(|_| Error::BadArg)?;
114        let n = unsafe {
115            opus_repacketizer_out_range(self.rp.as_ptr(), begin, end, out.as_mut_ptr(), out_len_i32)
116        };
117        if n < 0 {
118            return Err(Error::from_code(n));
119        }
120        usize::try_from(n).map_err(|_| Error::InternalError)
121    }
122
123    /// Emit a packet with all queued frames.
124    ///
125    /// # Errors
126    /// Returns an error if the output buffer is too small.
127    pub fn emit(&mut self, out: &mut [u8]) -> Result<usize> {
128        if out.is_empty() {
129            return Err(Error::BadArg);
130        }
131        let out_len_i32 = i32::try_from(out.len()).map_err(|_| Error::BadArg)?;
132        let n = unsafe { opus_repacketizer_out(self.rp.as_ptr(), out.as_mut_ptr(), out_len_i32) };
133        if n < 0 {
134            return Err(Error::from_code(n));
135        }
136        usize::try_from(n).map_err(|_| Error::InternalError)
137    }
138
139    /// Size of a repacketizer state in bytes for external allocation.
140    ///
141    /// # Errors
142    /// Returns [`Error::InternalError`] if libopus reports an invalid size.
143    pub fn size() -> Result<usize> {
144        let raw = unsafe { opus_repacketizer_get_size() };
145        if raw <= 0 {
146            return Err(Error::InternalError);
147        }
148        usize::try_from(raw).map_err(|_| Error::InternalError)
149    }
150
151    /// Initialize a previously allocated repacketizer state.
152    ///
153    /// # Safety
154    /// The caller must provide a valid pointer to `Repacketizer::size()` bytes,
155    /// aligned to at least `align_of::<usize>()` (malloc-style alignment).
156    ///
157    /// # Errors
158    /// Returns [`Error::BadArg`] if `ptr` is null.
159    pub unsafe fn init_in_place(ptr: *mut OpusRepacketizer) -> Result<()> {
160        if ptr.is_null() {
161            return Err(Error::BadArg);
162        }
163        if !crate::opus_ptr_is_aligned(ptr.cast()) {
164            return Err(Error::BadArg);
165        }
166        unsafe { opus_repacketizer_init(ptr) };
167        Ok(())
168    }
169}
170
171impl<'a> RepacketizerRef<'a> {
172    /// Wrap an externally-initialized repacketizer without taking ownership.
173    ///
174    /// # Safety
175    /// - `ptr` must point to valid, initialized memory of at least [`Repacketizer::size()`] bytes
176    /// - `ptr` must be aligned to at least `align_of::<usize>()` (malloc-style alignment)
177    /// - The memory must remain valid for the lifetime `'a`
178    /// - Caller is responsible for freeing the memory after this wrapper is dropped
179    ///
180    /// Use [`Repacketizer::init_in_place`] to initialize the memory before calling this.
181    #[must_use]
182    pub unsafe fn from_raw(ptr: *mut OpusRepacketizer) -> Self {
183        debug_assert!(!ptr.is_null(), "from_raw called with null ptr");
184        debug_assert!(crate::opus_ptr_is_aligned(ptr.cast()));
185        let repacketizer =
186            Repacketizer::from_raw(unsafe { NonNull::new_unchecked(ptr) }, Ownership::Borrowed);
187        Self {
188            inner: repacketizer,
189            _marker: PhantomData,
190        }
191    }
192
193    /// Initialize and wrap an externally allocated buffer.
194    ///
195    /// # Errors
196    /// Returns [`Error::BadArg`] if the buffer is too small.
197    pub fn init_in(buf: &'a mut AlignedBuffer) -> Result<Self> {
198        let required = Repacketizer::size()?;
199        if buf.capacity_bytes() < required {
200            return Err(Error::BadArg);
201        }
202        let ptr = buf.as_mut_ptr::<OpusRepacketizer>();
203        unsafe { Repacketizer::init_in_place(ptr)? };
204        Ok(unsafe { Self::from_raw(ptr) })
205    }
206}
207
208impl Deref for RepacketizerRef<'_> {
209    type Target = Repacketizer;
210
211    fn deref(&self) -> &Self::Target {
212        &self.inner
213    }
214}
215
216impl DerefMut for RepacketizerRef<'_> {
217    fn deref_mut(&mut self) -> &mut Self::Target {
218        &mut self.inner
219    }
220}