今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

iOS 音声バックグラウンド再生 && ロック画面表示およびコントロール

再生ロック画面通知バー表示#

背景#

音声を再生する際、通知画面に表示され、音声再生を制御できることを望んでいます。以前の要件では、バックグラウンドに入ると再生が一時停止するため、通知画面を開くたびに再生が一時停止し、音楽プレーヤーのような効果を見ることができませんでした。その後、バックグラウンドでの一時停止コードを削除すると、通知画面にプレーヤーが表示されるようになりましたが、制御できず、進行状況もありませんでした。

実装#

バックグラウンド再生のサポート#

まず、APP がバックグラウンド再生をサポートする必要があります。つまり、一方でバックグラウンドに入ると再生が一時停止するコードロジックを削除し、もう一方で Target -> Signing & Capabilities で Backgroud Modes を追加し、Audio, AirPlay, and Picture in Picture を有効にします。画像は以下の通りです:

企業微信 20211229-141138.png

AVAudioSessionを設定する際、再生前に実際のニーズに応じて設定し、再生後に閉じます。

AVAudioSessionCategoryタイプ

Category タイプ"サイレント" またはロック画面時にサイレントになるか他のサポート混合アプリと混合再生できるかバックグラウンドサポートシーンの例
AVAudioSessionCategoryAmbientはいはいいいえアプリのバックグラウンド音に一般的に使用され、ゲームをプレイ中に音楽を聴くことができます
AVAudioSessionCategorySoloAmbientはいいいえいいえ同様にバックグラウンド音ですが、ゲームをプレイ中に音楽を聴きたくないシーンに使用されます
AVAudioSessionCategoryPlaybackいいえデフォルトではできませんが、サポート可能はい音楽再生中、ロック画面でも音楽を聴くことができます
AVAudioSessionCategoryRecordいいえいいえ、録音のみはい録音機、録音中は他の音楽は再生できません
AVAudioSessionCategoryPlayAndRecordいいえデフォルトでは可能、録音も再生もできますはい再生しながら録音する、例えば VOIP のようなシーン
AVAudioSessionCategoryAudioProcessingいいえいいえ、ハードウェアデコード音声、再生および録音できませんはい音声フォーマット処理に使用されます
AVAudioSessionCategoryMultiRouteいいえはい、複数の入出力いいえヘッドフォン、USB デバイスが同時に再生されます

AVAudioSessionCategoryOptionタイプ

CategoryOption タイプ説明適用カテゴリ
AVAudioSessionCategoryOptionMixWithOthers他のアプリと混合再生をサポートAVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryPlayback、AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionDuckOthers他のアプリの音声音量を下げ、本アプリの音量を強調AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryPlayback、AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionAllowBluetoothBluetooth 音声入力をサポートAVAudioSessionCategoryRecord、AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionDefaultToSpeakerデフォルトの出力音声をスピーカーに設定AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthersアプリが音声再生を使用することがあり、再生中に他のアプリの音声を停止AVAudioSessionCategoryPlayback、AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionAllowBluetoothA2DPステレオ Bluetooth をサポートAVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionAllowAirPlayAirPlay デバイスをサポートAVAudioSessionCategoryPlayAndRecord

    func setupAudioSession() {
        do {
            // .notifyOthersOnDeactivationを設定し、Activeがfalseのときに有効になり、システムに本アプリの再生が終了したことを通知し、他のアプリの再生を続けることができます
            try AVAudioSession.sharedInstance().setActive(true, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation)
            
            // 実際のニーズに応じて異なるCategoryを切り替えます
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: AVAudioSession.CategoryOptions.duckOthers)
        } catch {
            print("set AudioSession error: %@", error)
        }
    }


ロック画面通知バー表示#

APP がバックグラウンド再生をサポートした後、通知バーに表示されるようになりますが、再生時には進行状況、タイトル、画像がなく、APP の名前と小アイコンのみが表示されます。これらの情報を変更するためのコードは以下の通りです:

#import <MediaPlayer/MPNowPlayingInfoCenter.h>
#import <MediaPlayer/MPRemoteCommandCenter.h>
#import <MediaPlayer/MPRemoteCommand.h>
#import <MediaPlayer/MPMediaItem.h>

