Utiliser AVAssetReader pour lire (stream) à partir d'une ressource distante

mon but principal est de diffuser une vidéo à partir d'un serveur, et de la couper image par image en streaming (afin qu'elle puisse être utilisée par OpenGL). Pour cela, j'ai utilisé ce code que j'ai trouvé partout sur Internet (comme je me souviens qu'il était du code échantillon de GLVideoFrame D'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:%dnDOMAIN:%@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];
}

mais j'ai toujours cette exception à [[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'

donc mes deux questions sont:

  1. Est l'exception, vraiment me dire que AVAssetReader doit avoir une URL locale? Peut-il être utilisé pour le streaming (tout comme le reste des classes AVFoundation )?
  2. si l'approche AVFoundation ne fonctionne pas, quelles sont les autres suggestions pour diffuser la vidéo et partager ses images en même temps?

Merci beaucoup pour votre aide.

30
demandé sur Chris Frederick 2011-06-05 13:49:32

3 réponses

@Cocoanetics, Je ne suis pas en mesure de commenter en raison de la mauvaise réputation. Mais ce que vous avez dit ne semble pas tout à fait correct. AVFoundation ne semble pas faire autant de distinction entre les fichiers locaux et non locaux qu'entre les types de fichiers ou de protocoles utilisés. Il y a une distinction très claire entre l'utilisation de mp4/mov et l'utilisation du protocole HTTP Live streaming via m3u8, mais les différences avec un mp4 local ou distant sont un peu plus floues.

pour développer sur le ci-dessus:

a) Si votre ressource 'distante' est un M3U8 (c'est-à-dire que vous utilisez le streaming HTTP 'live'), alors aucune chance que ce soit. Peu importe si le M3U8 est dans votre système de fichiers local ou sur un serveur distant, pour une multitude de raisons AVAssetReader et toutes les fonctionnalités associées à AVAsset ne fonctionnent tout simplement pas. However, AVPlayer, AVPlayerItem etc would work just fine.

b) si C'est un MP4/MOV, un peu plus d'enquête est due. Local MP4/MOV's work flawlessly. alors que dans le cas de MP4/MOV distant, je suis capable de créer (ou récupérez à partir D'un AVPlayerItem ou AVPlayer ou AVAssetTracks) un AVURLAsset avec lequel je suis parfois en mesure d'initialiser un AVAssetReader avec succès (je vais développer sur le 'parfois' ainsi, sous peu). Cependant, copyNextSampleBuffer always returns nil in case of remote MP4's . Depuis plusieurs choses JUSQU'au point d'invoquer copyNextSampleBuffer travail, je ne suis pas sûr à 100% Si:

i) copyNextSampleBuffer ne travaillant pas pour les mp4 à distance, après toutes les autres étapes ayant été couronnées de succès, est but/fonctionnalité attendue.

ii) que les 'autres étapes' semblent fonctionner du tout pour les MP4 à distance est un accident de L'implémentation D'Apple, et cette incompatibilité est tout simplement venir à l'avant-plan quand nous frappons copyNextSampleBuffer..............quelles sont ces "autres étapes", je vais les détailler sous peu.

iii) je fais quelque chose de mal en essayant d'invoquer copyNextSampleBuffer pour les MP4 à distance.

donc @Paula vous pourriez essayer d'enquêter un peu plus loin avec MOV/MP4 à distance.

pour référence, voici les approches que j'ai essayées pour capturer un cadre à partir de vidéos:

a)

créez un AVURLAsset directement à partir de l'URL de la vidéo.

récupérez la piste vidéo en utilisant [asset tracks withmediatype:AVMediaTypeVideo]

Préparer une sortie AVAssetReaderTrackOutput en utilisant la piste vidéo comme source.

créer un lecteur AVAssetReader en utilisant L'AVURLAsset.

ajouter AVAssetReaderTrackOutput à AVAssetReader et startReading.

récupère des images en utilisant copyNextSampleBuffer.

b)

créer un AVPlayerItem à partir de L'URL de la vidéo, puis un AVPlayer à partir de là (ou créer le AVPlayer directement à partir de L'URL).

récupère la propriété 'asset' de L'AVPlayer et charge ses 'tracks' en utilisant "loadValuesAsynchronouslyForKeys:".

sépare les pistes de type AVMediaTypeVideo (ou appelle tout simplement tracksWithMediaType: on the asset une fois que les pistes sont chargées), et crée votre AVAssetReaderTrackOutput en utilisant la piste vidéo.

