版本記錄
| 版本號 | 時間 |
|---|---|
| V1.0 | 2017.06.04 |
前言
以前做過移動直播項目,做直播的推流和拉流用的是樂視的移動直播業(yè)務(wù),集成樂視的移動業(yè)務(wù)的SDK。前面已經(jīng)對樂視移動直播進(jìn)行了簡單的介紹,感興趣的可以看下。這一篇主要講一下移動直播推流和拉流的封裝。
1.樂視移動直播的集成(一)
詳情
相信看過我第一篇文章的人都已經(jīng)對樂視移動直播的業(yè)務(wù)和SDK都有了一個大概的了解,而目前公司的app應(yīng)用,應(yīng)該基本都是定制,也就是說不用樂視移動直播提供的皮膚,都是自己定制的皮膚,還好樂視SDK中提供了無皮膚的推流拉流,在集成使用中我們可以直接使用樂視SDK的接口,還可以封裝一層,方便使用,我這里就是封裝了一層。具體如下:
一、推流端封裝
直接看代碼吧
1.JJPushManager.h
#import <Foundation/Foundation.h>
#import <LeCloudStreamingDynamic/LCStreamingManager.h>
@interface JJPushManager : NSObject <LCStreamingManagerDelegate>
@property (nonatomic, copy) NSString * pushURLStr; //推流地址
@property (nonatomic, copy) void(^pushSuccessBlock)(); //推流成功回調(diào)
@property (nonatomic, copy) void(^pushStopBlock)(); //推流停止回調(diào)
@property (nonatomic, assign) BOOL isFront; //是否是后置攝像頭
@property (nonatomic, strong) LCStreamingManager *manager;
//初始化函數(shù) 1:垂直 2:左方向 3:右方向
- (instancetype)initWithIsPortrait:(NSInteger)isPortrait;
//預(yù)覽視圖
- (UIView *) videoView;
//開始預(yù)覽
- (int)startPreview:(UIView*)view;
//停止預(yù)覽
- (void) stopPreView;
//暫停推流
- (void) pausePush;
//繼續(xù)推流
- (void) resumePush;
//停止推流
- (void) stopPush;
//切換攝像頭
- (void) switchCamera:(BOOL)isFront;
//開啟/關(guān)閉美顏
- (void) switchBeautyFilterDepth:(float)_beauty_level setWhiteningFilterDepth:(float)_whitening_level;
//開啟/關(guān)閉閃光燈
- (void) toggleTorch:(BOOL)isfront;
//手動對焦***暫時未開啟
- (void)setFocusPosition:(CGPoint)touchPoint;
@end
2. JJPushManager.m
#import "JJPushManager.h"
@interface JJPushManager ()
@end
@implementation JJPushManager
- (instancetype)initWithIsPortrait:(NSInteger)isPortrait
{
self.manager = [LCStreamingManager sharedManager];
self.manager.delegate = self;
if (isPortrait == 1) {
self.manager.pushOrientation = UIInterfaceOrientationPortrait;
[self.manager configVCSessionWithVideoSize:CGSizeMake(720, 1280) frameRate:18 bitrate:1500 useInterfaceOrientation:YES];
}
else if (isPortrait == 2){
self.manager.pushOrientation = UIInterfaceOrientationLandscapeRight;
[self.manager configVCSessionWithVideoSize:CGSizeMake(1280, 720) frameRate:18 bitrate:1500 useInterfaceOrientation:YES];
}
else if(isPortrait == 3){
self.manager.pushOrientation = UIInterfaceOrientationLandscapeLeft;
[self.manager configVCSessionWithVideoSize:CGSizeMake(1280, 720) frameRate:18 bitrate:1500 useInterfaceOrientation:YES];
}
[self.manager configVideoViewFrame:[UIScreen mainScreen].bounds];
[self.manager setFilter:LCVideoFilterBeautyFace];
[self.manager setCamareOrientationState:LCCamareOrientationStateFront];
return self;
}
//開始預(yù)覽
- (int)startPreview:(UIView*)view
{
[view addSubview:[self.manager videoView]];
return 0;
}
//停止預(yù)覽
- (void) stopPreView
{
}
//開始推流
- (void) setPushURLStr:(NSString *)pushURLStr
{
_pushURLStr = pushURLStr;
[self.manager startStreamingWithRtmpAdress:pushURLStr];
}
//預(yù)覽視圖
- (UIView *)videoView
{
return [self.manager videoView];
}
//停止推流
- (void)stopPush
{
[self.manager stopStreaming];
[self.manager cleanSession];
}
//推流狀態(tài)變化通知
- (void)connectionStatusChanged:(LCStreamingSessionState)sessionState
{
if (sessionState == LCStreamingSessionStatePreviewStarted) {
NSLog(@"開始預(yù)覽");
}
else if (sessionState == LCStreamingSessionStateStarting) {
NSLog(@"推流中");
}
else if (sessionState == LCStreamingSessionStateStarted) {
NSLog(@"開始推流");
if (self.pushSuccessBlock) {
self.pushSuccessBlock();
}
}
else if (sessionState == LCStreamingSessionStateEnded) {
NSLog(@"推流結(jié)束");
if (self.pushStopBlock) {
self.pushStopBlock();
}
}
else if (sessionState == LCStreamingSessionStateError) {
NSLog(@"推流錯誤");
if (self.pushStopBlock) {
self.pushStopBlock();
}
}
}
//切換攝像頭
- (void) switchCamera:(BOOL)isFront
{
if (isFront) {
[self.manager setCamareOrientationState:LCCamareOrientationStateFront];
}
else {
[self.manager setCamareOrientationState:LCCamareOrientationStateBack];
}
}
//開啟/關(guān)閉美顏
- (void) switchBeautyFilterDepth:(float)_beauty_level setWhiteningFilterDepth:(float)_whitening_level
{
if (_beauty_level > 0) {
[self.manager setFilter:LCVideoFilterBeautyFace];
}
else {
[self.manager setFilter:LCVideoFilterNone];
}
}
//開啟/關(guān)閉閃光燈
- (void) toggleTorch:(BOOL)isfront
{
[self.manager setTorchOpenState:isfront];
}
//手動對焦***暫時未開啟
- (void)setFocusPosition:(CGPoint)touchPoint
{
}
//暫停推流
- (void) pausePush
{
}
//繼續(xù)推流
- (void) resumePush
{
}
@end
在使用過程中有幾點注意:
- 樂視不支持邊直播推流邊改變推流方向,所以要在推流前就確定好推流方向,推流開始后就不能更改。這里我采取的方案就是直播開始前,利用本地相機(jī)開始預(yù)覽和橫豎屏轉(zhuǎn)換。
- 這個封裝需要從后臺服務(wù)器獲取節(jié)目推流地址,rtmp格式的,一般是后臺配好了傳遞給服務(wù)端,利用setter方法給pushURLStr賦值就是默認(rèn)了開始推流了,不用調(diào)用其他的方法。
二、拉流端封裝
移動直播觀眾端需要拉流才能夠觀看視頻,下面看封裝代碼。
1. JJPlayManager.h
#import <Foundation/Foundation.h>
@protocol JJLiveVodDelegate <NSObject>
- (void) position:(int64_t) position
cacheDuration:(int64_t) cacheDuration
duration:(int64_t) duration;
@end
@interface ZBPlayManager : NSObject
@property (nonatomic, copy) NSString *playURL;
@property (nonatomic, assign) BOOL isLoading;
@property (nonatomic, weak) id<ZBLiveVodDelegate>delegate;
//拉流成功操作
@property (nonatomic, copy) void(^playBeginBlock)();
//手動結(jié)束操作
@property (nonatomic, copy) void(^endPlayBlock)();
//意外拉流失敗操作
@property (nonatomic, copy) void(^endPlayBlock2)();
//load狀態(tài)操作
@property (nonatomic, copy) void(^loadingBlock)();
//結(jié)束load狀態(tài)操作
@property (nonatomic, copy) void(^endLoadingBlock)();
- (void) removePlayer;
- (void) stopStartPlayer;
- (BOOL) seekToPosition:(NSInteger) position;
//暫停拉流
- (void) pausePlay;
//繼續(xù)拉流
- (void) resumePlay;
- (UIView *)videoView;
//獲取推流狀態(tài)
- (BOOL)getPushStatus;
@end
2. JJPlayManager.m
#import "JJPlayManager.h"
#import "LECVODPlayer.h"
@interface JJPlayManager () <LECPlayerDelegate>
@property (nonatomic, strong) LECPlayer *player;
@end
@implementation JJPlayManager
- (void)setPlayURL:(NSString *)playURL
{
_playURL = playURL;
[self startPlay];
}
//懶加載
- (LECPlayer *)player
{
if (!_player) {
_player = [[LECPlayer alloc] init];
[_player setDelegate:self];
}
return _player;
}
//開始播放
- (void) startPlay
{
if ([self.player registerWithURLString:self.playURL completion:^(BOOL result) {
[self.player prepare];
[self.player play];
}]) {
NSLog(@"拉流成功");
} else {
NSLog(@"拉流失敗");
}
[self.player prepare];
[self.player play];
}
//暫停播放
- (void)pausePlay
{
[self.player pause];
}
//繼續(xù)播放
- (void) resumePlay
{
[self.player resume];
}
- (UIView *) videoView
{
return [self.player videoView];
}
/*播放器播放狀態(tài)*/
- (void) lecPlayer:(LECPlayer *) player
playerEvent:(LECPlayerPlayEvent) playerEvent
{
if (playerEvent == LECPlayerPlayEventPrepareDone) {
NSLog(@"準(zhǔn)備結(jié)束");
}
else if (playerEvent == LECPlayerPlayEventEOS) {
NSLog(@"播放結(jié)束");
}
else if (playerEvent == LECPlayerPlayEventGetVideoSize) {
NSLog(@"視頻源Size");
}
else if (playerEvent == LECPlayerPlayEventRenderFirstPic) {
NSLog(@"獲取到第一幀");
if (self.playBeginBlock) {
self.playBeginBlock();
}
}
else if (playerEvent == LECPlayerPlayEventBufferStart) {
NSLog(@"開始緩沖");
if (self.loadingBlock) {
self.loadingBlock();
}
}
else if (playerEvent == LECPlayerPlayEventBufferEnd) {
NSLog(@"緩沖結(jié)束");
if (self.endLoadingBlock) {
self.endLoadingBlock();
}
}
else if (playerEvent == LECPlayerPlayEventSeekComplete) {
NSLog(@"seek結(jié)束");
}
else if (playerEvent == LECPlayerPlayEventPlayError) {
NSLog(@"播放出錯");
if (self.endPlayBlock2) {
self.endPlayBlock2();
}
}
else if (playerEvent == LECPlayerPlayEventDisplayError) {
NSLog(@"播放出錯");
if (self.endPlayBlock2) {
self.endPlayBlock2();
}
}
else if (playerEvent == LECPlayerPlayEventSuspend) {
NSLog(@"直播結(jié)束");
if (self.endPlayBlock) {
self.endPlayBlock();
}
}
else if (playerEvent == LECPlayerPlayEventNotStarted) {
NSLog(@"直播未開始");
}
}
/*播放器播放時間回調(diào)*/
- (void) lecPlayer:(LECPlayer *) player
position:(int64_t) position
cacheDuration:(int64_t) cacheDuration
duration:(int64_t) duration
{
if ([self.delegate respondsToSelector:@selector(position:cacheDuration:duration:)]) {
[self.delegate position:position cacheDuration:cacheDuration duration:duration];
}
NSLog(@"播放時間:%lld", duration);
}
//獲取推流方向
- (BOOL) getPushStatus
{
if (self.player.actualVideoWidth > self.player.actualVideoHeight) {
return NO;
}
return YES;
}
//seek到視頻相應(yīng)位置
- (BOOL) seekToPosition:(NSInteger) position
{
return [self.player seekToPosition:position];
}
- (void) stopStartPlayer
{
[self.player stop];
[self.player unregister];
}
- (void)onRecvConnectNofity
{
}
- (void) removePlayer
{
}
@end
后記
集成樂視的移動直播SDK碰到了不少的坑,但是通過和樂視技術(shù)人員溝通和自己的努力還是都解決了,樂視的文檔寫的確實有點差,但是技術(shù)人員態(tài)度很好。樂視想在移動直播服務(wù)上想分一杯羹,還是需要繼續(xù)努力的,謝謝大家~~

美女福利