前言:直播模塊主要采用了
BiliBili視頻提供的開源框架ijkplayer
完成拉流收毫,優(yōu)酷的來瘋直播提供的開源框架LFLiveKit
完成直播推流需纳。項目中同時還涉及了動畫疮薇,繪圖畜疾,定位龟再,自動布局,三方登錄等等熱門技術(shù)审胸。
# 核心代碼
#import <IJKMediaFramework/IJKMediaFramework.h>
@interface ZYSPlayerViewController ()
@property(atomic, retain) id<IJKMediaPlayback> player;
@end
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.navigationBarHidden = YES;
//注冊直播需要用的通知
[self installMovieNotificationObservers];
//準備播放
[self.player prepareToPlay];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.navigationController.navigationBarHidden = NO;
//關(guān)閉直播
[self.player shutdown];
[self removeMovieNotificationObservers];
}
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化
[self initPlayer];
}
- (void)initPlayer {
///初始化ijkplayer并添加
IJKFFOptions * options = [IJKFFOptions optionsByDefault];
IJKFFMoviePlayerController * player = [[IJKFFMoviePlayerController alloc] initWithContentURLString:self.live.streamAddr withOptions:options];
self.player = player;
self.player.view.frame = self.view.bounds;
self.player.shouldAutoplay = YES;
[self.view addSubview:self.player.view];
}
注冊通知&移除通知注冊通知(ijkplayer是對FFMPeg進行封裝,對拉流得到的H264視頻編碼以及AAC的音頻進行解析,通過通知做響應(yīng)的操作)
# 關(guān)鍵代碼
#pragma mark Install Movie Notifications(注冊通知ijkplayer是對FFMPeg進行封裝,對拉流得到的H264視頻編碼以及AAC的音頻進行解析,通過通知做響應(yīng)的操作)
/* Register observers for the various movie object notifications. */
-(void)installMovieNotificationObservers
{
//監(jiān)聽網(wǎng)絡(luò)環(huán)境亥宿,監(jiān)聽緩沖方法
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(loadStateDidChange:)
name:IJKMPMoviePlayerLoadStateDidChangeNotification
object:_player];
//監(jiān)聽直播完成回調(diào)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayBackDidFinish:)
name:IJKMPMoviePlayerPlaybackDidFinishNotification
object:_player];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mediaIsPreparedToPlayDidChange:)
name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
object:_player];
//監(jiān)聽用戶主動操作
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayBackStateDidChange:)
name:IJKMPMoviePlayerPlaybackStateDidChangeNotification
object:_player];
}
#pragma mark Remove Movie Notification Handlers
/* Remove the movie notification observers from the movie object. */
-(void)removeMovieNotificationObservers
{
[[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMoviePlayerLoadStateDidChangeNotification object:_player];
[[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMoviePlayerPlaybackDidFinishNotification object:_player];
[[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification object:_player];
[[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMoviePlayerPlaybackStateDidChangeNotification object:_player];
}
///直播狀態(tài)變化的監(jiān)聽
- (void)moviePlayBackStateDidChange:(NSNotification*)notification
{
// MPMoviePlaybackStateStopped,
// MPMoviePlaybackStatePlaying,
// MPMoviePlaybackStatePaused,
// MPMoviePlaybackStateInterrupted,
// MPMoviePlaybackStateSeekingForward,
// MPMoviePlaybackStateSeekingBackward
switch (_player.playbackState)
{
case IJKMPMoviePlaybackStateStopped: {
NSLog(@"IJKMPMoviePlayBackStateDidChange %d: stoped", (int)_player.playbackState);
break;
}
case IJKMPMoviePlaybackStatePlaying: {
NSLog(@"IJKMPMoviePlayBackStateDidChange %d: playing", (int)_player.playbackState);
break;
}
case IJKMPMoviePlaybackStatePaused: {
NSLog(@"IJKMPMoviePlayBackStateDidChange %d: paused", (int)_player.playbackState);
break;
}
case IJKMPMoviePlaybackStateInterrupted: {
NSLog(@"IJKMPMoviePlayBackStateDidChange %d: interrupted", (int)_player.playbackState);
break;
}
case IJKMPMoviePlaybackStateSeekingForward:
case IJKMPMoviePlaybackStateSeekingBackward: {
NSLog(@"IJKMPMoviePlayBackStateDidChange %d: seeking", (int)_player.playbackState);
break;
}
default: {
NSLog(@"IJKMPMoviePlayBackStateDidChange %d: unknown", (int)_player.playbackState);
break;
}
}
}
///直播結(jié)束的監(jiān)聽
- (void)moviePlayBackDidFinish:(NSNotification*)notification
{
// MPMovieFinishReasonPlaybackEnded, 直播結(jié)束
// MPMovieFinishReasonPlaybackError, 直播錯誤
// MPMovieFinishReasonUserExited 用戶退出
int reason = [[[notification userInfo] valueForKey:IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
switch (reason)
{
case IJKMPMovieFinishReasonPlaybackEnded:
NSLog(@"playbackStateDidChange: IJKMPMovieFinishReasonPlaybackEnded: %d\n", reason);
break;
case IJKMPMovieFinishReasonUserExited:
NSLog(@"playbackStateDidChange: IJKMPMovieFinishReasonUserExited: %d\n", reason);
break;
case IJKMPMovieFinishReasonPlaybackError:
NSLog(@"playbackStateDidChange: IJKMPMovieFinishReasonPlaybackError: %d\n", reason);
break;
default:
NSLog(@"playbackPlayBackDidFinish: ???: %d\n", reason);
break;
}
}
///相關(guān)設(shè)置狀態(tài)
- (void)loadStateDidChange:(NSNotification*)notification
{
// MPMovieLoadStateUnknown = 0,未知
// MPMovieLoadStatePlayable = 1 << 0,緩沖結(jié)束可以播放
// MPMovieLoadStatePlaythroughOK = 1 << 1, // Playback will be automatically started in this state when shouldAutoplay is YES 緩沖結(jié)束自動播放
// MPMovieLoadStateStalled = 1 << 2, // Playback will be automatically paused in this state, if started
//暫停
IJKMPMovieLoadState loadState = _player.loadState;
if ((loadState & IJKMPMovieLoadStatePlaythroughOK) != 0) {
NSLog(@"loadStateDidChange: IJKMPMovieLoadStatePlaythroughOK: %d\n", (int)loadState);
} else if ((loadState & IJKMPMovieLoadStateStalled) != 0) {
NSLog(@"loadStateDidChange: IJKMPMovieLoadStateStalled: %d\n", (int)loadState);
} else {
NSLog(@"loadStateDidChange: ???: %d\n", (int)loadState);
}
self.blurImageView.hidden = YES;
[self.blurImageView removeFromSuperview];
}