4

After the user gave us permission to access his Camera Roll. We would like to grab the data and upload it to our services from inside our app. Is there a way to access the video data from the file? The only way to open the video file is to create an AVAsset. But that's not enough for me.

I'm aware off

func requestExportSessionForVideo(_ asset: PHAsset!,
                      options options: PHVideoRequestOptions!,
                 exportPreset exportPreset: String!,
                resultHandler resultHandler: ((AVAssetExportSession!,
                    [NSObject : AnyObject]!) -> Void)!) -> PHImageRequestID

But in my case I just want to upload the video to our service I don't want to do:

  1. first copy the video by doing an export into my local app data
  2. and then send that data up to our service.
  3. delete the data.

This approach above uses a lot of extra space and time and users with full 16GB iPhones it doesn't work well.

Ok this is what I tried so far using the URL

var anAcces = sourceURL?.startAccessingSecurityScopedResource

if !NSFileManager.defaultManager().fileExistsAtPath(sourceURL!.path!) {
    NSLog("not exist")
}

var aFileCoordinator = NSFileCoordinator(filePresenter:nil)
var anError: NSError?

aFileCoordinator.coordinateReadingItemAtURL(sourceURL!, options:.ForUploading, error:&anError, byAccessor:  { (newURL: NSURL!) -> Void in
    var data = NSData(contentsOfURL: newURL)
    })
if let unError = anError {
    NSLog("Error \(unError)")
}

sourceURL?.stopAccessingSecurityScopedResource

This logs the following:

2015-02-08 16:20:01.947 Cameo[15706:2288691] not exist
2015-02-08 16:20:01.991 Cameo[15706:2288691] Error Error Domain=NSCocoaErrorDomain Code=257 "The operation couldn’t be completed. (Cocoa error 257.)" UserInfo=0x170876480 {NSURL=file:///var/mobile/Media/DCIM/100APPLE/IMG_0155.MOV, NSFilePath=/var/mobile/Media/DCIM/100APPLE/IMG_0155.MOV, NSUnderlyingError=0x17005a9a0 "The operation couldn’t be completed. Operation not permitted"}
mvo
  • 609
  • 1
  • 5
  • 11
  • you should be able to get a URL from the AVAsset, then pass that to your uploader. It has been awhile since I've looked at this. Look at my questions/answers. I think someone suggested something that might work for you. – Paul Cezanne Feb 08 '15 at 19:33
  • I updated my questions with code I used to try to access the URL. I tried 2 techniques: NSFileManager and NSFileCoordinator – mvo Feb 08 '15 at 21:30
  • 1
    See http://stackoverflow.com/a/26144019/1050482 – Paul Cezanne Feb 09 '15 at 13:12
  • 3
    To my knowledge this does not solve the issue. I can retrieve the AVURLAsset from the PHAsset and read from the resulting URL (for example, read into an NSData object). But attempting to create an NSURLSessionUploadTask from that URL throws an error: `Failed to issue sandbox extension for file file:///var/mobile/Media/DCIM/100APPLE/IMG_0730.MOV, errno = 1`. This is the heart of the issue. @mvo mind adding this detail to the question? Anyone have a workaround for this? My shared app group is set up properly... – Alfie Hanssen Feb 10 '15 at 16:42

2 Answers2

2

Thanks to Paul suggestion I figured it out:

You have to create an PHImageManager requestAVAssetForVideo session in that block you have access to the file and read its data from the url.

    let imageManager = PHImageManager.defaultManager()
    let videoRequestOptions = PHVideoRequestOptions()

    videoRequestOptions.deliveryMode = .HighQualityFormat
    videoRequestOptions.version = .Current
    videoRequestOptions.networkAccessAllowed = true
    videoRequestOptions.progressHandler = { (progress: Double, error: NSError!, stop: UnsafeMutablePointer<ObjCBool>, [NSObject : AnyObject]!) -> Void in

        NSLog("Progress: %@", progress.description)
    }

    videoRequestOptions.progressHandler = { (progress: Double, error: NSError!, stop: UnsafeMutablePointer<ObjCBool>, [NSObject : AnyObject]!) -> Void in

        NSLog("Progress: %@", progress.description)
    }

    imageManager.requestAVAssetForVideo(nextAsset, options: videoRequestOptions, resultHandler: { (avAsset: AVAsset!, avAudioMix: AVAudioMix!, info: [NSObject : AnyObject]!) -> Void in

        if let nextURLAsset = avAsset as? AVURLAsset {

            let sourceURL = nextURLAsset.URL

            if NSFileManager.defaultManager().fileExistsAtPath(sourceURL.path!) {
                NSLog("exist file")
            }

            var data = NSData(contentsOfURL: sourceURL)

            if let aData = data {
                NSLog("length : <\(aData.length)")
            }
            else {
                NSLog("no data read.")
            }
        }
    }
mvo
  • 609
  • 1
  • 5
  • 11
  • 1
    See comment on question above...Reading into an NSData object works but attempting to create an NSURLSessionUploadTask from the AVURLAsset's URL throws an error: `Failed to issue sandbox extension for file file:///var/mobile/Media/DCIM/100APPLE/IMG_0730.MOV, errno = 1`. Any ideas on how to get around this or why it might be occurring? – Alfie Hanssen Feb 10 '15 at 16:43
  • Tumblr is seeing similar error, albeit from a URL constructed differently: https://github.com/tumblr/ios-extension-issues/blob/master/samples/BackgroundSessionErrors/ShareExtension/ShareViewController.m – Alfie Hanssen Feb 10 '15 at 16:46
  • 1
    I'm getting same error message as Alfie: "Failed to issue sandbox extension for file ...". Any solution to this? – Alyoshak Nov 15 '17 at 20:11
0

Regarding the issue:

Failed to issue sandbox extension for file file:///var/mobile/Media/DCIM/100APPLE/IMG_0730.MOV, errno = 1

My workaround for this issue was to create a temporary path which I was able to access the the Media-File:

  Future<void> loadAssets() async {
    List<Asset> resultList = <Asset>[];
    String error = 'No Error Detected';
    final temp = await Directory.systemTemp.create();
    List<File> imagesFileList = [];

    try {
      resultList = await MultiImagePicker.pickImages(
        maxImages: 300,
        enableCamera: true,
        selectedAssets: imagesAssetsList,
        cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
        materialOptions: MaterialOptions(
          actionBarColor: "#abcdef",
          actionBarTitle: "Example App",
          allViewTitle: "All Photos",
          useDetailsView: false,
          selectCircleStrokeColor: "#000000",
        ),
      );
    } on Exception catch (e) {
      error = e.toString();
    }
    if (!mounted) return;

    for (int i = 0; i < resultList.length; i++) {
      final data = await resultList[i].getByteData();
      imagesFileList.add(await File('${temp.path}/img$i').writeAsBytes(
          data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes)));
      print('pathnew: ${imagesFileList[i].path}');
      await uploadFileToStorage(imagesFileList[i].path);
    }

    setState(() {
      imagesAssetsList = resultList;

      _error = error;
    });
  }

I hope it will work!

Ayrix
  • 33
  • 6