ryanheise / just_audio

Audio Player
1.06k stars 680 forks source link

Support Microsoft Windows ? #103

Open chuoichien opened 4 years ago

chuoichien commented 4 years ago

Sorry, is this plugin support microsoft windows ?

sachaarbonel commented 4 years ago

Some of my research notes on the subject. We could make FFI bindings to: "ASIO is an audio driver protocol by Steinberg. While it is available on multiple operating systems, it is most commonly used on Windows to work around limitations of WASAPI including access to large numbers of channels and lower-latency audio processing.". This the design decision the cross-platform Rust library Cpal made.

hacker1024 commented 4 years ago

I may attempt this. @ryanheise, would using Windows code from Musikcube be okay? It has a BSD 3-Clause "New" or "Revised" License.

Musikcore supports gapless playback, so it looks like a good fit.

It's also written in C++, so if FFI bindings are done right, we could share the code between Windows and Linux (and even macOS, if it's better than AVPlayer).

hacker1024 commented 4 years ago

I've looked at Musikcube / Musikcore a little more - the core is completely undocumented, but there's a plugin system. There exists a documented plugin that exposes a web server to interface with the core.

It shouldn't be too hard to modify the server to use Flutter platform channels instead of HTTP, and then all the plugin documentation will apply.

https://github.com/clangen/musikcube/wiki/remote-api-documentation

If we were to uses Musikcore, that leaves two choices:

ryanheise commented 4 years ago

The Musikcore license still looks complicated to me since the individual decoders have separate license agreements (including the GPL). It's also not clear to me what the codec support is for each platform target. ExoPlayer is mentioned, for example, but ExoPlayer taps into hardware decoders that are available on device via the Android SDK, so I would probably expect to see a different set of codecs supported for each platform.

hacker1024 commented 4 years ago

That's difficult, then. On another note, Musikcore uses the Visual Studio build system on Windows. I've been trying to build a CMake script instead (since Flutter uses CMake), but it's proving difficult.

Unfortunately, there aren't many high-level C/C++ Windows audio libraries out there... I don't think ASIO can decode codecs, and it definitely can't fetch and play stuff from the Internet. We'd need to either find a library to do those things, or do them manually.

alexmercerind commented 4 years ago

@chuoichien @ryanheise I made a little project here. It can play audio files on Windows 10 & Linux now. I'm ready to help add support for Windows to your this project, if you say. (I'm not pro however 😅).

I used miniaudio because it was only audio library with MIT license (even though it wasn't high level & initially I used closed source junk irrKlang). Right now it supports MP3, FLAC & WAV. For supporting more formats, I think I can write something using FFmpeg to convert audio stream to MP3 before playback.

I'm using MethodChannel instead of dart::ffi (even though I first used dart::ffi), but main problem came out that one still had to compile different dynamic libraries & change path while loading in Flutter project. But using MethodChannel makes installation simple as to just adding in pubspec.yaml.

@hacker1024, C++ libraries I agree aren't super user friendly, some are closed source, some are not openly licensed & some are too low level.

Thankyou @ryanheise 💝!

ryanheise commented 4 years ago

@alexmercerind great!

Perhaps the way to go is to use the federated plugin model. I wasn't really a fan of this model for my plugins since I had intended to write all of the platform implementations myself and I thought it would be a maintenance burden to have a separate plugin for each implementation and keep them in sync. However, I believe this model may actually be a potential solution to our license issue.

The LGPL is basically incompatible with iOS because iOS doesn't provide a way for users to install their own version of a DLL. There are ways you could get close by having a separate app to hold the LGPL code and then use IPC between the two apps, but as far as I can see the same developer needs to own both apps, so the user couldn't substitute the LGPL one and have the non-LGPL one use it. Android might be a bit more flexible with IPC but still it's not convenient for most users to have to install the LGPL'd component as a separate app.

However, Windows and Linux don't have this restriction with DLLs, so in theory we could have the Windows and Linux implementations have different sets of licenses, which will be easier to manage in the federated model.

alexmercerind commented 4 years ago

😃 Thankyou for your reply!

I'm understanding the situation & your problem with the management of the plugin right now. But I don't know how can one can distribute the .dll or .so to the users. I think it will just even more responsibility of distribution & management of dynamic libraries as you said, if you decide to use federated model. I feel it will be difficult for both, you & user to setup dynamic libraries in the app. Best way in my opinion is still to use the MethodChannel for a plugin instead of dealing with DLLs or SOs with dart:ffi. From the developer point of view, he just needs to mention the package in the pubspec.yaml & the plugin starts working, but I doubt dealing with pre-compiled .dll or .so will be difficult for an average Flutter developer.

Right now, in my plugin... I'm using same C++ code for supporting audio playback in both Linux & Windows (I modified the CMakeLists a little). And same plugin is compatible with both Windows & Linux. You can try out... It will be pretty easy to add same code to your project as I tried to keep the method & class names close to your API.

I just wanted to help (if I can)... & sorry if any of my knowledge is incorrect. (I'm just 17. I'm learning everything by reading the documentation online) 😄

ryanheise commented 4 years ago

Just to be sure we're on the same page, section 6a of the LGPL reads:

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.)

