2012-05-09 4 views
5

AVPlayer과 함께 정말 이상한 문제가 있습니다. 매우 간단한 뮤직 플레이어 (iTunes Store에서 샘플 음악을 스트리밍)가 시뮬레이터에서 제대로 작동하지만 (iOS 5.1이 적용된 iPhone 및 iPad 모두) 실제 장치에서는 비정상적으로 작동합니다.iOS AVPlayer 스트리밍 음악 문제

iOS 5.1.1이 설치된 iPad 2에서 헤드폰을 장치에 연결해도 제대로 재생됩니다. 하지만 스피커를 연결 해제하자마자 스피커를 통해 소리가 나지 않습니다 (다시 연결하면 노래를들을 수 있음).

iOS 5.1이 설치된 iPhone 4에서는 스피커를 통해 전혀 재생되지 않지만 내 헤드폰을 통해 음악을들을 수 있습니다. 스피커를 통해 연주하는 것 같지는 않지만 현재는 듣고 잠시 동안 음악을 재생할 수 있습니다 (실제로 UI가 그에 따라 반응하기 때문에 실제로 재생되는 것을 확인할 수 있습니다).

나는 요구 사항을 충족시키기 때문에 AVPlayer을 사용하고 있습니다. 다른 라이브러리를 사용해야합니까? 사운드를 수동으로 라우트해야합니까? 왜 이런 종류의 문제가 있습니까? 그리고 오디오 세션을 올바르게 사용하고 있습니까?

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

나는 그것이 길다는 것을 알고 있지만 setCategory를 호출 한 후에 setActive를 호출 해보십시오. – SteveB

+0

글쎄, 내가 동시에 다른 부분을 변경했기 때문에 그렇게 한 것은 아닙니다.하지만 스피커를 연결 해제 할 때마다 스피커로 라우트되지 않는 사운드는 예외입니다. 헤드폰. 고마워요! – dvieira

+0

AVAudioSessionCategoryPlayback을 사용하면 사운드가 자동으로 스피커로 라우팅됩니다. 그 일을 막는 코드가 있어야합니다. 더 많은 코드를 게시하거나 어딘가에 프로젝트를 업로드 할 수 있다면 한 번 살펴볼 수 있습니다. – SteveB

답변

0

것 같습니다. 오디오 경로 변경 리스너 콜백을 추가하는 것이 좋습니다.

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

그런 다음 init 메소드에 콜백을 추가합니다 : 우선이 방법을 선언

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

그리고 마지막으로, 콜백 구현을 추가

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.."); 
    } 
} 

희망이 도움이! 적어도 다른 사람들에게는 유용한 팁이 될 것입니다.

관련 문제