2010-11-18 8 views
19

Weiß jemand, ob AVQueuePlayer beginnt, die nächste AVPlayerItem zu puffern, wenn der aktuelle Gegenstand gerade fertig ist zu spielen?Pre-Buffering für AVQueuePlayer

Ich weiß, dass es nichts in den Dokumenten gibt, das dies vorschlägt, ich frage meistens, ob jemand diese Art von Verhalten beobachtet hat oder nicht.

Antwort

20

Ok, ich habe dieses Problem noch einmal untersucht und einen Code geschrieben, um AVQueuePlayer auszuprobieren.

Die Antwort von jollyCacoa wies mich in die richtige Richtung, indem sie vorschlug, die Statuseigenschaft auf AVPlayerItem zu beobachten. Die Dokumentation scheint jedoch nicht darauf hinzuweisen, dass diese Eigenschaft (und insbesondere ihr Wert AVPlayerItemStatusReadyToPlay) mit der Pufferung zusammenhängen könnte.

Allerdings scheint die AVPlayerItem'sloadedTimeRanges Eigenschaft eher auf Pufferung bezogen zu sein.

Doing KVO auf diesem Array war ein bisschen komplizierter - das Array-Objekt selbst ändert sich nicht, nur seine Elemente tun - so habe ich Zuflucht zum Ausdrucken seiner Inhalte jede Sekunde.

Was ich herausgefunden habe ist, dass ein paar Sekunden in der Warteschlange das erste Element, die loadedTimeRanges für das zweite Element zeigt eine neue CMTimeRange mit Startzeit 0 und einige kleine Dauer. Die Dauer kann sich um bis zu 60 Sekunden erhöhen, während der vorherige Gegenstand weiterspielt.

Kurze Antwort:AVQueuePlayer wird die nächste AVPlayerItem puffern, während das aktuelle spielen.

+0

Das Laden eines AVPlayerItems verursacht nicht die Verzögerung des Players; Wenn Sie nicht einverstanden sind, zeigen Sie mir Code, der es beweist. Ich habe eine funktionierende App, die ein AVPlayerItem für jedes Video-Asset auf deinem iPhone in einer Sammelansichtszelle lädt. Sie können so schnell blättern, wie Sie möchten, und das erste Bild jedes Videos wird so angezeigt, als ob Sie nur ein einzelnes Bild vom Objekt angefordert hätten. Eine Wiedergabeverzögerung erscheint jedoch NUR, wenn sie beim Scrollen gestartet wird. Das liegt daran, dass Sie zu viele Dinge in einen Thread und in eine Warteschlange legen. Initiiere die Wiedergabe in einem separaten Thread und einer Warteschlange und die Verzögerung ist weg. –

2

Ich experimentierte mit dem AVQueuePlayer mit Filmen, die von einem Server bereitgestellt wurden. In diesem speziellen Test habe ich einen queuePlayer mit AVPlayerItems eingerichtet, der mit URLs auf zwei verschiedene Video-Assets verschiedener Typen initialisiert wurde. Einer ist eine MP4-Datei (progressiver Download), der andere eine .m3u8 http-Streaming-Datei. Es wirkt ein bisschen irre, muss ich sagen.

Abhängig von der Reihenfolge, in der ich die Dateien in die Warteschlange bringe, bekomme ich ein ganz anderes Verhalten. Wenn ich mit der .mp4-Datei beginne, wird die AVPlayerLayer leer und leise, wenn das nextItem Player startet (die .m3u8-Datei). Wenn ich die Reihenfolge ändere und mit der m3u8-Datei beginne, wird die AVPlayerLayer leer, aber das Audio aus der zweiten Datei spielt gut.

Mein Eindruck von dem, was ich bisher gesehen habe, ist, dass die AVQueuePlayer tut NICHT Start der nächsten AVPlayerItem Download, bevor die aktuelle AVPlayerItem abgespielt ist. Aber ich könnte mich irren. Ok, also habe ich einige Tests gemacht und es scheint die nächsten Gegenstände beginnt das Herunterladen, bevor der letzte Punkt spielt beendet:

Um Ich denke, ...

bearbeiten werden fortgesetzt. Siehe Beitrag unten. Stroke das "NOT" ...

Edit2: Hinzufügen meiner Erklärung hier statt, da der Kommentar mir keine Formatierungsoptionen verlassen. (Best Practice hierfür ist willkommen, wenn dies eine schlechte Art und Weise ist die Antwort Updates zu handhaben)

Wie auch immer, iteriert ich durch meine Array Zeilen Hinzufügen Beobachtung des Status mit KVO ...

[playerItem addObserver: self forKeyPath:@"status" options: 0 context: NULL]; 

ich auch gesetzt mein Controller als Beobachter der AVPlayerItem Mitteilung AVPlayerItemDidPlayToEndTimeNotification:

[[NSNotificationCenter defaultCenter] addObserver: self 
             selector: @selector(playerItemDidReachEnd:) 
              name: AVPlayerItemDidPlayToEndTimeNotification 
              object: nil]; 