If any app developer uses ffmpeg in their app, then they must comply with this license, and this makes it difficult to "legally" distribute an app on the app store or play store where section 6a can't be fulfilled. That is why I do not accept any LGPL-licensed code in just_audio.

But, this issue only exists for mobile apps. Since we're talking about Windows and Linux, it is possible to meet Section 6a of the LGPL, so there is no problem using an LGPL'd library.

The federated plugin model makes it possible to keep the LGPL'd portion separate from the rest of the plugin so that the license files can be separate for each part. You can read about federated plugins here:

https://flutter.dev/docs/development/packages-and-plugins/developing-packages

alexmercerind commented 4 years ago

Great! It's good thing that we can separate licenses in federated plugin model. 😁

I just came to know that miniaudio already supports OGG aswell. Only audio format that seems missing in my sight, is M4A. Both DASH & AAC do not work on miniaudio.

Possibly we might not need LGPL'd FFmpeg.

ryanheise commented 4 years ago

I've just had a look at miniaudio and I'm really impressed. Even though it has limited decoder support, I might use this for some other projects just for the mp3 decoder.

I didn't see OGG mentioned in the README.

hacker1024 commented 4 years ago

I'm not too familiar with the federated plugin architecture, but would it be possible to write two decoding implementations, each in their own federated plugin that connects to the windows implementation plugin?

One could use FFmpeg with the appropriate license, and the other could be a thin implementation that lets miniaudio do the decoding.

This way, users could choose whichever suits their needs. Abstracting the decoding would also make it easier to switch to something with a better license later, if needed.

