2014 snapchat source code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
3.5 KiB

  1. //
  2. // SCSingleFrameStreamCapturer.m
  3. // Snapchat
  4. //
  5. // Created by Benjamin Hollis on 5/3/16.
  6. // Copyright © 2016 Snapchat, Inc. All rights reserved.
  7. //
  8. #import "SCSingleFrameStreamCapturer.h"
  9. #import "SCManagedCapturer.h"
  10. @implementation SCSingleFrameStreamCapturer {
  11. sc_managed_capturer_capture_video_frame_completion_handler_t _callback;
  12. }
  13. - (instancetype)initWithCompletion:(sc_managed_capturer_capture_video_frame_completion_handler_t)completionHandler
  14. {
  15. self = [super init];
  16. if (self) {
  17. _callback = completionHandler;
  18. }
  19. return self;
  20. }
  21. #pragma mark - SCManagedVideoDataSourceListener
  22. - (void)managedVideoDataSource:(id<SCManagedVideoDataSource>)managedVideoDataSource
  23. didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
  24. devicePosition:(SCManagedCaptureDevicePosition)devicePosition
  25. {
  26. if (_callback) {
  27. UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
  28. _callback(image);
  29. }
  30. _callback = nil;
  31. }
  32. /**
  33. * Decode a CMSampleBufferRef to our native camera format (kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
  34. * as set in SCManagedVideoStreamer) to a UIImage.
  35. *
  36. * Code from http://stackoverflow.com/a/31553521/11284
  37. */
  38. #define clamp(a) (a > 255 ? 255 : (a < 0 ? 0 : a))
  39. // TODO: Use the transform code from SCImageProcessIdentityYUVCommand
  40. - (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer
  41. {
  42. CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
  43. CVPixelBufferLockBaseAddress(imageBuffer, 0);
  44. size_t width = CVPixelBufferGetWidth(imageBuffer);
  45. size_t height = CVPixelBufferGetHeight(imageBuffer);
  46. uint8_t *yBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
  47. size_t yPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
  48. uint8_t *cbCrBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1);
  49. size_t cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1);
  50. int bytesPerPixel = 4;
  51. uint8_t *rgbBuffer = malloc(width * height * bytesPerPixel);
  52. for (int y = 0; y < height; y++) {
  53. uint8_t *rgbBufferLine = &rgbBuffer[y * width * bytesPerPixel];
  54. uint8_t *yBufferLine = &yBuffer[y * yPitch];
  55. uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch];
  56. for (int x = 0; x < width; x++) {
  57. int16_t y = yBufferLine[x];
  58. int16_t cb = cbCrBufferLine[x & ~1] - 128;
  59. int16_t cr = cbCrBufferLine[x | 1] - 128;
  60. uint8_t *rgbOutput = &rgbBufferLine[x * bytesPerPixel];
  61. int16_t r = (int16_t)roundf(y + cr * 1.4);
  62. int16_t g = (int16_t)roundf(y + cb * -0.343 + cr * -0.711);
  63. int16_t b = (int16_t)roundf(y + cb * 1.765);
  64. rgbOutput[0] = 0xff;
  65. rgbOutput[1] = clamp(b);
  66. rgbOutput[2] = clamp(g);
  67. rgbOutput[3] = clamp(r);
  68. }
  69. }
  70. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  71. CGContextRef context = CGBitmapContextCreate(rgbBuffer, width, height, 8, width * bytesPerPixel, colorSpace,
  72. kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
  73. CGImageRef quartzImage = CGBitmapContextCreateImage(context);
  74. // TODO: Hardcoding UIImageOrientationRight seems cheesy
  75. UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight];
  76. CGContextRelease(context);
  77. CGColorSpaceRelease(colorSpace);
  78. CGImageRelease(quartzImage);
  79. free(rgbBuffer);
  80. CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
  81. return image;
  82. }
  83. @end