2011-06-05 20 views
29

Mi objetivo principal es transmitir un video de un servidor y cortarlo fotograma a fotograma durante la transmisión (para que pueda ser utilizado por OpenGL). Para ello, he utilizado el código que he encontrado en todas partes en Internet (por lo que recuerdo que era de código de ejemplo GLVideoFrame de Apple):Uso de AVAssetReader para leer (transmitir) desde un activo remoto

NSArray * tracks = [asset tracks]; 
NSLog(@"%d", tracks.count); 

for(AVAssetTrack* track in tracks) { 

    NSLog(@"type: %@", [track mediaType]); 

    initialFPS = track.nominalFrameRate; 
    width = (GLuint)track.naturalSize.width; 
    height = (GLuint)track.naturalSize.height; 


    NSError * error = nil; 

    // _movieReader is a member variable 
    @try { 
     self._movieReader = [[[AVAssetReader alloc] initWithAsset:asset error:&error] autorelease]; 
    } 
    @catch (NSException *exception) { 
     NSLog(@"%@ -- %@", [exception name], [exception reason]); 
     NSLog(@"skipping track"); 

     continue; 
    } 


    if (error) 
    { 
     NSLog(@"CODE:%d\nDOMAIN:%@\nDESCRIPTION:%@\nFAILURE_REASON:%@", [error code], [error domain], error.localizedDescription, [error localizedFailureReason]);           
     continue; 
    } 

    NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey; 
    NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]; 
    NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; 
    [_movieReader addOutput:[AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track 
                     outputSettings:videoSettings]]; 
    [_movieReader startReading]; 
    [self performSelectorOnMainThread:@selector(frameStarter) withObject:nil waitUntilDone:NO]; 
} 

Pero siempre consigo esta excepción en [[AVAssetReader alloc] initWithAsset:error:].

NSInvalidArgumentException -- *** -[AVAssetReader initWithAsset:error:] Cannot initialize an instance of AVAssetReader with an asset at non-local URL 'http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8' 

Así que mis dos preguntas son:

  1. es la excepción realmente me dice que AVAssetReader debe tener una URL local? ¿Se puede usar para transmitir (al igual que el resto de las clases AVFoundation)?
  2. Si el enfoque AVFoundation no funciona, ¿qué otras sugerencias para transmitir el video y dividir sus marcos al mismo tiempo?

Muchas gracias por su ayuda.

+1

consiguió un problema similar aquí.Sin embargo, en mi caso, en lugar de obtener una excepción, el AVAssetReader simplemente se niega a inicializar y arroja un 'Error Desconocido' (AVFoundationErrorDomain -11800). También intenté tomar las pistas de video, alimentarlo a una nueva composición de AVMutable y la correspondiente AVMutableCompositionTrack, y luego intenté crear un AVAssetReader a partir de ahí. Eso tampoco funciona. Editar: Por cierto, ese enfoque funciona para archivos mp4/mov locales. –

+0

Hay alguna API nueva en iOS que te ayudará a alcanzar tu objetivo final. Ver http://stackoverflow.com/questions/12500408/can-i-use-avfoundation-to-stream-downloaded-video-frames-into-an-opengl-es-textu/12500409#12500409 –

Respuesta

35

@Cocoanetics, no puedo hacer comentarios debido a la baja repetición. Pero lo que has dicho no parece ser del todo correcto. AVFoundation no parece distinguir tanto entre archivos locales como no locales, como lo hace entre la CLASE de archivos o protocolos utilizados. Hay una distinción MUY clara entre el uso de mp4/mov versus el uso del protocolo de transmisión HTTP Live a través de m3u8, pero las diferencias usando un mp4 local o remoto son un poco más confusas.

Para ampliar en lo anterior:

a) Si el activo 'a distancia' es un M3U8 (es decir, que está utilizando HTTP 'en vivo' streaming), entonces no hay posibilidad alguna. No importa si el M3U8 está en su sistema de archivos local o en un servidor remoto, por una multitud de razones, AVAssetReader y todas las funcionalidades asociadas con AVAsset simplemente NO funcionan. However, AVPlayer, AVPlayerItem etc would work just fine.

b) Si se trata de un MP4/MOV, se necesita un poco más de investigación. Local MP4/MOV's work flawlessly. Mientras que en el caso de MP4/MOV remotos, puedo crear (o recuperar de un AVPlayerItem o AVPlayer o AVAssetTracks) un AVURLAsset con el que a veces puedo inicializar un AVAssetReader con éxito (ampliaré el 'a veces 'también, en breve). SIN EMBARGO, copyNextSampleBuffer always returns nil in case of remote MP4's. Desde varias cosas UPTO el punto de invocar el trabajo copyNextSampleBuffer, no estoy 100% seguro de si:

i) copyNextSampleBuffer que no trabaja para de mp4 a distancia, después de todos los otros pasos después de haber tenido éxito, se pretende/espera funcionalidad.

ii) Que los 'otros pasos' parecen funcionar en absoluto para los MP4 remotos es un accidente de la implementación de Apple, y esta incompatibilidad simplemente está saliendo a relucir cuando pulsamos copyNextSampleBuffer .......... .... cuáles son estos 'otros pasos', detallaré en breve.

iii) Estoy haciendo algo mal al tratar de invocar copyNextSampleBuffer para MP4 remotos.

Así @Paula usted podría tratar de investigar un poco más con MOV a distancia/MP4 de.

Como referencia, aquí están los enfoques que probé para capturar un fotograma de vídeo:

a)