(I need AAC, and I think it's a fairly popular codec for music streaming services.)

ryanheise commented 4 years ago

My understanding is that the app developer could choose one implementation among perhaps multiple alternatives. E.g. they could choose an FFmpeg implemention over another one. I am not sure what you mean by "federated plugin that connects to the windows implementation plugin" in that I don't think alternative implementations would talk to each other and you would only link one of them, but each implementation would link to the platform interface.

alexmercerind commented 4 years ago

@ryanheise I just noticed that you made a seprate dart file for the web platform. Can I do the same or embed the code in the original just_audio.dart (Making separate file will be easy for me) ? I have to take a look at playing audio from network... My implementation is just simple & plays local files only right now.

I'm forking your project today & make a good PR soon, as Windows alpha support just came in.

ryanheise commented 4 years ago

Hi @alexmercerind , going forward the plan would be to use the federated plugin model mentioned above, so it wouldn't be a separate dart file as is the case for the web platform. But, you can try to get the windows implementation working in any which way, and then integration can wait until we sort out the federated plugin issue. I'll try to have a crack at it on the weekend.

alexmercerind commented 4 years ago

👋 @ryanheise, I did a bit of stuff in my fork alexmercerind@e67d821c205e85aab789ba7523e8c9b663a7b682

All I did right now is add basic functionality (You can see your table in README). Adding things like changing playback speed, clipping, looping, playing from assets shouldn't be hard. I also changed the main.dart of example to make it work on Windows. (Original main.dart is now main-original.dart for now).

I also changed the name of methods channel to match yours.

Ideally, it would've been great if Flutter team used C# in Windows like they are doing Java in Android etc. If it was C# I could help a lot more in terms of functionality (I still will) ... but they are using C++ for both Linux & Windows. And writing C++ isn't the most friendly thing when libraries are not great & native APIs look like this (I have no idea of this difficult mess).

😟 FFmpeg is hard...

It's very basic right now.

alexmercerind commented 4 years ago

Ah I forgot to say... Don't forget to save the miniaudio.h in the desktop folder of my fork before trying. Rest should go well for now.

ryanheise commented 4 years ago

Well done, @alexmercerind !

You can probably request c# support to the Flutter team (although it probably still won't make FFmpeg any easier to use ;-)

Is there an issue with checking the miniaudio.h file into Git?

alexmercerind commented 4 years ago

@ryanheise, 😃 No, there isn't any issue about miniaudio.h but if you add it then 95% of the code on this repository will be C. So...😅 I didn't add it.

ryanheise commented 4 years ago

I had forgotten how large that file is :-) I guess it is more like a library. What is the convention for adding dependencies in Windows and Linux plugins? Can it be tied into the build script in some way to automatically download the dependencies?

hacker1024 commented 4 years ago

I am not sure what you mean by "federated plugin that connects to the windows implementation plugin"

@ryanheise Perhaps "federated plugin" was the wrong terminology. Is there a way to declare some sort of "decoder" interface, and have multiple plugins that can provide an implementation?

As in, a structure like this, where the developer can choose the decoder plugin:

                                  just_audio_windows_decoder_miniaudio
                                /
             just_audio_windows - just_audio_windows_decoder_ffmpeg
           /
just_audio - just_audio_android
           \
             etc...

just_audio_windows_decoder_miniaudio could use miniaudio to decode media, and just_audio_windows_decoder_ffmpeg could use ffmpeg.

just_audio_windows would then use the provided decoding implementation to decode media, and then play it with miniaudio.

hacker1024 commented 4 years ago

Can it be tied into the build script in some way to automatically download the dependencies?

I feel like that would contradict pub.dev's policy:

It is central to this service that consumers of packages can trust that their dependencies do not suddenly disappear.

Downloading a source from another place would mean the plugin could break at any time, and it's out of pub.dev's control.

This seems to almost violate this part, too:

Thus, once a package has been published it cannot be unpublished or deleted.

alexmercerind commented 4 years ago

@ryanheise

Oh, when you push the package to the pub.dev just remove the miniaudio.h from the .gitignore... so that the miniaudio.h is present in the package. (You don't have to download on user's machine separately). I'm doing the same in my flutter_audio_desktop & it works just fine.

There is not any specific way of adding the dependencies... I modified the CMakeLists to use the miniaudio.h from desktop folder. And I haven't really found any dedicated guide from Flutter yet for building plugins for Windows & Linux. All this stuff is completely written by reading the code of their engine & trial-error.

I'm not pro in any of C++ or Dart. Just doing my stuff.

ryanheise commented 4 years ago

@hacker1024 hmm, this is exactly the way that build scripts for Android and iOS/macOS work. If you depend on a large library like ExoPlayer, you do not embed the library into your git repo, you add the download URL in the build script so that it can be downloaded and cached. So I guess the question I'm asking is, what is the equivalent of this for Linux and Windows? It would not make sense to embed these large 3rd party libraries directly into every pub package.

ryanheise commented 4 years ago

I am not sure what you mean by "federated plugin that connects to the windows implementation plugin"

@ryanheise Perhaps "federated plugin" was the wrong terminology. Is there a way to declare some sort of "decoder" interface, and have multiple plugins that can provide an implementation?