// 通知バー表示を更新
- (void)updateNowPlaingInfo {
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    // 曲のタイトルを設定
    [dict setValue:@"タイトル" forKey:MPMediaItemPropertyTitle];
    // アーティスト名を設定
    [dict setValue:@"アーティスト" forKey:MPMediaItemPropertyArtist];
    // アルバム名を設定
    [dict setValue:@"アルバムタイトル" forKey:MPMediaItemPropertyAlbumTitle];
    // 表示する画像を設定
    MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:ArtImage];
    [dict setValue:artwork forKey:MPMediaItemPropertyArtwork];
    // 曲の長さを設定
    NSTimeInterval duration = self.player.duration;
    [dict setValue:[NSNumber numberWithDouble:duration] forKey:MPMediaItemPropertyPlaybackDuration];
    // すでに再生された長さを設定
    NSTimeInterval currentTime = self.player.currentTime;
    [dict setValue:[NSNumber numberWithDouble:currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
    // 再生速度を設定
    [dict setValue:@(1.0) forKey:MPNowPlayingInfoPropertyPlaybackRate];
    
    // 更新
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
}

再生が完了した後、通知バーに表示しないようにするには、以下のように設定できます。


    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:@{}];

通知バーで再生を制御するための一時停止、前の曲、次の曲を設定するには、MPRemoteCommandCenterのプロパティを設定して対応する機能を有効にし、イベントの処理には 2 つの方法があります:

  • 方法 1:remoteControlReceivedWithEvent:メソッドを使用して、対応するイベントに応答します
  • 方法 2:MPRemoteCommandCenterCommandを使用してaddTargetで対応するイベントを処理します

通知バーの対応機能を有効にするためのコードは以下の通りです:


// AppDelegate内、または対応する再生のController内で、システム制御イベントの受信を開始します
// システム制御イベントを受信
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];

- (void)setupCommandCenter {
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    [commandCenter.playCommand removeTarget:self];
    [commandCenter.pauseCommand removeTarget:self];
    
    // 前の曲、次の曲を無効にします
    commandCenter.previousTrackCommand.enabled = NO;
    commandCenter.nextTrackCommand.enabled = NO;
    
    // 再生
    commandCenter.playCommand.enabled = YES;
    
    // 一時停止
    commandCenter.pauseCommand.enabled = YES;
    
    // 再生と一時停止(ヘッドフォン制御)
    commandCenter.togglePlayPauseCommand.enabled = NO;

    // 進行状況をドラッグ
    commandCenter.changePlaybackPositionCommand.enable = YES;
}

イベント応答処理方法 1 のコードは以下の通りです:


// リモートイベントに応答
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    if (event.type == UIEventTypeRemoteControl) {
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlPlay:
            {
                NSLog(@"RemoteControlEvents: 再生");
            }
                break;
            case UIEventSubtypeRemoteControlPause:
            {
                NSLog(@"RemoteControlEvents: 一時停止");
            }
                break;
            case UIEventSubtypeRemoteControlTogglePlayPause:
                NSLog(@"ヘッドフォン制御:一時停止||再生");
                break;
            case UIEventSubtypeRemoteControlNextTrack:
            {
                NSLog(@"RemoteControlEvents: 次の曲");
            }
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
            {
                NSLog(@"RemoteControlEvents: 前の曲");
            }
                break;
            default:
                break;
        }
    }
}

イベント応答処理方法 2 のコードは以下の通りです:


- (void)setupCommandCenter {
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];

    // 再生
    [commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
        NSLog(@"再生");
        return MPRemoteCommandHandlerStatusSuccess;
    }];

    // 一時停止
    [commandCenter.pauseCommand addTarget:self action:@selector(handlePauseCommand:)];

    // 進行状況をドラッグ
    [commandCenter.changePlaybackPositionCommand addTarget:self action:@selector(handlePlaybackPositionCommand:)];
}

- (MPRemoteCommandHandlerStatus):(id)sender {
    NSLog(@"一時停止");
    return MPRemoteCommandHandlerStatusSuccess;
}

- (MPRemoteCommandHandlerStatus)handlePlaybackPositionCommand:
(MPChangePlaybackPositionCommandEvent *) event

{
    [self.palyer seekToTime:CMTimeMakeWithSeconds(event.positionTime, 1)];

    NSLog(@"changePlaybackPosition to %f", event.positionTime);

    return MPRemoteCommandHandlerStatusSuccess;
}

問題#

#

beginReceivingRemoteControlEventsを追加しない場合、通知バーは表示されますか? それは 2 つの方法の処理に影響しますか?
応答イベント処理方法 2 の応答が 2 回呼び出される
カスタム再生の進行状況と通知バーの進行状況が一致しない

参考#

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。