Подтвердить что ты не робот

IOS 7 SDK не поддерживает фоновый звук

Я провел много исследований, как в Google, так и в StackOverflow. Все ответы, которые я нашел, не работают в iOS 7. Я начал писать новое приложение в iOS 7 SDK с Xcode 5.

Все, что я пытаюсь сделать, это воспроизвести аудио в приложении из файла, хранящегося в комплекте приложения (не из библиотеки музыки). Я хочу, чтобы звук воспроизводился в фоновом режиме и контролировался при блокировке экрана (в дополнение к Control Center).

Я установил ключ APPNAME-Info.plist, UIBackgroundModes, в аудио. Он не обрабатывает вещи в делегате приложения; все делается внутри ViewController

@interface ViewController : UIViewController <AVAudioPlayerDelegate>

В рамках реализации метода viewDidAppear: я вызываю super, а затем следующий код:

// Once the view has loaded then we can register to begin receiving controls and we can become the first responder
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];

В моем методе реализации viewWillDisappear: у меня есть следующий код:

// End receiving events
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];

Я также внедрил метод canBecomeFirstResponder, который возвращает ДА. Затем я применил метод remoteControlReceivedWithEvent::

- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    // If it is a remote control event handle it correctly
    if (event.type == UIEventTypeRemoteControl) {
        if (event.subtype == UIEventSubtypeRemoteControlPlay) {
            [self playPauseAudio:self];
        } else if (event.subtype == UIEventSubtypeRemoteControlPause) {
            [self playPauseAudio:self];
        } else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause) {
            [self playPauseAudio:self];
        }
    }
}

Что меня смущает, так это то, что эта точная настройка отлично работает на iOS 6. На iOS 7 она не работает. Раньше это было так просто в iOS 6. Что-то принципиально изменилось в iOS 7 SDK. Что мне не хватает?

4b9b3361

Ответ 1

Мне удалось это решить, и сохранить волосы, тянущие еще одна бедная душа, здесь:

Во-первых, убедитесь, что ваш Info.plist правильно отображает аудио в качестве фонового режима.

(Если вы не знаете, что я говорю, выберите YOURAPPNAME-Info.plist, выберите это. Нажмите знак плюса и добавьте новый ключ с именем UIBackgroundModes и разверните его. Добавьте значение под названием audio.)

Вам понадобится ссылка на любой объект воспроизведения, создающий аудио. Поскольку я только воспроизвожу аудио и AVplayer не соблюдает фоновый звук, используйте это в своем заголовке контроллера вида:

@property (nonatomic, retain) MPMoviePlayerController *audioPlayer;

В реализации выполните следующие действия:

 [super viewDidAppear:animated];

    //Once the view has loaded then we can register to begin recieving controls and we can become the first responder
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];

и

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    //End recieving events
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];

добавить два метода

//Make sure we can recieve remote control events
- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (void) registerForAudioObjectNotifications {

    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

    [notificationCenter addObserver: self
                           selector: @selector (handlePlaybackStateChanged:)
                               name: MixerHostAudioObjectPlaybackStateDidChangeNotification
                             object: audioObject];
}

теперь ВСЕ важный код - это позволяет вашему приложению управлять аудио из "центра управления" и с экрана блокировки:

- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {

    if (receivedEvent.type == UIEventTypeRemoteControl) {

        switch (receivedEvent.subtype) {

            case UIEventSubtypeRemoteControlTogglePlayPause:
                [self playOrStop: nil];
                break;

            default:
                break;
        }
    }
}

здесь вы можете добавить много типов типов событий и вызвать любой метод.

Типичные события:

 UIEventSubtypeRemoteControlPlay                 = 100,  //Parent EVENT

// All below are sub events and you can catch them using switch or If /else.
    UIEventSubtypeRemoteControlPause                = 101,
    UIEventSubtypeRemoteControlStop                 = 102,
    UIEventSubtypeRemoteControlTogglePlayPause      = 103,
    UIEventSubtypeRemoteControlNextTrack            = 104,
    UIEventSubtypeRemoteControlPreviousTrack        = 105,
    UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
    UIEventSubtypeRemoteControlEndSeekingBackward   = 107,
    UIEventSubtypeRemoteControlBeginSeekingForward  = 108,
    UIEventSubtypeRemoteControlEndSeekingForward    = 109,