As in, a structure like this, where the developer can choose the decoder plugin:

                                  just_audio_windows_decoder_miniaudio
                                /
             just_audio_windows - just_audio_windows_decoder_ffmpeg
           /
just_audio - just_audio_android
           \
             etc...

just_audio_windows_decoder_miniaudio could use miniaudio to decode media, and just_audio_windows_decoder_ffmpeg could use ffmpeg.

just_audio_windows would then use the provided decoding implementation to decode media, and then play it with miniaudio.

I suppose if just_audio_windows has a dependency on some other package just_audio_windows_decoder which in turn is a federated plugin, there might be a way for an app to specify the deeply nested dependency alternative for just_audio_windows_decoder with dependency overrides, although I'm not 100% sure. This is probably a good question for the Flutter team.

hacker1024 commented 4 years ago

If you depend on a large library like ExoPlayer, you do not embed the library into your git repo, you add the download URL in the build script so that it can be downloaded and cached.

Right, but ExoPlayer and its own dependencies all come from the same place. It's unlikely that ExoPlayer's dependencies will go down. If you could upload the single file to pub.dev that would be fine, but it would need to be uploaded elsewhere, meaning the plugin (and all older versions) could break if that file goes down. I don't think they allow that kind of possibility in their policy.

alexmercerind commented 4 years ago

Well the conversation went too far about this... miniaudio.h is just about 1 MB... And is in MIT license... So there is no problem (I feel) in including that in the source control.

ryanheise commented 4 years ago

It could be worth posting an issue on flutter/flutter to request some documentation on this. Although in the meantime, I'm fine with checking the file into the repo.

alexmercerind commented 4 years ago

Okay, today I'll try to add AAC/M4A & WMA support.

ryanheise commented 4 years ago

I have just finished converting just_audio into a federated plugin. The way this works is that the Linux and Windows implementations would each be defined as a separate package each with its own license.

An implementation can also be "endorsed" which means that the app developer merely adds the dependency for just_audio and it will automatically find the endorsed platform implementation and add it as an implicit dependency. Endorsement is also not required, and if the Linux implementation resides in package just_audio_linux for example, then an app that wants to use that particular Linux implementation can choose it by adding it as a dependency:

dependencies:
  just_audio: ...
  just_audio_linux: ...  # not sure if this is such a good name, though.

We might need to think about a naming policy for platform implementations in case we end up with two different Linux implementations based on different libraries and with different licenses, since they can't both be called just_audio_linux. I can update the README file once we decide on exactly what that naming policy should be.

As for how to implement platform support under the federated model, your package will include a dependency on the package just_audio_platform_interface but not just_audio. The platform interface package provides a file called method_channel_just_audio.dart which defines how the Flutter plugin interfaces with a platform implementation, what method and event channels it uses and what the message types are.

The main thing to note is that the event and method channel names are the same as before, but the message types have changed slightly in a way that will be more future proof. All values that were previously lists are now maps, and all method return values are now also maps. In many cases these are empty maps for now, but this allows new data to be included in a method return value without breaking the API. The other main change is that all position and duration values are now communicated over method channels in microseconds instead of milliseconds (so we also now need to take more care to avoid overflows). DateTime values are still communicated as before (milliseconds since the epoch). Lastly, some map keys have changed. Particularly, composite audio sources that have children used to store those children in the audioSources and audioSource keys but these are now stored in the children and child keys.

One other way to see what's changed is to look at the commit diff for the Android or iOS implementation:

https://github.com/ryanheise/just_audio/commit/13e257e80e9fe88f3c68da755c4bc048f6494ce6 (iOS) https://github.com/ryanheise/just_audio/commit/1d7134ff31c778d41aa27dc01b379291d4f8e6c4 (Android part 1) https://github.com/ryanheise/just_audio/commit/0efd8e45218662050c97574d162f2464d124e612 (Android part 2)

