Canardoux / flutter_sound

Flutter plugin for sound. Audio recorder and player.
Mozilla Public License 2.0
877 stars 573 forks source link

[HELP] How to retrieve the file or byte list from a recording on the flutter_web? #592

Closed ReniDelonzek closed 3 years ago

ReniDelonzek commented 3 years ago

I looked in the documentation but found nothing about it. I need the file / bit list to send the recording to the server

Larpoux commented 3 years ago

I do not really understand your question. Downloading a file from internet is something probably easy, but has nothing to do with Flutter Sound. The file can be anything, maybe the picture of a cat, a sound, a video, ... This is not Flutter Sound job to download a remote file.

ReniDelonzek commented 3 years ago

Sorry, I must have expressed myself badly.

What I need to do is retrieve the file recorded by flutter_sound using the command flutterSoundRecorder.startRecorder (toFile: path). This running in the browser (web flutter)

Larpoux commented 3 years ago

So, you record a file "path" (toFile: path). This file contains your recording, and you can upload it to your server.

Do I understand correctly ?

ReniDelonzek commented 3 years ago

Yes. However, it is not possible to recover a file through its path on the web (because the file class comes from dart.io).

In the documentation it is mentioned that the file url on the web is saved in localStorage the LocalSessionStorage key will contain the URL of the recorded object

However there is no mention of any way to recover this url or how to manipulate it

Larpoux commented 3 years ago

Whow! Do you mean that your are using Flutter Sound On Web ?

I did not understood that. Sorry.

Just a moment, please...

Larpoux commented 3 years ago

This thing is a little tricky, because Web App does not have access to a real storage. I am going back in a few minutes...

Larpoux commented 3 years ago

The answer is here.

But I agree that it is not very explicit how to retrieve the URI. I am going to do better doc, now. I will post you something when done. (1/2 hour ?)

ReniDelonzek commented 3 years ago

Whow! Do you mean that your are using Flutter Sound On Web ?

Yes!

I will post you something when done. (1/2 hour ?)

Of course, I wait without problems

Larpoux commented 3 years ago

You can read this

Basically, if you specified "myPath" as the startRecorder() parameter, then you can retrieve the URI doing :

myUrl = window.localStorage['myPath'];

I do not remember well. Just that it was really easy. Please let me know if you succeed. And send me your proposal that could be inserted in τ documentation.

ReniDelonzek commented 3 years ago

Okay, I will test this and put the result here. Thank you very much

ReniDelonzek commented 3 years ago

Hello, I tried with the following code

      String path = '_flutter_sound';
      await recorderModule.openAudioSession();
      await recorderModule.startRecorder(
        toFile: path,
        codec: codec,
        numChannels: 1,
      );

And

await recorderModule.stopRecorder();
await recorderModule.closeAudioSession();

var myPref = window.localStorage['_flutter_sound'];
print('URL: ${myPref}');

However the result produced by window.localStorage ['_ flutter_sound']; was null

I noticed that this may be related to the order of execution of the data, because moments later it was printed on the console

recorder stopped
URL: null
On data available : Blob flutter_sound_recorder.js:278
recorder stopped

recorder stopped It prints twice, once after calling recorderModule.stopRecorder(), and once, a while later

ReniDelonzek commented 3 years ago

It may be relevant to mention that I tested it on the edge browser. From what I understand, it behaves a little differently in Chrome, but neither worked as expected

Larpoux commented 3 years ago

@ReniDelonzek ,

I am going to look to your issue in a few days (you are now near the top of the TODO stack 😉 ). I will finish to complete #590 and I will handle your issue. Perhaps I will create a new API verb to get the URL or erase the recorded object.

Patience...

ReniDelonzek commented 3 years ago

Right. thanks

Larpoux commented 3 years ago

@ReniDelonzek ,

I am working on your issue. I have decided that the instruction stopRecorder() will return a future to the URI of the recorded object.

   URL myURL = await myRecorder.stopRecorder();

But I want also that things are same on iOS and Android.

  myRecorder.startRecorder(path: 'foo'); // 'foo' is not a valid path file, so a temporary file 'foo' will be created
  ...
  URL myURL = await  myRecorder.stopRecorder(); // Returns an URI to the temporary file

