乐视移动直播的集成(二)—— 推流和拉流端的封装

版本记录

版本号 时间
V1.0 2017.06.04

前言

以前做过移动直播项目,做直播的推流和拉流用的是乐视的移动直播业务,集成乐视的移动业务的SDK。前面已经对乐视移动直播进行了简单的介绍,感兴趣的可以看下。这一篇主要讲一下移动直播推流和拉流的封装。
1.乐视移动直播的集成(一)

详情

相信看过我第一篇文章的人都已经对乐视移动直播的业务和SDK都有了一个大概的了解,而目前公司的app应用,应该基本都是定制,也就是说不用乐视移动直播提供的皮肤,都是自己定制的皮肤,还好乐视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)();      //推流成功回调
@property (nonatomic, copy) void(^pushStopBlock)();         //推流停止回调
@property (nonatomic, assign) BOOL isFront;                 //是否是后置摄像头
@property (nonatomic, strong) LCStreamingManager *manager;

//初始化函数 1:垂直   2:左方向  3:右方向
- (instancetype)initWithIsPortrait:(NSInteger)isPortrait;

//预览视图
- (UIView *) videoView;

//开始预览
- (int)startPreview:(UIView*)view;

//停止预览
- (void) stopPreView;

//暂停推流
- (void) pausePush;

//继续推流
- (void) resumePush;

//停止推流
- (void) stopPush;

//切换摄像头
- (void) switchCamera:(BOOL)isFront;

//开启/关闭美颜
- (void) switchBeautyFilterDepth:(float)_beauty_level setWhiteningFilterDepth:(float)_whitening_level;

//开启/关闭闪光灯
- (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;
}

//开始预览
- (int)startPreview:(UIView*)view
{
    [view addSubview:[self.manager videoView]];
    
    return 0;
}

//停止预览
- (void) stopPreView
{
    
}

//开始推流
- (void) setPushURLStr:(NSString *)pushURLStr
{
    _pushURLStr = pushURLStr;
    [self.manager startStreamingWithRtmpAdress:pushURLStr];
}

//预览视图
- (UIView *)videoView
{
    return [self.manager videoView];
}

//停止推流
- (void)stopPush
{
    [self.manager stopStreaming];
    [self.manager cleanSession];
}

//推流状态变化通知
- (void)connectionStatusChanged:(LCStreamingSessionState)sessionState
{
    if (sessionState == LCStreamingSessionStatePreviewStarted) {
        NSLog(@"开始预览");
    }
    
    else if (sessionState == LCStreamingSessionStateStarting) {
        NSLog(@"推流中");
    }
    
    else if (sessionState == LCStreamingSessionStateStarted) {
        
        NSLog(@"开始推流");
        if (self.pushSuccessBlock) {
            self.pushSuccessBlock();
        }
    }
    
    else if (sessionState == LCStreamingSessionStateEnded) {
        
        NSLog(@"推流结束");
        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];
    }
}

//开启/关闭美颜
- (void) switchBeautyFilterDepth:(float)_beauty_level setWhiteningFilterDepth:(float)_whitening_level
{
    if (_beauty_level > 0) {
        [self.manager setFilter:LCVideoFilterBeautyFace];
    }
    else {
        [self.manager setFilter:LCVideoFilterNone];
    }
    
}

//开启/关闭闪光灯
- (void) toggleTorch:(BOOL)isfront
{
    [self.manager setTorchOpenState:isfront];
}

//手动对焦***暂时未开启
- (void)setFocusPosition:(CGPoint)touchPoint
{

}

//暂停推流
- (void) pausePush
{

}

//继续推流
- (void) resumePush
{
    
}

@end

在使用过程中有几点注意:

  • 乐视不支持边直播推流边改变推流方向,所以要在推流前就确定好推流方向,推流开始后就不能更改。这里我采取的方案就是直播开始前,利用本地相机开始预览和横竖屏转换。
  • 这个封装需要从后台服务器获取节目推流地址,rtmp格式的,一般是后台配好了传递给服务端,利用setter方法给pushURLStr赋值就是默认了开始推流了,不用调用其他的方法。

二、拉流端封装

移动直播观众端需要拉流才能够观看视频,下面看封装代码。

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)();

//手动结束操作
@property (nonatomic, copy) void(^endPlayBlock)();

//意外拉流失败操作
@property (nonatomic, copy) void(^endPlayBlock2)();

//load状态操作
@property (nonatomic, copy) void(^loadingBlock)();

//结束load状态操作
@property (nonatomic, copy) void(^endLoadingBlock)();

- (void) removePlayer;

- (void) stopStartPlayer;

- (BOOL) seekToPosition:(NSInteger) position;

//暂停拉流
- (void) pausePlay;

//继续拉流
- (void) resumePlay;

- (UIView *)videoView;

//获取推流状态
- (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];
}

//继续播放
- (void) resumePlay
{
    [self.player resume];
}

- (UIView *) videoView
{
    return [self.player videoView];
}

/*播放器播放状态*/
- (void) lecPlayer:(LECPlayer *) player
       playerEvent:(LECPlayerPlayEvent) playerEvent
{
    if (playerEvent == LECPlayerPlayEventPrepareDone) {
        NSLog(@"准备结束");
    }
    
    else if (playerEvent == LECPlayerPlayEventEOS) {
        NSLog(@"播放结束");
    }
    
    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(@"缓冲结束");
        if (self.endLoadingBlock) {
            self.endLoadingBlock();
        }
    }
    
    else if (playerEvent == LECPlayerPlayEventSeekComplete) {
        NSLog(@"seek结束");
    }
    
    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(@"直播结束");
        if (self.endPlayBlock) {
            self.endPlayBlock();
        }
    }
    
    else if (playerEvent == LECPlayerPlayEventNotStarted) {
        NSLog(@"直播未开始");
    }
}

/*播放器播放时间回调*/
- (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到视频相应位置
- (BOOL) seekToPosition:(NSInteger) position
{
    return [self.player seekToPosition:position];
}

- (void) stopStartPlayer
{
    [self.player stop];
    [self.player unregister];
}

- (void)onRecvConnectNofity
{
    
}

- (void) removePlayer
{
    
}

@end

后记

集成乐视的移动直播SDK碰到了不少的坑,但是通过和乐视技术人员沟通和自己的努力还是都解决了,乐视的文档写的确实有点差,但是技术人员态度很好。乐视想在移动直播服务上想分一杯羹,还是需要继续努力的,谢谢大家~~

美女福利
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容