Although the Android and iOS implementations are still included in the main plugin, you can look at the web implementation to see how it can be defined as a separate package. Note that the ios directory needs to be included to avoid a build error. You can also technically include a Linux and Windows implementation in the same package if there is some benefit to code sharing and code maintenance.

That's it! Let me know if there is anything I can clarify to help.

hacker1024 commented 4 years ago

Great!

Is there a way at the moment to stop out of date implementations working if there are breaking interface changes? I imagine version constraints in the plugin's pubspec.yaml would be enough.

ryanheise commented 4 years ago

I'm not sure how pub's magic works, I don't think it's fully documented anywhere, but I was thinking about how it would be able to resolve the dependency version of an endorsed implementation without that version number being mentioned anywhere in my pubspec. I think it "probably" looks at the version dependencies of the endorsed implementation to see what version of the platform interface it depends on. I was thinking whether it would make sense for me to be able to declare which version of an implementation I endorse, but maybe the reason it doesn't work that way is that it is more useful to allow the author of an implementation to publish bug fixes without waiting for them to be endorsed, as long as it depends on a compatible version of the platform interface.

Anyway, you can read the relevant part of Google's document here:

https://flutter.dev/go/platform-interface-breaking-changes

hacker1024 commented 4 years ago

My app uses some weird codecs, so I'm going to have a go at writing an LGPL-licensed implementation that uses libVLC.

(It should be able to work on all platforms, in theory.)

ryanheise commented 4 years ago

Cool. It'll be interesting to see how your experience goes with the platform interface!

hacker1024 commented 4 years ago

I've gotta walk before I can run - I'm playing around with libVLC, and I also need to decide whether to use dart:ffi or platform channels.

Reasons to use platform channels would be:

Reasons for using dart:ffi would be:

If I understand correctly, it should be possible to use dynamic links on desktop platforms, and release the implementation under a license like MIT. If I have to use LGPL, I think every Flutter project that uses it would have to be LGPL too - unless Flutter/Dart adds a feature to dynamically link packages. This is all due to the library-swaping issues on mobile platforms mentioned previously.

Some info from copyleft.org (this guide, section 10.7):

LGPLv2.1 §6(b) allows the distributor of a “work that uses the library” to simply use a dynamically linked, shared library mechanism to link with the library. This is by far the easiest and most straightforward option for distribution. In this case, the executable of the work that uses the library will contain only the “stub code” that is put in place by the shared library mechanism, and at runtime the executable will combine with the shared version of the library already resident on the user’s computer. If such a mechanism is used, it must allow the user to upgrade and replace the library with interface-compatible versions and still be able to use the “work that uses the library.” However, all modern shared library mechanisms function as such, and thus LGPLv2.1 §6(b) is the simplest option, since it does not even require that the distributor of the “work based on the library” ship copies of the library itself.

LGPLv2.1 §6(a) is the option to use when, for some reason, a shared library mechanism cannot be used. It requires that the source for the library be included, in the typical GPL fashion, but it also has a requirement beyond that. The user must be able to exercise her freedom to modify the library to its fullest extent, and that means recombining it with the “work based on the library.” If the full binary is linked without a shared library mechanism, the user must have available the object code for the “work based on the library,” so that the user can relink the application and build a new binary.

ryanheise commented 4 years ago

That's an interesting point about platform channels and static linking. Platform channels definitely don't sound like "static linking" in the traditional sense, so I'd be more inclined to just look at whether it is possible for the user to link a different version of the library into the app which seems technically possible even with platform channels. Though, IANAL.

ryanheise commented 4 years ago

Just as a heads up, I'm about to make a very small breaking change to the platform interface, but I'm going to sneak it in without a bumping the major version since major version bumps are disruptive and all currently published implementations are in this repo anyway so I'll be able to update them all consistently at the same time.

I'll post the details here after I commit/publish it.

ryanheise commented 3 years ago

FFI is now stable with Flutter 2.0 (and it seems more flexible than before). Maybe conditions are good to take another look at this?

Let's think about package naming: I think we could name each implementation's package something like:

