From 20aba2c48bf8ecbb441cb20620f74095304533da Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Sat, 26 Jun 2021 18:35:11 +0530 Subject: [PATCH 01/27] Changed license header --- README.md | 12 ++++++++++-- dartvlc/broadcast.hpp | 4 ++-- dartvlc/chromecast.hpp | 4 ++-- dartvlc/device.hpp | 4 ++-- dartvlc/equalizer.hpp | 4 ++-- dartvlc/internal/events.hpp | 4 ++-- dartvlc/internal/getters.hpp | 4 ++-- dartvlc/internal/internal.hpp | 4 ++-- dartvlc/internal/setters.hpp | 4 ++-- dartvlc/internal/state.hpp | 4 ++-- dartvlc/main.cpp | 4 ++-- dartvlc/mediasource/media.hpp | 4 ++-- dartvlc/mediasource/mediasource.hpp | 4 ++-- dartvlc/mediasource/playlist.hpp | 4 ++-- dartvlc/player.hpp | 4 ++-- dartvlc/record.hpp | 4 ++-- 16 files changed, 40 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 47b52e9c..f23f1e92 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,14 @@ Playlist playlist = new Playlist( ``` #### Open a media or playlist into a player. + +```dart +player.open( + await Media.file(new File('C:/music0.mp3')), + autoStart: true, // default +); +``` + ```dart player.open( new Playlist( @@ -66,7 +74,7 @@ player.open( await Media.file(new File('C:/music2.mp3')), ], ), - autoStart: true, // default + autoStart: false, ); ``` @@ -382,7 +390,7 @@ Contributions to the project are open, it will be appreciated if you discuss the ## License -Copyright (C) 2021, Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. +Copyright (C) 2021, Hitesh Kumar Saini. This library & work under this repository is licensed under GNU Lesser General Public License v2.1. diff --git a/dartvlc/broadcast.hpp b/dartvlc/broadcast.hpp index f6d0d33a..556b2f61 100644 --- a/dartvlc/broadcast.hpp +++ b/dartvlc/broadcast.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/chromecast.hpp b/dartvlc/chromecast.hpp index 2107a1ba..1fbe1089 100644 --- a/dartvlc/chromecast.hpp +++ b/dartvlc/chromecast.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/device.hpp b/dartvlc/device.hpp index 9f72548b..05fd5258 100644 --- a/dartvlc/device.hpp +++ b/dartvlc/device.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/equalizer.hpp b/dartvlc/equalizer.hpp index 43cd5633..1ef31f6e 100644 --- a/dartvlc/equalizer.hpp +++ b/dartvlc/equalizer.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/internal/events.hpp b/dartvlc/internal/events.hpp index b2fe23b4..e0229ccc 100644 --- a/dartvlc/internal/events.hpp +++ b/dartvlc/internal/events.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/internal/getters.hpp b/dartvlc/internal/getters.hpp index a2a1ce57..27cd1d4d 100644 --- a/dartvlc/internal/getters.hpp +++ b/dartvlc/internal/getters.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/internal/internal.hpp b/dartvlc/internal/internal.hpp index 917d8410..ca1794bd 100644 --- a/dartvlc/internal/internal.hpp +++ b/dartvlc/internal/internal.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/internal/setters.hpp b/dartvlc/internal/setters.hpp index 9a3a1b0a..2827083f 100644 --- a/dartvlc/internal/setters.hpp +++ b/dartvlc/internal/setters.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/internal/state.hpp b/dartvlc/internal/state.hpp index 03c95e43..2bb490ac 100644 --- a/dartvlc/internal/state.hpp +++ b/dartvlc/internal/state.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/main.cpp b/dartvlc/main.cpp index 7e6f2271..b04de321 100644 --- a/dartvlc/main.cpp +++ b/dartvlc/main.cpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/mediasource/media.hpp b/dartvlc/mediasource/media.hpp index 3dccf6c3..455ac035 100644 --- a/dartvlc/mediasource/media.hpp +++ b/dartvlc/mediasource/media.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/mediasource/mediasource.hpp b/dartvlc/mediasource/mediasource.hpp index 493d7797..4224896e 100644 --- a/dartvlc/mediasource/mediasource.hpp +++ b/dartvlc/mediasource/mediasource.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/mediasource/playlist.hpp b/dartvlc/mediasource/playlist.hpp index 1694d601..fa9d0f98 100644 --- a/dartvlc/mediasource/playlist.hpp +++ b/dartvlc/mediasource/playlist.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/player.hpp b/dartvlc/player.hpp index 1fbf53c7..68c2617c 100644 --- a/dartvlc/player.hpp +++ b/dartvlc/player.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/dartvlc/record.hpp b/dartvlc/record.hpp index 5f5f9ed5..e476a819 100644 --- a/dartvlc/record.hpp +++ b/dartvlc/record.hpp @@ -1,9 +1,9 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ From 4ac9165c6a87d1736d9726f28d7b983575fa83e6 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Sat, 26 Jun 2021 18:36:38 +0530 Subject: [PATCH 02/27] Updated C binding --- ffi/CreateSharedLibrary.sh | 4 +- ffi/native/callbackmanager.hpp | 161 +++++++++++++++++++++++++ ffi/native/eventmanager.hpp | 138 ++++++++++++++++++++++ ffi/native/main.cpp | 207 +++++++++++++++++++++++++++++++++ ffi/pubspec.lock | 2 +- 5 files changed, 509 insertions(+), 3 deletions(-) create mode 100644 ffi/native/callbackmanager.hpp create mode 100644 ffi/native/eventmanager.hpp create mode 100644 ffi/native/main.cpp diff --git a/ffi/CreateSharedLibrary.sh b/ffi/CreateSharedLibrary.sh index fe4683c1..5d1ae3d5 100755 --- a/ffi/CreateSharedLibrary.sh +++ b/ffi/CreateSharedLibrary.sh @@ -1,6 +1,6 @@ # dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. # -# Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. +# Hitesh Kumar Saini & contributors. # https://github.com/alexmercerind # alexmercerind@gmail.com # GNU Lesser General Public License v2.1 @@ -13,7 +13,7 @@ # Assuming include directory contains libVLC++ headers. # Setting C++ standard to 17 for std::filesystem. # -g++ -c source/main.cpp -o main.o -fPIC -I./ -I./../linux/include -std=c++17 +g++ -c native/main.cpp -o main.o -fPIC -I./ -I./../linux/include -std=c++17 # Create shared library from the generated object code. # Linking libVLC shared library. diff --git a/ffi/native/callbackmanager.hpp b/ffi/native/callbackmanager.hpp new file mode 100644 index 00000000..793b85bc --- /dev/null +++ b/ffi/native/callbackmanager.hpp @@ -0,0 +1,161 @@ +#ifndef EXPORT +#ifdef __WIN32 +#define EXPORT declspec(__dllexport) +#else +#define EXPORT +#endif +#endif + +#include + +#ifndef CallbackManager_h +#define CallbackManager_h + + +typedef int64_t Dart_Port; + + +typedef enum { + Dart_TypedData_kByteData = 0, + Dart_TypedData_kInt8, + Dart_TypedData_kUint8, + Dart_TypedData_kUint8Clamped, + Dart_TypedData_kInt16, + Dart_TypedData_kUint16, + Dart_TypedData_kInt32, + Dart_TypedData_kUint32, + Dart_TypedData_kInt64, + Dart_TypedData_kUint64, + Dart_TypedData_kFloat32, + Dart_TypedData_kFloat64, + Dart_TypedData_kFloat32x4, + Dart_TypedData_kInvalid +} Dart_TypedData_Type; + + +typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle; + + +typedef void (*Dart_WeakPersistentHandleFinalizer) (void* isolate_callback_data, Dart_WeakPersistentHandle handle, void* peer); + + +typedef enum { + Dart_CObject_kNull = 0, + Dart_CObject_kBool, + Dart_CObject_kInt32, + Dart_CObject_kInt64, + Dart_CObject_kDouble, + Dart_CObject_kString, + Dart_CObject_kArray, + Dart_CObject_kTypedData, + Dart_CObject_kExternalTypedData, + Dart_CObject_kSendPort, + Dart_CObject_kCapability, + Dart_CObject_kUnsupported, + Dart_CObject_kNumberOfTypes +} Dart_CObject_Type; + + +typedef struct _Dart_CObject { + Dart_CObject_Type type; + union { + bool as_bool; + int32_t as_int32; + int64_t as_int64; + double as_double; + char* as_string; + struct { + Dart_Port id; + Dart_Port origin_id; + } as_send_port; + struct { + int64_t id; + } as_capability; + struct { + intptr_t length; + struct _Dart_CObject** values; + } as_array; + struct { + Dart_TypedData_Type type; + intptr_t length; + uint8_t* values; + } as_typed_data; + struct { + Dart_TypedData_Type type; + intptr_t length; + uint8_t* data; + void* peer; + Dart_WeakPersistentHandleFinalizer callback; + } as_external_typed_data; + } value; +} Dart_CObject; + + +typedef bool (*Dart_PostCObjectType)(Dart_Port port_id, Dart_CObject* message); + + +#ifdef __cplusplus +extern "C" { +#endif + +Dart_PostCObjectType dartPostCObject = NULL; +Dart_Port callbackPort; + +EXPORT void RegisterDart_PostCObject(Dart_PostCObjectType _dartPostCObject) { + dartPostCObject = _dartPostCObject; +} + +EXPORT void RegisterDart_CallbackPort(Dart_Port _callbackPort) { + callbackPort = _callbackPort; +} + +EXPORT void callbackInt32(int32_t value) { + Dart_CObject dart_object; + dart_object.type = Dart_CObject_kInt32; + dart_object.value.as_int32 = value; + dartPostCObject(callbackPort, &dart_object); +} + +EXPORT void callbackStringArray(int length, char** values) { + Dart_CObject** valueObjects = new Dart_CObject*[length]; + for (int i = 0; i < length; i++) { + Dart_CObject* valueObject = new Dart_CObject; + valueObject->type = Dart_CObject_kString; + valueObject->value.as_string = values[i]; + valueObjects[i] = valueObject; + } + Dart_CObject dart_object; + dart_object.type = Dart_CObject_kArray; + dart_object.value.as_array.length = length; + dart_object.value.as_array.values = valueObjects; + dartPostCObject(callbackPort, &dart_object); + for (int i = 0; i < length; i++) { + delete valueObjects[i]; + } + delete[] valueObjects; +} + +EXPORT void callbackByteArray(int length, uint8_t* values) { + Dart_CObject** valueObjects = new Dart_CObject*[length]; + for (int i = 0; i < length; i++) { + Dart_CObject* valueObject = new Dart_CObject; + valueObject->type = Dart_CObject_kInt32; + valueObject->value.as_int32 = static_cast(values[i]); + valueObjects[i] = valueObject; + } + Dart_CObject dart_object; + dart_object.type = Dart_CObject_kArray; + dart_object.value.as_array.length = length; + dart_object.value.as_array.values = valueObjects; + dartPostCObject(callbackPort, &dart_object); + for (int i = 0; i < length; i++) { + delete valueObjects[i]; + } + delete[] valueObjects; +} + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/ffi/native/eventmanager.hpp b/ffi/native/eventmanager.hpp new file mode 100644 index 00000000..2206907a --- /dev/null +++ b/ffi/native/eventmanager.hpp @@ -0,0 +1,138 @@ +/* + * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. + * + * Hitesh Kumar Saini & contributors. + * https://github.com/alexmercerind + * alexmercerind@gmail.com + * + * GNU Lesser General Public License v2.1 + */ + +#include "../dartvlc/main.cpp" +#include "callbackmanager.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +void Player_onPlayPauseStop(PlayerState* state) { + std::vector event; + event.emplace_back(std::to_string(state->id)); + event.emplace_back("playbackEvent"); + event.emplace_back(std::to_string(state->isPlaying)); + event.emplace_back(std::to_string(state->isSeekable)); + int _size = event.size(); + char** _event = new char*[_size]; + for (int index = 0; index < _size; index++) + _event[index] = const_cast(event[index].c_str()); + callbackStringArray( + _size, + _event + ); + + delete[] _event; +} + +void Player_onPosition(PlayerState* state) { + std::vector event; + event.emplace_back(std::to_string(state->id)); + event.emplace_back("positionEvent"); + event.emplace_back(std::to_string(state->index)); + event.emplace_back(std::to_string(state->position)); + event.emplace_back(std::to_string(state->duration)); + int _size = event.size(); + char** _event = new char*[_size]; + for (int index = 0; index < _size; index++) + _event[index] = const_cast(event[index].c_str()); + callbackStringArray( + _size, + _event + ); + + delete[] _event; +} + +void Player_onComplete(PlayerState* state) { + std::vector event; + event.emplace_back(std::to_string(state->id)); + event.emplace_back("completeEvent"); + event.emplace_back(std::to_string(state->isCompleted)); + int _size = event.size(); + char** _event = new char*[_size]; + for (int index = 0; index < _size; index++) + _event[index] = const_cast(event[index].c_str()); + callbackStringArray( + _size, + _event + ); + + delete[] _event; +} + +void Player_onVolume(PlayerState* state) { + std::vector event; + event.emplace_back(std::to_string(state->id)); + event.emplace_back("volumeEvent"); + event.emplace_back(std::to_string(state->volume)); + int _size = event.size(); + char** _event = new char*[_size]; + for (int index = 0; index < _size; index++) + _event[index] = const_cast(event[index].c_str()); + callbackStringArray( + _size, + _event + ); + + delete[] _event; +} + +void Player_onRate(PlayerState* state) { + std::vector event; + event.emplace_back(std::to_string(state->id)); + event.emplace_back("rateEvent"); + event.emplace_back(std::to_string(state->rate)); + int _size = event.size(); + char** _event = new char*[_size]; + for (int index = 0; index < _size; index++) + _event[index] = const_cast(event[index].c_str()); + callbackStringArray( + _size, + _event + ); + + delete[] _event; +} + +void Player_onOpen(PlayerState* state) { + std::vector event; + event.emplace_back(std::to_string(state->id)); + event.emplace_back("openEvent"); + event.emplace_back(std::to_string(state->index)); + event.emplace_back(std::to_string(state->isPlaylist)); + for (Media* media: state->medias->medias) { + event.emplace_back(std::to_string(media->id)); + event.emplace_back(media->mediaType); + event.emplace_back(media->resource); + } + int _size = event.size(); + char** _event = new char*[_size]; + for (int index = 0; index < _size; index++) + _event[index] = const_cast(event[index].c_str()); + callbackStringArray( + _size, + _event + ); + + delete[] _event; +} + +void Player_onVideo(int size, uint8_t* frame) { + callbackByteArray( + size, + frame + ); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/ffi/native/main.cpp b/ffi/native/main.cpp new file mode 100644 index 00000000..b7d90f18 --- /dev/null +++ b/ffi/native/main.cpp @@ -0,0 +1,207 @@ +/* + * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. + * + * Hitesh Kumar Saini & contributors. + * https://github.com/alexmercerind + * alexmercerind@gmail.com + * + * GNU Lesser General Public License v2.1 + */ + +#ifndef EXPORT +#ifdef _WIN32 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif +#endif + +#include "eventmanager.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DART_VLC_FFI +#define DART_VLC_FFI + + +EXPORT void Player_create(int id, int videoWidth, int videoHeight, int commandLineArgumentsCount, const char** commandLineArguments) { + std::vector args; + for (int index = 0; index < commandLineArgumentsCount; index++) args.emplace_back(commandLineArguments[index]); + Player* player = players->get(id, args); + player->videoWidth = videoWidth; + player->videoHeight = videoHeight; + player->onPlay([=]() -> void { + Player_onPlayPauseStop(player->state); + }); + player->onPause([=]() -> void { + Player_onPlayPauseStop(player->state); + }); + player->onStop([=]() -> void { + Player_onPlayPauseStop(player->state); + Player_onPosition(player->state); + }); + player->onComplete([=]() -> void { + Player_onComplete(player->state); + }); + player->onVolume([=](float _) -> void { + Player_onVolume(player->state); + }); + player->onRate([=](float _) -> void { + Player_onRate(player->state); + }); + player->onPosition([=](int _) -> void { + Player_onPosition(player->state); + }); + player->onOpen([=](VLC::Media _) -> void { + Player_onOpen(player->state); + }); + player->onPlaylist([=]() -> void { + Player_onOpen(player->state); + }); + player->onVideo([=](uint8_t* frame) -> void { + Player_onVideo(player->videoHeight * player->videoWidth * 4, frame); + }); +} + +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 < 3 * sourceSize; index += 3) { + Media* media; + int id = atoi(source[index]); + const char* type = source[index + 1]; + const char* resource = source[index + 2]; + if (strcmp(type, "MediaType.file") == 0) + media = Media::file(id, resource, false); + else if (strcmp(type, "MediaType.network") == 0) + media = Media::network(id, resource, false); + else + media = Media::asset(id, resource, false); + medias.emplace_back(media); + } + player->open( + new Playlist(medias), + autoStart + ); +} + +EXPORT void Player_play(int id) { + Player* player = players->get(id); + player->play(); +} + +EXPORT void Player_pause(int id) { + Player* player = players->get(id); + player->pause(); +} + +EXPORT void Player_playOrPause(int id) { + Player* player = players->get(id); + player->playOrPause(); +} + +EXPORT void Player_stop(int id) { + Player* player = players->get(id); + player->stop(); +} + +EXPORT void Player_next(int id) { + Player* player = players->get(id); + player->next(); +} + +EXPORT void Player_back(int id) { + Player* player = players->get(id); + player->back(); +} + +EXPORT void Player_jump(int id, int index) { + Player* player = players->get(id); + player->jump(index); +} + +EXPORT void Player_seek(int id, int position) { + Player* player = players->get(id); + player->seek(position); +} + +EXPORT void Player_setVolume(int id, float volume) { + Player* player = players->get(id); + player->setVolume(volume); +} + +EXPORT void Player_setRate(int id, float rate) { + Player* player = players->get(id); + player->setRate(rate); +} + +EXPORT void Player_setPlaylistMode(int id, const char* mode) { + Player* player = players->get(id); + PlaylistMode playlistMode; + if (strcmp(mode, "playlistMode.repeat") == 0) + playlistMode = PlaylistMode::repeat; + else if (strcmp(mode, "playlistMode.loop") == 0) + playlistMode = PlaylistMode::loop; + else + playlistMode = PlaylistMode::single; + player->setPlaylistMode(playlistMode); +} + +EXPORT void Player_add(int id, int mediaId, const char* type, const char* resource) { + Player* player = players->get(id); + Media* media; + if (strcmp(type, "MediaType.file") == 0) + media = Media::file(mediaId, resource, false); + else if (strcmp(type, "MediaType.network") == 0) + media = Media::network(mediaId, resource, false); + else + media = Media::asset(mediaId, resource, false); + player->add(media); +} + +EXPORT void Player_remove(int id, int index) { + Player* player = players->get(id); + player->remove(index); +} + +EXPORT void Player_insert(int id, int index, int mediaId, const char* type, const char* resource) { + Player* player = players->get(id); + Media* media; + if (strcmp(type, "MediaType.file") == 0) + media = Media::file(mediaId, resource, false); + else if (strcmp(type, "MediaType.network") == 0) + media = Media::network(mediaId, resource, false); + else + media = Media::asset(mediaId, resource, false); + player->insert(index, media); +} + +EXPORT void Player_move(int id, int initialIndex, int finalIndex) { + Player* player = players->get(id); + player->move(initialIndex, finalIndex); +} + +EXPORT char** Media_parse(const char* type, const char* resource, int timeout) { + Media* media; + if (strcmp(type, "MediaType.file") == 0) + media = Media::file(0, resource, false, timeout); + else if (strcmp(type, "MediaType.network") == 0) + media = Media::network(0, resource, false, timeout); + else + media = Media::asset(0, resource, false, timeout); + char** metas = new char*[media->metas.size()]; + int index = 0; + for (auto &meta: media->metas) { + metas[index] = meta.second.data(); + index++; + } + return metas; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/ffi/pubspec.lock b/ffi/pubspec.lock index 34d558dc..52f4f2f1 100644 --- a/ffi/pubspec.lock +++ b/ffi/pubspec.lock @@ -16,4 +16,4 @@ packages: source: hosted version: "1.8.0" sdks: - dart: ">=2.12.0-259.9.beta <3.0.0" + dart: ">=2.12.0 <3.0.0" From 85383c8d5db429a4de5698ae5522753d28588ea9 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Sat, 3 Jul 2021 16:07:15 +0530 Subject: [PATCH 03/27] More FFI bindings --- ffi/lib/dart_vlc.dart | 25 ++ ffi/lib/source/internal/dynamiclibrary.dart | 18 -- ffi/lib/src/broadcast.dart | 127 ++++++++ ffi/lib/src/chromecast.dart | 58 ++++ ffi/lib/src/device.dart | 42 +++ ffi/lib/src/enums/equalizerMode.dart | 23 ++ ffi/lib/src/enums/mediaSourceType.dart | 8 + ffi/lib/src/enums/mediaType.dart | 14 + ffi/lib/src/enums/playlistMode.dart | 11 + ffi/lib/src/equalizer.dart | 92 ++++++ ffi/lib/src/internal/dynamiclibrary.dart | 3 + .../internal/ffi.dart} | 53 +-- ffi/lib/src/internal/typedefs/broadcast.dart | 0 .../internal/typedefs/callback.dart | 10 - ffi/lib/src/internal/typedefs/media.dart | 7 + .../internal/typedefs/player.dart | 27 +- ffi/lib/src/mediaSource/media.dart | 116 +++++++ ffi/lib/src/mediaSource/mediaSource.dart | 6 + ffi/lib/src/mediaSource/playlist.dart | 33 ++ ffi/lib/src/player.dart | 302 ++++++++++++++++++ ffi/lib/src/playerState/playerState.dart | 46 +++ ffi/lib/src/record.dart | 58 ++++ ffi/main.dart | 27 +- ffi/native/main.cpp | 69 +++- ffi/pubspec.yaml | 2 +- ffi/source/callbackmanager.hpp | 142 -------- ffi/source/eventmanager.hpp | 125 -------- ffi/source/main.cpp | 187 ----------- windows/dart_vlc_plugin.cpp | 2 +- windows/include/flutter_types.hpp | 2 +- 30 files changed, 1076 insertions(+), 559 deletions(-) create mode 100644 ffi/lib/dart_vlc.dart delete mode 100644 ffi/lib/source/internal/dynamiclibrary.dart create mode 100644 ffi/lib/src/broadcast.dart create mode 100644 ffi/lib/src/chromecast.dart create mode 100644 ffi/lib/src/device.dart create mode 100644 ffi/lib/src/enums/equalizerMode.dart create mode 100644 ffi/lib/src/enums/mediaSourceType.dart create mode 100644 ffi/lib/src/enums/mediaType.dart create mode 100644 ffi/lib/src/enums/playlistMode.dart create mode 100644 ffi/lib/src/equalizer.dart create mode 100644 ffi/lib/src/internal/dynamiclibrary.dart rename ffi/lib/{dart_vlc_ffi.dart => src/internal/ffi.dart} (53%) create mode 100644 ffi/lib/src/internal/typedefs/broadcast.dart rename ffi/lib/{source => src}/internal/typedefs/callback.dart (60%) create mode 100644 ffi/lib/src/internal/typedefs/media.dart rename ffi/lib/{source => src}/internal/typedefs/player.dart (73%) create mode 100644 ffi/lib/src/mediaSource/media.dart create mode 100644 ffi/lib/src/mediaSource/mediaSource.dart create mode 100644 ffi/lib/src/mediaSource/playlist.dart create mode 100644 ffi/lib/src/player.dart create mode 100644 ffi/lib/src/playerState/playerState.dart create mode 100644 ffi/lib/src/record.dart delete mode 100644 ffi/source/callbackmanager.hpp delete mode 100644 ffi/source/eventmanager.hpp delete mode 100644 ffi/source/main.cpp diff --git a/ffi/lib/dart_vlc.dart b/ffi/lib/dart_vlc.dart new file mode 100644 index 00000000..89f8d580 --- /dev/null +++ b/ffi/lib/dart_vlc.dart @@ -0,0 +1,25 @@ +/* + * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. + * + * Hitesh Kumar Saini + * https://github.com/alexmercerind + * alexmercerind@gmail.com + * + * GNU Lesser General Public License v2.1 + */ + +export 'package:dart_vlc_ffi/src/player.dart'; +export 'package:dart_vlc_ffi/src/equalizer.dart'; +export 'package:dart_vlc_ffi/src/broadcast.dart'; +export 'package:dart_vlc_ffi/src/record.dart'; +export 'package:dart_vlc_ffi/src/chromecast.dart'; +export 'package:dart_vlc_ffi/src/device.dart'; +export 'package:dart_vlc_ffi/src/playerState/playerState.dart'; +export 'package:dart_vlc_ffi/src/mediaSource/mediaSource.dart'; +export 'package:dart_vlc_ffi/src/mediaSource/media.dart'; +export 'package:dart_vlc_ffi/src/mediaSource/playlist.dart'; +export 'package:dart_vlc_ffi/src/enums/equalizerMode.dart'; +export 'package:dart_vlc_ffi/src/enums/mediaSourceType.dart'; +export 'package:dart_vlc_ffi/src/enums/mediaType.dart'; +export 'package:dart_vlc_ffi/src/enums/playlistMode.dart'; +export 'package:dart_vlc_ffi/src/internal/ffi.dart'; \ No newline at end of file diff --git a/ffi/lib/source/internal/dynamiclibrary.dart b/ffi/lib/source/internal/dynamiclibrary.dart deleted file mode 100644 index 1cf9c684..00000000 --- a/ffi/lib/source/internal/dynamiclibrary.dart +++ /dev/null @@ -1,18 +0,0 @@ -/* - * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. - * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. - * https://github.com/alexmercerind - * alexmercerind@gmail.com - * - * GNU Lesser General Public License v2.1 - */ - -import 'dart:ffi'; -import 'dart:io'; -import 'package:path/path.dart' as path; - - -final DynamicLibrary dynamicLibrary = DynamicLibrary.open( - path.join(Directory.current.path, 'dart_vlc.so'), -); diff --git a/ffi/lib/src/broadcast.dart b/ffi/lib/src/broadcast.dart new file mode 100644 index 00000000..cfe7408c --- /dev/null +++ b/ffi/lib/src/broadcast.dart @@ -0,0 +1,127 @@ +import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; + +/// Internally used class to avoid direct creation of the object of a [Broadcast] class. +class _Broadcast extends Broadcast {} + +/// Used to declare a [Broadcast] configuration. +/// +/// An example configuration for a [Broadcast] can be as follows. +/// +/// ```dart +/// new BroadcastConfiguration( +/// access: 'http', +/// mux: 'mpeg1', +/// dst: '127.0.0.1:8080', +/// vcodec: 'mp1v', +/// vb: 1024, +/// acodec: 'mpga', +/// ab: 128, +/// ); +/// ``` +class BroadcastConfiguration { + /// Type of access for [Broadcast] e.g. `http`. + final String access; + + /// MUX e.g. `mpeg1`. + final String mux; + + /// Destination e.g. `127.0.0.1:8080`. + final String dst; + + /// Video codec for transcoding the broadcast e.g. `mp1v`. + final String vcodec; + + /// Video bitrate for transcoding the broadcast. + final int vb; + + /// Audio codec for transcoding the broadcast e.g. `mpga`. + final String acodec; + + /// Audio bitrate for transcoding the broadcast. + final int ab; + + const BroadcastConfiguration({ + required this.access, + required this.mux, + required this.dst, + required this.vcodec, + required this.vb, + required this.acodec, + required this.ab, + }); +} + +/// Creates new [Broadcast] for a [Media]. +/// +/// Example creation can be as follows. +/// +/// ```dart +/// Broadcast broadcast = Broadcast.create( +/// id: 0, +/// media: await Media.file(new File('C:/music.ogg')), +/// configuration: new BroadcastConfiguration( +/// access: 'http', +/// mux: 'mpeg1', +/// dst: '127.0.0.1:8080', +/// vcodec: 'mp1v', +/// vb: 1024, +/// acodec: 'mpga', +/// ab: 128, +/// ), +/// ); +/// +/// broadcast.start(); +/// ``` +/// +/// Call [Broadcast.dispose] for releasing the resources. +/// +abstract class Broadcast { + /// ID for this broadcast. + late int id; + + /// Broadcasting [Media]. + late Media media; + + /// Configuration of this broadcast. + late BroadcastConfiguration configuration; + + /// Creates a new [Broadcast] instance. + static Future create( + {required int id, + required Media media, + required BroadcastConfiguration configuration}) async { + Broadcast broadcast = new _Broadcast(); + broadcast.id = id; + broadcast.media = media; + broadcast.configuration = configuration; + await channel.invokeMethod( + 'Broadcast.create', + { + 'id': id, + 'media': media.toMap(), + 'configuration': configuration.toMap(), + }, + ); + return broadcast; + } + + /// Starts broadcasting the [Media]. + void start() { + await channel.invokeMethod( + 'Broadcast.start', + { + 'id': id, + }, + ); + } + + /// Disposes this instance of [Broadcast]. + void dispose() { + await channel.invokeMethod( + 'Broadcast.dispose', + { + 'id': id, + }, + ); + } +} diff --git a/ffi/lib/src/chromecast.dart b/ffi/lib/src/chromecast.dart new file mode 100644 index 00000000..578f3acd --- /dev/null +++ b/ffi/lib/src/chromecast.dart @@ -0,0 +1,58 @@ +import 'package:dart_vlc/dart_vlc.dart'; +import 'package:dart_vlc/src/channel.dart'; + + +/// Internally used class to avoid direct creation of the object of a [Chromecast] class. +class _Chromecast extends Chromecast {} + + +class Chromecast { + /// ID for this [Chromecast]. + late int id; + + /// Sending from [Media]. + late Media media; + + /// IP address of your chromecast: `192.168.1.XXX` + late String ipAddress; + + /// Creates a new [Chromecast] instance. + static Future create( + {required int id, + required Media media, + required String ipAddress}) async { + Chromecast chromecast = new _Chromecast(); + chromecast.id = id; + chromecast.media = media; + chromecast.ipAddress = ipAddress; + await channel.invokeMethod( + 'Chromecast.create', + { + 'id': id, + 'media': media.toMap(), + 'ipAddress': ipAddress, + }, + ); + return chromecast; + } + + /// Start sending [Media] content to chromecast + Future send() async { + await channel.invokeMethod( + 'Chromecast.send', + { + 'id': id, + }, + ); + } + + /// Disposes this instance of [Chromecast]. + Future stop() async { + await channel.invokeMethod( + 'Chromecast.dispose', + { + 'id': id, + }, + ); + } +} diff --git a/ffi/lib/src/device.dart b/ffi/lib/src/device.dart new file mode 100644 index 00000000..17e8385d --- /dev/null +++ b/ffi/lib/src/device.dart @@ -0,0 +1,42 @@ +import 'dart:io'; + +/// Represents a playback [Device] for the [Player]. +class Device { + /// ID corresponding to the [Device]. + String id; + + /// Name of the [Device]. + String name; + + Device(this.id, this.name); + + /// Internally used method to easily transform data for sending through Platform channel. + static Device fromMap(dynamic map) => Device( + Platform.isWindows + ? (map['id'] != '' + ? '{0.0.0.00000000}.' + map['id'].toLowerCase() + : '') + : map['id'], + map['name'], + ); + + /// Internally used method to easily transform data for sending through Platform channel. + Map toMap() => { + 'id': this.id, + 'name': this.name, + }; +} + +/// [Devices.all] getter is used to get [List] of all available [Device] for playback in the [Player]. +class Devices { + /// Gets [List] of all available playback [Device]. + static Future> get all async { + dynamic devices = await channel.invokeMethod('Devices.all', {}); + return devices + .map( + (dynamic device) => Device.fromMap(device), + ) + .toList() + .cast(); + } +} diff --git a/ffi/lib/src/enums/equalizerMode.dart b/ffi/lib/src/enums/equalizerMode.dart new file mode 100644 index 00000000..ebe8f4a3 --- /dev/null +++ b/ffi/lib/src/enums/equalizerMode.dart @@ -0,0 +1,23 @@ + + +/// Predefined presets for [Equalizer]. +enum EqualizerMode { + flat, + classical, + club, + dance, + fullBass, + fullBassAndTreble, + fullTreble, + headphones, + largeHall, + live, + party, + pop, + reggae, + rock, + ska, + soft, + softRock, + techno +} diff --git a/ffi/lib/src/enums/mediaSourceType.dart b/ffi/lib/src/enums/mediaSourceType.dart new file mode 100644 index 00000000..2db8f5c1 --- /dev/null +++ b/ffi/lib/src/enums/mediaSourceType.dart @@ -0,0 +1,8 @@ +/// Enum to specify the type of [MediaSource] passed in [Player.open]. +enum MediaSourceType { + /// A single [Media]. + media, + + /// A [Playlist] containing multiple [Media] to play sequencially. + playlist, +} diff --git a/ffi/lib/src/enums/mediaType.dart b/ffi/lib/src/enums/mediaType.dart new file mode 100644 index 00000000..b6c553c2 --- /dev/null +++ b/ffi/lib/src/enums/mediaType.dart @@ -0,0 +1,14 @@ +/// Enum to specify the type of [Media]. +enum MediaType { + /// A [Media] opened from a [File]. + file, + + /// A [Media] opened from a [Uri]. + network, + + /// A [Media] opened from assets. + asset, + + /// A [Media] created from direct show. + directShow, +} diff --git a/ffi/lib/src/enums/playlistMode.dart b/ffi/lib/src/enums/playlistMode.dart new file mode 100644 index 00000000..5816da5e --- /dev/null +++ b/ffi/lib/src/enums/playlistMode.dart @@ -0,0 +1,11 @@ +/// Enum to specify the playback mode of [Playlist]. +enum PlaylistMode { + /// Indicates single [Playlist] mode + single, + + /// Indicates loop [Playlist] mode + loop, + + /// Indicates repeat [Playlist] mode + repeat +} diff --git a/ffi/lib/src/equalizer.dart b/ffi/lib/src/equalizer.dart new file mode 100644 index 00000000..a0700636 --- /dev/null +++ b/ffi/lib/src/equalizer.dart @@ -0,0 +1,92 @@ +import 'package:dart_vlc_ffi/src/enums/equalizerMode.dart'; + + +/// Internally used class to avoid direct creation of the object of a [Equalizer] class. +class _Equalizer extends Equalizer {} + + +/// Defines an [Equalizer] instance for usage in a [Player]. +/// +/// Use [Equalizer.createEmpty] for creating a default equalizer & [Equalizer.createMode] for creating an equalizer using a preset from [EqualizerMode]. +/// +/// Example +/// +/// ```dart +/// Player player = new Player(id: 0); +/// Equalizer equalizer = Equalizer.createMode(EqualizerMode.party); +/// player.setEqualizer(equalizer); +/// ``` +/// +class Equalizer { + /// Unique Id associated with this [Equalizer]. + late int id; + /// Preamp value of the [Equalizer]. Use [Equalizer.setPreAmp] to change value. + double preAmp = 0.0; + /// Values of amps of various bands in [Equalizer]. Use [Equalizer.setBandAmp] to change values. + Map bandAmps = {}; + /// Preset if [Equalizer] is initialized using [Equalizer.createMode], else `null`. + EqualizerMode? mode; + + /// Creates a default [Equalizer] instance with all values set to `0.0`. + static Future createEmpty() async { + Equalizer equalizer = new _Equalizer(); + dynamic _equalizer = await channel.invokeMethod( + 'Equalizer.createEmpty', + {}, + ); + equalizer.id = _equalizer['id']; + equalizer.preAmp = _equalizer['preAmp']; + equalizer.bandAmps = Map.from(_equalizer['bandAmps']); + return equalizer; + } + + /// Creates an [Equalizer] instance with any preset from [EqualizerMode]. + static Future createMode(EqualizerMode mode) async { + Equalizer equalizer = new _Equalizer(); + dynamic _equalizer = await channel.invokeMethod( + 'Equalizer.createMode', + { + 'mode': mode.index, + }, + ); + equalizer.id = _equalizer['id']; + equalizer.mode = mode; + equalizer.preAmp = _equalizer['preAmp']; + equalizer.bandAmps = Map.from(_equalizer['bandAmps']); + return equalizer; + } + + /// Sets value of [Equalizer.preAmp]. + /// + /// Constraints: + /// `-20.0 < amp < 20.0` + /// + Future setPreAmp(double amp) async { + await channel.invokeMethod( + 'Equalizer.setPreAmp', + { + 'id': this.id, + 'preAmp': amp, + }, + ); + this.preAmp = amp; + } + + /// Sets value of any particular band from [Equalizer.bandAmps]. + /// Band whose `amp` needs to be changed, must be present in [Equalizer.bandAmps]. + /// + /// Constraints: + /// `-20.0 < amp < 20.0` + /// + Future setBandAmp(double band, double amp) async { + await channel.invokeMethod( + 'Equalizer.setBandAmp', + { + 'id': this.id, + 'band': band, + 'amp': amp, + }, + ); + this.bandAmps[band] = amp; + } +} diff --git a/ffi/lib/src/internal/dynamiclibrary.dart b/ffi/lib/src/internal/dynamiclibrary.dart new file mode 100644 index 00000000..4def985c --- /dev/null +++ b/ffi/lib/src/internal/dynamiclibrary.dart @@ -0,0 +1,3 @@ +import 'dart:ffi'; + +late DynamicLibrary dynamicLibrary; diff --git a/ffi/lib/dart_vlc_ffi.dart b/ffi/lib/src/internal/ffi.dart similarity index 53% rename from ffi/lib/dart_vlc_ffi.dart rename to ffi/lib/src/internal/ffi.dart index d2d7e084..faf747c0 100644 --- a/ffi/lib/dart_vlc_ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -1,21 +1,23 @@ -/* - * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. - * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. - * https://github.com/alexmercerind - * alexmercerind@gmail.com - * - * GNU Lesser General Public License v2.1 - */ - import 'dart:ffi'; import 'dart:isolate'; +import 'package:dart_vlc_ffi/src/internal/typedefs/media.dart'; +import 'package:ffi/ffi.dart'; -import 'package:dart_vlc_ffi/source/internal/typedefs/player.dart'; -import 'package:dart_vlc_ffi/source/internal/typedefs/callback.dart'; -import 'package:dart_vlc_ffi/source/internal/dynamiclibrary.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/player.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/callback.dart'; +import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; +extension NativeTypes on List { + Pointer> toNativeUtf8Array() { + final List> listPointer = this.map((String string) => string.toNativeUtf8()).toList().cast>(); + final Pointer> pointerPointer = calloc.allocate(this.join('').length); + for (int index = 0; index < this.length; index++) pointerPointer[index] = listPointer[index]; + return pointerPointer; + } +} + +bool isInitialized = false; final ReceivePort receiver = new ReceivePort(); @@ -45,6 +47,10 @@ abstract class PlayerFFI { static final PlayerSetRateDart setRate = dynamicLibrary.lookup>('Player_setRate').asFunction(); + static final PlayerSetUserAgentDart setUserAgent = dynamicLibrary.lookup>('Player_setUserAgent').asFunction(); + + static final PlayerSetPlaylistModeDart setPlaylistMode = dynamicLibrary.lookup>('Player_setPlaylistMode').asFunction(); + static final PlayerAddDart add = dynamicLibrary.lookup>('Player_add').asFunction(); static final PlayerRemoveDart remove = dynamicLibrary.lookup>('Player_remove').asFunction(); @@ -55,12 +61,21 @@ abstract class PlayerFFI { } -abstract class CallbackFFI { +abstract class MediaFFI { + static final MediaParseDart parse = dynamicLibrary.lookup>('Media_parse').asFunction(); +} + + +abstract class DartVLC { - static void initialize() { - RegisterPostCObjectDart registerPostCObject = dynamicLibrary.lookup>('RegisterDart_PostCObject').asFunction(); - RegisterCallbackPortDart registerCallbackPort = dynamicLibrary.lookup>('RegisterDart_CallbackPort').asFunction(); - registerPostCObject(NativeApi.postCObject); - registerCallbackPort(receiver.sendPort.nativePort); + static void initialize(String dynamicLibraryPath) { + if (!isInitialized) { + dynamicLibrary = DynamicLibrary.open(dynamicLibraryPath); + RegisterPostCObjectDart registerPostCObject = dynamicLibrary.lookup>('RegisterDart_PostCObject').asFunction(); + RegisterCallbackPortDart registerCallbackPort = dynamicLibrary.lookup>('RegisterDart_CallbackPort').asFunction(); + registerPostCObject(NativeApi.postCObject); + registerCallbackPort(receiver.sendPort.nativePort); + isInitialized = true; + } } } diff --git a/ffi/lib/src/internal/typedefs/broadcast.dart b/ffi/lib/src/internal/typedefs/broadcast.dart new file mode 100644 index 00000000..e69de29b diff --git a/ffi/lib/source/internal/typedefs/callback.dart b/ffi/lib/src/internal/typedefs/callback.dart similarity index 60% rename from ffi/lib/source/internal/typedefs/callback.dart rename to ffi/lib/src/internal/typedefs/callback.dart index b5a8d99f..f837b2b6 100644 --- a/ffi/lib/source/internal/typedefs/callback.dart +++ b/ffi/lib/src/internal/typedefs/callback.dart @@ -1,13 +1,3 @@ -/* - * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. - * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. - * https://github.com/alexmercerind - * alexmercerind@gmail.com - * - * GNU Lesser General Public License v2.1 - */ - import 'dart:ffi'; typedef RegisterPostCObjectCXX = Void Function(Pointer)>> functionPointer); diff --git a/ffi/lib/src/internal/typedefs/media.dart b/ffi/lib/src/internal/typedefs/media.dart new file mode 100644 index 00000000..2f822d11 --- /dev/null +++ b/ffi/lib/src/internal/typedefs/media.dart @@ -0,0 +1,7 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +/// Following typedef is used for: +/// Media::parse +typedef MediaParseCXX = Pointer> Function(Pointer type, Pointer resource, Int32 timeout); +typedef MediaParseDart = Pointer> Function(Pointer type, Pointer resource, int timeout); diff --git a/ffi/lib/source/internal/typedefs/player.dart b/ffi/lib/src/internal/typedefs/player.dart similarity index 73% rename from ffi/lib/source/internal/typedefs/player.dart rename to ffi/lib/src/internal/typedefs/player.dart index cccbcce3..2b86567a 100644 --- a/ffi/lib/source/internal/typedefs/player.dart +++ b/ffi/lib/src/internal/typedefs/player.dart @@ -1,20 +1,10 @@ -/* - * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. - * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. - * https://github.com/alexmercerind - * alexmercerind@gmail.com - * - * GNU Lesser General Public License v2.1 - */ - import 'dart:ffi'; import 'package:ffi/ffi.dart'; /// Following typedef is used for: /// Player::create -typedef PlayerCreateCXX = Void Function(Int32 id, Int32 videoHeight, Int32 videoWidth); -typedef PlayerCreateDart = void Function(int id, int videoHeight, int videoWidth); +typedef PlayerCreateCXX = Void Function(Int32 id, Int32 videoHeight, Int32 videoWidth, Int32 commandlineArgumentsCount, Pointer> commandlineArguments); +typedef PlayerCreateDart = void Function(int id, int videoHeight, int videoWidth, int commandlineArgumentsCount, Pointer> commandlineArguments); /// Following typedef is used for: /// Player::open @@ -51,6 +41,11 @@ typedef PlayerSetVolumeDart = void Function(int id, double volume); typedef PlayerSetRateCXX = Void Function(Int32 id, Float volume); typedef PlayerSetRateDart = void Function(int id, double volume); +/// Following typedef is used for: +/// Player::setUserAgent +typedef PlayerSetUserAgentCXX = Void Function(Int32 id, Pointer userAgent); +typedef PlayerSetUserAgentDart = void Function(int id, Pointer userAgent); + /// Following typedef is used for: /// Player::setPlaylistMode /// Following typedef is used for: @@ -60,8 +55,8 @@ typedef PlayerSetPlaylistModeDart = void Function(int id, Pointer mode); /// Following typedef is used for: /// Player::add -typedef PlayerAddCXX = Void Function(Int32 id, Int32 mediaId, Pointer type, Pointer resource); -typedef PlayerAddDart = void Function(int id, int mediaId, Pointer type, Pointer resource); +typedef PlayerAddCXX = Void Function(Int32 id, Pointer type, Pointer resource); +typedef PlayerAddDart = void Function(int id, Pointer type, Pointer resource); /// Following typedef is used for: /// Player::remove @@ -70,8 +65,8 @@ typedef PlayerRemoveDart = void Function(int id, int index); /// Following typedef is used for: /// Player::jump -typedef PlayerInsertCXX = Void Function(Int32 id, Int32 index, Int32 mediaId, Pointer type, Pointer resource); -typedef PlayerInsertDart = void Function(int id, int index, int mediaId, Pointer type, Pointer resource); +typedef PlayerInsertCXX = Void Function(Int32 id, Int32 index, Pointer type, Pointer resource); +typedef PlayerInsertDart = void Function(int id, int index, Pointer type, Pointer resource); /// Following typedef is used for: /// Player::move diff --git a/ffi/lib/src/mediaSource/media.dart b/ffi/lib/src/mediaSource/media.dart new file mode 100644 index 00000000..19d0ba3a --- /dev/null +++ b/ffi/lib/src/mediaSource/media.dart @@ -0,0 +1,116 @@ +import 'dart:io'; +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:dart_vlc_ffi/src/internal/ffi.dart'; +import 'package:dart_vlc_ffi/src/mediaSource/mediaSource.dart'; +import 'package:dart_vlc_ffi/src/enums/mediaSourceType.dart'; +import 'package:dart_vlc_ffi/src/enums/mediaType.dart'; + +/// Internally used class to avoid direct creation of the object of a [Media] class. +class _Media extends Media {} + +/// A media object to open inside a [Player]. +/// +/// Pass `true` to [parse] for retrieving the metadata of the [Media]. +/// [timeout] sets the time-limit for retriveing metadata. +/// [Media.metas] can be then, accessed to get the retrived metadata as `Map`. +/// +/// * A [Media] from a [File]. +/// +/// ```dart +/// Media media = await Media.file(new File('C:/music.ogg')); +/// ``` +/// +/// * A [Media] from a [Uri]. +/// +/// ```dart +/// Media media = await Media.network('http://alexmercerind.github.io/music.mp3'); +/// ``` +/// +/// +abstract class Media extends MediaSource { + MediaSourceType mediaSourceType = MediaSourceType.media; + Map metas = {}; + Map extras = {}; + late MediaType mediaType; + late String resource; + + /// Makes [Media] object from a [File]. + static Future file(File file, + {bool parse: false, + Map? extras, + Duration timeout: const Duration(seconds: 10)}) async { + Media media = new _Media(); + media.mediaType = MediaType.file; + media.resource = file.path; + if (parse) { + await media.parse(timeout); + } + if (extras != null) { + media.extras = extras; + } + return media; + } + + /// Makes [Media] object from url. + static Future network(dynamic url, + {bool parse: false, + Map? extras, + Duration timeout: const Duration(seconds: 10)}) async { + Media media = new _Media(); + media.mediaType = MediaType.network; + if (url is Uri) + media.resource = url.toString(); + else + media.resource = url; + if (parse) { + await media.parse(timeout); + } + if (extras != null) { + media.extras = extras; + } + return media; + } + + /// Makes [Media] object from direct show. + static Future directShow( + {String vdev = '', String adev = '', required int liveCaching}) async { + Media media = new _Media(); + media.mediaType = MediaType.directShow; + media.resource = + 'dshow:// :dshow-vdev=$vdev :dshow-adev=$adev :live-caching=$liveCaching'; + return media; + } + + Future parse(Duration timeout) async { + Pointer> metas = MediaFFI.parse( + this.mediaType.toString().toNativeUtf8(), + this.resource.toNativeUtf8(), + timeout.inSeconds + ); + this.metas['title'] = metas.elementAt(0).value.toDartString(); + this.metas['artist'] = metas.elementAt(1).value.toDartString(); + this.metas['genre'] = metas.elementAt(2).value.toDartString(); + this.metas['copyright'] = metas.elementAt(3).value.toDartString(); + this.metas['album'] = metas.elementAt(4).value.toDartString(); + this.metas['trackNumber'] = metas.elementAt(5).value.toDartString(); + this.metas['description'] = metas.elementAt(6).value.toDartString(); + this.metas['rating'] = metas.elementAt(7).value.toDartString(); + this.metas['date'] = metas.elementAt(8).value.toDartString(); + this.metas['settings'] = metas.elementAt(9).value.toDartString(); + this.metas['url'] = metas.elementAt(10).value.toDartString(); + this.metas['language'] = metas.elementAt(11).value.toDartString(); + this.metas['nowPlaying'] = metas.elementAt(12).value.toDartString(); + this.metas['encodedBy'] = metas.elementAt(13).value.toDartString(); + this.metas['artworkUrl'] = metas.elementAt(14).value.toDartString(); + this.metas['trackTotal'] = metas.elementAt(15).value.toDartString(); + this.metas['director'] = metas.elementAt(16).value.toDartString(); + this.metas['season'] = metas.elementAt(17).value.toDartString(); + this.metas['episode'] = metas.elementAt(18).value.toDartString(); + this.metas['actors'] = metas.elementAt(19).value.toDartString(); + this.metas['albumArtist'] = metas.elementAt(20).value.toDartString(); + this.metas['discNumber'] = metas.elementAt(21).value.toDartString(); + this.metas['discTotal'] = metas.elementAt(22).value.toDartString(); + this.metas['duration'] = metas.elementAt(23).value.toDartString(); + } +} diff --git a/ffi/lib/src/mediaSource/mediaSource.dart b/ffi/lib/src/mediaSource/mediaSource.dart new file mode 100644 index 00000000..672ca6a0 --- /dev/null +++ b/ffi/lib/src/mediaSource/mediaSource.dart @@ -0,0 +1,6 @@ +import 'package:dart_vlc_ffi/src/enums/mediaSourceType.dart'; + +/// Parent abstract class of [Media] and [Playlist]. +abstract class MediaSource { + late MediaSourceType mediaSourceType; +} diff --git a/ffi/lib/src/mediaSource/playlist.dart b/ffi/lib/src/mediaSource/playlist.dart new file mode 100644 index 00000000..6ac8e748 --- /dev/null +++ b/ffi/lib/src/mediaSource/playlist.dart @@ -0,0 +1,33 @@ +import 'package:dart_vlc_ffi/src/enums/playlistMode.dart'; +import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; +import 'package:dart_vlc_ffi/src/mediaSource/mediaSource.dart'; +import 'package:dart_vlc_ffi/src/enums/mediaSourceType.dart'; + +/// A playlist object to open inside a [Player.open]. +/// +/// Takes [List] of [Media] as parameter to open inside the [Player] instance, for playing sequencially. +/// +/// ```dart +/// new Playlist( +/// medias: [ +/// Media.file( +/// new File('C:/music.mp3'), +/// ), +/// Media.network( +/// 'https://alexmercerind.github.io/music.mp3', +/// ), +/// Media.file( +/// new File('C:/audio.mp3'), +/// ), +/// ], +/// ); +/// ``` +/// +class Playlist extends MediaSource { + + /// [List] of [Media] present in the playlist. + List medias; + MediaSourceType mediaSourceType = MediaSourceType.playlist; + PlaylistMode playlistMode; + Playlist({required this.medias, this.playlistMode = PlaylistMode.single}); +} diff --git a/ffi/lib/src/player.dart b/ffi/lib/src/player.dart new file mode 100644 index 00000000..9277f3df --- /dev/null +++ b/ffi/lib/src/player.dart @@ -0,0 +1,302 @@ +import 'dart:async'; +import 'package:ffi/ffi.dart'; +import 'package:dart_vlc_ffi/dart_vlc.dart'; +import 'package:dart_vlc_ffi/src/equalizer.dart'; +import 'package:dart_vlc_ffi/src/internal/ffi.dart'; +import 'package:dart_vlc_ffi/src/playerState/playerState.dart'; +import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; +import 'package:dart_vlc_ffi/src/mediaSource/mediaSource.dart'; +import 'package:dart_vlc_ffi/src/device.dart'; + + +/// A [Player] to open & play a [Media] or [Playlist] from file, network or asset. +/// +/// Use [Player] constructor to create a new instance of a [Player]. +/// Provide a unique [id] while instanciating. +/// +/// ```dart +/// Player player = new Player(id: 0); +/// ``` +/// +/// If you wish to use this instance for [Video] playback, then provide [videoWidth] & [videoHeight] optional parameters. +/// Higher value may lead to degraded performance. +/// +/// ```dart +/// Player player = new Player( +/// id: 0, +/// videoWidth: 1920, +/// videoHeight: 1080, +/// ); +/// ``` +/// +/// Do not provide [videoWidth] & [videoHeight], if you wish to use the [Player] for only audio playback. +/// +/// Use various methods & event streams avaiable to control & listen to events of the playback. +/// +class Player { + /// Id associated with the [Player] instance. + final int id; + + /// Width of the [Video] frames to be extracted. Higher value may lead to degraded performance. + late int videoWidth; + + /// Height of the [Video] frames to be extracted. Higher value may lead to degraded performance. + late int videoHeight; + + /// Commandline arguments passed to this instance of [Player]. + List commandlineArguments = []; + + /// State of the current & opened [MediaSource] in [Player] instance. + CurrentState current = new CurrentState(); + + /// Stream to listen to current & opened [MediaSource] state of the [Player] instance. + late Stream currentStream; + + /// Position & duration state of the [Player] instance. + PositionState position = new PositionState(); + + /// Stream to listen to position & duration state of the [Player] instance. + late Stream positionStream; + + /// Playback state of the [Player] instance. + PlaybackState playback = new PlaybackState(); + + /// Stream to listen to playback state of the [Player] instance. + late Stream playbackStream; + + /// Volume & Rate state of the [Player] instance. + GeneralState general = new GeneralState(); + + /// Stream to listen to volume & rate state of the [Player] instance. + late Stream generalStream; + + /// Creates a new [Player] instance. + /// + /// Takes unique id as parameter. + /// + /// ```dart + /// Player player = new Player(id: 0); + /// ``` + /// + Player({required this.id, int videoWidth: 0, int videoHeight: 0, Device? device, List? commandlineArguments}) { + if (device is Device) { + this.setDevice(device); + } + if (commandlineArguments != null) this.commandlineArguments = commandlineArguments; + this.videoWidth = videoWidth; + this.videoHeight = videoHeight; + this.currentController = StreamController.broadcast(); + this.currentStream = this.currentController.stream; + this.positionController = StreamController.broadcast(); + this.positionStream = this.positionController.stream; + this.playbackController = StreamController.broadcast(); + this.playbackStream = this.playbackController.stream; + this.generalController = StreamController.broadcast(); + this.generalStream = this.generalController.stream; + players[id] = this; + PlayerFFI.create( + this.id, + this.videoWidth, + this.videoHeight, + this.commandlineArguments.length, + this.commandlineArguments.toNativeUtf8Array(), + ); + } + + /// Opens a new media source into the player. + /// + /// Takes a [Media] or [Playlist] to open in the player. + /// + /// Starts playback itself by default. Pass `autoStart: false` to stop this from happening. + /// + /// * Open a new [Media]. + /// + /// ```dart + /// player.open( + /// Media.file( + /// new File('C:/music.ogg'), + /// ), + /// autoStart: false, + /// ); + /// ``` + /// + /// * Open a new [Playlist]. + /// + /// ```dart + /// player.open( + /// new Playlist( + /// medias: [ + /// Media.file( + /// new File('C:/music.mp3'), + /// ), + /// Media.file( + /// new File('C:/audio.mp3'), + /// ), + /// Media.network('https://alexmercerind.github.io/music.mp3'), + /// ], + /// ), + /// ); + /// ``` + /// + void open(MediaSource source, {bool autoStart: true}) { + if (source is Media) { + PlayerFFI.open( + this.id, + autoStart ? 1: 0, + [ source.resource ].toNativeUtf8Array(), + 1 + ); + } + if (source is Playlist) { + PlayerFFI.open( + this.id, + autoStart ? 1: 0, + source.medias.map((media) => media.toString()).toList().toNativeUtf8Array(), + source.medias.length + ); + } + } + + /// Plays opened [MediaSource], + void play() { + PlayerFFI.play(this.id); + } + + /// Pauses opened [MediaSource], + void pause() { + PlayerFFI.pause(this.id); + } + + /// Play or Pause opened [MediaSource], + void playOrPause() { + PlayerFFI.playOrPause(this.id); + } + + /// Stops the [Player]. + /// + /// Also resets the [Device] set using [Player.setDevice]. + /// A new instance must be created, once this method is called. + /// + void stop() { + PlayerFFI.stop(this.id); + } + + /// Jumps to the next [Media] in the [Playlist] opened. + void next() { + PlayerFFI.next(this.id); + } + + /// Jumps to the previous [Media] in the [Playlist] opened. + void back() { + PlayerFFI.back(this.id); + } + + /// Jumps to [Media] at specific index in the [Playlist] opened. + /// Pass index as parameter. + void jump(int index) { + PlayerFFI.jump(this.id, index); + } + + /// Seeks the [Media] currently playing in the [Player] instance, to the provided [Duration]. + void seek(Duration duration) { + PlayerFFI.seek(this.id, duration.inMilliseconds); + } + + /// Sets volume of the [Player] instance. + void setVolume(double volume) { + PlayerFFI.setVolume(this.id, volume); + } + + /// Sets playback rate of the [Media] currently playing in the [Player] instance. + void setRate(double rate) { + PlayerFFI.setRate(this.id, rate); + } + + + /// Sets user agent for dart_vlc player. + void setUserAgent(String userAgent) { + PlayerFFI.setUserAgent(this.id, userAgent.toNativeUtf8()); + } + + /// Changes [Playlist] playback mode. + void setPlaylistMode(PlaylistMode playlistMode) { + PlayerFFI.setPlaylistMode(this.id, playlistMode.toString().toNativeUtf8()); + } + + /// Appends [Media] to the [Playlist] of the [Player] instance. + void add(Media source) { + PlayerFFI.add( + this.id, + source.mediaType.toString().toNativeUtf8(), + source.resource.toString().toNativeUtf8() + ); + } + + /// Removes [Media] from the [Playlist] at a specific index. + void remove(int index) { + PlayerFFI.remove(this.id, index); + } + + /// Inserts [Media] to the [Playlist] of the [Player] instance at specific index. + void insert(int index, Media source) { + PlayerFFI.insert( + this.id, + index, + source.mediaType.toString().toNativeUtf8(), + source.resource.toString().toNativeUtf8() + ); + } + + /// Moves [Media] already present in the [Playlist] of the [Player] from [initialIndex] to [finalIndex]. + void move(int initialIndex, int finalIndex) { + PlayerFFI.move( + this.id, + initialIndex, + finalIndex + ); + } + + /// Sets playback [Device] for the instance of [Player]. + /// + /// Use [Devices.all] getter to get [List] of all [Device]. + /// + /// A playback [Device] for a [Player] instance cannot be changed in the middle of playback. + /// Device will be switched once a new [Media] is played. + /// + Future setDevice(Device device) async { + await this._isInstanceCreated.future; + await channel.invokeMethod( + 'Player.setDevice', + { + 'id': this.id, + 'device': device.toMap(), + }, + ); + } + + /// Sets [Equalizer] for the [Player]. + Future setEqualizer(Equalizer equalizer) async { + await this._isInstanceCreated.future; + await channel.invokeMethod( + 'Player.setEqualizer', + { + 'id': this.id, + 'equalizerId': equalizer.id, + }, + ); + } + + /// Destroys the instance of [Player] & closes all [StreamController]s in it. + Future dispose() async { + await this.currentController.close(); + await this.positionController.close(); + await this.playbackController.close(); + await this.generalController.close(); + } + + /// Internally used [StreamController]s, + late StreamController currentController; + late StreamController positionController; + late StreamController playbackController; + late StreamController generalController; +} diff --git a/ffi/lib/src/playerState/playerState.dart b/ffi/lib/src/playerState/playerState.dart new file mode 100644 index 00000000..d196113b --- /dev/null +++ b/ffi/lib/src/playerState/playerState.dart @@ -0,0 +1,46 @@ +import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; + +/// State of a [Player] instance. +class CurrentState { + /// Index of currently playing [Media]. + int? index; + + /// Currently playing [Media]. + Media? media; + + /// [List] of [Media] currently opened in the [Player] instance. + List medias = []; + + /// Whether a [Playlist] is opened or a [Media]. + bool isPlaylist = false; +} + +/// Position & duration state of a [Player] instance. +class PositionState { + /// Position of playback in [Duration] of currently playing [Media]. + Duration? position = Duration.zero; + + /// Length of currently playing [Media] in [Duration]. + Duration? duration = Duration.zero; +} + +/// Playback state of a [Player] instance. +class PlaybackState { + /// Whether [Player] instance is playing or not. + bool isPlaying = false; + + /// Whether [Player] instance is seekable or not. + bool isSeekable = true; + + /// Whether the current [Media] has ended playing or not. + bool isCompleted = false; +} + +/// Volume & Rate state of a [Player] instance. +class GeneralState { + /// Volume of [Player] instance. + double volume = 1.0; + + /// Rate of playback of [Player] instance. + double rate = 1.0; +} diff --git a/ffi/lib/src/record.dart b/ffi/lib/src/record.dart new file mode 100644 index 00000000..36707107 --- /dev/null +++ b/ffi/lib/src/record.dart @@ -0,0 +1,58 @@ +import 'dart:io'; + +import 'package:dart_vlc_ffi/dart_vlc.dart'; +import 'package:dart_vlc_ffi/src/channel.dart'; + + +/// Internally used class to avoid direct creation of the object of a [Record] class. +class _Record extends Record {} + + +class Record { + /// ID for this record. + late int id; + + /// Recording from [Media]. + late Media media; + + /// Path where the recording is saved example: `/home/alexmercerind/recording.mp3` + late File savingFile; + + /// Creates a new [Record] instance. + static Future create( + {required int id, required Media media, required File savingFile}) async { + Record record = new _Record(); + record.id = id; + record.media = media; + record.savingFile = savingFile; + await channel.invokeMethod( + 'Record.create', + { + 'id': id, + 'media': media.toMap(), + 'savingFile': savingFile.path, + }, + ); + return record; + } + + /// Starts recording the [Media]. + Future start() async { + await channel.invokeMethod( + 'Record.start', + { + 'id': id, + }, + ); + } + + /// Disposes this instance of [Record]. + Future stop() async { + await channel.invokeMethod( + 'Record.dispose', + { + 'id': id, + }, + ); + } +} diff --git a/ffi/main.dart b/ffi/main.dart index a3614ca0..2b724236 100644 --- a/ffi/main.dart +++ b/ffi/main.dart @@ -1,30 +1,13 @@ -import 'dart:ffi'; -import 'package:ffi/ffi.dart'; - -import 'package:dart_vlc_ffi/dart_vlc_ffi.dart'; - - -extension on List { - /// Converts List to const char**. - Pointer> toNativeUtf8Array() { - final List> listPointer = this.map((String string) => string.toNativeUtf8()).toList().cast>(); - final Pointer> pointerPointer = calloc.allocate(this.join('').length); - for (int index = 0; index < this.length; index++) pointerPointer[index] = listPointer[index]; - return pointerPointer; - } -} +import 'package:dart_vlc_ffi/source/internal/dart_vlc.dart'; Future main() async { receiver.listen((event) => print(event)); CallbackFFI.initialize(); - PlayerFFI.create(0, 0, 0); + PlayerFFI.create(0, 320, 240, 0, [].toNativeUtf8Array()); PlayerFFI.open( 0, 1, - ['0', 'MediaType.file', '/home/alexmercerind/music.OGG', '1', 'MediaType.file', '/home/alexmercerind/audio.OGG'].toNativeUtf8Array(), - 2 + ['0', 'MediaType.file', '/home/alexmercerind/video.mp4'].toNativeUtf8Array(), + 1 ); await Future.delayed(Duration(seconds: 5)); - PlayerFFI.pause(0); - PlayerFFI.seek(0, 20000); - PlayerFFI.play(0); -} \ No newline at end of file +} diff --git a/ffi/native/main.cpp b/ffi/native/main.cpp index b7d90f18..ad2bc3a0 100644 --- a/ffi/native/main.cpp +++ b/ffi/native/main.cpp @@ -1,7 +1,7 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind * alexmercerind@gmail.com * @@ -78,7 +78,7 @@ EXPORT void Player_open(int id, bool autoStart, const char** source, int sourceS else if (strcmp(type, "MediaType.network") == 0) media = Media::network(id, resource, false); else - media = Media::asset(id, resource, false); + media = Media::directShow(id, resource); medias.emplace_back(media); } player->open( @@ -137,6 +137,11 @@ EXPORT void Player_setRate(int id, float rate) { player->setRate(rate); } +EXPORT void Player_setUserAgent(int id, const char* userAgent) { + Player* player = players->get(id); + player->setUserAgent(userAgent); +} + EXPORT void Player_setPlaylistMode(int id, const char* mode) { Player* player = players->get(id); PlaylistMode playlistMode; @@ -144,20 +149,20 @@ EXPORT void Player_setPlaylistMode(int id, const char* mode) { playlistMode = PlaylistMode::repeat; else if (strcmp(mode, "playlistMode.loop") == 0) playlistMode = PlaylistMode::loop; - else + else if (strcmp(mode, "playlistMode.single") == 0) playlistMode = PlaylistMode::single; player->setPlaylistMode(playlistMode); } -EXPORT void Player_add(int id, int mediaId, const char* type, const char* resource) { +EXPORT void Player_add(int id, const char* type, const char* resource) { Player* player = players->get(id); Media* media; if (strcmp(type, "MediaType.file") == 0) - media = Media::file(mediaId, resource, false); + media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) - media = Media::network(mediaId, resource, false); - else - media = Media::asset(mediaId, resource, false); + media = Media::network(0, resource, false); + else if (strcmp(type, "MediaType.directShow") == 0) + media = Media::directShow(0, resource); player->add(media); } @@ -166,15 +171,15 @@ EXPORT void Player_remove(int id, int index) { player->remove(index); } -EXPORT void Player_insert(int id, int index, int mediaId, const char* type, const char* resource) { +EXPORT void Player_insert(int id, int index, const char* type, const char* resource) { Player* player = players->get(id); Media* media; if (strcmp(type, "MediaType.file") == 0) - media = Media::file(mediaId, resource, false); + media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) - media = Media::network(mediaId, resource, false); - else - media = Media::asset(mediaId, resource, false); + media = Media::network(0, resource, false); + else if (strcmp(type, "MediaType.directShow") == 0) + media = Media::directShow(0, resource); player->insert(index, media); } @@ -186,11 +191,11 @@ EXPORT void Player_move(int id, int initialIndex, int finalIndex) { EXPORT char** Media_parse(const char* type, const char* resource, int timeout) { Media* media; if (strcmp(type, "MediaType.file") == 0) - media = Media::file(0, resource, false, timeout); + media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) - media = Media::network(0, resource, false, timeout); - else - media = Media::asset(0, resource, false, timeout); + media = Media::network(0, resource, false); + else if (strcmp(type, "MediaType.directShow") == 0) + media = Media::directShow(0, resource); char** metas = new char*[media->metas.size()]; int index = 0; for (auto &meta: media->metas) { @@ -200,6 +205,36 @@ EXPORT char** Media_parse(const char* type, const char* resource, int timeout) { return metas; } +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; + if (strcmp(type, "MediaType.file") == 0) + media = Media::file(0, resource, false); + else if (strcmp(type, "MediaType.network") == 0) + media = Media::network(0, resource, false); + else if (strcmp(type, "MediaType.directShow") == 0) + media = Media::directShow(0, resource); + BroadcastConfiguration configuration( + access, + mux, + dst, + vcodec, + vb, + acodec, + ab + ); + Broadcast* broadcast = broadcasts->get(id, media, &configuration); +} + +EXPORT void Broadcast_start(int id) { + Broadcast* broadcast = broadcasts->get(id, nullptr, nullptr); + broadcast->start(); +} + +EXPORT void Broadcast_dispose(int id) { + Broadcast* broadcast = broadcasts->get(id, nullptr, nullptr); + broadcast->dispose(); +} + #endif #ifdef __cplusplus diff --git a/ffi/pubspec.yaml b/ffi/pubspec.yaml index 10dd960d..61fca21c 100644 --- a/ffi/pubspec.yaml +++ b/ffi/pubspec.yaml @@ -3,7 +3,7 @@ description: FFI based binding to dart_vlc wrapper (Based on libVLC & libVLC++). version: 0.0.1 environment: - sdk: '>=2.10.0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: ffi: ^1.0.0 diff --git a/ffi/source/callbackmanager.hpp b/ffi/source/callbackmanager.hpp deleted file mode 100644 index 88444d29..00000000 --- a/ffi/source/callbackmanager.hpp +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef EXPORT -#ifdef __WIN32 -#define EXPORT declspec(__dllexport) -#else -#define EXPORT -#endif -#endif - -#include - -#ifndef CallbackManager_h -#define CallbackManager_h - - -typedef int64_t Dart_Port; - - -typedef enum { - Dart_TypedData_kByteData = 0, - Dart_TypedData_kInt8, - Dart_TypedData_kUint8, - Dart_TypedData_kUint8Clamped, - Dart_TypedData_kInt16, - Dart_TypedData_kUint16, - Dart_TypedData_kInt32, - Dart_TypedData_kUint32, - Dart_TypedData_kInt64, - Dart_TypedData_kUint64, - Dart_TypedData_kFloat32, - Dart_TypedData_kFloat64, - Dart_TypedData_kFloat32x4, - Dart_TypedData_kInvalid -} Dart_TypedData_Type; - - -typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle; - - -typedef void (*Dart_WeakPersistentHandleFinalizer) (void* isolate_callback_data, Dart_WeakPersistentHandle handle, void* peer); - - -typedef enum { - Dart_CObject_kNull = 0, - Dart_CObject_kBool, - Dart_CObject_kInt32, - Dart_CObject_kInt64, - Dart_CObject_kDouble, - Dart_CObject_kString, - Dart_CObject_kArray, - Dart_CObject_kTypedData, - Dart_CObject_kExternalTypedData, - Dart_CObject_kSendPort, - Dart_CObject_kCapability, - Dart_CObject_kUnsupported, - Dart_CObject_kNumberOfTypes -} Dart_CObject_Type; - - -typedef struct _Dart_CObject { - Dart_CObject_Type type; - union { - bool as_bool; - int32_t as_int32; - int64_t as_int64; - double as_double; - char* as_string; - struct { - Dart_Port id; - Dart_Port origin_id; - } as_send_port; - struct { - int64_t id; - } as_capability; - struct { - intptr_t length; - struct _Dart_CObject** values; - } as_array; - struct { - Dart_TypedData_Type type; - intptr_t length; - uint8_t* values; - } as_typed_data; - struct { - Dart_TypedData_Type type; - intptr_t length; - uint8_t* data; - void* peer; - Dart_WeakPersistentHandleFinalizer callback; - } as_external_typed_data; - } value; -} Dart_CObject; - - -typedef bool (*Dart_PostCObjectType)(Dart_Port port_id, Dart_CObject* message); - - -#ifdef __cplusplus -extern "C" { -#endif - -Dart_PostCObjectType dartPostCObject = NULL; -Dart_Port callbackPort; - -EXPORT void RegisterDart_PostCObject(Dart_PostCObjectType _dartPostCObject) { - dartPostCObject = _dartPostCObject; -} - -EXPORT void RegisterDart_CallbackPort(Dart_Port _callbackPort) { - callbackPort = _callbackPort; -} - -EXPORT void callbackInt32(int32_t value) { - Dart_CObject dart_object; - dart_object.type = Dart_CObject_kInt32; - dart_object.value.as_int32 = value; - dartPostCObject(callbackPort, &dart_object); -} - -EXPORT void callbackStringArray(int length, char** values) { - Dart_CObject **valueObjects = new Dart_CObject *[length]; - for (int i = 0; i < length; i++) { - Dart_CObject *valueObject = new Dart_CObject; - valueObject->type = Dart_CObject_kString; - valueObject->value.as_string = values[i]; - valueObjects[i] = valueObject; - } - Dart_CObject dart_object; - dart_object.type = Dart_CObject_kArray; - dart_object.value.as_array.length = length; - dart_object.value.as_array.values = valueObjects; - dartPostCObject(callbackPort, &dart_object); - for (int i = 0; i < length; i++) { - delete valueObjects[i]; - } - delete[] valueObjects; -} - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/ffi/source/eventmanager.hpp b/ffi/source/eventmanager.hpp deleted file mode 100644 index a6c2b696..00000000 --- a/ffi/source/eventmanager.hpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. - * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. - * https://github.com/alexmercerind - * alexmercerind@gmail.com - * - * GNU Lesser General Public License v2.1 - */ - -#include "../dartvlc/main.cpp" -#include "callbackmanager.hpp" - -#ifdef __cplusplus -extern "C" { -#endif - -void Player_onPlayPauseStop(PlayerState* state) { - std::vector event; - event.push_back(std::to_string(state->id)); - event.push_back("playbackEvent"); - event.push_back(std::to_string(state->isPlaying)); - event.push_back(std::to_string(state->isSeekable)); - int _size = event.size(); - char** _event = new char*[_size]; - for (int index = 0; index < _size; index++) - _event[index] = const_cast(event[index].c_str()); - callbackStringArray( - _size, - _event - ); - delete[] _event; -} - -void Player_onPosition(PlayerState* state) { - std::vector event; - event.push_back(std::to_string(state->id)); - event.push_back("positionEvent"); - event.push_back(std::to_string(state->index)); - event.push_back(std::to_string(state->position)); - event.push_back(std::to_string(state->duration)); - int _size = event.size(); - char** _event = new char*[_size]; - for (int index = 0; index < _size; index++) - _event[index] = const_cast(event[index].c_str()); - callbackStringArray( - _size, - _event - ); - delete[] _event; -} - -void Player_onComplete(PlayerState* state) { - std::vector event; - event.push_back(std::to_string(state->id)); - event.push_back("completeEvent"); - event.push_back(std::to_string(state->isCompleted)); - int _size = event.size(); - char** _event = new char*[_size]; - for (int index = 0; index < _size; index++) - _event[index] = const_cast(event[index].c_str()); - callbackStringArray( - _size, - _event - ); - delete[] _event; -} - -void Player_onVolume(PlayerState* state) { - std::vector event; - event.push_back(std::to_string(state->id)); - event.push_back("volumeEvent"); - event.push_back(std::to_string(state->volume)); - int _size = event.size(); - char** _event = new char*[_size]; - for (int index = 0; index < _size; index++) - _event[index] = const_cast(event[index].c_str()); - callbackStringArray( - _size, - _event - ); - delete[] _event; -} - -void Player_onRate(PlayerState* state) { - std::vector event; - event.push_back(std::to_string(state->id)); - event.push_back("rateEvent"); - event.push_back(std::to_string(state->rate)); - int _size = event.size(); - char** _event = new char*[_size]; - for (int index = 0; index < _size; index++) - _event[index] = const_cast(event[index].c_str()); - callbackStringArray( - _size, - _event - ); - delete[] _event; -} - -void Player_onOpen(PlayerState* state) { - std::vector event; - event.push_back(std::to_string(state->id)); - event.push_back("openEvent"); - event.push_back(std::to_string(state->index)); - event.push_back(std::to_string(state->isPlaylist)); - for (Media* media: state->medias->medias) { - event.push_back(std::to_string(media->id)); - event.push_back(media->mediaType); - event.push_back(media->resource); - } - int _size = event.size(); - char** _event = new char*[_size]; - for (int index = 0; index < _size; index++) - _event[index] = const_cast(event[index].c_str()); - callbackStringArray( - _size, - _event - ); - delete[] _event; -} - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/ffi/source/main.cpp b/ffi/source/main.cpp deleted file mode 100644 index 86956b23..00000000 --- a/ffi/source/main.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. - * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. - * https://github.com/alexmercerind - * alexmercerind@gmail.com - * - * GNU Lesser General Public License v2.1 - */ - -#ifndef EXPORT -#ifdef __WIN32 -#define EXPORT declspec(__dllexport) -#else -#define EXPORT -#endif -#endif - -#include "eventmanager.hpp" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef DART_VLC_FFI -#define DART_VLC_FFI - - -EXPORT void Player_create(int id, int videoWidth, int videoHeight) { - Player* player = players->get(id); - player->videoWidth = videoWidth; - player->videoHeight = videoHeight; - player->onPlay([=]() -> void { - Player_onPlayPauseStop(player->state); - }); - player->onPause([=]() -> void { - Player_onPlayPauseStop(player->state); - }); - player->onStop([=]() -> void { - Player_onPlayPauseStop(player->state); - Player_onPosition(player->state); - }); - player->onComplete([=]() -> void { - Player_onComplete(player->state); - }); - player->onVolume([=](float _) -> void { - Player_onVolume(player->state); - }); - player->onRate([=](float _) -> void { - Player_onRate(player->state); - }); - player->onPosition([=](int _) -> void { - Player_onPosition(player->state); - }); - player->onOpen([=](VLC::Media _) -> void { - Player_onOpen(player->state); - }); - player->onPlaylist([=]() -> void { - Player_onOpen(player->state); - }); - /// TODO: Handle Player::onException. -} - -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 < 3 * sourceSize; index += 3) { - Media* media; - int id = atoi(source[index]); - const char* type = source[index + 1]; - const char* resource = source[index + 2]; - if (strcmp(type, "MediaType.file") == 0) - media = Media::file(id, resource, false); - else if (strcmp(type, "MediaType.network") == 0) - media = Media::network(id, resource, false); - else - media = Media::asset(id, resource, false); - medias.emplace_back(media); - } - player->open( - new Playlist(medias), - autoStart - ); - /// TODO: Improve safety. -} - -EXPORT void Player_play(int id) { - Player* player = players->get(id); - player->play(); -} - -EXPORT void Player_pause(int id) { - Player* player = players->get(id); - player->pause(); -} - -EXPORT void Player_playOrPause(int id) { - Player* player = players->get(id); - player->playOrPause(); -} - -EXPORT void Player_stop(int id) { - Player* player = players->get(id); - player->stop(); -} - -EXPORT void Player_next(int id) { - Player* player = players->get(id); - player->next(); -} - -EXPORT void Player_back(int id) { - Player* player = players->get(id); - player->back(); -} - -EXPORT void Player_jump(int id, int index) { - Player* player = players->get(id); - player->jump(index); -} - -EXPORT void Player_seek(int id, int position) { - Player* player = players->get(id); - player->seek(position); -} - -EXPORT void Player_setVolume(int id, float volume) { - Player* player = players->get(id); - player->setVolume(volume); -} - -EXPORT void Player_setRate(int id, float rate) { - Player* player = players->get(id); - player->setRate(rate); -} - -EXPORT void Player_setPlaylistMode(int id, const char* mode) { - Player* player = players->get(id); - PlaylistMode playlistMode; - if (strcmp(mode, "playlistMode.repeat") == 0) - playlistMode = PlaylistMode::repeat; - else if (strcmp(mode, "playlistMode.loop") == 0) - playlistMode = PlaylistMode::loop; - else - playlistMode = PlaylistMode::single; - player->setPlaylistMode(playlistMode); -} - -EXPORT void Player_add(int id, int mediaId, const char* type, const char* resource) { - Player* player = players->get(id); - Media* media; - if (strcmp(type, "MediaType.file") == 0) - media = Media::file(mediaId, resource, false); - else if (strcmp(type, "MediaType.network") == 0) - media = Media::network(mediaId, resource, false); - else - media = Media::asset(mediaId, resource, false); - player->add(media); -} - -EXPORT void Player_remove(int id, int index) { - Player* player = players->get(id); - player->remove(index); -} - -EXPORT void Player_insert(int id, int index, int mediaId, const char* type, const char* resource) { - Player* player = players->get(id); - Media* media; - if (strcmp(type, "MediaType.file") == 0) - media = Media::file(mediaId, resource, false); - else if (strcmp(type, "MediaType.network") == 0) - media = Media::network(mediaId, resource, false); - else - media = Media::asset(mediaId, resource, false); - player->insert(index, media); -} - -EXPORT void Player_move(int id, int initialIndex, int finalIndex) { - Player* player = players->get(id); - player->move(initialIndex, finalIndex); -} - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/windows/dart_vlc_plugin.cpp b/windows/dart_vlc_plugin.cpp index 2917b82c..39ce294a 100644 --- a/windows/dart_vlc_plugin.cpp +++ b/windows/dart_vlc_plugin.cpp @@ -3,7 +3,7 @@ * * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ diff --git a/windows/include/flutter_types.hpp b/windows/include/flutter_types.hpp index 9575bdc9..05b6b14d 100644 --- a/windows/include/flutter_types.hpp +++ b/windows/include/flutter_types.hpp @@ -3,7 +3,7 @@ * * Hitesh Kumar Saini * https://github.com/alexmercerind - * alexmercerind@gmail.com + * saini123hitesh@gmail.com; alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ From 44d337988cdd839d36e446ed70d32bcf41765f93 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Sat, 3 Jul 2021 18:41:19 +0530 Subject: [PATCH 04/27] Added Broadcast & Chromecast FFI declarations --- dartvlc/chromecast.hpp | 6 +-- ffi/lib/src/broadcast.dart | 38 ++++++++---------- ffi/lib/src/chromecast.dart | 40 +++++++------------ ffi/lib/src/internal/ffi.dart | 28 ++++++++++++- ffi/lib/src/internal/typedefs/broadcast.dart | 17 ++++++++ ffi/lib/src/internal/typedefs/chromecast.dart | 17 ++++++++ ffi/native/main.cpp | 21 ++++++++++ 7 files changed, 115 insertions(+), 52 deletions(-) create mode 100644 ffi/lib/src/internal/typedefs/chromecast.dart diff --git a/dartvlc/chromecast.hpp b/dartvlc/chromecast.hpp index 1fbe1089..87cc7ddd 100644 --- a/dartvlc/chromecast.hpp +++ b/dartvlc/chromecast.hpp @@ -29,7 +29,7 @@ class Chromecast { this->instance = VLC::Instance(0, nullptr); } - void send() { + void start() { std::stringstream sout; sout << "#chromecast{ip="<< this->ipAddress <<", demux-filter=demux_chromecast, conversion-quality=0}"; libvlc_vlm_add_broadcast( @@ -59,9 +59,9 @@ class Chromecast { class Chromecasts { public: - Chromecast* get(int id, Media* media, std::string pathFile) { + Chromecast* get(int id, Media* media, std::string ipAddress) { if (this->chromecasts.find(id) == this->chromecasts.end()) { - this->chromecasts[id] = new Chromecast(id, media, pathFile); + this->chromecasts[id] = new Chromecast(id, media, ipAddress); } return this->chromecasts[id]; } diff --git a/ffi/lib/src/broadcast.dart b/ffi/lib/src/broadcast.dart index cfe7408c..a01ca3e1 100644 --- a/ffi/lib/src/broadcast.dart +++ b/ffi/lib/src/broadcast.dart @@ -1,3 +1,5 @@ +import 'package:ffi/ffi.dart'; +import 'package:dart_vlc_ffi/src/internal/ffi.dart'; import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; /// Internally used class to avoid direct creation of the object of a [Broadcast] class. @@ -86,42 +88,36 @@ abstract class Broadcast { late BroadcastConfiguration configuration; /// Creates a new [Broadcast] instance. - static Future create( + static Broadcast create( {required int id, required Media media, - required BroadcastConfiguration configuration}) async { + required BroadcastConfiguration configuration}) { Broadcast broadcast = new _Broadcast(); broadcast.id = id; broadcast.media = media; broadcast.configuration = configuration; - await channel.invokeMethod( - 'Broadcast.create', - { - 'id': id, - 'media': media.toMap(), - 'configuration': configuration.toMap(), - }, + BroadcastFFI.create( + id, + media.mediaType.toString().toNativeUtf8(), + media.resource.toNativeUtf8(), + configuration.access.toNativeUtf8(), + configuration.mux.toNativeUtf8(), + configuration.dst.toNativeUtf8(), + configuration.vcodec.toNativeUtf8(), + configuration.vb, + configuration.acodec.toNativeUtf8(), + configuration.ab ); return broadcast; } /// Starts broadcasting the [Media]. void start() { - await channel.invokeMethod( - 'Broadcast.start', - { - 'id': id, - }, - ); + BroadcastFFI.start(this.id); } /// Disposes this instance of [Broadcast]. void dispose() { - await channel.invokeMethod( - 'Broadcast.dispose', - { - 'id': id, - }, - ); + BroadcastFFI.dispose(this.id); } } diff --git a/ffi/lib/src/chromecast.dart b/ffi/lib/src/chromecast.dart index 578f3acd..bfe8276b 100644 --- a/ffi/lib/src/chromecast.dart +++ b/ffi/lib/src/chromecast.dart @@ -1,6 +1,6 @@ -import 'package:dart_vlc/dart_vlc.dart'; -import 'package:dart_vlc/src/channel.dart'; - +import 'package:ffi/ffi.dart'; +import 'package:dart_vlc_ffi/src/internal/ffi.dart'; +import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; /// Internally used class to avoid direct creation of the object of a [Chromecast] class. class _Chromecast extends Chromecast {} @@ -10,10 +10,10 @@ class Chromecast { /// ID for this [Chromecast]. late int id; - /// Sending from [Media]. + /// Chromecast [Media]. late Media media; - /// IP address of your chromecast: `192.168.1.XXX` + /// IP address of your chromecast e.g. `192.168.1.XXX`. late String ipAddress; /// Creates a new [Chromecast] instance. @@ -25,34 +25,22 @@ class Chromecast { chromecast.id = id; chromecast.media = media; chromecast.ipAddress = ipAddress; - await channel.invokeMethod( - 'Chromecast.create', - { - 'id': id, - 'media': media.toMap(), - 'ipAddress': ipAddress, - }, + ChromecastFFI.create( + id, + media.mediaType.toString().toNativeUtf8(), + media.resource.toNativeUtf8(), + ipAddress.toNativeUtf8() ); return chromecast; } /// Start sending [Media] content to chromecast - Future send() async { - await channel.invokeMethod( - 'Chromecast.send', - { - 'id': id, - }, - ); + void start() { + ChromecastFFI.start(this.id); } /// Disposes this instance of [Chromecast]. - Future stop() async { - await channel.invokeMethod( - 'Chromecast.dispose', - { - 'id': id, - }, - ); + void dispose() { + ChromecastFFI.dispose(this.id); } } diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart index faf747c0..639decb3 100644 --- a/ffi/lib/src/internal/ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -1,11 +1,13 @@ import 'dart:ffi'; import 'dart:isolate'; -import 'package:dart_vlc_ffi/src/internal/typedefs/media.dart'; import 'package:ffi/ffi.dart'; +import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/player.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/callback.dart'; -import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/media.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/broadcast.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/chromecast.dart'; extension NativeTypes on List { @@ -62,7 +64,29 @@ abstract class PlayerFFI { abstract class MediaFFI { + static final MediaParseDart parse = dynamicLibrary.lookup>('Media_parse').asFunction(); + +} + + +abstract class BroadcastFFI { + + static final BroadcastCreateDart create = dynamicLibrary.lookup>('Broadcast_create').asFunction(); + + static final BroadcastStartDart start = dynamicLibrary.lookup>('Broadcast_start').asFunction(); + + static final BroadcastDisposeDart dispose = dynamicLibrary.lookup>('Broadcast_dispose').asFunction(); +} + + +abstract class ChromecastFFI { + + static final ChromecastCreateDart create = dynamicLibrary.lookup>('Chromecast_create').asFunction(); + + static final ChromecastStartDart start = dynamicLibrary.lookup>('Chromecast_start').asFunction(); + + static final ChromecastDisposeDart dispose = dynamicLibrary.lookup>('Chromecast_dispose').asFunction(); } diff --git a/ffi/lib/src/internal/typedefs/broadcast.dart b/ffi/lib/src/internal/typedefs/broadcast.dart index e69de29b..4edfcde6 100644 --- a/ffi/lib/src/internal/typedefs/broadcast.dart +++ b/ffi/lib/src/internal/typedefs/broadcast.dart @@ -0,0 +1,17 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +/// Following typedef is used for: +/// Broadcast::create +typedef BroadcastCreateCXX = Void Function(Int32 id, Pointer type, Pointer resource, Pointer access, Pointer mux, Pointer dst, Pointer vcodec, Int32 vb, Pointer acodec, Int32 ab); +typedef BroadcastCreateDart = void Function(int id, Pointer type, Pointer resource, Pointer access, Pointer mux, Pointer dst, Pointer vcodec, int vb, Pointer acodec, int ab); + +/// Following typedef is used for: +/// Broadcast::start +typedef BroadcastStartCXX = Void Function(Int32 id); +typedef BroadcastStartDart = void Function(int id); + +/// Following typedef is used for: +/// Broadcast::dispose +typedef BroadcastDisposeCXX = Void Function(Int32 id); +typedef BroadcastDisposeDart = void Function(int id); diff --git a/ffi/lib/src/internal/typedefs/chromecast.dart b/ffi/lib/src/internal/typedefs/chromecast.dart new file mode 100644 index 00000000..81cf86bc --- /dev/null +++ b/ffi/lib/src/internal/typedefs/chromecast.dart @@ -0,0 +1,17 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +/// Following typedef is used for: +/// Chromecast::create +typedef ChromecastCreateCXX = Void Function(Int32 id, Pointer type, Pointer resource, Pointer ipAddress); +typedef ChromecastCreateDart = void Function(int id, Pointer type, Pointer resource, Pointer ipAddress); + +/// Following typedef is used for: +/// Chromecast::start +typedef ChromecastStartCXX = Void Function(Int32 id); +typedef ChromecastStartDart = void Function(int id); + +/// Following typedef is used for: +/// Chromecast::dispose +typedef ChromecastDisposeCXX = Void Function(Int32 id); +typedef ChromecastDisposeDart = void Function(int id); diff --git a/ffi/native/main.cpp b/ffi/native/main.cpp index ad2bc3a0..277e34c6 100644 --- a/ffi/native/main.cpp +++ b/ffi/native/main.cpp @@ -235,6 +235,27 @@ EXPORT void Broadcast_dispose(int id) { broadcast->dispose(); } +EXPORT void Chromecast_create(int id, const char* type, const char* resource, const char* ipAddress) { + Media* media; + if (strcmp(type, "MediaType.file") == 0) + media = Media::file(0, resource, false); + else if (strcmp(type, "MediaType.network") == 0) + media = Media::network(0, resource, false); + else if (strcmp(type, "MediaType.directShow") == 0) + media = Media::directShow(0, resource); + Chromecast* chromecast = chromecasts->get(id, media, ipAddress); +} + +EXPORT void Chromecast_start(int id) { + Chromecast* chromecast = chromecasts->get(id, nullptr, nullptr); + chromecast->start(); +} + +EXPORT void Chromecast_dispose(int id) { + Chromecast* chromecast = chromecasts->get(id, nullptr, nullptr); + chromecast->dispose(); +} + #endif #ifdef __cplusplus From db518f1340bef775fc99fc5f70a5e5967c1eb7b6 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Sun, 4 Jul 2021 16:47:28 +0530 Subject: [PATCH 05/27] Added Record & Equalizer mappings for shared library --- ffi/native/main.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/ffi/native/main.cpp b/ffi/native/main.cpp index 277e34c6..d999a522 100644 --- a/ffi/native/main.cpp +++ b/ffi/native/main.cpp @@ -142,6 +142,12 @@ EXPORT void Player_setUserAgent(int id, const char* userAgent) { player->setUserAgent(userAgent); } +EXPORT void Player_setEqualizer(int id, int equalizerId) { + Player* player = players->get(id); + Equalizer* equalizer = equalizers->get(equalizerId); + player->setEqualizer(equalizer); +} + EXPORT void Player_setPlaylistMode(int id, const char* mode) { Player* player = players->get(id); PlaylistMode playlistMode; @@ -256,6 +262,75 @@ EXPORT void Chromecast_dispose(int id) { chromecast->dispose(); } +EXPORT void Record_create(int id, const char* savingFile, const char* type, const char* resource) { + Media* media; + if (strcmp(type, "MediaType.file") == 0) + media = Media::file(0, resource, false); + else if (strcmp(type, "MediaType.network") == 0) + media = Media::network(0, resource, false); + else if (strcmp(type, "MediaType.directShow") == 0) + media = Media::directShow(0, resource); + records->get(id, media, savingFile); +} + +EXPORT void Record_start(int id) { + records->get(id, nullptr, "")->start(); +} + +EXPORT void Record_dispose(int id) { + records->get(id, nullptr, "")->dispose(); +} + +EXPORT const char** Devices_all() { + devices->refresh(); + char** _devices = new char*[(devices->all.size() * 2) + 1]; + _devices[0] = std::to_string(devices->all.size()).data(); + for (int i = 1; i < devices->all.size(); i += 2) { + _devices[i] = devices->all[i]->id.data(); + _devices[i + 1] = devices->all[i + 1]->name.data(); + } + devices->all.size(); +} + + +EXPORT const char** Equalizer_createEmpty() { + int id = equalizers->createEmpty(); + Equalizer* equalizer = equalizers->get(id); + char** _equalizer = new char*[2 * equalizer->bandAmps.size() + 2]; + _equalizer[0] = std::to_string(id).data(); + _equalizer[1] = std::to_string(equalizer->preAmp).data(); + int index = 0; + for (const auto&[band, amp]: equalizer->bandAmps) { + _equalizer[index + 2] = std::to_string(band).data(); + _equalizer[index + 3] = std::to_string(amp).data(); + } + return _equalizer; +} + +EXPORT const char** Equalizer_createMode(int mode) { + int id = equalizers->createMode( + static_cast(mode) + ); + Equalizer* equalizer = equalizers->get(id); + char** _equalizer = new char*[2 * equalizer->bandAmps.size() + 2]; + _equalizer[0] = std::to_string(id).data(); + _equalizer[1] = std::to_string(equalizer->preAmp).data(); + int index = 0; + for (const auto&[band, amp]: equalizer->bandAmps) { + _equalizer[index + 2] = std::to_string(band).data(); + _equalizer[index + 3] = std::to_string(amp).data(); + } + return _equalizer; +} + +EXPORT void Equalizer_setBandAmp(int id, float band, float amp) { + equalizers->get(id)->setBandAmp(band, amp); +} + +EXPORT void Equalizer_setPreAmp(int id, float amp) { + equalizers->get(id)->setPreAmp(amp); +} + #endif #ifdef __cplusplus From 88b58f86fe6de422779d000081a7dd8c7ae419ff Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Sun, 4 Jul 2021 17:12:40 +0530 Subject: [PATCH 06/27] Updated PlayerFFI class --- ffi/lib/src/internal/ffi.dart | 11 +++++++++++ ffi/lib/src/player.dart | 33 +++++++++++---------------------- ffi/native/main.cpp | 6 ++++++ 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart index 639decb3..2c8c08d3 100644 --- a/ffi/lib/src/internal/ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -1,5 +1,6 @@ import 'dart:ffi'; import 'dart:isolate'; +import 'package:dart_vlc_ffi/src/internal/typedefs/devices.dart'; import 'package:ffi/ffi.dart'; import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; @@ -51,6 +52,10 @@ abstract class PlayerFFI { static final PlayerSetUserAgentDart setUserAgent = dynamicLibrary.lookup>('Player_setUserAgent').asFunction(); + static final PlayerSetEqualizerDart setEqualizer = dynamicLibrary.lookup>('Player_setEqualizer').asFunction(); + + static final PlayerSetDeviceDart setDevice = dynamicLibrary.lookup>('Player_setDevice').asFunction(); + static final PlayerSetPlaylistModeDart setPlaylistMode = dynamicLibrary.lookup>('Player_setPlaylistMode').asFunction(); static final PlayerAddDart add = dynamicLibrary.lookup>('Player_add').asFunction(); @@ -90,6 +95,12 @@ abstract class ChromecastFFI { } +abstract class DevicesFFI { + + static final DevicesAllDart all = dynamicLibrary.lookup>('Devices_all').asFunction(); +} + + abstract class DartVLC { static void initialize(String dynamicLibraryPath) { diff --git a/ffi/lib/src/player.dart b/ffi/lib/src/player.dart index 9277f3df..87f7f704 100644 --- a/ffi/lib/src/player.dart +++ b/ffi/lib/src/player.dart @@ -93,7 +93,6 @@ class Player { this.playbackStream = this.playbackController.stream; this.generalController = StreamController.broadcast(); this.generalStream = this.generalController.stream; - players[id] = this; PlayerFFI.create( this.id, this.videoWidth, @@ -264,34 +263,24 @@ class Player { /// Device will be switched once a new [Media] is played. /// Future setDevice(Device device) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.setDevice', - { - 'id': this.id, - 'device': device.toMap(), - }, + PlayerFFI.setDevice( + this.id, + device.id.toNativeUtf8(), + device.name.toNativeUtf8() ); } /// Sets [Equalizer] for the [Player]. - Future setEqualizer(Equalizer equalizer) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.setEqualizer', - { - 'id': this.id, - 'equalizerId': equalizer.id, - }, - ); + void setEqualizer(Equalizer equalizer) { + PlayerFFI.setEqualizer(this.id, equalizer.id); } /// Destroys the instance of [Player] & closes all [StreamController]s in it. - Future dispose() async { - await this.currentController.close(); - await this.positionController.close(); - await this.playbackController.close(); - await this.generalController.close(); + void dispose() { + this.currentController.close(); + this.positionController.close(); + this.playbackController.close(); + this.generalController.close(); } /// Internally used [StreamController]s, diff --git a/ffi/native/main.cpp b/ffi/native/main.cpp index d999a522..66e309aa 100644 --- a/ffi/native/main.cpp +++ b/ffi/native/main.cpp @@ -142,6 +142,12 @@ EXPORT void Player_setUserAgent(int id, const char* userAgent) { player->setUserAgent(userAgent); } +EXPORT void Player_setDevice(int id, const char* deviceId, const char* deviceName) { + Player* player = players->get(id); + Device device(deviceId, deviceName); + player->setDevice(&device); +} + EXPORT void Player_setEqualizer(int id, int equalizerId) { Player* player = players->get(id); Equalizer* equalizer = equalizers->get(equalizerId); From cc81c5d6990bd970682925f7d183377f30bd8beb Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Sun, 4 Jul 2021 17:30:08 +0530 Subject: [PATCH 07/27] Updated FFI bindings --- ffi/lib/src/internal/ffi.dart | 26 ++++++++++++++++++-- ffi/lib/src/internal/typedefs/devices.dart | 7 ++++++ ffi/lib/src/internal/typedefs/equalizer.dart | 22 +++++++++++++++++ ffi/lib/src/internal/typedefs/player.dart | 12 +++++++-- ffi/lib/src/internal/typedefs/record.dart | 17 +++++++++++++ 5 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 ffi/lib/src/internal/typedefs/devices.dart create mode 100644 ffi/lib/src/internal/typedefs/equalizer.dart create mode 100644 ffi/lib/src/internal/typedefs/record.dart diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart index 2c8c08d3..2470ffc9 100644 --- a/ffi/lib/src/internal/ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -1,12 +1,13 @@ import 'dart:ffi'; import 'dart:isolate'; -import 'package:dart_vlc_ffi/src/internal/typedefs/devices.dart'; import 'package:ffi/ffi.dart'; - import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/player.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/callback.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/media.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/devices.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/equalizer.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/record.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/broadcast.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/chromecast.dart'; @@ -95,11 +96,32 @@ abstract class ChromecastFFI { } +abstract class RecordFFI { + + static final RecordCreateDart create = dynamicLibrary.lookup>('Record_create').asFunction(); + + static final RecordStartDart start = dynamicLibrary.lookup>('Record_start').asFunction(); + + static final RecordDisposeDart dispose = dynamicLibrary.lookup>('Record_dispose').asFunction(); +} + + abstract class DevicesFFI { static final DevicesAllDart all = dynamicLibrary.lookup>('Devices_all').asFunction(); } +abstract class EqualizerFFI { + + static final EqualizerCreateEmptyDart createEmpty = dynamicLibrary.lookup>('Equalizer_createEmpty').asFunction(); + + static final EqualizerCreateModeDart createMode = dynamicLibrary.lookup>('Equalizer_createMode').asFunction(); + + static final EqualizerSetBandAmpDart setBandAmp = dynamicLibrary.lookup>('Equalizer_setBandAmp').asFunction(); + + static final EqualizerSetPreAmpDart setPreAmp = dynamicLibrary.lookup>('Equalizer_setPreAmp').asFunction(); +} + abstract class DartVLC { diff --git a/ffi/lib/src/internal/typedefs/devices.dart b/ffi/lib/src/internal/typedefs/devices.dart new file mode 100644 index 00000000..6029c1b3 --- /dev/null +++ b/ffi/lib/src/internal/typedefs/devices.dart @@ -0,0 +1,7 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +/// Following typedef is used for: +/// Devices::all +typedef DevicesAllCXX = Pointer> Function(); +typedef DevicesAllDart = Pointer> Function(); diff --git a/ffi/lib/src/internal/typedefs/equalizer.dart b/ffi/lib/src/internal/typedefs/equalizer.dart new file mode 100644 index 00000000..10240d9e --- /dev/null +++ b/ffi/lib/src/internal/typedefs/equalizer.dart @@ -0,0 +1,22 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +/// Following typedef is used for: +/// Equalizer::createEmpty +typedef EqualizerCreateEmptyCXX = Pointer> Function(); +typedef EqualizerCreateEmptyDart = Pointer> Function(); + +/// Following typedef is used for: +/// Equalizer::createMode +typedef EqualizerCreateModeCXX = Pointer> Function(); +typedef EqualizerCreateModeDart = Pointer> Function(); + +/// Following typedef is used for: +/// Equalizer::setBandAmp +typedef EqualizerSetBandAmpCXX = Void Function(Int32 id, Float band, Float amp); +typedef EqualizerSetBandAmpDart = void Function(int id, double band, double amp); + +/// Following typedef is used for: +/// Equalizer::setPreAmp +typedef EqualizerSetPreAmpCXX = Void Function(Int32 id, Float amp); +typedef EqualizerSetPreAmpDart = void Function(int id, double amp); diff --git a/ffi/lib/src/internal/typedefs/player.dart b/ffi/lib/src/internal/typedefs/player.dart index 2b86567a..237a68bf 100644 --- a/ffi/lib/src/internal/typedefs/player.dart +++ b/ffi/lib/src/internal/typedefs/player.dart @@ -47,9 +47,17 @@ typedef PlayerSetUserAgentCXX = Void Function(Int32 id, Pointer userAgent) typedef PlayerSetUserAgentDart = void Function(int id, Pointer userAgent); /// Following typedef is used for: -/// Player::setPlaylistMode +/// Player::setEqualizer +typedef PlayerSetEqualizerCXX = Void Function(Int32 id, Int32 equalizerId); +typedef PlayerSetEqualizerDart = void Function(int id, int equalizerId); + /// Following typedef is used for: -/// Player::setRate +/// Player::setDevice +typedef PlayerSetDeviceCXX = Void Function(Int32 id, Pointer deviceId, Pointer deviceName); +typedef PlayerSetDeviceDart = void Function(int id, Pointer deviceId, Pointer deviceName); + +/// Following typedef is used for: +/// Player::setPlaylistMode typedef PlayerSetPlaylistModeCXX = Void Function(Int32 id, Pointer mode); typedef PlayerSetPlaylistModeDart = void Function(int id, Pointer mode); diff --git a/ffi/lib/src/internal/typedefs/record.dart b/ffi/lib/src/internal/typedefs/record.dart new file mode 100644 index 00000000..6d440aae --- /dev/null +++ b/ffi/lib/src/internal/typedefs/record.dart @@ -0,0 +1,17 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +/// Following typedef is used for: +/// Record::create +typedef RecordCreateCXX = Void Function(Int32 id, Pointer savingFile, Pointer type, Pointer resource); +typedef RecordCreateDart = void Function(int id, Pointer savingFile, Pointer type, Pointer resource); + +/// Following typedef is used for: +/// Record::start +typedef RecordStartCXX = Void Function(Int32 id); +typedef RecordStartDart = void Function(int id); + +/// Following typedef is used for: +/// Record::dispose +typedef RecordDisposeCXX = Void Function(Int32 id); +typedef RecordDisposeDart = void Function(int id); From 68760ab84ee739297eac0f099665feb2f9690d96 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Sun, 4 Jul 2021 18:45:54 +0530 Subject: [PATCH 08/27] Final FFI bindings --- ffi/lib/dart_vlc.dart | 1 - ffi/lib/src/device.dart | 40 +++++++++------------- ffi/lib/src/equalizer.dart | 54 +++++++++++------------------- ffi/lib/src/internal/ffi.dart | 2 +- ffi/lib/src/mediaSource/media.dart | 24 +++++-------- ffi/lib/src/player.dart | 16 +++++---- ffi/lib/src/record.dart | 36 ++++++-------------- 7 files changed, 64 insertions(+), 109 deletions(-) diff --git a/ffi/lib/dart_vlc.dart b/ffi/lib/dart_vlc.dart index 89f8d580..e2b8e7c4 100644 --- a/ffi/lib/dart_vlc.dart +++ b/ffi/lib/dart_vlc.dart @@ -11,7 +11,6 @@ export 'package:dart_vlc_ffi/src/player.dart'; export 'package:dart_vlc_ffi/src/equalizer.dart'; export 'package:dart_vlc_ffi/src/broadcast.dart'; -export 'package:dart_vlc_ffi/src/record.dart'; export 'package:dart_vlc_ffi/src/chromecast.dart'; export 'package:dart_vlc_ffi/src/device.dart'; export 'package:dart_vlc_ffi/src/playerState/playerState.dart'; diff --git a/ffi/lib/src/device.dart b/ffi/lib/src/device.dart index 17e8385d..8e7e4e70 100644 --- a/ffi/lib/src/device.dart +++ b/ffi/lib/src/device.dart @@ -1,4 +1,7 @@ -import 'dart:io'; +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:dart_vlc_ffi/src/internal/ffi.dart'; + /// Represents a playback [Device] for the [Player]. class Device { @@ -9,34 +12,23 @@ class Device { String name; Device(this.id, this.name); - - /// Internally used method to easily transform data for sending through Platform channel. - static Device fromMap(dynamic map) => Device( - Platform.isWindows - ? (map['id'] != '' - ? '{0.0.0.00000000}.' + map['id'].toLowerCase() - : '') - : map['id'], - map['name'], - ); - - /// Internally used method to easily transform data for sending through Platform channel. - Map toMap() => { - 'id': this.id, - 'name': this.name, - }; } /// [Devices.all] getter is used to get [List] of all available [Device] for playback in the [Player]. class Devices { /// Gets [List] of all available playback [Device]. - static Future> get all async { - dynamic devices = await channel.invokeMethod('Devices.all', {}); - return devices - .map( - (dynamic device) => Device.fromMap(device), + static List get all { + List devices = []; + Pointer> devicesPtr = DevicesFFI.all(); + int count = int.parse(devicesPtr.elementAt(0).value.toDartString()); + for (int i = 1; i < count; i += 2) { + devices.add( + Device( + devicesPtr.elementAt(i).value.toDartString(), + devicesPtr.elementAt(i + 1).value.toDartString() ) - .toList() - .cast(); + ); + } + return devices; } } diff --git a/ffi/lib/src/equalizer.dart b/ffi/lib/src/equalizer.dart index a0700636..c24d0471 100644 --- a/ffi/lib/src/equalizer.dart +++ b/ffi/lib/src/equalizer.dart @@ -1,3 +1,6 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:dart_vlc_ffi/src/internal/ffi.dart'; import 'package:dart_vlc_ffi/src/enums/equalizerMode.dart'; @@ -28,31 +31,25 @@ class Equalizer { EqualizerMode? mode; /// Creates a default [Equalizer] instance with all values set to `0.0`. - static Future createEmpty() async { + static Equalizer createEmpty() { Equalizer equalizer = new _Equalizer(); - dynamic _equalizer = await channel.invokeMethod( - 'Equalizer.createEmpty', - {}, - ); - equalizer.id = _equalizer['id']; - equalizer.preAmp = _equalizer['preAmp']; - equalizer.bandAmps = Map.from(_equalizer['bandAmps']); + Pointer> _equalizer = EqualizerFFI.createEmpty(); + equalizer.id = int.parse(_equalizer.elementAt(0).value.toDartString()); + equalizer.preAmp = double.parse(_equalizer.elementAt(1).value.toDartString()); + /// TODO: Fix Equalizer. + /// equalizer.bandAmps = Map.from(_equalizer['bandAmps']); return equalizer; } /// Creates an [Equalizer] instance with any preset from [EqualizerMode]. - static Future createMode(EqualizerMode mode) async { + static Equalizer createMode(EqualizerMode mode) { Equalizer equalizer = new _Equalizer(); - dynamic _equalizer = await channel.invokeMethod( - 'Equalizer.createMode', - { - 'mode': mode.index, - }, - ); - equalizer.id = _equalizer['id']; + Pointer> _equalizer = EqualizerFFI.createEmpty(); + equalizer.id = int.parse(_equalizer.elementAt(0).value.toDartString()); + equalizer.preAmp = double.parse(_equalizer.elementAt(1).value.toDartString()); equalizer.mode = mode; - equalizer.preAmp = _equalizer['preAmp']; - equalizer.bandAmps = Map.from(_equalizer['bandAmps']); + /// TODO: Fix Equalizer. + // equalizer.bandAmps = Map.from(_equalizer['bandAmps']); return equalizer; } @@ -61,14 +58,8 @@ class Equalizer { /// Constraints: /// `-20.0 < amp < 20.0` /// - Future setPreAmp(double amp) async { - await channel.invokeMethod( - 'Equalizer.setPreAmp', - { - 'id': this.id, - 'preAmp': amp, - }, - ); + void setPreAmp(double amp) { + EqualizerFFI.setPreAmp(this.id, amp); this.preAmp = amp; } @@ -78,15 +69,8 @@ class Equalizer { /// Constraints: /// `-20.0 < amp < 20.0` /// - Future setBandAmp(double band, double amp) async { - await channel.invokeMethod( - 'Equalizer.setBandAmp', - { - 'id': this.id, - 'band': band, - 'amp': amp, - }, - ); + void setBandAmp(double band, double amp) { + EqualizerFFI.setBandAmp(this.id, band, amp); this.bandAmps[band] = amp; } } diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart index 2470ffc9..d8c014e6 100644 --- a/ffi/lib/src/internal/ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -123,7 +123,7 @@ abstract class EqualizerFFI { } -abstract class DartVLC { +class DartVLC { static void initialize(String dynamicLibraryPath) { if (!isInitialized) { diff --git a/ffi/lib/src/mediaSource/media.dart b/ffi/lib/src/mediaSource/media.dart index 19d0ba3a..db04a413 100644 --- a/ffi/lib/src/mediaSource/media.dart +++ b/ffi/lib/src/mediaSource/media.dart @@ -31,32 +31,28 @@ class _Media extends Media {} abstract class Media extends MediaSource { MediaSourceType mediaSourceType = MediaSourceType.media; Map metas = {}; - Map extras = {}; late MediaType mediaType; late String resource; /// Makes [Media] object from a [File]. - static Future file(File file, + static Media file(File file, {bool parse: false, Map? extras, - Duration timeout: const Duration(seconds: 10)}) async { + Duration timeout: const Duration(seconds: 10)}) { Media media = new _Media(); media.mediaType = MediaType.file; media.resource = file.path; if (parse) { - await media.parse(timeout); - } - if (extras != null) { - media.extras = extras; + media.parse(timeout); } return media; } /// Makes [Media] object from url. - static Future network(dynamic url, + static Media network(dynamic url, {bool parse: false, Map? extras, - Duration timeout: const Duration(seconds: 10)}) async { + Duration timeout: const Duration(seconds: 10)}) { Media media = new _Media(); media.mediaType = MediaType.network; if (url is Uri) @@ -64,17 +60,13 @@ abstract class Media extends MediaSource { else media.resource = url; if (parse) { - await media.parse(timeout); - } - if (extras != null) { - media.extras = extras; + media.parse(timeout); } return media; } /// Makes [Media] object from direct show. - static Future directShow( - {String vdev = '', String adev = '', required int liveCaching}) async { + static Media directShow({String vdev = '', String adev = '', required int liveCaching}) { Media media = new _Media(); media.mediaType = MediaType.directShow; media.resource = @@ -82,7 +74,7 @@ abstract class Media extends MediaSource { return media; } - Future parse(Duration timeout) async { + void parse(Duration timeout) { Pointer> metas = MediaFFI.parse( this.mediaType.toString().toNativeUtf8(), this.resource.toNativeUtf8(), diff --git a/ffi/lib/src/player.dart b/ffi/lib/src/player.dart index 87f7f704..e59e8975 100644 --- a/ffi/lib/src/player.dart +++ b/ffi/lib/src/player.dart @@ -78,10 +78,7 @@ class Player { /// Player player = new Player(id: 0); /// ``` /// - Player({required this.id, int videoWidth: 0, int videoHeight: 0, Device? device, List? commandlineArguments}) { - if (device is Device) { - this.setDevice(device); - } + Player({required this.id, int videoWidth: 0, int videoHeight: 0, List? commandlineArguments}) { if (commandlineArguments != null) this.commandlineArguments = commandlineArguments; this.videoWidth = videoWidth; this.videoHeight = videoHeight; @@ -142,16 +139,21 @@ class Player { PlayerFFI.open( this.id, autoStart ? 1: 0, - [ source.resource ].toNativeUtf8Array(), + [ source.mediaType.toString(), source.resource ].toNativeUtf8Array(), 1 ); } if (source is Playlist) { + List medias = []; + source.medias.forEach((media) { + medias.add(media.mediaType.toString()); + medias.add(media.resource); + }); PlayerFFI.open( this.id, autoStart ? 1: 0, - source.medias.map((media) => media.toString()).toList().toNativeUtf8Array(), - source.medias.length + medias.toNativeUtf8Array(), + source.medias.length * 2 ); } } diff --git a/ffi/lib/src/record.dart b/ffi/lib/src/record.dart index 36707107..ba449efc 100644 --- a/ffi/lib/src/record.dart +++ b/ffi/lib/src/record.dart @@ -1,7 +1,6 @@ import 'dart:io'; - +import 'package:ffi/ffi.dart'; import 'package:dart_vlc_ffi/dart_vlc.dart'; -import 'package:dart_vlc_ffi/src/channel.dart'; /// Internally used class to avoid direct creation of the object of a [Record] class. @@ -19,40 +18,27 @@ class Record { late File savingFile; /// Creates a new [Record] instance. - static Future create( - {required int id, required Media media, required File savingFile}) async { + static Record create({required int id, required Media media, required File savingFile}) { Record record = new _Record(); record.id = id; record.media = media; record.savingFile = savingFile; - await channel.invokeMethod( - 'Record.create', - { - 'id': id, - 'media': media.toMap(), - 'savingFile': savingFile.path, - }, + RecordFFI.create( + record.id, + savingFile.path.toNativeUtf8(), + media.mediaType.toString().toNativeUtf8(), + media.resource.toNativeUtf8() ); return record; } /// Starts recording the [Media]. - Future start() async { - await channel.invokeMethod( - 'Record.start', - { - 'id': id, - }, - ); + void start() { + RecordFFI.start(this.id); } /// Disposes this instance of [Record]. - Future stop() async { - await channel.invokeMethod( - 'Record.dispose', - { - 'id': id, - }, - ); + void dispose() { + RecordFFI.dispose(this.id); } } From b0897c7dbba908cadea1b640e5e337fe2b2ae580 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 11:25:18 +0530 Subject: [PATCH 09/27] Fixed Player::open & Equalizer C mappings --- ffi/lib/dart_vlc.dart | 2 +- ffi/lib/src/equalizer.dart | 21 ++++++-- ffi/lib/src/internal/ffi.dart | 16 ------ ffi/lib/src/internal/initializer.dart | 19 +++++++ ffi/lib/src/internal/typedefs/equalizer.dart | 4 +- ffi/lib/src/record.dart | 1 + ffi/main.dart | 25 +++++----- ffi/native/eventmanager.hpp | 1 - ffi/native/main.cpp | 52 +++++++++++--------- 9 files changed, 83 insertions(+), 58 deletions(-) create mode 100644 ffi/lib/src/internal/initializer.dart diff --git a/ffi/lib/dart_vlc.dart b/ffi/lib/dart_vlc.dart index e2b8e7c4..f82a1b11 100644 --- a/ffi/lib/dart_vlc.dart +++ b/ffi/lib/dart_vlc.dart @@ -21,4 +21,4 @@ export 'package:dart_vlc_ffi/src/enums/equalizerMode.dart'; export 'package:dart_vlc_ffi/src/enums/mediaSourceType.dart'; export 'package:dart_vlc_ffi/src/enums/mediaType.dart'; export 'package:dart_vlc_ffi/src/enums/playlistMode.dart'; -export 'package:dart_vlc_ffi/src/internal/ffi.dart'; \ No newline at end of file +export 'package:dart_vlc_ffi/src/internal/initializer.dart'; diff --git a/ffi/lib/src/equalizer.dart b/ffi/lib/src/equalizer.dart index c24d0471..78505199 100644 --- a/ffi/lib/src/equalizer.dart +++ b/ffi/lib/src/equalizer.dart @@ -36,20 +36,31 @@ class Equalizer { Pointer> _equalizer = EqualizerFFI.createEmpty(); equalizer.id = int.parse(_equalizer.elementAt(0).value.toDartString()); equalizer.preAmp = double.parse(_equalizer.elementAt(1).value.toDartString()); - /// TODO: Fix Equalizer. - /// equalizer.bandAmps = Map.from(_equalizer['bandAmps']); + equalizer.mode = null; + equalizer.bandAmps = { + double.parse(_equalizer.elementAt(2).value.toDartString()): double.parse(_equalizer.elementAt(3).value.toDartString()), + double.parse(_equalizer.elementAt(4).value.toDartString()): double.parse(_equalizer.elementAt(5).value.toDartString()), + double.parse(_equalizer.elementAt(6).value.toDartString()): double.parse(_equalizer.elementAt(7).value.toDartString()), + double.parse(_equalizer.elementAt(8).value.toDartString()): double.parse(_equalizer.elementAt(9).value.toDartString()), + double.parse(_equalizer.elementAt(10).value.toDartString()): double.parse(_equalizer.elementAt(11).value.toDartString()), + }; return equalizer; } /// Creates an [Equalizer] instance with any preset from [EqualizerMode]. static Equalizer createMode(EqualizerMode mode) { Equalizer equalizer = new _Equalizer(); - Pointer> _equalizer = EqualizerFFI.createEmpty(); + Pointer> _equalizer = EqualizerFFI.createMode(mode.index); equalizer.id = int.parse(_equalizer.elementAt(0).value.toDartString()); equalizer.preAmp = double.parse(_equalizer.elementAt(1).value.toDartString()); equalizer.mode = mode; - /// TODO: Fix Equalizer. - // equalizer.bandAmps = Map.from(_equalizer['bandAmps']); + equalizer.bandAmps = { + double.parse(_equalizer.elementAt(2).value.toDartString()): double.parse(_equalizer.elementAt(3).value.toDartString()), + double.parse(_equalizer.elementAt(4).value.toDartString()): double.parse(_equalizer.elementAt(5).value.toDartString()), + double.parse(_equalizer.elementAt(6).value.toDartString()): double.parse(_equalizer.elementAt(7).value.toDartString()), + double.parse(_equalizer.elementAt(8).value.toDartString()): double.parse(_equalizer.elementAt(9).value.toDartString()), + double.parse(_equalizer.elementAt(10).value.toDartString()): double.parse(_equalizer.elementAt(11).value.toDartString()), + }; return equalizer; } diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart index d8c014e6..e6a09a74 100644 --- a/ffi/lib/src/internal/ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -3,7 +3,6 @@ import 'dart:isolate'; import 'package:ffi/ffi.dart'; import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/player.dart'; -import 'package:dart_vlc_ffi/src/internal/typedefs/callback.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/media.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/devices.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/equalizer.dart'; @@ -121,18 +120,3 @@ abstract class EqualizerFFI { static final EqualizerSetPreAmpDart setPreAmp = dynamicLibrary.lookup>('Equalizer_setPreAmp').asFunction(); } - - -class DartVLC { - - static void initialize(String dynamicLibraryPath) { - if (!isInitialized) { - dynamicLibrary = DynamicLibrary.open(dynamicLibraryPath); - RegisterPostCObjectDart registerPostCObject = dynamicLibrary.lookup>('RegisterDart_PostCObject').asFunction(); - RegisterCallbackPortDart registerCallbackPort = dynamicLibrary.lookup>('RegisterDart_CallbackPort').asFunction(); - registerPostCObject(NativeApi.postCObject); - registerCallbackPort(receiver.sendPort.nativePort); - isInitialized = true; - } - } -} diff --git a/ffi/lib/src/internal/initializer.dart b/ffi/lib/src/internal/initializer.dart new file mode 100644 index 00000000..7ce8c805 --- /dev/null +++ b/ffi/lib/src/internal/initializer.dart @@ -0,0 +1,19 @@ +import 'dart:ffi'; +import 'package:dart_vlc_ffi/src/internal/ffi.dart'; +import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; +import 'package:dart_vlc_ffi/src/internal/typedefs/callback.dart'; + + +class DartVLC { + + static void initialize(String dynamicLibraryPath) { + if (!isInitialized) { + dynamicLibrary = DynamicLibrary.open(dynamicLibraryPath); + RegisterPostCObjectDart registerPostCObject = dynamicLibrary.lookup>('RegisterDart_PostCObject').asFunction(); + RegisterCallbackPortDart registerCallbackPort = dynamicLibrary.lookup>('RegisterDart_CallbackPort').asFunction(); + registerPostCObject(NativeApi.postCObject); + registerCallbackPort(receiver.sendPort.nativePort); + isInitialized = true; + } + } +} diff --git a/ffi/lib/src/internal/typedefs/equalizer.dart b/ffi/lib/src/internal/typedefs/equalizer.dart index 10240d9e..41cbc1f8 100644 --- a/ffi/lib/src/internal/typedefs/equalizer.dart +++ b/ffi/lib/src/internal/typedefs/equalizer.dart @@ -8,8 +8,8 @@ typedef EqualizerCreateEmptyDart = Pointer> Function(); /// Following typedef is used for: /// Equalizer::createMode -typedef EqualizerCreateModeCXX = Pointer> Function(); -typedef EqualizerCreateModeDart = Pointer> Function(); +typedef EqualizerCreateModeCXX = Pointer> Function(Int32 mode); +typedef EqualizerCreateModeDart = Pointer> Function(int mode); /// Following typedef is used for: /// Equalizer::setBandAmp diff --git a/ffi/lib/src/record.dart b/ffi/lib/src/record.dart index ba449efc..032db28f 100644 --- a/ffi/lib/src/record.dart +++ b/ffi/lib/src/record.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:ffi/ffi.dart'; import 'package:dart_vlc_ffi/dart_vlc.dart'; +import 'package:dart_vlc_ffi/src/internal/ffi.dart'; /// Internally used class to avoid direct creation of the object of a [Record] class. diff --git a/ffi/main.dart b/ffi/main.dart index 2b724236..8c0eb976 100644 --- a/ffi/main.dart +++ b/ffi/main.dart @@ -1,13 +1,16 @@ -import 'package:dart_vlc_ffi/source/internal/dart_vlc.dart'; - -Future main() async { - receiver.listen((event) => print(event)); - CallbackFFI.initialize(); - PlayerFFI.create(0, 320, 240, 0, [].toNativeUtf8Array()); - PlayerFFI.open( - 0, 1, - ['0', 'MediaType.file', '/home/alexmercerind/video.mp4'].toNativeUtf8Array(), - 1 +import 'dart:io'; + +import 'package:dart_vlc_ffi/dart_vlc.dart'; + + +String get dynamicLibraryPath { + String directory = Platform.script.path.split('/').sublist(0, Platform.script.path.split('/').length - 1).join('/'); + return directory + '/' + 'dart_vlc.so'; +} + +Future main() async { + DartVLC.initialize(dynamicLibraryPath); + Equalizer.createMode( + EqualizerMode.live ); - await Future.delayed(Duration(seconds: 5)); } diff --git a/ffi/native/eventmanager.hpp b/ffi/native/eventmanager.hpp index 2206907a..7426c042 100644 --- a/ffi/native/eventmanager.hpp +++ b/ffi/native/eventmanager.hpp @@ -110,7 +110,6 @@ void Player_onOpen(PlayerState* state) { event.emplace_back(std::to_string(state->index)); event.emplace_back(std::to_string(state->isPlaylist)); for (Media* media: state->medias->medias) { - event.emplace_back(std::to_string(media->id)); event.emplace_back(media->mediaType); event.emplace_back(media->resource); } diff --git a/ffi/native/main.cpp b/ffi/native/main.cpp index 66e309aa..a3ae1881 100644 --- a/ffi/native/main.cpp +++ b/ffi/native/main.cpp @@ -68,17 +68,16 @@ EXPORT void Player_create(int id, int videoWidth, int videoHeight, int commandLi 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 < 3 * sourceSize; index += 3) { + for (int index = 0; index < 2 * sourceSize; index += 2) { Media* media; - int id = atoi(source[index]); - const char* type = source[index + 1]; - const char* resource = source[index + 2]; + const char* type = source[index]; + const char* resource = source[index + 1]; if (strcmp(type, "MediaType.file") == 0) - media = Media::file(id, resource, false); + media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) - media = Media::network(id, resource, false); + media = Media::network(0, resource, false); else - media = Media::directShow(id, resource); + media = Media::directShow(0, resource); medias.emplace_back(media); } player->open( @@ -203,9 +202,9 @@ EXPORT void Player_move(int id, int initialIndex, int finalIndex) { EXPORT char** Media_parse(const char* type, const char* resource, int timeout) { Media* media; if (strcmp(type, "MediaType.file") == 0) - media = Media::file(0, resource, false); + media = Media::file(0, resource, true); else if (strcmp(type, "MediaType.network") == 0) - media = Media::network(0, resource, false); + media = Media::network(0, resource, true); else if (strcmp(type, "MediaType.directShow") == 0) media = Media::directShow(0, resource); char** metas = new char*[media->metas.size()]; @@ -287,7 +286,7 @@ EXPORT void Record_dispose(int id) { records->get(id, nullptr, "")->dispose(); } -EXPORT const char** Devices_all() { +EXPORT char** Devices_all() { devices->refresh(); char** _devices = new char*[(devices->all.size() * 2) + 1]; _devices[0] = std::to_string(devices->all.size()).data(); @@ -296,35 +295,45 @@ EXPORT const char** Devices_all() { _devices[i + 1] = devices->all[i + 1]->name.data(); } devices->all.size(); + return _devices; } - -EXPORT const char** Equalizer_createEmpty() { +EXPORT char** Equalizer_createEmpty() { int id = equalizers->createEmpty(); Equalizer* equalizer = equalizers->get(id); char** _equalizer = new char*[2 * equalizer->bandAmps.size() + 2]; - _equalizer[0] = std::to_string(id).data(); - _equalizer[1] = std::to_string(equalizer->preAmp).data(); + _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); int index = 0; for (const auto&[band, amp]: equalizer->bandAmps) { - _equalizer[index + 2] = std::to_string(band).data(); - _equalizer[index + 3] = std::to_string(amp).data(); + _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); + index += 2; } return _equalizer; } -EXPORT const char** Equalizer_createMode(int mode) { +EXPORT char** Equalizer_createMode(int mode) { int id = equalizers->createMode( static_cast(mode) ); Equalizer* equalizer = equalizers->get(id); char** _equalizer = new char*[2 * equalizer->bandAmps.size() + 2]; - _equalizer[0] = std::to_string(id).data(); - _equalizer[1] = std::to_string(equalizer->preAmp).data(); + _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); int index = 0; for (const auto&[band, amp]: equalizer->bandAmps) { - _equalizer[index + 2] = std::to_string(band).data(); - _equalizer[index + 3] = std::to_string(amp).data(); + _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); + index += 2; } return _equalizer; } @@ -338,7 +347,6 @@ EXPORT void Equalizer_setPreAmp(int id, float amp) { } #endif - #ifdef __cplusplus } #endif From a84d2912e6d0418b0a07f4d734eb119a29b88d1a Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 11:57:04 +0530 Subject: [PATCH 10/27] Fixed Devices.all --- ffi/CreateSharedLibrary.sh | 2 +- ffi/lib/src/device.dart | 7 ++++--- ffi/main.dart | 5 +---- ffi/native/main.cpp | 13 +++++++++---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/ffi/CreateSharedLibrary.sh b/ffi/CreateSharedLibrary.sh index 5d1ae3d5..9f7c85b1 100755 --- a/ffi/CreateSharedLibrary.sh +++ b/ffi/CreateSharedLibrary.sh @@ -1,6 +1,6 @@ # dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. # -# Hitesh Kumar Saini & contributors. +# Hitesh Kumar Saini. # https://github.com/alexmercerind # alexmercerind@gmail.com # GNU Lesser General Public License v2.1 diff --git a/ffi/lib/src/device.dart b/ffi/lib/src/device.dart index 8e7e4e70..435c3528 100644 --- a/ffi/lib/src/device.dart +++ b/ffi/lib/src/device.dart @@ -21,11 +21,12 @@ class Devices { List devices = []; Pointer> devicesPtr = DevicesFFI.all(); int count = int.parse(devicesPtr.elementAt(0).value.toDartString()); - for (int i = 1; i < count; i += 2) { + print(count); + for (int i = 1; i < 2 * count; i += 2) { devices.add( Device( - devicesPtr.elementAt(i).value.toDartString(), - devicesPtr.elementAt(i + 1).value.toDartString() + devicesPtr.elementAt(1).value.toDartString(), + devicesPtr.elementAt(2).value.toDartString() ) ); } diff --git a/ffi/main.dart b/ffi/main.dart index 8c0eb976..48f08156 100644 --- a/ffi/main.dart +++ b/ffi/main.dart @@ -8,9 +8,6 @@ String get dynamicLibraryPath { return directory + '/' + 'dart_vlc.so'; } -Future main() async { +void main() { DartVLC.initialize(dynamicLibraryPath); - Equalizer.createMode( - EqualizerMode.live - ); } diff --git a/ffi/native/main.cpp b/ffi/native/main.cpp index a3ae1881..b085243a 100644 --- a/ffi/native/main.cpp +++ b/ffi/native/main.cpp @@ -289,10 +289,15 @@ EXPORT void Record_dispose(int id) { EXPORT char** Devices_all() { devices->refresh(); char** _devices = new char*[(devices->all.size() * 2) + 1]; - _devices[0] = std::to_string(devices->all.size()).data(); - for (int i = 1; i < devices->all.size(); i += 2) { - _devices[i] = devices->all[i]->id.data(); - _devices[i + 1] = devices->all[i + 1]->name.data(); + _devices[0] = new char[200]; + strncpy(_devices[0], std::to_string(devices->all.size()).data(), 200); + int index = 1; + for (Device* device: devices->all) { + _devices[index] = new char[200]; + strncpy(_devices[index], device->id.data(), 200); + _devices[index + 1] = new char[200]; + strncpy(_devices[index + 1], device->name.data(), 200); + index += 2; } devices->all.size(); return _devices; From 3bef93ffbbec53f239b80c5d290a2cc5252a5054 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 12:55:13 +0530 Subject: [PATCH 11/27] Added FFI event callbacks & updated example --- ffi/example.dart | 92 ++++++++++++++++++++++++++ ffi/lib/src/internal/ffi.dart | 100 +++++++++++++++++++++++++---- ffi/lib/src/mediaSource/media.dart | 2 +- ffi/lib/src/player.dart | 4 ++ ffi/main.dart | 13 ---- 5 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 ffi/example.dart delete mode 100644 ffi/main.dart diff --git a/ffi/example.dart b/ffi/example.dart new file mode 100644 index 00000000..9ba38187 --- /dev/null +++ b/ffi/example.dart @@ -0,0 +1,92 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:dart_vlc_ffi/dart_vlc.dart'; + + +void main(List args) { + // Initialize the library. + DartVLC.initialize(dynamicLibraryPath); + + // Create a new player. Provide an ID to handle multiple players. + Player player = Player( + id: 0, + // Pass commandline VLC arguments. + commandlineArguments: [], + ); + + // Listen to events on the player e.g. position, rate, volume etc. + // Same can be accessed directly in the class without having to use the stream. + + player.generalStream.listen((event) { + // Player's rate. + print('Rate of the player is ${event.rate}'); + // Player's volume. + print('Volume of the player is ${event.volume}'); + }); + + player.positionStream.listen((event) { + // Player's current media playback position. + print('Position of the player is ${event.position}'); + // Player's current media playback duration. + print('Duration of the player is ${event.duration}'); + }); + + player.currentStream.listen((event) { + // Index of the currently playing media. + print('Current media index of the player is ${event.index}'); + // Currently playing media. + print('Current playing media is ${event.media}'); + // All the medias in the playlist. + print('Current playing media is ${event.medias}'); + // Whether current playback is a playlist or not. + print('Current playing media is ${event.isPlaylist}'); + }); + + player.playbackStream.listen((event) { + print('isPlaying :${event.isPlaying}'); + print('isSeekable :${event.isSeekable}'); + print('isCompleted :${event.isCompleted}'); + }); + + player.setRate(1.25); + + player.setVolume(1.0); + + // Create a new media using a URL. + Media media = Media.network( + Uri.parse('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3'), + // Pass parse as true for accessing the metadata. + parse: true + ); + // Get media's metadata. + print(media.metas); + + // Open the media into the player. You can also open a Playlist instead of a media. + player.open(media); + + Timer(Duration(seconds: 10), () { + // Alter playback rate. + player.setRate(1.25); + // Alter playback volume. + player.setVolume(0.5); + }); + + Timer(Duration(seconds: 20), () { + // Pause the player. + player.pause(); + Timer(Duration(seconds: 2), () { + // Resume the player. + player.play(); + + }); + }); + + // A lot lot of other options like device enumeration & changing, equalizer, recording, broadcasting, chromecasting etc. are also available. +} + +// Path to the dart_vlc.so +String get dynamicLibraryPath { + String directory = Platform.script.path.split('/').sublist(0, Platform.script.path.split('/').length - 1).join('/'); + return directory + '/' + 'dart_vlc.so'; +} \ No newline at end of file diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart index e6a09a74..e637d38a 100644 --- a/ffi/lib/src/internal/ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -1,5 +1,7 @@ import 'dart:ffi'; +import 'dart:io'; import 'dart:isolate'; +import 'package:dart_vlc_ffi/src/enums/mediaType.dart'; import 'package:ffi/ffi.dart'; import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/player.dart'; @@ -9,19 +11,9 @@ import 'package:dart_vlc_ffi/src/internal/typedefs/equalizer.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/record.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/broadcast.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/chromecast.dart'; - - -extension NativeTypes on List { - Pointer> toNativeUtf8Array() { - final List> listPointer = this.map((String string) => string.toNativeUtf8()).toList().cast>(); - final Pointer> pointerPointer = calloc.allocate(this.join('').length); - for (int index = 0; index < this.length; index++) pointerPointer[index] = listPointer[index]; - return pointerPointer; - } -} - -bool isInitialized = false; -final ReceivePort receiver = new ReceivePort(); +/// NOTE: Here for sending event callbacks. +import 'package:dart_vlc_ffi/src/player.dart'; +import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; abstract class PlayerFFI { @@ -120,3 +112,85 @@ abstract class EqualizerFFI { static final EqualizerSetPreAmpDart setPreAmp = dynamicLibrary.lookup>('Equalizer_setPreAmp').asFunction(); } + +bool isInitialized = false; +final ReceivePort receiver = new ReceivePort(); +final Stream receiverStream = receiver.asBroadcastStream()..listen( + (event) { + int playerId = int.parse(event[0]); + String playerEvent = event[1]; + switch(playerEvent) { + case 'playbackEvent': { + players[playerId]!.playback.isPlaying = event[2] == '1' ? true: false; + players[playerId]!.playback.isSeekable = event[3] == '1' ? true: false; + players[playerId]!.playback.isCompleted = false; + players[playerId]!.playbackController.add(players[playerId]!.playback); + break; + } + case 'positionEvent': { + players[playerId]!.position.position = Duration(milliseconds: int.parse(event[3])); + players[playerId]!.position.duration = Duration(milliseconds: int.parse(event[4])); + players[playerId]!.positionController.add(players[playerId]!.position); + break; + } + case 'openEvent': { + players[playerId]!.current.index = int.parse(event[2]); + players[playerId]!.current.isPlaylist = event[3] == '1' ? true: false; + int mediasLength = event.length - 4; + List medias = []; + for (int index = 4; index < 4 + mediasLength; index++) { + switch (event[index]) { + case 'MediaType.file': { + medias.add( + Media.file(File(event[index + 1])) + ); + break; + } + case 'MediaType.network': { + medias.add( + Media.network(Uri.parse(event[index + 1])) + ); + break; + } + case 'MediaType.directShow': { + Media media = Media(); + media.mediaType = MediaType.directShow; + media.resource = event[index + 1]; + medias.add(media); + break; + } + } + } + players[playerId]!.current.medias = medias; + players[playerId]!.current.media = medias[players[playerId]!.current.index!]; + players[playerId]!.currentController.add(players[playerId]!.current); + break; + } + case 'completeEvent': { + players[playerId]!.playback.isCompleted = event[2] == '1' ? true: false; + players[playerId]!.playbackController.add(players[playerId]!.playback); + break; + } + case 'volumeEvent': { + players[playerId]!.general.volume = double.parse(event[2]); + players[playerId]!.generalController.add(players[playerId]!.general); + break; + } + case 'rateEvent': { + players[playerId]!.general.rate = double.parse(event[2]); + players[playerId]!.generalController.add(players[playerId]!.general); + break; + } + } + } +); + + +extension NativeTypes on List { + Pointer> toNativeUtf8Array() { + final List> listPointer = this.map((String string) => string.toNativeUtf8()).toList().cast>(); + final Pointer> pointerPointer = calloc.allocate(this.join('').length); + for (int index = 0; index < this.length; index++) pointerPointer[index] = listPointer[index]; + return pointerPointer; + } +} diff --git a/ffi/lib/src/mediaSource/media.dart b/ffi/lib/src/mediaSource/media.dart index db04a413..dc6a087d 100644 --- a/ffi/lib/src/mediaSource/media.dart +++ b/ffi/lib/src/mediaSource/media.dart @@ -28,7 +28,7 @@ class _Media extends Media {} /// ``` /// /// -abstract class Media extends MediaSource { +class Media extends MediaSource { MediaSourceType mediaSourceType = MediaSourceType.media; Map metas = {}; late MediaType mediaType; diff --git a/ffi/lib/src/player.dart b/ffi/lib/src/player.dart index e59e8975..39637f83 100644 --- a/ffi/lib/src/player.dart +++ b/ffi/lib/src/player.dart @@ -8,6 +8,9 @@ import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; import 'package:dart_vlc_ffi/src/mediaSource/mediaSource.dart'; import 'package:dart_vlc_ffi/src/device.dart'; +/// Keeps various [Player] instances to manage event callbacks. +Map players = {}; + /// A [Player] to open & play a [Media] or [Playlist] from file, network or asset. /// @@ -90,6 +93,7 @@ class Player { this.playbackStream = this.playbackController.stream; this.generalController = StreamController.broadcast(); this.generalStream = this.generalController.stream; + players[this.id] = this; PlayerFFI.create( this.id, this.videoWidth, diff --git a/ffi/main.dart b/ffi/main.dart deleted file mode 100644 index 48f08156..00000000 --- a/ffi/main.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'dart:io'; - -import 'package:dart_vlc_ffi/dart_vlc.dart'; - - -String get dynamicLibraryPath { - String directory = Platform.script.path.split('/').sublist(0, Platform.script.path.split('/').length - 1).join('/'); - return directory + '/' + 'dart_vlc.so'; -} - -void main() { - DartVLC.initialize(dynamicLibraryPath); -} From 3e345df89cdef9945f8edff6d291a77d8d7ac75b Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 13:09:37 +0530 Subject: [PATCH 12/27] Added FFI documentation & example --- README.md | 12 ++++ ffi/README.md | 104 ++++++++++++++++++++++++++++++++-- ffi/lib/src/internal/ffi.dart | 3 +- ffi/pubspec.yaml | 3 + 4 files changed, 115 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f23f1e92..4595563c 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,24 @@ ## Installation +**Flutter** + ```yaml dependencies: ... dart_vlc: ^0.0.8 ``` +**Dart CLI** + +```yaml +dependencies: + ... + dart_vlc_ffi: ^0.0.1 +``` + +More on Dart CLI implementation [here](./ffi/README.md). + ![](https://github.com/alexmercerind/dart_vlc/blob/assets/dart_vlc_6.png?raw=true) ![](https://github.com/alexmercerind/dart_vlc/blob/assets/dart_vlc_7.png?raw=true) diff --git a/ffi/README.md b/ffi/README.md index c35717f3..de619895 100644 --- a/ffi/README.md +++ b/ffi/README.md @@ -1,9 +1,103 @@ -# dart_vlc_ffi +

dart_vlc

+

Flutter & Dart 🎞 media playback, broadcast, recording & chromecast library for Windows & Linux.

+
Written in C++ using libVLC & libVLC++.
-#### Bringing power of VLC to Flutter & Dart apps on all platforms. +A dependency package for [dart_vlc](https://github.com/alexmercerind/dart_vlc). -[WIP] FFI based binding to [dart_vlc wrapper](https://github.com/alexmercerind/dart_vlc/tree/master/dartvlc) (Based on [libVLC](https://github.com/videolan/vlc) & [libVLC++](https://github.com/videolan/libvlcpp)). +Contains FFI bindings & NativePort event callback handling for `dart_vlc.so`. -Planned to be used in Dart console apps & flutter apps other than Windows & Linux. +## Example -Uses NativePorts to notify about playback events to Dart from C++. +```dart +import 'dart:async'; +import 'dart:io'; + +import 'package:dart_vlc_ffi/dart_vlc.dart'; + + +void main(List args) { + // Initialize the library, pass path to dart_vlc.so or dart_vlc.dll + DartVLC.initialize(dynamicLibraryPath); + + // Create a new player. Provide an ID to handle multiple players. + Player player = Player( + id: 0, + // Pass commandline VLC arguments. + commandlineArguments: [], + ); + + // Listen to events on the player e.g. position, rate, volume etc. + // Same can be accessed directly in the class without having to use the stream. + + player.generalStream.listen((event) { + // Player's rate. + print('Rate of the player is ${event.rate}'); + // Player's volume. + print('Volume of the player is ${event.volume}'); + }); + + player.positionStream.listen((event) { + // Player's current media playback position. + print('Position of the player is ${event.position}'); + // Player's current media playback duration. + print('Duration of the player is ${event.duration}'); + }); + + player.currentStream.listen((event) { + // Index of the currently playing media. + print('Current media index of the player is ${event.index}'); + // Currently playing media. + print('Current playing media is ${event.media}'); + // All the medias in the playlist. + print('Current playing media is ${event.medias}'); + // Whether current playback is a playlist or not. + print('Current playing media is ${event.isPlaylist}'); + }); + + player.playbackStream.listen((event) { + print('isPlaying :${event.isPlaying}'); + print('isSeekable :${event.isSeekable}'); + print('isCompleted :${event.isCompleted}'); + }); + + player.setRate(1.25); + + player.setVolume(1.0); + + // Create a new media using a URL. + Media media = Media.network( + Uri.parse('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3'), + // Pass parse as true for accessing the metadata. + parse: true + ); + // Get media's metadata. + print(media.metas); + + // Open the media into the player. You can also open a Playlist instead of a media. + player.open(media); + + Timer(Duration(seconds: 10), () { + // Alter playback rate. + player.setRate(1.25); + // Alter playback volume. + player.setVolume(0.5); + }); + + Timer(Duration(seconds: 20), () { + // Pause the player. + player.pause(); + Timer(Duration(seconds: 2), () { + // Resume the player. + player.play(); + + }); + }); + + // A lot lot of other options like device enumeration & changing, equalizer, recording, broadcasting, chromecasting etc. are also available. +} +``` + + +## License + +MIT Licensed. Contributions welcomed. diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart index e637d38a..3784cb3b 100644 --- a/ffi/lib/src/internal/ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -114,8 +114,7 @@ abstract class EqualizerFFI { } bool isInitialized = false; -final ReceivePort receiver = new ReceivePort(); -final Stream receiverStream = receiver.asBroadcastStream()..listen( +final ReceivePort receiver = new ReceivePort()..asBroadcastStream()..listen( (event) { int playerId = int.parse(event[0]); String playerEvent = event[1]; diff --git a/ffi/pubspec.yaml b/ffi/pubspec.yaml index 61fca21c..3761560a 100644 --- a/ffi/pubspec.yaml +++ b/ffi/pubspec.yaml @@ -1,6 +1,9 @@ name: dart_vlc_ffi description: FFI based binding to dart_vlc wrapper (Based on libVLC & libVLC++). version: 0.0.1 +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 environment: sdk: '>=2.12.0 <3.0.0' From d0bb0c65a954f6b012c871e17a75ca3b7098b5f8 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 13:13:08 +0530 Subject: [PATCH 13/27] [dart_vlc_ffi] No video frame callbacks if frame size not passed. --- ffi/native/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ffi/native/main.cpp b/ffi/native/main.cpp index b085243a..fc9fb58c 100644 --- a/ffi/native/main.cpp +++ b/ffi/native/main.cpp @@ -60,9 +60,11 @@ EXPORT void Player_create(int id, int videoWidth, int videoHeight, int commandLi player->onPlaylist([=]() -> void { Player_onOpen(player->state); }); - player->onVideo([=](uint8_t* frame) -> void { - Player_onVideo(player->videoHeight * player->videoWidth * 4, frame); - }); + if (player->videoHeight > 0 && player->videoWidth > 0) { + player->onVideo([=](uint8_t* frame) -> void { + Player_onVideo(player->videoHeight * player->videoWidth * 4, frame); + }); + } } EXPORT void Player_open(int id, bool autoStart, const char** source, int sourceSize) { From 7fc4829bfc8b1996f722c16fe8501c268f45dac2 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 13:17:07 +0530 Subject: [PATCH 14/27] [dart_vlc_ffi] Final code formatting & clean-up --- ffi/LICENSE | 504 ++++++++++++++++++ ffi/example.dart | 21 +- ffi/lib/{dart_vlc.dart => dart_vlc_ffi.dart} | 0 ffi/lib/src/broadcast.dart | 21 +- ffi/lib/src/chromecast.dart | 9 +- ffi/lib/src/device.dart | 9 +- ffi/lib/src/enums/equalizerMode.dart | 38 +- ffi/lib/src/equalizer.dart | 57 +- ffi/lib/src/internal/ffi.dart | 358 ++++++++----- ffi/lib/src/internal/initializer.dart | 12 +- ffi/lib/src/internal/typedefs/broadcast.dart | 24 +- ffi/lib/src/internal/typedefs/callback.dart | 8 +- ffi/lib/src/internal/typedefs/chromecast.dart | 6 +- ffi/lib/src/internal/typedefs/equalizer.dart | 3 +- ffi/lib/src/internal/typedefs/media.dart | 6 +- ffi/lib/src/internal/typedefs/player.dart | 47 +- ffi/lib/src/internal/typedefs/record.dart | 6 +- ffi/lib/src/mediaSource/media.dart | 12 +- ffi/lib/src/mediaSource/playlist.dart | 1 - ffi/lib/src/player.dart | 57 +- ffi/lib/src/record.dart | 16 +- 21 files changed, 919 insertions(+), 296 deletions(-) create mode 100644 ffi/LICENSE rename ffi/lib/{dart_vlc.dart => dart_vlc_ffi.dart} (100%) diff --git a/ffi/LICENSE b/ffi/LICENSE new file mode 100644 index 00000000..8000a6fa --- /dev/null +++ b/ffi/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ffi/example.dart b/ffi/example.dart index 9ba38187..86d12945 100644 --- a/ffi/example.dart +++ b/ffi/example.dart @@ -1,8 +1,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:dart_vlc_ffi/dart_vlc.dart'; - +import 'package:dart_vlc_ffi/dart_vlc_ffi.dart'; void main(List args) { // Initialize the library. @@ -55,10 +54,10 @@ void main(List args) { // Create a new media using a URL. Media media = Media.network( - Uri.parse('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3'), - // Pass parse as true for accessing the metadata. - parse: true - ); + Uri.parse( + 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3'), + // Pass parse as true for accessing the metadata. + parse: true); // Get media's metadata. print(media.metas); @@ -71,14 +70,13 @@ void main(List args) { // Alter playback volume. player.setVolume(0.5); }); - + Timer(Duration(seconds: 20), () { // Pause the player. player.pause(); Timer(Duration(seconds: 2), () { // Resume the player. player.play(); - }); }); @@ -87,6 +85,9 @@ void main(List args) { // Path to the dart_vlc.so String get dynamicLibraryPath { - String directory = Platform.script.path.split('/').sublist(0, Platform.script.path.split('/').length - 1).join('/'); + String directory = Platform.script.path + .split('/') + .sublist(0, Platform.script.path.split('/').length - 1) + .join('/'); return directory + '/' + 'dart_vlc.so'; -} \ No newline at end of file +} diff --git a/ffi/lib/dart_vlc.dart b/ffi/lib/dart_vlc_ffi.dart similarity index 100% rename from ffi/lib/dart_vlc.dart rename to ffi/lib/dart_vlc_ffi.dart diff --git a/ffi/lib/src/broadcast.dart b/ffi/lib/src/broadcast.dart index a01ca3e1..2d7bbefe 100644 --- a/ffi/lib/src/broadcast.dart +++ b/ffi/lib/src/broadcast.dart @@ -97,17 +97,16 @@ abstract class Broadcast { broadcast.media = media; broadcast.configuration = configuration; BroadcastFFI.create( - id, - media.mediaType.toString().toNativeUtf8(), - media.resource.toNativeUtf8(), - configuration.access.toNativeUtf8(), - configuration.mux.toNativeUtf8(), - configuration.dst.toNativeUtf8(), - configuration.vcodec.toNativeUtf8(), - configuration.vb, - configuration.acodec.toNativeUtf8(), - configuration.ab - ); + id, + media.mediaType.toString().toNativeUtf8(), + media.resource.toNativeUtf8(), + configuration.access.toNativeUtf8(), + configuration.mux.toNativeUtf8(), + configuration.dst.toNativeUtf8(), + configuration.vcodec.toNativeUtf8(), + configuration.vb, + configuration.acodec.toNativeUtf8(), + configuration.ab); return broadcast; } diff --git a/ffi/lib/src/chromecast.dart b/ffi/lib/src/chromecast.dart index bfe8276b..500a89b9 100644 --- a/ffi/lib/src/chromecast.dart +++ b/ffi/lib/src/chromecast.dart @@ -5,7 +5,6 @@ import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; /// Internally used class to avoid direct creation of the object of a [Chromecast] class. class _Chromecast extends Chromecast {} - class Chromecast { /// ID for this [Chromecast]. late int id; @@ -25,12 +24,8 @@ class Chromecast { chromecast.id = id; chromecast.media = media; chromecast.ipAddress = ipAddress; - ChromecastFFI.create( - id, - media.mediaType.toString().toNativeUtf8(), - media.resource.toNativeUtf8(), - ipAddress.toNativeUtf8() - ); + ChromecastFFI.create(id, media.mediaType.toString().toNativeUtf8(), + media.resource.toNativeUtf8(), ipAddress.toNativeUtf8()); return chromecast; } diff --git a/ffi/lib/src/device.dart b/ffi/lib/src/device.dart index 435c3528..57c50c97 100644 --- a/ffi/lib/src/device.dart +++ b/ffi/lib/src/device.dart @@ -2,7 +2,6 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:dart_vlc_ffi/src/internal/ffi.dart'; - /// Represents a playback [Device] for the [Player]. class Device { /// ID corresponding to the [Device]. @@ -23,12 +22,8 @@ class Devices { int count = int.parse(devicesPtr.elementAt(0).value.toDartString()); print(count); for (int i = 1; i < 2 * count; i += 2) { - devices.add( - Device( - devicesPtr.elementAt(1).value.toDartString(), - devicesPtr.elementAt(2).value.toDartString() - ) - ); + devices.add(Device(devicesPtr.elementAt(1).value.toDartString(), + devicesPtr.elementAt(2).value.toDartString())); } return devices; } diff --git a/ffi/lib/src/enums/equalizerMode.dart b/ffi/lib/src/enums/equalizerMode.dart index ebe8f4a3..acc2fa5a 100644 --- a/ffi/lib/src/enums/equalizerMode.dart +++ b/ffi/lib/src/enums/equalizerMode.dart @@ -1,23 +1,21 @@ - - /// Predefined presets for [Equalizer]. enum EqualizerMode { - flat, - classical, - club, - dance, - fullBass, - fullBassAndTreble, - fullTreble, - headphones, - largeHall, - live, - party, - pop, - reggae, - rock, - ska, - soft, - softRock, - techno + flat, + classical, + club, + dance, + fullBass, + fullBassAndTreble, + fullTreble, + headphones, + largeHall, + live, + party, + pop, + reggae, + rock, + ska, + soft, + softRock, + techno } diff --git a/ffi/lib/src/equalizer.dart b/ffi/lib/src/equalizer.dart index 78505199..583e8d39 100644 --- a/ffi/lib/src/equalizer.dart +++ b/ffi/lib/src/equalizer.dart @@ -3,30 +3,31 @@ import 'package:ffi/ffi.dart'; import 'package:dart_vlc_ffi/src/internal/ffi.dart'; import 'package:dart_vlc_ffi/src/enums/equalizerMode.dart'; - /// Internally used class to avoid direct creation of the object of a [Equalizer] class. class _Equalizer extends Equalizer {} - /// Defines an [Equalizer] instance for usage in a [Player]. -/// +/// /// Use [Equalizer.createEmpty] for creating a default equalizer & [Equalizer.createMode] for creating an equalizer using a preset from [EqualizerMode]. -/// +/// /// Example -/// +/// /// ```dart /// Player player = new Player(id: 0); /// Equalizer equalizer = Equalizer.createMode(EqualizerMode.party); /// player.setEqualizer(equalizer); /// ``` -/// +/// class Equalizer { /// Unique Id associated with this [Equalizer]. late int id; + /// Preamp value of the [Equalizer]. Use [Equalizer.setPreAmp] to change value. double preAmp = 0.0; + /// Values of amps of various bands in [Equalizer]. Use [Equalizer.setBandAmp] to change values. Map bandAmps = {}; + /// Preset if [Equalizer] is initialized using [Equalizer.createMode], else `null`. EqualizerMode? mode; @@ -35,14 +36,20 @@ class Equalizer { Equalizer equalizer = new _Equalizer(); Pointer> _equalizer = EqualizerFFI.createEmpty(); equalizer.id = int.parse(_equalizer.elementAt(0).value.toDartString()); - equalizer.preAmp = double.parse(_equalizer.elementAt(1).value.toDartString()); + equalizer.preAmp = + double.parse(_equalizer.elementAt(1).value.toDartString()); equalizer.mode = null; equalizer.bandAmps = { - double.parse(_equalizer.elementAt(2).value.toDartString()): double.parse(_equalizer.elementAt(3).value.toDartString()), - double.parse(_equalizer.elementAt(4).value.toDartString()): double.parse(_equalizer.elementAt(5).value.toDartString()), - double.parse(_equalizer.elementAt(6).value.toDartString()): double.parse(_equalizer.elementAt(7).value.toDartString()), - double.parse(_equalizer.elementAt(8).value.toDartString()): double.parse(_equalizer.elementAt(9).value.toDartString()), - double.parse(_equalizer.elementAt(10).value.toDartString()): double.parse(_equalizer.elementAt(11).value.toDartString()), + double.parse(_equalizer.elementAt(2).value.toDartString()): + double.parse(_equalizer.elementAt(3).value.toDartString()), + double.parse(_equalizer.elementAt(4).value.toDartString()): + double.parse(_equalizer.elementAt(5).value.toDartString()), + double.parse(_equalizer.elementAt(6).value.toDartString()): + double.parse(_equalizer.elementAt(7).value.toDartString()), + double.parse(_equalizer.elementAt(8).value.toDartString()): + double.parse(_equalizer.elementAt(9).value.toDartString()), + double.parse(_equalizer.elementAt(10).value.toDartString()): + double.parse(_equalizer.elementAt(11).value.toDartString()), }; return equalizer; } @@ -52,23 +59,29 @@ class Equalizer { Equalizer equalizer = new _Equalizer(); Pointer> _equalizer = EqualizerFFI.createMode(mode.index); equalizer.id = int.parse(_equalizer.elementAt(0).value.toDartString()); - equalizer.preAmp = double.parse(_equalizer.elementAt(1).value.toDartString()); + equalizer.preAmp = + double.parse(_equalizer.elementAt(1).value.toDartString()); equalizer.mode = mode; equalizer.bandAmps = { - double.parse(_equalizer.elementAt(2).value.toDartString()): double.parse(_equalizer.elementAt(3).value.toDartString()), - double.parse(_equalizer.elementAt(4).value.toDartString()): double.parse(_equalizer.elementAt(5).value.toDartString()), - double.parse(_equalizer.elementAt(6).value.toDartString()): double.parse(_equalizer.elementAt(7).value.toDartString()), - double.parse(_equalizer.elementAt(8).value.toDartString()): double.parse(_equalizer.elementAt(9).value.toDartString()), - double.parse(_equalizer.elementAt(10).value.toDartString()): double.parse(_equalizer.elementAt(11).value.toDartString()), + double.parse(_equalizer.elementAt(2).value.toDartString()): + double.parse(_equalizer.elementAt(3).value.toDartString()), + double.parse(_equalizer.elementAt(4).value.toDartString()): + double.parse(_equalizer.elementAt(5).value.toDartString()), + double.parse(_equalizer.elementAt(6).value.toDartString()): + double.parse(_equalizer.elementAt(7).value.toDartString()), + double.parse(_equalizer.elementAt(8).value.toDartString()): + double.parse(_equalizer.elementAt(9).value.toDartString()), + double.parse(_equalizer.elementAt(10).value.toDartString()): + double.parse(_equalizer.elementAt(11).value.toDartString()), }; return equalizer; } /// Sets value of [Equalizer.preAmp]. - /// + /// /// Constraints: /// `-20.0 < amp < 20.0` - /// + /// void setPreAmp(double amp) { EqualizerFFI.setPreAmp(this.id, amp); this.preAmp = amp; @@ -76,10 +89,10 @@ class Equalizer { /// Sets value of any particular band from [Equalizer.bandAmps]. /// Band whose `amp` needs to be changed, must be present in [Equalizer.bandAmps]. - /// + /// /// Constraints: /// `-20.0 < amp < 20.0` - /// + /// void setBandAmp(double band, double amp) { EqualizerFFI.setBandAmp(this.id, band, amp); this.bandAmps[band] = amp; diff --git a/ffi/lib/src/internal/ffi.dart b/ffi/lib/src/internal/ffi.dart index 3784cb3b..681d3d1b 100644 --- a/ffi/lib/src/internal/ffi.dart +++ b/ffi/lib/src/internal/ffi.dart @@ -11,185 +11,265 @@ import 'package:dart_vlc_ffi/src/internal/typedefs/equalizer.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/record.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/broadcast.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/chromecast.dart'; + /// NOTE: Here for sending event callbacks. import 'package:dart_vlc_ffi/src/player.dart'; import 'package:dart_vlc_ffi/src/mediaSource/media.dart'; - abstract class PlayerFFI { - - static final PlayerCreateDart create = dynamicLibrary.lookup>('Player_create').asFunction(); - - static final PlayerOpenDart open = dynamicLibrary.lookup>('Player_open').asFunction(); - - static final PlayerTriggerDart play = dynamicLibrary.lookup>('Player_play').asFunction(); - - static final PlayerTriggerDart pause = dynamicLibrary.lookup>('Player_pause').asFunction(); - - static final PlayerTriggerDart playOrPause = dynamicLibrary.lookup>('Player_playOrPause').asFunction(); - - static final PlayerTriggerDart stop = dynamicLibrary.lookup>('Player_stop').asFunction(); - - static final PlayerTriggerDart next = dynamicLibrary.lookup>('Player_next').asFunction(); - - static final PlayerTriggerDart back = dynamicLibrary.lookup>('Player_back').asFunction(); - - static final PlayerJumpDart jump = dynamicLibrary.lookup>('Player_jump').asFunction(); - - static final PlayerSeekDart seek = dynamicLibrary.lookup>('Player_seek').asFunction(); - - static final PlayerSetVolumeDart setVolume = dynamicLibrary.lookup>('Player_setVolume').asFunction(); - - static final PlayerSetRateDart setRate = dynamicLibrary.lookup>('Player_setRate').asFunction(); - - static final PlayerSetUserAgentDart setUserAgent = dynamicLibrary.lookup>('Player_setUserAgent').asFunction(); - - static final PlayerSetEqualizerDart setEqualizer = dynamicLibrary.lookup>('Player_setEqualizer').asFunction(); - - static final PlayerSetDeviceDart setDevice = dynamicLibrary.lookup>('Player_setDevice').asFunction(); - - static final PlayerSetPlaylistModeDart setPlaylistMode = dynamicLibrary.lookup>('Player_setPlaylistMode').asFunction(); - - static final PlayerAddDart add = dynamicLibrary.lookup>('Player_add').asFunction(); - - static final PlayerRemoveDart remove = dynamicLibrary.lookup>('Player_remove').asFunction(); - - static final PlayerInsertDart insert = dynamicLibrary.lookup>('Player_insert').asFunction(); - - static final PlayerMoveDart move = dynamicLibrary.lookup>('Player_move').asFunction(); + static final PlayerCreateDart create = dynamicLibrary + .lookup>('Player_create') + .asFunction(); + + static final PlayerOpenDart open = dynamicLibrary + .lookup>('Player_open') + .asFunction(); + + static final PlayerTriggerDart play = dynamicLibrary + .lookup>('Player_play') + .asFunction(); + + static final PlayerTriggerDart pause = dynamicLibrary + .lookup>('Player_pause') + .asFunction(); + + static final PlayerTriggerDart playOrPause = dynamicLibrary + .lookup>('Player_playOrPause') + .asFunction(); + + static final PlayerTriggerDart stop = dynamicLibrary + .lookup>('Player_stop') + .asFunction(); + + static final PlayerTriggerDart next = dynamicLibrary + .lookup>('Player_next') + .asFunction(); + + static final PlayerTriggerDart back = dynamicLibrary + .lookup>('Player_back') + .asFunction(); + + static final PlayerJumpDart jump = dynamicLibrary + .lookup>('Player_jump') + .asFunction(); + + static final PlayerSeekDart seek = dynamicLibrary + .lookup>('Player_seek') + .asFunction(); + + static final PlayerSetVolumeDart setVolume = dynamicLibrary + .lookup>('Player_setVolume') + .asFunction(); + + static final PlayerSetRateDart setRate = dynamicLibrary + .lookup>('Player_setRate') + .asFunction(); + + static final PlayerSetUserAgentDart setUserAgent = dynamicLibrary + .lookup>('Player_setUserAgent') + .asFunction(); + + static final PlayerSetEqualizerDart setEqualizer = dynamicLibrary + .lookup>('Player_setEqualizer') + .asFunction(); + + static final PlayerSetDeviceDart setDevice = dynamicLibrary + .lookup>('Player_setDevice') + .asFunction(); + + static final PlayerSetPlaylistModeDart setPlaylistMode = dynamicLibrary + .lookup>( + 'Player_setPlaylistMode') + .asFunction(); + + static final PlayerAddDart add = dynamicLibrary + .lookup>('Player_add') + .asFunction(); + + static final PlayerRemoveDart remove = dynamicLibrary + .lookup>('Player_remove') + .asFunction(); + + static final PlayerInsertDart insert = dynamicLibrary + .lookup>('Player_insert') + .asFunction(); + + static final PlayerMoveDart move = dynamicLibrary + .lookup>('Player_move') + .asFunction(); } - abstract class MediaFFI { - - static final MediaParseDart parse = dynamicLibrary.lookup>('Media_parse').asFunction(); - + static final MediaParseDart parse = dynamicLibrary + .lookup>('Media_parse') + .asFunction(); } - abstract class BroadcastFFI { + static final BroadcastCreateDart create = dynamicLibrary + .lookup>('Broadcast_create') + .asFunction(); - static final BroadcastCreateDart create = dynamicLibrary.lookup>('Broadcast_create').asFunction(); - - static final BroadcastStartDart start = dynamicLibrary.lookup>('Broadcast_start').asFunction(); - - static final BroadcastDisposeDart dispose = dynamicLibrary.lookup>('Broadcast_dispose').asFunction(); -} + static final BroadcastStartDart start = dynamicLibrary + .lookup>('Broadcast_start') + .asFunction(); + static final BroadcastDisposeDart dispose = dynamicLibrary + .lookup>('Broadcast_dispose') + .asFunction(); +} abstract class ChromecastFFI { + static final ChromecastCreateDart create = dynamicLibrary + .lookup>('Chromecast_create') + .asFunction(); - static final ChromecastCreateDart create = dynamicLibrary.lookup>('Chromecast_create').asFunction(); - - static final ChromecastStartDart start = dynamicLibrary.lookup>('Chromecast_start').asFunction(); - - static final ChromecastDisposeDart dispose = dynamicLibrary.lookup>('Chromecast_dispose').asFunction(); -} + static final ChromecastStartDart start = dynamicLibrary + .lookup>('Chromecast_start') + .asFunction(); + static final ChromecastDisposeDart dispose = dynamicLibrary + .lookup>('Chromecast_dispose') + .asFunction(); +} abstract class RecordFFI { + static final RecordCreateDart create = dynamicLibrary + .lookup>('Record_create') + .asFunction(); - static final RecordCreateDart create = dynamicLibrary.lookup>('Record_create').asFunction(); - - static final RecordStartDart start = dynamicLibrary.lookup>('Record_start').asFunction(); - - static final RecordDisposeDart dispose = dynamicLibrary.lookup>('Record_dispose').asFunction(); -} + static final RecordStartDart start = dynamicLibrary + .lookup>('Record_start') + .asFunction(); + static final RecordDisposeDart dispose = dynamicLibrary + .lookup>('Record_dispose') + .asFunction(); +} abstract class DevicesFFI { - - static final DevicesAllDart all = dynamicLibrary.lookup>('Devices_all').asFunction(); + static final DevicesAllDart all = dynamicLibrary + .lookup>('Devices_all') + .asFunction(); } abstract class EqualizerFFI { + static final EqualizerCreateEmptyDart createEmpty = dynamicLibrary + .lookup>('Equalizer_createEmpty') + .asFunction(); - static final EqualizerCreateEmptyDart createEmpty = dynamicLibrary.lookup>('Equalizer_createEmpty').asFunction(); - - static final EqualizerCreateModeDart createMode = dynamicLibrary.lookup>('Equalizer_createMode').asFunction(); - - static final EqualizerSetBandAmpDart setBandAmp = dynamicLibrary.lookup>('Equalizer_setBandAmp').asFunction(); + static final EqualizerCreateModeDart createMode = dynamicLibrary + .lookup>('Equalizer_createMode') + .asFunction(); - static final EqualizerSetPreAmpDart setPreAmp = dynamicLibrary.lookup>('Equalizer_setPreAmp').asFunction(); + static final EqualizerSetBandAmpDart setBandAmp = dynamicLibrary + .lookup>('Equalizer_setBandAmp') + .asFunction(); + + static final EqualizerSetPreAmpDart setPreAmp = dynamicLibrary + .lookup>('Equalizer_setPreAmp') + .asFunction(); } bool isInitialized = false; -final ReceivePort receiver = new ReceivePort()..asBroadcastStream()..listen( - (event) { +final ReceivePort receiver = new ReceivePort() + ..asBroadcastStream() + ..listen((event) { int playerId = int.parse(event[0]); String playerEvent = event[1]; - switch(playerEvent) { - case 'playbackEvent': { - players[playerId]!.playback.isPlaying = event[2] == '1' ? true: false; - players[playerId]!.playback.isSeekable = event[3] == '1' ? true: false; - players[playerId]!.playback.isCompleted = false; - players[playerId]!.playbackController.add(players[playerId]!.playback); - break; - } - case 'positionEvent': { - players[playerId]!.position.position = Duration(milliseconds: int.parse(event[3])); - players[playerId]!.position.duration = Duration(milliseconds: int.parse(event[4])); - players[playerId]!.positionController.add(players[playerId]!.position); - break; - } - case 'openEvent': { - players[playerId]!.current.index = int.parse(event[2]); - players[playerId]!.current.isPlaylist = event[3] == '1' ? true: false; - int mediasLength = event.length - 4; - List medias = []; - for (int index = 4; index < 4 + mediasLength; index++) { - switch (event[index]) { - case 'MediaType.file': { - medias.add( - Media.file(File(event[index + 1])) - ); - break; - } - case 'MediaType.network': { - medias.add( - Media.network(Uri.parse(event[index + 1])) - ); - break; - } - case 'MediaType.directShow': { - Media media = Media(); - media.mediaType = MediaType.directShow; - media.resource = event[index + 1]; - medias.add(media); - break; + switch (playerEvent) { + case 'playbackEvent': + { + players[playerId]!.playback.isPlaying = + event[2] == '1' ? true : false; + players[playerId]!.playback.isSeekable = + event[3] == '1' ? true : false; + players[playerId]!.playback.isCompleted = false; + players[playerId]! + .playbackController + .add(players[playerId]!.playback); + break; + } + case 'positionEvent': + { + players[playerId]!.position.position = + Duration(milliseconds: int.parse(event[3])); + players[playerId]!.position.duration = + Duration(milliseconds: int.parse(event[4])); + players[playerId]! + .positionController + .add(players[playerId]!.position); + break; + } + case 'openEvent': + { + players[playerId]!.current.index = int.parse(event[2]); + players[playerId]!.current.isPlaylist = + event[3] == '1' ? true : false; + int mediasLength = event.length - 4; + List medias = []; + for (int index = 4; index < 4 + mediasLength; index++) { + switch (event[index]) { + case 'MediaType.file': + { + medias.add(Media.file(File(event[index + 1]))); + break; + } + case 'MediaType.network': + { + medias.add(Media.network(Uri.parse(event[index + 1]))); + break; + } + case 'MediaType.directShow': + { + Media media = Media(); + media.mediaType = MediaType.directShow; + media.resource = event[index + 1]; + medias.add(media); + break; + } } } + players[playerId]!.current.medias = medias; + players[playerId]!.current.media = + medias[players[playerId]!.current.index!]; + players[playerId]!.currentController.add(players[playerId]!.current); + break; + } + case 'completeEvent': + { + players[playerId]!.playback.isCompleted = + event[2] == '1' ? true : false; + players[playerId]! + .playbackController + .add(players[playerId]!.playback); + break; + } + case 'volumeEvent': + { + players[playerId]!.general.volume = double.parse(event[2]); + players[playerId]!.generalController.add(players[playerId]!.general); + break; + } + case 'rateEvent': + { + players[playerId]!.general.rate = double.parse(event[2]); + players[playerId]!.generalController.add(players[playerId]!.general); + break; } - players[playerId]!.current.medias = medias; - players[playerId]!.current.media = medias[players[playerId]!.current.index!]; - players[playerId]!.currentController.add(players[playerId]!.current); - break; - } - case 'completeEvent': { - players[playerId]!.playback.isCompleted = event[2] == '1' ? true: false; - players[playerId]!.playbackController.add(players[playerId]!.playback); - break; - } - case 'volumeEvent': { - players[playerId]!.general.volume = double.parse(event[2]); - players[playerId]!.generalController.add(players[playerId]!.general); - break; - } - case 'rateEvent': { - players[playerId]!.general.rate = double.parse(event[2]); - players[playerId]!.generalController.add(players[playerId]!.general); - break; - } } - } -); - + }); extension NativeTypes on List { Pointer> toNativeUtf8Array() { - final List> listPointer = this.map((String string) => string.toNativeUtf8()).toList().cast>(); - final Pointer> pointerPointer = calloc.allocate(this.join('').length); - for (int index = 0; index < this.length; index++) pointerPointer[index] = listPointer[index]; + final List> listPointer = this + .map((String string) => string.toNativeUtf8()) + .toList() + .cast>(); + final Pointer> pointerPointer = + calloc.allocate(this.join('').length); + for (int index = 0; index < this.length; index++) + pointerPointer[index] = listPointer[index]; return pointerPointer; } } diff --git a/ffi/lib/src/internal/initializer.dart b/ffi/lib/src/internal/initializer.dart index 7ce8c805..ac70d40f 100644 --- a/ffi/lib/src/internal/initializer.dart +++ b/ffi/lib/src/internal/initializer.dart @@ -3,14 +3,18 @@ import 'package:dart_vlc_ffi/src/internal/ffi.dart'; import 'package:dart_vlc_ffi/src/internal/dynamiclibrary.dart'; import 'package:dart_vlc_ffi/src/internal/typedefs/callback.dart'; - class DartVLC { - static void initialize(String dynamicLibraryPath) { if (!isInitialized) { dynamicLibrary = DynamicLibrary.open(dynamicLibraryPath); - RegisterPostCObjectDart registerPostCObject = dynamicLibrary.lookup>('RegisterDart_PostCObject').asFunction(); - RegisterCallbackPortDart registerCallbackPort = dynamicLibrary.lookup>('RegisterDart_CallbackPort').asFunction(); + RegisterPostCObjectDart registerPostCObject = dynamicLibrary + .lookup>( + 'RegisterDart_PostCObject') + .asFunction(); + RegisterCallbackPortDart registerCallbackPort = dynamicLibrary + .lookup>( + 'RegisterDart_CallbackPort') + .asFunction(); registerPostCObject(NativeApi.postCObject); registerCallbackPort(receiver.sendPort.nativePort); isInitialized = true; diff --git a/ffi/lib/src/internal/typedefs/broadcast.dart b/ffi/lib/src/internal/typedefs/broadcast.dart index 4edfcde6..94f83146 100644 --- a/ffi/lib/src/internal/typedefs/broadcast.dart +++ b/ffi/lib/src/internal/typedefs/broadcast.dart @@ -3,8 +3,28 @@ import 'package:ffi/ffi.dart'; /// Following typedef is used for: /// Broadcast::create -typedef BroadcastCreateCXX = Void Function(Int32 id, Pointer type, Pointer resource, Pointer access, Pointer mux, Pointer dst, Pointer vcodec, Int32 vb, Pointer acodec, Int32 ab); -typedef BroadcastCreateDart = void Function(int id, Pointer type, Pointer resource, Pointer access, Pointer mux, Pointer dst, Pointer vcodec, int vb, Pointer acodec, int ab); +typedef BroadcastCreateCXX = Void Function( + Int32 id, + Pointer type, + Pointer resource, + Pointer access, + Pointer mux, + Pointer dst, + Pointer vcodec, + Int32 vb, + Pointer acodec, + Int32 ab); +typedef BroadcastCreateDart = void Function( + int id, + Pointer type, + Pointer resource, + Pointer access, + Pointer mux, + Pointer dst, + Pointer vcodec, + int vb, + Pointer acodec, + int ab); /// Following typedef is used for: /// Broadcast::start diff --git a/ffi/lib/src/internal/typedefs/callback.dart b/ffi/lib/src/internal/typedefs/callback.dart index f837b2b6..30493a65 100644 --- a/ffi/lib/src/internal/typedefs/callback.dart +++ b/ffi/lib/src/internal/typedefs/callback.dart @@ -1,7 +1,11 @@ import 'dart:ffi'; -typedef RegisterPostCObjectCXX = Void Function(Pointer)>> functionPointer); -typedef RegisterPostCObjectDart = void Function(Pointer)>> functionPointer); +typedef RegisterPostCObjectCXX = Void Function( + Pointer)>> + functionPointer); +typedef RegisterPostCObjectDart = void Function( + Pointer)>> + functionPointer); typedef RegisterCallbackPortCXX = Void Function(Int64 nativePort); typedef RegisterCallbackPortDart = void Function(int nativePort); diff --git a/ffi/lib/src/internal/typedefs/chromecast.dart b/ffi/lib/src/internal/typedefs/chromecast.dart index 81cf86bc..6749d9ea 100644 --- a/ffi/lib/src/internal/typedefs/chromecast.dart +++ b/ffi/lib/src/internal/typedefs/chromecast.dart @@ -3,8 +3,10 @@ import 'package:ffi/ffi.dart'; /// Following typedef is used for: /// Chromecast::create -typedef ChromecastCreateCXX = Void Function(Int32 id, Pointer type, Pointer resource, Pointer ipAddress); -typedef ChromecastCreateDart = void Function(int id, Pointer type, Pointer resource, Pointer ipAddress); +typedef ChromecastCreateCXX = Void Function(Int32 id, Pointer type, + Pointer resource, Pointer ipAddress); +typedef ChromecastCreateDart = void Function(int id, Pointer type, + Pointer resource, Pointer ipAddress); /// Following typedef is used for: /// Chromecast::start diff --git a/ffi/lib/src/internal/typedefs/equalizer.dart b/ffi/lib/src/internal/typedefs/equalizer.dart index 41cbc1f8..e71d45e8 100644 --- a/ffi/lib/src/internal/typedefs/equalizer.dart +++ b/ffi/lib/src/internal/typedefs/equalizer.dart @@ -14,7 +14,8 @@ typedef EqualizerCreateModeDart = Pointer> Function(int mode); /// Following typedef is used for: /// Equalizer::setBandAmp typedef EqualizerSetBandAmpCXX = Void Function(Int32 id, Float band, Float amp); -typedef EqualizerSetBandAmpDart = void Function(int id, double band, double amp); +typedef EqualizerSetBandAmpDart = void Function( + int id, double band, double amp); /// Following typedef is used for: /// Equalizer::setPreAmp diff --git a/ffi/lib/src/internal/typedefs/media.dart b/ffi/lib/src/internal/typedefs/media.dart index 2f822d11..caedc8ff 100644 --- a/ffi/lib/src/internal/typedefs/media.dart +++ b/ffi/lib/src/internal/typedefs/media.dart @@ -3,5 +3,7 @@ import 'package:ffi/ffi.dart'; /// Following typedef is used for: /// Media::parse -typedef MediaParseCXX = Pointer> Function(Pointer type, Pointer resource, Int32 timeout); -typedef MediaParseDart = Pointer> Function(Pointer type, Pointer resource, int timeout); +typedef MediaParseCXX = Pointer> Function( + Pointer type, Pointer resource, Int32 timeout); +typedef MediaParseDart = Pointer> Function( + Pointer type, Pointer resource, int timeout); diff --git a/ffi/lib/src/internal/typedefs/player.dart b/ffi/lib/src/internal/typedefs/player.dart index 237a68bf..ca71bb94 100644 --- a/ffi/lib/src/internal/typedefs/player.dart +++ b/ffi/lib/src/internal/typedefs/player.dart @@ -3,13 +3,25 @@ import 'package:ffi/ffi.dart'; /// Following typedef is used for: /// Player::create -typedef PlayerCreateCXX = Void Function(Int32 id, Int32 videoHeight, Int32 videoWidth, Int32 commandlineArgumentsCount, Pointer> commandlineArguments); -typedef PlayerCreateDart = void Function(int id, int videoHeight, int videoWidth, int commandlineArgumentsCount, Pointer> commandlineArguments); +typedef PlayerCreateCXX = Void Function( + Int32 id, + Int32 videoHeight, + Int32 videoWidth, + Int32 commandlineArgumentsCount, + Pointer> commandlineArguments); +typedef PlayerCreateDart = void Function( + int id, + int videoHeight, + int videoWidth, + int commandlineArgumentsCount, + Pointer> commandlineArguments); /// Following typedef is used for: /// Player::open -typedef PlayerOpenCXX = Void Function(Int32 id, Int32 autoStart, Pointer> source, Int32 sourceSize); -typedef PlayerOpenDart = void Function(int id, int autoStart, Pointer> source, int sourceSize); +typedef PlayerOpenCXX = Void Function( + Int32 id, Int32 autoStart, Pointer> source, Int32 sourceSize); +typedef PlayerOpenDart = void Function( + int id, int autoStart, Pointer> source, int sourceSize); /// Following typedef is used for: /// Player::play @@ -43,7 +55,8 @@ typedef PlayerSetRateDart = void Function(int id, double volume); /// Following typedef is used for: /// Player::setUserAgent -typedef PlayerSetUserAgentCXX = Void Function(Int32 id, Pointer userAgent); +typedef PlayerSetUserAgentCXX = Void Function( + Int32 id, Pointer userAgent); typedef PlayerSetUserAgentDart = void Function(int id, Pointer userAgent); /// Following typedef is used for: @@ -53,8 +66,10 @@ typedef PlayerSetEqualizerDart = void Function(int id, int equalizerId); /// Following typedef is used for: /// Player::setDevice -typedef PlayerSetDeviceCXX = Void Function(Int32 id, Pointer deviceId, Pointer deviceName); -typedef PlayerSetDeviceDart = void Function(int id, Pointer deviceId, Pointer deviceName); +typedef PlayerSetDeviceCXX = Void Function( + Int32 id, Pointer deviceId, Pointer deviceName); +typedef PlayerSetDeviceDart = void Function( + int id, Pointer deviceId, Pointer deviceName); /// Following typedef is used for: /// Player::setPlaylistMode @@ -63,8 +78,10 @@ typedef PlayerSetPlaylistModeDart = void Function(int id, Pointer mode); /// Following typedef is used for: /// Player::add -typedef PlayerAddCXX = Void Function(Int32 id, Pointer type, Pointer resource); -typedef PlayerAddDart = void Function(int id, Pointer type, Pointer resource); +typedef PlayerAddCXX = Void Function( + Int32 id, Pointer type, Pointer resource); +typedef PlayerAddDart = void Function( + int id, Pointer type, Pointer resource); /// Following typedef is used for: /// Player::remove @@ -73,10 +90,14 @@ typedef PlayerRemoveDart = void Function(int id, int index); /// Following typedef is used for: /// Player::jump -typedef PlayerInsertCXX = Void Function(Int32 id, Int32 index, Pointer type, Pointer resource); -typedef PlayerInsertDart = void Function(int id, int index, Pointer type, Pointer resource); +typedef PlayerInsertCXX = Void Function( + Int32 id, Int32 index, Pointer type, Pointer resource); +typedef PlayerInsertDart = void Function( + int id, int index, Pointer type, Pointer resource); /// Following typedef is used for: /// Player::move -typedef PlayerMoveCXX = Void Function(Int32 id, Int32 initialIndex, Int32 finalIndex); -typedef PlayerMoveDart = void Function(int id, int initialIndex, int finalIndex); +typedef PlayerMoveCXX = Void Function( + Int32 id, Int32 initialIndex, Int32 finalIndex); +typedef PlayerMoveDart = void Function( + int id, int initialIndex, int finalIndex); diff --git a/ffi/lib/src/internal/typedefs/record.dart b/ffi/lib/src/internal/typedefs/record.dart index 6d440aae..e8fa93f6 100644 --- a/ffi/lib/src/internal/typedefs/record.dart +++ b/ffi/lib/src/internal/typedefs/record.dart @@ -3,8 +3,10 @@ import 'package:ffi/ffi.dart'; /// Following typedef is used for: /// Record::create -typedef RecordCreateCXX = Void Function(Int32 id, Pointer savingFile, Pointer type, Pointer resource); -typedef RecordCreateDart = void Function(int id, Pointer savingFile, Pointer type, Pointer resource); +typedef RecordCreateCXX = Void Function(Int32 id, Pointer savingFile, + Pointer type, Pointer resource); +typedef RecordCreateDart = void Function(int id, Pointer savingFile, + Pointer type, Pointer resource); /// Following typedef is used for: /// Record::start diff --git a/ffi/lib/src/mediaSource/media.dart b/ffi/lib/src/mediaSource/media.dart index dc6a087d..f92f4496 100644 --- a/ffi/lib/src/mediaSource/media.dart +++ b/ffi/lib/src/mediaSource/media.dart @@ -26,7 +26,7 @@ class _Media extends Media {} /// ```dart /// Media media = await Media.network('http://alexmercerind.github.io/music.mp3'); /// ``` -/// +/// /// class Media extends MediaSource { MediaSourceType mediaSourceType = MediaSourceType.media; @@ -66,7 +66,8 @@ class Media extends MediaSource { } /// Makes [Media] object from direct show. - static Media directShow({String vdev = '', String adev = '', required int liveCaching}) { + static Media directShow( + {String vdev = '', String adev = '', required int liveCaching}) { Media media = new _Media(); media.mediaType = MediaType.directShow; media.resource = @@ -76,10 +77,9 @@ class Media extends MediaSource { void parse(Duration timeout) { Pointer> metas = MediaFFI.parse( - this.mediaType.toString().toNativeUtf8(), - this.resource.toNativeUtf8(), - timeout.inSeconds - ); + this.mediaType.toString().toNativeUtf8(), + this.resource.toNativeUtf8(), + timeout.inSeconds); this.metas['title'] = metas.elementAt(0).value.toDartString(); this.metas['artist'] = metas.elementAt(1).value.toDartString(); this.metas['genre'] = metas.elementAt(2).value.toDartString(); diff --git a/ffi/lib/src/mediaSource/playlist.dart b/ffi/lib/src/mediaSource/playlist.dart index 6ac8e748..0328bfb6 100644 --- a/ffi/lib/src/mediaSource/playlist.dart +++ b/ffi/lib/src/mediaSource/playlist.dart @@ -24,7 +24,6 @@ import 'package:dart_vlc_ffi/src/enums/mediaSourceType.dart'; /// ``` /// class Playlist extends MediaSource { - /// [List] of [Media] present in the playlist. List medias; MediaSourceType mediaSourceType = MediaSourceType.playlist; diff --git a/ffi/lib/src/player.dart b/ffi/lib/src/player.dart index 39637f83..c4b7db59 100644 --- a/ffi/lib/src/player.dart +++ b/ffi/lib/src/player.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'package:ffi/ffi.dart'; -import 'package:dart_vlc_ffi/dart_vlc.dart'; +import 'package:dart_vlc_ffi/dart_vlc_ffi.dart'; import 'package:dart_vlc_ffi/src/equalizer.dart'; import 'package:dart_vlc_ffi/src/internal/ffi.dart'; import 'package:dart_vlc_ffi/src/playerState/playerState.dart'; @@ -11,7 +11,6 @@ import 'package:dart_vlc_ffi/src/device.dart'; /// Keeps various [Player] instances to manage event callbacks. Map players = {}; - /// A [Player] to open & play a [Media] or [Playlist] from file, network or asset. /// /// Use [Player] constructor to create a new instance of a [Player]. @@ -81,8 +80,13 @@ class Player { /// Player player = new Player(id: 0); /// ``` /// - Player({required this.id, int videoWidth: 0, int videoHeight: 0, List? commandlineArguments}) { - if (commandlineArguments != null) this.commandlineArguments = commandlineArguments; + Player( + {required this.id, + int videoWidth: 0, + int videoHeight: 0, + List? commandlineArguments}) { + if (commandlineArguments != null) + this.commandlineArguments = commandlineArguments; this.videoWidth = videoWidth; this.videoHeight = videoHeight; this.currentController = StreamController.broadcast(); @@ -141,11 +145,11 @@ class Player { void open(MediaSource source, {bool autoStart: true}) { if (source is Media) { PlayerFFI.open( - this.id, - autoStart ? 1: 0, - [ source.mediaType.toString(), source.resource ].toNativeUtf8Array(), - 1 - ); + this.id, + autoStart ? 1 : 0, + [source.mediaType.toString(), source.resource] + .toNativeUtf8Array(), + 1); } if (source is Playlist) { List medias = []; @@ -153,12 +157,8 @@ class Player { medias.add(media.mediaType.toString()); medias.add(media.resource); }); - PlayerFFI.open( - this.id, - autoStart ? 1: 0, - medias.toNativeUtf8Array(), - source.medias.length * 2 - ); + PlayerFFI.open(this.id, autoStart ? 1 : 0, medias.toNativeUtf8Array(), + source.medias.length * 2); } } @@ -217,7 +217,6 @@ class Player { PlayerFFI.setRate(this.id, rate); } - /// Sets user agent for dart_vlc player. void setUserAgent(String userAgent) { PlayerFFI.setUserAgent(this.id, userAgent.toNativeUtf8()); @@ -230,11 +229,8 @@ class Player { /// Appends [Media] to the [Playlist] of the [Player] instance. void add(Media source) { - PlayerFFI.add( - this.id, - source.mediaType.toString().toNativeUtf8(), - source.resource.toString().toNativeUtf8() - ); + PlayerFFI.add(this.id, source.mediaType.toString().toNativeUtf8(), + source.resource.toString().toNativeUtf8()); } /// Removes [Media] from the [Playlist] at a specific index. @@ -244,21 +240,13 @@ class Player { /// Inserts [Media] to the [Playlist] of the [Player] instance at specific index. void insert(int index, Media source) { - PlayerFFI.insert( - this.id, - index, - source.mediaType.toString().toNativeUtf8(), - source.resource.toString().toNativeUtf8() - ); + PlayerFFI.insert(this.id, index, source.mediaType.toString().toNativeUtf8(), + source.resource.toString().toNativeUtf8()); } /// Moves [Media] already present in the [Playlist] of the [Player] from [initialIndex] to [finalIndex]. void move(int initialIndex, int finalIndex) { - PlayerFFI.move( - this.id, - initialIndex, - finalIndex - ); + PlayerFFI.move(this.id, initialIndex, finalIndex); } /// Sets playback [Device] for the instance of [Player]. @@ -270,10 +258,7 @@ class Player { /// Future setDevice(Device device) async { PlayerFFI.setDevice( - this.id, - device.id.toNativeUtf8(), - device.name.toNativeUtf8() - ); + this.id, device.id.toNativeUtf8(), device.name.toNativeUtf8()); } /// Sets [Equalizer] for the [Player]. diff --git a/ffi/lib/src/record.dart b/ffi/lib/src/record.dart index 032db28f..0a3638a2 100644 --- a/ffi/lib/src/record.dart +++ b/ffi/lib/src/record.dart @@ -1,13 +1,11 @@ import 'dart:io'; import 'package:ffi/ffi.dart'; -import 'package:dart_vlc_ffi/dart_vlc.dart'; +import 'package:dart_vlc_ffi/dart_vlc_ffi.dart'; import 'package:dart_vlc_ffi/src/internal/ffi.dart'; - /// Internally used class to avoid direct creation of the object of a [Record] class. class _Record extends Record {} - class Record { /// ID for this record. late int id; @@ -19,17 +17,17 @@ class Record { late File savingFile; /// Creates a new [Record] instance. - static Record create({required int id, required Media media, required File savingFile}) { + static Record create( + {required int id, required Media media, required File savingFile}) { Record record = new _Record(); record.id = id; record.media = media; record.savingFile = savingFile; RecordFFI.create( - record.id, - savingFile.path.toNativeUtf8(), - media.mediaType.toString().toNativeUtf8(), - media.resource.toNativeUtf8() - ); + record.id, + savingFile.path.toNativeUtf8(), + media.mediaType.toString().toNativeUtf8(), + media.resource.toNativeUtf8()); return record; } From b898b801bf12eaf881ab19a29bc928ca558fdb55 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 15:25:05 +0530 Subject: [PATCH 15/27] Fixed build warnings & segmentation fault upon opening Playlist --- ffi/CreateSharedLibrary.sh | 2 +- ffi/lib/src/player.dart | 5 ++--- ffi/native/{main.cpp => dart_vlc.cpp} | 18 +++++++++--------- ffi/native/eventmanager.hpp | 2 +- ffi/pubspec.yaml | 2 +- 5 files changed, 14 insertions(+), 15 deletions(-) rename ffi/native/{main.cpp => dart_vlc.cpp} (95%) diff --git a/ffi/CreateSharedLibrary.sh b/ffi/CreateSharedLibrary.sh index 9f7c85b1..4e61c40d 100755 --- a/ffi/CreateSharedLibrary.sh +++ b/ffi/CreateSharedLibrary.sh @@ -13,7 +13,7 @@ # Assuming include directory contains libVLC++ headers. # Setting C++ standard to 17 for std::filesystem. # -g++ -c native/main.cpp -o main.o -fPIC -I./ -I./../linux/include -std=c++17 +g++ -c native/dart_vlc.cpp -o main.o -fPIC -I./ -I./../linux/include -std=c++17 # Create shared library from the generated object code. # Linking libVLC shared library. diff --git a/ffi/lib/src/player.dart b/ffi/lib/src/player.dart index c4b7db59..904c9eef 100644 --- a/ffi/lib/src/player.dart +++ b/ffi/lib/src/player.dart @@ -157,8 +157,7 @@ class Player { medias.add(media.mediaType.toString()); medias.add(media.resource); }); - PlayerFFI.open(this.id, autoStart ? 1 : 0, medias.toNativeUtf8Array(), - source.medias.length * 2); + PlayerFFI.open(this.id, autoStart ? 1 : 0, medias.toNativeUtf8Array(), source.medias.length); } } @@ -256,7 +255,7 @@ class Player { /// A playback [Device] for a [Player] instance cannot be changed in the middle of playback. /// Device will be switched once a new [Media] is played. /// - Future setDevice(Device device) async { + void setDevice(Device device) { PlayerFFI.setDevice( this.id, device.id.toNativeUtf8(), device.name.toNativeUtf8()); } diff --git a/ffi/native/main.cpp b/ffi/native/dart_vlc.cpp similarity index 95% rename from ffi/native/main.cpp rename to ffi/native/dart_vlc.cpp index fc9fb58c..afef2957 100644 --- a/ffi/native/main.cpp +++ b/ffi/native/dart_vlc.cpp @@ -162,7 +162,7 @@ EXPORT void Player_setPlaylistMode(int id, const char* mode) { playlistMode = PlaylistMode::repeat; else if (strcmp(mode, "playlistMode.loop") == 0) playlistMode = PlaylistMode::loop; - else if (strcmp(mode, "playlistMode.single") == 0) + else playlistMode = PlaylistMode::single; player->setPlaylistMode(playlistMode); } @@ -174,7 +174,7 @@ EXPORT void Player_add(int id, const char* type, const char* resource) { media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) media = Media::network(0, resource, false); - else if (strcmp(type, "MediaType.directShow") == 0) + else media = Media::directShow(0, resource); player->add(media); } @@ -191,7 +191,7 @@ EXPORT void Player_insert(int id, int index, const char* type, const char* resou media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) media = Media::network(0, resource, false); - else if (strcmp(type, "MediaType.directShow") == 0) + else media = Media::directShow(0, resource); player->insert(index, media); } @@ -207,7 +207,7 @@ EXPORT char** Media_parse(const char* type, const char* resource, int timeout) { media = Media::file(0, resource, true); else if (strcmp(type, "MediaType.network") == 0) media = Media::network(0, resource, true); - else if (strcmp(type, "MediaType.directShow") == 0) + else media = Media::directShow(0, resource); char** metas = new char*[media->metas.size()]; int index = 0; @@ -224,7 +224,7 @@ EXPORT void Broadcast_create(int id, const char* type, const char* resource, con media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) media = Media::network(0, resource, false); - else if (strcmp(type, "MediaType.directShow") == 0) + else media = Media::directShow(0, resource); BroadcastConfiguration configuration( access, @@ -235,7 +235,7 @@ EXPORT void Broadcast_create(int id, const char* type, const char* resource, con acodec, ab ); - Broadcast* broadcast = broadcasts->get(id, media, &configuration); + broadcasts->get(id, media, &configuration); } EXPORT void Broadcast_start(int id) { @@ -254,9 +254,9 @@ EXPORT void Chromecast_create(int id, const char* type, const char* resource, co media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) media = Media::network(0, resource, false); - else if (strcmp(type, "MediaType.directShow") == 0) + else media = Media::directShow(0, resource); - Chromecast* chromecast = chromecasts->get(id, media, ipAddress); + chromecasts->get(id, media, ipAddress); } EXPORT void Chromecast_start(int id) { @@ -275,7 +275,7 @@ EXPORT void Record_create(int id, const char* savingFile, const char* type, cons media = Media::file(0, resource, false); else if (strcmp(type, "MediaType.network") == 0) media = Media::network(0, resource, false); - else if (strcmp(type, "MediaType.directShow") == 0) + else media = Media::directShow(0, resource); records->get(id, media, savingFile); } diff --git a/ffi/native/eventmanager.hpp b/ffi/native/eventmanager.hpp index 7426c042..5a47cd96 100644 --- a/ffi/native/eventmanager.hpp +++ b/ffi/native/eventmanager.hpp @@ -8,7 +8,7 @@ * GNU Lesser General Public License v2.1 */ -#include "../dartvlc/main.cpp" +#include "../../dartvlc/main.cpp" #include "callbackmanager.hpp" #ifdef __cplusplus diff --git a/ffi/pubspec.yaml b/ffi/pubspec.yaml index 3761560a..b9a94727 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.1 +version: 0.0.2 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 From 9d53f286f8f3235ac7ba74fe24b152701f985315 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 15:25:31 +0530 Subject: [PATCH 16/27] Code formatting --- ffi/lib/src/player.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ffi/lib/src/player.dart b/ffi/lib/src/player.dart index 904c9eef..6ae9f6c2 100644 --- a/ffi/lib/src/player.dart +++ b/ffi/lib/src/player.dart @@ -157,7 +157,8 @@ class Player { medias.add(media.mediaType.toString()); medias.add(media.resource); }); - PlayerFFI.open(this.id, autoStart ? 1 : 0, medias.toNativeUtf8Array(), source.medias.length); + PlayerFFI.open(this.id, autoStart ? 1 : 0, medias.toNativeUtf8Array(), + source.medias.length); } } From 83db3e11a871cdb20a66d38ced53f13953120ae7 Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 15:26:49 +0530 Subject: [PATCH 17/27] [ffi] Updated changelog --- ffi/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ffi/CHANGELOG.md b/ffi/CHANGELOG.md index 0b7f6ef6..00fbbf4f 100644 --- a/ffi/CHANGELOG.md +++ b/ffi/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.0.2 + +- Fixed segmentation fault open calling `Player::open` with `Playlist` as argument. +- Fixed build warnings. + ## 0.0.1 - Initial version of dart_vlc_ffi. From c02ca734bb45ce1c533748e4bf8bfa8d2aab59ea Mon Sep 17 00:00:00 2001 From: alexmercrind Date: Mon, 5 Jul 2021 15:45:21 +0530 Subject: [PATCH 18/27] Cleared legacy platform channel code & migrated Linux to FFI --- example/lib/main.dart | 47 ++- example/pubspec.lock | 7 + lib/dart_vlc.dart | 44 ++- lib/src/broadcast.dart | 138 -------- lib/src/channel.dart | 112 ------ lib/src/chromecast.dart | 58 ---- lib/src/device.dart | 43 --- lib/src/enums/equalizerMode.dart | 23 -- lib/src/enums/mediaSourceType.dart | 8 - lib/src/enums/mediaType.dart | 14 - lib/src/enums/playlistMode.dart | 11 - lib/src/equalizer.dart | 93 ----- lib/src/mediaSource/media.dart | 177 ---------- lib/src/mediaSource/mediaSource.dart | 9 - lib/src/mediaSource/playlist.dart | 54 --- lib/src/player.dart | 398 --------------------- lib/src/playerState/playerState.dart | 46 --- lib/src/record.dart | 58 ---- lib/src/widgets/controls.dart | 78 ++--- lib/src/widgets/video.dart | 22 +- linux/CMakeLists.txt | 54 ++- linux/dart_vlc_plugin.cc | 497 +-------------------------- 22 files changed, 140 insertions(+), 1851 deletions(-) delete mode 100644 lib/src/broadcast.dart delete mode 100644 lib/src/channel.dart delete mode 100644 lib/src/chromecast.dart delete mode 100644 lib/src/device.dart delete mode 100644 lib/src/enums/equalizerMode.dart delete mode 100644 lib/src/enums/mediaSourceType.dart delete mode 100644 lib/src/enums/mediaType.dart delete mode 100644 lib/src/enums/playlistMode.dart delete mode 100644 lib/src/equalizer.dart delete mode 100644 lib/src/mediaSource/media.dart delete mode 100644 lib/src/mediaSource/mediaSource.dart delete mode 100644 lib/src/mediaSource/playlist.dart delete mode 100644 lib/src/player.dart delete mode 100644 lib/src/playerState/playerState.dart delete mode 100644 lib/src/record.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 5c6082b7..4e436a5d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,15 +4,16 @@ import 'package:flutter/material.dart'; import 'package:dart_vlc/dart_vlc.dart'; void main() { - runApp(DartVLC()); + DartVLC.initialize(); + runApp(DartVLCExample()); } -class DartVLC extends StatefulWidget { +class DartVLCExample extends StatefulWidget { @override - _DartVLCState createState() => _DartVLCState(); + DartVLCExampleState createState() => DartVLCExampleState(); } -class _DartVLCState extends State { +class DartVLCExampleState extends State { Player player = Player( id: 0, videoWidth: 480, @@ -51,10 +52,10 @@ class _DartVLCState extends State { @override Future didChangeDependencies() async { super.didChangeDependencies(); - this.devices = await Devices.all; - Equalizer equalizer = await Equalizer.createMode(EqualizerMode.live); - await equalizer.setPreAmp(10.0); - await equalizer.setBandAmp(31.25, 10.0); + this.devices = Devices.all; + Equalizer equalizer = Equalizer.createMode(EqualizerMode.live); + equalizer.setPreAmp(10.0); + equalizer.setBandAmp(31.25, 10.0); player.setEqualizer(equalizer); this.setState(() {}); } @@ -177,19 +178,13 @@ class _DartVLCState extends State { if (this.mediaType == MediaType.file) { this.medias.add( - await Media.file(new File( + Media.file(new File( controller.text)), ); } else if (this.mediaType == MediaType.network) { this.medias.add( - await Media.network( - controller.text), - ); - } else if (this.mediaType == - MediaType.asset) { - this.medias.add( - await Media.asset( + Media.network( controller.text), ); } @@ -468,18 +463,13 @@ class _DartVLCState extends State { child: ElevatedButton( onPressed: () async { if (this.mediaType == MediaType.file) { - this.metasMedia = await Media.file( + this.metasMedia = Media.file( new File( this.metasController.text), parse: true); } else if (this.mediaType == MediaType.network) { - this.metasMedia = await Media.network( - this.metasController.text, - parse: true); - } else if (this.mediaType == - MediaType.asset) { - this.metasMedia = await Media.asset( + this.metasMedia = Media.network( this.metasController.text, parse: true); } @@ -664,13 +654,22 @@ class _DartVLCState extends State { onReorder: (int initialIndex, int finalIndex) async { /// 🙏🙏🙏 + /// In the name of God, + /// With all due respect, + /// I ask all Flutter engineers to please fix this issue. + /// Peace. + /// 🙏🙏🙏 + /// + /// Issue: /// https://github.com/flutter/flutter/issues/24786 + /// Prevention: /// https://stackoverflow.com/a/54164333/12825435 + /// if (finalIndex > this.current.medias.length) finalIndex = this.current.medias.length; if (initialIndex < finalIndex) finalIndex--; - await this.player.move(initialIndex, finalIndex); + this.player.move(initialIndex, finalIndex); this.setState(() {}); }, scrollDirection: Axis.vertical, diff --git a/example/pubspec.lock b/example/pubspec.lock index 03f8c729..25f482c4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -36,6 +36,13 @@ packages: relative: true source: path version: "0.0.8" + dart_vlc_ffi: + dependency: transitive + description: + name: dart_vlc_ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" ffi: dependency: transitive description: diff --git a/lib/dart_vlc.dart b/lib/dart_vlc.dart index 38104860..faab2caa 100644 --- a/lib/dart_vlc.dart +++ b/lib/dart_vlc.dart @@ -1,25 +1,39 @@ /* * dart_vlc: A media playback library for Dart & Flutter. Based on libVLC & libVLC++. * - * Hitesh Kumar Saini, Domingo Montesdeoca Gonzalez & contributors. + * Hitesh Kumar Saini * https://github.com/alexmercerind * alexmercerind@gmail.com * * GNU Lesser General Public License v2.1 */ -export 'package:dart_vlc/src/player.dart'; -export 'package:dart_vlc/src/equalizer.dart'; -export 'package:dart_vlc/src/broadcast.dart'; -export 'package:dart_vlc/src/record.dart'; -export 'package:dart_vlc/src/chromecast.dart'; +import 'dart:io'; + +import 'package:dart_vlc_ffi/dart_vlc_ffi.dart' as FFI; +export 'package:dart_vlc_ffi/dart_vlc_ffi.dart' hide DartVLC; + export 'package:dart_vlc/src/widgets/video.dart'; -export 'package:dart_vlc/src/device.dart'; -export 'package:dart_vlc/src/playerState/playerState.dart'; -export 'package:dart_vlc/src/mediaSource/mediaSource.dart'; -export 'package:dart_vlc/src/mediaSource/media.dart'; -export 'package:dart_vlc/src/mediaSource/playlist.dart'; -export 'package:dart_vlc/src/enums/equalizerMode.dart'; -export 'package:dart_vlc/src/enums/mediaSourceType.dart'; -export 'package:dart_vlc/src/enums/mediaType.dart'; -export 'package:dart_vlc/src/enums/playlistMode.dart'; + +/// Initializes the DartVLC plugin. +/// +/// ```dart +/// void main() { +/// DartVLC.initialize(); +/// runApp(MyApp()); +/// } +/// ``` +/// +abstract class DartVLC { + static void initialize() { + if (Platform.isLinux) { + String directory = Platform.resolvedExecutable + .split('/') + .sublist(0, Platform.resolvedExecutable.split('/').length - 1) + .join('/'); + FFI.DartVLC.initialize( + directory + '/lib/' + 'libdartvlc.so' + ); + } + } +} diff --git a/lib/src/broadcast.dart b/lib/src/broadcast.dart deleted file mode 100644 index c8a61873..00000000 --- a/lib/src/broadcast.dart +++ /dev/null @@ -1,138 +0,0 @@ -import 'package:dart_vlc/src/channel.dart'; -import 'package:dart_vlc/src/mediaSource/media.dart'; - -/// Internally used class to avoid direct creation of the object of a [Broadcast] class. -class _Broadcast extends Broadcast {} - -/// Used to declare a [Broadcast] configuration. -/// -/// An example configuration for a [Broadcast] can be as follows. -/// -/// ```dart -/// new BroadcastConfiguration( -/// access: 'http', -/// mux: 'mpeg1', -/// dst: '127.0.0.1:8080', -/// vcodec: 'mp1v', -/// vb: 1024, -/// acodec: 'mpga', -/// ab: 128, -/// ); -/// ``` -class BroadcastConfiguration { - /// Type of access for [Broadcast] e.g. `http`. - final String access; - - /// MUX e.g. `mpeg1`. - final String mux; - - /// Destination e.g. `127.0.0.1:8080`. - final String dst; - - /// Video codec for transcoding the broadcast e.g. `mp1v`. - final String vcodec; - - /// Video bitrate for transcoding the broadcast. - final int vb; - - /// Audio codec for transcoding the broadcast e.g. `mpga`. - final String acodec; - - /// Audio bitrate for transcoding the broadcast. - final int ab; - - const BroadcastConfiguration({ - required this.access, - required this.mux, - required this.dst, - required this.vcodec, - required this.vb, - required this.acodec, - required this.ab, - }); - - Map toMap() => { - 'access': this.access, - 'mux': this.mux, - 'dst': this.dst, - 'vcodec': this.vcodec, - 'vb': this.vb, - 'acodec': this.acodec, - 'ab': this.ab, - }; -} - -/// Creates new [Broadcast] for a [Media]. -/// -/// Example creation can be as follows. -/// -/// ```dart -/// Broadcast broadcast = await Broadcast.create( -/// id: 0, -/// media: await Media.file(new File('C:/music.ogg')), -/// configuration: new BroadcastConfiguration( -/// access: 'http', -/// mux: 'mpeg1', -/// dst: '127.0.0.1:8080', -/// vcodec: 'mp1v', -/// vb: 1024, -/// acodec: 'mpga', -/// ab: 128, -/// ), -/// ); -/// -/// broadcast.start(); -/// ``` -/// -/// Call [Broadcast.dispose] for releasing the resources. -/// -abstract class Broadcast { - /// ID for this broadcast. - late int id; - - /// Broadcasting [Media]. - late Media media; - - /// Configuration of this broadcast. - late BroadcastConfiguration configuration; - - /// Creates a new [Broadcast] instance. - static Future create( - {required int id, - required Media media, - required BroadcastConfiguration configuration}) async { - Broadcast broadcast = new _Broadcast(); - broadcast.id = id; - broadcast.media = media; - broadcast.configuration = configuration; - await channel.invokeMethod( - 'Broadcast.create', - { - 'id': id, - 'media': media.toMap(), - 'configuration': configuration.toMap(), - }, - ); - return broadcast; - } - - /// Starts broadcasting the [Media]. - Future start() async { - await channel.invokeMethod( - 'Broadcast.start', - { - 'id': id, - }, - ); - } - - /// Disposes this instance of [Broadcast]. - Future dispose() async { - await channel.invokeMethod( - 'Broadcast.dispose', - { - 'id': id, - }, - ); - } -} diff --git a/lib/src/channel.dart b/lib/src/channel.dart deleted file mode 100644 index 08dd34c6..00000000 --- a/lib/src/channel.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:dart_vlc/src/player.dart'; -import 'package:dart_vlc/src/widgets/video.dart'; -import 'package:dart_vlc/src/widgets/controls.dart'; -import 'package:dart_vlc/src/mediaSource/media.dart'; - -/// Internally used map to keep [Player] instances & manage event streams. -Map players = {}; - -/// Internally used map to keep [ControlState] keys. -Map> controls = {}; - -/// Internally used map to keep [Media] instances & manage [Media.metas]. -Map> mediaMetas = {}; - -/// Internally used map to keep [Media] instances & manage [Media.extras]. -Map> mediaExtras = {}; - -/// Platform channel for invoking methods & handling calls. -final MethodChannel channel = new MethodChannel('dart_vlc') - ..setMethodCallHandler((MethodCall methodCall) async { - switch (methodCall.method) { - case 'playerState': - { - int id = methodCall.arguments['id']; - switch (methodCall.arguments['type']) { - case 'openEvent': - { - players[id]!.current.index = methodCall.arguments['index']; - players[id]!.current.medias = methodCall.arguments['medias'] - .map( - (media) => Media.fromMap(media), - ) - .toList() - .cast(); - players[id]!.current.media = - players[id]!.current.medias[players[id]!.current.index!]; - players[id]!.current.isPlaylist = - methodCall.arguments['isPlaylist']; - players[id]!.currentController.add(players[id]!.current); - players[id]!.playback.isCompleted = false; - players[id]!.playbackController.add(players[id]!.playback); - break; - } - case 'positionEvent': - { - players[id]!.position.position = Duration( - milliseconds: methodCall.arguments['position'] ?? 0); - players[id]!.position.duration = Duration( - milliseconds: methodCall.arguments['duration'] ?? 0); - players[id]!.positionController.add(players[id]!.position); - break; - } - case 'playbackEvent': - { - players[id]!.playback.isPlaying = - methodCall.arguments['isPlaying']; - players[id]!.playback.isSeekable = - methodCall.arguments['isSeekable']; - players[id]!.playbackController.add(players[id]!.playback); - controls[id]?.currentState?.setPlaybackMode(players[id]!.playback.isPlaying); - break; - } - case 'completeEvent': - { - players[id]!.playback.isCompleted = - methodCall.arguments['isCompleted']; - players[id]!.playbackController.add(players[id]!.playback); - controls[id]?.currentState?.setPlaybackMode(false); - break; - } - case 'volumeEvent': - { - players[id]!.general.volume = methodCall.arguments['volume']; - players[id]!.generalController.add(players[id]!.general); - break; - } - case 'rateEvent': - { - players[id]!.general.rate = methodCall.arguments['rate']; - players[id]!.generalController.add(players[id]!.general); - break; - } - case 'exceptionEvent': - { - /* TODO: Deal with exception case. */ - break; - } - default: - break; - } - break; - } - case 'videoFrame': - { - int playerId = methodCall.arguments['id']; - if (videoStreamControllers.containsKey(playerId)) { - VideoFrame frame = new VideoFrame( - playerId: playerId, - videoWidth: methodCall.arguments['videoWidth'], - videoHeight: methodCall.arguments['videoHeight'], - byteArray: methodCall.arguments['byteArray'], - ); - videoStreamControllers[playerId]!.add(frame); - } - break; - } - default: - break; - } - }); diff --git a/lib/src/chromecast.dart b/lib/src/chromecast.dart deleted file mode 100644 index 578f3acd..00000000 --- a/lib/src/chromecast.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:dart_vlc/dart_vlc.dart'; -import 'package:dart_vlc/src/channel.dart'; - - -/// Internally used class to avoid direct creation of the object of a [Chromecast] class. -class _Chromecast extends Chromecast {} - - -class Chromecast { - /// ID for this [Chromecast]. - late int id; - - /// Sending from [Media]. - late Media media; - - /// IP address of your chromecast: `192.168.1.XXX` - late String ipAddress; - - /// Creates a new [Chromecast] instance. - static Future create( - {required int id, - required Media media, - required String ipAddress}) async { - Chromecast chromecast = new _Chromecast(); - chromecast.id = id; - chromecast.media = media; - chromecast.ipAddress = ipAddress; - await channel.invokeMethod( - 'Chromecast.create', - { - 'id': id, - 'media': media.toMap(), - 'ipAddress': ipAddress, - }, - ); - return chromecast; - } - - /// Start sending [Media] content to chromecast - Future send() async { - await channel.invokeMethod( - 'Chromecast.send', - { - 'id': id, - }, - ); - } - - /// Disposes this instance of [Chromecast]. - Future stop() async { - await channel.invokeMethod( - 'Chromecast.dispose', - { - 'id': id, - }, - ); - } -} diff --git a/lib/src/device.dart b/lib/src/device.dart deleted file mode 100644 index fbd5d0ed..00000000 --- a/lib/src/device.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:io'; -import 'package:dart_vlc/src/channel.dart'; - -/// Represents a playback [Device] for the [Player]. -class Device { - /// ID corresponding to the [Device]. - String id; - - /// Name of the [Device]. - String name; - - Device(this.id, this.name); - - /// Internally used method to easily transform data for sending through Platform channel. - static Device fromMap(dynamic map) => Device( - Platform.isWindows - ? (map['id'] != '' - ? '{0.0.0.00000000}.' + map['id'].toLowerCase() - : '') - : map['id'], - map['name'], - ); - - /// Internally used method to easily transform data for sending through Platform channel. - Map toMap() => { - 'id': this.id, - 'name': this.name, - }; -} - -/// [Devices.all] getter is used to get [List] of all available [Device] for playback in the [Player]. -class Devices { - /// Gets [List] of all available playback [Device]. - static Future> get all async { - dynamic devices = await channel.invokeMethod('Devices.all', {}); - return devices - .map( - (dynamic device) => Device.fromMap(device), - ) - .toList() - .cast(); - } -} diff --git a/lib/src/enums/equalizerMode.dart b/lib/src/enums/equalizerMode.dart deleted file mode 100644 index ebe8f4a3..00000000 --- a/lib/src/enums/equalizerMode.dart +++ /dev/null @@ -1,23 +0,0 @@ - - -/// Predefined presets for [Equalizer]. -enum EqualizerMode { - flat, - classical, - club, - dance, - fullBass, - fullBassAndTreble, - fullTreble, - headphones, - largeHall, - live, - party, - pop, - reggae, - rock, - ska, - soft, - softRock, - techno -} diff --git a/lib/src/enums/mediaSourceType.dart b/lib/src/enums/mediaSourceType.dart deleted file mode 100644 index 2db8f5c1..00000000 --- a/lib/src/enums/mediaSourceType.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// Enum to specify the type of [MediaSource] passed in [Player.open]. -enum MediaSourceType { - /// A single [Media]. - media, - - /// A [Playlist] containing multiple [Media] to play sequencially. - playlist, -} diff --git a/lib/src/enums/mediaType.dart b/lib/src/enums/mediaType.dart deleted file mode 100644 index b6c553c2..00000000 --- a/lib/src/enums/mediaType.dart +++ /dev/null @@ -1,14 +0,0 @@ -/// Enum to specify the type of [Media]. -enum MediaType { - /// A [Media] opened from a [File]. - file, - - /// A [Media] opened from a [Uri]. - network, - - /// A [Media] opened from assets. - asset, - - /// A [Media] created from direct show. - directShow, -} diff --git a/lib/src/enums/playlistMode.dart b/lib/src/enums/playlistMode.dart deleted file mode 100644 index 5816da5e..00000000 --- a/lib/src/enums/playlistMode.dart +++ /dev/null @@ -1,11 +0,0 @@ -/// Enum to specify the playback mode of [Playlist]. -enum PlaylistMode { - /// Indicates single [Playlist] mode - single, - - /// Indicates loop [Playlist] mode - loop, - - /// Indicates repeat [Playlist] mode - repeat -} diff --git a/lib/src/equalizer.dart b/lib/src/equalizer.dart deleted file mode 100644 index 62df1db4..00000000 --- a/lib/src/equalizer.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:dart_vlc/src/channel.dart'; -import 'package:dart_vlc/src/enums/equalizerMode.dart'; - - -/// Internally used class to avoid direct creation of the object of a [Equalizer] class. -class _Equalizer extends Equalizer {} - - -/// Defines an [Equalizer] instance for usage in a [Player]. -/// -/// Use [Equalizer.createEmpty] for creating a default equalizer & [Equalizer.createMode] for creating an equalizer using a preset from [EqualizerMode]. -/// -/// Example -/// -/// ```dart -/// Player player = new Player(id: 0); -/// Equalizer equalizer = Equalizer.createMode(EqualizerMode.party); -/// player.setEqualizer(equalizer); -/// ``` -/// -class Equalizer { - /// Unique Id associated with this [Equalizer]. - late int id; - /// Preamp value of the [Equalizer]. Use [Equalizer.setPreAmp] to change value. - double preAmp = 0.0; - /// Values of amps of various bands in [Equalizer]. Use [Equalizer.setBandAmp] to change values. - Map bandAmps = {}; - /// Preset if [Equalizer] is initialized using [Equalizer.createMode], else `null`. - EqualizerMode? mode; - - /// Creates a default [Equalizer] instance with all values set to `0.0`. - static Future createEmpty() async { - Equalizer equalizer = new _Equalizer(); - dynamic _equalizer = await channel.invokeMethod( - 'Equalizer.createEmpty', - {}, - ); - equalizer.id = _equalizer['id']; - equalizer.preAmp = _equalizer['preAmp']; - equalizer.bandAmps = Map.from(_equalizer['bandAmps']); - return equalizer; - } - - /// Creates an [Equalizer] instance with any preset from [EqualizerMode]. - static Future createMode(EqualizerMode mode) async { - Equalizer equalizer = new _Equalizer(); - dynamic _equalizer = await channel.invokeMethod( - 'Equalizer.createMode', - { - 'mode': mode.index, - }, - ); - equalizer.id = _equalizer['id']; - equalizer.mode = mode; - equalizer.preAmp = _equalizer['preAmp']; - equalizer.bandAmps = Map.from(_equalizer['bandAmps']); - return equalizer; - } - - /// Sets value of [Equalizer.preAmp]. - /// - /// Constraints: - /// `-20.0 < amp < 20.0` - /// - Future setPreAmp(double amp) async { - await channel.invokeMethod( - 'Equalizer.setPreAmp', - { - 'id': this.id, - 'preAmp': amp, - }, - ); - this.preAmp = amp; - } - - /// Sets value of any particular band from [Equalizer.bandAmps]. - /// Band whose `amp` needs to be changed, must be present in [Equalizer.bandAmps]. - /// - /// Constraints: - /// `-20.0 < amp < 20.0` - /// - Future setBandAmp(double band, double amp) async { - await channel.invokeMethod( - 'Equalizer.setBandAmp', - { - 'id': this.id, - 'band': band, - 'amp': amp, - }, - ); - this.bandAmps[band] = amp; - } -} diff --git a/lib/src/mediaSource/media.dart b/lib/src/mediaSource/media.dart deleted file mode 100644 index 9fe4549b..00000000 --- a/lib/src/mediaSource/media.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'dart:io'; -import 'dart:typed_data'; -import 'package:flutter/services.dart'; -import 'package:path/path.dart' as _path; -import 'package:path_provider/path_provider.dart' as _path; -import 'package:dart_vlc/src/channel.dart'; -import 'package:dart_vlc/src/mediaSource/mediaSource.dart'; -import 'package:dart_vlc/src/enums/mediaSourceType.dart'; -import 'package:dart_vlc/src/enums/mediaType.dart'; - -/// Internally used class to avoid direct creation of the object of a [Media] class. -class _Media extends Media {} - -/// A media object to open inside a [Player]. -/// -/// Pass `true` to [parse] for retrieving the metadata of the [Media]. -/// [timeout] sets the time-limit for retriveing metadata. -/// [Media.metas] can be then, accessed to get the retrived metadata as `Map`. -/// -/// * A [Media] from a [File]. -/// -/// ```dart -/// Media media = await Media.file(new File('C:/music.ogg')); -/// ``` -/// -/// * A [Media] from a [Uri]. -/// -/// ```dart -/// Media media = await Media.network('http://alexmercerind.github.io/music.mp3'); -/// ``` -/// -/// * A [Media] from assets. -/// -/// ```dart -/// Media media = await Media.asset('asset/media/music.aac'); -/// ``` -/// -abstract class Media extends MediaSource { - int id = mediaExtras.length; - MediaSourceType mediaSourceType = MediaSourceType.media; - Map metas = {}; - Map extras = {}; - late MediaType mediaType; - late String resource; - - /// Makes [Media] object from a [File]. - static Future file(File file, - {bool parse: false, - Map? extras, - Duration timeout: const Duration(seconds: 10)}) async { - Media media = new _Media(); - media.mediaType = MediaType.file; - media.resource = file.path; - if (parse) { - await media.parse(timeout); - mediaMetas[media.id] = media.metas; - } - if (extras != null) { - media.extras = extras; - mediaExtras[media.id] = extras; - } - return media; - } - - /// Makes [Media] object from url. - static Future network(dynamic url, - {bool parse: false, - Map? extras, - Duration timeout: const Duration(seconds: 10)}) async { - Media media = new _Media(); - media.mediaType = MediaType.network; - if (url is Uri) - media.resource = url.toString(); - else - media.resource = url; - if (parse) { - await media.parse(timeout); - mediaMetas[media.id] = media.metas; - } - if (extras != null) { - media.extras = extras; - mediaExtras[media.id] = extras; - } - return media; - } - - /// Makes [Media] object from a asset. - static Future asset(String path, - {bool parse: false, - Map? extras, - Duration timeout: const Duration(seconds: 10)}) async { - Media media = new _Media(); - media.mediaType = MediaType.asset; - media.resource = path; - ByteData mediaByteData = await rootBundle.load(path); - Uint8List mediaBytes = mediaByteData.buffer.asUint8List(); - File mediaFile = new File(_path.join( - (await _path.getTemporaryDirectory()).path, - path, - )); - await mediaFile.create(recursive: true); - await mediaFile.writeAsBytes(mediaBytes); - if (parse) { - await media.parse(timeout); - mediaMetas[media.id] = media.metas; - } - if (extras != null) { - media.extras = extras; - mediaExtras[media.id] = extras; - } - return media; - } - - /// Makes [Media] object from direct show. - static Future directShow( - {String vdev = '', String adev = '', required int liveCaching}) async { - Media media = new _Media(); - media.mediaType = MediaType.directShow; - media.resource = - 'dshow:// :dshow-vdev=$vdev :dshow-adev=$adev :live-caching=$liveCaching'; - return media; - } - - /// Internally used method to easily transform data for sending through Platform channel. - static Media fromMap(dynamic map) { - Media media = new _Media(); - media.id = map['id']; - media.mediaType = { - 'MediaType.file': MediaType.file, - 'MediaType.network': MediaType.network, - 'MediaType.asset': MediaType.asset, - }[map['mediaType']]!; - media.resource = map['resource']; - media.metas = mediaMetas[media.id] ?? {}; - media.extras = mediaExtras[media.id] ?? {}; - return media; - } - - /// Internally used method to easily transform data for sending through Platform channel. - Map toMap() => { - 'id': this.id, - 'mediaSourceType': this.mediaSourceType.toString(), - 'mediaType': this.mediaType.toString(), - 'resource': this.resource, - }; - - Future parse(Duration timeout) async { - dynamic metas = await channel.invokeMethod('Media.parse', { - 'timeout': timeout.inMilliseconds, - 'source': this.toMap(), - }); - this.metas['title'] = metas['title']; - this.metas['artist'] = metas['artist']; - this.metas['genre'] = metas['genre']; - this.metas['copyright'] = metas['copyright']; - this.metas['album'] = metas['album']; - this.metas['trackNumber'] = metas['trackNumber']; - this.metas['description'] = metas['description']; - this.metas['rating'] = metas['rating']; - this.metas['date'] = metas['date']; - this.metas['settings'] = metas['settings']; - this.metas['url'] = metas['url']; - this.metas['language'] = metas['language']; - this.metas['nowPlaying'] = metas['nowPlaying']; - this.metas['encodedBy'] = metas['encodedBy']; - this.metas['artworkUrl'] = metas['artworkUrl']; - this.metas['trackTotal'] = metas['trackTotal']; - this.metas['director'] = metas['director']; - this.metas['season'] = metas['season']; - this.metas['episode'] = metas['episode']; - this.metas['actors'] = metas['actors']; - this.metas['albumArtist'] = metas['albumArtist']; - this.metas['discNumber'] = metas['discNumber']; - this.metas['discTotal'] = metas['discTotal']; - this.metas['duration'] = metas['duration']; - } -} diff --git a/lib/src/mediaSource/mediaSource.dart b/lib/src/mediaSource/mediaSource.dart deleted file mode 100644 index ca2dde88..00000000 --- a/lib/src/mediaSource/mediaSource.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:dart_vlc/src/enums/mediaSourceType.dart'; - -/// Parent abstract class of [Media] and [Playlist]. -abstract class MediaSource { - late MediaSourceType mediaSourceType; - - /// Internally used method to easily transform data for sending through Platform channel. - Map toMap(); -} diff --git a/lib/src/mediaSource/playlist.dart b/lib/src/mediaSource/playlist.dart deleted file mode 100644 index 169e8da6..00000000 --- a/lib/src/mediaSource/playlist.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:dart_vlc/src/enums/playlistMode.dart'; -import 'package:dart_vlc/src/mediaSource/media.dart'; -import 'package:dart_vlc/src/mediaSource/mediaSource.dart'; -import 'package:dart_vlc/src/enums/mediaSourceType.dart'; - -/// A playlist object to open inside a [Player.open]. -/// -/// Takes [List] of [Media] as parameter to open inside the [Player] instance, for playing sequencially. -/// -/// ```dart -/// new Playlist( -/// medias: [ -/// Media.file( -/// new File('C:/music.mp3'), -/// ), -/// Media.network( -/// 'https://alexmercerind.github.io/music.mp3', -/// ), -/// Media.file( -/// new File('C:/audio.mp3'), -/// ), -/// ], -/// ); -/// ``` -/// -class Playlist extends MediaSource { - MediaSourceType _mediaSourceType = MediaSourceType.playlist; - - /// [List] of [Media] present in the playlist. - List medias; - PlaylistMode playlistMode; - Playlist({required this.medias, this.playlistMode = PlaylistMode.single}); - - static Playlist fromMap(dynamic map) { - final playlistMode = { - 'PlaylistMode.single': PlaylistMode.single, - 'PlaylistMode.repeat': PlaylistMode.repeat, - 'PlaylistMode.loop': PlaylistMode.loop, - }[map['playlistMode']]!; - return new Playlist( - medias: map['medias'] - .map((media) => Media.fromMap(media)) - .toList() - .cast(), - playlistMode: playlistMode); - } - - /// Internally used method to easily transform data for sending through Platform channel. - Map toMap() => { - 'mediaSourceType': this._mediaSourceType.toString(), - 'medias': this.medias.map((Media media) => media.toMap()).toList(), - "playlistMode": playlistMode.toString() - }; -} diff --git a/lib/src/player.dart b/lib/src/player.dart deleted file mode 100644 index fff2e3dd..00000000 --- a/lib/src/player.dart +++ /dev/null @@ -1,398 +0,0 @@ -import 'dart:async'; -import 'package:dart_vlc/dart_vlc.dart'; -import 'package:dart_vlc/src/channel.dart'; -import 'package:dart_vlc/src/equalizer.dart'; -import 'package:dart_vlc/src/playerState/playerState.dart'; -import 'package:dart_vlc/src/mediaSource/media.dart'; -import 'package:dart_vlc/src/mediaSource/mediaSource.dart'; -import 'package:dart_vlc/src/device.dart'; - - -/// A [Player] to open & play a [Media] or [Playlist] from file, network or asset. -/// -/// Use [Player] constructor to create a new instance of a [Player]. -/// Provide a unique [id] while instanciating. -/// -/// ```dart -/// Player player = new Player(id: 0); -/// ``` -/// -/// If you wish to use this instance for [Video] playback, then provide [videoWidth] & [videoHeight] optional parameters. -/// Higher value may lead to degraded performance. -/// -/// ```dart -/// Player player = new Player( -/// id: 0, -/// videoWidth: 1920, -/// videoHeight: 1080, -/// ); -/// ``` -/// -/// Do not provide [videoWidth] & [videoHeight], if you wish to use the [Player] for only audio playback. -/// -/// Use various methods & event streams avaiable to control & listen to events of the playback. -/// -class Player { - /// Id associated with the [Player] instance. - final int id; - - /// Width of the [Video] frames to be extracted. Higher value may lead to degraded performance. - late int videoWidth; - - /// Height of the [Video] frames to be extracted. Higher value may lead to degraded performance. - late int videoHeight; - - /// Commandline arguments passed to this instance of [Player]. - List commandlineArguments = []; - - /// State of the current & opened [MediaSource] in [Player] instance. - CurrentState current = new CurrentState(); - - /// Stream to listen to current & opened [MediaSource] state of the [Player] instance. - late Stream currentStream; - - /// Position & duration state of the [Player] instance. - PositionState position = new PositionState(); - - /// Stream to listen to position & duration state of the [Player] instance. - late Stream positionStream; - - /// Playback state of the [Player] instance. - PlaybackState playback = new PlaybackState(); - - /// Stream to listen to playback state of the [Player] instance. - late Stream playbackStream; - - /// Volume & Rate state of the [Player] instance. - GeneralState general = new GeneralState(); - - /// Stream to listen to volume & rate state of the [Player] instance. - late Stream generalStream; - - /// Creates a new [Player] instance. - /// - /// Takes unique id as parameter. - /// - /// ```dart - /// Player player = await Player(id: 0); - /// ``` - /// - Player({required this.id, int videoWidth: 0, int videoHeight: 0, Device? device, List? commandlineArguments}) { - this._isInstanceCreated = new Completer(); - if (device is Device) { - this.setDevice(device); - } - if (commandlineArguments != null) this.commandlineArguments = commandlineArguments; - this.videoWidth = videoWidth; - this.videoHeight = videoHeight; - this.currentController = StreamController.broadcast(); - this.currentStream = this.currentController.stream; - this.positionController = StreamController.broadcast(); - this.positionStream = this.positionController.stream; - this.playbackController = StreamController.broadcast(); - this.playbackStream = this.playbackController.stream; - this.generalController = StreamController.broadcast(); - this.generalStream = this.generalController.stream; - players[id] = this; - this._create(); - } - - /// Opens a new media source into the player. - /// - /// Takes a [Media] or [Playlist] to open in the player. - /// - /// Starts playback itself by default. Pass `autoStart: false` to stop this from happening. - /// - /// * Open a new [Media]. - /// - /// ```dart - /// player.open( - /// Media.file( - /// new File('C:/music.ogg'), - /// ), - /// autoStart: false, - /// ); - /// ``` - /// - /// * Open a new [Playlist]. - /// - /// ```dart - /// player.open( - /// new Playlist( - /// medias: [ - /// Media.file( - /// new File('C:/music.mp3'), - /// ), - /// Media.file( - /// new File('C:/audio.mp3'), - /// ), - /// Media.network('https://alexmercerind.github.io/music.mp3'), - /// ], - /// ), - /// ); - /// ``` - /// - Future open(MediaSource source, {bool autoStart: true}) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.open', - { - 'id': this.id, - 'autoStart': autoStart, - 'source': source.toMap(), - }, - ); - if(!autoStart){ - await Future.delayed(Duration(milliseconds: 350), () => this.pause()); - } - } - - /// Plays opened [MediaSource], - Future play() async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.play', - { - 'id': this.id, - }, - ); - } - - /// Pauses opened [MediaSource], - Future pause() async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.pause', - { - 'id': this.id, - }, - ); - } - - /// Play or Pause opened [MediaSource], - Future playOrPause() async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.playOrPause', - { - 'id': this.id, - }, - ); - } - - /// Stops the [Player]. - /// - /// Also resets the [Device] set using [Player.setDevice]. - /// A new instance must be created, once this method is called. - /// - Future stop() async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.stop', - { - 'id': this.id, - }, - ); - } - - /// Jumps to the next [Media] in the [Playlist] opened. - Future next() async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.next', - { - 'id': this.id, - }, - ); - } - - /// Jumps to the previous [Media] in the [Playlist] opened. - Future back() async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.back', - { - 'id': this.id, - }, - ); - } - - /// Jumps to [Media] at specific index in the [Playlist] opened. - /// Pass index as parameter. - Future jump(int index) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.back', - { - 'id': this.id, - 'index': index, - }, - ); - } - - /// Seeks the [Media] currently playing in the [Player] instance, to the provided [Duration]. - Future seek(Duration duration) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.seek', - { - 'id': this.id, - 'duration': duration.inMilliseconds, - }, - ); - } - - /// Sets volume of the [Player] instance. - Future setVolume(double volume) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.setVolume', - { - 'id': this.id, - 'volume': volume, - }, - ); - } - - /// Sets playback rate of the [Media] currently playing in the [Player] instance. - Future setRate(double rate) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.setRate', - { - 'id': this.id, - 'rate': rate, - }, - ); - } - - /// Changes [Playlist] playback mode. - Future setPlaylistMode(PlaylistMode playlistMode) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.setPlaylistMode', - { - 'id': this.id, - 'playlistMode': playlistMode.toString() - }, - ); - } - - /// Sets user agent for dart_vlc player. - Future setUserAgent(String userAgent) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.setUserAgent', - { - 'id': this.id, - 'userAgent': userAgent - }, - ); - } - - /// Appends [Media] to the [Playlist] of the [Player] instance. - Future add(Media source) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.add', - { - 'id': this.id, - 'source': source.toMap(), - }, - ); - } - - /// Removes [Media] from the [Playlist] at a specific index. - Future remove(int index) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.remove', - { - 'id': this.id, - 'index': index, - }, - ); - } - - /// Inserts [Media] to the [Playlist] of the [Player] instance at specific index. - Future insert(int index, Media source) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.insert', - { - 'id': this.id, - 'index': index, - 'source': source.toMap(), - }, - ); - } - - /// Moves [Media] already present in the [Playlist] of the [Player] from [initialIndex] to [finalIndex]. - Future move(int initialIndex, int finalIndex) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.move', - {'id': this.id, 'initial': initialIndex, 'final': finalIndex}, - ); - } - - /// Sets playback [Device] for the instance of [Player]. - /// - /// Use [Devices.all] getter to get [List] of all [Device]. - /// - /// A playback [Device] for a [Player] instance cannot be changed in the middle of playback. - /// Device will be switched once a new [Media] is played. - /// - Future setDevice(Device device) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.setDevice', - { - 'id': this.id, - 'device': device.toMap(), - }, - ); - } - - /// Sets [Equalizer] for the [Player]. - Future setEqualizer(Equalizer equalizer) async { - await this._isInstanceCreated.future; - await channel.invokeMethod( - 'Player.setEqualizer', - { - 'id': this.id, - 'equalizerId': equalizer.id, - }, - ); - } - - /// Destroys the instance of [Player] & closes all [StreamController]s in it. - Future dispose() async { - await this.currentController.close(); - await this.positionController.close(); - await this.playbackController.close(); - await this.generalController.close(); - } - - /// Internally used [StreamController]s, - late StreamController currentController; - late StreamController positionController; - late StreamController playbackController; - late StreamController generalController; - - late Completer _isInstanceCreated; - - /// Creates new instance of [Player] from constructor. - Future _create() async { - await channel.invokeMethod( - 'Player.create', - { - 'id': this.id, - 'videoWidth': this.videoWidth, - 'videoHeight': this.videoHeight, - 'commandlineArguments': this.commandlineArguments, - }, - ); - this._isInstanceCreated.complete(true); - } -} diff --git a/lib/src/playerState/playerState.dart b/lib/src/playerState/playerState.dart deleted file mode 100644 index 68ffedc0..00000000 --- a/lib/src/playerState/playerState.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:dart_vlc/src/mediaSource/media.dart'; - -/// State of a [Player] instance. -class CurrentState { - /// Index of currently playing [Media]. - int? index; - - /// Currently playing [Media]. - Media? media; - - /// [List] of [Media] currently opened in the [Player] instance. - List medias = []; - - /// Whether a [Playlist] is opened or a [Media]. - bool isPlaylist = false; -} - -/// Position & duration state of a [Player] instance. -class PositionState { - /// Position of playback in [Duration] of currently playing [Media]. - Duration? position = Duration.zero; - - /// Length of currently playing [Media] in [Duration]. - Duration? duration = Duration.zero; -} - -/// Playback state of a [Player] instance. -class PlaybackState { - /// Whether [Player] instance is playing or not. - bool isPlaying = false; - - /// Whether [Player] instance is seekable or not. - bool isSeekable = true; - - /// Whether the current [Media] has ended playing or not. - bool isCompleted = false; -} - -/// Volume & Rate state of a [Player] instance. -class GeneralState { - /// Volume of [Player] instance. - double volume = 1.0; - - /// Rate of playback of [Player] instance. - double rate = 1.0; -} diff --git a/lib/src/record.dart b/lib/src/record.dart deleted file mode 100644 index dc0c52d7..00000000 --- a/lib/src/record.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:io'; - -import 'package:dart_vlc/dart_vlc.dart'; -import 'package:dart_vlc/src/channel.dart'; - - -/// Internally used class to avoid direct creation of the object of a [Record] class. -class _Record extends Record {} - - -class Record { - /// ID for this record. - late int id; - - /// Recording from [Media]. - late Media media; - - /// Path where the recording is saved example: `/home/alexmercerind/recording.mp3` - late File savingFile; - - /// Creates a new [Record] instance. - static Future create( - {required int id, required Media media, required File savingFile}) async { - Record record = new _Record(); - record.id = id; - record.media = media; - record.savingFile = savingFile; - await channel.invokeMethod( - 'Record.create', - { - 'id': id, - 'media': media.toMap(), - 'savingFile': savingFile.path, - }, - ); - return record; - } - - /// Starts recording the [Media]. - Future start() async { - await channel.invokeMethod( - 'Record.start', - { - 'id': id, - }, - ); - } - - /// Disposes this instance of [Record]. - Future stop() async { - await channel.invokeMethod( - 'Record.dispose', - { - 'id': id, - }, - ); - } -} diff --git a/lib/src/widgets/controls.dart b/lib/src/widgets/controls.dart index 4a76d3f9..ec692d5c 100644 --- a/lib/src/widgets/controls.dart +++ b/lib/src/widgets/controls.dart @@ -2,9 +2,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; -import 'package:dart_vlc/src/device.dart'; -import 'package:dart_vlc/src/channel.dart'; -import 'package:dart_vlc/src/playerState/playerState.dart'; +import 'package:dart_vlc_ffi/src/device.dart'; +import 'package:dart_vlc_ffi/src/player.dart'; +import 'package:dart_vlc_ffi/src/playerState/playerState.dart'; + class Control extends StatefulWidget { final Widget child; @@ -194,10 +195,8 @@ class ControlState extends State with SingleTickerProviderStateMixin { if (!(positionInMilliseconds - 10000) .isNegative) positionInMilliseconds -= 10000; - players[widget.playerId]! - .seek(Duration( - milliseconds: positionInMilliseconds)) - .then((_) => setState(() {})); + players[widget.playerId]!.seek(Duration(milliseconds: positionInMilliseconds)); + setState(() {}); }), SizedBox(width: 20), IconButton( @@ -209,14 +208,12 @@ class ControlState extends State with SingleTickerProviderStateMixin { ), onPressed: () { if (players[widget.playerId]!.playback.isPlaying) { - players[widget.playerId]! - .pause() - .then((_) => this.playPauseController.reverse()); + players[widget.playerId]!.pause(); + this.playPauseController.reverse(); } else { - players[widget.playerId]! - .play() - .then((_) => this.playPauseController.forward()); + players[widget.playerId]!.play(); + this.playPauseController.forward(); } }, ), @@ -244,8 +241,8 @@ class ControlState extends State with SingleTickerProviderStateMixin { players[widget.playerId]! .seek(Duration( milliseconds: - positionInMilliseconds)) - .then((_) => setState(() {})); + positionInMilliseconds)); + setState(() {}); } }), SizedBox(width: 50), @@ -271,33 +268,24 @@ class ControlState extends State with SingleTickerProviderStateMixin { activeColor: widget.volumeActiveColor, backgroundColor: widget.volumeBackgroundColor, ), - FutureBuilder( - future: Devices.all, - builder: (context, - AsyncSnapshot> snapshot) { - return PopupMenuButton( - iconSize: 30, - icon: - Icon(Icons.speaker, color: Colors.white), - onSelected: (Device device) async { - await players[widget.playerId]! - .setDevice(device); - setState(() {}); - }, - itemBuilder: (context) { - return snapshot.data! - .map( - (device) => PopupMenuItem( - child: Text(device.name, - style: TextStyle( - fontSize: 14.0, - )), - value: device, - ), - ) - .toList(); - }, - ); + PopupMenuButton( + iconSize: 30, + icon: Icon(Icons.speaker, color: Colors.white), + onSelected: (Device device) { + players[widget.playerId]!.setDevice(device); + setState(() {}); + }, + itemBuilder: (context) { + return Devices.all.map( + (device) => PopupMenuItem( + child: Text(device.name, + style: TextStyle( + fontSize: 14.0, + )), + value: device, + ), + ) + .toList(); }, ), ], @@ -434,12 +422,12 @@ class _VolumeControlState extends State { return Icons.volume_off_sharp; } - void muteUnmute() async { + void muteUnmute() { if (players[widget.playerId]!.general.volume > 0) { unmutedVolume = players[widget.playerId]!.general.volume; - await players[widget.playerId]!.setVolume(0); + players[widget.playerId]!.setVolume(0); } else { - await players[widget.playerId]!.setVolume(unmutedVolume); + players[widget.playerId]!.setVolume(unmutedVolume); } setState(() {}); } diff --git a/lib/src/widgets/video.dart b/lib/src/widgets/video.dart index 257a0b7b..2446708d 100644 --- a/lib/src/widgets/video.dart +++ b/lib/src/widgets/video.dart @@ -1,11 +1,10 @@ -import 'package:dart_vlc/src/channel.dart'; import 'package:flutter/material.dart'; import 'dart:async'; import 'dart:typed_data'; import 'dart:ui' as ui; -import 'package:dart_vlc/src/player.dart'; +import 'package:dart_vlc_ffi/src/player.dart'; import 'controls.dart'; /// Internally used map to keep [StreamController]s for [Video] [Widget]s. @@ -167,15 +166,16 @@ class VideoState extends State