opus-codec 0.2.0

Safe Rust bindings for the Opus audio codec
Documentation
use opus_codec::encoder::Encoder;
use opus_codec::error::{Error, Result};
use opus_codec::multistream::MultistreamEncoder;
use opus_codec::packet::{
    multistream_packet_pad, multistream_packet_unpad, packet_pad, packet_unpad,
};
use opus_codec::types::{Application, Channels, SampleRate};
use opus_codec::{OPUS_INTERNAL_ERROR, opus_multistream_packet_pad, opus_packet_pad};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};

const SINGLE_RANDOM_PACKET_CASES: usize = 24;
const SINGLE_RANDOM_PACKET_STEPS: usize = 8;
const EXTENSION_PACKET_CASES: usize = 24;
const EXTENSION_PACKET_STEPS: usize = 6;
const MULTISTREAM_PACKET_CASES: usize = 22;
const MULTISTREAM_PACKET_STEPS: usize = 8;
const RANDOMIZED_PAD_PARITY_TOTAL_CASES: usize = SINGLE_RANDOM_PACKET_CASES
    * SINGLE_RANDOM_PACKET_STEPS
    + EXTENSION_PACKET_CASES * EXTENSION_PACKET_STEPS
    + MULTISTREAM_PACKET_CASES * MULTISTREAM_PACKET_STEPS;
const _: [(); 512] = [(); RANDOMIZED_PAD_PARITY_TOTAL_CASES];

fn usize_below(rng: &mut impl Rng, upper_exclusive: usize) -> usize {
    if upper_exclusive <= 1 {
        0
    } else {
        (rng.next_u32() as usize) % upper_exclusive
    }
}

fn i16_sample(rng: &mut impl Rng) -> i16 {
    rng.next_u32() as i16
}

fn c_pad_result(code: i32) -> Result<()> {
    if code == 0 {
        Ok(())
    } else {
        Err(Error::from_code(code))
    }
}

fn assert_packet_pad_matches_opus(packet: &mut [u8], len: usize, new_len: usize) {
    let mut c_packet = packet.to_vec();
    let c_result = c_pad_result(unsafe {
        opus_packet_pad(
            c_packet.as_mut_ptr(),
            i32::try_from(len).unwrap(),
            i32::try_from(new_len).unwrap(),
        )
    });
    let rust_result = packet_pad(packet, len, new_len);
    assert_eq!(rust_result, c_result);
    if rust_result.is_ok() {
        assert_eq!(&packet[..new_len], &c_packet[..new_len]);
    }
}

fn assert_multistream_packet_pad_matches_opus(
    packet: &mut [u8],
    len: usize,
    new_len: usize,
    nb_streams: i32,
) {
    let mut c_packet = packet.to_vec();
    let c_result = c_pad_result(unsafe {
        opus_multistream_packet_pad(
            c_packet.as_mut_ptr(),
            i32::try_from(len).unwrap(),
            i32::try_from(new_len).unwrap(),
            nb_streams,
        )
    });
    let rust_result = multistream_packet_pad(packet, len, new_len, nb_streams);
    assert_eq!(rust_result, c_result);
    if rust_result.is_ok() {
        assert_eq!(&packet[..new_len], &c_packet[..new_len]);
    }
}

fn packet_with_valid_extensions() -> Vec<u8> {
    let payload = [
        0x7B, 0x41, 16, 0x00, 67, 7, b'a', b'b', b'c', b'd', b'e', b'f', b'g', 200, b'u', b'v',
        b'w', b'x', b'y', b'z',
    ];
    let mut packet = vec![0u8; 40];
    packet[..payload.len()].copy_from_slice(&payload);
    packet
}

fn packet_with_malformed_extension_len() -> Vec<u8> {
    let payload = [
        0x7B, 0x41, 16, 0x00, 67, 255, b'a', b'b', b'c', b'd', b'e', b'f', b'g', 200, b'u', b'v',
        b'w', b'x', b'y', b'z',
    ];
    let mut packet = vec![0u8; 40];
    packet[..payload.len()].copy_from_slice(&payload);
    packet
}

fn packet_with_short_id_two_extension_in_padding() -> Vec<u8> {
    let payload = [0x7B, 0x41, 2, 0x00, 0x05, b'a'];
    let mut packet = vec![0u8; 10];
    packet[..payload.len()].copy_from_slice(&payload);
    packet
}

