main.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. //
  2. // main.m
  3. // vsyncanalysis
  4. //
  5. // Created by Alexandre Janniaux on 27/10/2021.
  6. //
  7. #include <TargetConditionals.h>
  8. #if TARGET_OS_OSX
  9. #import <AppKit/AppKit.h>
  10. #else
  11. #import <UIKit/UIKit.h>
  12. #endif
  13. #import "AppDelegate.h"
  14. //
  15. // ViewController.m
  16. // vsyncanalysis
  17. //
  18. // Created by Alexandre Janniaux on 27/10/2021.
  19. //
  20. //#define USE_OPENGL_ES
  21. #ifdef USE_OPENGL_ES
  22. #import <OpenGLES/EAGL.h>
  23. #import <OpenGLES/ES2/gl.h>
  24. #import <OpenGLES/ES2/glext.h>
  25. #import <QuartzCore/QuartzCore.h>
  26. #endif
  27. #import <AVFoundation/AVFoundation.h>
  28. #import <AVKit/AVKit.h>
  29. #if TARGET_OS_IPHONE
  30. #define VIEW_TYPE UIView
  31. #else
  32. #define VIEW_TYPE NSView
  33. #endif
  34. /*
  35. @interface GLWindow : UIView {
  36. CAEAGLLayer *_layer;
  37. }
  38. @end
  39. */
  40. @interface PIPWindow : VIEW_TYPE
  41. @end
  42. @implementation PIPWindow : VIEW_TYPE
  43. + (Class)layerClass {
  44. return [AVSampleBufferDisplayLayer class];
  45. }
  46. - (CALayer *)makeBackingLayer {
  47. return [[AVSampleBufferDisplayLayer alloc] init];
  48. }
  49. @end
  50. #if 0
  51. @implementation GLWindow
  52. + (Class)layerClass {
  53. return [CAEAGLLayer class];
  54. }
  55. @end
  56. #endif
  57. #if TARGET_OS_IPHONE
  58. #define RESPONDER UIResponder
  59. #define APPLICATIONDELEGATE UIApplicationDelegate
  60. #else
  61. #define RESPONDER NSResponder
  62. #define APPLICATIONDELEGATE NSApplicationDelegate
  63. #endif
  64. @interface AppDelegate : RESPONDER <APPLICATIONDELEGATE, AVPictureInPictureSampleBufferPlaybackDelegate, AVPictureInPictureControllerDelegate> {
  65. PIPWindow *_pip;
  66. #ifdef USE_OPENGL_ES
  67. //GLWindow *_view;
  68. EAGLContext *_context;
  69. CAEAGLLayer *_eagllayer;
  70. #endif
  71. #if TARGET_OS_OSX
  72. NSWindow *_window;
  73. NSGestureRecognizer *_tap;
  74. #else
  75. CADisplayLink *_displayLink;
  76. UIButton *_pipButton;
  77. UIWindow *_window;
  78. UITapGestureRecognizer *_tap;
  79. #endif
  80. AVPictureInPictureController *_pipCtrl;
  81. AVPictureInPictureControllerContentSource *_pipSource;
  82. AVSampleBufferDisplayLayer *_pipLayer;
  83. GLuint _framebuffer;
  84. GLuint _renderbuffer;
  85. CFTimeInterval _lastRender;
  86. const char *_renderMode, *_dropMode;
  87. long long unsigned _frameNo;
  88. }
  89. - (void)setup;
  90. @end
  91. @implementation AppDelegate
  92. - (void)enablePip:(id)sender {
  93. NSLog(@"ENABLE PIP");
  94. //AVPictureInPictureControllerContentSource *source = [[AVPictureInPictureControllerContentSource alloc] initWithSampleBufferDisplayLayer:_pipLayer playbackDelegate:self];
  95. [_pipCtrl startPictureInPicture];
  96. //[_pipCtrl setContentSource:source];
  97. }
  98. #if TARGET_OS_OSX
  99. - (void)applicationDidFinishLaunching:(NSNotification *)notification {}
  100. - (void)setup {
  101. #else
  102. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  103. // Do any additional setup after loading the view.
  104. AVAudioSession * audioSession = [AVAudioSession sharedInstance];
  105. [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
  106. #endif
  107. #if TARGET_OS_OSX
  108. NSRect frame = NSMakeRect(0, 0, 200, 200);
  109. NSWindow* window = [[NSWindow alloc] initWithContentRect:frame
  110. styleMask:NSWindowStyleMaskResizable
  111. backing:NSBackingStoreBuffered
  112. defer:NO];
  113. [window setBackgroundColor:[NSColor blueColor]];
  114. _window = window;
  115. #else
  116. _window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
  117. _window.rootViewController = [[UIViewController alloc] init];
  118. _window.backgroundColor = [UIColor blackColor];
  119. #endif
  120. _pipLayer = [[AVSampleBufferDisplayLayer alloc] init];
  121. _pipLayer.frame = _window.frame;
  122. _pip = [[PIPWindow alloc] initWithFrame:_window.frame];
  123. _pip.userInteractionEnabled = NO;
  124. #if TARGET_OS_OSX
  125. _pip.wantsLayer = YES;
  126. #endif
  127. assert([AVPictureInPictureController isPictureInPictureSupported]);
  128. //assert([[_pip layer] isKindOfClass:[AVSampleBufferDisplayLayer class]]);
  129. AVSampleBufferDisplayLayer *pipLayer = _pip.layer; //_pipLayer;// [_pip layer];
  130. assert(pipLayer);
  131. _pipSource = [[AVPictureInPictureControllerContentSource alloc] initWithSampleBufferDisplayLayer:pipLayer playbackDelegate:self];
  132. _pipCtrl = [[AVPictureInPictureController alloc] initWithContentSource:_pipSource];
  133. _pipCtrl.delegate = self;
  134. assert(_pipCtrl);
  135. #if 0
  136. _pipButton = [UIButton buttonWithType:UIButtonTypeSystem];
  137. _pipButton.frame = CGRectMake(0, 0, 50, 50);
  138. _pipButton.backgroundColor = [UIColor whiteColor];
  139. [_pipButton setTitle:@"PIP" forState:UIControlStateNormal];
  140. UIImage* startImage = [AVPictureInPictureController pictureInPictureButtonStartImage];
  141. UIImage* stopImage = [AVPictureInPictureController pictureInPictureButtonStopImage];
  142. [_pipButton setImage:startImage forState:UIControlStateNormal];
  143. [_pipButton setImage:stopImage forState:UIControlStateSelected];
  144. [_pipButton addTarget:self action:@selector(enablePip:) forControlEvents:UIControlEventTouchUpInside];
  145. //[_pipButton setUserInteractionEnabled:YES];
  146. //[_pipButton setEnabled:YES];
  147. [_window addSubview:_pipButton];
  148. [_window bringSubviewToFront:_pipButton];
  149. #endif
  150. #if TARGET_OS_IPHONE
  151. _tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(enablePip:)];
  152. [_window addGestureRecognizer:_tap];
  153. [_tap setEnabled:YES];
  154. //[_pip.layer addSublayer:_pipLayer];
  155. #ifdef USE_OPENGL_ES
  156. [_pipLayer addSublayer:_eagllayer];
  157. #endif
  158. [_window addSubview:_pip];
  159. [_window makeKeyAndVisible];
  160. #else
  161. [_window setContentView:_pip];
  162. [_window makeKeyAndOrderFront:NSApp];
  163. #endif
  164. #ifdef USE_OPENGL_ES
  165. _eagllayer = [[CAEAGLLayer alloc] init];
  166. _eagllayer.frame = _pip.frame;
  167. _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
  168. EAGLContext *previous = [EAGLContext currentContext];
  169. [EAGLContext setCurrentContext:_context];
  170. glGenRenderbuffers(1, &_renderbuffer);
  171. glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
  172. glGenFramebuffers(1, &_framebuffer);
  173. glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
  174. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  175. GL_RENDERBUFFER, _renderbuffer);
  176. [_context renderbufferStorage:GL_RENDERBUFFER
  177. fromDrawable:_eagllayer];
  178. assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
  179. [EAGLContext setCurrentContext:previous];
  180. #endif
  181. _lastRender = CACurrentMediaTime();
  182. _frameNo = 0;
  183. _renderMode = getenv("RENDER_MODE");
  184. if (_renderMode == NULL)
  185. _renderMode = "vsync";
  186. _dropMode = getenv("DROP_MODE");
  187. if (_dropMode == NULL)
  188. _dropMode = "once";
  189. if (!strcmp(_renderMode, "loop"))
  190. {
  191. dispatch_async(dispatch_get_main_queue(), ^{
  192. [self periodicRender];
  193. });
  194. }
  195. else if (!strcmp(_renderMode, "vsync"))
  196. {
  197. #if TARGET_OS_IPHONE
  198. _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onVsync:)];
  199. [_displayLink addToRunLoop:[NSRunLoop mainRunLoop]
  200. forMode:NSRunLoopCommonModes];
  201. #else
  202. #endif
  203. }
  204. //assert([_pipCtrl isPictureInPicturePossible]);
  205. [_pipCtrl addObserver:self forKeyPath:@"isPictureInPicturePossible"
  206. options:NSKeyValueObservingOptionInitial context:(__bridge void *)self];
  207. [_pipCtrl addObserver:self forKeyPath:@"isPictureInPictureActive"
  208. options:NSKeyValueObservingOptionNew context:(__bridge void *)self];
  209. #if TARGET_OS_IPHONE
  210. return YES;
  211. #endif
  212. }
  213. - (void)observeValueForKeyPath:(NSString *)keyPath
  214. ofObject:(id)object
  215. change:(NSDictionary *)change
  216. context:(void *)context
  217. {
  218. NSLog(@"Can start PiP: %d", _pipCtrl.isPictureInPicturePossible);
  219. NSLog(@"Is active PiP: %d", _pipCtrl.isPictureInPictureActive);
  220. NSLog(@"Is suspended PiP: %d", _pipCtrl.isPictureInPictureSuspended);
  221. }
  222. - (void)periodicRender {
  223. [self render];
  224. dispatch_async(dispatch_get_main_queue(), ^{
  225. [self periodicRender];
  226. });
  227. }
  228. #if TARGET_OS_IPHONE
  229. - (void)onVsync:(CADisplayLink *)sender {
  230. [self render];
  231. }
  232. #endif
  233. - (void)render {
  234. CFTimeInterval begin_context = CACurrentMediaTime();
  235. #ifdef USE_OPENGL_ES
  236. EAGLContext *previous = [EAGLContext currentContext];
  237. [EAGLContext setCurrentContext:_context];
  238. CFTimeInterval begin_clear = CACurrentMediaTime();
  239. glClearColor(cos(begin_context), 0.f, 0.f, 1.f);
  240. glClear(GL_COLOR_BUFFER_BIT);
  241. CFTimeInterval begin_present = CACurrentMediaTime();
  242. [_context presentRenderbuffer:GL_RENDERBUFFER];
  243. CFTimeInterval begin_release = CACurrentMediaTime();
  244. [EAGLContext setCurrentContext:previous];
  245. CFTimeInterval end_render = CACurrentMediaTime();
  246. if (begin_present - begin_clear > 5. / 1000 || true)
  247. {
  248. NSLog(@"RENDER: total=%.3lf makeCurrent=%.3lf clear=%.3lf present=%.3lf release=%.3lf",
  249. 1000. * (end_render - begin_context),
  250. 1000. * (begin_clear - begin_context),
  251. 1000. * (begin_present - begin_clear),
  252. 1000. * (begin_release - begin_present),
  253. 1000. * (end_render - begin_release));
  254. NSLog(@"RENDER: FPS=%.3lf", 1.f / (begin_context - _lastRender));
  255. }
  256. _lastRender = begin_context;
  257. _frameNo++;
  258. #endif
  259. CVPixelBufferRef pixelBuffer=NULL;
  260. CVPixelBufferCreate(kCFAllocatorDefault, 1280, 720, kCVPixelFormatType_32BGRA, NULL, &pixelBuffer);
  261. CMSampleTimingInfo info = {
  262. kCMTimeIndefinite,
  263. kCMTimeZero,
  264. kCMTimeInvalid};
  265. CVPixelBufferLockBaseAddress(pixelBuffer, 0);
  266. size_t width = CVPixelBufferGetWidth(pixelBuffer);
  267. size_t height = CVPixelBufferGetHeight(pixelBuffer);
  268. UInt32* buffer = (UInt32*)CVPixelBufferGetBaseAddress(pixelBuffer);
  269. for ( unsigned long i = 0; i < width * height; i++ )
  270. {
  271. uint32_t value = (uint32_t)(0xff * fabs(cos(begin_context))) << 16;
  272. buffer[i] = CFSwapInt32HostToBig(0x00000fff + value);
  273. }
  274. CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
  275. CMFormatDescriptionRef formatDesc=NULL;
  276. CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, &formatDesc);
  277. CMSampleBufferRef sampleBuffer=NULL;
  278. CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault,
  279. pixelBuffer,
  280. formatDesc,
  281. &info,
  282. &sampleBuffer);
  283. CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true);
  284. CFMutableDictionaryRef dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);
  285. CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanTrue);
  286. //AVPictureInPictureControllerContentSource *source = _pipCtrl.contentSource;
  287. //AVSampleBufferDisplayLayer *pipLayer = _pipCtrl.pictureInPictureActive ?
  288. //_pipLayer : _pip.layer;
  289. //assert([pipLayer status] != AVQueuedSampleBufferRenderingStatusFailed);
  290. AVSampleBufferDisplayLayer *pipLayer = _pip.layer;
  291. [pipLayer enqueueSampleBuffer:sampleBuffer];
  292. [_pipLayer enqueueSampleBuffer:sampleBuffer];
  293. [pipLayer setNeedsDisplay];
  294. [_pipCtrl invalidatePlaybackState];
  295. CFRelease(sampleBuffer);
  296. CFRelease(formatDesc);
  297. CFRelease(pixelBuffer);
  298. }
  299. - (void)pictureInPictureController:(nonnull AVPictureInPictureController *)pictureInPictureController didTransitionToRenderSize:(CMVideoDimensions)newRenderSize {
  300. NSLog(@"RESIZED TO %fx%f", newRenderSize.width, newRenderSize.height);
  301. }
  302. - (void)pictureInPictureController:(nonnull AVPictureInPictureController *)pictureInPictureController setPlaying:(BOOL)playing {
  303. NSLog(@"PLAYING STATE: %d", playing);
  304. }
  305. - (void)pictureInPictureController:(nonnull AVPictureInPictureController *)pictureInPictureController skipByInterval:(CMTime)skipInterval completionHandler:(nonnull void (^)(void))completionHandler {
  306. }
  307. - (BOOL)pictureInPictureControllerIsPlaybackPaused:(nonnull AVPictureInPictureController *)pictureInPictureController {
  308. //NSLog(@"IS PLAYBACK PAUSED");
  309. return NO;
  310. }
  311. - (CMTimeRange)pictureInPictureControllerTimeRangeForPlayback:(nonnull AVPictureInPictureController *)pictureInPictureController {
  312. return CMTimeRangeMake(kCMTimePositiveInfinity, kCMTimeZero);
  313. }
  314. - (void)pictureInPictureControllerPlaybackPaused:(nonnull AVPictureInPictureController *)controller {
  315. // NSLog(@"PLAYBACK PAUSED");
  316. }
  317. - (CMTimeRange)pictureInPictureControllerPlaybackTimeRange:(nonnull AVPictureInPictureController *)pictureInPictureController
  318. {
  319. return CMTimeRangeMake(CMTimeMake(CACurrentMediaTime()*1000000, 1000000), CMTimeMake(100000000, 1000000));
  320. }
  321. - (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController
  322. failedToStartPictureInPictureWithError:(NSError *)error
  323. {
  324. }
  325. - (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController
  326. {
  327. }
  328. @end
  329. int main(int argc, char * argv[]) {
  330. NSString * appDelegateClassName;
  331. @autoreleasepool {
  332. // Setup code that might create autoreleased objects goes here.
  333. appDelegateClassName = NSStringFromClass([AppDelegate class]);
  334. }
  335. #if TARGET_OS_IPHONE
  336. return UIApplicationMain(argc, argv, nil, appDelegateClassName);
  337. #else
  338. AppDelegate *appDelegate = [[AppDelegate alloc] init];
  339. [appDelegate setup];
  340. [NSApp setDelegate:appDelegate];
  341. [NSApp run];
  342. return NSApplicationMain(argc, argv);
  343. #endif
  344. }