从ALAsset获取视频

12

在iOS 4中,使用新的资源库框架,我发现可以使用UIImagePickerControllerReferenceURL获取给定视频的URL。返回的URL格式如下:

assets-library://asset/asset.M4V?id=1000000004&ext=M4V
我正在尝试将这个视频上传到一个网站上,为了快速验证概念,我正在尝试以下操作。
NSData *data = [NSData dataWithContentsOfURL:videourl];
[data writeToFile:tmpfile atomically:NO];

在这种情况下,数据从未被初始化。有没有人成功通过新的资产库直接访问URL?感谢您的帮助。


我尝试使用Rich提出的选项,但没有成功。 我正在使用存储在iPhone库中的相同视频进行测试,有时返回的信息词典仅包含UIImagePickerControllerReferenceURL。我尝试使用该URL作为videoAssetURLToTempFile的输入,但是当执行该方法时,不会进入代码以更新结果块。 我无法确定UIImagePickerController didFinishPickingMediaWithInfo委托方法在何种情况下能正常工作。请求帮助。 谢谢! - user602414
这可能是iOS版本问题吗?UIImagePickerControllerReferenceURL是返回数据的旧方法。 - Rich Dominelli
6个回答

22

我在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

这就是我要找的.. 谢谢 @zoul - fyasar

17

这不是最好的方法。 我回答这个问题只是为了以防其他SO用户遇到相同的问题。

基本上,我的需求是将视频文件存储到临时文件中,以便使用ASIHTTPFormDataRequest将其上传到网站。 可能有一种从资产URL流式传输到ASIHTTPFormDataRequest上传的方法,但我无法弄清楚。 相反,我编写了以下函数将文件放入临时文件中,以添加到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;
}

你的程序有泄漏“缓冲区”的问题。 - randallmeadows

11

以下是一种干净简洁的解决方案,用于获取视频数据并将其作为NSData返回。

它使用了Photos框架,因为ALAssetLibrary在iOS9中已经过时:

重要提示:

Assets Library框架在iOS 9.0中已经弃用。 相反,请改用Photos框架,后者在iOS 8.0及更高版本中提供更多功能和更好的性能,用于处理用户的照片库。 有关更多信息,请参见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")
                    }
                }
            })
        }
    }
}

谢谢你!我被这个问题困扰了很长时间。 - Nico Dioso

10

就是这样...

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);
     }];
}

如果您想重新编码电影,可以使用不同的预设密钥(例如AVAssetExportPresetMediumQuality


1
我不明白你是如何获取资产本身的? - Dejell
可能已经晚了,但是您可以使用-[ALAssetLibrary assetForURL:resultBlock:failureBlock:]方法从引用URL实例化一个ALAsset对象。 - jpm
应该被接受的答案。但不要忘记在主线程中分派exportAsynchronouslyWithCompletionHandler:^,因为它默认在后台运行。 - m8labs
有没有使用PHAsset的方法?ALAsset API在iOS9中已被弃用。 - d0n13

1

这是使用照片框架的Alonzo答案的Objective C解决方案。

  -(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;
    }

0

这是来自Zoul的答案 谢谢

Similar Code in Xamarin C#

Xamarin C# 等效

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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接