2017-03-09 15 views
2

バックグラウンドで実行しているとき、私のAVPlayerの実装は、ダウンロードされたオーディオ(例えば、ポッドキャスト)を再生することはできませんが、ローカルに保存されている曲を再生できます。バックグラウンドで再生できないのは、電話機がコンピュータから切断されている場合だけです。私の電話がコンピュータ/デバッガに直接接続されている場合、ローカルまたはダウンロードされたメディアは問題なく再生されます。フォアグラウンドでは、どちらのメディアタイプでも問題はありません。ここで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; 

はUPDATE:上記の実装では、私はまた、バックグラウンドでポッドキャストを再生するためにAVPlayerを有効にすることを希望してバックグラウンドタスクを開始するための実験的な試みを示しています。これは助けにはならなかったが、私は参照のためにそれを含める。表示されていませんが、AVPlayerItemのplaybackLikelyToKeepUpステータスがTRUEに変更された後、バックグラウンドタスクも終了します。

それから私はキーパスの通知を処理するために、次のコードがあります:切断時に実行をトレースするために、ロギングツールを実装することで

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

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

そして、私は通知を通知センター処理するために、次のしているがコンピュータから、ここに私が観察しているものがあります:

コンピュータから接続されていないバックグラウンドで実行すると、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

@Rhythmic Fistmanはい、私はそれを試みた。それは助けになりませんでした。また、AVPlayerはバックグラウンドでローカルメディアを再生するのに問題はないので、iOSを私を寝かせる問題のようには見えません。 – JeffB6688

+0

ローカルファイルのタイミングプロパティは異なります。スケジュール解除されるのを避けるため、バックグラウンドオーディオアプリはオーディオを再生している必要があります。あなたの 'AVPlayer 'が再生を開始する前にオーディオデータをストリームする時間が必要だということが問題になります。 –

答えて

1

再生を開始する前にbeginBackgroundTaskに電話してみてください。 documentationには、バックグラウンドでの実行に使用すべきではないと言われていますが、未完成のビジネスには役立つとも言われています。

私の推論はこれです:それは、リモートメディアを再生するときのiOSは、通常

  • あなたをdescheduleだろうというときあなたはオーディオを再生しなければならないaudioバックグラウンドモードでバックグラウンドで実行するにはアプリの

    1. AVPlayerからplayまでの間に、そしてオーディオが実際に再生を開始してからアプリを続行できるようになるまで、ローカルメディアよりも時間がかかります。

    したがって、バックグラウンドタスクは、リモートメディアを読み込んでオーディオを再生するためにもう少し時間を(some say as much as 3 minutes)与えます。

    このトリックを行う必要がありますか? iOSスケジューラがバックグラウンドのオーディオケースで「公平」になるためには、AVPlayer.play()がバックグラウンドの先頭にマークを付けることによってオーディオを再生しようとする「ベストエフォート」の試みを本当に認識しなければなりません(おそらく、AVPlayer/iOSスケジューラ実装の詳細です)。オーディオ。何らかの理由で再生が失敗した場合は、細かく、スケジュールを設定します。とにかく、それについて強く感じたら、機能要求を提出することができます。

    p.s. endBackgroundTaskに電話することを忘れないでください!有効期限ハンドラと、良いモバイルデバイス市民であることの両方で、オーディオが再生されるとすぐに、あなたの継続的なバックグラウンドが確実に実行されます。 endBackgroundTaskがあなたのバックグラウンドでのオーディオ再生能力に影響を与えている場合は、早すぎると言います!

  • 関連する問題