Crear AVURLAsset directamente desde la URL del video.

recuperar la pista de vídeo utilizando [tracksWithMediaType activo: AVMediaTypeVideo]

Preparar un AVAssetReaderTrackOutput usando la pista de vídeo como fuente.

Cree un AVAssetReader utilizando el AVURLAsset.

Agregue AVAssetReaderTrackOutput al AVAssetReader y comience a leer.

Recuperar imágenes usando copyNextSampleBuffer.

b)

Crear AVPlayerItem de la URL del video, y luego una AVPlayer de ella (o crear el AVPlayer directamente desde la URL).

Recupere la propiedad 'activo' del AVPlayer y cargue sus 'pistas' usando "loadValuesAsynchronouslyForKeys:".

Separe las pistas de tipo AVMediaTypeVideo (o simplemente llame a tracksWithMediaType: en el activo una vez que se carguen las pistas) y cree su AVAssetReaderTrackOutput utilizando la pista de video.

Cree AVAssetReader utilizando el 'activo' del AVPlayer, 'startReading' y luego recupere imágenes usando copyNextSampleBuffer.

c)

+ Crear una AVPlayer AVPlayerItem o AVPlayer directamente desde la URL del video.

KVO la propiedad 'pistas' del AVPlayerItem, y una vez que se cargan las pistas, separe las AVAssetTracks del tipo AVMediaTypeVideo.

Recupere el AVAsset de la propiedad 'activo' de AVPlayerItem/AVPlayer/AVAssetTrack.

Los pasos restantes son similares al enfoque (b).

d)

+ Crear una AVPlayer AVPlayerItem o AVPlayer directamente desde la URL del video.

KVO la propiedad 'pistas' del AVPlayerItem, y una vez que se carguen las pistas, separe las del tipo AVMediaTypeVideo.

Crea una composición de AVMutable, e inicializa una AVMutableCompositionTrack asociada de tipo AVMediaTypeVideo.

Inserte el CMTimeRange apropiado de la pista de video recuperada anteriormente, en este AVMutableCompositionTrack.

Al igual que en (b) y (c), ahora crear su AVAssetReader y AVAssetReaderTrackOutput, pero con la diferencia de que se utiliza el AVMutableComposition como el AVAsset base para inicializar su AVAssetReader y AVMutableCompositionTrack como el AVAssetTrack de base para su AVAssetReaderTrackOutput.

'startReading' y use copyNextSampleBuffer para obtener marcos de AVAssetReader.

P.S: Intenté el enfoque (d) aquí para evitar el hecho de que el AVAsset recuperado directamente de AVPlayerItem o AVPlayer no se comportaba. Así que quería crear un nuevo AVAsset de AVAssetTracks que ya tenía a mano. Es cierto que hacky, y quizás sin sentido (¿en qué otra parte se recuperaría la información de la pista si no fuera el AVAsset original?), Pero de todos modos valió la pena intentarlo.

He aquí un resumen de los resultados para diferentes tipos de archivos:

1) MOV local/MP4 de - Los 4 enfoques funciona sin problemas.

2) MOV/MP4 remotos: el activo y las pistas se recuperan correctamente en los enfoques (b) a (d), y el AVAssetReader también se inicializa, pero copyNextSampleBuffer siempre devuelve nil. En el caso de (a), la creación de AVAssetReader falla con un 'Error desconocido' NSOSStatusErrorDomain -12407.

3) de local M3U8 (accede a través de un servidor HTTP en app/local) - Enfoques (a), (b) y (c) fallan estrepitosamente como tratando de obtener una AVURLAsset/AVAsset en cualquier forma o modalidad de archivos transmitido a través de M3U8 es un mandado de tontos.

En el caso de (a), el activo no se crea en absoluto, y la initWithURL: invocan AVURLAsset falla con un 'Unknown Error' AVFoundationErrorDomain -11.800.

En el caso de (b) y (c), la recuperación del AVURLAsset desde AVPlayer/AVPlayerItem o AVAssetTracks devuelve ALGUN objeto, pero al acceder a la propiedad 'tracks' siempre devuelve una matriz vacía.

En el caso de (d), puedo recuperar y aislar las pistas de video con éxito, pero al intentar crear el AVMutableCompositionTrack, falla al intentar insertar el CMTimeRange desde la pista de origen en el AVMutableCompositionTrack, con un 'Error desconocido' NSOSStatusErrorDomain -12780.

4) de M3U8 remoto, se comportan exactamente igual que M3U8 del local.

No estoy educado enteramente de por qué existen estas diferencias, o no podría haber sido mitigado por Apple. Pero ahí tienes.

-1

El uso de URL no locales no es posible actualmente con AVFoundation, al menos no directamente.

0

@Cocanetics, eso no es cierto. Usted puede obtener un archivo remoto en AVMutableCompositionTrack

AVURLAsset* soundTrackAsset = [[AVURLAsset alloc]initWithURL:[NSURL URLWithString:@"http://www.yoururl.com/yourfile.mp3"] options:nil]; 

AVMutableCompositionTrack *compositionAudioSoundTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; 
[compositionAudioSoundTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration) 
           ofTrack:[[soundTrackAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] 
           atTime:kCMTimeZero error:nil]; 

Sin embargo, este enfoque no funciona muy bien con los archivos que tienen una mayor compresión como archivos MP4

+1

Necesito acceder al audio y datos de la pista de video de live m3u8 url. ¿Podrías ayudarme? podemos obtener AVAssets de live m3u8 url? – Punita

Cuestiones relacionadas