之前說過推流的實(shí)現(xiàn) 瞄崇,現(xiàn)在來說一下拉流的實(shí)現(xiàn)。demo下載地址:https://pan.baidu.com/s/1miSAGre
拉流的實(shí)現(xiàn)我們主要通過LFLiveKit鸭你,LFLiveKit支持H264和AAC編碼爽彤,支持美顏和水印效果,傳輸協(xié)議是rtmp昂利,支持弱網(wǎng)丟幀届腐,記載網(wǎng)絡(luò)環(huán)境差的情況下,為了保證視頻的通常蜂奸,會(huì)自動(dòng)丟棄個(gè)別幀犁苏。
先看一下LFLiveKit提供的Demo運(yùn)行效果:(準(zhǔn)備工具VLC軟件)
1.github上搜索LFLiveKit。
2.打開下載后文件的LFLiveKitDemo工程(這里我們使用的是OC的Demo扩所,不是swift),控制臺(tái)中cd +這個(gè)工程的路徑围详,然后pod install,之后便可運(yùn)行在真機(jī)上。
3.打開LFLivePreview.m文件助赞,在這個(gè)文件的末尾會(huì)發(fā)現(xiàn)有這樣一行代碼: stream.url = @"rtmp://live.hkstv.hk.lxdns.com:1935/live/stream153”;并將這個(gè)網(wǎng)址復(fù)制到瀏覽器中打開买羞,此時(shí)會(huì)自動(dòng)打開VLC軟件,點(diǎn)擊playList中的網(wǎng)址雹食,便可播放手機(jī)端的拉流視頻畜普。
4.如果以上步驟執(zhí)行無誤,就說明手機(jī)端的拉流視頻已經(jīng)成功推送到服務(wù)器上群叶,我們通過VLC可以獲取到服務(wù)器上的推流視頻吃挑。
接下來看看如何將LFLiveKit繼承到我們自己的項(xiàng)目中去。
首先在podfile文件中添加pod ‘LFLiveKit’ 街立,然后 pod install舶衬。接下來就可以直接開始代碼部分了。
創(chuàng)建一個(gè)繼承與UIView的LFLivePreview類赎离,在LFLivePreview.h文件中對(duì)外提供兩個(gè)方法逛犹,開始拉直播和關(guān)閉直播方法。
@interface LFLivePreview : UIView
- (void)startLive;
- (void)stopLive;
@end
在LFLivePreview.m文件中蟹瘾,首先導(dǎo)入頭文件#import "LFLiveSession.h”圾浅,并遵守LFLiveSessionDelegate協(xié)議,然后添加一個(gè)屬性@property (nonatomic, strong) LFLiveSession *session;看一些這個(gè)屬性的懶加載代碼:
- (LFLiveSession *)session {
if (!_session) {
/*** 默認(rèn)分辨率368 * 640 音頻:44.1 iphone6以上48 雙聲道 方向豎屏 ***/
LFLiveAudioConfiguration * audioConfiguration =[LFLiveAudioConfiguration defaultConfiguration];
LFLiveVideoConfiguration * videoConfiguration = [LFLiveVideoConfiguration defaultConfiguration];
//最新寫法
_session = [[LFLiveSession alloc]initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration captureType:LFLiveCaptureDefaultMask];
// _session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration liveType:LFLiveRTMP];
_session.delegate = self;
_session.showDebugInfo = NO;
_session.preView = self;
}
return _session;
}
在初始化這個(gè)view的時(shí)候首先要實(shí)現(xiàn)音頻和視頻授權(quán)操作憾朴。
- (void)requestAccessForVideo {
__weak typeof(self) _self = self;
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (status) {
case AVAuthorizationStatusNotDetermined: {
// 許可對(duì)話沒有出現(xiàn)狸捕,發(fā)起授權(quán)許可
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[_self.session setRunning:YES];
});
}
}];
break;
}
case AVAuthorizationStatusAuthorized: {
// 已經(jīng)開啟授權(quán),可繼續(xù)
dispatch_async(dispatch_get_main_queue(), ^{
[_self.session setRunning:YES];
});
break;
}
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
// 用戶明確地拒絕授權(quán)众雷,或者相機(jī)設(shè)備無法訪問
break;
default:
break;
}
}
- (void)requestAccessForAudio {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
switch (status) {
case AVAuthorizationStatusNotDetermined: {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
}];
break;
}
case AVAuthorizationStatusAuthorized: {
break;
}
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
break;
default:
break;
}
}
開始直播
- (void)startLive {
LFLiveStreamInfo *stream = [LFLiveStreamInfo new];
//這是直播的網(wǎng)址,直接復(fù)制LFLiveKit提供的這個(gè)地址
stream.url = @"rtmp://live.hkstv.hk.lxdns.com:1935/live/stream153";
[self.session startLive:stream];
}
- (void)startLive:(LFLiveStreamInfo *)streamInfo {
if (!streamInfo) return;
_streamInfo = streamInfo;
_streamInfo.videoConfiguration = _videoConfiguration;
_streamInfo.audioConfiguration = _audioConfiguration;
[self.socket start];
}
停止直播
- (void)stopLive {
[self.session stopLive];
}
- (void)stopLive {
self.uploading = NO;
[self.socket stop];
self.socket = nil;
}
美顏效果
- (void)beautyBtnClick:(UIButton *)btn{
self.session.beautyFace = !self.session.beautyFace;
self.beautyButton.selected = !self.session.beautyFace;
}
鏡頭切換
- (void)cameraBtnClick:(UIButton *)btn{
AVCaptureDevicePosition devicePositon = self.session.captureDevicePosition;
self.session.captureDevicePosition = (devicePositon == AVCaptureDevicePositionBack) ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;
}
退出直播
- (void)closeBtnClick:(UIButton *)btn{
[self stopLive];
[self.controller dismissViewControllerAnimated:YES completion:nil];
}
下面這個(gè)代理方法是監(jiān)控直播狀態(tài)的代理方法
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state {
NSLog(@"liveStateDidChange: %ld", state);
switch (state) {
case LFLiveReady:
_stateLabel.text = @"未連接";
break;
case LFLivePending:
_stateLabel.text = @"連接中";
break;
case LFLiveStart:
_stateLabel.text = @"已連接";
break;
case LFLiveError:
_stateLabel.text = @"連接錯(cuò)誤";
break;
case LFLiveStop:
_stateLabel.text = @"未連接";
break;
default:
break;
}
}
外部直播控制器界面調(diào)用形式
- (IBAction)startLive:(id)sender {
UIView * back = [[UIView alloc] initWithFrame:self.view.bounds];
back.backgroundColor = [UIColor blackColor];
[self.view addSubview:back];
LFLivePreview * liveView = [[LFLivePreview alloc] initWithFrame:self.view.bounds withController:self];
[self.view addSubview:liveView];
[liveView startLive];
}