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