opus_dart

Wraps libopus in dart, and additionally provides a dart friendly API for encoding and decoding.

Table of Contents
  1. Versioning
  2. Choosing The Right Library
    2.1 The Bindings
    2.2 The Dart Friendly API
  3. Initialization
    3.1 The Bindings
    3.2 The Dart Friendly API
    3.3 What is lib?
    3.4 Flutter

Versioning

Current libopus version: 1.3.1

See the changelog for other versions of libopus.

On small patches, the patch version of this package will increase, if a new Opus version is wrapped (but there are no API changes), the minor version will increase, and on breaking API changes, the major version will increase. So to ensure to get a specific version of Opus, lock in on major and minor version (e.g. opus_dart: ">=2.0.0 <2.1.0"), to always use the newest Opus version with compatible API, lock on the major version (e.g. opus_dart: ">=2.0.0 <3.0.0").

Choosing The Right Library

The Bindings

There are automatically generated bindings for most functions of the Opus include headers. Variadic (especially the CTL) functions are not supported in Dart at the moment, so they are missing, as well as makros. The generated bindings can all be found in the /wrappers section and are named after the group they are from. Documentation of the bounded functions was copied from the Opus headers (and is thus not very well formated). For sake of completeness the tool folder contains the code that was used for generation. NOTE that ffi_tool ^0.4.0 is needed for generation, which might not be yet available on pub, so it's used directly from GitHub. Also, since in the meantime a somewhat official package to create ffi bindings - ffigen - emerged, this should be used to generate bindings for further opus versions.

The Dart Friendly API

Most users are interested in a more Dart like API to encode and decode Opus packages. The opus_dart library provides this, so you don't have to take care of memory allocation and similar tasks. The OpusEncoder and OpusDecoder are both implemented in two ways:

  • SimpleOpusEncoder / SimpleOpusDecoder
    These coders are very simple to use as they handle memory allocation/deallocation for you. As consequence, they make heavy use of buffer allocation.
  • BufferedOpusEncoder / BufferedOpusDecoder
    These coders allocate native memory just once. You can then directly write to the memory. When writing to the memory, you have to update the corresponding input index.

Performance gains of using the buffered versions over the simple versions varies. Befor trying to optimize your code using a buffered version, try the simple version first.

There are also StreamTransformers that can help you encoding PCM streams to Opus packets, or decode Opus packets to PCM streams.

WARNING: All classes have to be destroyed manually by calling their appropriate methods, so that the allocated native memory can be released. Otherwise, a memory leak may occur!

Initialization

The Bindings

Each generated library in /wrappers (except opus_defines.h) need to be initialized if used. The generated libraries are intended to be used with a prefix, because they sometimes have functions with the same signature. For example, you would import them using

import 'package:opus_dart/wrappers/opus_libinfo.dart' as opus_libinfo;
import 'package:opus_dart/wrappers/opus_custom.dart' as opus_custom;

and then you can call in your startup logic

late final opus_libinfo.FunctionsAndGlobals libinfo;
late final opus_custom.FunctionsAndGlobals custom;
void main(){
    libinfo=opus_libinfo.FunctionsAndGlobals(lib);
    custom=opus_custom.FunctionsAndGlobals(lib);
}

Finally, you can use the objects libinfo and custom to access the functions and globals.

The Dart Friendly API

If using the dart firendly library opus_dart, you also have to initialize it using the toplevel initOpus function, but unlike the bindings, there is no need to import it with a prefix. This would look like:

import 'package:opus_dart/opus_dart.dart';

void main(){
    initOpus(lib);
    // Check if you loaded the right version
    print(getOpusVersion());
}

What is lib?

As you may have noticed above, both, the Dart friendly API and the bindings need lib to initalize. lib is a DynamicLibrary instance, pointing to libopus. On a dart:vm platform, you can dynamically load it:

import 'dart:ffi';

void main() {
  DynamicLibrary lib;
  if (Platform.isWindows) {
    bool x64 = Platform.version.contains('x64');
    if (x64) {
      lib = new DynamicLibrary.open('path/to/libopus_x64.dll');
    } else {
      lib = new DynamicLibrary.open('path/to/libopus_x86.dll');
    }
  } else if (Platform.isLinux) {
    lib = new DynamicLibrary.open('/usr/local/lib/libopus.so');
  }
}

This package does not contain any binaries, and even if libopus is open source, no source files are included in this package either, since there is no native build system for the dart:vm at the moment. It's up to you to get them and to distribute them with your application. Keep in mind that you need a dynamic library for all operating systems and architectures you want to support. Whether you use prebuild binaries or compile libopus from source yourself, the version you use should match this packages wrapped version (see above). An example to build Opus can be found in the Dockerfile on this packages GitHub page.

Flutter

If you are using Flutter, you can use opus_flutter to easily obtain a DynamicLibrary of libopus:

import 'package:opus_dart/opus_dart.dart';
import 'package:opus_flutter/opus_flutter.dart' as opus_flutter;

void main(){
    initOpus(await opus_flutter.load());
    print(getOpusVersion());
}

Libraries

wrappers/opus_custom
WARNING! Trying to use opus_custom will FAIL if opus_custom support was not enabled during library building!
opus_dart
A dart friendly api for encoding and decoding opus packets. Must be initalized using the initOpus function.
wrappers/opus_decoder
Contains methods and structs from the opus_decoder group of opus.h. SHOULD be imported as opus_decoder and MUST be initalized using its init function.
wrappers/opus_defines
Contains methods defined contansts from opus_defines.h
wrappers/opus_encoder
Contains methods and structs from the opus_encoder group of opus.h. SHOULD be imported as opus_encoder and MUST be initalized using its init function.
wrappers/opus_libinfo
Contains methods and structs from the opus_libinfo group of opus_defines.h. SHOULD be imported as opus_libinfo and MUST be initalized using its init function.
wrappers/opus_multistream
Contains methods and structs from the opus_multistream group of opus_multistream.h. SHOULD be imported as opus_multistream and MUST be initalized using its init function.
wrappers/opus_projection
Contains methods and structs from the opus_projection group of opus_projection.h. SHOULD be imported as opus_projection and MUST be initalized using its init function.
wrappers/opus_repacketizer
Contains methods and structs from the opus_repacketizer group of opus.h. SHOULD be imported as opus_repacketizer and MUST be initalized using its init function.