23 Stimmen

Vorpufferung für AVQueuePlayer

Weiß jemand, ob AVQueuePlayer beginnt mit der Pufferung des nächsten AVPlayerItem wenn die Wiedergabe des aktuellen Elements bald beendet ist?

Ich weiß, es gibt nichts in den Dokumenten, um dies vorzuschlagen, ich frage vor allem, wenn jemand diese Art von Verhalten oder nicht beobachtet hat.

22voto

Mihai Damian Punkte 10906

Ok, ich habe mir das Problem noch einmal angesehen und etwas Code geschrieben, um es zu überprüfen AVQueuePlayer .

Die Antwort von jollyCocoa wies mich in die richtige Richtung, indem sie vorschlug, die Eigenschaft status auf AVPlayerItem . Die Dokumentation scheint jedoch nicht darauf hinzuweisen, dass diese Eigenschaft (und ihre AVPlayerItemStatusReadyToPlay Wert im Besonderen) könnte mit der Pufferung zusammenhängen.

Doch die AVPlayerItem's loadedTimeRanges Eigenschaft scheint eher mit der Pufferung zusammenzuhängen.

Doing KVO auf diesem Array war ein bisschen schwieriger - das Array-Objekt selbst ändert sich nicht, nur seine Elemente tun - so ich Zuflucht zu drucken, es ist Inhalt jede Sekunde.

Ich habe festgestellt, dass einige Sekunden nach dem ersten Eintrag in der Warteschlange die loadedTimeRanges für den zweiten Eintrag erscheint eine neue CMTimeRange mit der Startzeit 0 und einer kleinen Dauer. Die Dauer kann auf bis zu 60 oder so Sekunden ansteigen, während das vorherige Element weiter abgespielt wird.

Kurze Antwort: AVQueuePlayer puffert die nächste AVPlayerItem während der Wiedergabe des aktuellen Titels.

8voto

Matt Gallagher Punkte 14710

Ab iOS 5 scheint es, dass AVQueuePlayer nicht mehr vorpuffert. In iOS 4 wurde der nächste Titel vorgepuffert.

Ich bin mir nicht sicher, warum es zu dieser Änderung gekommen ist und ob sie von Apple absichtlich herbeigeführt wurde oder einfach ein Fehler ist. Auf jeden Fall ist es ärgerlich, und ich werde einen Fehler an sie darüber einreichen.

5voto

ryanwils Punkte 947

Ich habe eine Lösung gefunden!!! Nach vielen Stunden der Suche und fehlgeschlagenen Tests habe ich eine Lösung gefunden, die unter iOS 5 und höher funktioniert.

Dadurch werden die Dateien ohne Verzögerung abgespielt. Das ist zwar nicht so praktisch, wenn Sie zwischen den Dateien wechseln wollen, aber es fügt auf jeden Fall alles für Sie zusammen. Es ist immer noch möglich zu verfolgen, wo jede Datei beginnt, wenn Sie AVURLAsset hinzufügen, behalten Sie die CMTime-Variable, etwas wie:

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

und gehen dann einfach durch das Array, prüfen den aktuellen Zeitwert und vergleichen ihn mit CMTimeCompare.

Edit: Wurde gefunden auf diese Seite aus dem Webarchiv sichtbar (der Link führt derzeit zu Spam).

2voto

jollyCocoa Punkte 691

Ich habe mit dem AVQueuePlayer mit Filmen experimentiert, die von einem Server bereitgestellt werden. In diesem speziellen Test habe ich einen queuePlayer mit AVPlayerItems eingerichtet, die mit URLs zu zwei verschiedenen Video-Assets unterschiedlichen Typs initialisiert wurden. Eines ist eine .mp4-Datei (progressiver Download), das andere eine .m3u8 http-Streaming-Datei. Ich muss zugeben, dass sich das ein bisschen komisch anhört.

Je nach der Reihenfolge, in der ich die Dateien in die Warteschlange stelle, ergibt sich ein ganz anderes Verhalten. Wenn ich mit der .mp4-Datei beginne, wird der AVPlayerLayer leer und still, wenn das nächste Element den Player startet (die .m3u8-Datei). Wenn ich die Reihenfolge ändere und mit der m3u8-Datei beginne, wird der AVPlayerLayer leer, aber das Audio der zweiten Datei wird problemlos abgespielt.

