2012-05-09 13 views
5

Mam kilka naprawdę dziwnych problemów z AVPlayer. Mam bardzo prosty odtwarzacz muzyczny (który przesyła próbkę muzyki ze sklepu iTunes), który poprawnie z nim pracuje na symulatorze (zarówno iPhone, jak i iPad z systemem iOS 5.1), ale zachowuje się nienormalnie na prawdziwym urządzeniu.Problemy z odtwarzaniem muzyki w iOS AVPlayer

Na iPadzie 2 z iOS 5.1.1 gra poprawnie, nawet jeśli podłączę słuchawki do urządzenia. Ale zaraz po ich rozłączeniu nie będzie słychać dźwięku przez głośniki (nawet jeśli je ponownie połączę, będę mógł słuchać utworu).

Na iPhonie 4 z zainstalowanym systemem iOS 5.1 wydaje się, że nie gra wcale przez głośniki, ale mogę słuchać muzyki przez słuchawki. Chociaż wydaje się, że nie odtwarza się przez głośniki, przez chwilę słyszę muzykę odtwarzaną (i mogę potwierdzić, że tak naprawdę gra, ponieważ mój interfejs odpowiada odpowiednio), chociaż wydaje się, że jest przypadkowa.

Używam AVPlayer, ponieważ wydaje się, że pasuje do wymagań. Czy mam korzystać z innej biblioteki? Czy muszę ręcznie kierować dźwięk? Dlaczego mam tego typu problemy? Czy używam poprawnie sesji audio?

Media.h:

#import <Foundation/Foundation.h> 
#import <AVFoundation/AVFoundation.h> 
#import <AudioToolbox/AudioToolbox.h> 

#define NOT_PLAYING -1 

@protocol MediaDelegate <NSObject> 
@optional 
- (void) didEndPlayingAtIndex:(int) index; 
- (void) startedToPlayAtIndex:(int) index; 
- (void) stoppedToPlayAtIndex:(int) index; 
- (void) startedToPlayAtIndex:(int) to fromIndex:(int) from; 
- (void) pausedAtIndex:(int) index; 
@end 

@interface Media : NSObject <AVAudioSessionDelegate> 

@property (nonatomic, assign) int currentItem; 
@property (nonatomic, strong) NSURL *url; 
@property (nonatomic, strong) AVPlayer *player; 
@property (nonatomic, assign) id<MediaDelegate> delegate; 

- (void) toggle:(int) index; 
- (void) stop; 

@end 

Media.m:

#import "Media.h" 

@implementation Media 

@synthesize currentItem; 
@synthesize player; 
@synthesize delegate; 
@synthesize url; 

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     NSError *activationError = nil; 
     NSError *setCategoryError = nil; 
     AVAudioSession *session = [AVAudioSession sharedInstance]; 
     session.delegate = self; 
     [session setActive:YES error:&activationError]; 
     [session setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError]; 

     if(activationError || setCategoryError) 
      NSLog(@"ERROR: %@, %@",activationError,setCategoryError); 

     self.currentItem = NOT_PLAYING; 
    } 
    return self; 
} 

- (void)dealloc{ 
    NSError *activationError = nil; 
    [[AVAudioSession sharedInstance] setActive:NO error:&activationError]; 

    if(activationError) 
     NSLog(@"ERROR: %@",activationError); 

    [self.player removeObserver:self forKeyPath:@"status"]; 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{  
    switch (player.status) { 
     case AVPlayerItemStatusReadyToPlay: 
      [self.player play]; 
      if([self.delegate respondsToSelector:@selector(startedToPlayAtIndex:)]) 
       [self.delegate startedToPlayAtIndex:self.currentItem]; 
      break; 
     default: 
      break; 
    } 
} 

- (void) toggle:(int) index{ 
    if (self.currentItem == NOT_PLAYING) { 
     self.player = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithURL:self.url]]; 
     [[NSNotificationCenter defaultCenter] addObserver:self 
               selector:@selector(didEnd:) 
                name:AVPlayerItemDidPlayToEndTimeNotification 
                object:self.player]; 
     self.currentItem = index; 
     [self.player addObserver:self forKeyPath:@"status" options:0 context:nil]; 
    } 
    else { 
     if (self.currentItem == index) { 
      [self.player pause]; 
      if([self.delegate respondsToSelector:@selector(stoppedToPlayAtIndex:)]) 
       [self.delegate stoppedToPlayAtIndex:index]; 
      self.currentItem = NOT_PLAYING; 
      [self.player removeObserver:self forKeyPath:@"status"]; 
      self.player = nil; 
      [[NSNotificationCenter defaultCenter] removeObserver:self]; 
     } 
     else{ 
      [self.player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:self.url]]; 
      if([self.delegate respondsToSelector:@selector(startedToPlayAtIndex:fromIndex:)]) 
       [self.delegate startedToPlayAtIndex:index fromIndex:self.currentItem]; 
      self.currentItem = index; 
     } 
    } 
} 

