|
|
//
|
|
|
// PublishController.m
|
|
|
// RTMPiOSDemo
|
|
|
//
|
|
|
// Created by 蓝鲸 on 16/4/1.
|
|
|
// Copyright © 2016年 tencent. All rights reserved.
|
|
|
//
|
|
|
|
|
|
#import "PublishController.h"
|
|
|
#import "PlayController.h"
|
|
|
#import "ScanQRController.h"
|
|
|
//#import <Foundation/Foundation.h>
|
|
|
//#import "TXLiveSDKTypeDef.h"
|
|
|
#import <sys/types.h>
|
|
|
#import <sys/sysctl.h>
|
|
|
//#import <UIKit/UIKit.h>
|
|
|
//#import <mach/mach.h>
|
|
|
#import "TXRTMPAPI.h"
|
|
|
|
|
|
// 清晰度定义
|
|
|
#define HD_LEVEL_720P 1 // 1280 * 720
|
|
|
#define HD_LEVEL_640 2 // 640 * 360
|
|
|
#define HD_LEVEL_640_PLUS 3 // 640 * 360 且开启码率自适应
|
|
|
|
|
|
|
|
|
#define RTMP_PUBLISH_URL @"请输入或扫二维码获取推流地址" //调试期间您可以修改之以避免输入地址的麻烦
|
|
|
//#define RTMP_PUBLISH_URL @"rtmp://2157.livepush.myqcloud.com/live/2157_6cfbca22ecad11e5b91fa4dcbef5e35a?bizid=2157"
|
|
|
|
|
|
@interface PublishController ()<UITextFieldDelegate, TXLivePushListener>
|
|
|
|
|
|
@end
|
|
|
|
|
|
@implementation PublishController
|
|
|
{
|
|
|
}
|
|
|
|
|
|
- (void)viewDidLoad {
|
|
|
[super viewDidLoad];
|
|
|
|
|
|
[self initUI];
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDeactive:) name:UIApplicationWillResignActiveNotification object:nil];
|
|
|
|
|
|
_txLivePublisher = [[TXLivePush alloc] init];
|
|
|
[_txLivePublisher setLogLevel:LOGLEVEL_DEBUG];
|
|
|
TXLivePushConfig* _config = [[TXLivePushConfig alloc] init];
|
|
|
[_txLivePublisher setConfig:_config];
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)didReceiveMemoryWarning {
|
|
|
[super didReceiveMemoryWarning];
|
|
|
// Dispose of any resources that can be recreated.
|
|
|
}
|
|
|
|
|
|
#pragma -
|
|
|
-(void) onAppActive:(UIApplication*)app
|
|
|
{
|
|
|
if (_publish_switch == YES) {
|
|
|
[self startRtmp];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-(void) onAppDeactive:(UIApplication*)app
|
|
|
{
|
|
|
if (_publish_switch == YES) {
|
|
|
[self stopRtmp];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#pragma -
|
|
|
- (void)clearLog {
|
|
|
_tipsMsg = @"";
|
|
|
_logMsg = @"";
|
|
|
[_statusView setText:@""];
|
|
|
[_logViewEvt setText:@""];
|
|
|
_startTime = [[NSDate date]timeIntervalSince1970]*1000;
|
|
|
_lastTime = _startTime;
|
|
|
}
|
|
|
|
|
|
|
|
|
-(BOOL)startRtmp{
|
|
|
[self clearLog];
|
|
|
NSString* rtmpUrl = self.txtRtmpUrl.text;
|
|
|
if (rtmpUrl.length == 0) {
|
|
|
rtmpUrl = RTMP_PUBLISH_URL;
|
|
|
}
|
|
|
|
|
|
if (!([rtmpUrl hasPrefix:@"http:"] || [rtmpUrl hasPrefix:@"rtmp:"] )) {
|
|
|
[self toastTip:@"请输入正确的推流地址"];
|
|
|
return NO;
|
|
|
}
|
|
|
|
|
|
//是否有摄像头权限
|
|
|
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
|
|
|
if (status == AVAuthorizationStatusDenied) {
|
|
|
[self toastTip:@"获取摄像头权限失败,请前往隐私-相机设置里面打开应用权限"];
|
|
|
return NO;
|
|
|
}
|
|
|
|
|
|
NSArray* ver = [TXRtmpApi getSDKVersion];
|
|
|
if ([ver count] >= 3) {
|
|
|
_logMsg = [NSString stringWithFormat:@"rtmp sdk version: %@.%@.%@",ver[0],ver[1],ver[2]];
|
|
|
[_logViewEvt setText:_logMsg];
|
|
|
}
|
|
|
|
|
|
if(_txLivePublisher != nil)
|
|
|
{
|
|
|
_txLivePublisher.delegate = self;
|
|
|
[_txLivePublisher startPreview:self.view];
|
|
|
[_txLivePublisher startPush:rtmpUrl];
|
|
|
}
|
|
|
return YES;
|
|
|
}
|
|
|
|
|
|
|
|
|
- (void)stopRtmp {
|
|
|
if(_txLivePublisher != nil)
|
|
|
{
|
|
|
_txLivePublisher.delegate = nil;
|
|
|
[_txLivePublisher stopPreview];
|
|
|
[_txLivePublisher stopPush];
|
|
|
|
|
|
_sdBeauty.value = 0;
|
|
|
_sdWhitening.value = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// RTMP 推流事件通知
|
|
|
#pragma - TXLivePushListener
|
|
|
-(void) appendLog:(NSString*) evt time:(NSDate*) date mills:(int)mil
|
|
|
{
|
|
|
NSDateFormatter* format = [[NSDateFormatter alloc] init];
|
|
|
format.dateFormat = @"hh:mm:ss";
|
|
|
NSString* time = [format stringFromDate:date];
|
|
|
NSString* log = [NSString stringWithFormat:@"[%@.%-3.3d] %@", time, mil, evt];
|
|
|
if (_logMsg == nil) {
|
|
|
_logMsg = @"";
|
|
|
}
|
|
|
_logMsg = [NSString stringWithFormat:@"%@\n%@", _logMsg, log];
|
|
|
[_logViewEvt setText:_logMsg];
|
|
|
}
|
|
|
|
|
|
-(void) onPushEvent:(int)EvtID WithParam:(NSDictionary*)param;
|
|
|
{
|
|
|
NSDictionary* dict = param;
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
if (EvtID == PUSH_ERR_NET_DISCONNECT) {
|
|
|
[self clickPublish:_btnPublish];
|
|
|
}
|
|
|
// NSLog(@"evt:%d,%@", EvtID, dict);
|
|
|
long long time = [(NSNumber*)[dict valueForKey:EVT_TIME] longLongValue];
|
|
|
int mil = time % 1000;
|
|
|
NSDate* date = [NSDate dateWithTimeIntervalSince1970:time/1000];
|
|
|
NSString* Msg = (NSString*)[dict valueForKey:EVT_MSG];
|
|
|
[self appendLog:Msg time:date mills:mil];
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-(void) onNetStatus:(NSDictionary*) param
|
|
|
{
|
|
|
NSDictionary* dict = param;
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
int netspeed = [(NSNumber*)[dict valueForKey:NET_STATUS_NET_SPEED] intValue];
|
|
|
int vbitrate = [(NSNumber*)[dict valueForKey:NET_STATUS_VIDEO_BITRATE] intValue];
|
|
|
int abitrate = [(NSNumber*)[dict valueForKey:NET_STATUS_AUDIO_BITRATE] intValue];
|
|
|
int cachesize = [(NSNumber*)[dict valueForKey:NET_STATUS_CACHE_SIZE] intValue];
|
|
|
int dropsize = [(NSNumber*)[dict valueForKey:NET_STATUS_DROP_SIZE] intValue];
|
|
|
int jitter = [(NSNumber*)[dict valueForKey:NET_STATUS_NET_JITTER] intValue];
|
|
|
int fps = [(NSNumber*)[dict valueForKey:NET_STATUS_VIDEO_FPS] intValue];
|
|
|
int width = [(NSNumber*)[dict valueForKey:NET_STATUS_VIDEO_WIDTH] intValue];
|
|
|
int height = [(NSNumber*)[dict valueForKey:NET_STATUS_VIDEO_HEIGHT] intValue];
|
|
|
float cpu_usage = [(NSNumber*)[dict valueForKey:NET_STATUS_CPU_USAGE] floatValue];
|
|
|
|
|
|
|
|
|
NSString* log = [NSString stringWithFormat:@"CPU:%.1f%%\tRES:%d*%d\tSPD:%dkb/s\nJITT:%d\tFPS:%d\tARA:%dkb/s\nQUE:%d\tDRP:%d\tVRA:%dkb/s",
|
|
|
cpu_usage*100,
|
|
|
width,
|
|
|
height,
|
|
|
netspeed,
|
|
|
jitter,
|
|
|
fps,
|
|
|
abitrate,
|
|
|
cachesize,
|
|
|
dropsize,
|
|
|
vbitrate];
|
|
|
[_statusView setText:log];
|
|
|
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
#pragma - ui util
|
|
|
-(void) initUI
|
|
|
{
|
|
|
//主界面排版
|
|
|
|
|
|
[self.view setBackgroundColor:[UIColor orangeColor]];
|
|
|
|
|
|
CGSize size = [[UIScreen mainScreen] bounds].size;
|
|
|
|
|
|
int icon_size = size.width / 10;
|
|
|
|
|
|
_cover = [[UIView alloc]init];
|
|
|
_cover.frame = CGRectMake(10.0f, 55 + 2*icon_size, size.width - 20, size.height - 75 - 3 * icon_size);
|
|
|
_cover.backgroundColor = [UIColor whiteColor];
|
|
|
_cover.alpha = 0.5;
|
|
|
_cover.hidden = YES;
|
|
|
[self.view addSubview:_cover];
|
|
|
|
|
|
int logheadH = 50;
|
|
|
_statusView = [[UITextView alloc] initWithFrame:CGRectMake(10.0f, 55 + 2*icon_size, size.width - 20, logheadH)];
|
|
|
_statusView.backgroundColor = [UIColor clearColor];
|
|
|
_statusView.alpha = 1;
|
|
|
_statusView.textColor = [UIColor blackColor];
|
|
|
_statusView.editable = NO;
|
|
|
_statusView.hidden = YES;
|
|
|
[self.view addSubview:_statusView];
|
|
|
|
|
|
_logViewEvt = [[UITextView alloc] initWithFrame:CGRectMake(10.0f, 55 + 2*icon_size + logheadH, size.width - 20, size.height - 75 - 3 * icon_size - logheadH)];
|
|
|
_logViewEvt.backgroundColor = [UIColor clearColor];
|
|
|
_logViewEvt.alpha = 1;
|
|
|
_logViewEvt.textColor = [UIColor blackColor];
|
|
|
_logViewEvt.editable = NO;
|
|
|
_logViewEvt.hidden = YES;
|
|
|
[self.view addSubview:_logViewEvt];
|
|
|
|
|
|
|
|
|
NSArray *scArray = [[NSArray alloc] initWithObjects:@"推流",@"播放", nil];
|
|
|
UISegmentedControl* scSwitch = [[UISegmentedControl alloc] initWithItems:scArray];
|
|
|
scSwitch.frame = CGRectMake(75, 40, size.width-150, icon_size);
|
|
|
scSwitch.selectedSegmentIndex = 0;
|
|
|
scSwitch.tintColor = [UIColor blackColor];
|
|
|
|
|
|
[scSwitch addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
|
|
|
[self.view addSubview:scSwitch];
|
|
|
[self.txtRtmpUrl setBorderStyle:UITextBorderStyleRoundedRect];
|
|
|
|
|
|
self.txtRtmpUrl = [[UITextField alloc] initWithFrame:CGRectMake(10, 40 + icon_size + 10, size.width- 25 - icon_size, icon_size)];
|
|
|
self.txtRtmpUrl.placeholder = RTMP_PUBLISH_URL;
|
|
|
self.txtRtmpUrl.background = [UIImage imageNamed:@"Input_box"];
|
|
|
self.txtRtmpUrl.alpha = 0.5;
|
|
|
self.txtRtmpUrl.autocapitalizationType = UITextAutocorrectionTypeNo;
|
|
|
self.txtRtmpUrl.delegate = self;
|
|
|
|
|
|
[self.view addSubview:self.txtRtmpUrl];
|
|
|
|
|
|
UIButton* btnScan = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
btnScan.frame = CGRectMake(size.width - 10 - icon_size , 40 + icon_size + 10, icon_size, icon_size);
|
|
|
[btnScan setImage:[UIImage imageNamed:@"QR_code"] forState:UIControlStateNormal];
|
|
|
[btnScan addTarget:self action:@selector(clickScan:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
[self.view addSubview:btnScan];
|
|
|
|
|
|
int icon_gap = 3*icon_size/8;
|
|
|
|
|
|
//start or stop 按钮
|
|
|
_publish_switch = NO;
|
|
|
_btnPublish = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_btnPublish.frame = CGRectMake(icon_gap, size.height - icon_size - 10, icon_size, icon_size);
|
|
|
[_btnPublish setImage:[UIImage imageNamed:@"start"] forState:UIControlStateNormal];
|
|
|
[_btnPublish addTarget:self action:@selector(clickPublish:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
[self.view addSubview:_btnPublish];
|
|
|
|
|
|
|
|
|
//前置后置摄像头切换
|
|
|
_camera_switch = NO;
|
|
|
_btnCamera = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_btnCamera.frame = CGRectMake(2*icon_gap + icon_size, size.height - 10 - icon_size, icon_size, icon_size);
|
|
|
[_btnCamera setImage:[UIImage imageNamed:@"camera"] forState:UIControlStateNormal];
|
|
|
[_btnCamera addTarget:self action:@selector(clickCamera:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
[self.view addSubview:_btnCamera];
|
|
|
|
|
|
//美颜开关按钮
|
|
|
_beauty_level = 0;
|
|
|
_btnBeauty = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_btnBeauty.frame = CGRectMake(3*icon_gap + 2*icon_size, size.height - 10 - icon_size, icon_size, icon_size);
|
|
|
[_btnBeauty setImage:[UIImage imageNamed:@"beauty"] forState:UIControlStateNormal];
|
|
|
[_btnBeauty addTarget:self action:@selector(clickBeauty:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
[self.view addSubview:_btnBeauty];
|
|
|
|
|
|
//硬件加速
|
|
|
_hardware_switch = NO;
|
|
|
_btnHardware = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_btnHardware.frame = CGRectMake(size.width - 3*icon_gap - 3*icon_size, size.height - 10 - icon_size, icon_size, icon_size);
|
|
|
[_btnHardware setImage:[UIImage imageNamed:@"quick"] forState:UIControlStateNormal];
|
|
|
[_btnHardware addTarget:self action:@selector(clickHardware:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
[self.view addSubview:_btnHardware];
|
|
|
|
|
|
//log显示或隐藏
|
|
|
_log_switch = NO;
|
|
|
_btnLog = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_btnLog.frame = CGRectMake(size.width - 2*icon_gap - 2*icon_size, size.height - 10 - icon_size, icon_size, icon_size);
|
|
|
[_btnLog setImage:[UIImage imageNamed:@"log"] forState:UIControlStateNormal];
|
|
|
[_btnLog addTarget:self action:@selector(clickLog:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
[self.view addSubview:_btnLog];
|
|
|
|
|
|
//清晰度按钮
|
|
|
_btnResolution = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_btnResolution.frame = CGRectMake(size.width - icon_gap - icon_size, size.height - 10 - icon_size, icon_size, icon_size);
|
|
|
[_btnResolution setImage:[UIImage imageNamed:@"SD"] forState:UIControlStateNormal];
|
|
|
[_btnResolution addTarget:self action:@selector(clickHD:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
[self.view addSubview:_btnResolution];
|
|
|
|
|
|
//开启闪关灯按钮
|
|
|
_torch_switch= NO;
|
|
|
_btnTorch = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_btnTorch.frame = CGRectMake(size.width - 4*icon_gap - 4*icon_size, size.height - 10 - icon_size, icon_size, icon_size);
|
|
|
[_btnTorch setImage:[UIImage imageNamed:@"flash_off"] forState:UIControlStateNormal];
|
|
|
[_btnTorch setImage:[UIImage imageNamed:@"flash_on"] forState:UIControlStateSelected];
|
|
|
[_btnTorch addTarget:self action:@selector(clickTorch:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
[self.view addSubview:_btnTorch];
|
|
|
|
|
|
//美颜拉杆浮层
|
|
|
_vBeauty = [[UIControl alloc] init];
|
|
|
_vBeauty.frame = CGRectMake(0, size.height-120, size.width, 120);
|
|
|
[_vBeauty setBackgroundColor:[UIColor whiteColor]];
|
|
|
|
|
|
UILabel* txtBeauty = [[UILabel alloc]init];
|
|
|
txtBeauty.frame = CGRectMake(20, 25, 150, 150);
|
|
|
[txtBeauty setText:@"美颜效果"];
|
|
|
[txtBeauty setFont:[UIFont fontWithName:@"" size:14]];
|
|
|
[txtBeauty sizeToFit];
|
|
|
|
|
|
_sdBeauty = [[UISlider alloc] init];
|
|
|
_sdBeauty.frame = CGRectMake(txtBeauty.frame.origin.x + txtBeauty.frame.size.width + 10, 0, size.width - txtBeauty.frame.origin.x - txtBeauty.frame.size.width - 40, 60);
|
|
|
_sdBeauty.minimumValue = 0;
|
|
|
_sdBeauty.maximumValue = 9;
|
|
|
_sdBeauty.value = 0;
|
|
|
_sdBeauty.center = CGPointMake(_sdBeauty.center.x, txtBeauty.center.y);
|
|
|
|
|
|
[_sdBeauty setThumbImage:[UIImage imageNamed:@"circle"] forState:UIControlStateNormal];
|
|
|
[_sdBeauty setMinimumTrackTintColor:[UIColor blackColor]];
|
|
|
[_sdBeauty setMaximumTrackTintColor:[UIColor blackColor]];
|
|
|
[_sdBeauty addTarget:self action:@selector(sliderValueChange:) forControlEvents:UIControlEventValueChanged];
|
|
|
_sdBeauty.tag = 0;
|
|
|
|
|
|
UILabel* txtWhitening = [[UILabel alloc] init];
|
|
|
txtWhitening.frame = CGRectMake(20, txtBeauty.frame.origin.y + txtBeauty.frame.size.height + 25, 150, 150);
|
|
|
[txtWhitening setText:@"美白效果"];
|
|
|
[txtWhitening setFont:[UIFont fontWithName:@"" size:14]];
|
|
|
[txtWhitening sizeToFit];
|
|
|
|
|
|
_sdWhitening = [[UISlider alloc] init];
|
|
|
_sdWhitening.frame = CGRectMake(txtWhitening.frame.origin.x + txtWhitening.frame.size.width + 10, 0, size.width - txtWhitening.frame.origin.x - txtWhitening.frame.size.width - 40, 60);
|
|
|
_sdWhitening.minimumValue = 0;
|
|
|
_sdWhitening.maximumValue = 9;
|
|
|
_sdWhitening.center = CGPointMake(_sdWhitening.center.x, txtWhitening.center.y);
|
|
|
|
|
|
[_sdWhitening setThumbImage:[UIImage imageNamed:@"circle"] forState:UIControlStateNormal];
|
|
|
[_sdWhitening setMinimumTrackTintColor:[UIColor blackColor]];
|
|
|
[_sdWhitening setMaximumTrackTintColor:[UIColor blackColor]];
|
|
|
[_sdWhitening addTarget:self action:@selector(sliderValueChange:) forControlEvents:UIControlEventValueChanged];
|
|
|
_sdWhitening.tag = 1;
|
|
|
|
|
|
|
|
|
[_vBeauty addSubview:txtBeauty];
|
|
|
[_vBeauty addSubview:_sdBeauty];
|
|
|
[_vBeauty addSubview:txtWhitening];
|
|
|
[_vBeauty addSubview:_sdWhitening];
|
|
|
|
|
|
_vBeauty.hidden = YES;
|
|
|
[self.view addSubview: _vBeauty];
|
|
|
|
|
|
// 清晰度选项: 720p - 640 - 640+ (此处使用了三个普通按钮来模拟单选框, 目的是跟android demo 保持界面风格一致)
|
|
|
_vHD = [[UIControl alloc]init];
|
|
|
_vHD.frame = CGRectMake(0, size.height-120, size.width, 120);
|
|
|
[_vHD setBackgroundColor:[UIColor whiteColor]];
|
|
|
|
|
|
UILabel* txtHD= [[UILabel alloc]init];
|
|
|
txtHD.frame = CGRectMake(0, 0, size.width, 50);
|
|
|
[txtHD setText:@"清晰度"];
|
|
|
[txtHD setFont:[UIFont fontWithName:@"" size:14]];
|
|
|
|
|
|
[_vHD addSubview:txtHD];
|
|
|
|
|
|
int gap = 30;
|
|
|
int width = (size.width - gap*2 - 20) / 3;
|
|
|
_radioBtnHD = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_radioBtnHD.frame = CGRectMake(10, 60, width, 40);
|
|
|
[_radioBtnHD setTitle:@"720p" forState:UIControlStateNormal];
|
|
|
[_radioBtnHD addTarget:self action:@selector(changeHD:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
|
|
|
_radioBtnSD = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_radioBtnSD.frame = CGRectMake(10 + gap + width, 60, width, 40);
|
|
|
[_radioBtnSD setTitle:@"640" forState:UIControlStateNormal];
|
|
|
[_radioBtnSD addTarget:self action:@selector(changeHD:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
|
|
|
_radioBtnAUTO = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
_radioBtnAUTO.frame = CGRectMake(size.width - 10 - width, 60, width, 40);
|
|
|
[_radioBtnAUTO setTitle:@"640+" forState:UIControlStateNormal];
|
|
|
[_radioBtnAUTO addTarget:self action:@selector(changeHD:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
|
|
|
[_vHD addSubview:_radioBtnHD];
|
|
|
[_vHD addSubview:_radioBtnSD];
|
|
|
[_vHD addSubview:_radioBtnAUTO];
|
|
|
|
|
|
_vHD.hidden = YES;
|
|
|
[self.view addSubview: _vHD];
|
|
|
|
|
|
// DEMO 默认采用 640 * 360 分辨率, 避免在4S等机型上出现编码不足
|
|
|
_hd_level = [self isSuitableMachine] ? HD_LEVEL_720P : HD_LEVEL_640;
|
|
|
[self setHDUI:_hd_level];
|
|
|
|
|
|
#if TARGET_IPHONE_SIMULATOR
|
|
|
[self toastTip:@"iOS模拟器不支持推流和播放,请使用真机体验"];
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
-(void) setHDUI:(int) level
|
|
|
{
|
|
|
switch (level) {
|
|
|
case HD_LEVEL_720P:
|
|
|
[_radioBtnHD setBackgroundImage:[UIImage imageNamed:@"black"] forState:UIControlStateNormal];
|
|
|
[_radioBtnSD setBackgroundImage:[UIImage imageNamed:@"white"] forState:UIControlStateNormal];
|
|
|
[_radioBtnAUTO setBackgroundImage:[UIImage imageNamed:@"white"] forState:UIControlStateNormal];
|
|
|
[_radioBtnHD setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
|
|
[_radioBtnSD setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
|
|
[_radioBtnAUTO setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
|
|
[_btnResolution setImage:[UIImage imageNamed:@"HD"] forState:UIControlStateNormal];
|
|
|
break;
|
|
|
case HD_LEVEL_640:
|
|
|
[_radioBtnHD setBackgroundImage:[UIImage imageNamed:@"white"] forState:UIControlStateNormal];
|
|
|
[_radioBtnSD setBackgroundImage:[UIImage imageNamed:@"black"] forState:UIControlStateNormal];
|
|
|
[_radioBtnAUTO setBackgroundImage:[UIImage imageNamed:@"white"] forState:UIControlStateNormal];
|
|
|
[_radioBtnHD setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
|
|
[_radioBtnSD setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
|
|
[_radioBtnAUTO setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
|
|
[_btnResolution setImage:[UIImage imageNamed:@"SD"] forState:UIControlStateNormal];
|
|
|
|
|
|
break;
|
|
|
case HD_LEVEL_640_PLUS:
|
|
|
[_radioBtnHD setBackgroundImage:[UIImage imageNamed:@"white"] forState:UIControlStateNormal];
|
|
|
[_radioBtnSD setBackgroundImage:[UIImage imageNamed:@"white"] forState:UIControlStateNormal];
|
|
|
[_radioBtnAUTO setBackgroundImage:[UIImage imageNamed:@"black"] forState:UIControlStateNormal];
|
|
|
[_radioBtnHD setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
|
|
[_radioBtnSD setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
|
|
[_radioBtnAUTO setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
|
|
[_btnResolution setImage:[UIImage imageNamed:@"PU"] forState:UIControlStateNormal];
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#pragma - 事件处理
|
|
|
- (void) segmentAction:(UISegmentedControl*) sc
|
|
|
{
|
|
|
NSInteger idx = sc.selectedSegmentIndex;
|
|
|
if (idx == 1) {
|
|
|
PlayController *vc = [[PlayController alloc] init];
|
|
|
[self.navigationController pushViewController:vc animated:YES];
|
|
|
|
|
|
sc.selectedSegmentIndex = 0;
|
|
|
[_btnPublish setImage:[UIImage imageNamed:@"start"] forState:UIControlStateNormal];
|
|
|
_publish_switch = NO;
|
|
|
[self stopRtmp];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-(void) clickScan:(UIButton*) btn
|
|
|
{
|
|
|
[_btnPublish setImage:[UIImage imageNamed:@"start"] forState:UIControlStateNormal];
|
|
|
_publish_switch = NO;
|
|
|
[self stopRtmp];
|
|
|
ScanQRController* vc = [[ScanQRController alloc] init];
|
|
|
vc.pvc = self;
|
|
|
[self.navigationController pushViewController:vc animated:NO];
|
|
|
}
|
|
|
|
|
|
-(void) clickPublish:(UIButton*) btn
|
|
|
{
|
|
|
if (_publish_switch == YES) {
|
|
|
[self stopRtmp];
|
|
|
[_btnPublish setImage:[UIImage imageNamed:@"start"] forState:UIControlStateNormal];
|
|
|
_publish_switch = NO;
|
|
|
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if(![self startRtmp])
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
[_btnPublish setImage:[UIImage imageNamed:@"suspend"] forState:UIControlStateNormal];
|
|
|
_publish_switch = YES;
|
|
|
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
-(void) clickCamera:(UIButton*) btn
|
|
|
{
|
|
|
_camera_switch = !_camera_switch;
|
|
|
|
|
|
[btn setImage:[UIImage imageNamed:(_camera_switch? @"camera2" : @"camera")] forState:UIControlStateNormal];
|
|
|
[_txLivePublisher switchCamera];
|
|
|
}
|
|
|
|
|
|
-(void) clickBeauty:(UIButton*) btn
|
|
|
{
|
|
|
_sdBeauty.value = _beauty_level;
|
|
|
_vBeauty.hidden = NO;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@method 获取指定宽度width的字符串在UITextView上的高度
|
|
|
@param textView 待计算的UITextView
|
|
|
@param Width 限制字符串显示区域的宽度
|
|
|
@result float 返回的高度
|
|
|
*/
|
|
|
- (float) heightForString:(UITextView *)textView andWidth:(float)width{
|
|
|
CGSize sizeToFit = [textView sizeThatFits:CGSizeMake(width, MAXFLOAT)];
|
|
|
return sizeToFit.height;
|
|
|
}
|
|
|
- (void) toastTip:(NSString*)toastInfo
|
|
|
{
|
|
|
CGRect frameRC = [[UIScreen mainScreen] bounds];
|
|
|
frameRC.origin.y = frameRC.size.height - 110;
|
|
|
frameRC.size.height -= 110;
|
|
|
__block UITextView * toastView = [[UITextView alloc] init];
|
|
|
|
|
|
toastView.editable = NO;
|
|
|
toastView.selectable = NO;
|
|
|
|
|
|
frameRC.size.height = [self heightForString:toastView andWidth:frameRC.size.width];
|
|
|
|
|
|
toastView.frame = frameRC;
|
|
|
|
|
|
toastView.text = toastInfo;
|
|
|
toastView.backgroundColor = [UIColor whiteColor];
|
|
|
toastView.alpha = 0.5;
|
|
|
|
|
|
[self.view addSubview:toastView];
|
|
|
|
|
|
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
|
|
|
|
|
|
dispatch_after(popTime, dispatch_get_main_queue(), ^(){
|
|
|
[toastView removeFromSuperview];
|
|
|
toastView = nil;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-(void) clickHardware:(UIButton*) btn
|
|
|
{
|
|
|
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
|
|
|
[self toastTip:@"iOS 版本低于8.0,不支持硬件加速."];
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if(_txLivePublisher != nil) {
|
|
|
if (_publish_switch == YES) {
|
|
|
[self stopRtmp];
|
|
|
}
|
|
|
|
|
|
TXLivePushConfig * configTmp = _txLivePublisher.config;
|
|
|
if (configTmp.enableHWAcceleration == NO)
|
|
|
{
|
|
|
NSString* strTip = @"iOS SDK启用硬件加速.";
|
|
|
if (_publish_switch == YES)
|
|
|
{
|
|
|
strTip = @"iOS SDK启用硬件加速,切换后会重新开始推流";
|
|
|
}
|
|
|
|
|
|
[self toastTip:strTip];
|
|
|
configTmp.enableHWAcceleration = YES;
|
|
|
[btn setImage:[UIImage imageNamed:@"quick"] forState:UIControlStateNormal];
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
NSString* strTip = @"iOS SDK停止硬件加速.";
|
|
|
if (_publish_switch == YES)
|
|
|
{
|
|
|
strTip = @"iOS SDK停止硬件加速,切换后会重新开始推流";
|
|
|
}
|
|
|
|
|
|
[self toastTip:strTip];
|
|
|
configTmp.enableHWAcceleration = NO;
|
|
|
[btn setImage:[UIImage imageNamed:@"quick2"] forState:UIControlStateNormal];
|
|
|
}
|
|
|
_txLivePublisher.config = configTmp;
|
|
|
|
|
|
if (_publish_switch == YES) {
|
|
|
[self startRtmp];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-(void) clickLog:(UIButton*) btn
|
|
|
{
|
|
|
if (_log_switch == YES)
|
|
|
{
|
|
|
_statusView.hidden = YES;
|
|
|
_logViewEvt.hidden = YES;
|
|
|
[btn setImage:[UIImage imageNamed:@"log"] forState:UIControlStateNormal];
|
|
|
_cover.hidden = YES;
|
|
|
_log_switch = NO;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
_statusView.hidden = NO;
|
|
|
_logViewEvt.hidden = NO;
|
|
|
[btn setImage:[UIImage imageNamed:@"log2"] forState:UIControlStateNormal];
|
|
|
_cover.hidden = NO;
|
|
|
_log_switch = YES;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-(void) clickTorch:(UIButton*) btn
|
|
|
{
|
|
|
if (_txLivePublisher) {
|
|
|
btn.selected = !btn.selected;
|
|
|
_torch_switch = !_torch_switch;
|
|
|
// if (![_txLivePublisher toggleTorch:_torch_switch]) {
|
|
|
// _torch_switch = !_torch_switch;
|
|
|
// [self toastTip:@"闪光灯启动失败"];
|
|
|
// }
|
|
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
|
|
|
|
|
if (_torch_switch) {
|
|
|
if ([device hasTorch]) {
|
|
|
[device lockForConfiguration:nil];
|
|
|
[device setTorchMode: AVCaptureTorchModeOn];
|
|
|
[device unlockForConfiguration];
|
|
|
}else {
|
|
|
[self toastTip:@"闪光灯启动失败"];
|
|
|
}
|
|
|
}else {
|
|
|
if ([device hasTorch]) {
|
|
|
[device lockForConfiguration:nil];
|
|
|
[device setTorchMode: AVCaptureTorchModeOff];
|
|
|
[device unlockForConfiguration];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-(void) clickHD:(UIButton*) btn
|
|
|
{
|
|
|
_vHD.hidden = NO;
|
|
|
}
|
|
|
|
|
|
|
|
|
-(void) changeHD:(UIButton*) btn
|
|
|
{
|
|
|
if ([btn.titleLabel.text isEqualToString:@"720p"] && NO == [self isSuitableMachine]) {
|
|
|
UIAlertView * alert = [[UIAlertView alloc] initWithTitle: @"硬件加速"
|
|
|
message: @"iphone 6 及以上机型适合开启720p!"
|
|
|
delegate: nil
|
|
|
cancelButtonTitle: @"确认"
|
|
|
otherButtonTitles: nil];
|
|
|
[alert show];
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (_txLivePublisher == nil) return;
|
|
|
|
|
|
if (_publish_switch == YES) {
|
|
|
[self stopRtmp];
|
|
|
}
|
|
|
|
|
|
if ([btn.titleLabel.text isEqualToString:@"720p"]) {
|
|
|
_hd_level = HD_LEVEL_720P;
|
|
|
|
|
|
TXLivePushConfig* _config = _txLivePublisher.config;
|
|
|
_config.videoBitratePIN = 1500;
|
|
|
_config.videoResolution = [self isSuitableMachine ] ? VIDEO_RESOLUTION_1280_720 : VIDEO_RESOLUTION_960_540;
|
|
|
_config.enableAutoBitrate = NO;
|
|
|
[_txLivePublisher setConfig:_config];
|
|
|
|
|
|
} else if ([btn.titleLabel.text isEqualToString:@"640"]) {
|
|
|
_hd_level = HD_LEVEL_640;
|
|
|
|
|
|
TXLivePushConfig* _config = _txLivePublisher.config;
|
|
|
_config.videoBitratePIN = 800;
|
|
|
_config.videoResolution = VIDEO_RESOLUTION_640_360;
|
|
|
_config.enableAutoBitrate = NO;
|
|
|
[_txLivePublisher setConfig:_config];
|
|
|
|
|
|
} else if ([btn.titleLabel.text isEqualToString:@"640+"]) {
|
|
|
_hd_level = HD_LEVEL_640_PLUS;
|
|
|
|
|
|
TXLivePushConfig* _config = _txLivePublisher.config;
|
|
|
_config.videoBitrateMin = 500;
|
|
|
_config.videoBitrateMax = 1500;
|
|
|
_config.enableAutoBitrate = YES;
|
|
|
_config.videoResolution = VIDEO_RESOLUTION_640_360;
|
|
|
[_txLivePublisher setConfig:_config]; // 此模式下设置bitrate无效
|
|
|
}
|
|
|
|
|
|
[self setHDUI:_hd_level];
|
|
|
_vHD.hidden = YES;
|
|
|
|
|
|
if (_publish_switch == YES) {
|
|
|
[self startRtmp];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-(void) sliderValueChange:(UISlider*) obj
|
|
|
{
|
|
|
// todo
|
|
|
if (obj.tag == 0) { //美颜
|
|
|
_beauty_level = obj.value;
|
|
|
} else if (obj.tag == 1) { //美白
|
|
|
_whitening_level = obj.value;
|
|
|
}
|
|
|
|
|
|
[_txLivePublisher setBeautyFilterDepth:_beauty_level setWhiteningFilterDepth:_whitening_level];
|
|
|
}
|
|
|
|
|
|
|
|
|
// iphone 6 及以上机型适合开启720p, 否则20帧的帧率可能无法达到, 这种"流畅不足,清晰有余"的效果并不好
|
|
|
-(BOOL) isSuitableMachine
|
|
|
{
|
|
|
int mib[2] = {CTL_HW, HW_MACHINE};
|
|
|
size_t len = 0;
|
|
|
char* machine;
|
|
|
|
|
|
sysctl(mib, 2, NULL, &len, NULL, 0);
|
|
|
|
|
|
machine = (char*)malloc(len);
|
|
|
sysctl(mib, 2, machine, &len, NULL, 0);
|
|
|
|
|
|
NSString* platform = [NSString stringWithCString:machine encoding:NSASCIIStringEncoding];
|
|
|
free(machine);
|
|
|
if ([platform length] > 6) {
|
|
|
NSString * platNum = [NSString stringWithFormat:@"%C", [platform characterAtIndex: 6 ]];
|
|
|
return ([platNum intValue] >= 7);
|
|
|
} else {
|
|
|
return NO;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma -- UITextFieldDelegate
|
|
|
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
|
|
[textField resignFirstResponder];
|
|
|
return YES;
|
|
|
}
|
|
|
|
|
|
- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
|
|
{
|
|
|
[self.txtRtmpUrl resignFirstResponder];
|
|
|
_vHD.hidden = YES;
|
|
|
_vBeauty.hidden = YES;
|
|
|
}
|
|
|
@end |