Nach dem, was ich bisher gesehen habe, ist mein Eindruck, dass der AVQueuePlayer NICHT mit dem Herunterladen des nächsten AVPlayerItems beginnen, bevor die Wiedergabe des aktuellen AVPlayerItems beendet ist. Aber ich könnte falsch liegen.

Fortsetzung folgt, denke ich...

Edit: Ok, ich habe noch ein paar Tests durchgeführt und es scheint, dass das nächste Element heruntergeladen wird, bevor das letzte Element fertig abgespielt ist. Siehe Beitrag unten. Streichen Sie das "NOT"...

Edit2: Ich füge stattdessen meine Erklärung hier ein, da der Kommentar mir keine Formatierungsmöglichkeiten ließ. (Bewährte Verfahren hierfür sind willkommen, falls dies eine schlechte Art ist, Antwortaktualisierungen zu handhaben)

Anyhow, ich iteriert durch meine itemsArray Hinzufügen von Beobachtung der Eigenschaft status mit KVO...

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

Ich habe auch meinen Controller als Beobachter der AVPlayerItem-Benachrichtigung festgelegt AVPlayerItemDidPlayToEndTimeNotification :

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

Jetzt konnte ich die Statusänderung des AVPlayerItems protokollieren, und es stellte sich heraus, dass das nächste AVPlayerItem in der Warteschlange mit einer Statusänderung protokolliert wird AVPlayerItemStatusReadyToPlay bevor die Wiedergabe des aktuellen Elements endet.

Daraus schließe ich, dass das Herunterladen des nächsten Elements in der Reihe beginnt, bevor das aktuelle Element endet.

Aber! Ich erstelle ein Array mit AVPlayerItems, das ich zum Erstellen meines Players verwende. Wenn ich die AVFoundation-Dokumente über AVAssets lese, habe ich den Eindruck, dass diese ihre Assets asynchron herunterladen, aber ich bin immer noch unsicher, wann diese Downloads vom IAPlayerItem initiiert werden. Ich habe immer noch einige funky Verhalten, wenn ich die .m3u8-Datei in die Warteschlange, die mich fragen, ob diese Vermögenswerte beginnen das Herunterladen zum Zeitpunkt der Erstellung (scheint ein bisschen seltsam für mich aber).

0voto

iPatel Punkte 43289

Ich habe eine einfache Demo für Sie und andere gemacht, die die Pufferungszeit jedes Videos von der URL reduzieren wollen.

HINWEIS: Ich weiß nicht, ob das der richtige Weg ist oder nicht, aber bei mir funktioniert es perfekt und ohne Probleme. Wenn jemand mehr wissen oder wollen Code für besseres Ergebnis zu verbessern, dann willkommen, meine Antwort zu ändern.

Im folgenden Code benötigt das erste Video eine normale Pufferzeit. Das nächste Video wird automatisch gestartet, wenn das aktuelle Video fertig ist, und Sie können nach links und rechts wischen, um zum nächsten und vorherigen Video zu wechseln.

Führen Sie die folgenden Schritte aus.

1) In der 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 mit einem Klick auf die Schaltfläche ein Video abspielen. Unten sehen Sie 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
    }];
}

Jetzt schreiben playVideos Methodencode.

#pragma mark - AVPlayer Methods -

-(void)playVideos
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // remove current "AVPlayerItem" from the notification observe.

    if(arrOfAVPItems.count > 0)
    {
        currentAVPlyrItem = (AVPlayerItem *)arrOfAVPItems[currentVideoNO]; // Add "AVPlayerItem" from the array.

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // Add notification observer to indication current "AVPlayerItem" is finish.

        // pause and nil previous player if available.
        [avPlyrViewController.player pause];
        avPlyrViewController.player = nil;

        avPlyrViewController.player = (AVPlayer *)arrOfPlyer[currentVideoNO]; // add new player from the array
        [avPlyrViewController.player.currentItem seekToTime:kCMTimeZero]; // set for start video on initial position.
        [avPlyrViewController.player play]; // Play video

    }
}

Benachrichtigungsbeobachter, um anzuzeigen, dass das Video beendet 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 zum Abspielen des nächsten Videos

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

Methode zur Wiedergabe des vorherigen Videos.

-(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 Sie Zweifel haben, kommentieren Sie bitte.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X