سؤال

Using the new asset library framework available in iOS 4 i see that I can get the url for a given video using the UIImagePickerControllerReferenceURL. The url returned is in the following format:

assets-library://asset/asset.M4V?id=1000000004&ext=M4V

I am trying to upload this video to a website so as a quick proof of concept I am trying the following

NSData *data = [NSData dataWithContentsOfURL:videourl];
[data writeToFile:tmpfile atomically:NO];

Data is never initialized in this case. Has anyone managed to access the url directly via the new assets library? Thanks for your help.

هل كانت مفيدة؟

المحلول

Here is a clean swift solution to get videos as NSData. It uses the Photos framework as ALAssetLibrary is deprecated as of iOS9:

IMPORTANT

The Assets Library framework is deprecated as of iOS 9.0. Instead, use the Photos framework instead, which in iOS 8.0 and later provides more features and better performance for working with a user’s photo library. For more information, see Photos Framework Reference.

import Photos

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
    self.dismissViewControllerAnimated(true, completion: nil)

    if let referenceURL = info[UIImagePickerControllerReferenceURL] as? NSURL {
        let fetchResult = PHAsset.fetchAssetsWithALAssetURLs([referenceURL], options: nil)
        if let phAsset = fetchResult.firstObject as? PHAsset {
            PHImageManager.defaultManager().requestAVAssetForVideo(phAsset, options: PHVideoRequestOptions(), resultHandler: { (asset, audioMix, info) -> Void in
                if let asset = asset as? AVURLAsset {
                    let videoData = NSData(contentsOfURL: asset.URL)

                    // optionally, write the video to the temp directory
                    let videoPath = NSTemporaryDirectory() + "tmpMovie.MOV"
                    let videoURL = NSURL(fileURLWithPath: videoPath)
                    let writeResult = videoData?.writeToURL(videoURL, atomically: true)

                    if let writeResult = writeResult where writeResult {
                        print("success")
                    }
                    else {
                        print("failure")
                    }
                }
            })
        }
    }
}

نصائح أخرى

I use the following category on ALAsset:

static const NSUInteger BufferSize = 1024*1024;

@implementation ALAsset (Export)

- (BOOL) exportDataToURL: (NSURL*) fileURL error: (NSError**) error
{
    [[NSFileManager defaultManager] createFileAtPath:[fileURL path] contents:nil attributes:nil];
    NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:fileURL error:error];
    if (!handle) {
        return NO;
    }

    ALAssetRepresentation *rep = [self defaultRepresentation];
    uint8_t *buffer = calloc(BufferSize, sizeof(*buffer));
    NSUInteger offset = 0, bytesRead = 0;

    do {
        @try {
            bytesRead = [rep getBytes:buffer fromOffset:offset length:BufferSize error:error];
            [handle writeData:[NSData dataWithBytesNoCopy:buffer length:bytesRead freeWhenDone:NO]];
            offset += bytesRead;
        } @catch (NSException *exception) {
            free(buffer);
            return NO;
        }
    } while (bytesRead > 0);

    free(buffer);
    return YES;
}

@end

This is not the best way to do this. I am answering this question in case another SO user comes across the same issue.

Basically my need was to be able to spool the video file to a tmp file so I can upload it to a website using ASIHTTPFormDataRequest. There is probably a way of streaming from the asset url to the ASIHTTPFormDataRequest upload but I could not figure it out. Instead I wrote the following function to drop the file to a tmp file to add to ASIHTTPFormDataRequest.