I create also two new verbs

      myRecorder.deleteRecord('foo');
      URL myURL = await myRecorder.getRecordURL('foo'); // not sure that it is necessary
ReniDelonzek commented 3 years ago

This looks pretty good. The important thing is to maintain consistency in the way you work for both mobile and web.

Looking forward to testing!

Larpoux commented 3 years ago

Hi @ReniDelonzek ,

I did the improvements that you requested. It works fine on the three platforms :

I am currently rewriting asynchronous processing done by τ. This is a major change and tricky. After the changes, we will be able to do :

myPlayer.openAudioSession(); // no need to wait
myPlayer.startPlayer(...); // no need to wait anymore the end of `openAudioSession()`

Also if the app is waiting on something (for example) :

await myPlayer.pausePlayer();

and the app does for example :

await myPlayer.pausePlayer(); // another call to the same function

or worst :

myPlayer.stopPlayer();

The first call to pausePlayer will receive an exception instead of being stuck for ever.

τ is in the middle of two asynchronous clients : the OS and the App. Managing asynchronous processing is really tough.

I must do many tests before doing any release. Probably in a few days. Thank you for your patience.

Larpoux commented 3 years ago

@ReniDelonzek , Flutter Sound 7.6.0 is released. stopRecorder() now returns the URL of the recorded file. see here. It works on

Sorry for the delay. I hope that I am not too late for your own developments.

ReniDelonzek commented 3 years ago

Hello, first of all thank you for your excellent work! I tried to test this on the web with:

path = '_flutter_sound_${DateTime.now().millisecondsSinceEpoch}';
      await recorderModule.openAudioSession();

      await recorderModule.startRecorder(
        toFile: path,
        codec: codec,
        numChannels: 1,
      );

and

 String url = await recorderModule.stopRecorder();
      print('url: ${url}');

However, recording never starts, and no error log is printed. Am I doing something wrong?

Larpoux commented 3 years ago

Which codec ?

ReniDelonzek commented 3 years ago

Codec.opusWebM

Larpoux commented 3 years ago

Are you using chrome or firefox ? On witch device ? With chrome you can open the console with "More tools > Developer tools"

ReniDelonzek commented 3 years ago

Hello, I am using Edge in MacOs

I am creating min example -> https://github.com/ReniDelonzek/flutter_example

My edge version -> 88.0.705.56 (64 bits)

Flutter Doctor

[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 3.5)
[✓] VS Code (version 1.52.1)
[✓] Connected device (4 available)

• No issues found!
Larpoux commented 3 years ago

Reni,

I really needs your logs to help you.

ReniDelonzek commented 3 years ago

What logs can I provide for you? As I said, the application prints few logs, and no errors.

The only logs I have are the ones printed on the browser console:

FS:---> openAudioSession ---> openAudioSession flutter_sound_recorder.js:45 initializeFlautoRecorder main.dart.js:4741 <--- openAudioSession

I hosted my example on github pages, you can try it here

ReniDelonzek commented 3 years ago

Hello @Larpoux, any predictions to look at this, or even some roadmap that I can follow?

Larpoux commented 3 years ago

OK reni, I am going look to your example on Github. I will post you something in a few hours.

Larpoux commented 3 years ago

Reni, I confirm that it seems that there is a big problem in τ for Flutter Web.

I am going to fix it this afternoon. Sorry for this inconvenience.

Larpoux commented 3 years ago

@ReniDelonzek ,

I fixed something in Flutter Sound on Web. But before doing a release, I would like to test your App. Could you post the sources ?

ReniDelonzek commented 3 years ago

@Larpoux My original app is a little too big to share, but you can use the example I published, working on it I can port it to my

Larpoux commented 3 years ago

@ReniDelonzek ,

I fixed a little bug in version 7.6.7 .

Your index.html is not good. The correct includes are :

  <script src="https://cdn.jsdelivr.net/npm/tau_sound_core@7.6.7/js/flutter_sound/flutter_sound.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/tau_sound_core@7.6.7/js/flutter_sound/flutter_sound_player.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/tau_sound_core@7.6.7/js/flutter_sound/flutter_sound_recorder.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/howler@2/dist/howler.min.js"></script>

I run your example with this modifications in index.html and it runs OK now.

Note: I must look how to specify version range instead of a specific version.

ReniDelonzek commented 3 years ago

@Larpoux