#[test]
fn packet_pad_handles_repadding_large_packet() {
    let mut encoder = Encoder::new(SampleRate::Hz48000, Channels::Mono, Application::Voip).unwrap();
    let pcm = vec![0i16; 960];
    let mut encoded_packet = [0u8; 512];
    let encoded_len = encoder.encode(&pcm, &mut encoded_packet).unwrap();

    let mut packet = vec![0u8; 70_001];
    packet[..encoded_len].copy_from_slice(&encoded_packet[..encoded_len]);
    let original = packet[..encoded_len].to_vec();

    packet_pad(&mut packet, encoded_len, 70_000).unwrap();
    packet_pad(&mut packet, 70_000, 70_001).unwrap();
    assert_eq!(packet_unpad(&mut packet, 70_001).unwrap(), encoded_len);
    assert_eq!(&packet[..encoded_len], original.as_slice());
}

#[test]
fn packet_pad_rejects_zero_len_even_when_noop() {
    let mut packet = [0u8; 1];
    let err = packet_pad(&mut packet, 0, 0).unwrap_err();
    assert_eq!(err, Error::BadArg);
}

#[test]
fn packet_unpad_rejects_zero_len() {
    let mut packet = [0u8; 1];
    let err = packet_unpad(&mut packet, 0).unwrap_err();
    assert_eq!(err, Error::BadArg);
}

#[test]
fn packet_pad_matches_opus_for_existing_extensions() {
    let mut packet = packet_with_valid_extensions();
    assert_packet_pad_matches_opus(&mut packet, 20, 40);
}

#[test]
fn packet_pad_matches_opus_internal_error_for_malformed_extensions() {
    let mut packet = packet_with_malformed_extension_len();
    let mut c_packet = packet.clone();

    let c_result = unsafe { opus_packet_pad(c_packet.as_mut_ptr(), 20, 40) };
    assert_eq!(c_result, OPUS_INTERNAL_ERROR);
    assert_eq!(packet_pad(&mut packet, 20, 40), Err(Error::InternalError));
}

#[test]
fn packet_pad_matches_opus_for_short_id_two_extension_in_padding() {
    let mut packet = packet_with_short_id_two_extension_in_padding();
    assert_packet_pad_matches_opus(&mut packet, 6, 10);
}

#[cfg(opus_codec_frame_bounded_extensions)]
#[test]
fn packet_pad_matches_opus_for_trailing_repeat_with_empty_payloads() {
    let payload = [0x7B, 0x45, 2, b'a', b'b', b'c', b'd', b'e', 0x12, 0x05];
    let mut packet = vec![0u8; 32];
    packet[..payload.len()].copy_from_slice(&payload);
    let new_len = packet.len();
    assert_packet_pad_matches_opus(&mut packet, payload.len(), new_len);
}

#[test]
fn packet_pad_matches_opus_randomized_repadding() {
    let mut rng = StdRng::seed_from_u64(0x5EED_CAFE);
    let frame_sizes = [120usize, 240, 480, 960, 1920, 2880];

    for _ in 0..SINGLE_RANDOM_PACKET_CASES {
        let frame_size = frame_sizes[usize_below(&mut rng, frame_sizes.len())];
        let mut encoder =
            Encoder::new(SampleRate::Hz48000, Channels::Mono, Application::Voip).unwrap();
        let pcm: Vec<i16> = (0..frame_size).map(|_| i16_sample(&mut rng)).collect();
        let mut encoded_packet = [0u8; 1500];
        let encoded_len = encoder.encode(&pcm, &mut encoded_packet).unwrap();

        let mut packet = vec![0u8; encoded_len + 1024];
        packet[..encoded_len].copy_from_slice(&encoded_packet[..encoded_len]);
        let mut len = encoded_len;

        for _ in 0..SINGLE_RANDOM_PACKET_STEPS {
            let remaining = packet.len() - len;
            let growth = usize_below(&mut rng, remaining + 1);
            let new_len = len + growth;
            assert_packet_pad_matches_opus(&mut packet, len, new_len);
            len = new_len;
        }
    }
}

#[test]
fn packet_pad_matches_opus_randomized_existing_extensions() {
    let mut rng = StdRng::seed_from_u64(0xA11C_E55E);

    for _ in 0..EXTENSION_PACKET_CASES {
        let mut packet = vec![0u8; 160];
        let base = packet_with_valid_extensions();
        packet[..20].copy_from_slice(&base[..20]);
        let mut len = 20usize;

        for _ in 0..EXTENSION_PACKET_STEPS {
            let remaining = packet.len() - len;
            let growth = usize_below(&mut rng, remaining + 1);
            let new_len = len + growth;
            assert_packet_pad_matches_opus(&mut packet, len, new_len);
            len = new_len;
        }
    }
}