Jetzt habe ich die Statusänderung des AVPlayerItem einloggen konnte, und es stellt sich die nächste AVPlayerItem in t out Die Warteschlange wird mit einer Statusänderung AVPlayerItemStatusReadyToPlay geloggt, bevor das aktuelle Element die Wiedergabe beendet.

Meine Schlussfolgerung ist daher, dass das nächste Element in der Zeile beginnt zu downloaden, bevor das aktuelle Element endet.

Jedoch!Ich erstelle ein Array mit AVPlayerItems, mit denen ich meinen Player erstellen kann. Beim Lesen der AVFoundation-Dokumente auf AVAssets habe ich den Eindruck, dass diese ihre Assets asynchron herunterladen, aber ich bin immer noch unsicher, ob diese Downloads vom IAPlayerItem initiiert werden. Ich habe immer noch etwas funkiges Verhalten, wenn ich die .m3u8-Datei zur Warteschlange hinzufüge, was mich fragt, ob diese Assets zum Zeitpunkt der Erstellung heruntergeladen werden (scheint mir ein bisschen komisch).

+0

Ok, also ich Iterate durch die Elemente in der Artikel-Array, Hinzufügen von Beobachtung über KVO ... Dieser Kommentar erlaubt keine Formatierung. Stattdessen meinen Hauptbeitrag bearbeiten! – jollyCocoa

6

Ab iOS 5 scheint es, dass AVQueuePlayer nicht mehr vorpuffert. Es hat den nächsten Track in iOS 4 pre-gepuffert.

Ich bin nicht sicher, warum es eine Änderung gab oder ob es sogar von Apple oder einfach nur ein Fehler absichtlich ist. Auf jeden Fall ist es ein Schmerz und ich werde ihnen einen Fehler darüber einreichen.

+0

Wenn Sie den Fehlerbericht eingereicht haben, können Sie das Programm öffnen, damit wir es dupen können? –

+0

Gab es Fortschritte in Bezug auf diesen Fehlerbericht? Meine App ist von demselben Fehler betroffen. Wenn Sie zwei lokal gespeicherte mp4-Dateien anordnen, führt dies zu einem unangenehmen Stopp der Wiedergabe, wenn AVQueuePlayer zur nächsten Datei wechselt. –

+0

@ matt-gallagher, hast du jemals von Apple über den Mangel an Pre-Pufferung im AVQueuePlayer gehört? –

5

Eine Arbeit gefunden !!! Nach vielen Stunden der Suche und fehlgeschlagenen Tests fand ich eine Lösung, die auf iOS 5 und höher funktioniert.

Dies wird Dateien ohne Verzögerung zwischen spielen. Nicht so praktisch, wenn Sie zwischen Dateien wechseln möchten, aber sicherlich alles für Sie zusammenstellen. Es ist immer noch möglich, den Überblick zu behalten, wo jede Datei beginnt, wenn AVURLAsset Hinzufügen, halten Sie die cmtime Variable, so etwas wie:

NSValue *toJumpTo = [NSValue valueWithCMTime:composition.duration]; 
[array addObject:toJumpTo]; 

und dann nur das Array durchlaufen, den aktuellen Zeitwert überprüft und es mit CMTimeCompare zu vergleichen.

Bearbeiten: Wurde gefunden auf http://www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html aber wie Stefan Kendall in den Kommentaren erwähnt, ist es jetzt eine Spam-Website. Ich dachte, ich würde den Link für die Zwecke der Zuordnung verlassen, aber besuchen Sie ihn nicht!

+1

Oh wow, dieser AVComposition Trick war GENAU, was ich brauchte. Prost! :) – Goz

1

Ich machte eine einfache Demo für Sie und andere, die Pufferzeit jedes Videos von URL reduzieren möchten.

HINWEIS: Sie wissen nicht, dies der richtige Weg ist oder nicht, aber es funktioniert perfekt für mich ohne jede Frage. Wenn jemand mehr weiß oder den Code verbessern möchte, um ein besseres Ergebnis zu erzielen, dann können Sie gerne meine Antwort ändern.

In der folgenden Code, erste Video nehmen normale Pufferzeit. Das nächste Video wird automatisch gestartet, wenn das aktuelle Video beendet ist, und Sie können nach links und rechts wischen, um das nächste und vorherige Video zu verschieben.

Folgen Sie den folgenden Schritten.

1) In der Datei videoPlayerVC.h Datei.

#import <AVFoundation/AVFoundation.h> 
#import <AVKit/AVKit.h> 

@interface videoPlayerVC : UIViewController 
{ 
    AVPlayerViewController *avPlyrViewController; 
    AVPlayerItem *currentAVPlyrItem; 

    int currentVideoNO; // For current video track. 
    NSMutableArray *arrOfAVPItems; 
    NSMutableArray *arrOfPlyer; 
} 

2) In der videoPlayerVC.m Datei

