FullScreenVideoPlayer.mm 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #import <UIKit/UIKit.h>
  2. #import <AVFoundation/AVFoundation.h>
  3. #import <AVKit/AVKit.h>
  4. #import <UIKit/UIGestureRecognizerSubclass.h>
  5. #include "UnityAppController.h"
  6. #include "UI/UnityView.h"
  7. #include "UI/UnityViewControllerBase.h"
  8. #include "UI/OrientationSupport.h"
  9. #include "UI/UnityAppController+ViewHandling.h"
  10. #include "Unity/ObjCRuntime.h"
  11. #include "Unity/VideoPlayer.h"
  12. #include "PluginBase/UnityViewControllerListener.h"
  13. @interface UICancelGestureRecognizer : UITapGestureRecognizer
  14. @end
  15. @interface AVKitVideoPlayback : NSObject<VideoPlayerDelegate, UIViewControllerTransitioningDelegate, UIGestureRecognizerDelegate>
  16. {
  17. AVPlayerViewController* videoViewController;
  18. VideoPlayer* videoPlayer;
  19. UIColor* bgColor;
  20. const NSString* videoGravity;
  21. BOOL showControls;
  22. BOOL cancelOnTouch;
  23. }
  24. - (void)onPlayerReady;
  25. - (void)onPlayerDidFinishPlayingVideo;
  26. - (id)initAndPlay:(NSURL*)url bgColor:(UIColor*)color showControls:(BOOL)controls videoGravity:(const NSString*)scaling cancelOnTouch:(BOOL)cot;
  27. - (void)actuallyStartTheMovie:(NSURL*)url;
  28. - (void)finish;
  29. @end
  30. static AVKitVideoPlayback* _AVKitVideoPlayback = nil;
  31. @implementation AVKitVideoPlayback
  32. #if PLATFORM_IOS
  33. static void AVPlayerViewController_SetAllowsPictureInPicturePlayback_OldIOSImpl(id self_, SEL _cmd, BOOL allow) {}
  34. static NSUInteger supportedInterfaceOrientations_DefaultImpl(id self_, SEL _cmd)
  35. {
  36. return GetAppController().rootViewController.supportedInterfaceOrientations;
  37. }
  38. static bool prefersStatusBarHidden_DefaultImpl(id self_, SEL _cmd)
  39. {
  40. if (_AVKitVideoPlayback) // video is still playing
  41. return _AVKitVideoPlayback->videoViewController.showsPlaybackControls ? NO : YES;
  42. else // video has beed stopped
  43. return GetAppController().rootViewController.prefersStatusBarHidden;
  44. }
  45. #endif
  46. + (void)initialize
  47. {
  48. if (self == [AVKitVideoPlayback class])
  49. {
  50. #if PLATFORM_IOS
  51. class_replaceMethod([AVPlayerViewController class], @selector(supportedInterfaceOrientations), (IMP)&supportedInterfaceOrientations_DefaultImpl, UIViewController_supportedInterfaceOrientations_Enc);
  52. class_replaceMethod([AVPlayerViewController class], @selector(prefersStatusBarHidden), (IMP)&prefersStatusBarHidden_DefaultImpl, UIViewController_prefersStatusBarHidden_Enc);
  53. #endif
  54. }
  55. }
  56. - (id)initAndPlay:(NSURL*)url bgColor:(UIColor*)color showControls:(BOOL)controls videoGravity:(const NSString*)scaling cancelOnTouch:(BOOL)cot
  57. {
  58. if ((self = [super init]))
  59. {
  60. UnityPause(1);
  61. showControls = controls;
  62. videoGravity = scaling;
  63. bgColor = color;
  64. cancelOnTouch = cot;
  65. [self performSelector: @selector(actuallyStartTheMovie:) withObject: url afterDelay: 0];
  66. }
  67. return self;
  68. }
  69. - (void)dealloc
  70. {
  71. [self finish];
  72. }
  73. - (void)actuallyStartTheMovie:(NSURL*)url
  74. {
  75. @autoreleasepool
  76. {
  77. videoViewController = [[AVPlayerViewController alloc] init];
  78. videoViewController.showsPlaybackControls = showControls;
  79. videoViewController.view.backgroundColor = bgColor;
  80. videoViewController.videoGravity = (NSString*)videoGravity;
  81. videoViewController.transitioningDelegate = self;
  82. #if PLATFORM_IOS
  83. videoViewController.allowsPictureInPicturePlayback = NO;
  84. #endif
  85. #if PLATFORM_TVOS
  86. // In tvOS clicking Menu button while video is playing will exit the app. So when
  87. // app disables exiting to menu behavior, we need to catch the click and ignore it.
  88. if (!UnityGetAppleTVRemoteAllowExitToMenu())
  89. {
  90. UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(handleTap:)];
  91. tapRecognizer.allowedPressTypes = @[@(UIPressTypeMenu)];
  92. [videoViewController.view addGestureRecognizer: tapRecognizer];
  93. }
  94. #endif
  95. if (cancelOnTouch)
  96. {
  97. UICancelGestureRecognizer *cancelTouch = [[UICancelGestureRecognizer alloc] initWithTarget: self action: @selector(handleTap:)];
  98. cancelTouch.delegate = self;
  99. [videoViewController.view addGestureRecognizer: cancelTouch];
  100. }
  101. videoPlayer = [[VideoPlayer alloc] init];
  102. videoPlayer.delegate = self;
  103. [videoPlayer loadVideo: url];
  104. }
  105. }
  106. - (void)handleTap:(UITapGestureRecognizer*)sender
  107. {
  108. if (cancelOnTouch && (sender.state == UIGestureRecognizerStateEnded))
  109. [self finish];
  110. }
  111. - (void)onPlayerReady
  112. {
  113. videoViewController.player = videoPlayer.player;
  114. CGSize screenSize = GetAppController().rootView.bounds.size;
  115. BOOL ret = [VideoPlayer CheckScalingModeAspectFill: videoPlayer.videoSize screenSize: screenSize];
  116. if (ret == YES && [videoViewController.videoGravity isEqualToString: AVLayerVideoGravityResizeAspect] == YES)
  117. {
  118. videoViewController.videoGravity = AVLayerVideoGravityResizeAspectFill;
  119. }
  120. [videoPlayer playVideoPlayer];
  121. #if PLATFORM_TVOS
  122. GetAppController().window.rootViewController = videoViewController;
  123. #else
  124. UIViewController *viewController = [GetAppController() topMostController];
  125. if ([viewController isEqual: videoViewController] == NO && [videoViewController isBeingPresented] == NO)
  126. [viewController presentViewController: videoViewController animated: NO completion: nil];
  127. #endif
  128. }
  129. - (void)onPlayerDidFinishPlayingVideo
  130. {
  131. [self finish];
  132. }
  133. - (void)onPlayerTryResume
  134. {
  135. if (![videoPlayer isPlaying])
  136. [videoPlayer resume];
  137. }
  138. - (void)onPlayerError:(NSError*)error
  139. {
  140. [self finish];
  141. }
  142. - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
  143. {
  144. if ([dismissed isEqual: videoViewController] == YES)
  145. {
  146. [self finish];
  147. }
  148. return nil;
  149. }
  150. - (void)finish
  151. {
  152. @synchronized(self)
  153. {
  154. #if PLATFORM_TVOS
  155. GetAppController().window.rootViewController = GetAppController().rootViewController;
  156. #else
  157. UIViewController *viewController = [GetAppController() topMostController];
  158. if ([viewController isEqual: videoViewController] == YES && [viewController isBeingDismissed] == NO)
  159. [viewController dismissViewControllerAnimated: NO completion: nil];
  160. #endif
  161. [videoPlayer unloadPlayer];
  162. videoPlayer = nil;
  163. videoViewController = nil;
  164. _AVKitVideoPlayback = nil;
  165. #if PLATFORM_TVOS
  166. UnityCancelTouches();
  167. #endif
  168. if (UnityIsPaused())
  169. UnityPause(0);
  170. }
  171. }
  172. @end
  173. @implementation UICancelGestureRecognizer
  174. //instead of having lots of UITapGestureRecognizers with different finger numbers
  175. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
  176. {
  177. [self setState: UIGestureRecognizerStateRecognized];
  178. }
  179. @end
  180. extern "C" void UnityPlayFullScreenVideo(const char* path, const float* color, unsigned controls, unsigned scaling)
  181. {
  182. const BOOL cancelOnTouch[] = { NO, NO, YES, NO };
  183. UIColor* bgColor = [UIColor colorWithRed: color[0] green: color[1] blue: color[2] alpha: color[3]];
  184. const bool isURL = ::strstr(path, "://") != 0;
  185. NSURL* url = nil;
  186. if (isURL)
  187. {
  188. url = [NSURL URLWithString: [NSString stringWithUTF8String: path]];
  189. }
  190. else
  191. {
  192. NSString* relPath = path[0] == '/' ? [NSString stringWithUTF8String: path] : [NSString stringWithFormat: @"Data/Raw/%s", path];
  193. NSString* fullPath = [[NSString stringWithUTF8String: UnityDataBundleDir()] stringByAppendingPathComponent: relPath];
  194. url = [NSURL fileURLWithPath: fullPath];
  195. }
  196. const BOOL showControls[] = { YES, YES, NO, NO };
  197. const NSString* videoGravity[] =
  198. {
  199. AVLayerVideoGravityResizeAspectFill, // ???
  200. AVLayerVideoGravityResizeAspect,
  201. AVLayerVideoGravityResizeAspectFill,
  202. AVLayerVideoGravityResize,
  203. };
  204. if (_AVKitVideoPlayback)
  205. [_AVKitVideoPlayback finish];
  206. _AVKitVideoPlayback = [[AVKitVideoPlayback alloc] initAndPlay: url bgColor: bgColor
  207. showControls: showControls[controls] videoGravity: videoGravity[scaling] cancelOnTouch: cancelOnTouch[controls]];
  208. }
  209. extern "C" void UnityStopFullScreenVideoIfPlaying()
  210. {
  211. if (_AVKitVideoPlayback)
  212. [_AVKitVideoPlayback finish];
  213. }
  214. extern "C" int UnityIsFullScreenPlaying()
  215. {
  216. return _AVKitVideoPlayback ? 1 : 0;
  217. }
  218. extern "C" void TryResumeFullScreenVideo()
  219. {
  220. if (_AVKitVideoPlayback)
  221. [_AVKitVideoPlayback onPlayerTryResume];
  222. }