SKInCallViewController.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. //
  2. // InCallViewController.m
  3. // JCSampleOC
  4. //
  5. // Created by Ginger on 2017/10/12.
  6. // Copyright © 2017年 juphoon. All rights reserved.
  7. //
  8. #import "SKInCallViewController.h"
  9. #import "VideoManager+ringtone.h"
  10. #define kDoodleVersionChecker 1
  11. #define AUDIO_RECORD_DIR @"audio_record"
  12. #define SNAPSHOT_DIR @"snapshot"
  13. #define VIDEO_RECORD_DIR @"video_record"
  14. @interface SKInCallViewController() {
  15. JCMediaDeviceVideoCanvas* _localCanvas;
  16. JCMediaDeviceVideoCanvas* _remoteCanvas;
  17. JCMediaDeviceVideoCanvas* _mSmallCanvas;
  18. NSTimer* _timer;
  19. BOOL isTouchInSmallCanvas;
  20. BOOL isMoveInSmallCanvas;
  21. CGPoint mStartPoint;
  22. }
  23. @property (weak, nonatomic) IBOutlet UIImageView *backgroudView;
  24. @property (weak, nonatomic) IBOutlet UIView *mIngHeaderView;
  25. @property (weak, nonatomic) IBOutlet UILabel *mIngNameLabel;
  26. @property (weak, nonatomic) IBOutlet UILabel *mIngTimeLabel;
  27. @property (weak, nonatomic) IBOutlet UILabel *mIngNetWorkLabel;
  28. @property (weak, nonatomic) IBOutlet UIView *mWaitHeaderView;
  29. @property (weak, nonatomic) IBOutlet UIImageView *mWaitHeadImage;
  30. @property (weak, nonatomic) IBOutlet UILabel *mWaitNameLabel;
  31. @property (weak, nonatomic) IBOutlet UILabel *mWaitInHintLabel;
  32. @property (weak, nonatomic) IBOutlet UILabel *mWaitOutHintLabel;
  33. @property (weak, nonatomic) IBOutlet UIView *mInVideoView;
  34. @property (weak, nonatomic) IBOutlet UIView *mOutView;
  35. @property (weak, nonatomic) IBOutlet UIView *mIngView;
  36. // selected 为相机打开状态, default 为相机关闭状态
  37. @property (weak, nonatomic) IBOutlet UIButton *switchCameraBtn;
  38. // selected 为保持通话状态, default 为未保持通话状态, disable 为被保持通话状态
  39. @property (weak, nonatomic) IBOutlet UIButton *holdBtn;
  40. // selected 为静音状态, default 为正常状态
  41. @property (weak, nonatomic) IBOutlet UIButton *muteBtn;
  42. // selected 为听筒模式, default 为扬声器模式
  43. @property (weak, nonatomic) IBOutlet UIButton *speakerBtn;
  44. @property (weak, nonatomic) IBOutlet UIButton *endCallBtn;
  45. @end
  46. @implementation SKInCallViewController
  47. - (void)viewDidLoad {
  48. [super viewDidLoad];
  49. [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
  50. [EUtil setViewShadow:self.mWaitNameLabel color:[UIColor colorWithRed:0/255.0f green:1/255.0f blue:75/255.0f alpha:0.2] offset:CGSizeMake(0, 2) opacity:1 radius:4];
  51. [EUtil setViewShadow:self.mWaitOutHintLabel color:[UIColor colorWithRed:0/255.0f green:1/255.0f blue:75/255.0f alpha:0.2] offset:CGSizeMake(0, 2) opacity:1 radius:4];
  52. [EUtil setViewShadow:self.mWaitInHintLabel color:[UIColor colorWithRed:0/255.0f green:1/255.0f blue:75/255.0f alpha:0.2] offset:CGSizeMake(0, 2) opacity:1 radius:4];
  53. [VideoManager shared].mJuphoonMediaDevice.videoAngle = JCMediaDeviceVideoAngel0;
  54. [EUtil setViewShadow:self.mIngNameLabel color:[UIColor colorWithRed:0/255.0f green:1/255.0f blue:75/255.0f alpha:0.2] offset:CGSizeMake(0, 2) opacity:1 radius:4];
  55. [EUtil setViewShadow:self.mIngTimeLabel color:[UIColor colorWithRed:0/255.0f green:1/255.0f blue:75/255.0f alpha:0.2] offset:CGSizeMake(0, 2) opacity:1 radius:4];
  56. [EUtil setViewShadow:self.mIngNetWorkLabel color:[UIColor colorWithRed:0/255.0f green:1/255.0f blue:75/255.0f alpha:0.2] offset:CGSizeMake(0, 2) opacity:1 radius:4];
  57. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUI:) name:kCallNotification object:nil];
  58. _mSmallCanvas = nil;
  59. isTouchInSmallCanvas = NO;
  60. isMoveInSmallCanvas = NO;
  61. [self initView];
  62. [self updateUI:nil];
  63. [[VideoManager shared] playVideoWaitingRingtone];
  64. }
  65. - (void)viewWillAppear:(BOOL)animated {
  66. [super viewWillAppear:animated];
  67. [self.navigationController setNavigationBarHidden:YES animated:NO];
  68. }
  69. - (void)viewDidAppear:(BOOL)animated {
  70. [[VideoManager shared].mJuphoonMediaDevice enableSpeaker:YES];
  71. }
  72. - (void)viewWillDisappear:(BOOL)animated {
  73. [super viewWillDisappear:animated];
  74. [self.navigationController setNavigationBarHidden:NO animated:NO];
  75. [self stopTimer];
  76. self.mIngTimeLabel.text = @"";
  77. self.mIngNetWorkLabel.text = @"";
  78. }
  79. - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  80. return UIInterfaceOrientationMaskPortrait;
  81. }
  82. - (void)dealloc {
  83. [[NSNotificationCenter defaultCenter] removeObserver:self];
  84. [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
  85. [[VideoManager shared] playVideoFinishRingtone];
  86. }
  87. - (NSObject*)getRemoteCanvas {
  88. return _remoteCanvas;
  89. }
  90. - (void)initView {
  91. [EUtil setURLImage:self.mWaitHeadImage placeHolderName:@"avatar" imageURL:[[VideoManager shared] getTalkHeadImageURL] cornerRadius:self.mWaitHeadImage.frame.size.width/2];
  92. }
  93. - (void)updateUI:(NSNotification *)noti {
  94. NSInteger count = [VideoManager shared].mJuphoonCall.callItems.count;
  95. JCCallItem *activeCall = [VideoManager shared].mJuphoonCall.callItems.firstObject;
  96. HDNormalLog(([NSString stringWithFormat:@"SKJuphoon: updateUI count:%ld updateUI: %ld", count, activeCall.state]));
  97. if (activeCall.state == JCCallStateInit || activeCall.state == JCCallStatePending) {
  98. self.mInVideoView.hidden = !activeCall.video || activeCall.direction == JCCallDirectionOut;
  99. self.mOutView.hidden = activeCall.direction != JCCallDirectionOut;
  100. self.mWaitHeaderView.hidden = NO;
  101. self.mIngHeaderView.hidden = YES;
  102. self.mIngView.hidden = YES;
  103. self.mIngNameLabel.text = @"";
  104. self.mWaitNameLabel.text = [[VideoManager shared] getTalkName];
  105. self.mWaitInHintLabel.hidden = activeCall.direction != JCCallDirectionIn;
  106. self.mWaitOutHintLabel.hidden = activeCall.direction != JCCallDirectionOut;
  107. if (activeCall.video) {
  108. if (_localCanvas == nil && activeCall.uploadVideoStreamSelf) {
  109. _localCanvas = [[VideoManager shared].mJuphoonMediaDevice startCameraVideo:JCMediaDeviceRenderFullScreen];
  110. //_localCanvas.videoView.frame = CGRectMake(SCREEN_WIDTH - 150, SCREEN_HEIGHT - 150 - 160, 120, 160);
  111. _localCanvas.videoView.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
  112. [self.view insertSubview:_localCanvas.videoView aboveSubview:self.backgroudView];
  113. } else if (_localCanvas != nil && !activeCall.uploadVideoStreamSelf) {
  114. [[VideoManager shared].mJuphoonMediaDevice stopVideo:_localCanvas];
  115. [_localCanvas.videoView removeFromSuperview];
  116. _localCanvas = nil;
  117. }
  118. }
  119. } else if (activeCall.state == JCCallStateTalking) {
  120. self.mInVideoView.hidden = YES;
  121. self.mOutView.hidden = YES;
  122. self.mWaitHeaderView.hidden = YES;
  123. self.mIngHeaderView.hidden = NO;
  124. self.mIngView.hidden = NO;
  125. self.mIngNameLabel.text = [[VideoManager shared] getTalkName];
  126. self.switchCameraBtn.enabled = activeCall.video;
  127. self.holdBtn.enabled = !activeCall.held;
  128. self.holdBtn.selected = activeCall.hold;
  129. self.muteBtn.selected = activeCall.mute;
  130. self.speakerBtn.selected = [VideoManager shared].mJuphoonMediaDevice.isSpeakerOn;
  131. if (activeCall != nil && activeCall.audioRecord && (activeCall.hold || activeCall.held)) {
  132. [[VideoManager shared].mJuphoonCall audioRecord:activeCall enable:false filePath:@""];
  133. }
  134. if (activeCall != nil && activeCall.localVideoRecord && !activeCall.uploadVideoStreamSelf) {
  135. //[[VideoManager shared].mJuphoonCall videoRecord:activeCall enable:false remote:false width:0 height:0 filePath:@""];
  136. [[VideoManager shared].mJuphoonCall videoRecord:activeCall enable:false remote:false width:0 height:0 filePath:@"" bothAudio:false keyFrame:10];
  137. }
  138. if (activeCall != nil && activeCall.remoteVideoRecord && !activeCall.uploadVideoStreamOther) {
  139. [[VideoManager shared].mJuphoonCall videoRecord:activeCall enable:false remote:true width:0 height:0 filePath:@"" bothAudio:false keyFrame:10];
  140. }
  141. if (activeCall.video) {
  142. if (_localCanvas != nil && activeCall.uploadVideoStreamSelf) {
  143. if (_mSmallCanvas == nil) {
  144. _localCanvas.videoView.frame = CGRectMake(SCREEN_WIDTH - 130, SCREEN_STATUS_HEIGHT+10, 120, 160);
  145. }
  146. _mSmallCanvas = _localCanvas;
  147. }
  148. if (_remoteCanvas == nil && activeCall.uploadVideoStreamOther) {
  149. _remoteCanvas = [[VideoManager shared].mJuphoonMediaDevice startVideo:activeCall.renderId renderType:JCMediaDeviceRenderFullContent];
  150. _remoteCanvas.videoView.frame = self.view.frame;
  151. _remoteCanvas.videoView.hidden = !activeCall.uploadVideoStreamSelf;
  152. [self.view insertSubview:_remoteCanvas.videoView aboveSubview:self.backgroudView];
  153. [[VideoManager shared].mJuphoonMediaDevice enableSpeaker:YES];
  154. } else if (_remoteCanvas != nil && !activeCall.uploadVideoStreamOther) {
  155. [[VideoManager shared].mJuphoonMediaDevice stopVideo:_remoteCanvas];
  156. [_remoteCanvas.videoView removeFromSuperview];
  157. _remoteCanvas = nil;
  158. }
  159. }
  160. [self startTimer];
  161. [[VideoManager shared] stopVideoRingtone];
  162. } else {
  163. self.mInVideoView.hidden = YES;
  164. }
  165. if (!activeCall.video) {
  166. [self removeCanvas];
  167. }
  168. }
  169. #pragma mark - Incoming Actions
  170. - (IBAction)endCall:(id)sender {
  171. //通话中挂断; 自己发起时,对方未接通,自己挂断//
  172. [[VideoManager shared] videoEnd:NO isSendMessage:YES];
  173. }
  174. - (IBAction)endCallWhenIncoming:(id)sender {
  175. //对方发起时,自己拒接//
  176. [[VideoManager shared] videoEnd:YES isSendMessage:YES];
  177. }
  178. - (IBAction)videoAnswer:(id)sender {
  179. [[VideoManager shared].mJuphoonCall answer:[self getActiveCall] video:true];
  180. }
  181. - (IBAction)voiceAnswer:(id)sender {
  182. [[VideoManager shared].mJuphoonCall answer:[self getActiveCall] video:false];
  183. }
  184. #pragma mark - In Calling Actions
  185. - (IBAction)setCameraState:(id)sender {
  186. [[VideoManager shared].mJuphoonCall enableUploadVideoStream:[self getActiveCall]];
  187. }
  188. - (IBAction)addNewCall:(id)sender {
  189. }
  190. - (IBAction)switchCamera:(id)sender {
  191. [[VideoManager shared].mJuphoonMediaDevice switchCamera];
  192. }
  193. - (IBAction)hold:(id)sender {
  194. [[VideoManager shared].mJuphoonCall hold:[self getActiveCall]];
  195. }
  196. - (IBAction)mute:(id)sender {
  197. [[VideoManager shared].mJuphoonCall mute:[self getActiveCall]];
  198. }
  199. - (IBAction)audioRecord:(id)sender {
  200. }
  201. - (IBAction)videoRecord:(id)sender {
  202. }
  203. - (IBAction)snapshot:(id)sender {
  204. }
  205. - (IBAction)speaker:(id)sender {
  206. [[VideoManager shared].mJuphoonMediaDevice enableSpeaker:![VideoManager shared].mJuphoonMediaDevice.isSpeakerOn];
  207. self.speakerBtn.selected = [VideoManager shared].mJuphoonMediaDevice.isSpeakerOn;
  208. }
  209. - (IBAction)callStatistics:(id)sender {
  210. }
  211. #pragma mark - 工具函数
  212. -(JCCallItem*)getActiveCall {
  213. for (JCCallItem* item in [VideoManager shared].mJuphoonCall.callItems) {
  214. if (item.active) {
  215. return item;
  216. }
  217. }
  218. return nil;
  219. }
  220. - (NSString *)genCallInfo:(JCCallItem*)item {
  221. switch (item.state) {
  222. case JCCallStateTalking:
  223. return [self formatTalkingTime:((long)[[NSDate date] timeIntervalSince1970] - item.talkingBeginTime)];
  224. default:
  225. return @"";
  226. }
  227. }
  228. - (NSString *)genNetStatus:(JCCallItem *)item {
  229. if (item.state != JCCallStateTalking) {
  230. return @"";
  231. }
  232. switch (item.videoNetSendStatus) {
  233. case JCCallNetWorkDisconnected:
  234. return NSLocalizedString(@"Video.Network.Status.0", nil);
  235. case JCCallNetWorkVeryBad:
  236. return NSLocalizedString(@"Video.Network.Status.1", nil);
  237. case JCCallNetWorkBad:
  238. return NSLocalizedString(@"Video.Network.Status.2", nil);
  239. case JCCallNetWorkNormal:
  240. return NSLocalizedString(@"Video.Network.Status.3", nil);
  241. case JCCallNetWorkGood:
  242. return NSLocalizedString(@"Video.Network.Status.4", nil);
  243. case JCCallNetWorkVeryGood:
  244. return NSLocalizedString(@"Video.Network.Status.5", nil);
  245. default:
  246. return @"";
  247. }
  248. }
  249. - (NSString *)formatTalkingTime:(long)time
  250. {
  251. return [NSString stringWithFormat:@"%02ld:%02ld", time/60, time%60];
  252. }
  253. - (void)removeCanvas {
  254. if (_localCanvas) {
  255. [[VideoManager shared].mJuphoonMediaDevice stopVideo:_localCanvas];
  256. [_localCanvas.videoView removeFromSuperview];
  257. _localCanvas = nil;
  258. _mSmallCanvas = nil;
  259. }
  260. if (_remoteCanvas) {
  261. [[VideoManager shared].mJuphoonMediaDevice stopVideo:_remoteCanvas];
  262. [_remoteCanvas.videoView removeFromSuperview];
  263. _remoteCanvas = nil;
  264. }
  265. }
  266. #pragma mark - Timer
  267. - (void)startTimer
  268. {
  269. if (_timer == nil) {
  270. _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerProc) userInfo:nil repeats:YES];
  271. }
  272. }
  273. - (void)stopTimer {
  274. if (_timer) {
  275. if ([_timer isValid]) {
  276. [_timer invalidate];
  277. _timer = nil;
  278. }
  279. }
  280. }
  281. - (void)timerProc {
  282. self.mIngHeaderView.hidden = NO;
  283. JCCallItem *activeCall = [VideoManager shared].mJuphoonCall.callItems.firstObject;
  284. self.mIngTimeLabel.text = [self genCallInfo:activeCall];
  285. self.mIngNetWorkLabel.text = [self genNetStatus:activeCall];
  286. if (activeCall.direction == JCCallDirectionOut) {
  287. }
  288. }
  289. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  290. //如果不在通话
  291. JCCallItem *activeCall = [VideoManager shared].mJuphoonCall.callItems.firstObject;
  292. if (!activeCall.video && activeCall.state != JCCallStateTalking)
  293. return;
  294. UITouch *touch = [touches anyObject];
  295. //当前的坐标,第一次触摸
  296. CGPoint point = [touch locationInView: self.view];
  297. mStartPoint = point;
  298. point = [_mSmallCanvas.videoView.layer convertPoint:point fromLayer:self.view.layer];
  299. if ([_mSmallCanvas.videoView.layer containsPoint:point]) {
  300. isTouchInSmallCanvas = YES;
  301. }
  302. else {
  303. isTouchInSmallCanvas = NO;
  304. }
  305. }
  306. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  307. //如果不在通话
  308. JCCallItem *activeCall = [VideoManager shared].mJuphoonCall.callItems.firstObject;
  309. if (!activeCall.video && activeCall.state != JCCallStateTalking)
  310. return;
  311. if (!isTouchInSmallCanvas)
  312. return;
  313. UITouch *touch = [touches anyObject];
  314. CGPoint movePoint = [touch locationInView: self.view];
  315. CGPoint offsetPoint = CGPointMake(movePoint.x - mStartPoint.x, movePoint.y - mStartPoint.y);
  316. if (offsetPoint.x == 0 && offsetPoint.y == 0)
  317. return;
  318. isMoveInSmallCanvas = YES;
  319. CGRect canvasRect = _mSmallCanvas.videoView.frame;
  320. CGPoint endPoint = CGPointMake(canvasRect.origin.x + offsetPoint.x, canvasRect.origin.y + offsetPoint.y);
  321. //避免超框
  322. if (endPoint.x < 0.0) {
  323. endPoint.x = 0.0;
  324. }
  325. if (endPoint.y < SCREEN_STATUS_HEIGHT) {
  326. endPoint.y = SCREEN_STATUS_HEIGHT;
  327. }
  328. if (endPoint.x + canvasRect.size.width > SCREEN_WIDTH) {
  329. endPoint.x = SCREEN_WIDTH - canvasRect.size.width;
  330. }
  331. if (endPoint.y + canvasRect.size.height > SCREEN_HEIGHT) {
  332. endPoint.y = SCREEN_HEIGHT - canvasRect.size.height;
  333. }
  334. [_mSmallCanvas.videoView setOrigin:endPoint];
  335. mStartPoint = movePoint;
  336. }
  337. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  338. //如果不在通话
  339. JCCallItem *activeCall = [VideoManager shared].mJuphoonCall.callItems.firstObject;
  340. if (!activeCall.video && activeCall.state != JCCallStateTalking)
  341. return;
  342. isTouchInSmallCanvas = NO;
  343. isMoveInSmallCanvas = NO;
  344. mStartPoint = CGPointMake(0, 0);
  345. }
  346. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  347. //如果不在通话
  348. JCCallItem *activeCall = [VideoManager shared].mJuphoonCall.callItems.firstObject;
  349. if (!activeCall.video && activeCall.state != JCCallStateTalking)
  350. return;
  351. isTouchInSmallCanvas = NO;
  352. isMoveInSmallCanvas = NO;
  353. mStartPoint = CGPointMake(0, 0);
  354. }
  355. @end