- (void) stop{ 
    [self.player pause]; 
    if([self.delegate respondsToSelector:@selector(stoppedToPlayAtIndex:)]) 
     [self.delegate stoppedToPlayAtIndex:self.currentItem]; 
} 

-(void) didEnd:(id)sender{ 
    if([self.delegate respondsToSelector:@selector(didEndPlayingAtIndex:)]) 
     [self.delegate didEndPlayingAtIndex:self.currentItem]; 
    self.currentItem = NOT_PLAYING; 
    [self.player removeObserver:self forKeyPath:@"status"]; 
    self.player = nil; 
    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
} 

#pragma mark - AVAudioSession delegate 

-(void)beginInterruption{ 
    NSLog(@"Interruption"); 
    if(self.currentItem != NOT_PLAYING){ 
     [self.player pause]; 
     if([self.delegate respondsToSelector:@selector(pausedAtIndex:)]) 
      [self.delegate pausedAtIndex:self.currentItem]; 
    } 
} 

-(void)endInterruption{ 
    NSLog(@"Ended interruption"); 
    if(self.currentItem != NOT_PLAYING){ 
     [self.player play]; 
     if([self.delegate respondsToSelector:@selector(startedToPlayAtIndex:)]) 
      [self.delegate startedToPlayAtIndex:self.currentItem]; 
    } 
} 

@end 
+0

Wiem, że to jest długie ujęcie, ale spróbuj wywołać setActive po wywołaniu setCategory. – SteveB

+0

Cóż, nie jestem absolutnie pewny, że tak się stało, ponieważ wprowadziłem kilka innych zmian w tym samym czasie, ale teraz działa normalnie (do tej pory) z wyjątkiem dźwięku, który nie jest kierowany do głośników za każdym razem, gdy odłączę słuchawki. Dziękuję Ci bardzo! – dvieira

+0

Przy AVAudioSessionCategoryPlayback dźwięk powinien być automatycznie kierowany do głośników. W innym miejscu musi być jakiś kod, który to powstrzymuje. Jeśli możesz zamieścić więcej kodu lub przesłać swój projekt gdzieś, mogę rzucić okiem. – SteveB

Odpowiedz

0

Wygląda na to, że masz problem z prawidłowym routing audio. Proponuję dodać wywołanie zwrotne zmiany słuchacza trasy audio. Przede wszystkim zadeklarować metodę:

void audioRouteChangeListenerCallback(void *inUserData, AudioSessionPropertyID inPropertyID, UInt32 inPropertyValueSize, const void *inPropertyValue); 

Następnie w metodzie init, dodać zwrotnego:

// Prevent from audio issues when you pull out earphone 
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, audioRouteChangeListenerCallback, (__bridge void *)(self)); 

I wreszcie dodać realizację oddzwonienia:

void audioRouteChangeListenerCallback(void *inUserData, AudioSessionPropertyID inPropertyID, UInt32 inPropertyValueSize, const void *inPropertyValue) { 
    if (inPropertyID != kAudioSessionProperty_AudioRouteChange) { 
     return; 
    } 

    CFDictionaryRef routeChangeDictionary = inPropertyValue; 
    CFNumberRef  routeChangeReasonRef = CFDictionaryGetValue(routeChangeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason)); 
    SInt32   routeChangeReason; 
    CFNumberGetValue(routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); 

    if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) { 
     // Headset is unplugged.. 
     NSLog(@"Headset is unplugged.."); 
     // Delayed play: 0.5 s. 
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 
      [CORE.player play]; // NOTE: change this line according your current player implementation 
      NSLog(@"resumed play"); 
     }); 
    } 
    if (routeChangeReason == kAudioSessionRouteChangeReason_NewDeviceAvailable) { 
     // Headset is plugged in.. 
     NSLog(@"Headset is plugged in.."); 
    } 
} 

nadzieję, że to pomaga! Przynajmniej dla innych osób będzie to wskazówka pomocna.

Powiązane problemy