Skip to content

Commit

Permalink
Add pull/get_page API (#3)
Browse files Browse the repository at this point in the history
* 🚩 add pull/get_page API

Change-Id: I2fbd448abd39cc123f483bc19963c1f7c75b8122

* fix: directly return ref vec

Co-authored-by: David C. <[email protected]>

* Add examples

* Use audio sample for examples

* Update changelog

---------

Co-authored-by: David C. <[email protected]>
  • Loading branch information
Saafo and d-k-bo authored Oct 21, 2024
1 parent 1a8dfe1 commit c36abce
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Support libopusenc's pull/`get_page`-based API ([#3](https://github.com/d-k-bo/opusenc-rs/pull/3))

## [0.2.1] - 2024-01-21

### Changed
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ cstr = "0.2.11"
num_enum = "0.7.2"
opusenc-sys = { version = "0.2.1", path = "opusenc-sys" }

[dev-dependencies]
hound = "3.5.1"

[[package.metadata.release.pre-release-replacements]]
file = "CHANGELOG.md"
search = "## \\[Unreleased\\]"
Expand Down
28 changes: 28 additions & 0 deletions examples/file_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use hound::WavReader;
use opusenc::{Comments, Encoder, MappingFamily, RecommendedTag};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut wav = WavReader::open("examples/speech_orig.wav")?;

let spec = wav.spec();
assert_eq!(spec.channels, 1);
assert_eq!(spec.sample_format, hound::SampleFormat::Int);
assert_eq!(spec.bits_per_sample, 16);

let audio_data = wav.samples::<i16>().collect::<hound::Result<Vec<i16>>>()?;

let mut encoder = Encoder::create_file(
"/tmp/speech.opus",
Comments::create()
.add(RecommendedTag::Title, "Opus Speech Samples")?
.add(RecommendedTag::Artist, "Various Artists")?,
48_000,
1,
MappingFamily::MonoStereo,
)?;

encoder.write(&audio_data)?;
encoder.drain()?;

Ok(())
}
44 changes: 44 additions & 0 deletions examples/pull_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::io::Write;

use hound::WavReader;
use opusenc::{Comments, Encoder, MappingFamily, RecommendedTag};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut wav = WavReader::open("examples/speech_orig.wav")?;

let spec = wav.spec();
assert_eq!(spec.channels, 1);
assert_eq!(spec.sample_format, hound::SampleFormat::Int);
assert_eq!(spec.bits_per_sample, 16);

let audio_data = wav.samples::<i16>().collect::<hound::Result<Vec<i16>>>()?;

let mut encoder = Encoder::create_pull(
Comments::create()
.add(RecommendedTag::Title, "Opus Speech Samples")?
.add(RecommendedTag::Artist, "Various Artists")?,
48_000,
1,
MappingFamily::MonoStereo,
)?;

let mut output_file = std::fs::File::create("/tmp/speech.opus")?;

// let's simulate that we are reading the audio data from multiple buffers
// as if we were reading it from a network stream
for chunk in audio_data.chunks(audio_data.len() / 4) {
encoder.write(chunk)?;

while let Some(page) = encoder.get_page(true) {
output_file.write_all(page)?;
}
}

encoder.drain()?;

while let Some(page) = encoder.get_page(true) {
output_file.write_all(page)?;
}

Ok(())
}
Binary file added examples/speech_orig.wav
Binary file not shown.
2 changes: 2 additions & 0 deletions examples/speech_orig.wav.license
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: Copyright © 2011-2024 Xiph.Org Foundation
# SPDX-License-Identifier: CC-BY-3.0
46 changes: 46 additions & 0 deletions src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,31 @@ impl Encoder {

Ok(Self { ptr, channels })
}

/// Create a new OggOpus encoder in pull style.
///
/// Use together with [`Encoder::get_page`] to get the encoded data.
pub fn create_pull(
comments: impl Borrow<Comments>,
rate: i32,
channels: usize,
family: MappingFamily,
) -> Result<Self> {
let mut error = 0;
let ptr = unsafe {
crate::ffi::ope_encoder_create_pull(
comments.borrow().ptr(),
rate,
channels.try_into().unwrap(),
family as i32,
&mut error,
)
};
error.check_result()?;
assert!(!ptr.is_null());

Ok(Self { ptr, channels })
}
}

impl Encoder {
Expand Down Expand Up @@ -94,6 +119,27 @@ impl Encoder {
}
.check_result()
}

/// Get the next page from the stream
///
/// Only use if creating encoder with [`Encoder::create_pull`].
pub fn get_page(&mut self, flush: bool) -> Option<&[u8]> {
let mut page_ptr: *mut std::ffi::c_uchar = std::ptr::null_mut();
let mut page_size = 0;
unsafe {
let available = crate::ffi::ope_encoder_get_page(
self.ptr,
&mut page_ptr,
&mut page_size,
flush as i32,
);
if available == 1 {
Some(std::slice::from_raw_parts(page_ptr, page_size as usize))
} else {
None
}
}
}
/// Finalizes the stream.
pub fn drain(&mut self) -> Result<()> {
unsafe { crate::ffi::ope_encoder_drain(self.ptr) }.check_result()
Expand Down

0 comments on commit c36abce

Please sign in to comment.