I did a quick test here and it looks like it worked really well!

There was only one detail, that even after the stop the browser window continued to indicate that it is recording, is it because in my example I do not call .closeAudioSession ()?

Captura de Tela 2021-02-15 às 13 56 54

Thank you very much for your excellent work

Larpoux commented 3 years ago

Calling several time "openAudioSession" without any "closeAudioSession" is not OK. But I am not sure this explains your problem.

I am going to look to this now.

Larpoux commented 3 years ago

BTW:

  <script src="https://cdn.jsdelivr.net/npm/tau_sound_core@7/js/flutter_sound/flutter_sound.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/tau_sound_core@7/js/flutter_sound/flutter_sound_player.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/tau_sound_core@7/js/flutter_sound/flutter_sound_recorder.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/howler@2/dist/howler.min.js"></script>

This seems ok, to accept a version range "7.x.x". But I think this is dangerous in production, because you will not be able to control the version which will be used by your customer. Probably you want to test/check that the version used by your customer works well with your App.

ReniDelonzek commented 3 years ago

Yes, I added the full version to my dependencies so I don't have any problems with that.

Regarding the problem, if I'm not mistaken in previous versions the 'closeAudioSession' was called under the hood by stopRecorder, but it seems that this has been removed, so that may be the problem

ReniDelonzek commented 3 years ago

I tested it here by calling closeAudioSession right after stopRecorder, but the recording indicator continues

Larpoux commented 3 years ago
     floatingActionButton: FloatingActionButton(
        onPressed: () async {
          if (!recorderModule.isRecording) {
            String path =
                '_flutter_sound_${DateTime.now().millisecondsSinceEpoch}';
            await recorderModule.openAudioSession();

            await recorderModule.startRecorder(
              toFile: path,
              codec: Codec.opusWebM,
              numChannels: 1,
            );
          } else {
            String uri = await recorderModule.stopRecorder();
            print(uri);
            await recorderModule.closeAudioSession();
          }
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),

I do not see any problem : your button is a flip/flop : once it starts the recorder, and after it closes the recorder.

Note : in your real application, if you need to do several records, you will probably open the session during the initialization and not each time you startRecorder()

Larpoux commented 3 years ago

Note also that Flutter is doing multi-tasks : it is possible that your App calls two times your function very quickly and the recorder is not yet stopped the second time. In a real app, you probably need to disable your button, or something like that during the stopRecorder processing.

ReniDelonzek commented 3 years ago

I tried this code, but the browser window still indicates it is recording, even after stopRecorder and closeAudioSession

Captura de Tela 2021-02-15 às 14 32 46

My real use case will be a chat application. However, I believe that the audio focus must be requested and released between the audio recording, since when not recording, the user may want to use the focus for another application.

Note also that Flutter is doing multi-tasks : it is possible that your App calls two times your function very quickly and the recorder is not yet stopped the second time. In a real app, you probably need to disable your button, or something like that during the stopRecorder processing.

Yes, I will take this care in the actual application

Larpoux commented 3 years ago

By the way, I am curious how you can live with an App which does not work on iOS or Safari.

The current situation is really bad : we really must support webkit soon.

ReniDelonzek commented 3 years ago

Almost none (or none) of my clients use safari as a browser, in any case it will not be difficult to guide you to use edge / chrome instead. I just need it to work well on these browsers

ReniDelonzek commented 3 years ago

@Larpoux

I'm looking at this now and I couldn't understand how I can work with the returned url. Could you give me an example of how I get a List of bytes from the url returned by stopRecorder? I need to send the file to Firebase

ReniDelonzek commented 3 years ago

Hello!

I've been working on it today. The correct way to retrieve a list of bytes (which was my original question) is with:

final result = await http.get(_recordingUrl);
return result.bodyBytes;

I came to this code looking at the example here

I tried this code, but the browser window still indicates it is recording, even after stopRecorder and closeAudioSession

Captura de Tela 2021-02-15 às 14 32 46

Regarding this problem, I found out that it is a problem in your js code, I found this answer in the stackoverflow that indicates how to solve

https://stackoverflow.com/questions/44274410/mediarecorder-stop-doesnt-clear-the-recording-icon-in-the-tab

Thank you very much for your patience!

ReniDelonzek commented 3 years ago

With this merged PR solves the last problem. Thank you very much