本文借鉴官方文档编写,集成项目需要的推流和拉流部分
点击下载SDK
获取测试用的 License,您会获得两个字符串:一个字符串是 licenseURL,另一个字符串是解密 key。
配置运行系统为 iOS 9.0 以上。
如需要进入后台仍然运行相关功能,可选中当前工程项目,在 Capabilities 下设置 Background Modes 为 ON,并勾选 Audio,AirPlay and Picture in Picture
POD:
# 腾讯云短视频SDK
pod 'TXLiteAVSDK_Professional'
在用到SDK的地方引入头文件#import "TXLiteAVSDK_Smart/TXLiteAVSDK.h"
在项目启动时配置SDK,这里有个坑的地方,licenceURL在控制台生成的时候是http开头,我们iOS需要将它改为https的,才能下载成功。
//直播
#define TX_Live_licenceKey @"a3a6**********0877"
#define TX_Live_licenceURL @"https://license.vod2.myqcloud.com/license/v1/d766fa35**********955c322/TXLiveSDK.licence"
[TXLiveBase setLicenceURL:TX_Live_licenceURL key:TX_Live_licenceKey];
3.1初始化 TXLivePush 组件
首先创建一个TXLivePushConfig对象。该对象可以指定一些高级配置参数,但一般情况下我们不建议您操作该对象,因为我们已经在其内部配置好了所有需要校调的参数。之后再创建一个TXLivePush对象,该对象负责完成推流的主要工作
{
TCBeautyPanel *_beautyPanel; // 美颜控件
}
@property (nonatomic, strong) TXLivePush *livePusher;
#pragma mark == setUpLivePusher
- (void)setUpLivePusher {
// config初始化
TXLivePushConfig *config = [[TXLivePushConfig alloc] init];
config.pauseFps = 10;
config.pauseTime = 300;
config.pauseImg = [UIImage imageNamed:@"pause_publish"];
//是否允许点击曝光聚焦
config.touchFocus = NO;
//是否允许双指手势放大预览画面
config.enableZoom = NO;
//否为纯音频推流
config.enablePureAudioPush = NO;
//是否开启耳返特效
config.enableAudioPreview = NO;
// config.frontCamera = _btnCamera.tag == 0 ? YES : NO;
// /水印图片,设为 nil 等同于关闭水印
config.watermark = nil;
config.watermarkPos = CGPointMake(10, 10);
// 推流器初始化
self.livePusher = [[TXLivePush alloc] initWithConfig:config];
//设置混响效果
[self.livePusher setReverbType:NO];
//设置变声类型
[self.livePusher setVoiceChangerType:NO];
//打开后置摄像头旁边的闪关灯
[self.livePusher toggleTorch:NO];
//设置视频镜像效果
[self.livePusher setMirror:NO];
//开启静音
[self.livePusher setMute:NO];
//设置视频编码质量
[self.livePusher setVideoQuality:VIDEO_QUALITY_HIGH_DEFINITION adjustBitrate:NO adjustResolution:NO];
#ifdef ENABLE_CUSTOM_MODE_AUDIO_CAPTURE
config.enableAEC = NO;
config.customModeType = CUSTOM_MODE_AUDIO_CAPTURE;
config.audioSampleRate = CUSTOM_AUDIO_CAPTURE_SAMPLERATE;
config.audioChannels = CUSTOM_AUDIO_CAPTURE_CHANNEL;
#endif
// 修改软硬编需要在setVideoQuality之后设置config.enableHWAcceleration
config.enableHWAcceleration = YES;
// 横屏推流需要先设置config.homeOrientation = HOME_ORIENTATION_RIGHT,然后再[pusher setRenderRotation:90]
config.homeOrientation = HOME_ORIENTATION_DOWN;
[self.livePusher setRenderRotation:0];
[self.livePusher setLogViewMargin:UIEdgeInsetsMake(120, 10, 60, 10)];
//打开包含视频状态信息的调试浮层--调试模式下打开
[self.livePusher showVideoDebugLog:YES];
//设置推流是否覆盖时钟
[self.livePusher setEnableClockOverlay:NO];
[self.livePusher setConfig:config];
}
3.2 开启推流
从接口获取推流地址rtmpUrl,
需要开启预览展示到自己视图上
设置美颜 等控件
启动推流:如果已经通过startPreview接口启动了摄像头预览,就可以调用 TXLivePush 中的startPush接口开始推流。如果 startPush 接口返回 -5,则代表您的 License 校验失败了,请检查第2步“给 SDK 配置 License 授权”中的工作是否有问题
#pragma mark - 推流逻辑
- (BOOL)startPush {
//推流地址
NSString *rtmpUrl = self.pushModel.push;
if (!([rtmpUrl hasPrefix:@"rtmp://"])) {
Alert(@"推流地址不合法,目前只支持rtmp推流!");
return NO;
}
// 检查摄像头权限
AVAuthorizationStatus statusVideo = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (statusVideo == AVAuthorizationStatusDenied) {
Alert(@"获取摄像头权限失败,请前往隐私-相机设置里面打开应用权限");
return NO;
}
// 检查麦克风权限
AVAuthorizationStatus statusAudio = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
if (statusAudio == AVAuthorizationStatusDenied) {
Alert(@"获取麦克风权限失败,请前往隐私-麦克风设置里面打开应用权限");
return NO;
}
// 设置delegate
[self.livePusher setDelegate:self];
/*美颜 --设置初始值*/
[self setDefaultBeauty];
// 开启预览
[self.livePusher startPreview:self.pushV];
#ifdef ENABLE_CUSTOM_MODE_AUDIO_CAPTURE
[CustomAudioFileReader sharedInstance].delegate = self;
[[CustomAudioFileReader sharedInstance] start:CUSTOM_AUDIO_CAPTURE_SAMPLERATE
channels:CUSTOM_AUDIO_CAPTURE_CHANNEL
framLenInSample:1024];
#endif
// 开始推流
int ret = [self.livePusher startPush:rtmpUrl];
if (ret != 0) {
NSString *alerText = [NSString stringWithFormat:@"推流器启动失败: %d", ret];
Alert(alerText);
NSLog(@"推流器启动失败");
return NO;
}
return YES;
}
#pragma mark - 美颜滤镜相关接口函数
/* @name 美颜滤镜相关接口函数
getBeautyManager
* 获取美颜管理对象
*
* 通过美颜管理,您可以使用以下功能:
* - 设置"美颜风格"、“美白”、“红润”、“大眼”、“瘦脸”、“V脸”、“下巴”、“短脸”、“小鼻”、“亮眼”、“白牙”、“祛眼袋”、“祛皱纹”、“祛法令纹”等美容效果。
* - 调整“发际线”、“眼间距”、“眼角”、“嘴形”、“鼻翼”、“鼻子位置”、“嘴唇厚度”、“脸型”
* - 设置人脸挂件(素材)等动态效果
* - 添加美妆
* - 进行手势识别
*/
- (void)setDefaultBeauty{
//设置美颜(磨皮)算法
[[self.livePusher getBeautyManager] setBeautyStyle:TXBeautyStyleSmooth];
/**
* 设置美颜级别
* @param level 美颜级别,取值范围0 - 9; 0表示关闭,1 - 9值越大,效果越明显。
*/
[[self.livePusher getBeautyManager] setBeautyLevel:6];
/**
* 设置美白级别
*
* @param level 美白级别,取值范围0 - 9;0表示关闭,1 - 9值越大,效果越明显。
*/
[[self.livePusher getBeautyManager] setWhitenessLevel:1];
/**
* 设置红润级别
*
* @param level 红润级别,取值范围0 - 9;0表示关闭,1 - 9值越大,效果越明显。
*/
[[self.livePusher getBeautyManager] setRuddyLevel:1];
/**
#if TARGET_OS_IPHONE
* 设置大眼级别(企业版有效,其它版本设置此参数无效)
*
* @param level 大眼级别,取值范围0 - 9;0表示关闭,1 - 9值越大,效果越明显。
*/
[[self.livePusher getBeautyManager] setEyeScaleLevel:0];
/**
* 设置瘦脸级别(企业版有效,其它版本设置此参数无效)
*
* @param level 瘦脸级别,取值范围0 - 9;0表示关闭,1 - 9值越大,效果越明显。
*/
[[self.livePusher getBeautyManager] setFaceSlimLevel:0];
}
3.3 直播推流-代理
#pragma mark ------(直播delegate) TXLivePushListener
- (void)onPushEvent:(int)evtID withParam:(NSDictionary *)param {
dispatch_async(dispatch_get_main_queue(), ^{
if (evtID == PUSH_ERR_NET_DISCONNECT || evtID == PUSH_ERR_INVALID_ADDRESS) {
// 断开连接时,模拟点击一次关闭推流
[self stopPush];
} else if (evtID == PUSH_ERR_OPEN_CAMERA_FAIL) {
[self stopPush];
Alert(@"获取摄像头权限失败,请前往隐私-相机设置里面打开应用权限");
} else if (evtID == PUSH_EVT_OPEN_CAMERA_SUCC) {
[self.livePusher toggleTorch:NO];
} else if (evtID == PUSH_ERR_OPEN_MIC_FAIL) {
[self stopPush];
Alert(@"获取麦克风权限失败,请前往隐私-麦克风设置里面打开应用权限");
} else if (evtID == PUSH_EVT_CONNECT_SUCC) {
//打开后置摄像头旁边的闪关灯
[self.livePusher toggleTorch:NO];
//设置视频镜像效果
[self.livePusher setMirror:NO];
[self.livePusher setMute:NO];
[self.livePusher showVideoDebugLog:NO];
[self.livePusher setMirror:NO];
[self.livePusher setReverbType:NO];
[self.livePusher setVoiceChangerType:NO];
BOOL isWifi = [AFNetworkReachabilityManager sharedManager].reachableViaWiFi;
if (!isWifi) {
__weak __typeof(self) weakSelf = self;
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (weakSelf.pushModel.push.length == 0) {
return;
}
if (status == AFNetworkReachabilityStatusReachableViaWiFi) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@""
message:@"您要切换到WiFi再推流吗?"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"是" style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) {
[alert dismissViewControllerAnimated:YES completion:nil];
// 先暂停,再重新推流
[weakSelf.livePusher stopPush];
[weakSelf.livePusher startPush:weakSelf.pushModel.push];
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"否" style:UIAlertActionStyleCancel handler:^(UIAlertAction *_Nonnull action) {
[alert dismissViewControllerAnimated:YES completion:nil];
}]];
[weakSelf presentViewController:alert animated:YES completion:nil];
}
}];
}
} else if (evtID == PUSH_WARNING_NET_BUSY) {
Alert(@"您当前的网络环境不佳,请尽快更换网络保证正常直播");
}
});
}
- (void)onNetStatus:(NSDictionary *)param {
// 这里可以上报相关推流信息到业务服务器
// 比如:码率,分辨率,帧率,cpu使用,缓存等信息
// 字段请在TXLiveSDKTypeDef.h中定义
/*
dispatch_async(dispatch_get_main_queue(), ^{
int netspeed = [(NSNumber *) [param valueForKey:NET_STATUS_NET_SPEED] intValue];
int videoEncBitrate = [(NSNumber *) [param valueForKey:NET_STATUS_VIDEO_BITRATE] intValue];
int fps = [(NSNumber *) [param valueForKey:NET_STATUS_VIDEO_FPS] intValue];
int gop = [(NSNumber *) [param valueForKey:NET_STATUS_VIDEO_GOP] intValue];
NSString *encBitrateText = [NSString stringWithFormat:@"%dkbps", videoEncBitrate];
NSString *upBitrateText = [NSString stringWithFormat:@"%dkbps", netspeed];
NSString *fpsText = [NSString stringWithFormat:@"%d", fps];
NSString *gopText = [NSString stringWithFormat:@"%ds", gop];
NSLog(@"%@\n%@\n%@\n%@\n",encBitrateText,upBitrateText,fpsText,gopText);
});
*/
}
3.4推流结束后,可以调用 TXLivePush 中的stopPush接口结束推流。请注意,如果已经启动了摄像头预览,请在结束推流时将其关闭,否则会导致 SDK 的表现异常。
- (void)stopPush {
if (_livePusher) {
[_livePusher setDelegate:nil];
[_livePusher stopPreview];
[_livePusher stopBGM];
[_livePusher stopRecord];
[_livePusher stopPush];
}
#ifdef ENABLE_CUSTOM_MODE_AUDIO_CAPTURE
[[CustomAudioFileReader sharedInstance] stop];
[CustomAudioFileReader sharedInstance].delegate = nil;
#endif
}
4.1初始化TXLivePlayer
@property (nonatomic, strong) TXLivePlayer *livePlayer;
- (TXLivePlayer *)livePlayer {
if (!_livePlayer) {
_livePlayer = [[TXLivePlayer alloc] init];
_livePlayer.enableHWAcceleration = YES;
// _livePlayer.config.connectRetryCount = 5;
// _livePlayer.config.connectRetryInterval = 5;
//设置播放器代理
_livePlayer.delegate = self;
}
return _livePlayer;
}
4.2从服务器拉取直播拉流地址,并检查是否符合要求,
添加到视图上开始播放
#pragma mark == setUpPlayer (初始化播放器)
-(BOOL)checkPlayUrl:(NSString*)playUrl {
if ([playUrl hasPrefix:@"rtmp:"]) {
_playType = PLAY_TYPE_LIVE_RTMP;
} else if (([playUrl hasPrefix:@"https:"] || [playUrl hasPrefix:@"http:"]) && ([playUrl rangeOfString:@".flv"].length > 0)) {
_playType = PLAY_TYPE_LIVE_FLV;
} else if (([playUrl hasPrefix:@"https:"] || [playUrl hasPrefix:@"http:"]) && [playUrl rangeOfString:@".m3u8"].length > 0) {
_playType = PLAY_TYPE_VOD_HLS;
} else{
Alert(@"播放地址不合法,直播目前仅支持rtmp,flv播放方式!");
return NO;
}
return YES;
}
- (BOOL)startPlay {
// CGRect frame = CGRectMake(self.view.bounds.size.width, 0, self.view.bounds.size.width, self.view.bounds.size.height);
// _videoView.frame = frame;
// [_loadingImageView removeFromSuperview];
NSString *playUrl = self.roomModel.play_url;
if (![self checkPlayUrl:playUrl]) {
return NO;
}
[self.livePlayer setDelegate:self];
// [_livePlayer setupVideoWidget:CGRectZero containView:self.playerView insertIndex:0];
[self.livePlayer setupVideoWidget:CGRectZero containView:self.roomV.bgView insertIndex:0];
int ret = [_livePlayer startPlay:playUrl type:_playType];
// frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
// [UIView animateWithDuration:0.4 animations:^{
// _videoView.frame = frame;
// } completion:^(BOOL finished) {
// [self.view addSubview:_loadingImageView];
// }];
if (ret != 0) {
Alert(@"播放器启动失败");
return NO;
}
// 播放参数初始化
//显示调试日志--调试模式下打开
[self.livePlayer showVideoDebugLog:NO];//YES
[self setCacheStrategy:CACHE_STRATEGY_AUTO]; // 默认自动
//竖屏直播模式
[self.livePlayer setRenderRotation:HOME_ORIENTATION_DOWN];
//图像铺满屏幕
[self.livePlayer setRenderMode:RENDER_MODE_FILL_SCREEN];
return YES;
}
备注:在iOS工程中添加一些 C 文件或 C++文件(例如美颜),有时会报错:
unknown type name 'NSString' NSObjcRuntime.h
parse issue expected unqualified-id
解决方案:
在 prefix.pch 文件中 添加一下代码:
#ifdef __OBJC__
/*你自己的Demo */
#endif