apache / cordova-plugin-camera

Apache Cordova Plugin camera
https://cordova.apache.org/
Apache License 2.0
960 stars 1.52k forks source link

iOS13 file_uri error picking video from gallery #506

Closed litiobat closed 4 years ago

litiobat commented 4 years ago

Bug Report

Problem

In iOS 13, picking video from Roll returns a bad FILE_URI no proprietary (only affected picking video, no photo)

What is expected to happen?

Return a valid proprietary FILE_URI url to work with it (for upload to server, for example) like: file:///var/mobile/Containers/Data/Application/98D7C3AE-A24F-46CE-A3B4-CF03B26355BE/tmp/video.mp4

What does actually happen?

Returns a invalid non proprietary FILE_URI with no access to work with, like: file:///private/var/mobile/Containers/Data/PluginKitPlugin/43792FA5-A8F8-4ECA-8CCE-0C5877088858/tmp/trim.7F9B8FC4-3307-4452-9F96-46A5D9A9DDD5.MOV

Information

Using any iOS device, like iPhone upgraded to iOS 13, try to pick a video (no photo) and then load the FILE_URI returned by plugin. If you'll try to use this FILE_URI to any action, like upload the file to server for example, the log in xcode says "Error opening file. Error Domain=NSCocoaErrorDomain Code=257. You are not allowed to read this file." ** Note: This function works correctly before upgrade to iOS13. I've tried it to many iOS devices with the same result.

Command or Code

var options = { destinationType: 1, sourceType : 0, mediaType: 1 }; navigator.camera.getPicture(correcto, fail, options);

Environment, Platform, Device

MacOS Mojave, using Xcode 11 and iPhone Xs upgraded to iOS 13

Version information

Cordova 9, Camera Plugin upgraded today MacOS Mojave, using Xcode 11 and iPhone Xs upgraded to iOS 13

Checklist

litiobat commented 4 years ago

*** ADITIONAL INFO: I've found in Stack Overflow this information about this problem (to help you to solve it)

The problem was the image picker delegate passed the file's url to another class that then handled copying the file. This has worked fine for several years in our application but with iOS 13, once the image picker delegate goes away, the url immediately becomes invalid.

litiobat commented 4 years ago

