1. 程式人生 > >ios 獲取avplayer播放聲音完成時的衝突探究

ios 獲取avplayer播放聲音完成時的衝突探究

最近專案裡有一個需求,是個直播專案,需要主播在開播端,點選聊天區某條語音資訊的播放按鈕來播放觀眾的語音(這裡我用的是在語音播放單例新增播放完畢監聽;在本vc控制器將出現時為語音播放處理單例新增語音播放完成監聽,在vc控制器將要消失時為單例移出監聽),並且播放完成,按鈕切換為初始狀態;而且在主播端開播前會播放一個“倒計時”視訊,並且播放視訊;但是在這裡遇到一個問題:沒有語音播放,但是卻走了單裡裡邊語音播放完成時監聽的方法?

在這裡猜測可能是開播前“倒計時”視訊播放完畢也發出了播放完畢的通知,並且驗證了下,的確是這樣的,以後要注意。

程式碼如下:

單例.m檔案,主要是add,remove播放完成通知方法被vc呼叫.

#import "JJHVoiceOpenQueue.h"
#import "JJHVoiceVentor.h"

@implementation JJHVoiceOpenQueue

+(JJHVoiceOpenQueue *)sharedQueue {
    static JJHVoiceOpenQueue  * instance = nil;
    static dispatch_once_t predict;
    dispatch_once(&predict, ^{
        instance = [[JJHVoiceOpenQueue alloc] init];
        //朱明俊:初始化的時候不用清除訊息和暫停播放器
        //[instance otherOperationShouldStopNowPlayAndClearMp3Queue];
    });
    return instance;
}
//佇列新增一個發言語音
-(void)addVoiceChatMp3FileUrlStr:(NSString*)Mp3UrlStr{
    
    if (self.voiceArray.count == 0) {
        //說明播放佇列是播放完的,或者是剛啟動程式初次播放,再加入就直接播放
        [self.voiceArray addObject:Mp3UrlStr];
        [self playVoiceArrFirst];
    }else{
        //說明播放佇列沒播放完,語音正在播放中,只新增
        [self.voiceArray addObject:Mp3UrlStr];
    }
}
//接著播放下一段
-(void)playWithNext{
    //流程:先刪除播放完的錄音->判斷佇列還有錄音沒有->有繼續播放,沒有不做操作
    if (self.voiceArray.count>0) {
        //刪除剛剛播放過的語音
        [self.voiceArray removeObjectAtIndex:0];
        //如果大於0,繼續下一首的播放
        [self playVoiceArrFirst];
    }
}
-(void)playVoiceArrFirst{
    //播放前畢傳送通知,重置聊天區語音圖示
    [[NSNotificationCenter defaultCenter] postNotificationName:VOICE_PLAYED_OVER_MSG object:nil userInfo:nil];
    
    //播放下一條
    if (self.voiceArray.count>0) {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback error:nil];
        [session setActive:YES error:nil];
        
        //http://jjhsound.jjhgame.com/181109105926-2690188-90602998.mp3
        NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:@"%@",self.voiceArray[0]]];
        AVPlayerItem * songItem = [[AVPlayerItem alloc]initWithURL:url];
        [self.voicePlayer replaceCurrentItemWithPlayerItem:songItem];
        [self.voicePlayer play];
        
        //播放傳送通知,修改聊天區語音狀態,就是修改圖片為播放過的圖片
        NSDictionary *voiceDic = @{@"urlStr":[NSString stringWithFormat:@"%@",self.voiceArray[0]]};
        [[NSNotificationCenter defaultCenter] postNotificationName:VOICE_PLAYED_MSG object:nil userInfo:voiceDic];
    }
    
    
}