Hier können Sie starten Video durch Klick auf den Button zu spielen. Unten ist die Aktionsmethode der Schaltfläche.

-(void)clickOnPlayVideosImage:(UIButton *)sender 
{ 
    // In my App. all video URLs is up to 15 second. 
    currentVideoNO = 0; 
    NSMutableArray *arrForVideoURL = [[NSMutableArray alloc]initWithObjects: 
             [NSURL URLWithString:@"http://videos/url1.mov"], 
             [NSURL URLWithString:@"http://videos/url2.mov"], 
             [NSURL URLWithString:@"http://videos/url3.mov"], 
             [NSURL URLWithString:@"http://videos/url3.mov"], 
             [NSURL URLWithString:@"http://videos/url4.mov"], 
             [NSURL URLWithString:@"http://videos/url5.mov"], nil]; 

    AVPlayerItem *thePlayerItemA = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:0]]; 
    AVPlayerItem *thePlayerItemB = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:1]]; 
    AVPlayerItem *thePlayerItemC = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:2]]; 
    AVPlayerItem *thePlayerItemD = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:3]]; 
    AVPlayerItem *thePlayerItemE = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:4]]; 
    AVPlayerItem *thePlayerItemF = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:5]]; 

    if(arrOfAVPItems.count > 0) 
     [arrOfAVPItems removeAllObjects]; 
    arrOfAVPItems = nil; 

    if(arrOfPlyer.count > 0) 
     [arrOfPlyer removeAllObjects]; 
    arrOfPlyer = nil; 
    arrOfPlyer = [[NSMutableArray alloc] init]; 

    arrOfAVPItems = [NSMutableArray arrayWithObjects:thePlayerItemA, thePlayerItemB, thePlayerItemC, thePlayerItemD, thePlayerItemE, thePlayerItemF, nil]; // Add All items in the Array 

    for(AVPlayerItem *myPlyrItem in arrOfAVPItems) 
    { 
     AVPlayer *videoPlayerNext = [AVPlayer playerWithPlayerItem:myPlyrItem]; /// Add item in the player 
     [videoPlayerNext play]; 
     [videoPlayerNext pause]; 
     [arrOfPlyer addObject:videoPlayerNext]; /// Make Array of "AVPlayer" just reduce buffering of each video. 
    } 

    avPlyrViewController = [AVPlayerViewController new]; 
    avPlyrViewController.delegate = self; 
    avPlyrViewController.player = (AVPlayer *)arrOfPlyer[0]; // Add first player from the Array. 
    avPlyrViewController.showsPlaybackControls = YES; 
    avPlyrViewController.allowsPictureInPicturePlayback = YES; 
    avPlyrViewController.videoGravity = AVLayerVideoGravityResizeAspect; 

    [self presentViewController:avPlyrViewController animated:YES completion:^{ 
     [self performSelector:@selector(playVideos) withObject:nil afterDelay:1];/// call method after one second for play video. 

     UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeleftToNextVideo:)]; 
     swipeleft.direction=UISwipeGestureRecognizerDirectionLeft; 
     [avPlyrViewController.view addGestureRecognizer:swipeleft]; // Add left swipe for move on next video 

     UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeleftToPerviousVideo:)]; 
     swiperight.direction=UISwipeGestureRecognizerDirectionRight; 
     [avPlyrViewController.view addGestureRecognizer:swiperight]; // Add right swipe for move on previous video 
    }]; 
} 

schreiben Jetzt playVideos Methodencode.

Benachrichtigungsbeobachter, um anzuzeigen, dass das Video fertig ist.

- (void)playerItemDidReachEnd:(NSNotification *)notification 
{ 
    NSLog(@"IT REACHED THE END"); 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // remove current "AVPlayerItem" from the notification observe. 

    [self swipeleftToNextVideo:nil]; // Call method for next video. 
} 

Methode für das Spiel nächste Video

-(void)swipeleftToNextVideo:(UISwipeGestureRecognizer*)gestureRecognizer 
{ 
    currentVideoNO++; 
    if(currentVideoNO > (arrOfAVPItems.count -1)) 
     currentVideoNO = 0; 

    NSLog(@"current - %d and last - %d", currentVideoNO, (int)(arrOfAVPItems.count -1)); 

    [self playVideos]; 
} 

Verfahren zum vorherigen Video-Wiedergabe.

-(void)swipeleftToPerviousVideo:(UISwipeGestureRecognizer*)gestureRecognizer 
{ 
    currentVideoNO--; 
    if(currentVideoNO < 0) 
     currentVideoNO = (int)(arrOfAVPItems.count -1); 

    NSLog(@"current - %d and last - %d", currentVideoNO, (int)(arrOfAVPItems.count -1)); 

    [self playVideos]; 
} 

Folgen Sie diesen Schritten. Wenn irgendwelche Zweifel dann kommentieren Sie bitte.

+0

werden Sie bitte ein Beispiel mit dem obigen Code für avqueuplayer geben –

Verwandte Themen