2010-12-28 13 views
12

Usando el nuevo marco de la biblioteca de activos disponible en iOS 4 veo que puedo obtener la url de un video dado usando UIImagePickerControllerReferenceURL. La url devuelto está en el siguiente formato:Obtener video de ALAsset

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

que estoy tratando de colocar este vídeo en una página web con el fin de una prueba rápida del concepto Estoy intentando el siguiente

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

datos nunca se inicializa en este caso. ¿Alguien ha logrado acceder a la url directamente a través de la nueva biblioteca de activos? Gracias por tu ayuda.

+0

Intenté con la opción propuesta por Rich pero no funciona. Estoy usando el mismo video almacenado en la biblioteca de iPhone para mi prueba y, a veces, el diccionario de información devuelto solo contiene UIImagePickerControllerReferenceURL. Traté de usar esa URL como entrada para videoAssetURLToTempFile pero al ejecutar ese método no ingresa al código para actualizar el bloque de resultados. No puedo identificar en qué circunstancias el método delegado UIImagePickerController didFinishPickingMediaWithInfo funciona correctamente. ¿Alguna ayuda, por favor? Gracias de antemano! –

+0

¿Es posible que esto sea un problema de versión de iOS? UIImagePickerControllerReferenceURL es el antiguo método para devolver los datos. –

Respuesta

9

Aquí hay una solución limpia y rápida para obtener videos a s NSData. Se utiliza el marco de fotos como ALAssetLibrary está obsoleta de iOS9:

IMPORTANTE

El marco de elementos de bibliotecas está en desuso a partir de iOS 9.0. En su lugar, utilice el marco de Fotos en su lugar, que en iOS 8.0 y posterior proporciona más funciones y un mejor rendimiento para trabajar con la biblioteca de fotos de un usuario. Para obtener más información, consulte la Referencia del marco de las fotos.

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") 
        } 
       } 
      }) 
     } 
    } 
} 
17

Esta no es la mejor manera de hacerlo. Estoy respondiendo esta pregunta en caso de que otro usuario de SO encuentre el mismo problema.

Básicamente mi necesidad era poder poner el archivo de video en un archivo tmp para poder subirlo a un sitio web usando ASIHTTPFormDataRequest. Probablemente haya una forma de transmisión desde la URL del recurso a la carga de ASIHTTPFormDataRequest pero no pude resolverlo. En cambio, escribí la siguiente función para colocar el archivo en un archivo tmp para agregarlo a 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; 
} 
+0

FYI: Tiene una fuga de 'buffer'. – randallmeadows

+0

Tiene razón, gracias. –

+0

@RichDominelli, fragmento increíble! ¡¡¡Gracias!!! – Suran

22

utilizo la siguiente categoría en 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 
+0

¡Gracias por esto! –

+0

Eso es lo que estoy buscando ... Gracias @zoul – fyasar

10

Hay que ir ...

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

Se puede utilizar una tecla predeterminada diferente si desea volver a codificar el película (AVAssetExportPresetMediumQuality por ejemplo)

+1

No entendí cómo obtuviste el activo en sí mismo? – Dejell

+0

Probablemente es demasiado tarde aquí, pero puede instanciar un objeto ALAsset desde una URL de referencia utilizando - el método [ALAssetLibrary assetForURL: resultBlock: failureBlock:]. – jpm

+0

Se debe aceptar la respuesta. Pero no se olvide de enviar exportAsynchronouslyWithCompletionHandler:^en el hilo principal, porque se ejecuta en segundo plano de forma predeterminada. – faviomob

1

Aquí está la solución Objetivo C de Alonzo respuesta, Utilización de las fotos marco

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

esto desde respuesta de Zoul gracias

Similar Code in Xamarin C# 

Xamarin C# Equivalente

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