just_audio_platformname_nativelibraryname

Maybe in theory it would be possible for a single package to provide multiple platform implementations based on the same native library, but the problem with that is that app developers should still be able to choose which platform implementation they want to use for each platform independently. So in that case if some code is reusable between platforms, I think it would probably still be better to extract that into a more low-level reusable package that two separate platform implementations could then internally depend on.

Thoughts?

hacker1024 commented 3 years ago

I like this idea.

The VLC library, for example, runs on all 5 native platforms. Common code could be kept in one package (just_audio_common_vlc?) and the sole responsibility of platform specific VLC packages would be to provide the actual VLC library for use with FFI, and register the platform implementation from the common package.

If licensing permits, one could then use VLC on every platform to maintain consistency with supported codecs.

ryanheise commented 3 years ago

just_audio_common_vlc?

That sounds like a good name to me. It looks like libVLC 4 (in preview) has gapless playback. I haven't checked the API to know how feasible it is to have clipped items in a gapless playlist.

ryanheise commented 3 years ago

Besides libVLC, it would be great in my opinion to have @alexmercerind 's code above integrated into the federated plugin model since it is already written and supports Windows and Linux. With this settled naming convention, we would have:

just_audio_common_miniaudio
just_audio_windows_miniaudio
just_audio_linux_miniaudio

Of course we already have a complete macOS desktop implementation so the above two would complete the set.

@alexmercerind are you still interested in working together on this?

alexmercerind commented 3 years ago

Hi there @ryanheise !

I've been silently working to get libVLC working on Windows. And I'm nearly done aswell. I'm able to fetch libVLC DLLs directly from their servers using CMake & manage linking & all the headers.

And since, it is under LGPL, developers shouldn't have problem about distributing their apps.

Now I'm really confident that I've pulled it off with libVLC. Now, I'm in college & have learnt a lot more about C++.

And, as I'm more comfortable with objects & classes, I used libVLC++ (also LGPL) instead of libVLC

I've nearly implemented all, network streaming, playlists, event callbacks, streams etc. etc.

I can also add metadata support.

For the proof.... main main2

I really wanna add libVLC support to your library as I'm nearly done figuring out.

It took me a lot of time to get here... I can add you as a collaborator to my private repository to take a look at the things I have achieved, if you want me to add Windows & Linux support.

Please consider buying me a coffee 🙏 or mentioning me in the README 📖. I'm ready to get it all here for both Linux & Windows.

And, since its VLC... the codec/format support we all know is uncomparable.

alexmercerind commented 3 years ago

What do you think @ryanheise ? 👀

I decided to switch from miniaudio to libVLC, since it was't really powerful.

hacker1024 commented 3 years ago

@alexmercerind Amazing work! I attempted to start a LibVLC implementation a while ago, but struggled with CMake. Are you using FFI or platform channels? I think FFI may be worth a shot, as it opens up the possibility for use with Dart CLI apps as well.

Also, do you think LibVLC could be easily used on other platforms as well?

ryanheise commented 3 years ago

@alexmercerind really awesome! I am sure it was a lot of time and effort to get on top of all of these technologies.

For these federated plugin implementations, I would like to list them all prominently in the main README along with who is maintaining them, and links to their pub.dev page.

By the way, quick poll: Should we go for naming like just_audio_linux_libvlc or just_audio_libvlc_linux? Not really an important question at all, but it is timely before anyone starts publishing pub.dev packages.

For the current set of features I think you're right that FFI offers no real benefit over platform channels. But I am excited by some of FFI's potential. For example, sending actual audio data over platform channels is not the most efficient thing, but with FFI we could hope to achieve similar efficiency as say dart:io. This could open the door to doing custom audio processing on the Dart side as application logic (i.e. similar to what is achievable using iOS's audio tap processor, or ExoPlayer's AudioProcessor).

Although we certainly don't need to worry about that in the short term, and there are other considerations as you say, so I'd say go with whichever's simplest.

hacker1024 commented 3 years ago

