在 iOS 7 和 8 中使用 AVPlayer 进行远程控制

Posted

技术标签:

【中文标题】在 iOS 7 和 8 中使用 AVPlayer 进行远程控制【英文标题】:Using Remote Control with AVPlayer in iOS 7 & 8 【发布时间】:2015-03-29 16:54:33 【问题描述】:

我正在尝试编写一个简单的个人广播应用程序,但遇到了一个烦人的问题。

我尝试使用 AVPlayer 从远程服务器播放 .pls(流)文件,我成功地让它工作。我现在可以播放流,但正如我在标题中提到的,当应用程序处于后台时,我无法远程控制播放/暂停选项。

我在互联网上搜索了很多关于该主题的内容,并找到了两种解决方案。

1- 就像在 Apple 的 Remote Control Events 文档中一样,使用“MPRemoteCommandCenter”类来处理这个问题,但是对于这个解决方案,我找不到任何好的解释来说明我将如何使用 AVPlayer。这让我想到了第二种解决方案,因为我不知道如何使用 MPRemoteCommandCenter 类。

2- 几乎所有人都提到过 remoteControlReceivedWithEvent: 方法。 但是,当我尝试使用这种方法时,我无法得到回应。我的 remoteControlReceivedWithEvent: 方法从未被调用过。这是我如何实现行为的代码:

我有两个类,一个名为 EGPlayRadio.m,其中包含 AVPlayer 和 AVAudiosession 内容,另一个是 EGMainMenuViewController.m,它显示播放按钮和暂停按钮以流式传输广播。

注意在调用[[UIApplication sharedApplication] beginReceivingRemoteControlEvents]方法之前先调用[AVSession sharedInstance]

我还注意到了从项目的构建设置中授予应用背景音频播放权限。

但是,在互联网上搜索了很多之后,我找不到适合我的问题的解决方案。这是我实现的两个类:

EGPlayRadio.m

#import "EGPlayRadio.h"
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>

@interface EGPlayRadio () <AVAudioPlayerDelegate>

@property (nonatomic, strong) AVAudioSession *audioSession;
@property (nonatomic, strong) AVPlayer *player;

@end

@implementation EGPlayRadio

- (id)initDefault

    self = [super init];
    if (self)
    
        _audioSession = [AVAudioSession sharedInstance];
        [_audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
        [[AVAudioSession sharedInstance] setActive: YES error: nil];
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
        NSURL *url = [[NSURL alloc] initWithString:@"http://LINK-OF-THE-PLS-FILE"];
        _player = [[AVPlayer alloc] initWithURL:url];
    
    return self;


- (void)playRadio

    [self.player play];


- (void)pauseRadio

    [self.player pause];

EGMainMenuViewController.m

@interface EGRMainMenuViewController ()

@property (nonatomic, strong) IBOutlet UIButton *playButton;
@property (nonatomic, strong) IBOutlet UIButton *pauseButton;
@property (nonatomic, strong) EGPlayRadio *radioControl;

@end

@implementation EGRMainMenuViewController

- (void)viewDidLoad 
    [super viewDidLoad];
    self.radioControl = [[EGPlayRadio alloc] initDefault];
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
    self.pauseButton.enabled = NO;
    self.pauseButton.alpha = 0.3f;


- (BOOL)canBecomeFirstResponder

    return YES;


- (IBAction)musicInteracted:(id)sender

    if (sender == self.playButton)
    
        self.pauseButton.enabled = YES;
        self.pauseButton.alpha = 1.0f;
        self.playButton.enabled = NO;
        self.playButton.alpha = 0.3f;
        [self.radioControl playRadio];
    
    else
    
        self.pauseButton.enabled = NO;
        self.pauseButton.alpha = 0.3f;
        self.playButton.enabled = YES;
        self.playButton.alpha = 1.0f;
        [self.radioControl pauseRadio];
    



-(void)remoteControlReceivedWithEvent:(UIEvent *)event

    switch (event.subtype) 
        case UIEventSubtypeRemoteControlTogglePlayPause:
            //[streamer pause];
            break;
        case UIEventSubtypeRemoteControlPlay:
            //[streamer start];
            break;
        case UIEventSubtypeRemoteControlPause:
            //[streamer pause];
            break;
        case UIEventSubtypeRemoteControlStop:
            //[streamer stop];
            break;
        default:
            break;
    



@end

【问题讨论】:

【参考方案1】:

感谢@Onur Şahindur,经过几年的努力,我终于完成了这项工作。不敢相信这在任何地方都没有记录。正如 Onur 所说,*** 上有很多人都在为此苦苦挣扎,但这是第一个真正有效的解决方案。

我还使用“MPRemoteCommandCenter”类进行了此操作。我通过对管理我的 AVPlayer 的单例的方法调用实现了 appDelegate 中的所有内容。这是我的解决方案:

_audioSession = [AVAudioSession sharedInstance];

// Previously, I used [_audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&setCategoryError];
// As noted by @Onur, it has to be the following (without options)
[_audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];

// Apple documentation says you don't need the following if you are using
// the MPRemoteCommandCenter, but that is not true. It is needed.
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];


MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.togglePlayPauseCommand setEnabled:YES];
[commandCenter.playCommand setEnabled:YES];
[commandCenter.pauseCommand setEnabled:YES];
[commandCenter.nextTrackCommand setEnabled:YES];
[commandCenter.previousTrackCommand setEnabled:YES];
[commandCenter.togglePlayPauseCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) 
    [[MediaController sharedInstance] playOrPauseMusic];    // Begin playing the current track.
    return MPRemoteCommandHandlerStatusSuccess;
];

[commandCenter.playCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) 
    [[MediaController sharedInstance] playOrPauseMusic];    // Begin playing the current track.
    return MPRemoteCommandHandlerStatusSuccess;
];

[commandCenter.pauseCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) 
    [[MediaController sharedInstance] playOrPauseMusic];    // Begin playing the current track.
    return MPRemoteCommandHandlerStatusSuccess;
];

[commandCenter.nextTrackCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) 
    [[MediaController sharedInstance] fastForward];    // forward to next track.
    return MPRemoteCommandHandlerStatusSuccess;
];

[commandCenter.previousTrackCommand addTargetWithHandler: ^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) 
    [[MediaController sharedInstance] rewind];    // back to previous track.
    return MPRemoteCommandHandlerStatusSuccess;
];

[commandCenter.stopCommand setEnabled:NO];
[commandCenter.skipForwardCommand setEnabled:NO];
[commandCenter.skipBackwardCommand setEnabled:NO];
[commandCenter.enableLanguageOptionCommand setEnabled:NO];
[commandCenter.disableLanguageOptionCommand setEnabled:NO];
[commandCenter.changeRepeatModeCommand setEnabled:NO];
[commandCenter.changePlaybackRateCommand setEnabled:NO];
[commandCenter.changeShuffleModeCommand setEnabled:NO];
// Seek Commands
[commandCenter.seekForwardCommand setEnabled:NO];
[commandCenter.seekBackwardCommand setEnabled:NO];
[commandCenter.changePlaybackPositionCommand setEnabled:NO];
// Rating Command
[commandCenter.ratingCommand setEnabled:NO];

// Feedback Commands
// These are generalized to three distinct actions. Your application can provide
// additional context about these actions with the localizedTitle property in
// MPFeedbackCommand.
[commandCenter.likeCommand setEnabled:NO];
[commandCenter.dislikeCommand setEnabled:NO];
[commandCenter.bookmarkCommand setEnabled:NO];

除了上述(我把它放在 appDelegate - application:didFinishLaunchingWithOptions 中)之外,还将它添加到你的 appDelegate 中:

- (BOOL) canBecomeFirstResponder

    return YES;

然后,每次更改要播放的媒体项目时,我都会在我的媒体控制器单例中执行以下代码:

ipodControlArtwork = [[MPMediaItemArtwork alloc]initWithImage:artworkImage];
if (artworkImage != nil && nowPlayingArtist != nil && nowPlayingTitle != nil && ipodControlArtwork != nil) 

    [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                                             nowPlayingTitle, MPMediaItemPropertyTitle,
                                                             nowPlayingArtist, MPMediaItemPropertyArtist,
                                                             ipodControlArtwork, MPMediaItemPropertyArtwork,
                                                             [NSNumber numberWithDouble:0.0], MPNowPlayingInfoPropertyPlaybackRate, nil];

【讨论】:

使用未声明的标识符“MediaController”给我这个错误@JeffB6688【参考方案2】:

对于其他有同样问题的人,我通过以下方式解决了我的问题:

EGPlayRadio.m 类中,我使用分配了 AVAudioSession

[_audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];

我们必须使用它:

[_audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];

这是我的问题,改了之后,我可以成功收到远程通知了。

【讨论】:

但是为什么呢?如果我们真的需要这些选项怎么办?【参考方案3】:

您需要使用 MPNowPlayingInfoCenter。它在 Apple 的文档中有详细记录。

【讨论】:

以上是关于在 iOS 7 和 8 中使用 AVPlayer 进行远程控制的主要内容,如果未能解决你的问题,请参考以下文章

iOS 7 上的 MPNowPlayingInfoCenter AVPlayer

iOS 7中的iOS 7 AVPlayer AVPlayerItem持续时间不正确

AVPlayer 从 iOS 7 后台通知播放音频

iOS 7(子层)AVPlayer Fullscreen animate 需要上面(覆盖)UINavigationBar

iOS 8.4 AVFoundation _attachToPlayer 或 _attachToFigPlayer 崩溃 |一个 AVPlayerItem 不能与多个 AVPlayer 实例关联

在 iOS 上使用 avaudioengine 会破坏 avplayer