//直播間直接點選播放
//播放語音
-(void)playVoiceWithVoiceUrlStr:(NSString*)voiceUrlStr{
    //清空播放佇列
    [self otherOperationShouldStopNowPlayAndClearMp3Queue];
    
    //將獲取的url新增到第一個
    [self addVoiceChatMp3FileUrlStr:voiceUrlStr];
    
}
//暫停播放,語音佇列清空
//而且如果正在播放,需要暫停
//不必移除播放監聽,播放監聽只是順序播放下一首,程式執行期間和本單例一直存在
-(void)otherOperationShouldStopNowPlayAndClearMp3Queue{
    [self.voicePlayer pause];
    [self.voiceArray removeAllObjects];
    
    //其他異常操作傳送通知,重置聊天區語音圖示,如,正在播放,點選錄音等等情況
    [[NSNotificationCenter defaultCenter] postNotificationName:VOICE_PLAYED_OVER_MSG object:nil userInfo:nil];
}
#pragma - lazy
-(AVPlayer *)voicePlayer{
    if (!_voicePlayer) {
        _voicePlayer = [[AVPlayer alloc] initWithPlayerItem:nil];
    }
    return _voicePlayer;
}
//直播間成對呼叫
-(void)addNoti{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
-(void)removeNoti{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
-(void)playbackFinished{
    //播放完畢傳送通知,重置聊天區語音圖示
    [[NSNotificationCenter defaultCenter] postNotificationName:VOICE_PLAYED_OVER_MSG object:nil userInfo:nil];
    
NSLog(@"測試語音和視訊播放完畢是否會發出走這個通知");
    //接著播放下一條錄音,
    //[self playWithNext];
}
//聲音url佇列
-(NSMutableArray *)voiceArray{
    if (_voiceArray == nil) {
        _voiceArray = [[NSMutableArray alloc] init];
    }
    return _voiceArray;
}

@end

然後是vc的主要.m方法

- (void)playMovieBegin {
    
    NSString * path = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"mp4"];
    
    //NSString *path =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSLog(@"path - %@",path);
    
    [self.moviePlayer setContentURL:[NSURL fileURLWithPath:path]];
    
    [self.moviePlayer prepareToPlay];
    
    [self.moviePlayer play];
    
}


- (MPMoviePlayerController *)moviePlayer {
    
    if(!_moviePlayer){
        _moviePlayer = [[MPMoviePlayerController alloc]init];
        [_moviePlayer.view setFrame:kYBScreenBounds];
        ///關閉自動播放
        [_moviePlayer setShouldAutoplay:NO];
        ///檔案型別
        [_moviePlayer setMovieSourceType:MPMovieSourceTypeFile];
        ///取出控制進度條
        [_moviePlayer setControlStyle:MPMovieControlStyleNone];
        [[UIApplication sharedApplication].keyWindow addSubview:_moviePlayer.view];
        
    
    return _moviePlayer;
}

-(void)viewwillappear{
[super viewwillappear];
[[JJHVoiceOpenQueue sharedQueue] addNoti];
}

然後在沒有聊天區語音資訊播放的時候卻在控制檯打印出了

2018-12-19 11:41:54.514349+0800 Daka2[1404:397617] path - /var/containers/Bundle/Application/80173D9D-1D0A-4226-A62F-C2B6EA72392B/Daka2.app/Prepare.mp4
2018-12-19 11:41:58.397335+0800 Daka2[1404:397617] 測試語音和視訊播放完畢是否會發出走這個通知

說明mp4視訊播放完畢,也會發出AVPlayerItemDidPlayToEndTimeNotification通知,執行了我以為的語音播放完畢的方法,要注意(使用了MPMoviePlayerControllerAVPlayer來播放的檔案,不管是聲音還是視訊,都會發出AVPlayerItemDidPlayToEndTimeNotification通知),要識別到底是聲音播放完畢,還是視訊播放完畢,還需要做更細緻的區分.

另外使用MPMoviePlayerController播放視訊,不需要設定成vc的strong屬性,去持有它,不然p呼叫lay方法也無法正常播放

更多交流,請加群討論:565191947