créer AVAssetReader en utilisant le AVPlayer est 'actif', 'startReading" puis récupérer des images à l'aide de copyNextSampleBuffer.

c)

créez un AVPlayerItem+AVPlayer ou AVPlayer directement à partir de l'URL de la vidéo.

KVO la propriété 'tracks' de AVPlayerItem, et une fois les tracks chargés, séparez les Tracks Avasset de type AVMediaTypeVideo.

récupérer L'AVAsset de AVPlayerItem/AVPlayer / propriété "asset" D'AVAssetTrack.

les autres étapes sont semblables à l'approche b).

d)

créez un AVPlayerItem+AVPlayer ou AVPlayer directement à partir de l'URL de la vidéo.

KVO la propriété 'tracks' de AVPlayerItem, et une fois les tracks chargés, séparer ceux de type AVMediaTypeVideo.

crée une composition AVMutableComposition, et initialise une composition Avmutablecomposition associée de type AVMediaTypeVideo.

insérez la CMTimeRange appropriée de la piste vidéo récupérée plus tôt, dans ce pack AVMutableCompositionTrack.

similaire à (b) et (c), maintenant créer votre AVAssetReader et Avassetrreadertrackoutput, mais avec la différence que vous utilisez la composition AVMutableComposition comme base AVAsset pour initialiser votre AVAssetReader, et AVMutableCompositionTrack comme la base AVAssetTrack pour votre AVAssetReaderTrackOutput.

"lecture d'étoile" et utilisez copyNextSampleBuffer pour obtenir des cadres de L'AVAssetReader.

P. S: j'ai essayé approche (d) ici pour contourner le fait que L'AVAsset récupéré directement à partir AVPlayerItem ou AVPlayer ne se comportait pas. Je voulais donc créer un nouvel AVAsset à partir des tracks Avasset que j'avais déjà en main. Certes hacky, et peut-être inutile (où autrement l'information de piste serait finalement récupéré de si pas L'AVAsset original! mais ça valait le coup d'essayer désespérément.

voici un résumé des résultats pour différents types de fichiers:

1) MOV/MP4 locaux - les 4 approches fonctionnent parfaitement.

2) MOV/MP4 à distance - le bien et les pistes sont récupérés correctement dans les approches (b) à (d), et la AVAssetReader est initialisé en tant que bien, mais copyNextSampleBuffer renvoie toujours zéro. Dans le cas de (a), la création du lecteur AVAssetReader lui-même échoue avec une 'erreur inconnue' NSOSStatusErrorDomain -12407.

3) les approches (a), (b) et (c) locales de M3U8 (accessibles via un serveur HTTP in-app/local) - approches (a), (b) et (c) échouent misérablement comme essayer d'obtenir un AVURLAsset/AVAsset dans n'importe quelle forme ou forme pour les fichiers diffusés via M3U8 est une course idiote.

en cas de (a), le bien n'est pas créé du tout, et l'initWithURL: appel sur AVURLAsset échoue avec une 'erreur inconnue' AVFoundationErrorDomain -11800.

dans le cas de (b) et (c), extraire L'AVURLAsset de AVPlayer/AVPlayerItem ou AVAssetTracks renvoie un objet, mais accéder à la propriété 'tracks' renvoie toujours un tableau vide.

en cas de (d), je suis capable de récupérer et d'isoler le la vidéo suit avec succès, mais en essayant de créer la compilation Avmutablecomposition, elle échoue en essayant d'insérer la CMTimeRange à partir de la piste source dans la compilation Avmutablecomposition, avec une "erreur inconnue" NSOSStatusErrorDomain -12780.

4) à Distance M3U8, se comportent exactement de la même local M3U8.

Je ne suis pas tout à fait instruit sur les raisons pour lesquelles ces différences existent, ou n'auraient pas pu être atténuées par Apple. Mais là vous allez.

35
répondu Dev Kanchen 2011-06-20 10:34:27

@Cocanetics, ce n'est pas vrai. Vous pouvez obtenir un fichier distant sur 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];

cependant, cette approche ne fonctionne pas très bien avec les fichiers qui ont une compression plus élevée comme MP4s

0
répondu Julio Bailon 2011-09-29 21:50:46

L'utilisation d'URLs non locales n'est pas possible à L'heure actuelle avec AVFoundation, du moins pas directement.

-1
répondu Cocoanetics 2011-06-18 10:12:49