+(NSString*) videoAssetURLToTempFile:(NSURL*)url
{

    NSString * surl = [url absoluteString];
    NSString * ext = [surl substringFromIndex:[surl rangeOfString:@"ext="].location + 4];
    NSTimeInterval ti = [[NSDate date]timeIntervalSinceReferenceDate];
    NSString * filename = [NSString stringWithFormat: @"%f.%@",ti,ext];
    NSString * tmpfile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];

    ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
    {

        ALAssetRepresentation * rep = [myasset defaultRepresentation];

        NSUInteger size = [rep size];
        const int bufferSize = 8192;

        NSLog(@"Writing to %@",tmpfile);
        FILE* f = fopen([tmpfile cStringUsingEncoding:1], "wb+");
        if (f == NULL) {
            NSLog(@"Can not create tmp file.");
            return;
        }

        Byte * buffer = (Byte*)malloc(bufferSize);
        int read = 0, offset = 0, written = 0;
        NSError* err;
        if (size != 0) {
            do {
                read = [rep getBytes:buffer
                          fromOffset:offset
                              length:bufferSize 
                               error:&err];
                written = fwrite(buffer, sizeof(char), read, f);
                offset += read;
            } while (read != 0);


        }
        fclose(f);


    };


    ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *myerror)
    {
        NSLog(@"Can not get asset - %@",[myerror localizedDescription]);

    };

    if(url)
    {
        ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
        [assetslibrary assetForURL:url 
                       resultBlock:resultblock
                      failureBlock:failureblock];
    }

    return tmpfile;
}

There you go...

AVAssetExportSession* m_session=nil;

-(void)export:(ALAsset*)asset withHandler:(void (^)(NSURL* url, NSError* error))handler
{
    ALAssetRepresentation* representation=asset.defaultRepresentation;
    m_session=[AVAssetExportSession exportSessionWithAsset:[AVURLAsset URLAssetWithURL:representation.url options:nil] presetName:AVAssetExportPresetPassthrough];
    m_session.outputFileType=AVFileTypeQuickTimeMovie;
    m_session.outputURL=[NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%f.mov",[NSDate timeIntervalSinceReferenceDate]]]];
    [m_session exportAsynchronouslyWithCompletionHandler:^
     {
         if (m_session.status!=AVAssetExportSessionStatusCompleted)
         {
             NSError* error=m_session.error;
             m_session=nil;
             handler(nil,error);
             return;
         }
         NSURL* url=m_session.outputURL;
         m_session=nil;
         handler(url,nil);
     }];
}

You can use a different preset key if you wish to re-encode the movie (AVAssetExportPresetMediumQuality for example)

Here is the Objective C solution of Alonzo answer, Using photos framework

  -(NSURL*)createVideoCopyFromReferenceUrl:(NSURL*)inputUrlFromVideoPicker{

        NSURL __block *videoURL;
        PHFetchResult *phAssetFetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[inputUrlFromVideoPicker ] options:nil];
        PHAsset *phAsset = [phAssetFetchResult firstObject];
        dispatch_group_t group = dispatch_group_create();
        dispatch_group_enter(group);

        [[PHImageManager defaultManager] requestAVAssetForVideo:phAsset options:nil resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {

            if ([asset isKindOfClass:[AVURLAsset class]]) {
                NSURL *url = [(AVURLAsset *)asset URL];
                NSLog(@"Final URL %@",url);
                NSData *videoData = [NSData dataWithContentsOfURL:url];

                // optionally, write the video to the temp directory
                NSString *videoPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%f.mp4",[NSDate timeIntervalSinceReferenceDate]]];

                videoURL = [NSURL fileURLWithPath:videoPath];
                BOOL writeResult = [videoData writeToURL:videoURL atomically:true];

                if(writeResult) {
                    NSLog(@"video success");
                }
                else {
                    NSLog(@"video failure");
                }
                 dispatch_group_leave(group);
                // use URL to get file content
            }
        }];
        dispatch_group_wait(group,  DISPATCH_TIME_FOREVER);
        return videoURL;
    }

this from Zoul's Answer thanks

Similar Code in Xamarin C#

Xamarin C# Equivalent

IntPtr buffer = CFAllocator.Malloc.Allocate(representation.Size);
NSError error;
            nuint buffered = representation.GetBytes(buffer, Convert.ToInt64(0.0),Convert.ToUInt32(representation.Size),out error);

            NSData sourceData = NSData.FromBytesNoCopy(buffer,buffered,true);
            NSFileManager fileManager = NSFileManager.DefaultManager;
            NSFileAttributes attr = NSFileAttributes.FromDictionary(NSDictionary.FromFile(outputPath));
            fileManager.CreateFile(outputPath, sourceData,attr);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top