Skip to main content

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};
9#[cfg(opus_codec_rust_packet_ops)]
10use crate::packet;
11use crate::{AlignedBuffer, Ownership, RawHandle};
12use std::marker::PhantomData;
13use std::ops::Deref;
14use std::ptr::NonNull;
15
16/// Repackages Opus frames into packets.
17pub struct Repacketizer {
18    rp: RawHandle<OpusRepacketizer>,
19    packets: Vec<RetainedPacket>,
20}
21
22struct RetainedPacket {
23    data: Vec<u8>,
24    #[cfg(opus_codec_rust_packet_ops)]
25    layout: Result<packet::PacketRepacketizerLayout>,
26}
27
28unsafe impl Send for Repacketizer {}
29
30/// Borrowed wrapper around a repacketizer state.
31///
32/// The owning handle cannot be moved out of this borrowed wrapper:
33///
34/// ```compile_fail
35/// use opus_codec::repacketizer::{Repacketizer, RepacketizerRef};
36/// fn extract<'a>(state: &mut RepacketizerRef<'a>, replacement: Repacketizer) -> Repacketizer {
37///     std::mem::replace(&mut **state, replacement)
38/// }
39/// ```
40pub struct RepacketizerRef<'a> {
41    inner: Repacketizer,
42    _marker: PhantomData<&'a mut OpusRepacketizer>,
43}
44
45unsafe impl Send for RepacketizerRef<'_> {}
46
47impl Repacketizer {
48    fn from_raw(ptr: NonNull<OpusRepacketizer>, ownership: Ownership) -> Self {
49        Self {
50            rp: RawHandle::new(ptr, ownership, opus_repacketizer_destroy),
51            packets: Vec::new(),
52        }
53    }
54
55    /// Create a new repacketizer.
56    ///
57    /// # Errors
58    /// Returns `AllocFail` if allocation fails.
59    pub fn new() -> Result<Self> {
60        let rp = unsafe { opus_repacketizer_create() };
61        let rp = NonNull::new(rp).ok_or(Error::AllocFail)?;
62        Ok(Self::from_raw(rp, Ownership::Owned))
63    }
64
65    /// Reset internal state.
66    pub fn reset(&mut self) {
67        unsafe { opus_repacketizer_init(self.rp.as_ptr()) };
68        self.packets.clear();
69    }
70
71    /// Add a packet to the current state.
72    ///
73    /// The packet data is copied and retained until the next call to [`Self::reset`].
74    ///
75    /// # Errors
76    /// Returns an error if the packet is invalid for the current state.
77    pub fn push(&mut self, packet: &[u8]) -> Result<()> {
78        if packet.is_empty() || i32::try_from(packet.len()).is_err() {
79            return Err(Error::BadArg);
80        }
81        self.push_owned(packet.to_vec())
82    }
83
84    /// Add an owned packet to the current state without copying its payload.
85    ///
86    /// The packet is retained until the next call to [`Self::reset`].
87    ///
88    /// # Errors
89    /// Returns an error if the packet is invalid for the current state.
90    pub fn push_owned(&mut self, packet: Vec<u8>) -> Result<()> {
91        if packet.is_empty() {
92            return Err(Error::BadArg);
93        }
94        let len_i32 = i32::try_from(packet.len()).map_err(|_| Error::BadArg)?;
95        #[cfg(opus_codec_rust_packet_ops)]
96        let layout = packet::packet_repacketizer_layout(&packet);
97        let r = unsafe { opus_repacketizer_cat(self.rp.as_ptr(), packet.as_ptr(), len_i32) };
98        if r != 0 {
99            return Err(Error::from_code(r));
100        }
101        // libopus stores pointers into packet data; keep owned buffers alive.
102        self.packets.push(RetainedPacket {
103            data: packet,
104            #[cfg(opus_codec_rust_packet_ops)]
105            layout,
106        });
107        Ok(())
108    }
109
110    /// Number of frames currently queued.
111    #[must_use]
112    pub fn frame_count(&self) -> i32 {
113        unsafe { opus_repacketizer_get_nb_frames(self.rp.as_ptr()) }
114    }
115
116    /// Number of frames currently queued as a `usize`.
117    #[must_use]
118    pub fn len(&self) -> usize {
119        let frames = self.frame_count();
120        debug_assert!(
121            frames >= 0,
122            "repacketizer frame count should be non-negative"
123        );
124        usize::try_from(frames).unwrap_or(0)
125    }
126
127    /// Returns true when there are no queued frames.
128    #[must_use]
129    pub fn is_empty(&self) -> bool {
130        self.len() == 0
131    }
132
133    /// Emit a packet containing frames in range [begin, end).
134    ///
135    /// # Errors
136    /// Returns [`Error::BadArg`] if the range is invalid or `out` is empty, or
137    /// [`Error::BufferTooSmall`] if `out` cannot hold the packet.
138    pub fn emit_range(&mut self, begin: i32, end: i32, out: &mut [u8]) -> Result<usize> {
139        if out.is_empty() {
140            return Err(Error::BadArg);
141        }
142        if begin < 0 || end <= begin {
143            return Err(Error::BadArg);
144        }
145        #[cfg(opus_codec_rust_packet_ops)]
146        let begin_index = usize::try_from(begin).map_err(|_| Error::BadArg)?;
147        #[cfg(opus_codec_rust_packet_ops)]
148        let end_index = usize::try_from(end).map_err(|_| Error::BadArg)?;
149        #[cfg(not(opus_codec_rust_packet_ops))]
150        {
151            self.emit_range_via_c(begin, end, out)
152        }
153        #[cfg(opus_codec_rust_packet_ops)]
154        {
155            let mut frames = [&[][..]; packet::MAX_FRAMES_PER_PACKET];
156            let mut paddings = [packet::PacketPadding::EMPTY; packet::MAX_FRAMES_PER_PACKET];
157            if let Some((toc, frame_count)) =
158                self.repacketizer_inputs(&mut frames, &mut paddings)?
159            {
160                if end_index > frame_count {
161                    return Err(Error::BadArg);
162                }
163                return packet::repacketize_frames_range(
164                    toc,
165                    &frames[..frame_count],
166                    &paddings[..frame_count],
167                    begin_index,
168                    end_index,
169                    out,
170                );
171            }
172            self.emit_range_via_c(begin, end, out)
173        }
174    }
175
176    /// Emit a packet with all queued frames.
177    ///
178    /// # Errors
179    /// Returns [`Error::BadArg`] if `out` is empty, or [`Error::BufferTooSmall`]
180    /// if `out` cannot hold the packet.
181    pub fn emit(&mut self, out: &mut [u8]) -> Result<usize> {
182        if out.is_empty() {
183            return Err(Error::BadArg);
184        }
185        #[cfg(not(opus_codec_rust_packet_ops))]
186        {
187            self.emit_via_c(out)
188        }
189        #[cfg(opus_codec_rust_packet_ops)]
190        {
191            let mut frames = [&[][..]; packet::MAX_FRAMES_PER_PACKET];
192            let mut paddings = [packet::PacketPadding::EMPTY; packet::MAX_FRAMES_PER_PACKET];
193            if let Some((toc, frame_count)) =
194                self.repacketizer_inputs(&mut frames, &mut paddings)?
195            {
196                return packet::repacketize_frames(
197                    toc,
198                    &frames[..frame_count],
199                    &paddings[..frame_count],
200                    out,
201                );
202            }
203            self.emit_via_c(out)
204        }
205    }
206
207    /// Size of a repacketizer state in bytes for external allocation.
208    ///
209    /// # Errors
210    /// Returns [`Error::InternalError`] if libopus reports an invalid size.
211    pub fn size() -> Result<usize> {
212        let raw = unsafe { opus_repacketizer_get_size() };
213        if raw <= 0 {
214            return Err(Error::InternalError);
215        }
216        usize::try_from(raw).map_err(|_| Error::InternalError)
217    }
218
219    /// Initialize a previously allocated repacketizer state.
220    ///
221    /// # Safety
222    /// The caller must provide a valid pointer to `Repacketizer::size()` bytes,
223    /// aligned to at least `align_of::<usize>()` (malloc-style alignment).
224    ///
225    /// # Errors
226    /// Returns [`Error::BadArg`] if `ptr` is null.
227    pub unsafe fn init_in_place(ptr: *mut OpusRepacketizer) -> Result<()> {
228        if ptr.is_null() {
229            return Err(Error::BadArg);
230        }
231        if !crate::opus_ptr_is_aligned(ptr.cast()) {
232            return Err(Error::BadArg);
233        }
234        unsafe { opus_repacketizer_init(ptr) };
235        Ok(())
236    }
237
238    #[cfg(opus_codec_rust_packet_ops)]
239    fn repacketizer_inputs<'a>(
240        &'a self,
241        frames: &mut [&'a [u8]; packet::MAX_FRAMES_PER_PACKET],
242        paddings: &mut [packet::PacketPadding<'a>; packet::MAX_FRAMES_PER_PACKET],
243    ) -> Result<Option<(u8, usize)>> {
244        let c_frame_count = self.len();
245        if self.packets.is_empty() {
246            return if c_frame_count == 0 {
247                Err(Error::BadArg)
248            } else {
249                Ok(None)
250            };
251        }
252
253        let mut toc = None;
254        let mut frame_count = 0usize;
255        for packet in &self.packets {
256            let layout = match &packet.layout {
257                Ok(layout) => layout,
258                // The packet was already accepted by opus_repacketizer_cat().
259                // Treat a Rust/C structural-parser disagreement as an
260                // unavailable optimization and let the caller use C output.
261                Err(Error::InvalidPacket) => return Ok(None),
262                Err(err) => return Err(err.clone()),
263            };
264            toc.get_or_insert(layout.toc);
265            let packet_frame_count = layout.frames().len();
266            if frame_count + packet_frame_count > packet::MAX_FRAMES_PER_PACKET {
267                return Ok(None);
268            }
269            for (packet_frame, frame) in layout.frames().iter().enumerate() {
270                frames[frame_count] = frame.slice(&packet.data)?;
271                paddings[frame_count] = if packet_frame == 0 {
272                    layout.packet_padding(&packet.data)
273                } else {
274                    packet::PacketPadding::EMPTY
275                };
276                frame_count += 1;
277            }
278        }
279
280        if frame_count != c_frame_count {
281            return Ok(None);
282        }
283
284        let toc = toc.ok_or(Error::BadArg)?;
285        Ok(Some((toc, frame_count)))
286    }
287
288    fn emit_range_via_c(&self, begin: i32, end: i32, out: &mut [u8]) -> Result<usize> {
289        if out.is_empty() {
290            return Err(Error::BadArg);
291        }
292        let out_len_i32 = i32::try_from(out.len()).map_err(|_| Error::BadArg)?;
293        let n = unsafe {
294            opus_repacketizer_out_range(self.rp.as_ptr(), begin, end, out.as_mut_ptr(), out_len_i32)
295        };
296        if n < 0 {
297            return Err(Error::from_code(n));
298        }
299        usize::try_from(n).map_err(|_| Error::InternalError)
300    }
301
302    fn emit_via_c(&self, out: &mut [u8]) -> Result<usize> {
303        if out.is_empty() {
304            return Err(Error::BadArg);
305        }
306        let out_len_i32 = i32::try_from(out.len()).map_err(|_| Error::BadArg)?;
307        let n = unsafe { opus_repacketizer_out(self.rp.as_ptr(), out.as_mut_ptr(), out_len_i32) };
308        if n < 0 {
309            return Err(Error::from_code(n));
310        }
311        usize::try_from(n).map_err(|_| Error::InternalError)
312    }
313}
314
315impl<'a> RepacketizerRef<'a> {
316    /// Wrap an externally-initialized repacketizer without taking ownership.
317    ///
318    /// # Safety
319    /// - `ptr` must point to valid, initialized memory of at least [`Repacketizer::size()`] bytes
320    /// - `ptr` must be aligned to at least `align_of::<usize>()` (malloc-style alignment)
321    /// - The memory must remain valid for the lifetime `'a`
322    /// - Caller is responsible for freeing the memory after this wrapper is dropped
323    /// - If `ptr` already contains packet pointers, their backing storage must
324    ///   remain valid while this wrapper is used.
325    ///
326    /// Use [`Repacketizer::init_in_place`] to initialize the memory before calling this.
327    /// If packets are pushed through this wrapper, dropping it resets the raw
328    /// state to avoid leaving dangling pointers to the wrapper's owned buffers.
329    ///
330    /// # Panics
331    /// Panics if `ptr` is null or not pointer-aligned.
332    #[must_use]
333    pub unsafe fn from_raw(ptr: *mut OpusRepacketizer) -> Self {
334        let repacketizer = Repacketizer::from_raw(
335            crate::checked_non_null(ptr, "RepacketizerRef::from_raw"),
336            Ownership::Borrowed,
337        );
338        Self {
339            inner: repacketizer,
340            _marker: PhantomData,
341        }
342    }
343
344    /// Initialize and wrap an externally allocated buffer.
345    ///
346    /// # Errors
347    /// Returns [`Error::BadArg`] if the buffer is too small.
348    pub fn init_in(buf: &'a mut AlignedBuffer) -> Result<Self> {
349        let required = Repacketizer::size()?;
350        if buf.capacity_bytes() < required {
351            return Err(Error::BadArg);
352        }
353        let ptr = buf.as_mut_ptr::<OpusRepacketizer>();
354        unsafe { Repacketizer::init_in_place(ptr)? };
355        Ok(unsafe { Self::from_raw(ptr) })
356    }
357
358    delegate_ref_mut_methods! {
359        fn reset() -> ();
360        fn push(packet: &[u8]) -> Result<()>;
361        fn push_owned(packet: Vec<u8>) -> Result<()>;
362        fn emit_range(begin: i32, end: i32, out: &mut [u8]) -> Result<usize>;
363        fn emit(out: &mut [u8]) -> Result<usize>;
364    }
365}
366
367impl Drop for RepacketizerRef<'_> {
368    fn drop(&mut self) {
369        if !self.inner.packets.is_empty() {
370            // Reinitialize the external C state to clear packet pointers that
371            // reference our `self.inner.packets` buffers, which are about to be
372            // freed.  Without this the caller could reuse the external
373            // OpusRepacketizer and dereference dangling pointers.
374            unsafe { opus_repacketizer_init(self.inner.rp.as_ptr()) };
375        }
376    }
377}
378
379impl Deref for RepacketizerRef<'_> {
380    type Target = Repacketizer;
381
382    fn deref(&self) -> &Self::Target {
383        &self.inner
384    }
385}
386
387#[cfg(all(test, opus_codec_rust_packet_ops))]
388mod tests {
389    use super::*;
390
391    #[test]
392    fn invalid_cached_layout_falls_back_to_c_emit() {
393        let packet = vec![0x78, b'a'];
394        let mut rp = Repacketizer::new().unwrap();
395        rp.push_owned(packet.clone()).unwrap();
396        rp.packets[0].layout = Err(Error::InvalidPacket);
397
398        let mut out = [0u8; 8];
399        let out_len = rp.emit(&mut out).unwrap();
400        assert_eq!(&out[..out_len], packet);
401
402        let out_len = rp.emit_range(0, 1, &mut out).unwrap();
403        assert_eq!(&out[..out_len], packet);
404    }
405
406    #[test]
407    fn unexpected_cached_layout_error_is_not_hidden() {
408        let packet = vec![0x78, b'a'];
409        let mut rp = Repacketizer::new().unwrap();
410        rp.push_owned(packet).unwrap();
411        rp.packets[0].layout = Err(Error::InternalError);
412
413        let mut out = [0u8; 8];
414        assert_eq!(rp.emit(&mut out).unwrap_err(), Error::InternalError);
415        assert_eq!(
416            rp.emit_range(0, 1, &mut out).unwrap_err(),
417            Error::InternalError
418        );
419    }
420}