Чтобы отладить справку, вы можете использовать:

 MPMoviePlayerController *mp1= (MPMoviePlayerController *)[notification object];
    NSLog(@"Movie State is: %d",[mp1 playbackState]);

    switch ([mp1 playbackState]) {
        case 0:
            NSLog(@"******* video has stopped");
            break;
        case 1:
            NSLog(@"******* video is playing after being paused or moved.");
                       break;
        case 2:
            NSLog(@"******* video is paused");
                break;
        case 3:
            NSLog(@"******* video was interrupted");
            break;
        case 4:
            NSLog(@"******* video is seeking forward");
                     break;
        case 5:
            NSLog(@"******* video is seeking Backwards");
            break;
        default:
            break;

и это все - надеюсь, что это поможет кому-то! - это отлично работает на iOS 7 и iOS 6 с помощью приложения Storyboard, а также управляет с помощью наушников и всего нового центра управления.

Ответ 2

Очевидно, проблема была на стороне Apple, поскольку обновление iOS 7.0.3 устраняет эту проблему. Кроме того, что Алекс заметил о изменениях UIEventSubtype, код, который работал на iOS6, теперь работает на iOS7.

Для полноты, вот мой соответствующий код, который работает как в iOS6, так и в iOS7 - после udpate до 7.0.3. Также включены AVFoundation.framework и MediaPlayer.framework в Project Build Phases → Link binary с библиотеками. Нет кода для этого в делегате приложения.

В файле viewcontroller.h:

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

@interface NewsDetailViewController : UIViewController <UIWebViewDelegate, AVAudioSessionDelegate>
@property (nonatomic) MPMoviePlayerController *audioPlayer;

В файле viewcontroller.m:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.audioPlayer = [[MPMoviePlayerController alloc] initWithContentURL:audioUrl];
    [self.audioPlayer prepareToPlay];
    [self.audioPlayer.view setFrame:CGRectMake(0, 0, self.audioView.frame.size.width,     42)];
    self.audioPlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    [self.audioView addSubview:self.audioPlayer.view];
    [self.audioPlayer play];

    NSError *setCategoryError = nil;
    NSError *activationError = nil;
    [[AVAudioSession sharedInstance] setActive:YES error:&activationError];
    [[AVAudioSession sharedInstance] setDelegate:self];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated {
    [self.audioPlayer stop];
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];

    [super viewWillDisappear:animated];
}

- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {

    if (receivedEvent.type == UIEventTypeRemoteControl) {
        switch (receivedEvent.subtype) {
            case UIEventSubtypeRemoteControlPlay:
                [self.audioPlayer play];
                break;
            case UIEventSubtypeRemoteControlPause:
                [self.audioPlayer pause];
                break;
            case UIEventSubtypeRemoteControlTogglePlayPause:
                if (self.audioPlayer.playbackState == MPMoviePlaybackStatePlaying) {
                    [self.audioPlayer pause];
                }
                else {
                    [self.audioPlayer play];
                }
                break;
            default:
                break;
        }
    }
}

Ответ 3

Если вы хотите воспроизводить аудио в фоновом режиме в iphone и симуляторе, тогда вам нужно написать этот код в plist и во-первых, убедитесь, что ваш Info.plist правильно отображает аудио в качестве фонового режима.

(Если вы не знаете, что я говорю о выборе YOURAPPNAME-Info.plist, выберите это. Нажмите на знак плюса и введите ключ UIBackgroundModes и введите. Добавьте значение, называемое "Воспроизведение приложений" (для симулятора) или "Приложение воспроизводит аудио или передает аудио/видео с помощью AirPlay" (для iphone).)

enter image description here

в AppDelegate.m

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    __block UIBackgroundTaskIdentifier task = 0;
    task=[application beginBackgroundTaskWithExpirationHandler:^{
    NSLog(@"Expiration handler called %f",[application backgroundTimeRemaining]);
    [application endBackgroundTask:task];
    task=UIBackgroundTaskInvalid;
    }];
}

Добавьте эти две структуры в свой проект и некоторую строку кода в ViewController.h

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

@interface ViewController : UIViewController <UIWebViewDelegate, AVAudioSessionDelegate>
@property (nonatomic) MPMoviePlayerController *audioPlayer;

Напоминаем, что эти фреймворки должны быть добавлены в ваш проект.

Затем в Viewcontrller.m

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated {
    [self.audioPlayer stop];
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self resignFirstResponder];

    [super viewWillDisappear:animated];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSURL *audioUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"songName" ofType:@"mp3"]];
    self.audioPlayer = [[MPMoviePlayerController alloc] initWithContentURL:audioUrl];
    [self.audioPlayer prepareToPlay];
    [self.audioPlayer.view setFrame:CGRectMake(0, 0, self.view.frame.size.width-100,     42)];
    self.audioPlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    [self.view addSubview:self.audioPlayer.view];
    [self.audioPlayer play];


    // for run application in background
    NSError *setCategoryError = nil;
    NSError *activationError = nil;
    [[AVAudioSession sharedInstance] setActive:YES error:&activationError];
    [[AVAudioSession sharedInstance] setDelegate:self];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
}

Я надеюсь, что это поможет вам воспроизвести аудио в фоновом режиме в iphone и симуляторе.

Ответ 4

Следует отметить, что отличие от iOS6 до iOS7 с событиями удаленного управления заключается в том, что в iOS6 события play/pause появились как один UIEventSubtype:
UIEventSubtypeRemoteControlTogglePlayPause
И в iOS7 они приходят как два отдельных подтипа:
UIEventSubtypeRemoteControlPause
UIEventSubtypeRemoteControlPlay

Ответ 5

Так как я также заинтересован в поиске этого решения, я добавлю еще немного информации с моей стороны.

Я испытываю ту же проблему, но документация Apple не изменилась в отношении управления событиями дистанционного управления.

Я попытался переместить кое-что, и произошло что-то интересное.

Я первоначально имел управление событиями дистанционного управления в моем контроллере TabBar. Теперь, когда я переместил все в контроллере просмотра плеера, я вижу, что я могу снова управлять воспроизведением музыки с помощью своей гарнитуры, но не с кнопок на новой панели управления iOS7 (той, которая появляется снизу на экране).

Это довольно странно.

Чтобы решить проблему, попробуйте пополнить поток нашим тестом.