diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1962bbcb..5b2ed891 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,6 +28,10 @@ jobs:
- run: flutter config --enable-windows-desktop
- run: flutter pub get
- run: flutter build windows --verbose
+ - uses: actions/upload-artifact@v1
+ with:
+ name: dart_vlc_example
+ path: example/build/windows/runner/Release
build_linux:
name: Build Linux
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 98e54bcb..92320040 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.1.1
+
+- Fixed setState being called after dispose (#75) (Finally)
+- Improved memory management.
+- Fixed ton of memory leaks.
+- Fixed `Devices::all` & `Media::parse` causing crash on Windows.
+
## 0.1.0
- Fixed build on Linux.
diff --git a/README.md b/README.md
index 64d137f7..2ebcaad7 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
Flutter 🎞 media playback, broadcast, recording & chromecast library for Windows & Linux.
Written in C++ using libVLC & libVLC++.
-![](https://github.com/alexmercerind/dart_vlc/blob/assets/dart_vlc_6.png?raw=true)
+![](https://github.com/alexmercerind/dart_vlc/blob/assets/dart_vlc_windows_11_1.PNG?raw=true)
-![](https://github.com/alexmercerind/dart_vlc/blob/assets/dart_vlc_7.png?raw=true)
+![](https://github.com/alexmercerind/dart_vlc/blob/assets/dart_vlc_windows_11_2.PNG?raw=true)
## Installation
@@ -13,7 +13,7 @@
```yaml
dependencies:
...
- dart_vlc: ^0.0.9
+ dart_vlc: ^0.1.1
```
**Dart CLI**
@@ -21,11 +21,12 @@ dependencies:
```yaml
dependencies:
...
- dart_vlc_ffi: ^0.0.1
+ dart_vlc_ffi: ^0.1.0
```
More on Dart CLI implementation [here](./ffi/README.md).
+Feel free to open issue, incase you find something to be not working.
## Support
@@ -33,13 +34,6 @@ Consider supporting the project by starring the repository or buying me a coffee
-**Donate in Crypto**
-
-- ETH (Ethereum)
- - `0x92f92BC204cDFDAB655e6A69e5Aa4bA5476D7661`
-- BTC (Bitcoin)
- - `1M3DNwnX1GDauPoPr7VywojMRWygje8ctq`
-
Thanks a lot for your support.
Looking for contributors for macOS port.
@@ -328,7 +322,7 @@ You can see an example project [here](https://github.com/alexmercerind/dart_vlc/
Windows
-![](https://github.com/alexmercerind/dart_vlc/blob/assets/dart_vlc_0.PNG?raw=true)
+![](https://github.com/alexmercerind/dart_vlc/blob/assets/dart_vlc_6.png?raw=true)
## Workings
diff --git a/dartvlc/broadcast.hpp b/dartvlc/broadcast.hpp
index 556b2f61..5e5ccec8 100644
--- a/dartvlc/broadcast.hpp
+++ b/dartvlc/broadcast.hpp
@@ -71,8 +71,9 @@ class Broadcast {
);
}
- void dispose() {
+ ~Broadcast() {
libvlc_vlm_release(this->instance.get());
+ delete this->media;
}
private:
@@ -89,6 +90,11 @@ class Broadcasts {
return this->broadcasts[id];
}
+ void dispose(int id, std::function callback = []() -> void {}) {
+ delete this->broadcasts[id];
+ callback();
+ }
+
private:
std::map broadcasts;
};
diff --git a/dartvlc/chromecast.hpp b/dartvlc/chromecast.hpp
index 87cc7ddd..3ad30998 100644
--- a/dartvlc/chromecast.hpp
+++ b/dartvlc/chromecast.hpp
@@ -48,8 +48,9 @@ class Chromecast {
);
}
- void dispose() {
+ ~Chromecast() {
libvlc_vlm_release(this->instance.get());
+ delete this->media;
}
private:
@@ -66,6 +67,11 @@ class Chromecasts {
return this->chromecasts[id];
}
+ void dispose(int id, std::function callback = []() -> void {}) {
+ delete this->chromecasts[id];
+ callback();
+ }
+
private:
std::map chromecasts;
};
diff --git a/dartvlc/device.hpp b/dartvlc/device.hpp
index 05fd5258..35c2f29c 100644
--- a/dartvlc/device.hpp
+++ b/dartvlc/device.hpp
@@ -41,6 +41,9 @@ class Devices {
std::vector all;
void refresh() {
+ for (Device* device: this->all) {
+ delete device;
+ }
this->all.clear();
VLC::Instance _ = VLC::Instance(0, nullptr);
VLC::MediaPlayer __ = VLC::MediaPlayer(_);
diff --git a/dartvlc/equalizer.hpp b/dartvlc/equalizer.hpp
index 1ef31f6e..12bbd8af 100644
--- a/dartvlc/equalizer.hpp
+++ b/dartvlc/equalizer.hpp
@@ -102,6 +102,11 @@ class Equalizers {
return id;
}
+ void dispose(int id, std::function callback = []() -> void {}) {
+ delete this->equalizers[id];
+ callback();
+ }
+
private:
std::map equalizers;
};
diff --git a/dartvlc/internal/events.hpp b/dartvlc/internal/events.hpp
index e0229ccc..efb2bc31 100644
--- a/dartvlc/internal/events.hpp
+++ b/dartvlc/internal/events.hpp
@@ -106,7 +106,7 @@ class PlayerEvents : public PlayerGetters {
if (this->isPlaylistModified) {
this->mediaListPlayer.setMediaList(this->mediaList);
if (!this->mediaList.count()) {
- this->state = new PlayerState();
+ this->state->reset();
this->mediaListPlayer.stop();
return;
}
diff --git a/dartvlc/internal/setters.hpp b/dartvlc/internal/setters.hpp
index 2827083f..996743b2 100644
--- a/dartvlc/internal/setters.hpp
+++ b/dartvlc/internal/setters.hpp
@@ -18,33 +18,34 @@
class PlayerSetters: public PlayerEvents {
public:
void open(MediaSource* mediaSource, bool autoStart = true) {
- if (this->state->device == nullptr)
- this->stop();
- this->state->medias = new Playlist({}, PlaylistMode::single);
+ /* Freed the previous `Media` objects when a new `Playlist` or `Media` is opened. */
+ for (Media* media: this->state->medias->medias) {
+ delete media;
+ }
+ this->stop();
+ this->state->medias->medias = {};
this->mediaList = VLC::MediaList(this->instance);
if (mediaSource->mediaSourceType() == "MediaSourceType.media") {
Media* media = dynamic_cast(mediaSource);
VLC::Media _ = VLC::Media(this->instance, media->location, VLC::Media::FromLocation);
this->mediaList.addMedia(_);
this->mediaListPlayer.setMediaList(this->mediaList);
- this->state->medias = new Playlist({ media }, PlaylistMode::single);
+ this->state->medias->medias = { media };
this->state->isPlaylist = false;
-
}
else if (mediaSource->mediaSourceType() == "MediaSourceType.playlist") {
Playlist* playlist = dynamic_cast(mediaSource);
if (playlist->medias.empty())
return;
- for (Media* _ : playlist->medias) {
- VLC::Media media = VLC::Media(this->instance, _->location, VLC::Media::FromLocation);
- this->mediaList.addMedia(media);
+ for (Media* media : playlist->medias) {
+ VLC::Media _ = VLC::Media(this->instance, media->location, VLC::Media::FromLocation);
+ this->mediaList.addMedia(_);
}
this->mediaListPlayer.setMediaList(this->mediaList);
- this->state->medias = playlist;
+ this->state->medias->medias = playlist->medias;
this->state->isPlaylist = true;
}
this->_onOpenCallback(this->mediaList.itemAtIndex(0));
-
this->mediaListPlayer.playItemAtIndex(0);
this->state->index = 0;
this->state->isPlaying = this->mediaListPlayer.isPlaying();
@@ -123,7 +124,6 @@ class PlayerSetters: public PlayerEvents {
void setEqualizer(Equalizer* equalizer) {
this->mediaPlayer.setEqualizer(equalizer->equalizer);
- this->state->equalizer = equalizer;
}
void setUserAgent(std::string userAgent) {
@@ -173,7 +173,12 @@ class PlayerSetters: public PlayerEvents {
}
void move(int initial, int final) {
- if (initial < 0 || initial >= this->state->medias->medias.size() || final < 0 || final >= this->state->medias->medias.size()) return;
+ if (
+ initial < 0 ||
+ initial >= this->state->medias->medias.size() ||
+ final < 0 ||
+ final >= this->state->medias->medias.size()
+ ) return;
if (initial == final) return;
this->isPlaylistModified = true;
Media* _ = this->state->medias->medias[initial];
diff --git a/dartvlc/internal/state.hpp b/dartvlc/internal/state.hpp
index 2bb490ac..4a993330 100644
--- a/dartvlc/internal/state.hpp
+++ b/dartvlc/internal/state.hpp
@@ -28,5 +28,29 @@ class PlayerState {
float rate = 1.0;
bool isPlaylist = false;
Device* device = nullptr;
+ /* TODO: Not used yet.
Equalizer* equalizer = nullptr;
+ */
+
+ void reset() {
+ this->index = 0;
+ this->medias->medias = {};
+ this->isPlaying = false;
+ this->isValid = true;
+ this->isSeekable = true;
+ this->isCompleted = false;
+ this->position = 0;
+ this->duration = 0;
+ this->volume = 1.0;
+ this->rate = 1.0;
+ this->isPlaylist = false;
+ this->device = nullptr;
+ /*
+ this->equalizer = nullptr;
+ */
+ }
+
+ ~PlayerState() {
+ delete this->medias;
+ }
};
diff --git a/dartvlc/mediasource/media.hpp b/dartvlc/mediasource/media.hpp
index 455ac035..a1c95d60 100644
--- a/dartvlc/mediasource/media.hpp
+++ b/dartvlc/mediasource/media.hpp
@@ -23,6 +23,7 @@
VLC::Instance instance = VLC::Instance(0, nullptr);
+/* Media object is cleared inside PlayerSetters::open. */
class Media: public MediaSource {
public:
int id;
@@ -51,24 +52,26 @@ class Media: public MediaSource {
return media;
}
- static Media* asset(int id, std::string path, bool parse = false, int timeout = 10000) {
+ static Media* directShow(int id, std::string resource) {
Media* media = new Media();
media->id = id;
- media->resource = path;
- media->location = "file:///" + std::filesystem::temp_directory_path().u8string() + "/" + path;
- media->mediaType = "MediaType.asset";
- if (parse) media->parse(timeout);
+ media->resource = resource;
+ media->location = resource;
+ media->mediaType = "MediaType.directShow";
return media;
}
- static Media* directShow(int id, std::string resource) {
+ /* Now done directly from dart_vlc.
+ static Media* asset(int id, std::string path, bool parse = false, int timeout = 10000) {
Media* media = new Media();
media->id = id;
- media->resource = resource;
- media->location = resource;
- media->mediaType = "MediaType.directShow";
+ media->resource = path;
+ media->location = "file:///" + std::filesystem::temp_directory_path().u8string() + "/" + path;
+ media->mediaType = "MediaType.asset";
+ if (parse) media->parse(timeout);
return media;
}
+ */
void parse(int timeout) {
VLC::Media media = VLC::Media(instance, this->location, VLC::Media::FromLocation);
@@ -113,14 +116,6 @@ class Media: public MediaSource {
std::string mediaSourceType() {
return "MediaSourceType.media";
}
-
- std::map get() {
- std::map media;
- media["id"] = this->id;
- media["mediaType"] = this->mediaType;
- media["resource"] = this->resource;
- return media;
- }
};
diff --git a/dartvlc/mediasource/playlist.hpp b/dartvlc/mediasource/playlist.hpp
index fa9d0f98..0748c0d2 100644
--- a/dartvlc/mediasource/playlist.hpp
+++ b/dartvlc/mediasource/playlist.hpp
@@ -35,15 +35,7 @@ class Playlist : public MediaSource {
this->medias = medias;
this->playlistMode = playlistMode;
};
-
- std::vector> get() {
- std::vector> _medias;
- for (Media* audio : this->medias) {
- _medias.emplace_back(audio->get());
- }
- return _medias;
- }
-
+
std::string mediaSourceType() {
return "MediaSourceType.playlist";
}
diff --git a/dartvlc/player.hpp b/dartvlc/player.hpp
index 68c2617c..af972991 100644
--- a/dartvlc/player.hpp
+++ b/dartvlc/player.hpp
@@ -21,9 +21,10 @@ class Player: public PlayerSetters {
this->instance = VLC::Instance(0, nullptr);
}
else {
- char** args = new char*[commandlineArguments.size()];
- for (int index = 0; index < commandlineArguments.size(); index++) args[index] = commandlineArguments[index].data();
- this->instance = VLC::Instance(static_cast(commandlineArguments.size()), args);
+ this->argsSize = commandlineArguments.size();
+ this->args = new char*[commandlineArguments.size()];
+ for (int index = 0; index < commandlineArguments.size(); index++) this->args[index] = commandlineArguments[index].data();
+ this->instance = VLC::Instance(static_cast(commandlineArguments.size()), this->args);
}
this->mediaPlayer = VLC::MediaPlayer(this->instance);
this->mediaListPlayer = VLC::MediaListPlayer(this->instance);
@@ -54,6 +55,18 @@ class Player: public PlayerSetters {
void onException(std::function callback) {
this->mediaPlayer.eventManager().onEncounteredError(callback);
}
+
+ ~Player() {
+ this->mediaPlayer.stop();
+ delete this->state;
+ delete this->_videoFrameBuffer;
+ for (size_t i = 0; i < argsSize; i++) delete this->args[i];
+ delete[] this->args;
+ }
+
+private:
+ char** args;
+ size_t argsSize;
};
@@ -66,6 +79,11 @@ class Players {
return this->players[id];
}
+ void dispose(int id, std::function callback = []() -> void {}) {
+ delete this->players[id];
+ callback();
+ }
+
private:
std::map players;
};
diff --git a/dartvlc/record.hpp b/dartvlc/record.hpp
index e476a819..662e70b2 100644
--- a/dartvlc/record.hpp
+++ b/dartvlc/record.hpp
@@ -48,8 +48,9 @@ class Record {
);
}
- void dispose() {
+ ~Record() {
libvlc_vlm_release(this->instance.get());
+ delete this->media;
}
private:
@@ -66,6 +67,11 @@ class Records {
return this->records[id];
}
+ void dispose(int id, std::function callback = []() -> void {}) {
+ delete this->records[id];
+ callback();
+ }
+
private:
std::map records;
};
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 54f7484f..29226e26 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -63,6 +63,8 @@ class DartVLCExampleState extends State {
@override
Widget build(BuildContext context) {
return MaterialApp(
+ themeMode: ThemeMode.dark,
+ darkTheme: ThemeData.dark(),
home: Scaffold(
appBar: AppBar(
title: const Text('dart_vlc'),
@@ -179,7 +181,7 @@ class DartVLCExampleState extends State {
MediaType.file) {
this.medias.add(
Media.file(new File(
- controller.text)),
+ controller.text.replaceAll('"', ''))),
);
} else if (this.mediaType ==
MediaType.network) {
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 028ec22b..8e3959d7 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -35,14 +35,14 @@ packages:
path: ".."
relative: true
source: path
- version: "0.0.9"
+ version: "0.1.1"
dart_vlc_ffi:
dependency: transitive
description:
name: dart_vlc_ffi
url: "https://pub.dartlang.org"
source: hosted
- version: "0.0.7"
+ version: "0.0.9"
ffi:
dependency: transitive
description:
diff --git a/example/windows/CMakeLists.txt b/example/windows/CMakeLists.txt
index 795aee5b..405e2b82 100644
--- a/example/windows/CMakeLists.txt
+++ b/example/windows/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.15)
-project(flutter_vlc_example LANGUAGES CXX)
+project(dart_vlc_example LANGUAGES CXX)
-set(BINARY_NAME "flutter_vlc_example")
+set(BINARY_NAME "dart_vlc_example")
cmake_policy(SET CMP0063 NEW)
diff --git a/example/windows/runner/CMakeLists.txt b/example/windows/runner/CMakeLists.txt
index 977e38b5..0b899a0b 100644
--- a/example/windows/runner/CMakeLists.txt
+++ b/example/windows/runner/CMakeLists.txt
@@ -4,7 +4,6 @@ project(runner LANGUAGES CXX)
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
- "run_loop.cpp"
"utils.cpp"
"win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp
index c4227230..27bef02c 100644
--- a/example/windows/runner/flutter_window.cpp
+++ b/example/windows/runner/flutter_window.cpp
@@ -4,9 +4,8 @@
#include "flutter/generated_plugin_registrant.h"
-FlutterWindow::FlutterWindow(RunLoop* run_loop,
- const flutter::DartProject& project)
- : run_loop_(run_loop), project_(project) {}
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
FlutterWindow::~FlutterWindow() {}
@@ -26,14 +25,12 @@ bool FlutterWindow::OnCreate() {
return false;
}
RegisterPlugins(flutter_controller_->engine());
- run_loop_->RegisterFlutterInstance(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
- run_loop_->UnregisterFlutterInstance(flutter_controller_->engine());
flutter_controller_ = nullptr;
}
diff --git a/example/windows/runner/flutter_window.h b/example/windows/runner/flutter_window.h
index b663ddd5..4d184577 100644
--- a/example/windows/runner/flutter_window.h
+++ b/example/windows/runner/flutter_window.h
@@ -6,7 +6,6 @@
#include
-#include "run_loop.h"
#include "win32_window.h"
// A window that does nothing but host a Flutter view.
@@ -14,8 +13,7 @@ class FlutterWindow : public Win32Window {
public:
// Creates a new FlutterWindow driven by the |run_loop|, hosting a
// Flutter view running |project|.
- explicit FlutterWindow(RunLoop* run_loop,
- const flutter::DartProject& project);
+ explicit FlutterWindow(const flutter::DartProject& project);
virtual ~FlutterWindow();
protected:
@@ -26,9 +24,6 @@ class FlutterWindow : public Win32Window {
LPARAM const lparam) noexcept override;
private:
- // The run loop driving events for this window.
- RunLoop* run_loop_;
-
// The project to run.
flutter::DartProject project_;
diff --git a/example/windows/runner/main.cpp b/example/windows/runner/main.cpp
index 1d9da865..e89d654c 100644
--- a/example/windows/runner/main.cpp
+++ b/example/windows/runner/main.cpp
@@ -3,40 +3,29 @@
#include
#include "flutter_window.h"
-#include "run_loop.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t *command_line, _In_ int show_command) {
- // Attach to console when present (e.g., 'flutter run') or create a
- // new console when running with a debugger.
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}
-
- // Initialize COM, so that it is available for use in the library and/or
- // plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
-
- RunLoop run_loop;
-
flutter::DartProject project(L"data");
-
- std::vector command_line_arguments =
- GetCommandLineArguments();
-
+ std::vector command_line_arguments = GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
-
- FlutterWindow window(&run_loop, project);
+ FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"dart_vlc_example", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
-
- run_loop.Run();
-
+ MSG msg;
+ while (GetMessage(&msg, nullptr, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
::CoUninitialize();
return EXIT_SUCCESS;
}
diff --git a/example/windows/runner/run_loop.cpp b/example/windows/runner/run_loop.cpp
deleted file mode 100644
index f23c5f21..00000000
--- a/example/windows/runner/run_loop.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "run_loop.h"
-
-#include
-
-#include
-
-RunLoop::RunLoop() {}
-
-RunLoop::~RunLoop() {}
-
-void RunLoop::Run() {
- MSG msg;
- while (GetMessage(&msg, nullptr, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-}
-
-void RunLoop::RegisterFlutterInstance(
- flutter::FlutterEngine* flutter_instance) {
- flutter_instances_.insert(flutter_instance);
-}
-
-void RunLoop::UnregisterFlutterInstance(
- flutter::FlutterEngine* flutter_instance) {
- flutter_instances_.erase(flutter_instance);
-}
-
-RunLoop::TimePoint RunLoop::ProcessFlutterMessages() {
- TimePoint next_event_time = TimePoint::max();
- for (auto instance : flutter_instances_) {
- std::chrono::nanoseconds wait_duration = instance->ProcessMessages();
- if (wait_duration != std::chrono::nanoseconds::max()) {
- next_event_time =
- std::min(next_event_time, TimePoint::clock::now() + wait_duration);
- }
- }
- return next_event_time;
-}
diff --git a/example/windows/runner/run_loop.h b/example/windows/runner/run_loop.h
deleted file mode 100644
index 000d3624..00000000
--- a/example/windows/runner/run_loop.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef RUNNER_RUN_LOOP_H_
-#define RUNNER_RUN_LOOP_H_
-
-#include
-
-#include
-#include
-
-// A runloop that will service events for Flutter instances as well
-// as native messages.
-class RunLoop {
- public:
- RunLoop();
- ~RunLoop();
-
- // Prevent copying
- RunLoop(RunLoop const&) = delete;
- RunLoop& operator=(RunLoop const&) = delete;
-
- // Runs the run loop until the application quits.
- void Run();
-
- // Registers the given Flutter instance for event servicing.
- void RegisterFlutterInstance(
- flutter::FlutterEngine* flutter_instance);
-
- // Unregisters the given Flutter instance from event servicing.
- void UnregisterFlutterInstance(
- flutter::FlutterEngine* flutter_instance);
-
- private:
- using TimePoint = std::chrono::steady_clock::time_point;
-
- // Processes all currently pending messages for registered Flutter instances.
- TimePoint ProcessFlutterMessages();
-
- std::set flutter_instances_;
-};
-
-#endif // RUNNER_RUN_LOOP_H_
diff --git a/ffi/CHANGELOG.md b/ffi/CHANGELOG.md
index d3a30f0f..f3676583 100644
--- a/ffi/CHANGELOG.md
+++ b/ffi/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.1.0
+
+- Added new native methods for handling with memory leaks.
+
+## 0.0.9
+
+- Fixed `Devices.all` & `Media.parse` causing crash on Windows.
+
## 0.0.8
- Fixed build warnings on Linux.
diff --git a/ffi/lib/src/device.dart b/ffi/lib/src/device.dart
index 5c781f55..d7ad34a9 100644
--- a/ffi/lib/src/device.dart
+++ b/ffi/lib/src/device.dart
@@ -24,7 +24,7 @@ class Devices {
devices.add(Device(devicesPtr.elementAt(i).value.toDartString(),
devicesPtr.elementAt(i + 1).value.toDartString()));
}
- calloc.free(devicesPtr);
+ CleanupFFI.devices();
return devices;
}
}
diff --git a/ffi/lib/src/equalizer.dart b/ffi/lib/src/equalizer.dart
index 583e8d39..ab3d3a7f 100644
--- a/ffi/lib/src/equalizer.dart
+++ b/ffi/lib/src/equalizer.dart
@@ -51,6 +51,7 @@ class Equalizer {
double.parse(_equalizer.elementAt(10).value.toDartString()):
double.parse(_equalizer.elementAt(11).value.toDartString()),
};
+ CleanupFFI.equalizer();
return equalizer;
}
@@ -74,6 +75,7 @@ class Equalizer {
double.parse(_equalizer.elementAt(10).value.toDartString()):
double.parse(_equalizer.elementAt(11).value.toDartString()),
};
+ CleanupFFI.equalizer();
return equalizer;
}
diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart
index 251eba9a..2ff7b8dc 100644
--- a/ffi/lib/src/internal/ffi.dart
+++ b/ffi/lib/src/internal/ffi.dart
@@ -22,6 +22,10 @@ abstract class PlayerFFI {
.lookup>('Player_create')
.asFunction();
+ static final PlayerDisposeDart dispose = dynamicLibrary
+ .lookup>('Player_dispose')
+ .asFunction();
+
static final PlayerOpenDart open = dynamicLibrary
.lookup>('Player_open')
.asFunction();
@@ -172,6 +176,23 @@ abstract class EqualizerFFI {
.asFunction();
}
+
+abstract class CleanupFFI {
+
+ static final MediaClearDart media = dynamicLibrary
+ .lookup>('Media_clear')
+ .asFunction();
+
+ static final DevicesClearDart devices = dynamicLibrary
+ .lookup>('Devices_clear')
+ .asFunction();
+
+ static final EqualizerClearDart equalizer = dynamicLibrary
+ .lookup>('Equalizer_clear')
+ .asFunction();
+}
+
+
bool isInitialized = false;
void Function(int playerId, Uint8List frame) videoFrameCallback = (_, __) {};
final ReceivePort receiver = new ReceivePort()
diff --git a/ffi/lib/src/internal/typedefs/devices.dart b/ffi/lib/src/internal/typedefs/devices.dart
index 6029c1b3..81b89a84 100644
--- a/ffi/lib/src/internal/typedefs/devices.dart
+++ b/ffi/lib/src/internal/typedefs/devices.dart
@@ -5,3 +5,7 @@ import 'package:ffi/ffi.dart';
/// Devices::all
typedef DevicesAllCXX = Pointer> Function();
typedef DevicesAllDart = Pointer> Function();
+
+/// Following typedef is used for cleaning memory used by char**.
+typedef DevicesClearCXX = Void Function();
+typedef DevicesClearDart = void Function();
diff --git a/ffi/lib/src/internal/typedefs/equalizer.dart b/ffi/lib/src/internal/typedefs/equalizer.dart
index e71d45e8..24c7c15f 100644
--- a/ffi/lib/src/internal/typedefs/equalizer.dart
+++ b/ffi/lib/src/internal/typedefs/equalizer.dart
@@ -21,3 +21,7 @@ typedef EqualizerSetBandAmpDart = void Function(
/// Equalizer::setPreAmp
typedef EqualizerSetPreAmpCXX = Void Function(Int32 id, Float amp);
typedef EqualizerSetPreAmpDart = void Function(int id, double amp);
+
+/// Following typedef is used for cleaning memory used by char**.
+typedef EqualizerClearCXX = Void Function();
+typedef EqualizerClearDart = void Function();
diff --git a/ffi/lib/src/internal/typedefs/media.dart b/ffi/lib/src/internal/typedefs/media.dart
index caedc8ff..5c9f5ad7 100644
--- a/ffi/lib/src/internal/typedefs/media.dart
+++ b/ffi/lib/src/internal/typedefs/media.dart
@@ -7,3 +7,7 @@ typedef MediaParseCXX = Pointer> Function(
Pointer type, Pointer resource, Int32 timeout);
typedef MediaParseDart = Pointer> Function(
Pointer type, Pointer resource, int timeout);
+
+/// Following typedef is used for cleaning memory used by char**.
+typedef MediaClearCXX = Void Function();
+typedef MediaClearDart = void Function();
diff --git a/ffi/lib/src/internal/typedefs/player.dart b/ffi/lib/src/internal/typedefs/player.dart
index ca71bb94..94922e89 100644
--- a/ffi/lib/src/internal/typedefs/player.dart
+++ b/ffi/lib/src/internal/typedefs/player.dart
@@ -16,6 +16,12 @@ typedef PlayerCreateDart = void Function(
int commandlineArgumentsCount,
Pointer> commandlineArguments);
+/// Following typedef is used for:
+/// Player::~Player
+typedef PlayerDisposeCXX = Void Function(Int32 id);
+typedef PlayerDisposeDart = void Function(int id);
+
+
/// Following typedef is used for:
/// Player::open
typedef PlayerOpenCXX = Void Function(
diff --git a/ffi/lib/src/mediaSource/media.dart b/ffi/lib/src/mediaSource/media.dart
index 6491706c..13248ae4 100644
--- a/ffi/lib/src/mediaSource/media.dart
+++ b/ffi/lib/src/mediaSource/media.dart
@@ -137,6 +137,6 @@ class Media extends MediaSource {
this.metas['trackNumber'] = metas.elementAt(21).value.toDartString();
this.metas['trackTotal'] = metas.elementAt(22).value.toDartString();
this.metas['url'] = metas.elementAt(23).value.toDartString();
- calloc.free(metas);
+ CleanupFFI.media();
}
}
diff --git a/ffi/lib/src/player.dart b/ffi/lib/src/player.dart
index 6ae9f6c2..7cb12330 100644
--- a/ffi/lib/src/player.dart
+++ b/ffi/lib/src/player.dart
@@ -272,6 +272,7 @@ class Player {
this.positionController.close();
this.playbackController.close();
this.generalController.close();
+ PlayerFFI.dispose(this.id);
}
/// Internally used [StreamController]s,
diff --git a/ffi/native/dart_vlc.cpp b/ffi/native/dart_vlc.cpp
index c53bdad0..0ea447ef 100644
--- a/ffi/native/dart_vlc.cpp
+++ b/ffi/native/dart_vlc.cpp
@@ -67,11 +67,16 @@ EXPORT void Player_create(int id, int videoWidth, int videoHeight, int commandLi
}
}
+EXPORT void Player_dispose(int id) {
+ players->dispose(id);
+}
+
EXPORT void Player_open(int id, bool autoStart, const char** source, int sourceSize) {
std::vector medias;
Player* player = players->get(id);
for (int index = 0; index < 2 * sourceSize; index += 2) {
Media* media;
+ /* Freed inside PlayerSetters::open */
const char* type = source[index];
const char* resource = source[index + 1];
if (strcmp(type, "MediaType.file") == 0)
@@ -171,6 +176,7 @@ EXPORT void Player_setPlaylistMode(int id, const char* mode) {
EXPORT void Player_add(int id, const char* type, const char* resource) {
Player* player = players->get(id);
Media* media;
+ /* Freed inside PlayerSetters::open */
if (strcmp(type, "MediaType.file") == 0)
media = Media::file(0, resource, false);
else if (strcmp(type, "MediaType.network") == 0)
@@ -202,6 +208,9 @@ EXPORT void Player_move(int id, int initialIndex, int finalIndex) {
player->move(initialIndex, finalIndex);
}
+char** _metasPointer = nullptr;
+size_t _metasSize = 0;
+
EXPORT char** Media_parse(const char* type, const char* resource, int timeout) {
Media* media;
if (strcmp(type, "MediaType.file") == 0)
@@ -210,17 +219,32 @@ EXPORT char** Media_parse(const char* type, const char* resource, int timeout) {
media = Media::network(0, resource, true);
else
media = Media::directShow(0, resource);
- char** metas = new char*[media->metas.size()];
+ _metasPointer = new char*[media->metas.size()];
+ _metasSize = media->metas.size();
int index = 0;
for (auto &meta: media->metas) {
- metas[index] = meta.second.data();
+ _metasPointer[index] = new char[200];
+ strncpy(_metasPointer[index], meta.second.data(), 200);
index++;
}
- return metas;
+ delete media;
+ return _metasPointer;
+}
+
+EXPORT void Media_clear() {
+ if (_metasPointer != nullptr) {
+ for (size_t i = 0; i < _metasSize; i++) {
+ delete _metasPointer[i];
+ }
+ delete[] _metasPointer;
+ _metasPointer = nullptr;
+ _metasSize = 0;
+ }
}
EXPORT void Broadcast_create(int id, const char* type, const char* resource, const char* access, const char* mux, const char* dst, const char* vcodec, int vb, const char* acodec, int ab) {
Media* media;
+ /* Freed inside ~Broadcast (Broadcasts::dispose) */
if (strcmp(type, "MediaType.file") == 0)
media = Media::file(0, resource, false);
else if (strcmp(type, "MediaType.network") == 0)
@@ -245,12 +269,12 @@ EXPORT void Broadcast_start(int id) {
}
EXPORT void Broadcast_dispose(int id) {
- Broadcast* broadcast = broadcasts->get(id, nullptr, nullptr);
- broadcast->dispose();
+ broadcasts->dispose(id);
}
EXPORT void Chromecast_create(int id, const char* type, const char* resource, const char* ipAddress) {
Media* media;
+ /* Freed inside ~Chromecast (Chromecasts::dispose) */
if (strcmp(type, "MediaType.file") == 0)
media = Media::file(0, resource, false);
else if (strcmp(type, "MediaType.network") == 0)
@@ -266,12 +290,12 @@ EXPORT void Chromecast_start(int id) {
}
EXPORT void Chromecast_dispose(int id) {
- Chromecast* chromecast = chromecasts->get(id, nullptr, nullptr);
- chromecast->dispose();
+ chromecasts->dispose(id);
}
EXPORT void Record_create(int id, const char* savingFile, const char* type, const char* resource) {
Media* media;
+ /* Freed inside ~Record (Records::dispose) */
if (strcmp(type, "MediaType.file") == 0)
media = Media::file(0, resource, false);
else if (strcmp(type, "MediaType.network") == 0)
@@ -286,42 +310,61 @@ EXPORT void Record_start(int id) {
}
EXPORT void Record_dispose(int id) {
- records->get(id, nullptr, "")->dispose();
+ records->dispose(id);
}
+char** _devicesPointer = nullptr;
+size_t _devicesSize = 0;
+
EXPORT char** Devices_all() {
devices->refresh();
- char** _devices = new char*[(devices->all.size() * 2) + 1];
- _devices[0] = new char[200];
- strncpy(_devices[0], std::to_string(devices->all.size()).data(), 200);
+ _devicesPointer = new char*[(devices->all.size() * 2) + 1];
+ _devicesSize = (devices->all.size() * 2) + 1;
+ _devicesPointer[0] = new char[200];
+ strncpy(_devicesPointer[0], std::to_string(devices->all.size()).data(), 200);
int index = 0;
for (Device* device: devices->all) {
- _devices[index + 1] = new char[200];
- strncpy(_devices[index + 1], device->id.data(), 200);
- _devices[index + 2] = new char[200];
- strncpy(_devices[index + 2], device->name.data(), 200);
+ _devicesPointer[index + 1] = new char[200];
+ strncpy(_devicesPointer[index + 1], device->id.data(), 200);
+ _devicesPointer[index + 2] = new char[200];
+ strncpy(_devicesPointer[index + 2], device->name.data(), 200);
index += 2;
}
- return _devices;
+ return _devicesPointer;
}
+EXPORT void Devices_clear() {
+ if (_devicesPointer != nullptr) {
+ for (size_t i = 0; i < _devicesSize; i++) {
+ delete _devicesPointer[i];
+ }
+ delete[] _devicesPointer;
+ _devicesPointer = nullptr;
+ _devicesSize = 0;
+ }
+}
+
+char** _equalizerPointer = nullptr;
+size_t _equalizerSize = 0;
+
EXPORT char** Equalizer_createEmpty() {
int id = equalizers->createEmpty();
Equalizer* equalizer = equalizers->get(id);
- char** _equalizer = new char*[2 * equalizer->bandAmps.size() + 2];
- _equalizer[0] = new char[200];
- strncpy(_equalizer[0], std::to_string(id).data(), 200);
- _equalizer[1] = new char[200];
- strncpy(_equalizer[1], std::to_string(equalizer->preAmp).data(), 200);
+ _equalizerPointer = new char*[2 * equalizer->bandAmps.size() + 2];
+ _equalizerSize = 2 * equalizer->bandAmps.size() + 2;
+ _equalizerPointer[0] = new char[200];
+ strncpy(_equalizerPointer[0], std::to_string(id).data(), 200);
+ _equalizerPointer[1] = new char[200];
+ strncpy(_equalizerPointer[1], std::to_string(equalizer->preAmp).data(), 200);
int index = 0;
for (const auto&[band, amp]: equalizer->bandAmps) {
- _equalizer[index + 2] = new char[200];
- strncpy(_equalizer[index + 2], std::to_string(band).data(), 200);
- _equalizer[index + 3] = new char[200];
- strncpy(_equalizer[index + 3], std::to_string(amp).data(), 200);
+ _equalizerPointer[index + 2] = new char[200];
+ strncpy(_equalizerPointer[index + 2], std::to_string(band).data(), 200);
+ _equalizerPointer[index + 3] = new char[200];
+ strncpy(_equalizerPointer[index + 3], std::to_string(amp).data(), 200);
index += 2;
}
- return _equalizer;
+ return _equalizerPointer;
}
EXPORT char** Equalizer_createMode(int mode) {
@@ -329,20 +372,32 @@ EXPORT char** Equalizer_createMode(int mode) {
static_cast(mode)
);
Equalizer* equalizer = equalizers->get(id);
- char** _equalizer = new char*[2 * equalizer->bandAmps.size() + 2];
- _equalizer[0] = new char[200];
- strncpy(_equalizer[0], std::to_string(id).data(), 200);
- _equalizer[1] = new char[200];
- strncpy(_equalizer[1], std::to_string(equalizer->preAmp).data(), 200);
+ _equalizerPointer = new char*[2 * equalizer->bandAmps.size() + 2];
+ _equalizerSize = 2 * equalizer->bandAmps.size() + 2;
+ _equalizerPointer[0] = new char[200];
+ strncpy(_equalizerPointer[0], std::to_string(id).data(), 200);
+ _equalizerPointer[1] = new char[200];
+ strncpy(_equalizerPointer[1], std::to_string(equalizer->preAmp).data(), 200);
int index = 0;
for (const auto&[band, amp]: equalizer->bandAmps) {
- _equalizer[index + 2] = new char[200];
- strncpy(_equalizer[index + 2], std::to_string(band).data(), 200);
- _equalizer[index + 3] = new char[200];
- strncpy(_equalizer[index + 3], std::to_string(amp).data(), 200);
+ _equalizerPointer[index + 2] = new char[200];
+ strncpy(_equalizerPointer[index + 2], std::to_string(band).data(), 200);
+ _equalizerPointer[index + 3] = new char[200];
+ strncpy(_equalizerPointer[index + 3], std::to_string(amp).data(), 200);
index += 2;
}
- return _equalizer;
+ return _equalizerPointer;
+}
+
+EXPORT void Equalizer_clear() {
+ if (_equalizerPointer != nullptr) {
+ for (size_t i = 0; i < _equalizerSize; i++) {
+ delete _equalizerPointer[i];
+ }
+ delete[] _equalizerPointer;
+ _equalizerPointer = nullptr;
+ _equalizerSize = 0;
+ }
}
EXPORT void Equalizer_setBandAmp(int id, float band, float amp) {
diff --git a/ffi/pubspec.yaml b/ffi/pubspec.yaml
index 7fdf28db..65252101 100644
--- a/ffi/pubspec.yaml
+++ b/ffi/pubspec.yaml
@@ -1,6 +1,6 @@
name: dart_vlc_ffi
description: FFI based binding to dart_vlc wrapper (Based on libVLC & libVLC++).
-version: 0.0.8
+version: 0.1.0
homepage: https://github.com/alexmercerind/dart_vlc
repository: https://github.com/alexmercerind/dart_vlc
documentation: https://github.com/alexmercerind/dart_vlc/blob/master/README.md
diff --git a/lib/src/widgets/video.dart b/lib/src/widgets/video.dart
index f453d7af..276b97ee 100644
--- a/lib/src/widgets/video.dart
+++ b/lib/src/widgets/video.dart
@@ -172,9 +172,9 @@ class VideoState extends State