By the way, quick poll: Should we go for naming like just_audio_linux_libvlc or just_audio_libvlc_linux?

I prefer just_audio_libvlc_linux, as I think livvlc_* implementations have more in common with each other than linux_* implementations, so libvlc is the more general component. The name then has components in order of decreasing specificity.

For the current set of features I think you're right that FFI offers no real benefit over platform channels.

I like FFI because it allows code to be re-used in non-Flutter applications, but I understand if that's less important than maintainability for this project. I'm thinking of making a CLI version of my app (most of the code will be applicable), and at the moment no audio playback package exists for the CLI.

alexmercerind commented 3 years ago

@ryanheise , I'm agreed to what @hacker1024 said i.e. just_audio_libvlc_X. And I'm thinking, that I should make my libVLC project public. Because, your library isn't the only audio library that, I want to bring Windows & Linux support to. (Don't get me wrong, your library is awesome, but there are other libraries I would love to see get support for Windows & Linux).. By this way, all the audio libraries for Flutter will be able to use the work that I did & maintainers like you will be able modify or wrap the things, the way they want, in their own "coding style". What do you think? (I'm also targeting Video playback support for Windows & Linux.)

I like FFI because it allows code to be re-used in non-Flutter applications, but I understand if that's less important than maintainability for this project. I'm thinking of making a CLI version of my app (most of the code will be applicable), and at the moment no audio playback package exists for the CLI.

@hacker1024 I'm also really interested in getting an audio library for plain Dart apps (without Flutter). But, I don't really know how do I distribute shared libraries. Give me some more time to figure out (less than a week) a "professional" way.

Thankyou all.

alexmercerind commented 3 years ago

One other thing is, how do I call Dart methods from C/C++ using FFI? In platform channels, we can communicate on both sides. Why is there no "platform channel" for plain Dart.

EDIT: Apparently, there are callbacks... but on the same thread. let's have some tinkering & try to bring my platform channel work to dart:ffi.

EDIT2: Okay, callbacks work. Plain C callbacks, I'm more used to passing `std::function, but whatever, it works. So, I can pull things off using FFI, but boilerplate is insane.

By the way, you guys can ping me at alexmercerind#3898 on Discord. I'm not really having fun in issue threads lol.

I'll just wrap my existing C++ code in extern "C" clause, and use "C things" wherever necessary, I don't have patience to write everything in bare C.

ryanheise commented 3 years ago

I'm agreed to what @hacker1024 said i.e. just_audio_libvlc_X. And I'm thinking, that I should make my libVLC project public. Because, your library isn't the only audio library that, I want to bring Windows & Linux support to.

Definitely agree with that approach. :+1:

You will definitely get a lot more support from collaborators after opening it up to everyone.

alexmercerind commented 3 years ago

@ryanheise @hacker1024

I just tested out the dart:ffi along with the event callbacks from the libVLC. (As we will definitely need async callbacks from C to Dart for notifying about playback events like position or volume change).

So, I directly get this exception... Discussed in more detail here. Seems dart:ffi isn't that ready (or maybe it is, and I need to study more).

Event got fired twice. But exception after that.

Event!
Event!
../../third_party/dart/runtime/vm/runtime_entry.cc: 3417: error: Cannot invoke native callback outside an isolate.
version=2.13.0-81.0.dev (dev) (Thu Feb 25 17:01:55 2021 -0800) on "windows_x64"
pid=9876, thread=3276, isolate_group=(nil)(0000000000000000), isolate=(nil)(0000000000000000)
isolate_instructions=0, vm_instructions=7ff6151ffa30
  pc 0x00007ff6153814ee fp 0x0000002197aff3e0 Unknown symbol
-- End of DumpStackTrace

My Dart code to call my c++ libvlc wrapper.

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();
  on(
    ffi.Pointer.fromFunction<callback>(dartMethod)
  );
  open();
  await Future.delayed(Duration(days: 1));
}

EDIT: Removed callbacks... literally no playback. Falling back to the Platform channel approach, getting Linux version ready.