web-dev-qa-db-fra.com

iOS 7 SDK ne conserve pas l’arrière-plan audio

J'ai fait beaucoup de recherches, à la fois sur Google et StackOverflow. Toutes les réponses que j'ai trouvées ne fonctionnent pas sous iOS 7. J'ai commencé à écrire une nouvelle application dans iOS 7 SDK avec Xcode 5.

Tout ce que j'essaie de faire, c'est de lire de l'audio dans l'application à partir d'un fichier stocké dans l'ensemble d'applications (et non de la bibliothèque musicale). Je veux avoir le son est joué en arrière-plan et contrôlé lorsque l'écran est verrouillé (en plus du Centre de contrôle).

Je règle la clé APPNAME-Info.plist, UIBackgroundModes, sur audio . Il ne gère pas les choses dans le délégué de l'application; tout est fait à l'intérieur du ViewController

@interface ViewController : UIViewController <AVAudioPlayerDelegate>

Dans la méthode viewDidAppear: de l'implémentation, j'appelle super, puis le code suivant:

// 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];

Dans la méthode viewWillDisappear: de mon implémentation, j'ai le code suivant:

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

J'ai également implémenté la méthode canBecomeFirstResponder, qui renvoie YES. Ensuite, j'ai implémenté la méthode 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];
        }
    }
}

Ce qui me trouble, c'est que cette même configuration fonctionnait parfaitement sous iOS 6. Sur iOS 7, cela ne fonctionne pas. C'était si simple sous iOS 6. Quelque chose a fondamentalement changé dans iOS 7 SDK. Qu'est-ce que je rate?

24
codejunkie

J'ai réussi à résoudre ce problème et à sauver les cheveux tirés par une autre pauvre âme, voici ce qui suit:

Tout d'abord, assurez-vous que votre Info.plist répertorie correctement l'audio en tant que mode d'arrière-plan.

(Si vous ne savez pas de quoi je parle, sélectionnez YOURAPPNAME-Info.plist et sélectionnez-le. Cliquez sur le signe plus, ajoutez une nouvelle clé appelée UIBackgroundModes et développez-la. Ajoutez une valeur appelée audio.)

Vous aurez besoin d'une référence à l'objet de lecture créant l'audio. Puisque je ne joue que de l'audio et qu'AVplayer ne respecte pas l'audio d'arrière-plan, utilisez ceci dans l'en-tête de votre contrôleur de vue:

@property (nonatomic, retain) MPMoviePlayerController *audioPlayer;

Dans la mise en œuvre, procédez comme suit:

 [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];

et

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

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

ajouter deux méthodes

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

maintenant TOUS le code important - ceci permet à votre application de contrôler l'audio à partir du "centre de contrôle" et de l'écran de verrouillage:

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

    if (receivedEvent.type == UIEventTypeRemoteControl) {

        switch (receivedEvent.subtype) {

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

            default:
                break;
        }
    }
}

vous pouvez ajouter ici de nombreux types de types d'événements et appeler n'importe quelle méthode.

Les événements typiques sont:

 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,

Pour déboguer de l’aide, vous pouvez utiliser:

 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;

et c'est tout - espérons que cela aidera quelqu'un là-bas! - Cela fonctionne parfaitement sur iOS 7 et iOS 6 avec l’application Storyboard, ainsi que le contrôle à l’aide du casque et de tous les nouveaux centres de contrôle.

16
codejunkie

Apparemment, le problème était du côté d’Apple, car la mise à jour iOS 7.0.3 corrige ce problème. Outre ce que Alex a noté à propos de UIEventSubtype, le code qui fonctionnait sur iOS6 fonctionne désormais sur iOS7.

Par souci d’exhaustivité, voici mon code pertinent qui fonctionne à la fois dans iOS6 et iOS7 - après la mise à jour vers 7.0.3. Également inclus AVFoundation.framework et MediaPlayer.framework dans les phases de construction du projet -> Lien binaire avec les bibliothèques. Aucun code pour ce délégué dans l'application.

Dans le fichier viewcontroller .h:

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

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

Dans le fichier 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;
        }
    }
}
7
MikeFieldsNE

Si vous voulez aussi jouer de l'audio en arrière-plan dans iphone et simulateur, vous devez écrire ce code en plusieurs fois. Assurez-vous tout d'abord que votre Info.plist répertorie correctement l'audio en tant qu'arrière-plan.

(Si vous ne savez pas de quoi je parle, sélectionnez YOURAPPNAME-Info.plist et sélectionnez-le. Cliquez sur le signe plus, tapez une clé UIBackgroundModes et entrez. Ajoutez une valeur appelée "App lit l'audio" (pour le simulateur) ou "App joue audio ou flux audio/vidéo avec AirPlay "(For iphone).)

enter image description here

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

Ajoutez ces deux frameworks dans votre projet et une ligne de code dans ViewController.h

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

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

Rappelez-vous que ces références de cadres doivent être ajoutées dans votre projet.

Puis dans 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];
}

J'espère que cela vous aidera à lire de l'audio en arrière-plan dans un iphone et un simulateur.

6
chandan

Une chose à noter qui diffère d'iOS6 à iOS7 avec des événements de contrôle à distance est que, dans iOS6, les événements de lecture/pause se présentaient sous la forme d'une UIEventSubtype:
UIEventSubtypeRemoteControlTogglePlayPause
Et dans iOS7, ils sont divisés en deux sous-types distincts:
UIEventSubtypeRemoteControlPause
UIEventSubtypeRemoteControlPlay

4
Alex Lindblad

Puisque je suis également intéressé à trouver cette solution, je vais ajouter quelques informations supplémentaires de mon côté.

Je rencontre le même problème, mais la documentation Apple n'a toujours pas changé en ce qui concerne la gestion des événements de contrôle à distance.

J'ai essayé de déplacer des choses et quelque chose d'intéressant s'est passé.

À l'origine, j'avais la gestion des événements de contrôle à distance dans mon contrôleur TabBar. Maintenant que j'ai tout déplacé dans le contrôleur de vue du lecteur, je peux voir que je peux à nouveau contrôler la lecture de musique avec mon casque mais pas à l'aide des boutons du nouveau panneau de configuration d'iOS7 (celui qui vient du bas à l'écran).

C'est assez bizarre.

Afin de résoudre le problème, essayons d'enrichir le fil avec notre test.

0
super