Hi again! Here is the solution (sorry, I don't know how to open a new tree), to help any other user.

in CDVCamera.m change THIS:

to THIS:

markerio commented 4 years ago

@litiobat This is the case now for iOS 13.

We've just upgraded our testing devices to iOS 13, and we found that we aren't able to select video files from the gallery camera roll. Your fix should be merged with the master branch.

Any clues? Many thanks @jcesarmobile @shazron @timbru31 and to anybody who can help

markerio commented 4 years ago

Attention!

If your app has video upload feature, then this issue will be one of the most important issues you will face when your app users start upgrading their iOS devices to v13. I believe that not all cordova developers have upgraded their testing devices yet, thats why this thread is not getting so much attention.

Thanks for @litiobat and for his efforts in helping the community solving this issue. Please read his description and the comment he added then after.

I have an app in production, which has video upload feature. I forked the master plugin and applied @litiobat's fix, for any of you guys interested in solving this issue, you can use this fork below:

<plugin spec="https://github.com/markerio/cordova-plugin-camera.git" source="git" />

I have to add that this didn't solely fix the issue for me, because the returned path is not prefixed with the file:// protocol, so please keep in mind that in the success callback check for the file protocol and fix that accordingly, as follows:

function onVideoSourceSuccess(fileURI) { // fix fileURI = fileURI.indexOf('file://')==0?fileURI:'file://'+fileURI; // continue your code here }

I believe applying this fix in the master plugin is better and safer for all, however some developers can't wait for the next release.

Wish the best for all.

breautek commented 4 years ago

Sounds like there are already some potential solutions here. We'd welcome any PRs. Cordova is a small team of volunteers.

claudiozam commented 4 years ago

Hi, same problem on IOS 13.

rdcs commented 4 years ago

Just wanted to add my 2 cents. I have seen this problem too, when I upgraded to IOS 13. I am using Xam.Plugin.Media to access movie files and in my code I was using System.IO.File.Move and that is where the exception occurs. In looking at my internal debug and crash log I saw that the exception is on System.IO.FileSystem.DeleteFile, which is accurate since a move will delete the source file. I used System.IO.File.Copy and there was no issue. The only problem may be, what happens to all the files in the private directory? Do they get deleted when the app closes? Not really sure. But, the bottom line is that you can access movies from the Photos using the picker.

rdcs commented 4 years ago

Hi joaolnpr,

I am able to grab a camera video and using the share mechanism, get it into my app.  I don’t use the url directly, I immediately copy it local storage and use that item to access the video.

I use System.IO.File.Copy(src, dest)  - src is the url from the actual camera video and dest is my local storage.  This is under IOS 13.  Maybe this will help.

Best Regards,

rdcs

From: joaolnpr notifications@github.com Reply-To: apache/cordova-plugin-camera reply@reply.github.com Date: Monday, October 14, 2019 at 4:10 AM To: apache/cordova-plugin-camera cordova-plugin-camera@noreply.github.com Cc: Daly Robert rdcs@optonline.net, Comment comment@noreply.github.com Subject: Re: [apache/cordova-plugin-camera] iOS13 file_uri error picking video from gallery (#506)

Hello guys,

We are facing the same issue, any solution?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

tmk1991 commented 4 years ago

The directory for videos simply changed. I parse the file uri response to add in file:/// if missing and to remove private if its in the path. However, the change seeps to be within the URL :

Videos Broken: file:///private/var/mobile/Containers/Data/PluginKitPlugin/...

Photos work: file:///var/mobile/Containers/Data/Application/...

Even if i switch to DATA_URL it still returns the path above on videos

tmk1991 commented 4 years ago

After forking with @markerio 's suggestion, I found that my FILE_URI was still not being accepted. This time I was getting error code 5 - Encoding Err. :(

Now the URL is: file:///var/mobile/Containers/Data/Application/6CDD6A9A-4694-40FF-A381-82DFB8AAC19E/tmp/trim.0932D2E4-2811-49EF-8D12-6FA86EFDCA34.MOV

This is what ended up working: https://github.com/apache/cordova-plugin-file/issues/346#issuecomment-537696640

rituparnaGuha commented 4 years ago

@litiobat Hi is this work for ios lower version like 12 or else?

Taylorsuk commented 4 years ago

@litiobat fix worked for me on selection of the video from library, however the issue remains when recording a video and then returning the path for viewing.

Weirdly I also have a broken Android app. See the URL differences between Android and iOS in the images below:

Has anyone else had a broken android version?

Android

Screenshot 2019-10-23 at 22 13 12

iOS

Screenshot 2019-10-23 at 21 34 51
maxymczech commented 4 years ago

You sir are a true intellectual! Thank you, very cool.

gitdisrupt commented 4 years ago

Hi again! Here is the solution (sorry, I don't know how to open a new tree), to help any other user.

in CDVCamera.m change THIS:

  • (CDVPluginResult)resultForVideo:(NSDictionary)info { NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString]; return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; }

to THIS:

  • (CDVPluginResult)resultForVideo:(NSDictionary)info { NSString moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] path]; NSArray spliteArray = [moviePath componentsSeparatedByString: @"/"]; NSString lastString = [spliteArray lastObject]; NSError error; NSFileManager fileManager = [NSFileManager defaultManager]; NSString documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"]; NSString *filePath = [documentsDirectory stringByAppendingPathComponent:lastString]; [fileManager copyItemAtPath:moviePath toPath:filePath error:&error]; return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; }

I can confirm that this fix worked for us for iOS 13.2.2. We also tested this on an iPhone running iOS 12.4.1 for backward compatibility. Both are working correctly with this fix. I have not yet tested 13.2 or 13.1. Anyone?

riddletd commented 4 years ago

Hi again! Here is the solution (sorry, I don't know how to open a new tree), to help any other user. in CDVCamera.m change THIS:

  • (CDVPluginResult)resultForVideo:(NSDictionary)info { NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString]; return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; }

to THIS:

  • (CDVPluginResult)resultForVideo:(NSDictionary)info { NSString moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] path]; NSArray spliteArray = [moviePath componentsSeparatedByString: @"/"]; NSString lastString = [spliteArray lastObject]; NSError error; NSFileManager fileManager = [NSFileManager defaultManager]; NSString documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"]; NSString *filePath = [documentsDirectory stringByAppendingPathComponent:lastString]; [fileManager copyItemAtPath:moviePath toPath:filePath error:&error]; return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; }

I can confirm that this fix worked for us for iOS 13.2.2. We also tested this on an iPhone running iOS 12.4.1 for backward compatibility. Both are working correctly with this fix. I have not yet tested 13.2 or 13.1. Anyone?

This works for me. iOS 13.1.3

neerajsaxena0711 commented 4 years ago

So I am having a "Upload Video from gallery" feature on my app. When I run it on iOS emulator, it works as expected. That is, I am able to choose a video file from the gallery, and then upload it. However when I run the app on my iPhone, I am able to pick the file from the gallery and then I get error code - 1.

Here is my code -

My options are -

public options = { allowEdit: true, SAVEDPHOTOALBUM: true, sourceType: this.camera.PictureSourceType.SAVEDPHOTOALBUM, mediaType: this.camera.MediaType.VIDEO, destinationType: this.camera.DestinationType.NATIVE_URI, }

My getVideo function -

async openVideo() { try { await this.camera.getPicture(this.options).then((filreUri => { window.resolveLocalFileSystemURL(filreUri, FE => { FE.file(async file => { // this.presentToast("I have come here"); this.fileInfo = file; if (file.type == "video/mp4") { const FR = new FileReader(); FR.onloadend = ((res: any) => { let AF = res.target.result; this.blob = new Blob([new Uint8Array(AF)], { type: file.type }); console.log("Done"); }); FR.readAsArrayBuffer(file); } else { // } }); }, (error)=>{ console.log("error resolving file"+ error) }); })).catch((err) => { this.fileInfo = "" console.log("Inside err of camera.getpicture"); }); } catch (e) { } finally { // console.log( "Finally", JSON.stringify(this.fileInfo)); if (this.fileInfo != undefined) this.navCtrl.push('UpoadMediaPage', { "blob": this.blob}); } }

My code goes into - error resolving file - with error code - 1

I have tried the changes mentioned above by @markerio - changed my camera.h and camera.m files and also solution provided by @litiobat.

Am I doing something wrong? Kindly help.

saurabh-dk commented 4 years ago

Hi, can someone please give a sample code for video upload which is working? I have exactly same problem as @neerajsaxena0711 and @litiobat's solution and @markerio's fork did not work for me too. Same with @tmk1991's comment. Thanks.

markerio commented 4 years ago

@saurabhDuzumaki @neerajsaxena0711 Please find below the simplest implementation of picking a video from the camera roll in iOS13. Tested in all v13 releases including the latest v13.2.3.

First, please follow @litiobat fix and change CDVCamera.m file as suggested. I forked the master plugin and applied @litiobat's fix, here is the link for any of you guys interested in this fork:

<plugin spec="https://github.com/markerio/cordova-plugin-camera.git" source="git" />

Then, setup the plugin to retrieve a video from the device's gallery. The picker should be triggered as follows:

navigator.camera.getPicture(onSuccess, onFail, {
  saveToPhotoAlbum: false,
  correctOrientation: true,
  allowEdit: false,
  mediaType: Camera.MediaType.VIDEO,
  sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
  destinationType: Camera.DestinationType.FILE_URI
  });

The success callback should be as follows:

function onSuccess(fileURI) {
  // fix path for file:// protocol
  fileURI = (parseInt(fileURI.indexOf('file://'))==0)?fileURI:'file://'+fileURI;
  // resolve and locate file
  window.resolveLocalFileSystemURL(fileURI, function success(fileEntry) {
    // retrieve file entry
    fileEntry.file(function(fileObj) {
      // debug
      console.log(fileEntry.nativeURL);
      console.log(fileObj.localURL);
      console.log(fileObj.name);
      console.log(fileObj.size);
      // continue from here
      // ...
      // ...
    }, function(error) {
      console.log('file entry error');
      });
  }, function (error) {
    console.log('resolve error');
    });
  }

Please reply back if you find this helpful. Hope this helps.

guillem-sopra commented 4 years ago

@markerio 's solution works. If you want to use the Promise you could do as follows:

fileUri = await navigator.camera.getPicture(options).then((fileURI) => {
    return fileURI.indexOf('file://') === -1 ? `file://${fileURI}` : fileURI;
});
neerajsaxena0711 commented 4 years ago

Yes, I can confirm that @markerio 's solution is working on iOS 13.1.2. Tested on real device. Thanks a ton! Was scratching my head over this since a long time. Another issue that I am facing is that, I am unable to play the chosen video file using the video tag. The path that I receive is -

"cdvfile://localhost/temporary/trim.8AEDB9C9-6E4C-4F65-8029-D453842E91F6.MOV" I even tried converting it to nativePath using entry.toURL(); method which gives me the path

"file:///Users/Neeraj-saxena/Library/Developer/CoreSimulator/Devices/9D5725F4-968E-4669-8EAF-CCC0971BE8AF/data/Containers/Data/Application/6A311514-1F0D-416B-8895-84F1EBE0F58C/tmp/trim.0032325E-5FD8-4B5B-9937-A478BD958F8C.MOV"

My video tag is - <video autoplay controls #myvideo>

guillem-sopra commented 4 years ago

We are still seeing the problem when building in certain machines. In my Mac with Catalina 10.15.1 and Xcode 11.2.1 the issue is fully solved, building the exact same code in another one with Xcode 11.2 the problem happens. What I've noticed is that the uri retrieved with getPicture is different for the exact same file:

Notice 3 things: the private/ folder at the beginning, PluginKitPlugin instead of Applications and the different 'hash'.

I'm lost here ... 😢

breautek commented 4 years ago

You can't deploy to the app store using xcode 11.2, apple will flatly refuse the app during upload and tell you to upgrade tot he latest version. You need at least version 11.2.1 Perhaps this is an xcode bug?

Would be great if anybody else can confirm that they see the same behaviour between xcode 11.2 and 11.2.1.

markerio commented 4 years ago

@breautek You’re absolutely right! I agree.

@guillem-sopra I’d advise you to build your app using the latest Xcode version 11.2.1. Otherwise, as @breautek has mentioned, you may have issues when you upload your app to the store (using the Application Loader).

breautek commented 4 years ago

This stackoverflow post also appears to be related with a potential native solution, although the solution is written in swift, not objective-c.

Not really an iOS expert, so someone else will have to determine if this is applicable.

guillem-sopra commented 4 years ago

I've just updated to Xcode 11.2.1 and the problem still happens. The only thing that works is @litiobat 's solution: https://github.com/apache/cordova-plugin-camera/issues/506#issuecomment-534070130

breautek commented 4 years ago

@litiobat

Are you able to create a PR that implements your solution?

guillem-sopra commented 4 years ago

If he does not, I can. By no means I want to be credited for his work, but we really need this to be fixed 😞

markerio commented 4 years ago

@guillem-sopra Thanks for your update. I agree that applying @litiobat's fix should solve this issue, that's what I was talking about in my earlier comments.

guillem-sopra commented 4 years ago

There it is, just in case: https://github.com/apache/cordova-plugin-camera/pull/533

neerajsaxena0711 commented 4 years ago

Can anyone tell me how do I play the "chosen" video from the gallery? The URL that I get doesn't seem to work when embedded with the video tag. I have tried a lot of solutions like converting the URL to localURL, nativeURL, etc. But couldn't get the path right. Happening both on Android and iOS.

My File entry object is -

 {"name":"20191107_170431.mp4",
"localURL":"cdvfile://localhost/root/storage/07ED-F818/DCIM/Camera/20191107_170431.mp4",
"type":"video/mp4","lastModified":1573183466000,
"lastModifiedDate":1573183466000,
"size":15140263,"start":0,"end":15140263}
tryhardest commented 4 years ago

@Taylorsuk did you get it sorted on Android?

Taylorsuk commented 4 years ago

@tryhardest Afraid not. I elected to use standard HTML inputs in a round about way and took the opportunity to remove another Cordova plugin from my app.

neerajsaxena0711 commented 4 years ago

@Taylorsuk What issue are you facing on android?

neerajsaxena0711 commented 4 years ago

@litiobat @markerio

Can anyone tell me how do I play the "chosen" video from the gallery? The URL that I get doesn't seem to work when embedded with the video tag. I have tried a lot of solutions like converting the URL to localURL, nativeURL, etc. But couldn't get the path right. Happening both on Android and iOS.

My File entry object is -

{"name":"20191107_170431.mp4", "localURL":"cdvfile://localhost/root/storage/07ED-F818/DCIM/Camera/20191107_170431.mp4", "type":"video/mp4","lastModified":1573183466000, "lastModifiedDate":1573183466000, "size":15140263,"start":0,"end":15140263}

Taylorsuk commented 4 years ago

@neerajsaxena0711 I summarised all the issues in this here https://github.com/apache/cordova-plugin-camera/issues/506#issuecomment-545640764

paivaric commented 4 years ago

Tks @markerio, it worked for me.

wingo7 commented 4 years ago

IOS: The same issue, when I want to copy file with File plugin or upload it to firebase storage I have an error, FIle not found. Received file path is: 'file:///private/var/mobile/Containers/Data/PluginKitPlugin/89C88915-49F8-4B74-83D0-011C8B617DD9/tmp/trim.654E18A8-8AE3-4D51-96BD-3FB1D990E77C.MOV'. I can watch a video, I don't have any issues with that, the problem is only with upload/copy functions

samuelfaj commented 4 years ago

Thank GOD i found this!!!!

Thank you so much @neerajsaxena0711

rricamar commented 4 years ago

Does this mean that https://github.com/apache/cordova-plugin-camera/pull/533 is not necessary anymore? 🤔

rricamar commented 4 years ago

Does this mean that #533 is not necessary anymore? 🤔

Didn't see that issue mentioned was from UnityNativeGallery 😰

gritsa commented 3 years ago

Hi again! Here is the solution (sorry, I don't know how to open a new tree), to help any other user.

in CDVCamera.m change THIS:

  • (CDVPluginResult)resultForVideo:(NSDictionary)info { NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString]; return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; }

to THIS:

  • (CDVPluginResult)resultForVideo:(NSDictionary)info { NSString moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] path]; NSArray spliteArray = [moviePath componentsSeparatedByString: @"/"]; NSString lastString = [spliteArray lastObject]; NSError error; NSFileManager fileManager = [NSFileManager defaultManager]; NSString documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"]; NSString *filePath = [documentsDirectory stringByAppendingPathComponent:lastString]; [fileManager copyItemAtPath:moviePath toPath:filePath error:&error]; return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; }

THIS IS THE ONE THAT WORKED FOR ME!

I hope this is incorporated in the official plugin soon.

TylerBrinks commented 3 years ago

Are there plans to incorporate this change? While it works on a developer's environment, CI/CD processes build apps by way if install pods will revert manual changes to the non-working contained in the plugin's package.

PegasusAwesome commented 3 years ago

Hi all,

Just used the 5.0.1 version in my app, and this version has solved this problem for us. Thanks a million!

HassanALi128 commented 3 years ago

Hi all,

Just used the 5.0.1 version in my app, and this version has solved this problem for us. Thanks a million!

this works for me. Thanks

abhishek0196 commented 2 years ago

Hello everyone I tried the same method, I am still getting nil from

 NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] path];

Can anyone please help, I have all these three permissions related to camera in my info.plist

Is there any other key which I need to add to the plist file ?

codal-shubhamb commented 2 years ago

I have solved this issue just by removing and adding camera plugin in my application, You should try this too. The changes in Cordova-camera plugin which solves the issue are as following: Filename - https://github.com/apache/cordova-plugin-camera/blob/master/src/ios/CDVCamera.m and just search "ResultForVideo" and there are these three lines are added in latest plugin // On iOS 13 the movie path becomes inaccessible, create and return a copy if (IsAtLeastiOSVersion(@"13.0")) { moviePath = [self createTmpVideo:[[info objectForKey:UIImagePickerControllerMediaURL] path]]; }

MustakTalukder commented 1 month ago

Hi again! Here is the solution (sorry, I don't know how to open a new tree), to help any other user.

in CDVCamera.m change THIS:

  • (CDVPluginResult)resultForVideo:(NSDictionary)info { NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString]; return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; }

to THIS:

  • (CDVPluginResult)resultForVideo:(NSDictionary)info { NSString moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] path]; NSArray spliteArray = [moviePath componentsSeparatedByString: @"/"]; NSString lastString = [spliteArray lastObject]; NSError error; NSFileManager fileManager = [NSFileManager defaultManager]; NSString documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"]; NSString *filePath = [documentsDirectory stringByAppendingPathComponent:lastString]; [fileManager copyItemAtPath:moviePath toPath:filePath error:&error]; return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:filePath]; }

Hi All,