opus_codec/
encoder.rs

1//! Opus encoder implementation with safe wrappers
2
3use crate::bindings::{
4    OPUS_AUTO, OPUS_BANDWIDTH_FULLBAND, OPUS_BITRATE_MAX, OPUS_GET_BANDWIDTH_REQUEST,
5    OPUS_GET_BITRATE_REQUEST, OPUS_GET_COMPLEXITY_REQUEST, OPUS_GET_DTX_REQUEST,
6    OPUS_GET_EXPERT_FRAME_DURATION_REQUEST, OPUS_GET_FINAL_RANGE_REQUEST,
7    OPUS_GET_FORCE_CHANNELS_REQUEST, OPUS_GET_IN_DTX_REQUEST, OPUS_GET_INBAND_FEC_REQUEST,
8    OPUS_GET_LOOKAHEAD_REQUEST, OPUS_GET_LSB_DEPTH_REQUEST, OPUS_GET_MAX_BANDWIDTH_REQUEST,
9    OPUS_GET_PACKET_LOSS_PERC_REQUEST, OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST,
10    OPUS_GET_PREDICTION_DISABLED_REQUEST, OPUS_GET_SIGNAL_REQUEST, OPUS_GET_VBR_CONSTRAINT_REQUEST,
11    OPUS_GET_VBR_REQUEST, OPUS_SET_BANDWIDTH_REQUEST, OPUS_SET_BITRATE_REQUEST,
12    OPUS_SET_COMPLEXITY_REQUEST, OPUS_SET_DTX_REQUEST, OPUS_SET_EXPERT_FRAME_DURATION_REQUEST,
13    OPUS_SET_FORCE_CHANNELS_REQUEST, OPUS_SET_INBAND_FEC_REQUEST, OPUS_SET_LSB_DEPTH_REQUEST,
14    OPUS_SET_MAX_BANDWIDTH_REQUEST, OPUS_SET_PACKET_LOSS_PERC_REQUEST,
15    OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, OPUS_SET_PREDICTION_DISABLED_REQUEST,
16    OPUS_SET_SIGNAL_REQUEST, OPUS_SET_VBR_CONSTRAINT_REQUEST, OPUS_SET_VBR_REQUEST, OpusEncoder,
17    opus_encode, opus_encode_float, opus_encoder_create, opus_encoder_ctl, opus_encoder_destroy,
18};
19use crate::constants::max_frame_samples_for;
20use crate::error::{Error, Result};
21use crate::types::{
22    Application, Bandwidth, Bitrate, Channels, Complexity, ExpertFrameDuration, SampleRate, Signal,
23};
24
25/// Safe wrapper around a libopus `OpusEncoder`.
26pub struct Encoder {
27    raw: *mut OpusEncoder,
28    sample_rate: SampleRate,
29    channels: Channels,
30}
31
32unsafe impl Send for Encoder {}
33unsafe impl Sync for Encoder {}
34
35impl Encoder {
36    /// Create a new encoder.
37    ///
38    /// # Errors
39    /// Returns an error if allocation fails or arguments are invalid.
40    pub fn new(
41        sample_rate: SampleRate,
42        channels: Channels,
43        application: Application,
44    ) -> Result<Self> {
45        // Validate sample rate
46        if !sample_rate.is_valid() {
47            return Err(Error::BadArg);
48        }
49
50        let mut error = 0i32;
51        let encoder = unsafe {
52            opus_encoder_create(
53                sample_rate.as_i32(),
54                channels.as_i32(),
55                application as i32,
56                std::ptr::addr_of_mut!(error),
57            )
58        };
59
60        if error != 0 {
61            return Err(Error::from_code(error));
62        }
63
64        if encoder.is_null() {
65            return Err(Error::AllocFail);
66        }
67
68        Ok(Self {
69            raw: encoder,
70            sample_rate,
71            channels,
72        })
73    }
74
75    /// Encode 16-bit PCM into an Opus packet.
76    ///
77    /// # Errors
78    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for
79    /// invalid buffer sizes or frame size, or a mapped libopus error.
80    pub fn encode(&mut self, input: &[i16], output: &mut [u8]) -> Result<usize> {
81        if self.raw.is_null() {
82            return Err(Error::InvalidState);
83        }
84
85        // Validate input buffer size
86        if input.is_empty() {
87            return Err(Error::BadArg);
88        }
89
90        // Ensure input buffer is properly sized for the number of channels
91        if !input.len().is_multiple_of(self.channels.as_usize()) {
92            return Err(Error::BadArg);
93        }
94
95        let frame_size = input.len() / self.channels.as_usize();
96        // Validate frame size is within Opus limits for the configured sample rate
97        if frame_size == 0 || frame_size > max_frame_samples_for(self.sample_rate) {
98            return Err(Error::BadArg);
99        }
100
101        // Validate output buffer size
102        if output.is_empty() {
103            return Err(Error::BadArg);
104        }
105        if output.len() > i32::MAX as usize {
106            return Err(Error::BadArg);
107        }
108
109        let frame_size_i32 = i32::try_from(frame_size).map_err(|_| Error::BadArg)?;
110        let out_len_i32 = i32::try_from(output.len()).map_err(|_| Error::BadArg)?;
111        let result = unsafe {
112            opus_encode(
113                self.raw,
114                input.as_ptr(),
115                frame_size_i32,
116                output.as_mut_ptr(),
117                out_len_i32,
118            )
119        };
120
121        if result < 0 {
122            return Err(Error::from_code(result));
123        }
124
125        usize::try_from(result).map_err(|_| Error::InternalError)
126    }
127
128    /// Encode 16-bit PCM, capping output to `max_data_bytes`.
129    ///
130    /// Note: This does not itself enable FEC; use `set_inband_fec(true)` and
131    /// `set_packet_loss_perc(…)` to actually make the encoder produce FEC.
132    ///
133    /// # Errors
134    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for
135    /// invalid buffer sizes or frame size, or a mapped libopus error.
136    pub fn encode_limited(
137        &mut self,
138        input: &[i16],
139        output: &mut [u8],
140        max_data_bytes: usize,
141    ) -> Result<usize> {
142        if self.raw.is_null() {
143            return Err(Error::InvalidState);
144        }
145
146        // Validate input buffer size
147        if input.is_empty() {
148            return Err(Error::BadArg);
149        }
150
151        // Ensure input buffer is properly sized for the number of channels
152        if !input.len().is_multiple_of(self.channels.as_usize()) {
153            return Err(Error::BadArg);
154        }
155
156        let frame_size = input.len() / self.channels.as_usize();
157        // Validate frame size is within Opus limits for the configured sample rate
158        if frame_size == 0 || frame_size > max_frame_samples_for(self.sample_rate) {
159            return Err(Error::BadArg);
160        }
161
162        // Validate output buffer size
163        if output.is_empty() {
164            return Err(Error::BadArg);
165        }
166        if output.len() > i32::MAX as usize {
167            return Err(Error::BadArg);
168        }
169        // Validate max_data_bytes parameter
170        if max_data_bytes == 0 || max_data_bytes > output.len() {
171            return Err(Error::BadArg);
172        }
173
174        let frame_size_i32 = i32::try_from(frame_size).map_err(|_| Error::BadArg)?;
175        let max_bytes_i32 = i32::try_from(max_data_bytes).map_err(|_| Error::BadArg)?;
176        let result = unsafe {
177            opus_encode(
178                self.raw,
179                input.as_ptr(),
180                frame_size_i32,
181                output.as_mut_ptr(),
182                max_bytes_i32,
183            )
184        };
185
186        if result < 0 {
187            return Err(Error::from_code(result));
188        }
189
190        usize::try_from(result).map_err(|_| Error::InternalError)
191    }
192
193    /// Deprecated alias for `encode_limited` (does not itself enable FEC).
194    ///
195    /// # Errors
196    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for
197    /// invalid buffer sizes or frame size, or a mapped libopus error.
198    #[deprecated(
199        note = "Renamed to encode_limited; enabling FEC requires set_inband_fec + set_packet_loss_perc"
200    )]
201    pub fn encode_with_fec(
202        &mut self,
203        input: &[i16],
204        output: &mut [u8],
205        max_data_bytes: usize,
206    ) -> Result<usize> {
207        self.encode_limited(input, output, max_data_bytes)
208    }
209
210    /// Encode f32 PCM into an Opus packet.
211    ///
212    /// # Errors
213    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for
214    /// invalid buffer sizes or frame size, or a mapped libopus error.
215    pub fn encode_float(&mut self, input: &[f32], output: &mut [u8]) -> Result<usize> {
216        if self.raw.is_null() {
217            return Err(Error::InvalidState);
218        }
219        if input.is_empty() {
220            return Err(Error::BadArg);
221        }
222        if !input.len().is_multiple_of(self.channels.as_usize()) {
223            return Err(Error::BadArg);
224        }
225        let frame_size = input.len() / self.channels.as_usize();
226        if frame_size == 0 || frame_size > max_frame_samples_for(self.sample_rate) {
227            return Err(Error::BadArg);
228        }
229        if output.is_empty() || output.len() > i32::MAX as usize {
230            return Err(Error::BadArg);
231        }
232        let frame_i32 = i32::try_from(frame_size).map_err(|_| Error::BadArg)?;
233        let out_len_i32 = i32::try_from(output.len()).map_err(|_| Error::BadArg)?;
234        let n = unsafe {
235            opus_encode_float(
236                self.raw,
237                input.as_ptr(),
238                frame_i32,
239                output.as_mut_ptr(),
240                out_len_i32,
241            )
242        };
243        if n < 0 {
244            return Err(Error::from_code(n));
245        }
246        usize::try_from(n).map_err(|_| Error::InternalError)
247    }
248
249    // ===== Common encoder CTLs =====
250
251    /// Enable/disable in-band FEC generation (decoder can recover from losses).
252    ///
253    /// # Errors
254    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
255    pub fn set_inband_fec(&mut self, enabled: bool) -> Result<()> {
256        self.simple_ctl(OPUS_SET_INBAND_FEC_REQUEST as i32, i32::from(enabled))
257    }
258    /// Query in-band FEC setting.
259    ///
260    /// # Errors
261    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
262    pub fn inband_fec(&mut self) -> Result<bool> {
263        self.get_bool_ctl(OPUS_GET_INBAND_FEC_REQUEST as i32)
264    }
265
266    /// Hint expected packet loss percentage [0..=100].
267    ///
268    /// # Errors
269    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for out-of-range values,
270    /// or a mapped libopus error.
271    pub fn set_packet_loss_perc(&mut self, perc: i32) -> Result<()> {
272        if !(0..=100).contains(&perc) {
273            return Err(Error::BadArg);
274        }
275        self.simple_ctl(OPUS_SET_PACKET_LOSS_PERC_REQUEST as i32, perc)
276    }
277    /// Query packet loss percentage hint.
278    ///
279    /// # Errors
280    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
281    pub fn packet_loss_perc(&mut self) -> Result<i32> {
282        self.get_int_ctl(OPUS_GET_PACKET_LOSS_PERC_REQUEST as i32)
283    }
284
285    /// Enable/disable DTX (discontinuous transmission).
286    ///
287    /// # Errors
288    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
289    pub fn set_dtx(&mut self, enabled: bool) -> Result<()> {
290        self.simple_ctl(OPUS_SET_DTX_REQUEST as i32, i32::from(enabled))
291    }
292    /// Query DTX setting.
293    ///
294    /// # Errors
295    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
296    pub fn dtx(&mut self) -> Result<bool> {
297        self.get_bool_ctl(OPUS_GET_DTX_REQUEST as i32)
298    }
299    /// Returns true if encoder is currently in DTX.
300    ///
301    /// # Errors
302    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
303    pub fn in_dtx(&mut self) -> Result<bool> {
304        self.get_bool_ctl(OPUS_GET_IN_DTX_REQUEST as i32)
305    }
306
307    /// Constrain VBR to reduce instant bitrate swings.
308    ///
309    /// # Errors
310    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
311    pub fn set_vbr_constraint(&mut self, constrained: bool) -> Result<()> {
312        self.simple_ctl(
313            OPUS_SET_VBR_CONSTRAINT_REQUEST as i32,
314            i32::from(constrained),
315        )
316    }
317    /// Query VBR constraint setting.
318    ///
319    /// # Errors
320    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
321    pub fn vbr_constraint(&mut self) -> Result<bool> {
322        self.get_bool_ctl(OPUS_GET_VBR_CONSTRAINT_REQUEST as i32)
323    }
324
325    /// Set maximum audio bandwidth the encoder may use.
326    ///
327    /// # Errors
328    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
329    pub fn set_max_bandwidth(&mut self, bw: Bandwidth) -> Result<()> {
330        self.simple_ctl(OPUS_SET_MAX_BANDWIDTH_REQUEST as i32, bw as i32)
331    }
332    /// Query max bandwidth.
333    ///
334    /// # Errors
335    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
336    pub fn max_bandwidth(&mut self) -> Result<Bandwidth> {
337        self.get_bandwidth_ctl(OPUS_GET_MAX_BANDWIDTH_REQUEST as i32)
338    }
339
340    /// Force a specific bandwidth (overrides automatic).
341    ///
342    /// # Errors
343    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
344    pub fn set_bandwidth(&mut self, bw: Bandwidth) -> Result<()> {
345        self.simple_ctl(OPUS_SET_BANDWIDTH_REQUEST as i32, bw as i32)
346    }
347    /// Query current forced bandwidth.
348    ///
349    /// # Errors
350    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
351    pub fn bandwidth(&mut self) -> Result<Bandwidth> {
352        self.get_bandwidth_ctl(OPUS_GET_BANDWIDTH_REQUEST as i32)
353    }
354
355    /// Force mono/stereo output, or None for automatic.
356    ///
357    /// # Errors
358    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
359    pub fn set_force_channels(&mut self, channels: Option<Channels>) -> Result<()> {
360        let val = match channels {
361            Some(Channels::Mono) => 1,
362            Some(Channels::Stereo) => 2,
363            None => crate::bindings::OPUS_AUTO,
364        };
365        self.simple_ctl(OPUS_SET_FORCE_CHANNELS_REQUEST as i32, val)
366    }
367    /// Query forced channels.
368    ///
369    /// # Errors
370    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
371    pub fn force_channels(&mut self) -> Result<Option<Channels>> {
372        let v = self.get_int_ctl(OPUS_GET_FORCE_CHANNELS_REQUEST as i32)?;
373        Ok(match v {
374            1 => Some(Channels::Mono),
375            2 => Some(Channels::Stereo),
376            _ => None,
377        })
378    }
379
380    /// Hint content type (voice or music).
381    ///
382    /// # Errors
383    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
384    pub fn set_signal(&mut self, signal: Signal) -> Result<()> {
385        self.simple_ctl(OPUS_SET_SIGNAL_REQUEST as i32, signal as i32)
386    }
387    /// Query current signal hint.
388    ///
389    /// # Errors
390    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
391    pub fn signal(&mut self) -> Result<Signal> {
392        let v = self.get_int_ctl(OPUS_GET_SIGNAL_REQUEST as i32)?;
393        match v {
394            x if x == crate::bindings::OPUS_SIGNAL_VOICE as i32 => Ok(Signal::Voice),
395            x if x == crate::bindings::OPUS_SIGNAL_MUSIC as i32 => Ok(Signal::Music),
396            _ => Err(Error::InternalError),
397        }
398    }
399
400    /// Encoder algorithmic lookahead (in samples at 48 kHz domain).
401    ///
402    /// # Errors
403    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
404    pub fn lookahead(&mut self) -> Result<i32> {
405        self.get_int_ctl(OPUS_GET_LOOKAHEAD_REQUEST as i32)
406    }
407    /// Final RNG state from the last encode (debugging/bitstream id).
408    ///
409    /// # Errors
410    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
411    pub fn final_range(&mut self) -> Result<u32> {
412        let mut val: u32 = 0;
413        let r =
414            unsafe { opus_encoder_ctl(self.raw, OPUS_GET_FINAL_RANGE_REQUEST as i32, &mut val) };
415        if r != 0 {
416            return Err(Error::from_code(r));
417        }
418        Ok(val)
419    }
420
421    /// Set input LSB depth (typically 16-24 bits).
422    ///
423    /// # Errors
424    /// Returns [`Error::InvalidState`] if the encoder is invalid, [`Error::BadArg`] for an
425    /// out-of-range bit depth, or a mapped libopus error.
426    pub fn set_lsb_depth(&mut self, bits: i32) -> Result<()> {
427        if !(8..=24).contains(&bits) {
428            return Err(Error::BadArg);
429        }
430        self.simple_ctl(OPUS_SET_LSB_DEPTH_REQUEST as i32, bits)
431    }
432    /// Query input LSB depth.
433    ///
434    /// # Errors
435    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
436    pub fn lsb_depth(&mut self) -> Result<i32> {
437        self.get_int_ctl(OPUS_GET_LSB_DEPTH_REQUEST as i32)
438    }
439
440    /// Set expert frame duration choice.
441    ///
442    /// # Errors
443    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
444    pub fn set_expert_frame_duration(&mut self, dur: ExpertFrameDuration) -> Result<()> {
445        self.simple_ctl(OPUS_SET_EXPERT_FRAME_DURATION_REQUEST as i32, dur as i32)
446    }
447    /// Query expert frame duration.
448    ///
449    /// # Errors
450    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
451    pub fn expert_frame_duration(&mut self) -> Result<ExpertFrameDuration> {
452        let v = self.get_int_ctl(OPUS_GET_EXPERT_FRAME_DURATION_REQUEST as i32)?;
453        let vu = u32::try_from(v).map_err(|_| Error::InternalError)?;
454        Ok(match vu {
455            x if x == crate::bindings::OPUS_FRAMESIZE_2_5_MS => ExpertFrameDuration::Ms2_5,
456            x if x == crate::bindings::OPUS_FRAMESIZE_5_MS => ExpertFrameDuration::Ms5,
457            x if x == crate::bindings::OPUS_FRAMESIZE_10_MS => ExpertFrameDuration::Ms10,
458            x if x == crate::bindings::OPUS_FRAMESIZE_20_MS => ExpertFrameDuration::Ms20,
459            x if x == crate::bindings::OPUS_FRAMESIZE_40_MS => ExpertFrameDuration::Ms40,
460            x if x == crate::bindings::OPUS_FRAMESIZE_60_MS => ExpertFrameDuration::Ms60,
461            x if x == crate::bindings::OPUS_FRAMESIZE_80_MS => ExpertFrameDuration::Ms80,
462            x if x == crate::bindings::OPUS_FRAMESIZE_100_MS => ExpertFrameDuration::Ms100,
463            _ => ExpertFrameDuration::Ms120,
464        })
465    }
466
467    /// Disable/enable inter-frame prediction (expert option).
468    ///
469    /// # Errors
470    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
471    pub fn set_prediction_disabled(&mut self, disabled: bool) -> Result<()> {
472        self.simple_ctl(
473            OPUS_SET_PREDICTION_DISABLED_REQUEST as i32,
474            i32::from(disabled),
475        )
476    }
477    /// Query prediction disabled flag.
478    ///
479    /// # Errors
480    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
481    pub fn prediction_disabled(&mut self) -> Result<bool> {
482        self.get_bool_ctl(OPUS_GET_PREDICTION_DISABLED_REQUEST as i32)
483    }
484
485    /// Disable/enable phase inversion (stereo decorrelation) in CELT (expert option).
486    ///
487    /// # Errors
488    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
489    pub fn set_phase_inversion_disabled(&mut self, disabled: bool) -> Result<()> {
490        self.simple_ctl(
491            OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST as i32,
492            i32::from(disabled),
493        )
494    }
495    /// Query phase inversion disabled flag.
496    ///
497    /// # Errors
498    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
499    pub fn phase_inversion_disabled(&mut self) -> Result<bool> {
500        self.get_bool_ctl(OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST as i32)
501    }
502
503    // --- internal helpers ---
504    fn simple_ctl(&mut self, req: i32, val: i32) -> Result<()> {
505        if self.raw.is_null() {
506            return Err(Error::InvalidState);
507        }
508        let r = unsafe { opus_encoder_ctl(self.raw, req, val) };
509        if r != 0 {
510            return Err(Error::from_code(r));
511        }
512        Ok(())
513    }
514    fn get_bool_ctl(&mut self, req: i32) -> Result<bool> {
515        Ok(self.get_int_ctl(req)? != 0)
516    }
517    fn get_int_ctl(&mut self, req: i32) -> Result<i32> {
518        if self.raw.is_null() {
519            return Err(Error::InvalidState);
520        }
521        let mut v: i32 = 0;
522        let r = unsafe { opus_encoder_ctl(self.raw, req, &mut v) };
523        if r != 0 {
524            return Err(Error::from_code(r));
525        }
526        Ok(v)
527    }
528    fn get_bandwidth_ctl(&mut self, req: i32) -> Result<Bandwidth> {
529        let v = self.get_int_ctl(req)?;
530        let vu = u32::try_from(v).map_err(|_| Error::InternalError)?;
531        match vu {
532            x if x == crate::bindings::OPUS_BANDWIDTH_NARROWBAND => Ok(Bandwidth::Narrowband),
533            x if x == crate::bindings::OPUS_BANDWIDTH_MEDIUMBAND => Ok(Bandwidth::Mediumband),
534            x if x == crate::bindings::OPUS_BANDWIDTH_WIDEBAND => Ok(Bandwidth::Wideband),
535            x if x == crate::bindings::OPUS_BANDWIDTH_SUPERWIDEBAND => Ok(Bandwidth::SuperWideband),
536            x if x == OPUS_BANDWIDTH_FULLBAND => Ok(Bandwidth::Fullband),
537            _ => Err(Error::InternalError),
538        }
539    }
540
541    /// Set target bitrate.
542    ///
543    /// # Errors
544    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
545    pub fn set_bitrate(&mut self, bitrate: Bitrate) -> Result<()> {
546        if self.raw.is_null() {
547            return Err(Error::InvalidState);
548        }
549
550        let result =
551            unsafe { opus_encoder_ctl(self.raw, OPUS_SET_BITRATE_REQUEST as i32, bitrate.value()) };
552
553        if result != 0 {
554            return Err(Error::from_code(result));
555        }
556
557        Ok(())
558    }
559
560    /// Query current bitrate.
561    ///
562    /// # Errors
563    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
564    pub fn bitrate(&mut self) -> Result<Bitrate> {
565        if self.raw.is_null() {
566            return Err(Error::InvalidState);
567        }
568
569        let mut bitrate = 0i32;
570        let result =
571            unsafe { opus_encoder_ctl(self.raw, OPUS_GET_BITRATE_REQUEST as i32, &mut bitrate) };
572
573        if result != 0 {
574            return Err(Error::from_code(result));
575        }
576
577        match bitrate {
578            OPUS_AUTO => Ok(Bitrate::Auto),
579            OPUS_BITRATE_MAX => Ok(Bitrate::Max),
580            bps => Ok(Bitrate::Custom(bps)),
581        }
582    }
583
584    /// Set encoder complexity [0..=10].
585    ///
586    /// # Errors
587    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
588    pub fn set_complexity(&mut self, complexity: Complexity) -> Result<()> {
589        if self.raw.is_null() {
590            return Err(Error::InvalidState);
591        }
592
593        let result = unsafe {
594            opus_encoder_ctl(
595                self.raw,
596                OPUS_SET_COMPLEXITY_REQUEST as i32,
597                complexity.value() as i32,
598            )
599        };
600
601        if result != 0 {
602            return Err(Error::from_code(result));
603        }
604
605        Ok(())
606    }
607
608    /// Query encoder complexity.
609    ///
610    /// # Errors
611    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
612    pub fn complexity(&mut self) -> Result<Complexity> {
613        if self.raw.is_null() {
614            return Err(Error::InvalidState);
615        }
616
617        let mut complexity = 0i32;
618        let result = unsafe {
619            opus_encoder_ctl(
620                self.raw,
621                OPUS_GET_COMPLEXITY_REQUEST as i32,
622                &mut complexity,
623            )
624        };
625
626        if result != 0 {
627            return Err(Error::from_code(result));
628        }
629
630        Ok(Complexity::new(
631            u32::try_from(complexity).map_err(|_| Error::InternalError)?,
632        ))
633    }
634
635    /// Enable or disable VBR.
636    ///
637    /// # Errors
638    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
639    pub fn set_vbr(&mut self, enabled: bool) -> Result<()> {
640        if self.raw.is_null() {
641            return Err(Error::InvalidState);
642        }
643
644        let vbr = i32::from(enabled);
645        let result = unsafe { opus_encoder_ctl(self.raw, OPUS_SET_VBR_REQUEST as i32, vbr) };
646
647        if result != 0 {
648            return Err(Error::from_code(result));
649        }
650
651        Ok(())
652    }
653
654    /// Query VBR status.
655    ///
656    /// # Errors
657    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
658    pub fn vbr(&mut self) -> Result<bool> {
659        if self.raw.is_null() {
660            return Err(Error::InvalidState);
661        }
662
663        let mut vbr = 0i32;
664        let result = unsafe { opus_encoder_ctl(self.raw, OPUS_GET_VBR_REQUEST as i32, &mut vbr) };
665
666        if result != 0 {
667            return Err(Error::from_code(result));
668        }
669
670        Ok(vbr != 0)
671    }
672
673    /// The encoder's configured sample rate.
674    #[must_use]
675    pub const fn sample_rate(&self) -> SampleRate {
676        self.sample_rate
677    }
678
679    /// The encoder's channel configuration.
680    #[must_use]
681    pub const fn channels(&self) -> Channels {
682        self.channels
683    }
684
685    /// Reset the encoder to its initial state (same config, cleared history).
686    ///
687    /// # Errors
688    /// Returns [`Error::InvalidState`] if the encoder is invalid, or a mapped libopus error.
689    pub fn reset(&mut self) -> Result<()> {
690        if self.raw.is_null() {
691            return Err(Error::InvalidState);
692        }
693        let r = unsafe { opus_encoder_ctl(self.raw, crate::bindings::OPUS_RESET_STATE as i32) };
694        if r != 0 {
695            return Err(Error::from_code(r));
696        }
697        Ok(())
698    }
699}
700
701impl Drop for Encoder {
702    fn drop(&mut self) {
703        unsafe {
704            opus_encoder_destroy(self.raw);
705        }
706    }
707}