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

版本记录

版本号 时间
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碰到了不少的坑,但是通过和乐视技术人员沟通和自己的努力还是都解决了,乐视的文档写的确实有点差,但是技术人员态度很好。乐视想在移动直播服务上想分一杯羹,还是需要继续努力的,谢谢大家~~

美女福利
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容