Open chuoichien opened 4 years ago
Update... Playback started working... I was invoking a nullptr
by mistake.
This is FFI based binding to libVLC. But still, if I can't have callbacks from other threads... how will I notify Dart about events.
Dart code
import 'dart:ffi' as ffi;
final dylib = ffi.DynamicLibrary.open('C:/Users/alexmercerind/Documents/application/cxx/main.dll');
typedef cxxInit = ffi.Void Function();
typedef dartInit = void Function();
dartInit init = dylib
.lookup<ffi.NativeFunction<cxxInit>>('init')
.asFunction();
typedef cxxOpen = ffi.Void Function();
typedef dartOpen = void Function();
dartInit open = dylib
.lookup<ffi.NativeFunction<cxxInit>>('open')
.asFunction();
typedef callback = ffi.Void Function();
typedef cxxOn = ffi.Void Function(ffi.Pointer<ffi.NativeFunction<callback>>);
typedef dartOn = void Function(ffi.Pointer<ffi.NativeFunction<callback>>);
dartOn on = dylib
.lookup<ffi.NativeFunction<cxxOn>>('on')
.asFunction();
void dartMethod() {
print('Event!');
}
void main() async {
init();
open();
await Future.delayed(Duration(days: 1));
}
They moved it from FFI 1.0 to 1.1 😑. No progress since early 2019. http://dartbug.com/37022. This "1.0" isn't really ready.
I think this is the same problem with platform channels isn't it? Dart is single-threaded, so the platform code must switch to the main thread before sending a message to Dart over method/event channels.
To do async callbacks you would need to have a separate isolate to receive those messages. Now in terms of platform channels there is some news on that front: https://github.com/flutter/flutter/issues/13937#issuecomment-784571153 .
Some more ideas: https://github.com/audiooffler/JucyFluttering/
They are also doing async callbacks on the main thread using some "Native Ports".
I found this guy's minimal example to study https://github.com/mikeperri/flutter-native-callbacks-example.
Thanks for the repo you gave, let's see how they are calling Dart methods from C/C++ from other threads (in a way).
@ryanheise @hacker1024
Finally! 🎉.
I'm able to receive data (not call-back, but it will work) from another thread. Using that "Native Ports" approach.
Even then, its very different approach. I had to extract things from already defined method in 'package:isolate/port.dart
.
The example repository I mentioned above (https://github.com/mikeperri/flutter-native-callbacks-example), used to just show a single callback, but I don't want a single callback. I wanted a stream like behaviour (callbacks that happen own their own, not single time when I call something from Dart).
He was using this method to get result from another thread.
return await singleResponseFuture((port) => nMethodA(port.nativePort));
But it returned only single value & then there was no further thing that we could do. So I went to the declaration of singleResponseFuture
and saw this inside...
Future<R> singleResponseFuture<R>(void Function(SendPort responsePort) action,
{Duration timeout, R timeoutValue}) {
var completer = Completer<R>.sync();
var responsePort = RawReceivePort();
Timer timer;
var zone = Zone.current;
responsePort.handler = (Object response) {
responsePort.close();
timer?.cancel();
zone.run(() {
_castComplete<R>(completer, response);
});
};
if (timeout != null) {
timer = Timer(timeout, () {
responsePort.close();
completer.complete(timeoutValue);
});
}
try {
action(responsePort.sendPort);
} catch (error, stack) {
responsePort.close();
timer?.cancel();
// Delay completion because completer is sync.
scheduleMicrotask(() {
completer.completeError(error, stack);
});
}
return completer.future;
}
Seemed pretty confusing, then I saw RawReceivePort
. Turns out I can just instantiate it and and assign handler attribute to it, for just listening to the values that another thread will "yield".
Now, I'm pretty confident to pull it off directly from FFI.
Now I just use this simple lines (along with other boilerplate) to recieve values from another thread.
RawReceivePort reciever = new RawReceivePort();
reciever.handler = (Object response) => print(response); // Printing values sent from C++ to console. (I can forward this to a StreamController).
nMethodA(reciever.sendPort.nativePort);
On the c++ part
__declspec(dllexport) void method_a(Dart_Port callbackPort) {
std::thread t([=]() {
for (int index = 0; index < 100; index++) {
std::this_thread::sleep_for(std::chrono::seconds(1));
callbackToDartInt32(callbackPort, index);
}
});
t.detach();
};
And on the console... (Getting int32_t from another thread)
D:\Music\app\bin>dart app.dart
0
1
2
3
4
5
6
This can't send advance structures like map, but who cares. Let's go.
It looks like flutter-native-callbacks-example and JucyFluttering have stumbled onto variants of the same sort of idea which involves going through receive ports, although they differ a bit in the details. The former depends on package:isolate
while JucyFluttering just instantiates a ReceivePort directly.
Then your own evolution of the former example seems to make it look more similar to the latter example (i.e. JucyFluttering) except you are using RawReceiverPort instead of ReceiverPort.
Congrats on getting it to work :+1:
Hey @ryanheise!
I've made my current work public here: dart_vlc I feel it is stable and feature-full enough for you to get started implementing Windows support for this project.
You can import the channels.dart
from the plugin to build your implementation using that. You can see the other files for reference, I have added a lot of comments.
You're a really good & dedicated maintainer.
I can submit a pull request, if you want me to add Windows support to your project using dart_vlc
.
But, the best is to continue yourself, as I need time to get Linux version ready & add the remaining features as well. You're the developer of this project, so you know the best how you want the things to be.
I'm just an 18 year old doing all this C++/Dart myself, so I'm not sure how good my implementation is. Please let me know if something is wrong or you wanna get it changed.
I decided to not use FFI as of now, because it is just so basic. Most advance thing that it can send through NativePort
is just array of string (I know that is enough to get it done, but it will take time & I'm sure that people are also waiting to get Windows support).
I'll add Linux & Windows support & FFI version in future, so you don't have to worry after depending on the plugin.
Thanks! Finally this long issue thread on the repository will be closed.
Awesome! I think you've done a great job in making it reusable. This is definitely a nice approach that will help not only just_audio but the whole ecosystem, and I think wider adoption of your package should mean that it will end up receiving more open source contributions from a wider audience.
It also means that it should be easy enough for me to maintain a just_audio_vlc_windows
package as part of this repo (as opposed to having a completely separate project and repo that I endorse).
Why not just just_audio_windows
instead of just_audio_vlc_windows
? 😎
The federated plugin model allows for 3rd parties to create alternative implementations of the platform interface for the same platform, so they need to have different names. That is basically the purpose of our discussion above to establish a naming convention. The reason we may end up with multiple implementations of the same interface for the same platform is that different implementations may support different subsets of features, and may also come with different licenses, so depending on an app's requirements, they can choose which implementation they want to link into their app.
One example is that the iOS and macOS implementations are currently embedded within the main plugin but I am considering moving them out to separate, endorsed, packages, the same way that just_audio_web
was done. The reason I might do this is because the current iOS/macOS implementation is based on AVQueuePlayer but I would like to create an AVAudioEngine-based implementation. It is possible in the process that we may gain some features and also lose some features, so a choice may be useful.
@ryanheise, so I was just saying why not name it
just_audio_windows
likejust_audio_web
.
Had I thought of this naming convention earlier, I may have named it as just_audio_html5_web
since there are definitely alternative ways to implement the web platform that have different tradeoffs. For example, in the html5-based approach, a visualizer is impossible. In a web-audio-based approach a visualizer is possible, but it runs into CORS restrictions which the html5-based implementation doesn't have.
I've also added Linux support now. You're so good to go now.
I'll also add things like player state etc. and being able to address playlist during playback itself. (But it is still not as powerful as your library, you've added a ton of stuff since I last used. Great work.)
@ryanheise Are you planning to not use this way? 😶
@alexmercerind Sorry I've been a bit preoccupied this week dealing with a neck condition which has me a bit worried since the doctors say it's impacting on the nerves controlling my arm. Apparently I have disc damage and somehow neck arthritis, and the holes that my nerves go through have shrunk.
Assuming good health, I also don't want to raise expectations too high that I'll be able to implement this quickly even though I would be tempted to start working on this now. Actually, I have a long list of high priority issues, the highest of which is to get the next release of audio_service out (since many other things depend on that). So as quickly as a single person can do this, it will happen whenever I get up to it, but I am hoping contributors from the community will join in and contribute on tasks that I'm not currently working on, and with the amazing work you've done I expect it would not actually be too difficult to get the job done. Otherwise, see the above mention/link "Support Linux?" where I have commented that when I have time, Linux will probably be my first go at this task, since I personally have a Linux computer rather than a Windows one.
Anyway, sorry again for not updating in this thread what's been going on.
It may be good for me to have some way to broadcast to everyone what I'm currently working on, just so people can get a sense of how long it will take me to get up to a particular task in the todo list.
@ryanheise ,
No no nevermind. No worries at all.
I'm really sorry to hear that. 🙁
Nothing comes before your own personal health, get well soon. 🙏🏻
I appreciate you shared what you're going through presently. I pray for you to get okay again. Have good rest.
Thankyou very much.
I thought I try to help you out here since I have both linux and windows installed on my computer, so I tried to create a vlc implementation of just_audio using web plugin as an example and dartvlc as an underlying dependency: https://github.com/megamegax/just_audio I couldn't get it to work but maybe it helps when you get to it I don't know :D feel free to use up anything from it you can.
@megamegax. Great job getting it ready. I'll improve the things in dart_vlc over time (just_audio is still more feature-full).
I'm not sure how federated plugin model goes & licensing etc. So, ryanheise knows the best, how it should be.
@megamegax Nice job! I really appreciate that you are willing to help make this project better, especially since I may need to lean more on this amazing open source community's support going forward. More than ever before due to my condition, I am so happy to see every time someone makes a contribution.
I hadn't been a fan of a discord server before, but now that it is easier to talk than to type, I wonder if I should create one just for contributor/developer chats on how we can approach building a certain feature, etc.
Sorry I've been a bit preoccupied this week dealing with a neck condition which has me a bit worried since the doctors say it's impacting on the nerves controlling my arm. Apparently I have disc damage and somehow neck arthritis, and the holes that my nerves go through have shrunk.
I'm sorry to hear that. Get well soon!
I hadn't been a fan of a discord server before, but now that it is easier to talk than to type, I wonder if I should create one just for contributor/developer chats on how we can approach building a certain feature, etc.
What about GitHub discussions?
Thanks, I really appreciate it!
I hadn't been a fan of a discord server before, but now that it is easier to talk than to type, I wonder if I should create one just for contributor/developer chats on how we can approach building a certain feature, etc.
What about GitHub discussions?
I think GitHub discussions is for typing rather than talking (correct me if I'm wrong) not terribly different from issues, so a discord server may at least add another mode of communication. When it comes to (for example) bringing someone up to speed on the internals of a package, I suspect this mode of communication will probably be more efficient too, regardless of whether it helps prevent you from being locked into a single typing posture for too long.
Congratulations on reaching Flutter Favorite. Thank you @ryanheise, @alexmercerind and @megamegax for your efforts in preparing the windows and linux version.
Hello everyone! I have made some progress on the Windows version on @libwinmedia's fork (https://github.com/libwinmedia/just_audio).
All the essential features (play, pause, seek, volume, speed and playlist) are supported.
Here's the Feature list compared to the other platforms:
Feature | Android | iOS | macOS | Web | Windows |
---|---|---|---|---|---|
read from URL | ✅ | ✅ | ✅ | ✅ | ✅ |
read from file | ✅ | ✅ | ✅ | ✅ | ✅ |
read from asset | ✅ | ✅ | ✅ | ✅ | ✅ |
read from byte stream | ✅ | ✅ | ✅ | ✅ | (not tested) |
request headers | ✅ | ✅ | ✅ | ||
DASH | ✅ | ||||
HLS | ✅ | ✅ | ✅ | ||
ICY metadata | ✅ | ✅ | ✅ | ||
buffer status/position | ✅ | ✅ | ✅ | ✅ | ✅ |
play/pause/seek | ✅ | ✅ | ✅ | ✅ | ✅ |
set volume/speed | ✅ | ✅ | ✅ | ✅ | ✅ |
clip audio | ✅ | ✅ | ✅ | ✅ | |
playlists | ✅ | ✅ | ✅ | ✅ | ✅ |
looping/shuffling | ✅ | ✅ | ✅ | ✅ | |
compose audio | ✅ | ✅ | ✅ | ✅ | |
gapless playback | ✅ | ✅ | ✅ | ✅ | |
report player errors | ✅ | ✅ | ✅ | ✅ | |
handle phonecall interruptions | ✅ | ✅ | |||
buffering/loading options | ✅ | ✅ | ✅ | ||
set pitch | ✅ | ||||
skip silence | ✅ | ||||
equalizer | ✅ | ||||
volume boost | ✅ |
@alexmercerind, thanks for building libwinmedia, used on the windows version.
@ryanheise Do you think this would fit in a PR?
Awesome, @bdlukaa ! Would someone who has Windows like to test this?
In terms of a PR, the beauty of the federated plugin model is that you don't need my approval, you can hold just the windows implementation in a separate repository and publish it as a separate package so long as it conforms to the just_audio platform interface (and ideally follows some package naming conventions that we agreed on above -- just_audio_nativelib_platform
.)
@ryanheise good to hear!
I will publish and maintain the windows plugin, and whenever I do some changes I'll do a PR upgrading the just_audio_windows version. Sounds good?
Sounds good, just note the package name as mentioned above (since we will possibly have multiple different windows implementations based on different native libraries), and also if maintaining a separate package, your git repository only needs to contain the windows implementation since your pubspec.yaml
will point to the hosted version of just_audio_platform_interface
.
Okay! I am thinking of adding the Linux (and UWP support in the future) support too, since libwinmedia supports Linux as well!
@bdlukaa
Okay! I am thinking of adding the Linux (and UWP support in the future) support too, since libwinmedia supports Linux as well!
libwinmedia already supports Windows & Linux itself, and there is no big deal in it. Infact, I just wrapped WebKit GTK instance to play media (ON LINUX ONLY), so essentially its just a <audio>
tag (which in turn uses GStreamer
). I implemented playlists aswell, within C++ itself.
Biggest thing you forgot to mention is that, libwinmedia only supports later versions of Windows 10 (v1703+).
"UWP support", there is nothing UWP/win32 in bare audio playback, libwinmedia already uses modern UWP (WinRT) APIs.
@ryanheise
Would someone who has Windows like to test this?
libwinmedia
is part of Harmonoid project & internal dependency for playback on Windows & Linux. So, everything is being used in Harmonoid app, thus tested.
Sounds good, just note the package name as mentioned above (since we will possibly have multiple different windows implementations based on different native libraries), and also if maintaining a separate package, your git repository only needs to contain the windows implementation since your pubspec.yaml will point to the hosted version of just_audio_platform_interface.
libwinmedia is mainly a C++ library, and I had to create a basic Flutter package (FFI bindings) to power Harmonoid. A simple package can be enough (with no native code) or just use existing libwinmedia.dart
(which Harmonoid uses) pub.dev
dependency as @bdlukaa did above. I have no platform channel interface.
A lot of features are only implemented for Windows and not for Linux. Few things need testing, I can't do this alone. It's rather large thing to do. Would appreciate if community decides to expose more WinRT APIs
.
audio_service
Apart from just_audio
, libwinmedia
is also capable of adding support to audio_service
aswell, I exposed System Media Transport Control APIs aswell. Everything is well documented, examples are present on the repository. For Linux, there is already a MPRIS library.
So, I'll be glad if libwinmedia
powers that.
Why libwinmedia
?
I was just tired of libVLC's large size & other options being either too weak or just very strictly licensed. Thus, I created my own C++ library. Linux implementation can be made to use GStreamer
instead which will be a lot better than starting a WebKit
webview.
Why not libwinmedia
?
You can yourself use C++/WinRT
APIs (very much like C# to be honest, since it is a projection of those) instead of having another libwinmedia
abstraction. libwinmedia
's main goal was:
Features list
Most features mentioned in that list are already supported by WinRT APIs (and a lot more), its just that I didn't expose them all.
P.S. there really needs to be some discussions tab or discord to talk about just_audio
or audio_service
in general.
Windows support is now published with features and instructions listed in the README. All credit goes to @bdlukaa for the federated plugin implementation and @alexmercerind for libwinmedia
. Awesome work!
I'll leave this issue open for a bit and so please post below if you encounter any serious issues. Once immediate issues are resolved, I'll close this issue and just_audio_libwinmedia can be used to report any future issues.
I have tested it on my production app and it works as good as it should!
I tried my best to implement all the features. Here's the features that can't be supported on Windows at all:
Here are the options that are supported by Windows, but aren't implemented yet:
Name | Index | Description |
---|---|---|
AcousticEchoCancellation | 1 | An acoustic echo cancellation effect. |
AutomaticGainControl | 3 | A automatic gain control effect. |
BassBoost | 8 | A bass boost effect. |
BassManagement | 13 | A bass management effect. |
BeamForming | 4 | A beam forming effect. |
ConstantToneRemoval | 5 | A constant tone removal effect. |
DynamicRangeCompression | 17 | A dynamic range compression effect. |
EnvironmentalEffects | 14 | An environmental effect. |
Equalizer | 6 | A equalizer effect. |
FarFieldBeamForming | 18 | A far-field beam forming effect. |
LoudnessEqualizer | 7 | A loudness equalizer effect. |
NoiseSuppression | 2 | A noise suppression effect. |
Other | 0 | Other. |
RoomCorrection | 12 | A room correction effect. |
SpeakerCompensation | 16 | A speaker compensation effect. |
SpeakerFill | 11 | A speaker fill effect. |
SpeakerProtection | 15 | A speaker protection effect. |
VirtualHeadphones | 10 | A virtual headphones effect. |
VirtualSurround | 9 | A virtual surround sound effect. |
Nice, it looks like there's a lot of potential, and it's good to know that ClippingAudioSource
can be supported which I think is the last core feature that is conditionally disabled from example_playlist.dart
.
Some of those features you mentioned like caching and byte streams are currently handled in the Dart layer so shouldn't those work already? In both cases, just_audio sets up a proxy web server to implement the features, and then instead of getting the platform side of the plugin to play the original URL, it passes the proxy URL instead.
just_audio sets up a proxy web server to implement the features
I didn't know that. So you're saying that byte streams should be working?
Yes, I would have expected this to work. One way to test it could be the caching example which uses a stream audio source under the hood, and that should activate the local proxy on all platforms except web.
Hello everyone! As you all know, just_audio_libwinmedia
was the option used to play audio on Windows, but there are several limitations when using libwinmedia
.
While I was on my days off, I worked on a fully native implementation for windows that is working. Check it out on just_audio_windows
. It isn't published to pub.dev because I'd like to know @ryanheise's opinion on endorsing the project.
If you'd like to try it, you can add it as a dependency to your project. No extra setups required:
dependencies:
just_audio_windows:
git:
url: https://github.com/bdlukaa/just_audio.git
path: just_audio_windows/
Note that this is an early version with only a few features (most important ones) implemented, but example/main.dart
works as expected. Contributions are welcome!
I like the direction of this - I noticed it doesn't have a license yet, is that decided?
I'll just mention again that since I'm on Linux it is something I will need to rely on others to test.
From here, let's get some people trying out just_audio_windows. If it works for testers, let's publish it and get it advertised in the just_audio README where it will have more eyes to detect more bugs and help reach maturity.
Once it reaches maturity, we can start looking at the endorsement process which should include some quality assurance that is consistent with the Flutter Favorites programme. A key requirement I would have is to ensure that semantic versioning, as interpreted by pub.dev, is observed. This should guarantee that an update to just_audio_windows doesn't cause just_audio to break its own Flutter Favorite obligations via its transitive dependencies.
@ryanheise cool! I'll be looking forward to implement all the missing features and fix bugs. I'll let you know when it gets in beta <3
@ryanheise Hello! just_audio_windows
is finally in beta. This means that the example/main.dart
works completely. Playing, pausing, seeking, speed, volume and pitch are working properly. Also, you can use the play/pause button from the keyboard, and the app will update properly (thanks to the data event channel)
Next step is to impement playlists, documentated here.
just_audio_windows uses UWP's native MediaPlayer
(winrt
). This was chosen over the win32
because it's more recent. Also, Flutter deprecated support for Windows 7 and 8. UWP
also works on X Box, targeting this device as well. UWP
's MediaPlayer
is a lot easier to use than the win32 one, with less boilerplate code.
LMK your toughts on endorsing the windows implementation once fully implementated.
In case anyone wants to try it, add the following to your pubspec.yaml
;
dependencies:
just_audio:
git: https://github.com/bdlukaa/just_audio.git
Awesome!
Can people using Windows test this and let @bdlukaa know how it goes?
Of course I'd be happy to endorse it once it becomes stable, and in the meantime I should definitely update the README with instructions on how to use it by adding the explicit dependency.
This is great news, that only leaves us with Linux :)
For Linux, I've wanted to make a GStreamer package for a while but never got around to it, and I doubt it could be endorsed due to GStreamer's LGPL license (unless it doesn't really count since you wouldn't statically compile GStreamer?)
Actually I don't know if there really is a problem endorsing a federated plugin implementation for Linux licenced under the LGPL. An LGPL library does not require the package linking that library to itself become LGPL The issue with LGPL for Flutter apps has specifically been the dynamic linking clause which is incompatible with the iOS and Android app stores. However, when building for those platforms, the Linux implementation would not be linked anyway.
If I am wrong about that, it is no big hassle to simply provide instructions in the just_audio README for how to manually add an extra dependency for the Linux implementation. I actually prefer this over endorsement because it's really not too difficult an imposition on the app developer to add one line to their pubspec.yaml
, and it also protects the main plugin should anything ever break in the future.
Hi @UnicornsOnLSD , have you seen the latest comments on #332 regarding GStreamer? Let's discuss over in that thread.
@bdlukaa Ii am updating the main README to point to this new Windows plugin. Since your plugin is now in beta, would you consider publishing the beta on pub.dev using appropriate semantic versioning for a beta prerelease?
Sure! https://pub.dev/packages/just_audio_windows.
To use it, just add it to your dependencies:
dependencies:
just_audio:
just_audio_windows:
Hi everyone! I'm glad to announce that just_audio_windows
now has support for playlists (thanks to @chengyuhui - https://github.com/bdlukaa/just_audio_windows/pull/8) in the latest version. Check it out!
Awesome! @chengyuhui out of curiosity, is it gapless?
Regarding concatenatingMove
, if it's gapless, the iOS implementation might be a good basis for inspiration, but if it's not gapless, the web implementation might be more similar. For gapless, the idea was to handle all playlist reorderings through a single function (enqueueFrom
) which covers all cases including adding, removing and moving. enqueueFrom
is then a central place to first compare the old order to the new order and see if the "current item" would still be the same. If it is the same, we try not to interrupt its playback so the sound continues coming out smoothly.
I will try to look into the iOS implementation to see how this can be done, though I don't really speak Objective-C :)
Sorry, is this plugin support microsoft windows ?