Obtenir la vidéo à partir ALAsset
-
13-10-2019 - |
Question
Utilisation du nouveau cadre de la bibliothèque d'actifs disponible dans iOS 4 je vois que je peux obtenir l'URL pour une vidéo donnée en utilisant le UIImagePickerControllerReferenceURL. L'URL est retournée dans le format suivant:
assets-library://asset/asset.M4V?id=1000000004&ext=M4V
Je suis en train de télécharger cette vidéo sur un site Web afin comme une preuve rapide de concept que je suis en train les
NSData *data = [NSData dataWithContentsOfURL:videourl];
[data writeToFile:tmpfile atomically:NO];
Les données sont jamais initialisé dans ce cas. Quelqu'un at-il réussi à accéder à l'URL directement via la nouvelle bibliothèque d'actifs? Merci pour votre aide.
La solution
Voici une solution rapide propre pour obtenir des vidéos comme NSData. Il utilise le cadre de Photos comme ALAssetLibrary est obsolète iOS9:
IMPORTANT
Le cadre Bibliothèque actifs est obsolète iOS 9.0. Au lieu de cela, utilisez le cadre de photos à la place, qui dans iOS 8.0 et fournit plus tard plus de fonctionnalités et de meilleures performances pour travailler avec la bibliothèque de photos d'un utilisateur. Pour plus d'informations, voir des photos Cadre de référence.
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")
}
}
})
}
}
}
Autres conseils
J'utilise la catégorie suivante sur 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
Ce n'est pas la meilleure façon de le faire. Je réponds à cette question dans le cas où un autre utilisateur de SO est sur la même question.
En gros mon besoin était de pouvoir spouler le fichier vidéo dans un fichier tmp pour que je puisse le télécharger sur un site Web en utilisant ASIHTTPFormDataRequest. Il y a probablement un moyen de streaming à partir de l'url atout pour le téléchargement ASIHTTPFormDataRequest mais je ne pouvais pas comprendre. Au lieu de cela, j'ai écrit la fonction suivante pour supprimer le fichier dans un fichier tmp ajouter à 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;
}
Il vous allez ...
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);
}];
}
Vous pouvez utiliser une touche de préréglage différent si vous souhaitez ré-encoder le film (AVAssetExportPresetMediumQuality
par exemple)
Voici le Objectif C solution de Alonzo réponse, cadre photos à l'aide
-(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;
}
de la réponse de Zoul merci
Similar Code in Xamarin C#
Xamarin C # équivalent
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);