iphone - como - Usar AVAssetReader para leer(transmitir) desde un recurso remoto
como usar el arroba en instagram (3)
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 eso, he usado este código que encontré en todas partes en Internet (como recuerdo que fue del código de muestra GLVideoFrame de Apple):
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 recibo 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''
Entonces mis dos preguntas son:
- ¿La excepción realmente me está diciendo que
AVAssetReader
debe tener una URL local? ¿Se puede usar para transmisión (al igual que el resto de las clases deAVFoundation
)? - Si el enfoque de
AVFoundation
no funciona, ¿cuáles son otras sugerencias para transmitir el video y dividir sus cuadros al mismo tiempo?
Muchas gracias por tu ayuda.
@Cocanetics, eso no es verdad. 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 archivos que tienen una mayor compresión como MP4s
@Cocoanetics, no puedo hacer comentarios debido a la baja rep. 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. Existe una distinción MUY clara entre el uso de mp4 / mov en lugar del uso del protocolo de transmisión en vivo HTTP a través de m3u8, pero las diferencias con un mp4 local o remoto son un poco más difusas.
Para ampliar lo anterior:
a) Si su activo ''remoto'' es un M3U8 (es decir, está utilizando la transmisión HTTP ''en vivo''), entonces no hay posibilidad alguna. No importa si el M3U8 está en su sistema de archivos local o en un servidor remoto, por varias razones, AVAssetReader y todas las funciones asociadas con AVAsset 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''s remotos, puedo crear (o recuperar de un AVPlayerItem o AVPlayer o AVAssetTracks) un AVURLAsset con el que a veces puedo inicializar AVAssetReader correctamente (ampliaré el ''a veces'' también, en breve). SIN EMBARGO, copyNextSampleBuffer always returns nil in case of remote MP4''s
. Desde varias cosas hasta el punto de invocar el trabajo copyNextSampleBuffer, no estoy 100% seguro de si:
i) copyNextSampleBuffer no funciona para mp4 remotos, después de que todos los demás pasos hayan sido exitosos, es la funcionalidad prevista / esperada.
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'', los detallaré en breve.
iii) Estoy haciendo algo mal al intentar invocar copyNextSampleBuffer para MP4 remotos.
Así que @Paula podría intentar investigar un poco más con los MOV / MP4 remotos.
Para referencia, aquí están los enfoques que intenté para capturar un fotograma de los videos:
un)
Crea un AVURLAsset directamente desde la URL del video.
Recupere la pista de video usando [active tracksWithMediaType: AVMediaTypeVideo]
Prepare un AVAssetReaderTrackOutput utilizando la pista de video como fuente.
Cree un AVAssetReader utilizando el AVURLAsset.
Agregue AVAssetReaderTrackOutput al AVAssetReader y comience a leer.
Recuperar imágenes utilizando copyNextSampleBuffer.
segundo)
Cree un AVPlayerItem a partir de la URL del video, y luego un AVPlayer a partir de él (o cree 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 usando la pista de video.
Cree AVAssetReader usando el ''activo'' de AVPlayer, ''startReading'' y luego recupere imágenes usando copyNextSampleBuffer.
do)
Cree un AVPlayerItem + AVPlayer o AVPlayer directamente desde la URL del video.
KVO la propiedad de ''pistas'' de AVPlayerItem, y una vez que se cargan las pistas, separe las pistas de AVAsset del tipo AVMediaTypeVideo.
Recupere el AVAsset de la propiedad ''asset'' de AVPlayerItem / AVPlayer / AVAssetTrack.
Los pasos restantes son similares al enfoque (b).
re)
Cree un AVPlayerItem + AVPlayer o AVPlayer directamente desde la URL del video.
KVO la propiedad ''tracks'' del AVPlayerItem, y una vez que se carguen las pistas, separe las del tipo AVMediaTypeVideo.
Cree una composición de AVMutable, e inicialice 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 crea tu AVAssetReader y AVAssetReaderTrackOutput, pero con la diferencia de que usas AVMutableComposition como AVAsset base para inicializar tu AVAssetReader y AVMutableCompositionTrack para tu AVAssetReader
''startReading'' y use copyNextSampleBuffer para obtener marcos de AVAssetReader.
PD: intenté acercarme (d) aquí para evitar el hecho de que el AVAsset recuperado directamente de AVPlayerItem o AVPlayer no se estaba comportando. Así que quería crear un nuevo AVAsset a partir de las AVAssetTracks que ya tenía en la mano. Es cierto que intrépido, y tal vez sin sentido (¡en qué otro lugar se recuperaría finalmente la información de la pista, si no la AVAsset original!), Pero de todos modos valió la pena intentarlo desesperadamente.
Aquí hay un resumen de los resultados para diferentes tipos de archivos:
1) MOV / MP4 locales: los 4 enfoques funcionan perfectamente.
2) MOV / MP4 remotos: el activo y las pistas se recuperan correctamente en los enfoques (b) a (d), y el AVAssetReader se inicializa también, pero copyNextSampleBuffer siempre devuelve nulo. En el caso de (a), la creación del AVAssetReader falla con un ''Error desconocido'' NSOSStatusErrorDomain -12407.
3) M3U8 locales (a los que se accede a través de un servidor HTTP local / de la aplicación): los enfoques (a), (b) y (c) fallan miserablemente al intentar obtener un AVURLAsset / AVAsset en cualquier forma o forma para archivos transmitidos a través de M3U8. un tonto recado.
En el caso de (a), el activo no se crea en absoluto, y el initWithURL: llamada en AVURLAsset falla con un ''Error Desconocido'' AVFoundationErrorDomain -11800.
En el caso de (b) y (c), la recuperación del AVURLAsset desde AVPlayer / AVPlayerItem o AVAssetTracks devuelve ALGUN objeto, pero el acceso a la propiedad ''tracks'' en él 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 AVMutableCompositionTrack, falla cuando se intenta insertar el CMTimeRange de la pista de origen en AVMutableCompositionTrack, con un ''Error desconocido'' NSOSStatusErrorDomain -12780.
4) M3U8 remotos, se comportan exactamente igual que los M3U8 locales.
No estoy completamente informado sobre por qué existen estas diferencias, o Apple no las pudo haber mitigado. Pero ahí tienes.
El uso de URL no locales no es posible en la actualidad con AVFoundation, al menos no directamente.