2017-03-09 54 views
2

백그라운드에서 실행 중일 때 AVPlayer를 다운로드하면 오디오 (예 : podcast)를 재생할 수 없지만 다운로드 한 오디오는 재생할 수 없지만 로컬에 저장된 노래는 재생할 수 있습니다. 배경에서 재생하지 못하는 것은 휴대 전화가 내 컴퓨터에서 분리되었을 때뿐입니다. 내 전화기가 내 컴퓨터/디버거에 직접 연결되어 있으면 로컬 또는 다운로드 된 모든 미디어가 문제없이 재생됩니다. 포 그라운드에서는 미디어 유형을 재생하는 데 문제가 없습니다.AVPlayer 백그라운드에서 미디어가로드되지 않음

AVPlayer     *moviePlayer;    
AVPlayerItem    *playerItem; 
NSURL *address = /* the url of either a local song or remote media */ 

if (moviePlayer != nil) { 
    NSLog(@"removing rate, playbackBufferEmpty, playbackLikelyToKeepUp observers before creating new player"); 
    [moviePlayer removeObserver:self forKeyPath:@"rate"]; 
    [playerItem removeObserver:self forKeyPath:@"playbackBufferEmpty"]; 
    [playerItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"]; 
    [playerItem removeObserver:self forKeyPath:@"status"]; 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem]; 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemFailedToPlayToEndTimeNotification object:playerItem]; 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:playerItem]; 
    [self setMoviePlayer:nil]; // release and nilify 
} 

// The following block of code was an experiment to see if starting a background task would resolve the problem. As implemented, this did not help. 
if([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) 
{ 
    NSLog(@"Experiment. Starting background task to keep iOS awake"); 
    task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void) { 
    }]; 
} 

playerItem = [[AVPlayerItem alloc]initWithURL:address]; 
moviePlayer = [[AVPlayer alloc]initWithPlayerItem:playerItem]; 

// Add a notification to make sure that the player starts playing. This is handled by observeValueForKeyPath 
[moviePlayer addObserver:self 
       forKeyPath:@"rate" 
        options:NSKeyValueObservingOptionNew 
        context:nil]; 

[playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil]; 
[playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; 
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; 

// The following 2 notifications handle the end of play 
[[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(moviePlayBackDidFinish:) 
               name:AVPlayerItemDidPlayToEndTimeNotification 
              object:playerItem]; 

[[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(moviePlayBackDidFinish:) 
               name:AVPlayerItemFailedToPlayToEndTimeNotification 
              object:playerItem]; 

[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(moviePlayBackStalled:) 
              name:AVPlayerItemPlaybackStalledNotification 
              object:playerItem]; 

// Indicate the action the player should take when it finishes playing. 
moviePlayer.actionAtItemEnd = AVPlayerActionAtItemEndPause; 

moviePlayer.automaticallyWaitsToMinimizeStalling = NO; 

업데이트 : 위의 구현에, 나는 또한 백그라운드에서 팟 캐스트를 재생 AVPlayer를 수 있도록 희망 백그라운드 작업을 시작하는 실험적인 시도를 보여주는하고

여기 내 구현입니다. 이것은 도움이되지 못했지만 참조 용으로 포함 시켰습니다. 표시되지 않지만 AVPlayerItem playbackLikelyToKeepUp 상태가 TRUE로 변경된 후에도 백그라운드 작업이 종료됩니다. 그런 다음

나는 키 패스 알림 처리하기 위해 다음과 같은 코드가 있습니다

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
    if ([keyPath isEqualToString:@"rate"]) { 
     NSString *debugString = [NSString stringWithFormat: @"In observeValueForKeyPath: rate"]; 
     DLog(@"%@", debugString); 
     debugString = [self appendAvPlayerStatus: debugString]; 
     [[FileHandler sharedInstance] logDebugString:debugString]; 
    } 
    else if (object == playerItem && [keyPath isEqualToString:@"playbackBufferEmpty"]) { 

     NSString *debugString = [NSString stringWithFormat: @"In observeValueForKeyPath: playbackBufferEmpty"]; 
     DLog(@"%@", debugString); 
     debugString = [self appendAvPlayerStatus: debugString]; 
     [[FileHandler sharedInstance] logDebugString:debugString]; 
    } 
    else if (object == playerItem && [keyPath isEqualToString:@"playbackLikelyToKeepUp"]) { 

     NSString *debugString = [NSString stringWithFormat: @"In observeValueForKeyPath: playbackLikelyToKeepUp"]; 
     DLog(@"%@", debugString); 
     debugString = [self appendAvPlayerStatus: debugString]; 
     [[FileHandler sharedInstance] logDebugString:debugString]; 
    } 

    else if (object == playerItem && [keyPath isEqualToString:@"status"]) { 

     NSString *debugString = [NSString stringWithFormat: @"In observeValueForKeyPath: status"]; 
     DLog(@"%@", debugString); 
     debugString = [self appendAvPlayerStatus: debugString]; 
     [[FileHandler sharedInstance] logDebugString:debugString]; 
    } 
} 

을 그리고 알림 notificationCenter 처리하기 위해 다음과 같은 한 :

- (void) moviePlayBackDidFinish:(NSNotification*)notification { 

    NSLog(@"moviePlaybackDidFinish. Time to stopMoviePlayerWithMusicPlayIndication"); 
    [self stopMoviePlayer: YES]; // stop the movie player 
} 

- (void) moviePlayBackStalled:(NSNotification*)notification { 

    NSString *debugString = [NSString stringWithFormat: @"In moviePlayBackStalled. Restarting player"]; 
    DLog(@"%@", debugString); 
    debugString = [self appendAvPlayerStatus: debugString]; 
    [[FileHandler sharedInstance] logDebugString:debugString]; 
    [moviePlayer play]; 
} 

을 실행을 추적하는 로깅 툴을 구현함으로써 분리 할 때 컴퓨터에서 여기 내가 관찰하고있는 것이 있습니다 :

컴퓨터에서 연결이 끊긴 백그라운드에서 실행될 때, playerItem URL 주소가로드되고 AVPlayer는 playerItem으로 초기화됩니다. 이로 인해 observeValueForKeyPath : rate 알림이 게시되지만 마지막 수신 된 알림입니다. 오디오가 재생되지 않습니다. 플레이어가 멈 춥니 다. 다음과 같이 다양한 moviePlayer 및 playerItem 플래그를 보여주는 내 로그의 출력은 다음과 같습니다

ready to play movie/podcast/song dedication/Song from url: http://feedproxy.google.com/~r/1019TheMix-EricAndKathy/~5/ZZnF09tuxr0/20170309-1_1718764.mp3 
In observeValueForKeyPath: rate - timeCntrlStatus: AVPlayerTimeControlStatusPlaying, itemStatus: AVPlayerItemStatusUnknown, playbackToKeepUp: 0, playbackBufferEmpty: 1, playbackBufferFull: 0, rate: 1.0 

을하지만, 직접 컴퓨터에 연결하거나 포 그라운드에서 실행하면 백그라운드에서 실행하는 경우, 아래의 로그 출력에서 ​​볼 수 url 주소가로드되고 AVPlayer가 playerItem으로 초기화되면 keyPath : rate, playbackBufferEmpty, playbackLikelyToKeepUp 및 status에 대한 일련의 알림이 게시됩니다. 그러면 오디오가 재생되기 시작합니다. 다음과 같이 다양한 moviePlayer 및 playerItem 플래그를 보여주는 출력은 다음과 같습니다 포 그라운드에서 직접 컴퓨터/디버거에 연결하면 백그라운드에서 실행하는 경우

ready to play movie/podcast/song dedication/Song from url: http://feedproxy.google.com/~r/1019TheMix-EricAndKathy/~5/d3w52TBzd88/20170306-1-16_1717980.mp3 
In observeValueForKeyPath: rate - timeCntrlStatus: AVPlayerTimeControlStatusPlaying, itemStatus: AVPlayerItemStatusUnknown, playbackToKeepUp: 0, playbackBufferEmpty: 1, playbackBufferFull: 0, rate: 1.0 
In observeValueForKeyPath: playbackBufferEmpty - timeCntrlStatus: AVPlayerTimeControlStatusPlaying, itemStatus: AVPlayerItemStatusUnknown, playbackToKeepUp: 0, playbackBufferEmpty: 0, playbackBufferFull: 0, rate: 1.0 
In observeValueForKeyPath: playbackLikelyToKeepUp - timeCntrlStatus: AVPlayerTimeControlStatusPlaying, itemStatus: AVPlayerItemStatusUnknown, playbackToKeepUp: 0, playbackBufferEmpty: 0, playbackBufferFull: 0, rate: 1.0 
In observeValueForKeyPath: status - timeCntrlStatus: AVPlayerTimeControlStatusPlaying, itemStatus: AVPlayerItemStatusReadyToPlay, playbackToKeepUp: 0, playbackBufferEmpty: 0, playbackBufferFull: 0, rate: 1.0 
In observeValueForKeyPath: playbackLikelyToKeepUp - timeCntrlStatus: AVPlayerTimeControlStatusPlaying, itemStatus: AVPlayerItemStatusReadyToPlay, playbackToKeepUp: 1, playbackBufferEmpty: 0, playbackBufferFull: 0, rate: 1.0 
In observeValueForKeyPath: rate - timeCntrlStatus: AVPlayerTimeControlStatusPlaying, itemStatus: AVPlayerItemStatusReadyToPlay, playbackToKeepUp: 1, playbackBufferEmpty: 0, playbackBufferFull: 0, rate: 1.0 

그래서 요약, 그 위에 참조 AVPlayer를 성공적으로 재생 버퍼를로드 오디오를 재생합니다. 그러나 백그라운드에서 실행 중이고 컴퓨터/디버거에 연결되어 있지 않으면 플레이어가 미디어를로드하지 않고 멈추는 것처럼 보입니다.

모든 경우에 AVPlayerItemPlaybackStalledNotification은 절대로 수신되지 않습니다.

대단히 죄송합니다. 컴퓨터에 연결되어 있지 않을 때 플레이어가 백그라운드에서 멈추는 원인을 누가 볼 수 있습니까?

+0

재생을 시작할 때'beginBackgroundTask'를 호출 해 보았습니까? 문서는 백그라운드로 실행해서는 안되며, 끝나지 않은 비즈니스에도 유용하다고 말합니다. https://developer.apple.com/reference/uikit/uiapplication/1623031-beginbackgroundtask –

+0

@ 리듬의 주먹 예, 나는 그것을 시도했다. 도움이되지 않았다. 게다가 AVPlayer는 백그라운드에서 로컬 미디어를 재생할 때 아무런 문제가 없으므로 iOS를 잠자기 상태로 만드는 것과 같은 문제는 아닙니다. – JeffB6688

+0

로컬 파일의 타이밍 속성이 다릅니다. 일정이 잡히지 않게하려면 백그라운드 오디오 앱이 오디오를 재생 중이어야합니다. AVPlayer가 재생하기 전에 오디오 데이터를 스트리밍 할 시간이 필요하다는 사실이 문제 일 수 있습니다. –

답변

1

재생을 시작하기 전에 beginBackgroundTask으로 전화 해보십시오. documentation은 백그라운드에서 실행하지 말아야한다고 말하면서 또한 미완성 된 비즈니스에 유용하다고 말합니다.

나의 추론은 이것이다 :

원격 미디어를 재생할 때 아이폰 OS는 일반적으로 당신에게
  • 을 deschedule 할 때 응용 프로그램은, 당신이 오디오를 재생해야 audio 백그라운드 모드에서 백그라운드에서 실행하기위한
    1. AVPlayer에서 play으로 말할 때와 오디오가 실제로 재생되기 시작할 때까지 로컬 미디어보다 더 많은 시간이 자연스럽게 필요하므로 앱을 계속 사용할 수 있습니다.

    따라서 백그라운드 미디어는 원격 미디어를로드하고 오디오를 재생하는 데 조금 더 많은 시간 (some say as much as 3 minutes)을 제공합니다.

    이 트릭을해야합니까? 아마 iOS 스케줄러가 백그라운드 오디오 케이스에서 "공평"해지려면 이 배경의 시작을 표시하여 오디오를 재생하려는 "최선의 노력"을 인식해야합니다. 오디오. 어떤 이유로 든 재생이 실패하면 잘 정리하십시오. 어쨌든, 그것에 대해 강하게 생각하면 기능 요청을 제출할 수 있습니다.

    p.s. endBackgroundTask에 전화하는 것을 잊지 마세요! 만료 처리기에서, 그리고 좋은 모바일 장치 시민이되기 위해, 오디오가 재생되기 시작하자마자 계속되는 백그라운드 실행을 보장합니다. endBackgroundTask이 백그라운드에서 오디오를 재생하는 데 영향을 주면 너무 일찍 전화하는 것입니다.