First Cut

This commit is contained in:
Sarah Jamie Lewis 2024-02-11 15:12:51 -08:00
parent 68c83f2c9a
commit 15e4793ba8
60 changed files with 94411 additions and 19 deletions

View File

@ -4,11 +4,13 @@ import 'package:cwtch/main.dart';
import 'package:cwtch/models/appstate.dart';
import 'package:cwtch/models/contact.dart';
import 'package:cwtch/models/message.dart';
import 'package:cwtch/models/messages/VidMessage.dart';
import 'package:cwtch/models/profilelist.dart';
import 'package:cwtch/models/profileservers.dart';
import 'package:cwtch/models/remoteserver.dart';
import 'package:cwtch/models/servers.dart';
import 'package:cwtch/notification_manager.dart';
import 'package:cwtch/views/vidchatview.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
@ -187,6 +189,24 @@ class CwtchNotifier {
break;
case "NewMessageFromPeer":
var identifier = int.parse(data["ConversationID"]);
dynamic message = jsonDecode(data["Data"]);
var overlay = int.parse(message['o'].toString());
if (overlay == VideoOverlay) {
try {
VidMessage vmsg = VidMessage.fromJson(message);
EnvironmentConfig.debugLog("send latency = ${DateTime.now().toUtc().difference(vmsg.s ?? DateTime.now()).inMilliseconds}");
EnvironmentConfig.debugLog("tor latency = ${DateTime.now().toUtc().difference(vmsg.t ?? DateTime.now()).inMilliseconds}");
EnvironmentConfig.debugLog("recv latency = ${DateTime.now().toUtc().difference(vmsg.r ?? DateTime.now()).inMilliseconds}");
profileCN.getProfile(data["ProfileOnion"])!.contactList.getContact(identifier)!.vid = vmsg.d;
} catch (e) {
EnvironmentConfig.debugLog("error decoding video stream: $e: ${message['d']}");
}
break;
}
var messageID = int.parse(data["Index"]);
var timestamp = DateTime.tryParse(data['TimestampReceived'])!;
var senderHandle = data['RemotePeer'];

View File

@ -648,4 +648,20 @@ class MaterialLocalizationLu extends MaterialLocalizations {
@override
// TODO: implement scanTextButtonLabel
String get scanTextButtonLabel => throw UnimplementedError();
@override
// TODO: implement lookUpButtonLabel
String get lookUpButtonLabel => throw UnimplementedError();
@override
// TODO: implement menuDismissLabel
String get menuDismissLabel => throw UnimplementedError();
@override
// TODO: implement searchWebButtonLabel
String get searchWebButtonLabel => throw UnimplementedError();
@override
// TODO: implement shareButtonLabel
String get shareButtonLabel => throw UnimplementedError();
}

View File

@ -1,5 +1,7 @@
import 'dart:convert';
import 'dart:ffi';
import 'package:cwtch/config.dart';
import 'package:cwtch/main.dart';
import 'package:cwtch/models/message_draft.dart';
import 'package:cwtch/models/profile.dart';
@ -7,6 +9,7 @@ import 'package:cwtch/models/redaction.dart';
import 'package:cwtch/themes/opaque.dart';
import 'package:cwtch/views/contactsview.dart';
import 'package:cwtch/widgets/messagerow.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -133,6 +136,14 @@ class ContactInfoState extends ChangeNotifier {
MessageDraft get messageDraft => this._messageDraft;
DateTime get lastRetryTime => this._lastRetryTime;
Uint8List _vid = Uint8List(0);
Uint8List get vid => _vid;
set vid(data) {
this._vid = data;
notifyListeners();
}
set lastRetryTime(DateTime lastRetryTime) {
this._lastRetryTime = lastRetryTime;
notifyListeners();

View File

@ -20,7 +20,7 @@ const QuotedMessageOverlay = 10;
const SuggestContactOverlay = 100;
const InviteGroupOverlay = 101;
const FileShareOverlay = 200;
const VideoOverlay = 0xF07;
// Defines the length of the tor v3 onion address. Code using this constant will
// need to updated when we allow multiple different identifiers. At which time
// it will likely be prudent to define a proper Contact wrapper.

View File

@ -0,0 +1,23 @@
import 'package:cwtch/third_party/base85/base_85_encoder.dart';
import 'package:flutter/foundation.dart';
import '../../third_party/base85/base_85_codec.dart';
class VidMessage {
final int o;
final Uint8List d;
DateTime? s;
DateTime? r;
DateTime? t;
VidMessage({required this.o, required this.d});
VidMessage.fromJson(Map<String, dynamic> json)
: o = json['o'],
d = Base85Codec.new().decode(json['d']),
s = DateTime.tryParse(json['s'] ?? ""),
t = DateTime.tryParse(json['t'] ?? ""),
r = DateTime.tryParse(json['r'] ?? "");
Map<String, dynamic> toJson() => {'o': o, 'd': Base85Codec.new().encode(d), 's': DateTime.now().toUtc().toIso8601String()};
}

1
lib/third_party/base85 vendored Submodule

@ -0,0 +1 @@
Subproject commit 51d8c18dd869702fbef77ce574493078a2acf54d

View File

@ -0,0 +1,31 @@
## 0.0.1
* Initial release.
## 0.0.2
* Fixes in core API.
## 0.0.3
* Created API for storing frame into Base64 Image.
## 0.0.4
* Code refractor
## 0.0.5
* Converted API functions into asyc functions.
## 0.0.6
* Dart SDK version changed to 3.1.0
## 0.0.7
* OpenCV Dependencies added statically
## 0.0.8
* OpenCV static dependencies removed

60
lib/third_party/camera_linux/LICENSE vendored Normal file
View File

@ -0,0 +1,60 @@
BSD 3-Clause License
Copyright (c) 2023, Open Privacy Research Society
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----
Original License Follows...
BSD 3-Clause License
Copyright (c) 2023, Muhammad Islam
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

93
lib/third_party/camera_linux/README.md vendored Normal file
View File

@ -0,0 +1,93 @@
# camera_linux
Camera Linux is a Flutter FFI plugin designed specifically for the Linux platform. This plugin allows Flutter developers to access and capture images from the Linux camera, seamlessly integrating native code invocation using Dart's FFI.
## Getting Started
Dart FFI Integration: Built on top of Flutter's FFI (Foreign Function Interface) to directly invoke native functions.
Platform-Specific: Tailored for Linux, ensuring optimal performance and compatibility.
Easy Access to Camera: Provides straightforward functions to capture images directly from the Linux camera.
## Project structure
This template 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 using `dart:ffi`.
* platform folder (`linux`): Contains the build files
for building and bundling the native code library with the platform application.
## Building and bundling native code
The `pubspec.yaml` specifies FFI plugins as follows:
```yaml
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:
```yaml
plugin:
implements: some_other_plugin
platforms:
some_platform:
dartPluginClass: SomeClass
ffiPlugin: true
```
A plugin can have both FFI and method channels:
```yaml
plugin:
platforms:
some_platform:
pluginClass: SomeName
ffiPlugin: true
```
The native build systems that are invoked by FFI (and method channel) plugin is:
* For Linux and Windows: CMake.
* See the documentation in linux/CMakeLists.txt.
* See the documentation in windows/CMakeLists.txt.
## Binding to native code
Instead of manually writing the Dart bindings to native code, they are auto-generated from the header file (src/camera_linux.h) using the package:ffigen. To refresh these bindings, execute:
`flutter pub run ffigen --config ffigen.yaml`
## Invoking native code
Very short-running native functions can be directly invoked from any isolate.
For example, see `sum` in `lib/camera_linux.dart`.
Longer-running functions should be invoked on a helper isolate to avoid
dropping frames in Flutter applications.
For example, see `sumAsync` in `lib/camera_linux.dart`.
## Getting Started with the Plugin
Add the camera_linux plugin to your pubspec.yaml:
```dependencies:
camera_linux: ^0.1.0
```
Execute the following command to fetch the package:
`flutter pub get`
## Further Assistance
For comprehensive guidance on Flutter, visit the official documentation, offering tutorials, samples, mobile development insights, and a complete API reference.

View File

@ -0,0 +1,19 @@
# Run with `flutter pub run ffigen --config ffigen.yaml`.
name: CameraLinuxBindings
description: |
Bindings for `src/camera_linux.h`.
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
output: 'lib/camera_linux_bindings_generated.dart'
headers:
entry-points:
- 'src/camera_linux.h'
include-directives:
- 'src/camera_linux.h'
preamble: |
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
comments:
style: any
length: full

View File

@ -0,0 +1,38 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:typed_data';
import 'camera_linux_bindings_generated.dart';
import 'package:ffi/ffi.dart';
class CameraLinux {
late CameraLinuxBindings _bindings;
CameraLinux() {
final dylib = DynamicLibrary.open('libcamera_linux.so');
_bindings = CameraLinuxBindings(dylib);
}
// Open Default Camera
Future<void> initializeCamera() async {
_bindings.startVideoCaptureInThread();
}
// Close The Camera
void stopCamera() {
_bindings.stopVideoCapture();
}
// Capture The Frame
Future<Uint8List> captureImage() async {
final lengthPtr = calloc<Int>();
Pointer<Uint8> framePointer = _bindings.getLatestFrameBytes(lengthPtr);
final latestFrame = getLatestFrameData(framePointer, lengthPtr.value);
return latestFrame;
}
// Get The Latest Frame
Uint8List getLatestFrameData(Pointer<Uint8> framePointer, frameSize) {
List<int> frameList = framePointer.asTypedList(frameSize);
return Uint8List.fromList(frameList);
}
}

View File

@ -0,0 +1,48 @@
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
/// Bindings for `src/camera_linux.h`.
///
/// Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
///
class CameraLinuxBindings {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName) _lookup;
/// The symbols are looked up in [dynamicLibrary].
CameraLinuxBindings(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
CameraLinuxBindings.fromLookup(ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName) lookup) : _lookup = lookup;
void startVideoCaptureInThread() {
return _startVideoCaptureInThread();
}
late final _startVideoCaptureInThreadPtr = _lookup<ffi.NativeFunction<ffi.Void Function()>>('startVideoCaptureInThread');
late final _startVideoCaptureInThread = _startVideoCaptureInThreadPtr.asFunction<void Function()>();
void stopVideoCapture() {
return _stopVideoCapture();
}
late final _stopVideoCapturePtr = _lookup<ffi.NativeFunction<ffi.Void Function()>>('stopVideoCapture');
late final _stopVideoCapture = _stopVideoCapturePtr.asFunction<void Function()>();
ffi.Pointer<ffi.Uint8> getLatestFrameBytes(
ffi.Pointer<ffi.Int> length,
) {
return _getLatestFrameBytes(
length,
);
}
late final _getLatestFrameBytesPtr = _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Uint8> Function(ffi.Pointer<ffi.Int>)>>('getLatestFrameBytes');
late final _getLatestFrameBytes = _getLatestFrameBytesPtr.asFunction<ffi.Pointer<ffi.Uint8> Function(ffi.Pointer<ffi.Int>)>();
}

View File

@ -0,0 +1,22 @@
# The Flutter tooling requires that developers have CMake 3.10 or later
# installed. You should not increase this version, as doing so will cause
# the plugin to fail to compile for some customers of the plugin.
cmake_minimum_required(VERSION 3.10)
# Project-level configuration.
set(PROJECT_NAME "camera_linux")
project(${PROJECT_NAME} LANGUAGES CXX)
# Invoke the build for native code shared with the other target platforms.
# This can be changed to accommodate different builds.
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(camera_linux_bundled_libraries
# Defined in ../src/CMakeLists.txt.
# This can be changed to accommodate different builds.
$<TARGET_FILE:camera_linux>
PARENT_SCOPE
)

View File

@ -0,0 +1,74 @@
name: camera_linux
description: Take pictures from camere on linux platform.
version: 0.0.8
homepage: https://github.com/islamroshan/camera-linux.git
environment:
sdk: '>=3.1.0 <4.0.0'
flutter: '>=3.3.0'
dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
ffi: ^2.0.1
dev_dependencies:
ffigen: ^6.1.2
image: ^3.0.1
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
# which should be registered in the plugin registry. This is required for
# using method channels.
# The Android 'package' specifies package in which the registered class is.
# This is required for using method channels on Android.
# The 'ffiPlugin' specifies that native code should be built and bundled.
# This is required for using `dart:ffi`.
# All these are used by the tooling to maintain consistency when
# adding or updating assets for this project.
#
# Please refer to README.md for a detailed explanation.
plugin:
platforms:
linux:
ffiPlugin: true
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages

View File

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.10)
project(camera_linux_library VERSION 0.0.1 LANGUAGES C CXX)
set(SOURCE_FILES opencv_wrapper.cpp)
add_library(opencv_wrapper SHARED ${SOURCE_FILES})
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(opencv_wrapper ${OpenCV_LIBS})
add_library(camera_linux SHARED "camera_linux.c")
target_link_libraries(camera_linux opencv_wrapper)
set_target_properties(camera_linux PROPERTIES
PUBLIC_HEADER camera_linux.h
OUTPUT_NAME "camera_linux"
)
target_compile_definitions(camera_linux PUBLIC DART_SHARED_LIB)

View File

@ -0,0 +1,5 @@
#include "camera_linux.h"
void startVideoCaptureInThread();
void stopVideoCapture();

View File

@ -0,0 +1,15 @@
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void startVideoCaptureInThread();
void stopVideoCapture();
uint8_t* getLatestFrameBytes(int* length);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,86 @@
#include <iostream>
#include <opencv2/opencv.hpp>
#include <thread>
#include <atomic>
using namespace std;
using namespace cv;
std::thread videoThread;
std::atomic<bool> stopFlag(false);
Mat latestFrame;
// Extern C block to expose the function to C
extern "C" {
// Function to capture video frames
void runVideoCapture() {
VideoCapture cap;
int deviceID = 0; // 0 = open default camera
int apiID = cv::CAP_ANY; // 0 = autodetect default API
cap.open(deviceID, apiID);
cap.set(cv::CAP_PROP_FRAME_WIDTH, 160);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 120);
if (!cap.isOpened()) {
cerr << "ERROR! Unable to open camera\n";
return ;
}
while (!stopFlag.load()) {
cap >> latestFrame;
if (latestFrame.empty()) {
break;
}
}
cap.release();
}
void startVideoCaptureInThread() {
stopFlag = false;
videoThread = std::thread(runVideoCapture);
videoThread.detach();
}
void stopVideoCapture() {
stopFlag = true;
if (videoThread.joinable()) {
videoThread.join();
}
}
uint8_t* getLatestFrameBytes(int* length) {
// Ensure the pointer is valid
if (!length) {
return nullptr;
}
*length = 0; // Initialize length to 0
if (latestFrame.empty()) {
return nullptr;
}
vector<int> compression_params;
compression_params.push_back(IMWRITE_JPEG_QUALITY);
compression_params.push_back(50);
// Encode the frame as JPEG
vector<uint8_t> buf;
bool encodeSuccess = imencode(".jpg", latestFrame, buf, compression_params );
// Check if encoding was successful
if (!encodeSuccess || buf.empty()) {
return nullptr;
}
*length = static_cast<int>(buf.size());
cout << buf.size() << endl;
// Allocate memory and copy data
uint8_t* data = new uint8_t[(*length)];
if (!data) {
cout << "Memory allocation failed." << endl;
return nullptr;
}
memcpy(data, buf.data(), *length);
return data;
}
}

29
lib/third_party/miniaudio/.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/

42
lib/third_party/miniaudio/.metadata vendored Normal file
View File

@ -0,0 +1,42 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "41456452f29d64e8deb623a3c927524bcf9f111b"
channel: "stable"
project_type: plugin
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
- platform: android
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
- platform: ios
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
- platform: linux
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
- platform: macos
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
- platform: windows
create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
lib/third_party/miniaudio/LICENSE vendored Normal file
View File

@ -0,0 +1 @@
TODO: Add your license here.

15
lib/third_party/miniaudio/README.md vendored Normal file
View File

@ -0,0 +1,15 @@
# miniaudio
A new Flutter plugin project.
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.dev/developing-packages/),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter development, view the
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

View File

@ -0,0 +1,68 @@
group 'com.example.miniaudio'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
if (project.android.hasProperty("namespace")) {
namespace 'com.example.miniaudio'
}
compileSdkVersion 33
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
}
defaultConfig {
minSdkVersion 19
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
}
testOptions {
unitTests.all {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen {false}
showStandardStreams = true
}
}
}
}

View File

@ -0,0 +1 @@
rootProject.name = 'miniaudio'

View File

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.miniaudio">
</manifest>

View File

@ -0,0 +1,35 @@
package com.example.miniaudio
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** MiniaudioPlugin */
class MiniaudioPlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "miniaudio")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}

View File

@ -0,0 +1,27 @@
package com.example.miniaudio
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlin.test.Test
import org.mockito.Mockito
/*
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
*
* Once you have built the plugin's example app, you can run these tests from the command
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
* you can run them directly from IDEs that support JUnit such as Android Studio.
*/
internal class MiniaudioPluginTest {
@Test
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
val plugin = MiniaudioPlugin()
val call = MethodCall("getPlatformVersion", null)
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
plugin.onMethodCall(call, mockResult)
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
}
}

View File

@ -0,0 +1,8 @@
import 'miniaudio_platform_interface.dart';
class Miniaudio {
Future<String?> getPlatformVersion() {
return MiniaudioPlatform.instance.getPlatformVersion();
}
}

View File

@ -0,0 +1,17 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'miniaudio_platform_interface.dart';
/// An implementation of [MiniaudioPlatform] that uses method channels.
class MethodChannelMiniaudio extends MiniaudioPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodChannel = const MethodChannel('miniaudio');
@override
Future<String?> getPlatformVersion() async {
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
}

View File

@ -0,0 +1,29 @@
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'miniaudio_method_channel.dart';
abstract class MiniaudioPlatform extends PlatformInterface {
/// Constructs a MiniaudioPlatform.
MiniaudioPlatform() : super(token: _token);
static final Object _token = Object();
static MiniaudioPlatform _instance = MethodChannelMiniaudio();
/// The default instance of [MiniaudioPlatform] to use.
///
/// Defaults to [MethodChannelMiniaudio].
static MiniaudioPlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [MiniaudioPlatform] when
/// they register themselves.
static set instance(MiniaudioPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<String?> getPlatformVersion() {
throw UnimplementedError('platformVersion() has not been implemented.');
}
}

View File

@ -0,0 +1,94 @@
# The Flutter tooling requires that developers have CMake 3.10 or later
# installed. You should not increase this version, as doing so will cause
# the plugin to fail to compile for some customers of the plugin.
cmake_minimum_required(VERSION 3.10)
# Project-level configuration.
set(PROJECT_NAME "miniaudio")
project(${PROJECT_NAME} LANGUAGES CXX)
# This value is used when generating builds using this plugin, so it must
# not be changed.
set(PLUGIN_NAME "miniaudio_plugin")
# Any new source files that you add to the plugin should be added here.
list(APPEND PLUGIN_SOURCES
"miniaudio_plugin.cc"
)
# Define the plugin library target. Its name must not be changed (see comment
# on PLUGIN_NAME above).
add_library(${PLUGIN_NAME} SHARED
${PLUGIN_SOURCES}
)
# Apply a standard set of build settings that are configured in the
# application-level CMakeLists.txt. This can be removed for plugins that want
# full control over build settings.
apply_standard_settings(${PLUGIN_NAME})
# Symbols are hidden by default to reduce the chance of accidental conflicts
# between plugins. This should not be removed; any symbols that should be
# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
# Source include directories and library dependencies. Add any plugin-specific
# dependencies here.
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter)
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(miniaudio_bundled_libraries
""
PARENT_SCOPE
)
# === Tests ===
# These unit tests can be run from a terminal after building the example.
# Only enable test builds when building the example (which sets this variable)
# so that plugin clients aren't building the tests.
if (${include_${PROJECT_NAME}_tests})
if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
message("Unit tests require CMake 3.11.0 or later")
else()
set(TEST_RUNNER "${PROJECT_NAME}_test")
enable_testing()
# Add the Google Test dependency.
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/release-1.11.0.zip
)
# Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Disable install commands for gtest so it doesn't end up in the bundle.
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
FetchContent_MakeAvailable(googletest)
# The plugin's exported API is not very useful for unit testing, so build the
# sources directly into the test binary rather than using the shared library.
add_executable(${TEST_RUNNER}
test/miniaudio_plugin_test.cc
${PLUGIN_SOURCES}
)
apply_standard_settings(${TEST_RUNNER})
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(${TEST_RUNNER} PRIVATE flutter)
target_link_libraries(${TEST_RUNNER} PRIVATE PkgConfig::GTK)
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
# Enable automatic test discovery.
include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif() # CMake version check
endif() # include_${PROJECT_NAME}_tests

View File

@ -0,0 +1,26 @@
#ifndef FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_H_
#define FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_H_
#include <flutter_linux/flutter_linux.h>
G_BEGIN_DECLS
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define FLUTTER_PLUGIN_EXPORT
#endif
typedef struct _MiniaudioPlugin MiniaudioPlugin;
typedef struct {
GObjectClass parent_class;
} MiniaudioPluginClass;
FLUTTER_PLUGIN_EXPORT GType miniaudio_plugin_get_type();
FLUTTER_PLUGIN_EXPORT void miniaudio_plugin_register_with_registrar(
FlPluginRegistrar* registrar);
G_END_DECLS
#endif // FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_H_

92621
lib/third_party/miniaudio/linux/miniaudio.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
#include "include/miniaudio/miniaudio_plugin.h"
#include <flutter_linux/flutter_linux.h>
#include <gtk/gtk.h>
#include <sys/utsname.h>
#include <cstring>
#include "miniaudio_plugin_private.h"
#include "miniaudio.h"
#define MINIAUDIO_PLUGIN(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), miniaudio_plugin_get_type(), \
MiniaudioPlugin))
struct _MiniaudioPlugin {
GObject parent_instance;
};
G_DEFINE_TYPE(MiniaudioPlugin, miniaudio_plugin, g_object_get_type())
// Called when a method call is received from Flutter.
static void miniaudio_plugin_handle_method_call(
MiniaudioPlugin* self,
FlMethodCall* method_call) {
g_autoptr(FlMethodResponse) response = nullptr;
const gchar* method = fl_method_call_get_name(method_call);
//const FlValue* args = fl_method_call_get_args(method_call);
if (strcmp(method, "getPlatformVersion") == 0) {
response = get_platform_version();
}else if (strcmp(method, "playAudio") == 0) {
// ma_decoder decoder;
// ma_result result;
// result = ma_decoder_init_file(fl_value_get_string(0), NULL, &decoder);
// if (result != MA_SUCCESS) {
/// printf("Could not load file: %s\n", fl_value_get_string(0));
// }
//response = nullptr;
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
fl_method_call_respond(method_call, response, nullptr);
}
FlMethodResponse* get_platform_version() {
struct utsname uname_data = {};
uname(&uname_data);
g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version);
g_autoptr(FlValue) result = fl_value_new_string(version);
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
static void miniaudio_plugin_dispose(GObject* object) {
G_OBJECT_CLASS(miniaudio_plugin_parent_class)->dispose(object);
}
static void miniaudio_plugin_class_init(MiniaudioPluginClass* klass) {
G_OBJECT_CLASS(klass)->dispose = miniaudio_plugin_dispose;
}
static void miniaudio_plugin_init(MiniaudioPlugin* self) {}
static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,
gpointer user_data) {
MiniaudioPlugin* plugin = MINIAUDIO_PLUGIN(user_data);
miniaudio_plugin_handle_method_call(plugin, method_call);
}
void miniaudio_plugin_register_with_registrar(FlPluginRegistrar* registrar) {
MiniaudioPlugin* plugin = MINIAUDIO_PLUGIN(
g_object_new(miniaudio_plugin_get_type(), nullptr));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
g_autoptr(FlMethodChannel) channel =
fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
"miniaudio",
FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(channel, method_call_cb,
g_object_ref(plugin),
g_object_unref);
g_object_unref(plugin);
}

View File

@ -0,0 +1,10 @@
#include <flutter_linux/flutter_linux.h>
#include "include/miniaudio/miniaudio_plugin.h"
// This file exposes some plugin internals for unit testing. See
// https://github.com/flutter/flutter/issues/88724 for current limitations
// in the unit-testable API.
// Handles the getPlatformVersion method call.
FlMethodResponse *get_platform_version();

View File

@ -0,0 +1,31 @@
#include <flutter_linux/flutter_linux.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "include/miniaudio/miniaudio_plugin.h"
#include "miniaudio_plugin_private.h"
// This demonstrates a simple unit test of the C portion of this plugin's
// implementation.
//
// Once you have built the plugin's example app, you can run these tests
// from the command line. For instance, for a plugin called my_plugin
// built for x64 debug, run:
// $ build/linux/x64/debug/plugins/my_plugin/my_plugin_test
namespace miniaudio {
namespace test {
TEST(MiniaudioPlugin, GetPlatformVersion) {
g_autoptr(FlMethodResponse) response = get_platform_version();
ASSERT_NE(response, nullptr);
ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
FlValue* result = fl_method_success_response_get_result(
FL_METHOD_SUCCESS_RESPONSE(response));
ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING);
// The full string varies, so just validate that it has the right format.
EXPECT_THAT(fl_value_get_string(result), testing::StartsWith("Linux "));
}
} // namespace test
} // namespace miniaudio

View File

@ -0,0 +1,19 @@
import Cocoa
import FlutterMacOS
public class MiniaudioPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "miniaudio", binaryMessenger: registrar.messenger)
let instance = MiniaudioPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString)
default:
result(FlutterMethodNotImplemented)
}
}
}

View File

@ -0,0 +1,23 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint miniaudio.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'miniaudio'
s.version = '0.0.1'
s.summary = 'A new Flutter plugin project.'
s.description = <<-DESC
A new Flutter plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'FlutterMacOS'
s.platform = :osx, '10.11'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.swift_version = '5.0'
end

78
lib/third_party/miniaudio/pubspec.yaml vendored Normal file
View File

@ -0,0 +1,78 @@
name: miniaudio
description: "A new Flutter plugin project."
version: 0.0.1
homepage:
environment:
sdk: '>=3.2.6 <4.0.0'
flutter: '>=3.3.0'
dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
# which should be registered in the plugin registry. This is required for
# using method channels.
# The Android 'package' specifies package in which the registered class is.
# This is required for using method channels on Android.
# The 'ffiPlugin' specifies that native code should be built and bundled.
# This is required for using `dart:ffi`.
# All these are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
android:
package: com.example.miniaudio
pluginClass: MiniaudioPlugin
ios:
pluginClass: MiniaudioPlugin
linux:
pluginClass: MiniaudioPlugin
macos:
pluginClass: MiniaudioPlugin
windows:
pluginClass: MiniaudioPluginCApi
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages

View File

@ -0,0 +1,27 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:miniaudio/miniaudio_method_channel.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
MethodChannelMiniaudio platform = MethodChannelMiniaudio();
const MethodChannel channel = MethodChannel('miniaudio');
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');
});
}

View File

@ -0,0 +1,29 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:miniaudio/miniaudio.dart';
import 'package:miniaudio/miniaudio_platform_interface.dart';
import 'package:miniaudio/miniaudio_method_channel.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
class MockMiniaudioPlatform
with MockPlatformInterfaceMixin
implements MiniaudioPlatform {
@override
Future<String?> getPlatformVersion() => Future.value('42');
}
void main() {
final MiniaudioPlatform initialPlatform = MiniaudioPlatform.instance;
test('$MethodChannelMiniaudio is the default instance', () {
expect(initialPlatform, isInstanceOf<MethodChannelMiniaudio>());
});
test('getPlatformVersion', () async {
Miniaudio miniaudioPlugin = Miniaudio();
MockMiniaudioPlatform fakePlatform = MockMiniaudioPlatform();
MiniaudioPlatform.instance = fakePlatform;
expect(await miniaudioPlugin.getPlatformVersion(), '42');
});
}

View File

@ -0,0 +1,17 @@
flutter/
# Visual Studio user-specific files.
*.suo
*.user
*.userosscache
*.sln.docstates
# Visual Studio build-related files.
x64/
x86/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

View File

@ -0,0 +1,100 @@
# The Flutter tooling requires that developers have a version of Visual Studio
# installed that includes CMake 3.14 or later. You should not increase this
# version, as doing so will cause the plugin to fail to compile for some
# customers of the plugin.
cmake_minimum_required(VERSION 3.14)
# Project-level configuration.
set(PROJECT_NAME "miniaudio")
project(${PROJECT_NAME} LANGUAGES CXX)
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(VERSION 3.14...3.25)
# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "miniaudio_plugin")
# Any new source files that you add to the plugin should be added here.
list(APPEND PLUGIN_SOURCES
"miniaudio_plugin.cpp"
"miniaudio_plugin.h"
)
# Define the plugin library target. Its name must not be changed (see comment
# on PLUGIN_NAME above).
add_library(${PLUGIN_NAME} SHARED
"include/miniaudio/miniaudio_plugin_c_api.h"
"miniaudio_plugin_c_api.cpp"
${PLUGIN_SOURCES}
)
# Apply a standard set of build settings that are configured in the
# application-level CMakeLists.txt. This can be removed for plugins that want
# full control over build settings.
apply_standard_settings(${PLUGIN_NAME})
# Symbols are hidden by default to reduce the chance of accidental conflicts
# between plugins. This should not be removed; any symbols that should be
# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
# Source include directories and library dependencies. Add any plugin-specific
# dependencies here.
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(miniaudio_bundled_libraries
""
PARENT_SCOPE
)
# === Tests ===
# These unit tests can be run from a terminal after building the example, or
# from Visual Studio after opening the generated solution file.
# Only enable test builds when building the example (which sets this variable)
# so that plugin clients aren't building the tests.
if (${include_${PROJECT_NAME}_tests})
set(TEST_RUNNER "${PROJECT_NAME}_test")
enable_testing()
# Add the Google Test dependency.
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/release-1.11.0.zip
)
# Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Disable install commands for gtest so it doesn't end up in the bundle.
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
FetchContent_MakeAvailable(googletest)
# The plugin's C API is not very useful for unit testing, so build the sources
# directly into the test binary rather than using the DLL.
add_executable(${TEST_RUNNER}
test/miniaudio_plugin_test.cpp
${PLUGIN_SOURCES}
)
apply_standard_settings(${TEST_RUNNER})
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin)
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
# flutter_wrapper_plugin has link dependencies on the Flutter DLL.
add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${FLUTTER_LIBRARY}" $<TARGET_FILE_DIR:${TEST_RUNNER}>
)
# Enable automatic test discovery.
include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif()

View File

@ -0,0 +1,23 @@
#ifndef FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_C_API_H_
#define FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_C_API_H_
#include <flutter_plugin_registrar.h>
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
#endif
#if defined(__cplusplus)
extern "C" {
#endif
FLUTTER_PLUGIN_EXPORT void MiniaudioPluginCApiRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar);
#if defined(__cplusplus)
} // extern "C"
#endif
#endif // FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_C_API_H_

View File

@ -0,0 +1,59 @@
#include "miniaudio_plugin.h"
// This must be included before many other Windows headers.
#include <windows.h>
// For getPlatformVersion; remove unless needed for your plugin implementation.
#include <VersionHelpers.h>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <memory>
#include <sstream>
namespace miniaudio {
// static
void MiniaudioPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows *registrar) {
auto channel =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "miniaudio",
&flutter::StandardMethodCodec::GetInstance());
auto plugin = std::make_unique<MiniaudioPlugin>();
channel->SetMethodCallHandler(
[plugin_pointer = plugin.get()](const auto &call, auto result) {
plugin_pointer->HandleMethodCall(call, std::move(result));
});
registrar->AddPlugin(std::move(plugin));
}
MiniaudioPlugin::MiniaudioPlugin() {}
MiniaudioPlugin::~MiniaudioPlugin() {}
void MiniaudioPlugin::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (method_call.method_name().compare("getPlatformVersion") == 0) {
std::ostringstream version_stream;
version_stream << "Windows ";
if (IsWindows10OrGreater()) {
version_stream << "10+";
} else if (IsWindows8OrGreater()) {
version_stream << "8";
} else if (IsWindows7OrGreater()) {
version_stream << "7";
}
result->Success(flutter::EncodableValue(version_stream.str()));
} else {
result->NotImplemented();
}
}
} // namespace miniaudio

View File

@ -0,0 +1,31 @@
#ifndef FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_H_
#define FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_H_
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <memory>
namespace miniaudio {
class MiniaudioPlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
MiniaudioPlugin();
virtual ~MiniaudioPlugin();
// Disallow copy and assign.
MiniaudioPlugin(const MiniaudioPlugin&) = delete;
MiniaudioPlugin& operator=(const MiniaudioPlugin&) = delete;
// Called when a method is called on this plugin's channel from Dart.
void HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
};
} // namespace miniaudio
#endif // FLUTTER_PLUGIN_MINIAUDIO_PLUGIN_H_

View File

@ -0,0 +1,12 @@
#include "include/miniaudio/miniaudio_plugin_c_api.h"
#include <flutter/plugin_registrar_windows.h>
#include "miniaudio_plugin.h"
void MiniaudioPluginCApiRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
miniaudio::MiniaudioPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
}

View File

@ -0,0 +1,43 @@
#include <flutter/method_call.h>
#include <flutter/method_result_functions.h>
#include <flutter/standard_method_codec.h>
#include <gtest/gtest.h>
#include <windows.h>
#include <memory>
#include <string>
#include <variant>
#include "miniaudio_plugin.h"
namespace miniaudio {
namespace test {
namespace {
using flutter::EncodableMap;
using flutter::EncodableValue;
using flutter::MethodCall;
using flutter::MethodResultFunctions;
} // namespace
TEST(MiniaudioPlugin, GetPlatformVersion) {
MiniaudioPlugin plugin;
// Save the reply value from the success callback.
std::string result_string;
plugin.HandleMethodCall(
MethodCall("getPlatformVersion", std::make_unique<EncodableValue>()),
std::make_unique<MethodResultFunctions<>>(
[&result_string](const EncodableValue* result) {
result_string = std::get<std::string>(*result);
},
nullptr, nullptr));
// Since the exact string varies by host, just ensure that it's a string
// with the expected format.
EXPECT_TRUE(result_string.rfind("Windows ", 0) == 0);
}
} // namespace test
} // namespace miniaudio

View File

@ -15,6 +15,7 @@ import 'package:cwtch/models/messages/quotedmessage.dart';
import 'package:cwtch/models/profile.dart';
import 'package:cwtch/themes/opaque.dart';
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
import 'package:cwtch/views/vidchatview.dart';
import 'package:cwtch/widgets/malformedbubble.dart';
import 'package:cwtch/widgets/messageloadingbubble.dart';
import 'package:cwtch/widgets/profileimage.dart';
@ -102,6 +103,7 @@ class _MessageViewState extends State<MessageView> {
appBarButtons.add(IconButton(
splashRadius: Material.defaultSplashRadius / 2, icon: Icon(CwtchIcons.manage_files), tooltip: AppLocalizations.of(context)!.manageSharedFiles, onPressed: _pushFileSharingSettings));
}
appBarButtons.add(IconButton(splashRadius: Material.defaultSplashRadius / 2, icon: Icon(Icons.videocam), tooltip: AppLocalizations.of(context)!.manageSharedFiles, onPressed: _pushVidChat));
if (Provider.of<ContactInfoState>(context, listen: false).isOnline()) {
appBarButtons.add(IconButton(
@ -279,6 +281,23 @@ class _MessageViewState extends State<MessageView> {
return true;
}
void _pushVidChat() {
var profileInfoState = Provider.of<ProfileInfoState>(context, listen: false);
var contactInfoState = Provider.of<ContactInfoState>(context, listen: false);
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (builderContext, a1, a2) {
return MultiProvider(
providers: [ChangeNotifierProvider.value(value: profileInfoState), ChangeNotifierProvider.value(value: contactInfoState)],
child: VidChatView(),
);
},
transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child),
transitionDuration: Duration(milliseconds: 200),
),
);
}
void _pushFileSharingSettings() {
var profileInfoState = Provider.of<ProfileInfoState>(context, listen: false);
var contactInfoState = Provider.of<ContactInfoState>(context, listen: false);

View File

@ -0,0 +1,73 @@
import 'dart:convert';
import 'dart:ui';
import 'package:cwtch/config.dart';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/models/appstate.dart';
import 'package:cwtch/models/contact.dart';
import 'package:cwtch/widgets/camfeed.dart';
import 'package:cwtch/widgets/profileimage.dart';
import 'package:flutter/services.dart';
import 'package:cwtch/widgets/buttontextfield.dart';
import 'package:cwtch/widgets/cwtchlabel.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/settings.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:camera_linux/camera_linux.dart';
import '../main.dart';
import '../themes/opaque.dart';
class VidChatView extends StatefulWidget {
@override
_VidChatViewState createState() => _VidChatViewState();
}
class _VidChatViewState extends State<VidChatView> {
final _cameraLinuxPlugin = CameraLinux();
bool _isCameraOpen = false;
late String _base64Image;
@override
void dispose() {
_cameraLinuxPlugin.stopCamera();
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
var handle = Provider.of<ContactInfoState>(context).nickname;
if (handle.isEmpty) {
handle = Provider.of<ContactInfoState>(context).onion;
}
return Scaffold(
appBar: AppBar(
title: Container(
height: Provider.of<Settings>(context).fontScaling * 24.0,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: Text(handle + " " + AppLocalizations.of(context)!.conversationSettings)),
),
body: _buildVidChat(),
);
}
Widget _buildVidChat() {
return Column(children: [
CameraFeed(_cameraLinuxPlugin),
Image.memory(
Provider.of<ContactInfoState>(context).vid,
width: 320,
height: 240,
gaplessPlayback: true,
errorBuilder: (context, error, stackTrace) {
EnvironmentConfig.debugLog("error ${Provider.of<ContactInfoState>(context).vid.length}");
return Container();
},
),
]);
}
}

71
lib/widgets/camfeed.dart Normal file
View File

@ -0,0 +1,71 @@
import 'package:cwtch/config.dart';
import 'package:cwtch/models/message.dart';
import 'package:cwtch/third_party/base85/base_85_codec.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:camera_linux/camera_linux.dart';
import 'package:provider/provider.dart';
import '../main.dart';
import '../models/contact.dart';
import '../models/messages/VidMessage.dart';
class CameraFeed extends StatefulWidget {
final CameraLinux cameraLinuxPlugin;
CameraFeed(this.cameraLinuxPlugin, {super.key});
@override
State<CameraFeed> createState() => _CameraFeed();
}
class _CameraFeed extends State<CameraFeed> {
Uint8List _Image = Uint8List(0);
late Timer timer;
@override
void initState() {
super.initState();
_initializeCamera();
}
@override
void dispose() {
super.dispose();
timer.cancel();
}
// Open Default Camera
Future<void> _initializeCamera() async {
await widget.cameraLinuxPlugin.initializeCamera().then((value) {
timer = new Timer.periodic(
Duration(milliseconds: 150),
(Timer t) => widget.cameraLinuxPlugin.captureImage().then((value) {
if (mounted) {
setState(() {
_Image = value;
try {
Provider.of<FlwtchState>(context, listen: false).cwtch.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion,
Provider.of<ContactInfoState>(context, listen: false).identifier, jsonEncode(VidMessage(o: VideoOverlay, d: _Image)));
} catch (e) {
EnvironmentConfig.debugLog("could not send ${Base85Codec.new().encode(_Image)}: $e");
}
});
}
}));
});
}
@override
Widget build(BuildContext context) {
return Image.memory(
_Image,
width: 320,
height: 240,
gaplessPlayback: true,
errorBuilder: (context, error, stackTrace) {
return Container();
},
);
}
}

View File

@ -6,11 +6,15 @@
#include "generated_plugin_registrant.h"
#include <miniaudio/miniaudio_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) miniaudio_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MiniaudioPlugin");
miniaudio_plugin_register_with_registrar(miniaudio_registrar);
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);

View File

@ -3,12 +3,14 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
miniaudio
screen_retriever
url_launcher_linux
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
camera_linux
)
set(PLUGIN_BUNDLED_LIBRARIES)

View File

@ -7,6 +7,7 @@ import Foundation
import connectivity_plus
import flutter_local_notifications
import miniaudio
import package_info_plus
import path_provider_foundation
import screen_retriever
@ -16,6 +17,7 @@ import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
MiniaudioPlugin.register(with: registry.registrar(forPlugin: "MiniaudioPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))

View File

@ -113,6 +113,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.6.1"
camera_linux:
dependency: "direct main"
description:
path: "lib/third_party/camera_linux"
relative: true
source: path
version: "0.0.8"
characters:
dependency: transitive
description:
@ -157,10 +164,10 @@ packages:
dependency: transitive
description:
name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.17.2"
version: "1.18.0"
connectivity_plus:
dependency: "direct main"
description:
@ -471,10 +478,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.10.0"
mime:
dependency: transitive
description:
@ -483,6 +490,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
miniaudio:
dependency: "direct main"
description:
path: "lib/third_party/miniaudio"
relative: true
source: path
version: "0.0.1"
msix:
dependency: "direct dev"
description:
@ -591,10 +605,10 @@ packages:
dependency: transitive
description:
name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.2"
plugin_platform_interface:
dependency: transitive
description:
@ -724,18 +738,18 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
stream_transform:
dependency: transitive
description:
@ -772,10 +786,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
version: "0.6.1"
timezone:
dependency: transitive
description:
@ -884,10 +898,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f
sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
url: "https://pub.dev"
source: hosted
version: "11.7.1"
version: "11.10.0"
watcher:
dependency: transitive
description:
@ -900,10 +914,10 @@ packages:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
version: "0.3.0"
web_socket_channel:
dependency: transitive
description:
@ -961,7 +975,7 @@ packages:
source: hosted
version: "6.3.0"
yaml:
dependency: transitive
dependency: "direct main"
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
@ -969,5 +983,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.1.0-185.0.dev <4.0.0"
dart: ">=3.2.6 <4.0.0"
flutter: ">=3.7.0"

View File

@ -54,6 +54,10 @@ dependencies:
dbus: ^0.7.8
connectivity_plus:
path: lib/third_party/connectivity_plus/connectivity_plus
camera_linux:
path: lib/third_party/camera_linux
miniaudio:
path: lib/third_party/miniaudio
# misc plugins
qr_flutter: ^4.0.0

View File

@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <miniaudio/miniaudio_plugin_c_api.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <win_toast/win_toast_plugin.h>
@ -15,6 +16,8 @@
void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
MiniaudioPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MiniaudioPluginCApi"));
ScreenRetrieverPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
miniaudio
screen_retriever
url_launcher_windows
win_toast