├── .github ├── dependabot.yml └── workflows │ └── opus.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── decode.rs └── encode.rs ├── opus-sys ├── .gitignore ├── Cargo.toml ├── LICENSE ├── build.rs ├── data │ └── opus.h ├── examples │ ├── decode.rs │ └── encode.rs └── src │ └── lib.rs └── src ├── common.rs ├── decoder.rs ├── encoder.rs └── lib.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/opus.yml: -------------------------------------------------------------------------------- 1 | name: opus 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | linux-tests: 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Install libopus 15 | run: | 16 | sudo apt-get install libopus-dev 17 | 18 | - name: Run tests 19 | run: | 20 | cargo test --workspace --all-targets --all-features 21 | cargo test --workspace --all-targets --no-default-features 22 | 23 | windows-tests-gnu: 24 | 25 | runs-on: windows-latest 26 | 27 | env: 28 | MSYSTEM: MINGW64 29 | MSYS2_PATH_TYPE: inherit 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - name: Install Rust Windows gnu 35 | uses: actions-rs/toolchain@v1 36 | with: 37 | profile: minimal 38 | toolchain: stable-x86_64-pc-windows-gnu 39 | override: true 40 | 41 | - name: Install msys2 packages 42 | uses: msys2/setup-msys2@v2 43 | with: 44 | msystem: MINGW64 45 | install: mingw-w64-x86_64-pkgconf mingw-w64-x86_64-opus 46 | update: true 47 | 48 | - name: Run tests 49 | shell: msys2 {0} 50 | run: | 51 | cargo test --workspace --all-targets --all-features 52 | cargo test --workspace --all-targets --no-default-features 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libopus" 3 | version = "0.1.0" 4 | authors = ["Luca Barbato "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "libopus bindings" 8 | repository = "https://github.com/rust-av/opus-rs" 9 | readme = "README.md" 10 | keywords = ["libopus","opus"] 11 | 12 | [features] 13 | default = ["codec-trait"] 14 | codec-trait = ["av-codec", "av-bitstream", "av-data"] 15 | 16 | [dependencies] 17 | opus-sys = { version = "0.1.0", path = "opus-sys" } 18 | av-data = { version = "0.4.1", optional = true } 19 | av-bitstream = { version = "0.2.0", optional = true } 20 | av-codec = { version = "0.3.0", optional = true } 21 | 22 | [dev-dependencies] 23 | structopt = "0.3" 24 | av-bitstream = "0.2.0" 25 | 26 | [workspace] 27 | members = ["opus-sys"] 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Luca Barbato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libopus bindings 2 | 3 | [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 4 | [![Actions Status](https://github.com/rust-av/opus-rs/workflows/opus/badge.svg)](https://github.com/rust-av/opus-rs/actions) 5 | 6 | 7 | It is a simple [binding][1] and safe abstraction over [libopus][2]. 8 | 9 | ## Building 10 | 11 | To build the code, always have a look at [CI](https://github.com/rust-av/opus-rs/blob/master/.github/workflows/opus.yml) to install the necessary dependencies on all 12 | supported operating systems. 13 | 14 | 15 | ## Building with vcpkg for Windows x64 16 | 17 | To build with [vcpkg](https://vcpkg.io/en/index.html), you need to follow these 18 | steps: 19 | 20 | 1. Install `pkg-config` through [chocolatey](https://chocolatey.org/) 21 | 22 | choco install pkgconfiglite 23 | 24 | 2. Install `opus` 25 | 26 | vcpkg install opus:x64-windows 27 | 28 | 3. Add to the `PKG_CONFIG_PATH` environment variable the path `$VCPKG_INSTALLATION_ROOT\installed\x64-windows\lib\pkgconfig` 29 | 30 | 4. Build code 31 | 32 | cargo build --workspace 33 | 34 | To speed up the computation, you can build your packages only in `Release` mode 35 | adding the `set(VCPKG_BUILD_TYPE release)` line to the 36 | `$VCPKG_INSTALLATION_ROOT\triplets\x64-windows.cmake` file. 37 | 38 | Building for Windows x86 is the same, just replace `x64` with `x86` in the 39 | steps above. 40 | 41 | ## TODO 42 | - [ ] Simple bindings 43 | - [ ] Safe abstraction 44 | - [ ] Examples 45 | 46 | [1]: https://github.com/servo/rust-bindgen 47 | [2]: https://opus-codec.org/ 48 | -------------------------------------------------------------------------------- /examples/decode.rs: -------------------------------------------------------------------------------- 1 | use libopus::decoder::*; 2 | 3 | use structopt::StructOpt; 4 | 5 | use std::fs::File; 6 | use std::io::prelude::*; 7 | use std::path::PathBuf; 8 | 9 | use av_bitstream::byteread::get_i32b; 10 | 11 | #[derive(Debug, StructOpt)] 12 | #[structopt(name = "decoder", about = "Opus decoding example")] 13 | struct DecodingOpts { 14 | /// Input file 15 | #[structopt(parse(from_os_str))] 16 | input: PathBuf, 17 | /// Output file 18 | #[structopt(parse(from_os_str))] 19 | output: PathBuf, 20 | /// Sampling rate, in Hz 21 | #[structopt(default_value = "48000")] 22 | sampling_rate: usize, 23 | /// Channels, either 1 or 2 24 | #[structopt(default_value = "1")] 25 | channels: usize, 26 | /*/// Number of seconds to decode 27 | #[structopt(default_value = "10")] 28 | seconds: i32,*/ 29 | } 30 | 31 | trait Decode { 32 | fn get_decoder(&self) -> Option; 33 | } 34 | 35 | impl Decode for DecodingOpts { 36 | fn get_decoder(&self) -> Option { 37 | if self.channels > 2 { 38 | unimplemented!("Multichannel support missing"); 39 | } 40 | 41 | let coupled_streams = if self.channels > 1 { 1 } else { 0 }; 42 | Decoder::create( 43 | self.sampling_rate, 44 | self.channels, 45 | 1, 46 | coupled_streams, 47 | &[0u8, 1u8], 48 | ) 49 | .ok() 50 | } 51 | } 52 | 53 | use std::slice; 54 | 55 | fn main() { 56 | let dec_opt = DecodingOpts::from_args(); 57 | 58 | let mut dec = dec_opt.get_decoder().unwrap(); 59 | 60 | let mut in_f = File::open(dec_opt.input).unwrap(); 61 | let mut out_f = File::create(dec_opt.output).unwrap(); 62 | 63 | let max_packet = 1500; 64 | let max_frame = 48000 * 2; 65 | let max_frame_samples = max_frame * dec_opt.channels as usize; 66 | 67 | let mut pkt = Vec::with_capacity(max_packet); 68 | let mut samples = Vec::with_capacity(max_frame_samples); 69 | 70 | pkt.resize(max_packet, 0u8); 71 | samples.resize(max_frame_samples, 0i16); 72 | 73 | let mut buf = [0u8; 4]; 74 | while in_f.read_exact(&mut buf).is_ok() { 75 | let len = get_i32b(&buf) as usize; 76 | if len > max_packet { 77 | panic!("Impossible packet size {}", len); 78 | } 79 | 80 | in_f.read_exact(&mut buf).expect("End of file"); 81 | in_f.read_exact(&mut pkt[..len]).expect("End of file"); 82 | 83 | if let Ok(ret) = dec.decode(&pkt[..len], &mut samples[..], false) { 84 | // Write the actual group of samples 85 | let out = 86 | unsafe { slice::from_raw_parts(samples.as_ptr() as *const u8, ret as usize * 2) }; 87 | out_f.write_all(out).unwrap(); 88 | } else { 89 | panic!("Cannot decode"); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /examples/encode.rs: -------------------------------------------------------------------------------- 1 | use libopus::encoder::*; 2 | 3 | use structopt::StructOpt; 4 | 5 | use std::io::prelude::*; 6 | use std::fs::File; 7 | use std::path::PathBuf; 8 | 9 | use av_bitstream::bytewrite::put_i32b; 10 | 11 | #[derive(Debug, StructOpt)] 12 | #[structopt(name = "encoder", about = "Opus encoding example")] 13 | struct EncodingOpts { 14 | /// Input file 15 | #[structopt(parse(from_os_str))] 16 | input: PathBuf, 17 | /// Output file 18 | #[structopt(parse(from_os_str))] 19 | output: PathBuf, 20 | /// Sampling rate, in Hz 21 | #[structopt(default_value = "48000")] 22 | sampling_rate: usize, 23 | /// Channels, either 1 or 2 24 | #[structopt(default_value = "1")] 25 | channels: usize, 26 | /// Bitrate 27 | #[structopt(default_value = "16000")] 28 | bits_per_second: u32, 29 | /// Number of seconds to encode 30 | #[structopt(default_value = "10")] 31 | seconds: usize, 32 | } 33 | 34 | trait Encode { 35 | fn get_encoder(&self) -> Option; 36 | } 37 | 38 | impl Encode for EncodingOpts { 39 | fn get_encoder(&self) -> Option { 40 | if self.channels > 2 { 41 | unimplemented!("Multichannel support") 42 | } 43 | 44 | let coupled_streams = if self.channels > 1 { 1 } else { 0 }; 45 | 46 | Encoder::create(self.sampling_rate, self.channels, 1, coupled_streams, &[0u8, 1u8], Application::Audio).ok().map(|mut enc| { 47 | enc.set_option(OPUS_SET_BITRATE_REQUEST, self.bits_per_second).unwrap(); 48 | enc.set_option(OPUS_SET_BANDWIDTH_REQUEST, OPUS_BANDWIDTH_WIDEBAND).unwrap(); 49 | enc.set_option(OPUS_SET_COMPLEXITY_REQUEST, 10).unwrap(); 50 | enc.set_option(OPUS_SET_VBR_REQUEST, 0).unwrap(); 51 | enc.set_option(OPUS_SET_VBR_CONSTRAINT_REQUEST, 0).unwrap(); 52 | enc.set_option(OPUS_SET_PACKET_LOSS_PERC_REQUEST, 0).unwrap(); 53 | enc 54 | }) 55 | } 56 | } 57 | 58 | use std::slice; 59 | 60 | fn main() { 61 | let enc_opt = EncodingOpts::from_args(); 62 | 63 | let mut enc = enc_opt.get_encoder().unwrap(); 64 | 65 | let mut in_f = File::open(enc_opt.input).unwrap(); 66 | let mut out_f = File::create(enc_opt.output).unwrap(); 67 | 68 | let frame_size = 2880; 69 | let total_bytes = (enc_opt.channels * enc_opt.seconds * enc_opt.sampling_rate * 2) as usize; 70 | let max_packet = 1500; 71 | let mut processed_bytes = 0; 72 | let mut buf = Vec::with_capacity(frame_size * 2); 73 | let mut out_buf = Vec::with_capacity(max_packet); 74 | 75 | buf.resize(frame_size * 2, 0u8); 76 | out_buf.resize(max_packet, 0u8); 77 | 78 | while processed_bytes < total_bytes { 79 | in_f.read_exact(&mut buf).unwrap(); 80 | 81 | let samples: &[i16] = unsafe { 82 | slice::from_raw_parts(buf.as_ptr() as *const i16, frame_size) 83 | }; 84 | 85 | processed_bytes += frame_size * 2; 86 | 87 | 88 | if let Ok(ret) = enc.encode(samples, &mut out_buf) { 89 | let mut b = [0u8; 4]; 90 | // Write the packet size 91 | put_i32b(&mut b, ret as i32); 92 | out_f.write_all(&b).unwrap(); 93 | 94 | // Write the encoder ec final state 95 | let val = enc.get_option(OPUS_GET_FINAL_RANGE_REQUEST).unwrap(); 96 | put_i32b(&mut b, val); 97 | out_f.write_all(&b).unwrap(); 98 | 99 | // Write the actual packet 100 | out_f.write_all(&out_buf[..ret]).unwrap(); 101 | } else { 102 | panic!("Cannot encode"); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /opus-sys/.gitignore: -------------------------------------------------------------------------------- 1 | src/opus.rs 2 | -------------------------------------------------------------------------------- /opus-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "opus-sys" 3 | version = "0.1.0" 4 | authors = ["Luca Barbato "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "FFI bindings to libopus" 8 | repository = "https://github.com/rust-av/opus-rs" 9 | 10 | build = "build.rs" 11 | 12 | [build-dependencies] 13 | bindgen = "0.65" 14 | metadeps = "1.1" 15 | 16 | [package.metadata.pkg-config] 17 | opus = "1.3" 18 | 19 | [dev-dependencies] 20 | structopt = "0.3" 21 | av-bitstream = "0.1" 22 | -------------------------------------------------------------------------------- /opus-sys/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Luca Barbato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /opus-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | extern crate metadeps; 3 | 4 | use std::path::PathBuf; 5 | use std::io::Write; 6 | use std::env; 7 | use std::fs::File; 8 | 9 | fn format_write(builder: bindgen::Builder) -> String { 10 | builder 11 | .generate() 12 | .unwrap() 13 | .to_string() 14 | .replace("/**", "/*") 15 | .replace("/*!", "/*") 16 | } 17 | 18 | fn main() { 19 | let libs = metadeps::probe().unwrap(); 20 | let headers = libs.get("opus").unwrap().include_paths.clone(); 21 | 22 | let mut builder = bindgen::builder().header("data/opus.h"); 23 | 24 | for header in headers { 25 | builder = builder.clang_arg("-I").clang_arg(header.to_str().unwrap()); 26 | } 27 | 28 | // Manually fix the comment so rustdoc won't try to pick them 29 | let s = format_write(builder); 30 | 31 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 32 | 33 | let mut file = File::create(out_path.join("opus.rs")).unwrap(); 34 | 35 | let _ = file.write(s.as_bytes()); 36 | } 37 | -------------------------------------------------------------------------------- /opus-sys/data/opus.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | -------------------------------------------------------------------------------- /opus-sys/examples/decode.rs: -------------------------------------------------------------------------------- 1 | extern crate av_bitstream as bitstream; 2 | extern crate opus_sys; 3 | extern crate structopt; 4 | 5 | use opus_sys::*; 6 | 7 | use structopt::StructOpt; 8 | 9 | use std::mem::MaybeUninit; 10 | 11 | use std::fs::File; 12 | use std::io::prelude::*; 13 | use std::path::PathBuf; 14 | 15 | use bitstream::byteread::get_i32b; 16 | 17 | #[derive(Debug, StructOpt)] 18 | #[structopt(name = "decoder", about = "Opus decoding example")] 19 | struct DecodingOpts { 20 | /// Input file 21 | #[structopt(parse(from_os_str))] 22 | input: PathBuf, 23 | /// Output file 24 | #[structopt(parse(from_os_str))] 25 | output: PathBuf, 26 | /// Sampling rate, in Hz 27 | #[structopt(default_value = "48000")] 28 | sampling_rate: i32, 29 | /// Channels, either 1 or 2 30 | #[structopt(default_value = "1")] 31 | channels: i32, 32 | /*/// Number of seconds to decode 33 | #[structopt(default_value = "10")] 34 | seconds: i32,*/ 35 | } 36 | 37 | trait Decode { 38 | fn get_decoder(&self) -> Option<*mut OpusDecoder>; 39 | } 40 | 41 | impl Decode for DecodingOpts { 42 | fn get_decoder(&self) -> Option<*mut OpusDecoder> { 43 | let mut err = MaybeUninit::uninit(); 44 | let dec = 45 | unsafe { opus_decoder_create(self.sampling_rate, self.channels, err.as_mut_ptr()) }; 46 | let err = unsafe { err.assume_init() }; 47 | 48 | if err != OPUS_OK as i32 { 49 | None 50 | } else { 51 | Some(dec) 52 | } 53 | } 54 | } 55 | 56 | use std::slice; 57 | 58 | fn main() { 59 | let dec_opt = DecodingOpts::from_args(); 60 | 61 | let dec = dec_opt.get_decoder().unwrap(); 62 | 63 | let mut in_f = File::open(dec_opt.input).unwrap(); 64 | let mut out_f = File::create(dec_opt.output).unwrap(); 65 | 66 | let max_packet = 1500; 67 | let max_frame = 48000 * 2; 68 | let max_frame_samples = max_frame * dec_opt.channels as usize; 69 | 70 | let mut pkt = Vec::with_capacity(max_packet); 71 | let mut samples = Vec::with_capacity(max_frame_samples); 72 | 73 | pkt.resize(max_packet, 0u8); 74 | samples.resize(max_frame_samples, 0i16); 75 | 76 | let mut buf = [0u8; 4]; 77 | while in_f.read_exact(&mut buf).is_ok() { 78 | let len = get_i32b(&buf) as usize; 79 | if len > max_packet { 80 | panic!("Impossible packet size {}", len); 81 | } 82 | 83 | in_f.read_exact(&mut buf).expect("End of file"); 84 | 85 | in_f.read_exact(&mut pkt[..len]).expect("End of file"); 86 | 87 | let ret = unsafe { 88 | opus_decode( 89 | dec, 90 | pkt.as_ptr(), 91 | pkt.len() as i32, 92 | samples.as_mut_ptr(), 93 | samples.len() as i32, 94 | 0, 95 | ) 96 | }; 97 | 98 | if ret > 0 { 99 | // Write the actual group of samples 100 | let out = 101 | unsafe { slice::from_raw_parts(samples.as_ptr() as *const u8, ret as usize * 2) }; 102 | out_f.write_all(out).unwrap(); 103 | } else { 104 | panic!("Cannot decode"); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /opus-sys/examples/encode.rs: -------------------------------------------------------------------------------- 1 | extern crate opus_sys; 2 | extern crate structopt; 3 | extern crate av_bitstream as bitstream; 4 | 5 | use opus_sys::*; 6 | 7 | use structopt::StructOpt; 8 | 9 | use std::mem::MaybeUninit; 10 | 11 | use std::io::prelude::*; 12 | use std::fs::File; 13 | use std::path::PathBuf; 14 | 15 | use bitstream::bytewrite::put_i32b; 16 | 17 | #[derive(Debug, StructOpt)] 18 | #[structopt(name = "encoder", about = "Opus encoding example")] 19 | struct EncodingOpts { 20 | /// Input file 21 | #[structopt(parse(from_os_str))] 22 | input: PathBuf, 23 | /// Output file 24 | #[structopt(parse(from_os_str))] 25 | output: PathBuf, 26 | /// Sampling rate, in Hz 27 | #[structopt(default_value = "48000")] 28 | sampling_rate: i32, 29 | /// Channels, either 1 or 2 30 | #[structopt(default_value = "1")] 31 | channels: i32, 32 | /// Bitrate 33 | #[structopt(default_value = "16000")] 34 | bits_per_second: i32, 35 | /// Number of seconds to encode 36 | #[structopt(default_value = "10")] 37 | seconds: i32, 38 | } 39 | 40 | trait Encode { 41 | fn get_encoder(&self) -> Option<*mut OpusEncoder>; 42 | } 43 | 44 | impl Encode for EncodingOpts { 45 | fn get_encoder(&self) -> Option<*mut OpusEncoder> { 46 | let mut err = MaybeUninit::uninit(); 47 | let enc = unsafe { opus_encoder_create(self.sampling_rate, self.channels, OPUS_APPLICATION_AUDIO as i32, err.as_mut_ptr()) }; 48 | let err = unsafe { err.assume_init() }; 49 | 50 | if err != OPUS_OK as i32 { 51 | None 52 | } else { 53 | Some(enc) 54 | } 55 | } 56 | } 57 | 58 | use std::slice; 59 | 60 | fn main() { 61 | let enc_opt = EncodingOpts::from_args(); 62 | 63 | println!("{:?}", enc_opt); 64 | 65 | let enc = enc_opt.get_encoder().unwrap(); 66 | 67 | unsafe { 68 | opus_encoder_ctl(enc, OPUS_SET_APPLICATION_REQUEST as i32, OPUS_APPLICATION_AUDIO as i32); 69 | opus_encoder_ctl(enc, OPUS_SET_BITRATE_REQUEST as i32, enc_opt.bits_per_second); 70 | opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH_REQUEST as i32, OPUS_BANDWIDTH_WIDEBAND); 71 | opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY_REQUEST as i32, 10); 72 | opus_encoder_ctl(enc, OPUS_SET_VBR_REQUEST as i32, 0); 73 | opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT_REQUEST as i32, 0); 74 | opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC_REQUEST as i32, 0); 75 | } 76 | 77 | let mut in_f = File::open(enc_opt.input).unwrap(); 78 | let mut out_f = File::create(enc_opt.output).unwrap(); 79 | 80 | let frame_size = 2880; 81 | let total_bytes = (enc_opt.channels * enc_opt.seconds * enc_opt.sampling_rate * 2) as usize; 82 | let max_packet = 1500; 83 | let mut processed_bytes = 0; 84 | let mut buf = Vec::with_capacity(frame_size * 2); 85 | let mut out_buf = Vec::with_capacity(max_packet); 86 | 87 | buf.resize(frame_size * 2, 0u8); 88 | out_buf.resize(max_packet, 0u8); 89 | 90 | while processed_bytes < total_bytes { 91 | in_f.read_exact(&mut buf).unwrap(); 92 | 93 | let samples = unsafe { 94 | slice::from_raw_parts(buf.as_ptr() as *const i16, frame_size) 95 | }; 96 | 97 | processed_bytes += frame_size * 2; 98 | 99 | let ret = unsafe { 100 | opus_encode(enc, 101 | samples.as_ptr(), samples.len() as i32, 102 | out_buf.as_mut_ptr(), out_buf.len() as i32) 103 | }; 104 | 105 | if ret > 0 { 106 | let mut b = [0u8; 4]; 107 | 108 | // Write the packet size 109 | put_i32b(&mut b, ret); 110 | out_f.write_all(&b).unwrap(); 111 | 112 | // Write the encoder ec final state 113 | let mut val = 0i32; 114 | unsafe { opus_encoder_ctl(enc, OPUS_GET_FINAL_RANGE_REQUEST as i32, &mut val) }; 115 | put_i32b(&mut b, val); 116 | out_f.write_all(&b).unwrap(); 117 | 118 | // Write the actual packet 119 | out_f.write_all(&out_buf[..ret as usize]).unwrap(); 120 | } else { 121 | panic!("Cannot encode"); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /opus-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(non_upper_case_globals)] 5 | 6 | include!(concat!(env!("OUT_DIR"), "/opus.rs")); 7 | 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | use std::ffi::CStr; 12 | #[test] 13 | fn version() { 14 | println!("{}", unsafe { 15 | CStr::from_ptr(opus_get_version_string()).to_string_lossy() 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | use ffi::*; 2 | use std::fmt; 3 | use std::ffi::CStr; 4 | use std::i32; 5 | 6 | #[repr(i32)] 7 | #[derive(Copy, Clone, Debug)] 8 | pub enum ErrorCode { 9 | BadArg = OPUS_BAD_ARG, 10 | BufferTooSmall = OPUS_BUFFER_TOO_SMALL, 11 | InternalError = OPUS_INTERNAL_ERROR, 12 | InvalidPacket = OPUS_INVALID_PACKET, 13 | Unimplemented = OPUS_UNIMPLEMENTED, 14 | InvalidState = OPUS_INVALID_STATE, 15 | AllocFail = OPUS_ALLOC_FAIL, 16 | Unknown = i32::MAX, 17 | } 18 | 19 | impl fmt::Display for ErrorCode { 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 21 | let v = *self; 22 | let s = unsafe { CStr::from_ptr(opus_strerror(v as i32)) }; 23 | write!(f, "{}", s.to_string_lossy()) 24 | } 25 | } 26 | 27 | impl From for ErrorCode { 28 | fn from(v: i32) -> Self { 29 | use self::ErrorCode::*; 30 | match v { 31 | OPUS_BAD_ARG => BadArg, 32 | OPUS_BUFFER_TOO_SMALL => BufferTooSmall, 33 | OPUS_INTERNAL_ERROR => InternalError, 34 | OPUS_INVALID_PACKET => InvalidPacket, 35 | OPUS_UNIMPLEMENTED => Unimplemented, 36 | OPUS_INVALID_STATE => InvalidState, 37 | OPUS_ALLOC_FAIL => AllocFail, 38 | _ => Unknown, 39 | } 40 | } 41 | } 42 | 43 | pub enum AudioBuffer<'a> { 44 | F32(&'a [f32]), 45 | I16(&'a [i16]), 46 | } 47 | 48 | impl<'a> From<&'a [i16]> for AudioBuffer<'a> { 49 | fn from(v: &'a [i16]) -> Self { 50 | AudioBuffer::I16(v) 51 | } 52 | } 53 | 54 | impl<'a> From<&'a [f32]> for AudioBuffer<'a> { 55 | fn from(v: &'a [f32]) -> Self { 56 | AudioBuffer::F32(v) 57 | } 58 | } 59 | 60 | pub enum AudioBufferMut<'a> { 61 | F32(&'a mut [f32]), 62 | I16(&'a mut [i16]), 63 | } 64 | 65 | impl<'a> From<&'a mut [f32]> for AudioBufferMut<'a> { 66 | fn from(v: &'a mut [f32]) -> Self { 67 | AudioBufferMut::F32(v) 68 | } 69 | } 70 | 71 | impl<'a> From<&'a mut [i16]> for AudioBufferMut<'a> { 72 | fn from(v: &'a mut [i16]) -> Self { 73 | AudioBufferMut::I16(v) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/decoder.rs: -------------------------------------------------------------------------------- 1 | use crate::common::*; 2 | use crate::ffi::*; 3 | 4 | use std::ptr; 5 | 6 | pub struct Decoder { 7 | dec: *mut OpusMSDecoder, 8 | channels: usize, 9 | } 10 | 11 | unsafe impl Send for Decoder {} // TODO: Make sure it cannot be abused 12 | unsafe impl Sync for Decoder {} // TODO: Make sure it cannot be abused 13 | 14 | impl Decoder { 15 | pub fn create( 16 | sample_rate: usize, 17 | channels: usize, 18 | streams: usize, 19 | coupled_streams: usize, 20 | mapping: &[u8], 21 | ) -> Result { 22 | let mut err = 0; 23 | let dec = unsafe { 24 | opus_multistream_decoder_create( 25 | sample_rate as i32, 26 | channels as i32, 27 | streams as i32, 28 | coupled_streams as i32, 29 | mapping.as_ptr(), 30 | &mut err, 31 | ) 32 | }; 33 | 34 | if err < 0 { 35 | Err(err.into()) 36 | } else { 37 | Ok(Decoder { dec, channels }) 38 | } 39 | } 40 | 41 | pub fn decode<'a, I, O>( 42 | &mut self, 43 | input: I, 44 | out: O, 45 | decode_fec: bool, 46 | ) -> Result 47 | where 48 | I: Into>, 49 | O: Into>, 50 | { 51 | let (data, len) = input 52 | .into() 53 | .map_or((ptr::null(), 0), |v| (v.as_ptr(), v.len())); 54 | 55 | let ret = match out.into() { 56 | AudioBufferMut::F32(v) => unsafe { 57 | opus_multistream_decode_float( 58 | self.dec, 59 | data, 60 | len as i32, 61 | v.as_mut_ptr(), 62 | (v.len() / self.channels) as i32, 63 | decode_fec as i32, 64 | ) 65 | }, 66 | AudioBufferMut::I16(v) => unsafe { 67 | opus_multistream_decode( 68 | self.dec, 69 | data, 70 | len as i32, 71 | v.as_mut_ptr(), 72 | (v.len() / self.channels) as i32, 73 | decode_fec as i32, 74 | ) 75 | }, 76 | }; 77 | 78 | if ret < 0 { 79 | Err(ret.into()) 80 | } else { 81 | Ok(ret as usize) 82 | } 83 | } 84 | 85 | pub fn set_option(&mut self, key: u32, val: i32) -> Result<(), ErrorCode> { 86 | let ret = match key { 87 | OPUS_SET_GAIN_REQUEST => unsafe { 88 | opus_multistream_decoder_ctl(self.dec, key as i32, val) 89 | }, 90 | _ => unimplemented!(), 91 | }; 92 | 93 | if ret < 0 { 94 | Err(ret.into()) 95 | } else { 96 | Ok(()) 97 | } 98 | } 99 | 100 | pub fn reset(&mut self) { 101 | let _ = unsafe { opus_multistream_decoder_ctl(self.dec, OPUS_RESET_STATE as i32) }; 102 | } 103 | } 104 | 105 | impl Drop for Decoder { 106 | fn drop(&mut self) { 107 | unsafe { opus_multistream_decoder_destroy(self.dec) } 108 | } 109 | } 110 | 111 | #[cfg(feature = "codec-trait")] 112 | mod decoder_trait { 113 | use super::Decoder as OpusDecoder; 114 | use bitstream::byteread::get_i16l; 115 | use codec::decoder::*; 116 | use codec::error::*; 117 | use data::audiosample::formats::S16; 118 | use data::audiosample::ChannelMap; 119 | use data::frame::*; 120 | use data::packet::Packet; 121 | use ffi::OPUS_SET_GAIN_REQUEST; 122 | use std::collections::VecDeque; 123 | use std::sync::Arc; 124 | 125 | pub struct Des { 126 | descr: Descr, 127 | } 128 | 129 | pub struct Dec { 130 | dec: Option, 131 | extradata: Option>, 132 | pending: VecDeque, 133 | info: AudioInfo, 134 | } 135 | 136 | impl Dec { 137 | fn new() -> Self { 138 | Dec { 139 | dec: None, 140 | extradata: None, 141 | pending: VecDeque::with_capacity(1), 142 | info: AudioInfo { 143 | samples: 960 * 6, 144 | sample_rate: 48000, 145 | map: ChannelMap::new(), 146 | format: Arc::new(S16), 147 | block_len: None, 148 | }, 149 | } 150 | } 151 | } 152 | 153 | impl Descriptor for Des { 154 | type OutputDecoder = Dec; 155 | 156 | fn create(&self) -> Self::OutputDecoder { 157 | Dec::new() 158 | } 159 | 160 | fn describe(&self) -> &Descr { 161 | &self.descr 162 | } 163 | } 164 | 165 | const OPUS_HEAD_SIZE: usize = 19; 166 | 167 | impl Decoder for Dec { 168 | fn set_extradata(&mut self, extra: &[u8]) { 169 | self.extradata = Some(Vec::from(extra)); 170 | } 171 | fn send_packet(&mut self, pkt: &Packet) -> Result<()> { 172 | let mut f = Frame::new_default_frame(self.info.clone(), Some(pkt.t.clone())); 173 | 174 | let ret = { 175 | let buf: &mut [i16] = f.buf.as_mut_slice(0).unwrap(); 176 | 177 | self.dec 178 | .as_mut() 179 | .unwrap() 180 | .decode(pkt.data.as_slice(), buf, false) 181 | .map_err(|_e| Error::InvalidData) 182 | }; 183 | 184 | match ret { 185 | Ok(samples) => { 186 | if let MediaKind::Audio(ref mut info) = f.kind { 187 | info.samples = samples; 188 | } 189 | self.pending.push_back(Arc::new(f)); 190 | Ok(()) 191 | } 192 | Err(e) => Err(e), 193 | } 194 | } 195 | fn receive_frame(&mut self) -> Result { 196 | self.pending.pop_front().ok_or(Error::MoreDataNeeded) 197 | } 198 | fn configure(&mut self) -> Result<()> { 199 | let channels; 200 | let sample_rate = 48000; 201 | let mut gain_db = 0; 202 | let mut streams = 1; 203 | let mut coupled_streams = 0; 204 | let mut mapping: &[u8] = &[0u8, 1u8]; 205 | let mut channel_map = false; 206 | 207 | if let Some(ref extradata) = self.extradata { 208 | channels = *extradata.get(9).unwrap_or(&2) as usize; 209 | 210 | if extradata.len() >= OPUS_HEAD_SIZE { 211 | gain_db = get_i16l(&extradata[16..18]); 212 | channel_map = extradata[18] != 0; 213 | } 214 | if extradata.len() >= OPUS_HEAD_SIZE + 2 + channels { 215 | streams = extradata[OPUS_HEAD_SIZE] as usize; 216 | coupled_streams = extradata[OPUS_HEAD_SIZE + 1] as usize; 217 | if streams + coupled_streams != channels { 218 | unimplemented!() 219 | } 220 | mapping = &extradata[OPUS_HEAD_SIZE + 2..] 221 | } else { 222 | if channels > 2 || channel_map { 223 | return Err(Error::ConfigurationInvalid); 224 | } 225 | if channels > 1 { 226 | coupled_streams = 1; 227 | } 228 | } 229 | } else { 230 | return Err(Error::ConfigurationIncomplete); 231 | } 232 | 233 | if channels > 2 { 234 | unimplemented!() // TODO: Support properly channel mapping 235 | } else { 236 | self.info.map = ChannelMap::default_map(channels); 237 | } 238 | 239 | match OpusDecoder::create(sample_rate, channels, streams, coupled_streams, mapping) { 240 | Ok(mut d) => { 241 | let _ = d.set_option(OPUS_SET_GAIN_REQUEST, gain_db as i32); 242 | self.dec = Some(d); 243 | Ok(()) 244 | } 245 | Err(_) => Err(Error::ConfigurationInvalid), 246 | } 247 | } 248 | 249 | fn flush(&mut self) -> Result<()> { 250 | self.dec.as_mut().unwrap().reset(); 251 | Ok(()) 252 | } 253 | } 254 | 255 | pub const OPUS_DESCR: &Des = &Des { 256 | descr: Descr { 257 | codec: "opus", 258 | name: "libopus", 259 | desc: "libopus decoder", 260 | mime: "audio/OPUS", 261 | }, 262 | }; 263 | } 264 | 265 | #[cfg(feature = "codec-trait")] 266 | pub use self::decoder_trait::OPUS_DESCR; 267 | -------------------------------------------------------------------------------- /src/encoder.rs: -------------------------------------------------------------------------------- 1 | use crate::common::*; 2 | use crate::ffi::*; 3 | use std::str::FromStr; 4 | 5 | pub struct Encoder { 6 | enc: *mut OpusMSEncoder, 7 | channels: usize, 8 | } 9 | 10 | mod constants { 11 | pub use ffi::OPUS_SET_APPLICATION_REQUEST; 12 | pub use ffi::OPUS_SET_BANDWIDTH_REQUEST; 13 | pub use ffi::OPUS_SET_BITRATE_REQUEST; 14 | pub use ffi::OPUS_SET_COMPLEXITY_REQUEST; 15 | pub use ffi::OPUS_SET_DTX_REQUEST; 16 | pub use ffi::OPUS_SET_EXPERT_FRAME_DURATION_REQUEST; 17 | pub use ffi::OPUS_SET_FORCE_CHANNELS_REQUEST; 18 | pub use ffi::OPUS_SET_GAIN_REQUEST; 19 | pub use ffi::OPUS_SET_INBAND_FEC_REQUEST; 20 | pub use ffi::OPUS_SET_LSB_DEPTH_REQUEST; 21 | pub use ffi::OPUS_SET_MAX_BANDWIDTH_REQUEST; 22 | pub use ffi::OPUS_SET_PACKET_LOSS_PERC_REQUEST; 23 | pub use ffi::OPUS_SET_PREDICTION_DISABLED_REQUEST; 24 | pub use ffi::OPUS_SET_SIGNAL_REQUEST; 25 | pub use ffi::OPUS_SET_VBR_CONSTRAINT_REQUEST; 26 | pub use ffi::OPUS_SET_VBR_REQUEST; 27 | 28 | pub use ffi::OPUS_GET_FINAL_RANGE_REQUEST; 29 | pub use ffi::OPUS_GET_LOOKAHEAD_REQUEST; 30 | 31 | pub use ffi::OPUS_BANDWIDTH_FULLBAND; 32 | pub use ffi::OPUS_BANDWIDTH_MEDIUMBAND; 33 | pub use ffi::OPUS_BANDWIDTH_NARROWBAND; 34 | pub use ffi::OPUS_BANDWIDTH_SUPERWIDEBAND; 35 | pub use ffi::OPUS_BANDWIDTH_WIDEBAND; 36 | pub use ffi::OPUS_FRAMESIZE_100_MS; 37 | pub use ffi::OPUS_FRAMESIZE_10_MS; 38 | pub use ffi::OPUS_FRAMESIZE_120_MS; 39 | pub use ffi::OPUS_FRAMESIZE_20_MS; 40 | pub use ffi::OPUS_FRAMESIZE_2_5_MS; 41 | pub use ffi::OPUS_FRAMESIZE_40_MS; 42 | pub use ffi::OPUS_FRAMESIZE_5_MS; 43 | pub use ffi::OPUS_FRAMESIZE_60_MS; 44 | pub use ffi::OPUS_FRAMESIZE_80_MS; 45 | pub use ffi::OPUS_FRAMESIZE_ARG; 46 | } 47 | 48 | pub use self::constants::*; 49 | 50 | unsafe impl Send for Encoder {} // TODO: Make sure it cannot be abused 51 | 52 | #[repr(i32)] 53 | #[derive(Clone, Copy, Debug)] 54 | pub enum Application { 55 | Voip = OPUS_APPLICATION_VOIP as i32, 56 | Audio = OPUS_APPLICATION_AUDIO as i32, 57 | LowDelay = OPUS_APPLICATION_RESTRICTED_LOWDELAY as i32, 58 | } 59 | 60 | impl FromStr for Application { 61 | type Err = (); 62 | 63 | fn from_str(s: &str) -> Result { 64 | use self::Application::*; 65 | match s { 66 | "voip" => Ok(Voip), 67 | "audio" => Ok(Audio), 68 | "lowdelay" => Ok(LowDelay), 69 | _ => Err(()), 70 | } 71 | } 72 | } 73 | 74 | impl Encoder { 75 | pub fn create( 76 | sample_rate: usize, 77 | channels: usize, 78 | streams: usize, 79 | coupled_streams: usize, 80 | mapping: &[u8], 81 | application: Application, 82 | ) -> Result { 83 | let mut err = 0; 84 | let enc = unsafe { 85 | opus_multistream_encoder_create( 86 | sample_rate as i32, 87 | channels as i32, 88 | streams as i32, 89 | coupled_streams as i32, 90 | mapping.as_ptr(), 91 | application as i32, 92 | &mut err, 93 | ) 94 | }; 95 | 96 | if err < 0 { 97 | Err(err.into()) 98 | } else { 99 | Ok(Encoder { enc, channels }) 100 | } 101 | } 102 | 103 | pub fn encode<'a, I>(&mut self, input: I, output: &mut [u8]) -> Result 104 | where 105 | I: Into>, 106 | { 107 | let ret = match input.into() { 108 | AudioBuffer::F32(v) => unsafe { 109 | opus_multistream_encode_float( 110 | self.enc, 111 | v.as_ptr(), 112 | (v.len() / self.channels) as i32, 113 | output.as_mut_ptr(), 114 | output.len() as i32, 115 | ) 116 | }, 117 | AudioBuffer::I16(v) => unsafe { 118 | opus_multistream_encode( 119 | self.enc, 120 | v.as_ptr(), 121 | (v.len() / self.channels) as i32, 122 | output.as_mut_ptr(), 123 | output.len() as i32, 124 | ) 125 | }, 126 | }; 127 | 128 | if ret < 0 { 129 | Err(ret.into()) 130 | } else { 131 | Ok(ret as usize) 132 | } 133 | } 134 | 135 | pub fn set_option(&mut self, key: u32, val: u32) -> Result<(), ErrorCode> { 136 | let ret = match key { 137 | OPUS_SET_APPLICATION_REQUEST | 138 | OPUS_SET_BITRATE_REQUEST | 139 | OPUS_SET_MAX_BANDWIDTH_REQUEST | 140 | OPUS_SET_VBR_REQUEST | 141 | OPUS_SET_BANDWIDTH_REQUEST | 142 | OPUS_SET_COMPLEXITY_REQUEST | 143 | OPUS_SET_INBAND_FEC_REQUEST | 144 | OPUS_SET_PACKET_LOSS_PERC_REQUEST | 145 | OPUS_SET_DTX_REQUEST | 146 | OPUS_SET_VBR_CONSTRAINT_REQUEST | 147 | OPUS_SET_FORCE_CHANNELS_REQUEST | 148 | OPUS_SET_SIGNAL_REQUEST | 149 | OPUS_SET_GAIN_REQUEST | 150 | OPUS_SET_LSB_DEPTH_REQUEST | 151 | OPUS_SET_EXPERT_FRAME_DURATION_REQUEST | 152 | OPUS_SET_PREDICTION_DISABLED_REQUEST /* | 153 | OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST */ => unsafe { 154 | opus_multistream_encoder_ctl(self.enc, key as i32, val) 155 | }, 156 | _ => unimplemented!(), 157 | }; 158 | 159 | if ret < 0 { 160 | Err(ret.into()) 161 | } else { 162 | Ok(()) 163 | } 164 | } 165 | pub fn get_option(&self, key: u32) -> Result { 166 | let mut val: i32 = 0; 167 | let ret = match key { 168 | OPUS_GET_LOOKAHEAD_REQUEST | OPUS_GET_FINAL_RANGE_REQUEST => unsafe { 169 | opus_multistream_encoder_ctl(self.enc, key as i32, &mut val as *mut i32) 170 | }, 171 | _ => unimplemented!(), 172 | }; 173 | 174 | if ret < 0 { 175 | Err(ret.into()) 176 | } else { 177 | Ok(val) 178 | } 179 | } 180 | 181 | pub fn reset(&mut self) { 182 | let _ = unsafe { opus_multistream_encoder_ctl(self.enc, OPUS_RESET_STATE as i32) }; 183 | } 184 | } 185 | 186 | impl Drop for Encoder { 187 | fn drop(&mut self) { 188 | unsafe { opus_multistream_encoder_destroy(self.enc) }; 189 | } 190 | } 191 | 192 | #[cfg(feature = "codec-trait")] 193 | mod encoder_trait { 194 | use super::constants::*; 195 | use super::Application; 196 | use super::Encoder as OpusEncoder; 197 | // use std::rc::Rc; 198 | use codec::encoder::*; 199 | use codec::error::*; 200 | use data::audiosample::formats::S16; 201 | use data::audiosample::ChannelMap; 202 | use data::frame::{ArcFrame, FrameBufferConv, MediaKind}; 203 | use data::packet::Packet; 204 | use data::params::CodecParams; 205 | use data::rational::Rational64; 206 | use data::value::Value; 207 | use std::collections::VecDeque; 208 | 209 | pub struct Des { 210 | descr: Descr, 211 | } 212 | 213 | struct Cfg { 214 | channels: usize, 215 | streams: usize, 216 | coupled_streams: usize, 217 | mapping: Vec, 218 | application: Application, 219 | bitrate: usize, 220 | } 221 | 222 | impl Cfg { 223 | fn is_valid(&self) -> bool { 224 | self.channels > 0 225 | && self.streams + self.coupled_streams == self.channels 226 | && self.mapping.len() == self.channels 227 | } 228 | } 229 | 230 | pub struct Enc { 231 | enc: Option, 232 | pending: VecDeque, 233 | frame_size: usize, 234 | delay: usize, 235 | cfg: Cfg, 236 | flushing: bool, 237 | } 238 | 239 | impl Descriptor for Des { 240 | type OutputEncoder = Enc; 241 | 242 | fn create(&self) -> Self::OutputEncoder { 243 | Enc { 244 | enc: None, 245 | pending: VecDeque::new(), 246 | frame_size: 960, 247 | delay: 0, 248 | cfg: Cfg { 249 | channels: 0, 250 | streams: 0, 251 | coupled_streams: 0, 252 | mapping: vec![0, 1], 253 | application: Application::Audio, 254 | bitrate: 16000, 255 | }, 256 | flushing: false, 257 | } 258 | } 259 | 260 | fn describe(&self) -> &Descr { 261 | &self.descr 262 | } 263 | } 264 | 265 | // Values copied from libopusenc.c 266 | // A packet may contain up to 3 frames, each of 1275 bytes max. 267 | // The packet header may be up to 7 bytes long. 268 | 269 | const MAX_HEADER_SIZE: usize = 7; 270 | const MAX_FRAME_SIZE: usize = 1275; 271 | const MAX_FRAMES: usize = 3; 272 | 273 | /// 80ms in samples 274 | const CONVERGENCE_WINDOW: usize = 3840; 275 | 276 | impl Encoder for Enc { 277 | fn configure(&mut self) -> Result<()> { 278 | if self.enc.is_none() { 279 | if self.cfg.is_valid() { 280 | let mut enc = OpusEncoder::create( 281 | 48000, // TODO 282 | self.cfg.channels, 283 | self.cfg.streams, 284 | self.cfg.coupled_streams, 285 | &self.cfg.mapping, 286 | self.cfg.application, 287 | ) 288 | .map_err(|_e| unimplemented!())?; 289 | enc.set_option(OPUS_SET_BITRATE_REQUEST, self.cfg.bitrate as u32) 290 | .unwrap(); 291 | enc.set_option(OPUS_SET_BANDWIDTH_REQUEST, OPUS_BANDWIDTH_WIDEBAND) 292 | .unwrap(); 293 | enc.set_option(OPUS_SET_COMPLEXITY_REQUEST, 10).unwrap(); 294 | enc.set_option(OPUS_SET_VBR_REQUEST, 0).unwrap(); 295 | enc.set_option(OPUS_SET_VBR_CONSTRAINT_REQUEST, 0).unwrap(); 296 | enc.set_option(OPUS_SET_PACKET_LOSS_PERC_REQUEST, 0) 297 | .unwrap(); 298 | 299 | self.delay = enc.get_option(OPUS_GET_LOOKAHEAD_REQUEST).unwrap() as usize; 300 | self.enc = Some(enc); 301 | Ok(()) 302 | } else { 303 | unimplemented!() 304 | } 305 | } else { 306 | unimplemented!() 307 | } 308 | } 309 | // TODO: support multichannel 310 | fn get_extradata(&self) -> Option> { 311 | use bitstream::bytewrite::*; 312 | if self.cfg.channels > 2 { 313 | unimplemented!(); 314 | } 315 | 316 | let mut buf = b"OpusHead".to_vec(); 317 | 318 | buf.resize(19, 0); 319 | 320 | buf[8] = 1; 321 | buf[9] = self.cfg.channels as u8; 322 | put_i16l(&mut buf[10..12], self.delay as i16); 323 | put_i32l(&mut buf[12..16], 48000); // TODO 324 | put_i16l(&mut buf[16..18], 0); 325 | buf[18] = 0; 326 | 327 | Some(buf) 328 | } 329 | 330 | fn send_frame(&mut self, frame: &ArcFrame) -> Result<()> { 331 | let enc = self.enc.as_mut().unwrap(); 332 | let pending = &mut self.pending; 333 | if let MediaKind::Audio(ref info) = frame.kind { 334 | let channels = info.map.len(); 335 | let input_size = info.samples * channels; 336 | let input: &[i16] = frame.buf.as_slice(0).unwrap(); 337 | let data_size = MAX_HEADER_SIZE + MAX_FRAMES * MAX_FRAME_SIZE; 338 | let chunk_size = self.frame_size * channels; 339 | let mut buf = Vec::with_capacity(chunk_size); 340 | let mut pts = frame.t.pts.unwrap(); 341 | 342 | for chunk in input[..input_size].chunks(chunk_size) { 343 | let len = chunk.len(); 344 | let mut pkt = Packet::with_capacity(data_size); 345 | 346 | pkt.data.resize(data_size, 0); // TODO is it needed? 347 | 348 | let input_data = if len < chunk_size { 349 | buf.clear(); 350 | buf.extend_from_slice(chunk); 351 | buf.as_slice() 352 | } else { 353 | chunk 354 | }; 355 | 356 | match enc.encode(input_data, pkt.data.as_mut_slice()) { 357 | Ok(len) => { 358 | let duration = (Rational64::new(len as i64 / channels as i64, 48000) 359 | / frame.t.timebase.unwrap()) 360 | .to_integer(); 361 | pkt.t.pts = Some(pts); 362 | pkt.t.dts = Some(pts); 363 | pkt.t.duration = Some(duration as u64); 364 | pts += duration; 365 | pkt.data.truncate(len); 366 | pending.push_back(pkt); 367 | } 368 | Err(_) => unimplemented!(), 369 | } 370 | } 371 | Ok(()) 372 | } else { 373 | unimplemented!() // TODO mark it unreachable? 374 | } 375 | } 376 | 377 | fn receive_packet(&mut self) -> Result { 378 | self.pending.pop_front().ok_or(Error::MoreDataNeeded) 379 | } 380 | 381 | fn set_option<'a>(&mut self, key: &str, val: Value<'a>) -> Result<()> { 382 | match (key, val) { 383 | // ("format", Value::Formaton(f)) => self.format = Some(f), 384 | // ("mapping", Value::ChannelMap(map) => self.cfg.map = map::to_vec() 385 | ("channels", Value::U64(v)) => self.cfg.channels = v as usize, 386 | ("streams", Value::U64(v)) => self.cfg.streams = v as usize, 387 | ("coupled_streams", Value::U64(v)) => self.cfg.coupled_streams = v as usize, 388 | ("application", Value::Str(s)) => { 389 | if let Ok(a) = s.parse() { 390 | self.cfg.application = a; 391 | } else { 392 | return Err(Error::InvalidData); 393 | } 394 | } 395 | _ => return Err(Error::Unsupported("Unsupported option".to_owned())), 396 | } 397 | 398 | Ok(()) 399 | } 400 | 401 | fn set_params(&mut self, params: &CodecParams) -> Result<()> { 402 | use data::params::*; 403 | if let Some(MediaKind::Audio(ref info)) = params.kind { 404 | if let Some(ref map) = info.map { 405 | if map.len() > 2 { 406 | unimplemented!() 407 | } else { 408 | self.cfg.channels = map.len(); 409 | self.cfg.coupled_streams = self.cfg.channels - 1; 410 | self.cfg.streams = 1; 411 | self.cfg.mapping = if map.len() > 1 { vec![0, 1] } else { vec![0] }; 412 | } 413 | } 414 | } 415 | Ok(()) 416 | } 417 | 418 | // TODO: guard against calling it before configure() 419 | // is issued. 420 | fn get_params(&self) -> Result { 421 | use data::params::*; 422 | use std::sync::Arc; 423 | Ok(CodecParams { 424 | kind: Some(MediaKind::Audio(AudioInfo { 425 | rate: 48000, 426 | map: Some(ChannelMap::default_map(2)), 427 | format: Some(Arc::new(S16)), 428 | })), 429 | codec_id: Some("opus".to_owned()), 430 | extradata: self.get_extradata(), 431 | bit_rate: 0, // TODO: expose the information 432 | convergence_window: CONVERGENCE_WINDOW, 433 | delay: self.delay, 434 | }) 435 | } 436 | 437 | fn flush(&mut self) -> Result<()> { 438 | // unimplemented!() 439 | self.flushing = true; 440 | Ok(()) 441 | } 442 | } 443 | 444 | pub const OPUS_DESCR: &Des = &Des { 445 | descr: Descr { 446 | codec: "opus", 447 | name: "libopus", 448 | desc: "libopus encoder", 449 | mime: "audio/OPUS", 450 | }, 451 | }; 452 | } 453 | 454 | #[cfg(feature = "codec-trait")] 455 | pub use self::encoder_trait::OPUS_DESCR; 456 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate opus_sys as ffi; 2 | 3 | #[cfg(feature="codec-trait")] 4 | extern crate av_data as data; 5 | 6 | #[cfg(feature="codec-trait")] 7 | extern crate av_codec as codec; 8 | 9 | #[cfg(feature="codec-trait")] 10 | extern crate av_bitstream as bitstream; 11 | 12 | pub mod common; 13 | pub mod encoder; 14 | pub mod decoder; 15 | --------------------------------------------------------------------------------