How to take screenshot of video rendered in AVSampleBufferDisplayLayer

I am trying to play videos in AVSampleBufferDisplayLayer. Everything works well except it seems like the screenshot no longer works for the AVSBDPL when taken programatically.

I have tried a couple of approaches and the screenshot taken is always a black screen in the area of the AVSBDPL. Here are the approached that I have tried, but none of them works:

1. Get an image from image context with [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]

- (UIImage *)_screenshot:(UIView *)view {
  UIGraphicsBeginImageContextWithOptions(view.frame.size, view.opaque, 0.0);
  [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return image;
}

No matter which view I provided to the function(the screen, the player container view, etc.), the video area is always a black image. And I have tried different setup for the image context, or flip the afterScreenUpdates, the result is always the same.

2. Get an image from image context with [view.layer renderInContext:UIGraphicsGetCurrentContext()]

- (UIImage *)_screenshot:(UIView*)view {
  UIGraphicsBeginImageContextWithOptions(view.frame.size, view.opaque, 0.0);
  [view.layer renderInContext:UIGraphicsGetCurrentContext()];  
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return image;
}

[layer renderInContext:UIGraphicsGetCurrentContext()] is an old API that is used below iOS 10. This is very slow and replaced by [drawViewHierarchyInRect:view] after iOS 10. Same here, the screenshot just shows a black screen.

3. Use UIGraphicsImageRenderer

- (UIImage *)_screenshotNew:(UIView*)view {
  UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat new];
  format.opaque = view.opaque;
  format.scale = 0.0;
  UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:view.frame.size format:format];

  UIImage *screenshotImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
    [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
  }];
  
  return screenshotImage;
}

This is the latest API to take a screenshot and convert it to an UIImage, which does not work either.

4. Use [view snapshotViewAfterScreenUpdates:YES]

UIView *snapView = [self.view snapshotViewAfterScreenUpdates:YES];

UIView has an API called snapshotViewAfterScreenUpdates. Surprisingly, the UIView returned by this API can be rendered directly in the UI, and it shows the right screenshot(Woohoo!). However, when I tried to convert the UIView to an UIImage, it becomes a black screen again.

Some additional configurations that I have tried

  1. preventsCapture instance property in AVSBDPL. This is NO by default. And when it is set to YES, it prevents the user from taking screenshot of the layer by pressing the physical buttons on the phone. But it does not have any effect on programmatically taking screenshot.
  2. outputObscuredDueToInsufficientExternalProtection instance property of AVSBDPL. This property is always NO for me. Thus, I don't think it obscures anything. Also, this is a iOS 14.5+ API, and I do see the issue below 14.5.

There are also very few posts when I searched on Google and all of them have run into the same issue but cannot solve. It would be really appreciated if any one can help me with this!

Replies

Please file a report using the feedback assistant and, if possible, include a project that demonstrates the issue.