opus_dart
Wraps libopus in dart, and additionally provides a dart friendly API for encoding and decoding.
Table of Contents
- Versioning
- Choosing The Right Library
2.1 The Bindings
2.2 The Dart Friendly API - Initialization
3.1 The Bindings
3.2 The Dart Friendly API
3.3 What islib
?
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: ">=3.0.0 <3.1.0"
), to always use the newest Opus version with compatible API, lock on the major version (e.g. opus_dart: ">=4.0.0 <4.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.
Since web support was introduced in vesion 3.0.0
of this package, lib
is something different on platforms
that support dart:ffi
, and on the web, where dart:ffi
is not available, and where web_ffi is used to emulate dart:ffi
.
On a dart:ffi
platform, lib
is a dart:ffi DynamicLibrary instance, pointing to libopus. You can dynamically load it:
import 'dart:ffi';
import 'dart:io' show Platform;
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');
}
}
On the web, lib
is a web_ffi DynamicLibrary instance.
You can also dynamically load it, but you have to inject some JavaScript into your page first. A detailed walkthrough can be found in web_ffi's example.
Note: If you use the dart fiendly API with web_ffi
, all Opaque
types are registered automatically. If you use the bindings with web_ffi
, you have to register them manually!
Also, it might be interesting to study the example, what makes use of conditional imports and initalization.
Attention: 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 dart:ffi at the moment. There is also no build system for WebAssembly integrated in dart at the moment. It's up to you to get binaries 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. An example to build it for the web can be found in web_ffi's example.
Flutter
If you are using Flutter, you can use opus_flutter to easily obtain a DynamicLibrary
of libopus, for both, dart:ffi
and web platforms:
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
- opus_dart
- A dart friendly api for encoding and decoding opus packets. Must be initalized using the initOpus function.
- wrappers/opus_custom
- WARNING! Trying to use opus_custom will FAIL if opus_custom support was not enabled during library building!
- wrappers/opus_decoder
- Contains methods and structs from the opus_decoder group of opus.h. SHOULD be imported as opus_decoder.
- 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.
- wrappers/opus_libinfo
- Contains methods and structs from the opus_libinfo group of opus_defines.h. SHOULD be imported as opus_libinfo.
- wrappers/opus_multistream
- Contains methods and structs from the opus_multistream group of opus_multistream.h. SHOULD be imported as opus_multistream.
- wrappers/opus_projection
- Contains methods and structs from the opus_projection group of opus_projection.h. SHOULD be imported as opus_projection.
- wrappers/opus_repacketizer
- Contains methods and structs from the opus_repacketizer group of opus.h. SHOULD be imported as opus_repacketizer.