2012-09-29 8 views
5

W moim odtwarzaczu wideo mam dziwną sytuację, której podstawowy kod nie zmienił się znacznie w porównaniu z wcześniejszą aplikacją. Oto problem: wstawiam "_loadingLayer" (CATextLayer, który mówi, że film się ładuje), a następnie obserwuję właściwość statusową odtwarzacza AVPlayer, aby dowiedzieć się, kiedy usunąć "_loadingLayer" i zastąpić ją moją aktualną "_playerLayer" . Oto mój kod KVO za to:AV Foundation: Różnica między bieżącym elementem, który jest gotowy do gry, a - właściwością [AVPlayer readyForDisplay]?

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 

if ((object == _playerLayer) && (_playerLayer.player.currentItem.status == AVPlayerItemStatusReadyToPlay)) { 

    [CATransaction setAnimationDuration:1.8]; 

    _loadingLayer.opaque = NO; 

    if (_playerLayer.readyForDisplay) { 

     NSLog(@"Should be ready now."); 

    } 

    [self addPlayerLayerToLayerTree]; 

} 

} 

Moim problemem jest to, że film zaczyna, ale tylko dźwięk jest odtwarzany - warstwa pozostaje czarny. Kiedy wstawiłem powyższą instrukcję NSLog, dowiedziałem się dlaczego: Podobno chociaż status currentItem to "AVPlayerItemStatusReadyToPlay", warstwa odtwarzacza nie jest tak naprawdę readyForDisplay. To nie ma dla mnie sensu - wydaje się sprzeczne z intuicją. Czy ktoś może mi dać jakieś wskazówki na ten temat?

Udało mi się zweryfikować, że _playerLayer jest dodawany do drzewa warstw, ustawiając kolor jego tła na czerwony.

Innym dziwne, że myślę, że może być związane .... Byłem widząc te komunikaty w konsoli debuggera:

PSsetwindowlevel, błąd ustawiania poziomu okna (1000) CGSSetIgnoresCycle: Błąd 1000 lub ustawienie czyszczenie znaczników okna

Z góry dziękuję. To jest crosspost z forum Apple Dev.

Odpowiedz

3

Mieliśmy podobny problem i prześledziliśmy go, co uważam za błąd w systemie iOS 5.1 (i być może w starszych wersjach). Jest naprawiony w iOS 6.0. Ponieważ nie mogłem znaleźć nigdzie tego rozwiązania, piszę długą pracę dla przyszłych ludzi, którzy mają ten problem.

Jeśli AVPlayerItem zgłasza status AVPlayerStatusReadyToPlay przed uzyskaniem AVPlayerLayer, AVPlayer nigdy nie zgłosi, że jest readyForDisplay.

Więc kiedy zrobić:

self.player = [AVPlayer playerWithPlayerItem:self.playerItem]; 

upewnić się, że to następuje z:

self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; 

i że nie trzeba wiele, jeśli dowolny kod w między nimi.

Zbudowałem urządzenie testowe, aby działało w 100% lub w 100% przypadków. Zauważ, że może to być trudne, aby zobaczyć, co się dzieje w twojej aplikacji, ponieważ będziesz mieć różne czasy ładowania na wideo i to będzie miało wpływ na to, jak szybko playerItem zgłasza AVPlayerStatusReadyToPlay.

Jeśli chcesz przetestować w swojej aplikacji, umieść to w prostym widoku. Poniższy kod nie zadziała (tzn. Usłyszysz dźwięk, ale nie zobaczysz wideo) na iOS 5.1. Jeśli zmienisz metodę loadPlayerLayer na, aby była ona wywoływana na końcu loadPlayer, zawsze będzie działać.

Obserwacja przyszłych czytelników: kilka wydarzeń dla graczy może zmienić tę kolejność i sprawić, że myślisz, że działa. Są to czerwone śledzie, mimo że nieumyślnie odwracają kolejność ładowań, tak, że PlayerLayer jest chwytany przed AVStatusReadyToPlay. Zdarzenia to: poszukiwanie filmu, przejście do ekranu głównego, a następnie ponowna aktywacja aplikacji, a gracz przełącza się na inną ścieżkę wideo/audio w wideo HLS. Te akcje ponownie uruchamiają funkcję AVStatusReadyToPlay, dzięki czemu rozgrywka gracza stanie się ważniejsza niż AVStatusReadyToPlay.

Oto uprząż test, który wykorzystuje Apple Test HLS wideo:

-(void)loadPlayer 
{ 
    NSLog(@"loadPlayer invoked"); 

    NSURL *url = [NSURL URLWithString:@"https://devimages.apple.com.edgekey.net/resources/http-streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"]; 
    self.playerItem = [AVPlayerItem playerItemWithURL:url]; 
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:&kPlayerContext]; 
    self.player = [AVPlayer playerWithPlayerItem:self.playerItem]; 

} 

-(void)loadPlayerLayer 
{ 
    NSLog(@"starting player layer"); 
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; 
    [self.playerLayer addObserver:self forKeyPath:@"readyForDisplay" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:&kPlayerLayerContext]; 
    [self.playerLayer setFrame:[[self view] bounds]]; 
    [[[self view] layer] addSublayer:self.playerLayer]; 
} 

-(void)observeValueForKeyPath:(NSString*)path ofObject:(id)object change:(NSDictionary*)change context:(void*) context 
{ 
    if(context == &kPlayerContext){ 
    if([self.player status] == AVPlayerStatusReadyToPlay){ 
     NSLog(@"Player is ready to play"); 
     //Robert: Never works if after AVPlayerItem reports AVPlayerStatusReadyToPlay 
     if(!self.startedPlayerLayer){ 
     self.startedPlayerLayer = YES; 
     [self loadPlayerLayer]; 
     } 
    } 
    } 

    if(context == &kPlayerLayerContext){ 
    if([self.playerLayer isReadyForDisplay] == YES){ 
     NSLog(@"PlayerLayer says it's ready to display now"); 
     [self playTheVideoIfReady]; 
    } 
    } 
}