Search code examples
iosxcodecordovacordova-pluginsphonegap

Cordova iOS - can't record a media even though the file is created


I am trying to record a sound file using cordova-plugin-media, but I am always getting this error:

{message: "Failed to initialize AVAudioRecorder: (null)↵", code: 1}

I am first creating the file, like this

iOSCreateFile(fileName, callback) {
    window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, (fs) => {
        fs.root.getFile(fileName, {
            create: true, 
            exclusive: false
        }, (fileEntry) => {
            console.log("fileEntry is file?" + fileEntry.isFile.toString());
            // here I am getting true, so the file is obviously created
            callback(fileEntry.nativeURL);
        }, (error) => {
            console.log('error create file: ' + error);
        });
    }, (error) => {
        console.log('error loading file system: ' + error);
    });
},

The fileEntry.nativeURL I am sending back looks like this

file:///var/mobile/Containers/Data/Application/0EE019AA-EFBA-4FB9-97EC-1F16FFDDA36B/tmp/1496663387924.wav

then, when this calls back, I am doing the following

// that long file path string is passed here
let soundRecord = new Media(filePath, () => {
    // success
    // more code
}, (error) => {
    console.log(error);
});

And once it tries to execute the new statement, it gives me the Failed to initialize AVAudioRecorder error...

What am I doing wrong please?

EDIT: I also tried not creating a file, but only passing a file name string to the new Media object, like "1240215251.wav", and it's supposed to create it for me, but I am still getting the same error.

UPDATE: I have tried to catch the error in the native code, but I don't understand it very well, when I am debugging I can only see where the error is generated, but nothing else, please check the below code, I have commented where needed

- (void)startRecordingAudio:(CDVInvokedUrlCommand*)command {
    NSString* callbackId = command.callbackId;

#pragma unused(callbackId)

    NSString* mediaId = [command argumentAtIndex:0];
    CDVAudioFile* audioFile = [self audioFileForResource:[command argumentAtIndex:1] withId:mediaId doValidation:YES forRecording:YES];
    __block NSString* jsString = nil;
    __block NSString* errorMsg = @"";

    if ((audioFile != nil) && (audioFile.resourceURL != nil)) {
        __weak CDVSound* weakSelf = self;
        void (^startRecording)(void) = ^{
            NSError* __autoreleasing error = nil;
            if (audioFile.recorder != nil) {
                [audioFile.recorder stop];
                audioFile.recorder = nil;
            }
            if ([weakSelf hasAudioSession]) {
                if (![weakSelf.avSession.category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) {
                    [weakSelf.avSession setCategory:AVAudioSessionCategoryRecord error:nil];
                }

                if (![weakSelf.avSession setActive:YES error:&error]) {
                    // other audio with higher priority that does not allow mixing could cause this to fail
                    errorMsg = [NSString stringWithFormat:@"Unable to record audio: %@", [error localizedFailureReason]];
                    // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('cordova-plugin-media.Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_ABORTED];
                    jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova-plugin-media.Media').onStatus", mediaId, MEDIA_ERROR, [weakSelf createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
                    [weakSelf.commandDelegate evalJs:jsString];
                    return;
                }
            }
            NSDictionary *audioSettings = @{AVFormatIDKey: @(kAudioFormatMPEG4AAC),
                                             AVSampleRateKey: @(44100),
                                             AVNumberOfChannelsKey: @(1),
                                             AVEncoderAudioQualityKey: @(AVAudioQualityMedium)
                                             };
            audioFile.recorder = [[CDVAudioRecorder alloc] initWithURL:audioFile.resourceURL settings:audioSettings error:&error];
            // HERE ^
            // Just after this audioFile.recorder declaration, the error variable below gets a value -  NSError *   domain:
            // @"NSOSStatusErrorDomain" - code: 1718449215  0x0000000170247e90

            bool recordingSuccess = NO;
            if (error == nil) {
                audioFile.recorder.delegate = weakSelf;
                audioFile.recorder.mediaId = mediaId;
                audioFile.recorder.meteringEnabled = YES;
                recordingSuccess = [audioFile.recorder record];
                if (recordingSuccess) {
                    NSLog(@"Started recording audio sample '%@'", audioFile.resourcePath);
                    jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%d);", @"cordova.require('cordova-plugin-media.Media').onStatus", mediaId, MEDIA_STATE, MEDIA_RUNNING];
                    [weakSelf.commandDelegate evalJs:jsString];
                }
            }

            if ((error != nil) || (recordingSuccess == NO)) {
                // It then enters in one of below cases
                if (error != nil) {
                    errorMsg = [NSString stringWithFormat:@"Failed to initialize AVAudioRecorder: %@\n", [error localizedFailureReason]];
                } else {
                    errorMsg = @"Failed to start recording using AVAudioRecorder";
                }
                audioFile.recorder = nil;
                if (weakSelf.avSession) {
                    [weakSelf.avSession setActive:NO error:nil];
                }
                jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova-plugin-media.Media').onStatus", mediaId, MEDIA_ERROR, [weakSelf createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
                [weakSelf.commandDelegate evalJs:jsString];
            }
        };
        ...
        // More irrelevant code of the method
        ...
}

Solution

  • The issue could have probably happened because of the usage of cordova media plugin VERSION 3.0.0. This version had issues with recording "wav" files in iOS. An issue was already raised in media plugin's official issue tracker and its fixed in VERSION 3.0.1

    So upgrading to the latest version of the plugin should resolve this issue. In this particular, the plugin wouldn't have got installed properly in the first try. Hopefully it should resolve the issue on proper upgrade. Cheers