This is a Flutter plugin for the PJSIP library. It provides a Dart API for the PJSIP library, and it is implemented using Dart FFI.
You can use this plugin as a user by adding it to your pubspec.yaml
:
flutter pub add flutter_pjsip
Documentation for the API will be available later. TODO: Add link to API docs.
TODO: Move hacker's guide to separate document and add link to it here.
This project based on Flutter FFI plugin, a specialized package that includes native code directly invoked with Dart FFI.
For simplicity we use Melos to manage multi-package project. Melos is a Dart tool for managing Dart projects with multiple packages, inspired by Lerna for JavaScript.
dart pub global activate melos
Before you start hacking, you need to prepare the project and install Android NDK - the minimum recommended version is 25.1.8937393. Make sure that you have Android SDK installed and ANDROID_HOME
environment variable is set. Also you have to set ANDROID_NDK_HOME
environment variable to the path to Android NDK.
By running the following command, melos will boostrap the project, get PJSIP source code, generate FFI bindings, and build PJSIP for all platforms:
melos prepare
Alternatively, you can execute prepare steps one by one. First, you need to install dependencies for all packages:
melos bs
Here bs
means bootstrap
.
Then you need to get PJSIP source code:
melos get-pjsip
Next step is to generate FFI bindings:
melos gen-ffi-bindings
Now you ready to build the project. You can build PJSIP for all platforms at once or for a specific platform:
# All platforms:
melos build
# iOS:
melos build-ios
# Android:
melos build-android
If you have Can't load Kernel binary: Invalid SDK hash.
error when running melos
command, you need to run the following command:
dart pub global deactivate melos
dart pub global activate melos
This will reinstall melos and fix the issue.
If you have configure-android error: compiler not found, please check environment settings (TARGET ABI, etc)
error, make sure you have the minimum recommended version installed.
Before committing your changes, you should run static analysis and all tests for all packages:
melos check-all
Alternatively, you can run static analysis and tests for all packages:
# Static analysis:
melos analyze
# Check code formatting:
melos check-format
# Tests:
melos test
Also you can run test separately:
# Dart tests (but there is no Dart tests yet):
melos test-dart
# Flutter tests
melos test-flutter
# Integration tests
melos test-integration
Intergation tests can be run on one of you devices! So, make sure that you have at least one device connected to your computer. If you have multiple devices connected, you can specify device ID (elsewise, you will get 'More than one device connected...' error).
melos test-integration -- -d iPhone
It can be applied to run other commands that includes test-integration
command:
melos test -- -d iPhone
melos check-all -- -d iPhone
Usually you don't need to version and publish packages manually. All heavy lifting is done by CI/CD. But if you need to do it manually, you can do it by running the following command:
# Bump version
melos version
# Publish packages
melos publish-with-hooks
Yeah, melos don't support hooks for publish command, so we need to use publish-with-hooks
command instead, which runs pre-publish.sh
and post-publish.sh
scripts.
For creating an archive with compiled libraries, you can run the following command:
melos create-lib-archive
It will create lib.tar.gz
archive in packages/flutter_pjsip/target
folder. Note: this command doesn't build libraries, so you need to run melos build
command before. This command is useful for creating github release artifact.
In case of any issues, you can clean up everything and start from scratch. This will clean up all packages and remove all generated files:
melos clean
You can also clean up working tree, but keep generated files (dart, binary libraries, etc):
melos clean-working-tree
This can be useful while preparing gh artifact or publishing packages.
We forced to move actual project to packages/flutter_pjsip
folder because of Melos limitations (it's not compatible with the latest file
package which is used by ffi
package). So, we have the following structure:
/
- root folder withmelos.yaml
file/scripts
- scripts for melos/packages/flutter_pjsip
- actual project/packages/flutter_pjsip/example
- example project
The actual project flutter_pjsip
project uses the following structure:
-
src
: Contains the native source code, and a CmakeFile.txt file for building that source code into a dynamic library. -
lib
: Contains the Dart code that defines the API of the plugin, and which calls into the native code usingdart:ffi
. -
platform folders (
android
,ios
): Contains the build files for building and bundling the native code library with the platform application. -
android/libs
andios/Frameworks
: Contains the native libraries and frameworks that are bundled with the platform application.
The pubspec.yaml
specifies FFI plugins as follows:
plugin:
platforms:
some_platform:
ffiPlugin: true
This configuration invokes the native build for the various target platforms and bundles the binaries in Flutter applications using these FFI plugins.
This can be combined with dartPluginClass, such as when FFI is used for the implementation of one platform in a federated plugin:
plugin:
implements: some_other_plugin
platforms:
some_platform:
dartPluginClass: SomeClass
ffiPlugin: true
A plugin can have both FFI and method channels:
plugin:
platforms:
some_platform:
pluginClass: SomeName
ffiPlugin: true
The native build systems that are invoked by FFI (and method channel) plugins are:
- For Android: Gradle, which invokes the Android NDK for native builds.
- See the documentation in android/build.gradle.
- For iOS and MacOS: Xcode, via CocoaPods.
- See the documentation in ios/flutter_pjsip.podspec.
- See the documentation in macos/flutter_pjsip.podspec.
- For Linux and Windows: CMake.
- See the documentation in linux/CMakeLists.txt.
- See the documentation in windows/CMakeLists.txt.
To use the native code, bindings in Dart are needed.
To avoid writing these by hand, they are generated from the header file
(src/flutter_pjsip.h
) by package:ffigen
.
Regenerate the bindings by running flutter pub run ffigen --config ffigen.yaml
.
Very short-running native functions can be directly invoked from any isolate.
Longer-running functions should be invoked on a helper isolate to avoid dropping frames in Flutter applications.
For help getting started with Flutter, view our online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.