g729_sys/
lib.rs

1#![deny(missing_docs)]
2//! Safe wrappers around the bcg729 C library bundled in this crate.
3
4use std::ffi::c_void;
5
6#[allow(non_camel_case_types)]
7type int16_t = i16;
8#[allow(non_camel_case_types)]
9type uint8_t = u8;
10
11#[allow(non_camel_case_types)]
12#[repr(C)]
13struct bcg729EncoderChannelContextStruct(c_void);
14#[allow(non_camel_case_types)]
15#[repr(C)]
16struct bcg729DecoderChannelContextStruct(c_void);
17
18#[link(name = "bcg729", kind = "static")]
19unsafe extern "C" {
20    fn initBcg729EncoderChannel(enable_vad: uint8_t) -> *mut bcg729EncoderChannelContextStruct;
21    fn closeBcg729EncoderChannel(ctx: *mut bcg729EncoderChannelContextStruct);
22    fn bcg729Encoder(
23        ctx: *mut bcg729EncoderChannelContextStruct,
24        input_frame: *const int16_t,
25        bit_stream: *mut uint8_t,
26        bit_stream_length: *mut uint8_t,
27    );
28    fn bcg729GetRFC3389Payload(ctx: *mut bcg729EncoderChannelContextStruct, payload: *mut uint8_t);
29
30    fn initBcg729DecoderChannel() -> *mut bcg729DecoderChannelContextStruct;
31    fn closeBcg729DecoderChannel(ctx: *mut bcg729DecoderChannelContextStruct);
32    fn bcg729Decoder(
33        ctx: *mut bcg729DecoderChannelContextStruct,
34        bit_stream: *const uint8_t,
35        bit_stream_length: uint8_t,
36        frame_erasure_flag: uint8_t,
37        sid_frame_flag: uint8_t,
38        rfc3389_payload_flag: uint8_t,
39        signal: *mut int16_t,
40    );
41}
42
43/// One G.729 frame contains 80 16-bit PCM samples at 8 kHz.
44pub const FRAME_SAMPLES: usize = 80;
45/// Voice frame payload length in bytes (10 bytes = 80 bits).
46pub const VOICE_FRAME_BYTES: usize = 10;
47
48/// Encoder wrapper.
49pub struct Encoder {
50    ctx: *mut bcg729EncoderChannelContextStruct,
51}
52
53impl Encoder {
54    /// Create an encoder. Set enable_vad to true to enable VAD/DTX.
55    pub fn new(enable_vad: bool) -> anyhow::Result<Self> {
56        let ctx = unsafe { initBcg729EncoderChannel(if enable_vad { 1 } else { 0 }) };
57        if ctx.is_null() {
58            anyhow::bail!("initBcg729EncoderChannel returned null");
59        }
60        Ok(Self { ctx })
61    }
62
63    /// Encode one 10ms frame of 80 PCM samples. Returns the produced payload bytes.
64    /// With VAD, this may return 0 (no frame), 2 (SID), or 10 (voice) bytes.
65    pub fn encode(&mut self, input_80_samples: &[i16; FRAME_SAMPLES]) -> Vec<u8> {
66        let mut out = [0u8; VOICE_FRAME_BYTES];
67        let mut len: u8 = 0;
68        unsafe {
69            bcg729Encoder(
70                self.ctx,
71                input_80_samples.as_ptr(),
72                out.as_mut_ptr(),
73                &mut len,
74            );
75        }
76        out[..len as usize].to_vec()
77    }
78
79    /// If last frame was SID, get RFC3389 CN payload (11 bytes) for comfort noise.
80    pub fn rfc3389_payload(&mut self) -> [u8; 11] {
81        let mut p = [0u8; 11];
82        unsafe { bcg729GetRFC3389Payload(self.ctx, p.as_mut_ptr()) };
83        p
84    }
85}
86
87impl Drop for Encoder {
88    fn drop(&mut self) {
89        if !self.ctx.is_null() {
90            unsafe { closeBcg729EncoderChannel(self.ctx) };
91            self.ctx = std::ptr::null_mut();
92        }
93    }
94}
95
96/// Decoder wrapper.
97pub struct Decoder {
98    ctx: *mut bcg729DecoderChannelContextStruct,
99}
100
101impl Decoder {
102    /// Create a decoder.
103    pub fn new() -> anyhow::Result<Self> {
104        let ctx = unsafe { initBcg729DecoderChannel() };
105        if ctx.is_null() {
106            anyhow::bail!("initBcg729DecoderChannel returned null");
107        }
108        Ok(Self { ctx })
109    }
110
111    /// Decode a payload into one 80-sample frame.
112    /// Provide flags for erasure/SID/RFC3389 as appropriate.
113    pub fn decode(
114        &mut self,
115        payload: &[u8],
116        frame_erased: bool,
117        is_sid: bool,
118        rfc3389: bool,
119    ) -> [i16; FRAME_SAMPLES] {
120        let mut out = [0i16; FRAME_SAMPLES];
121        let len = payload.len() as u8;
122        unsafe {
123            bcg729Decoder(
124                self.ctx,
125                payload.as_ptr(),
126                len,
127                frame_erased as u8,
128                is_sid as u8,
129                rfc3389 as u8,
130                out.as_mut_ptr(),
131            );
132        }
133        out
134    }
135}
136
137impl Drop for Decoder {
138    fn drop(&mut self) {
139        if !self.ctx.is_null() {
140            unsafe { closeBcg729DecoderChannel(self.ctx) };
141            self.ctx = std::ptr::null_mut();
142        }
143    }
144}