樂視移動直播的集成(二)—— 推流和拉流端的封裝

版本記錄

版本號 時間
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ù)努力的,謝謝大家~~

美女福利
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容