#[test]
fn multistream_pad_rejects_invalid_stream_count() {
    let mut packet = vec![0u8; 8];
    assert_eq!(
        multistream_packet_pad(&mut packet, 1, 2, 0).unwrap_err(),
        Error::BadArg
    );
    assert_eq!(
        multistream_packet_pad(&mut packet, 1, 2, i32::MIN).unwrap_err(),
        Error::BadArg
    );
}

#[test]
fn multistream_pad_rejects_zero_len_even_when_noop() {
    let mut packet = [0u8; 1];
    let err = multistream_packet_pad(&mut packet, 0, 0, 1).unwrap_err();
    assert_eq!(err, Error::BadArg);
}

#[test]
fn multistream_pad_matches_opus_for_existing_extensions() {
    let first_stream = [0x00, 0x01, 0x00];
    let second_stream = &packet_with_valid_extensions()[..20];
    let mut packet = vec![0u8; 50];
    packet[..first_stream.len()].copy_from_slice(&first_stream);
    packet[first_stream.len()..first_stream.len() + second_stream.len()]
        .copy_from_slice(second_stream);
    assert_multistream_packet_pad_matches_opus(&mut packet, 23, 50, 2);
}

#[test]
fn multistream_pad_matches_opus_when_streams_exhaust_packet() {
    let mut packet = [0x00, 0x00, 0x00];
    let mut c_packet = packet;
    let c_result =
        c_pad_result(unsafe { opus_multistream_packet_pad(c_packet.as_mut_ptr(), 2, 3, 2) });

    assert_eq!(c_result, Err(Error::BadArg));
    assert_eq!(multistream_packet_pad(&mut packet, 2, 3, 2), c_result);
}

#[test]
fn multistream_pad_matches_opus_internal_error_for_malformed_extensions() {
    let mut packet = packet_with_malformed_extension_len();
    let mut c_packet = packet.clone();

    let c_result = unsafe { opus_multistream_packet_pad(c_packet.as_mut_ptr(), 20, 40, 1) };
    assert_eq!(c_result, OPUS_INTERNAL_ERROR);
    assert_eq!(
        multistream_packet_pad(&mut packet, 20, 40, 1),
        Err(Error::InternalError)
    );
}

#[test]
fn multistream_unpad_rejects_invalid_stream_count() {
    let mut packet = vec![0u8; 8];
    assert_eq!(
        multistream_packet_unpad(&mut packet, 1, 0).unwrap_err(),
        Error::BadArg
    );
    assert_eq!(
        multistream_packet_unpad(&mut packet, 1, i32::MIN).unwrap_err(),
        Error::BadArg
    );
}

#[test]
fn multistream_unpad_rejects_zero_len() {
    let mut packet = [0u8; 1];
    let err = multistream_packet_unpad(&mut packet, 0, 1).unwrap_err();
    assert_eq!(err, Error::BadArg);
}

#[test]
fn multistream_packet_pad_matches_opus_randomized_repadding() {
    let mut rng = StdRng::seed_from_u64(0xC0DE_BAAD);
    let frame_sizes = [120usize, 240, 480, 960];

    for _ in 0..MULTISTREAM_PACKET_CASES {
        let frame_size = frame_sizes[usize_below(&mut rng, frame_sizes.len())];
        let channels = 6usize;
        let (mut encoder, _) = MultistreamEncoder::new_surround(
            SampleRate::Hz48000,
            channels as u8,
            1,
            Application::Audio,
        )
        .unwrap();
        let nb_streams = i32::from(encoder.streams());
        let pcm: Vec<i16> = (0..frame_size * channels)
            .map(|_| i16_sample(&mut rng))
            .collect();
        let mut packet = vec![0u8; 4096];
        let len = encoder.encode(&pcm, frame_size, &mut packet).unwrap();
        let mut len = len;

        for _ in 0..MULTISTREAM_PACKET_STEPS {
            let remaining = packet.len() - len;
            let growth = usize_below(&mut rng, remaining + 1);
            let new_len = len + growth;
            assert_multistream_packet_pad_matches_opus(&mut packet, len, new_len, nb_streams);
            len = new_len;
        }
    }
}