Skip to content

Commit

Permalink
Added support for explicitly setting Opus bitrate and signal type. (#66)
Browse files Browse the repository at this point in the history
* Added support for explicitly setting Opus bitrate and signal type.

* Rename input validation to parse inputs, formatting.

Co-authored-by: Mateusz Front <[email protected]>

---------

Co-authored-by: Mateusz Front <[email protected]>
  • Loading branch information
CJRChang and mat-hek authored Jan 10, 2025
1 parent f542203 commit 1f1d381
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 4 deletions.
12 changes: 11 additions & 1 deletion c_src/membrane_opus_plugin/encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static char *get_error(int err_code) {
}
}

UNIFEX_TERM create(UnifexEnv *env, int input_rate, int channels, int application) {
UNIFEX_TERM create(UnifexEnv *env, int input_rate, int channels, int application, int bitrate, int signal_type) {
State *state = unifex_alloc_state(env);
state->buffer = calloc(MAX_PACKET, sizeof(unsigned char));

Expand All @@ -34,6 +34,16 @@ UNIFEX_TERM create(UnifexEnv *env, int input_rate, int channels, int application
unifex_release_state(env, state);
return unifex_raise(env, (char *)opus_strerror(error));
}
opus_encoder_ctl(state->encoder, OPUS_SET_BITRATE(bitrate), &error);
if (error != OPUS_OK) {
unifex_release_state(env, state);
return unifex_raise(env, (char *)opus_strerror(error));
}
opus_encoder_ctl(state->encoder, OPUS_SET_SIGNAL(signal_type), &error);
if (error != OPUS_OK) {
unifex_release_state(env, state);
return unifex_raise(env, (char *)opus_strerror(error));
}

UNIFEX_TERM res = create_result(env, state);
unifex_release_state(env, state);
Expand Down
2 changes: 1 addition & 1 deletion c_src/membrane_opus_plugin/encoder.spec.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Membrane.Opus.Encoder.Native

state_type "State"

spec create(input_rate :: int, channels :: int, application :: int) :: state
spec create(input_rate :: int, channels :: int, application :: int, bitrate :: int, signal_type :: int) :: state

spec encode_packet(state, payload, frame_size :: int) ::
{:ok :: label, payload}
Expand Down
61 changes: 59 additions & 2 deletions lib/membrane_opus/encoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,25 @@ defmodule Membrane.Opus.Encoder do
@allowed_channels [1, 2]
@allowed_applications [:voip, :audio, :low_delay]
@allowed_sample_rates [8000, 12_000, 16_000, 24_000, 48_000]
@allowed_bitrates [
:auto,
:max,
10_000,
24_000,
32_000,
64_000,
96_000,
128_000,
256_000,
450_000
]
@allowed_signal_type [:auto, :voice, :music]

@type allowed_channels :: unquote(Bunch.Typespec.enum_to_alternative(@allowed_channels))
@type allowed_applications :: unquote(Bunch.Typespec.enum_to_alternative(@allowed_applications))
@type allowed_sample_rates :: unquote(Bunch.Typespec.enum_to_alternative(@allowed_sample_rates))
@type allowed_bitrates :: unquote(Bunch.Typespec.enum_to_alternative(@allowed_bitrates))
@type allowed_signal_types :: unquote(Bunch.Typespec.enum_to_alternative(@allowed_signal_type))

def_options application: [
spec: allowed_applications(),
Expand All @@ -27,6 +42,26 @@ defmodule Membrane.Opus.Encoder do
See https://opus-codec.org/docs/opus_api-1.3.1/group__opus__encoder.html#gaa89264fd93c9da70362a0c9b96b9ca88.
"""
],
bitrate: [
spec: allowed_bitrates(),
default: :auto,
description: """
Explicit control of the Opus codec bitrate.
This can be :auto (default, OPUS_BITRATE_AUTO), :max (OPUS_BITRATE_MAX) or a value from 500 to 512000 bits per second.
See https://www.opus-codec.org/docs/html_api/group__opusencoder.html and https://www.opus-codec.org/docs/html_api/group__encoderctls.html#ga0bb51947e355b33d0cb358463b5101a7
"""
],
signal_type: [
spec: allowed_signal_types(),
default: :auto,
description: """
Explicit control of the Opus signal type.
This can be :auto (default, OPUS_SIGNAL_AUTO), :voice (OPUS_SIGNAL_VOICE) or :music (OPUS_SIGNAL_MUSIC)
See https://www.opus-codec.org/docs/html_api/group__opusencoder.html and https://www.opus-codec.org/docs/html_api/group__encoderctls.html#gaaa87ccee4ae46aa6c9528e03c5122b89
"""
],
input_stream_format: [
spec: RawAudio.t(),
type: :stream_format,
Expand Down Expand Up @@ -190,8 +225,10 @@ defmodule Membrane.Opus.Encoder do
defp mk_native!(state) do
with {:ok, channels} <- validate_channels(state.input_stream_format.channels),
{:ok, input_rate} <- validate_sample_rate(state.input_stream_format.sample_rate),
{:ok, application} <- map_application_to_value(state.application) do
Native.create(input_rate, channels, application)
{:ok, application} <- map_application_to_value(state.application),
{:ok, bitrate} <- parse_bitrate(state.bitrate),
{:ok, signal_type} <- parse_signal_type(state.signal_type) do
Native.create(input_rate, channels, application, bitrate, signal_type)
else
{:error, reason} ->
raise "Failed to create encoder, reason: #{inspect(reason)}"
Expand All @@ -216,6 +253,26 @@ defmodule Membrane.Opus.Encoder do
defp validate_channels(channels) when channels in @allowed_channels, do: {:ok, channels}
defp validate_channels(_invalid_channels), do: {:error, "Invalid channels"}

defp parse_bitrate(bitrate) when bitrate in @allowed_bitrates do
case bitrate do
:auto -> {:ok, -1000}
:max -> {:ok, -1}
value -> {:ok, value}
end
end

defp parse_bitrate(_invalid_bitrates), do: {:error, "Invalid bitrate"}

defp parse_signal_type(signal_type) when signal_type in @allowed_signal_type do
case signal_type do
:auto -> {:ok, -1000}
:voice -> {:ok, 3001}
:music -> {:ok, 3002}
end
end

defp parse_signal_type(_invalid_signal_type), do: {:error, "Invalid signal type"}

defp frame_size(state) do
# 20 milliseconds
div(state.input_stream_format.sample_rate, 1000) * 20
Expand Down

0 comments on commit 1f1d381

Please sign in to comment.