1

In my app, a user can send a file to others in a group chat. First, the user records some audio using their mic. The file is then touched up using FFMPEG. Then, the file is uploaded to Firebase Cloud Storage and if this is successful, a record is written in Firebase Realtime Database.

I'm getting the error below when the user records a long audio file and then presses submit. It almost seems as though FFMPEG hasn't finished processing the file...but I thought I used my async/await correctly to make sure that this processing is finished before moving on?

##MyAppFile## saveMyAppFileToCloudStorage Error: 'package:firebase_storage/src/reference.dart': Failed assertion: line 127 pos 12: 'file.absolute.existsSync()': is not true.

Psuedo-code:

  1. User records audio
  2. Audio file is processed using FFMPEG and the new processed file is created on the user's phone
  3. User hits submit, uploading the file to Cloud Storage and, if successful, writing a record to Realtime Database

Order of Functions After User Hits Submit:

  1. msgInput.dart -> sendMyAppFile()
  2. msgInput.dart -> prepareMyAppFileForSending()
  3. msgInput.dart -> runFFMPEGHighLow()
  4. message_dao.dart -> sendMyAppFile()
  5. message_dao.dart -> saveMyAppFileToCloudStorage() //ERROR COMES FROM THIS FUNCTION

The Code:

//msgInput.dart
Future<void> sendMyAppFile() async {
    if (sendableMyAppFileExists == 1) {
      final MyAppFileReadyToBeSent = await prepareMyAppFileForSending();

      if (MyAppFileReadyToBeSent == '1') {
        messageDao.sendMyAppFile(MyAppFile, filepath, filename); 
      } else {
      
      }
    }

    setState(() {
      sendableMyAppFileExists = 0;
    });
  }
  
  Future<String> prepareMyAppFileForSending() async {
    if (sendableMyAppFileExists == 1) {
      if (recordedMyAppFileFilterID == '1') {

        await runFFMPEGHighLow('1'); 

        return '1';
      }

      if (recordedMyAppFileFilterID == '2') {

        await runFFMPEGHighLow('2'); 

        return '1';
      }
    }

    return '0';
  }
  
  Future<void> runFFMPEGHighLow(String filterID) async { 
    if (filterID != '1' && filterID != '2') {
      return;
    }

    if (sendableMyAppFileExists == 1) {
      if (filterID == '1') {

        await FFmpegKit.executeAsync(/*...parms...*/);
        setState(() {
          currentMyAppFileFilename = currentMyAppFileFilename + '1.mp3'; 
        });

      }

      if (filterID == '2') {

        await FFmpegKit.executeAsync(/*...parms...*/);
        setState(() {
          currentMyAppFileFilename = currentMyAppFileFilename + '2.mp3';
        });

      }
    }
  }
  
//message_dao.dart
void sendMyAppFile(ChatData MyAppFile, String filepath, String filename) {
    saveMyAppFileToCloudStorage(filepath, filename).then((value) {
      if (value == true) {
        saveMyAppFileToRTDB(MyAppFile);
      }
    });
  }
  
Future<bool> saveMyAppFileToCloudStorage(String filepath, String filename) async {
    //filepath: /data/user/0/com.example.MyApp/app_flutter/MyApp/MyAppAudioFiles/MyAppFiles/2d7af6ae-6361-4be5-8209-8498dd17d77d1.mp3
    //filename: 2d7af6ae-6361-4be5-8209-8498dd17d77d1.mp3

    _firebaseStoragePath = MyAppFileStorageDir + filename;
    
    File file = File(filepath);

    try {
      await _firebaseStorage
          .ref(_firebaseStoragePath)
          .putFile(file);
      return true;
    } catch (e) {
      print('##MyAppFile## saveMyAppFileToCloudStorage Error: ' + e.toString()); //ERROR COMES FROM THIS LINE
      return false;
    }
    return true;
  }
whatwhatwhat
  • 1,991
  • 4
  • 31
  • 50

1 Answers1

1

I assume you're using the package ffmpeg_kit_flutter.

First, why it's not working: execute and executeAsync return FFmpegSession objects. The run of FFmpeg doesn't need to be finished for these methods to complete. In fact, the returned session object has methods like getState to monitor whether the run of FFmpeg has completed.

A good way to fix this: The documentation for executeAsync has a hint for what to do here.

Note that this method returns immediately and does not wait the execution to complete. You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.

You can set a completion callback by passing a function to executeAsync. Here's the full function signature from the docs:

Future<FFmpegSession> executeAsync(
  String command,
  [FFmpegSessionCompleteCallback? completeCallback = null,
  LogCallback? logCallback = null,
  StatisticsCallback? statisticsCallback = null]
)

FFmpegSessionCompleteCallback is just a function that accepts an FFmpegSession and returns nothing. You can provide your own.

void someCompletionFunction() {
  setState(() {
    currentMyAppFileFilename = currentMyAppFileFilename + '1.mp3'; 
  });
}

await FFmpegKit.executeAsync(/*...parms...*/, someCompletionFunction);

Future vs callback: If you prefer to use Futures and async-await instead of callbacks, you'll need to create your own Future and update it in the callback. See Dart, how to create a future to return in your own functions? for an example.

Wesley
  • 10,652
  • 4
  • 37
  • 52