Compare commits
No commits in common. "b95baa67996b276597ffde63b9601194cfa52b9b" and "3669312329bd8156d218aa7dbb770c96de8ebe3e" have entirely different histories.
b95baa6799
...
3669312329
4 changed files with 41 additions and 328 deletions
22
LICENSE
22
LICENSE
|
|
@ -1,21 +1 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025-2026 Andrew 'nuark' G.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
TODO: Add your license here.
|
||||
|
|
|
|||
235
README.md
235
README.md
|
|
@ -1,237 +1,8 @@
|
|||
# liblinphone_flutter
|
||||
|
||||
A Flutter plugin that provides integration with the Linphone library for VoIP calls (audio and video) on Android and iOS platforms.
|
||||
libLinPhone integration library for Flutter apps
|
||||
|
||||
## Third-Party Licenses
|
||||
## Getting Started
|
||||
|
||||
This project depends on the Linphone SDK, developed by Belledonne Communications, which is dual-licensed under the GNU Affero General Public License v3 (AGPLv3) and a proprietary commercial license.
|
||||
Better docs will be available some day
|
||||
|
||||
This wrapper library itself is licensed under the MIT License. However, it is designed to work with the Linphone SDK, and therefore **does not remove or replace the licensing requirements of Linphone**.
|
||||
|
||||
### Important
|
||||
|
||||
Any application using this plugin and linking against the Linphone SDK is subject to the terms of the AGPLv3, unless a commercial license for Linphone is obtained.
|
||||
|
||||
In practice, this means that applications using this plugin must comply with AGPLv3 requirements, which may include releasing the application's source code under a compatible license.
|
||||
|
||||
For proprietary or closed-source applications, you must obtain a commercial license for the Linphone SDK from Belledonne Communications.
|
||||
|
||||
## Description
|
||||
|
||||
This plugin wraps the native Linphone SDK (liblinphone) and exposes a Dart API for making SIP-based voice and video calls in Flutter applications.
|
||||
|
||||
## Features
|
||||
|
||||
- SIP account registration
|
||||
- Audio and video calls
|
||||
- Video streaming with local preview and remote view
|
||||
- Call management (answer, hangup, toggle video/microphone)
|
||||
- Registration and call state event streams
|
||||
|
||||
## Platform Support
|
||||
|
||||
| Platform | Status |
|
||||
| -------- | --------- |
|
||||
| Android | Supported |
|
||||
| iOS | Supported |
|
||||
|
||||
## Requirements
|
||||
|
||||
- Flutter SDK >= 3.3.0
|
||||
- Dart SDK >= 3.9.0-333.2.beta
|
||||
- Linphone SDK (native dependency)
|
||||
|
||||
## Installation
|
||||
|
||||
Add this to your `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
liblinphone_flutter: ^0.0.3
|
||||
```
|
||||
|
||||
Or if you want to go with git:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
liblinphone_flutter:
|
||||
git:
|
||||
url: https://git.nuark.xyz/nuark/liblinphone_flutter.git
|
||||
ref: commit-hash
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
### Platform-Specific Setup
|
||||
|
||||
#### Android
|
||||
|
||||
Ensure you have the necessary permissions in your `AndroidManifest.xml`:
|
||||
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
```
|
||||
|
||||
#### iOS
|
||||
|
||||
The plugin requires access to the camera and microphone. Add the following to your `Info.plist`:
|
||||
|
||||
```xml
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Camera access is required for video calls</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Microphone access is required for audio and video calls</string>
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
|
||||
```dart
|
||||
import 'package:liblinphone_flutter/liblinphone_flutter.dart';
|
||||
|
||||
final liblinphone = LiblinphoneFlutter();
|
||||
|
||||
// Initialize
|
||||
await liblinphone.checkPermissions();
|
||||
await liblinphone.initialize();
|
||||
|
||||
// Register to SIP server
|
||||
await liblinphone.register(
|
||||
'username',
|
||||
'password',
|
||||
'sip.server.com',
|
||||
5060,
|
||||
);
|
||||
|
||||
// Listen to registration events
|
||||
liblinphone.registrationEvents.listen((state) {
|
||||
print('Registration state: $state');
|
||||
});
|
||||
|
||||
// Listen to call events
|
||||
liblinphone.callEvents.listen((state) {
|
||||
print('Call state: $state');
|
||||
});
|
||||
|
||||
// Make a video call
|
||||
await liblinphone.makeCall('sip:recipient@sip.server.com', true);
|
||||
|
||||
// Answer incoming call
|
||||
await liblinphone.answerCall();
|
||||
|
||||
// Toggle video during call
|
||||
await liblinphone.toggleVideo();
|
||||
|
||||
// Toggle microphone
|
||||
await liblinphone.toggleMicrophone();
|
||||
|
||||
// Hangup
|
||||
await liblinphone.hangupCall();
|
||||
|
||||
// Unregister and cleanup
|
||||
await liblinphone.unregister();
|
||||
await liblinphone.stop();
|
||||
```
|
||||
|
||||
### Video Views
|
||||
|
||||
For video calls, use the provided widgets to display local and remote video streams:
|
||||
|
||||
```dart
|
||||
import 'package:liblinphone_flutter/widgets/local_view.dart';
|
||||
import 'package:liblinphone_flutter/widgets/remote_view.dart';
|
||||
|
||||
// In your widget tree
|
||||
Column(
|
||||
children: [
|
||||
// Remote video (full screen or large area)
|
||||
Expanded(
|
||||
child: RemoteView(),
|
||||
),
|
||||
// Local video preview (picture-in-picture)
|
||||
SizedBox(
|
||||
height: 150,
|
||||
width: 100,
|
||||
child: LocalView(),
|
||||
),
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Main Class: `LiblinphoneFlutter`
|
||||
|
||||
#### Methods
|
||||
|
||||
| Method | Description |
|
||||
| ----------------------------------------------------------------- | -------------------------------------------------- |
|
||||
| `Future<bool> checkPermissions()` | Checks and requests camera/microphone permissions |
|
||||
| `Future<bool> initialize()` | Initializes the Linphone core |
|
||||
| `Future<bool> register(username, password, serverIp, serverPort)` | Registers to a SIP server |
|
||||
| `Future<bool> unregister()` | Unregisters from the SIP server |
|
||||
| `Future<bool> makeCall(callTo, isVideoEnabled)` | Makes an outgoing call |
|
||||
| `Future<bool> answerCall()` | Answers an incoming call |
|
||||
| `Future<bool> hangupCall()` | Hangs up the current call |
|
||||
| `Future<bool> inCall()` | Returns true if there is an active call |
|
||||
| `Future<CallType> callType()` | Returns the type of the current call (audio/video) |
|
||||
| `Future<bool> toggleVideo()` | Toggles video enabled state during a call |
|
||||
| `Future<bool> toggleMicrophone()` | Toggles microphone muted state |
|
||||
| `Future<bool> stop()` | Stops the Linphone core |
|
||||
| `Future<void> syncCurrentState()` | Forces synchronization of current state |
|
||||
|
||||
#### Event Streams
|
||||
|
||||
| Stream | Type | Description |
|
||||
| -------------------- | --------------------------- | -------------------------------- |
|
||||
| `registrationEvents` | `Stream<RegistrationState>` | Emits registration state changes |
|
||||
| `callEvents` | `Stream<CallState>` | Emits call state changes |
|
||||
|
||||
### Enums
|
||||
|
||||
#### `RegistrationState`
|
||||
|
||||
- `None` - Not registered
|
||||
- `Progress` - Registration in progress
|
||||
- `Ok` - Successfully registered
|
||||
- `Cleared` - Registration cleared
|
||||
- `Failed` - Registration failed
|
||||
|
||||
#### `CallState`
|
||||
|
||||
- `Idle` - No active call
|
||||
- `IncomingReceived` - Incoming call received
|
||||
- `OutgoingInit` - Outgoing call initialized
|
||||
- `OutgoingProgress` - Outgoing call in progress
|
||||
- `OutgoingRinging` - Remote party ringing
|
||||
- `Connected` - Call connected
|
||||
- `StreamsRunning` - Media streams running
|
||||
- `Pausing` - Call pausing
|
||||
- `Paused` - Call paused
|
||||
- `Resuming` - Call resuming
|
||||
- `Error` - Call error
|
||||
- `End` - Call ended
|
||||
- And other states...
|
||||
|
||||
#### `CallType`
|
||||
|
||||
- `Audio` - Audio-only call
|
||||
- `Video` - Video call
|
||||
- `Unknown` - Call type unknown
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
|
||||
## Links
|
||||
|
||||
- [Homepage](https://git.nuark.xyz/nuark/liblinphone_flutter)
|
||||
- [Linphone Project](https://www.linphone.org/)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,27 @@
|
|||
// import 'package:flutter/services.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
// import 'package:liblinphone_flutter/liblinphone_flutter_method_channel.dart';
|
||||
import 'package:liblinphone_flutter/liblinphone_flutter_method_channel.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// MethodChannelLiblinphoneFlutter platform = MethodChannelLiblinphoneFlutter();
|
||||
// const MethodChannel channel = MethodChannel('liblinphone_flutter');
|
||||
MethodChannelLiblinphoneFlutter platform = MethodChannelLiblinphoneFlutter();
|
||||
const MethodChannel channel = MethodChannel('liblinphone_flutter');
|
||||
|
||||
setUp(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
channel,
|
||||
(MethodCall methodCall) async {
|
||||
return '42';
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
expect(await platform.getPlatformVersion(), '42');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,84 +1,29 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
// import 'package:liblinphone_flutter/liblinphone_flutter.dart';
|
||||
import 'package:liblinphone_flutter/liblinphone_flutter.dart';
|
||||
import 'package:liblinphone_flutter/liblinphone_flutter_platform_interface.dart';
|
||||
// import 'package:liblinphone_flutter/liblinphone_flutter_method_channel.dart';
|
||||
import 'package:liblinphone_flutter/models/call_type.dart';
|
||||
import 'package:liblinphone_flutter/liblinphone_flutter_method_channel.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
class MockLiblinphoneFlutterPlatform
|
||||
with MockPlatformInterfaceMixin
|
||||
implements LiblinphoneFlutterPlatform {
|
||||
@override
|
||||
Future<bool> answerCall() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CallType> callType() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkPermissions() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> hangupCall() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> inCall() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> initialize() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> makeCall(String callTo, bool isVideoEnabled) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> register(
|
||||
String username,
|
||||
String password,
|
||||
String serverIp,
|
||||
int serverPort,
|
||||
) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> stop() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> syncCurrentState() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> toggleMicrophone() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> toggleVideo() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> unregister() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
Future<String?> getPlatformVersion() => Future.value('42');
|
||||
}
|
||||
|
||||
void main() {
|
||||
// final LiblinphoneFlutterPlatform initialPlatform = LiblinphoneFlutterPlatform.instance;
|
||||
final LiblinphoneFlutterPlatform initialPlatform = LiblinphoneFlutterPlatform.instance;
|
||||
|
||||
test('$MethodChannelLiblinphoneFlutter is the default instance', () {
|
||||
expect(initialPlatform, isInstanceOf<MethodChannelLiblinphoneFlutter>());
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
LiblinphoneFlutter liblinphoneFlutterPlugin = LiblinphoneFlutter();
|
||||
MockLiblinphoneFlutterPlatform fakePlatform = MockLiblinphoneFlutterPlatform();
|
||||
LiblinphoneFlutterPlatform.instance = fakePlatform;
|
||||
|
||||
expect(await liblinphoneFlutterPlugin.getPlatformVersion(), '42');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue