如何截取AVplayerLayer的截图。我尝试使用以下代码,它运行良好,它捕获整个视图,因为它是
func screenShotMethod() {
let window = UIApplication.shared.delegate!.window!!
//capture the entire window into an image
UIGraphicsBeginImageContextWithOptions(window.bounds.size, false, UIScreen.main.scale)
window.drawHierarchy(in: window.bounds, afterScreenUpdates: false)
let windowImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//now position the image x/y away from the top-left corner to get the portion we want
UIGraphicsBeginImageContext(view.frame.size)
windowImage?.draw(at: CGPoint(x: -view.frame.origin.x, y: -view.frame.origin.y))
let croppedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext();
//embed image in an imageView, supports transforms.
let resultImageView = UIImageView(image: croppedImage)
UIImageWriteToSavedPhotosAlbum(croppedImage, nil, nil, nil)
}
但问题是,当我尝试在iPhone(设备)上运行相同的代码时,它会返回黑色图像。我不知道出了什么问题
任何建议都会有很大帮助!
答案 0 :(得分:2)
几天前,我们也遇到了同样的问题。在哪里,如果我们截取屏幕中有视频播放器的屏幕截图;截图在模拟器中看起来很好。但是,在设备上,它是一个黑屏。
经过多次尝试,我失败了,最后得到一个补丁(不确定这是否是解决问题的正确方法)。但是,解决方案完成了这个技巧,我也可以在设备上获得屏幕截图以及模拟器。
以下是我用来解决问题的方法。
1 - >从视频中获取当前时间的单帧(公共方法已经可用于此)
2 - >在CALayer的位置使用此缩略图(将其添加到层次结构中)
3 - >完成后,从内存中删除缩略图(从层次结构中删除)
以下是相同的演示示例(给定的解决方案在Objective-c中,但问题是在Swift中)。
目标 - C解决方案
- (void)SnapShot {
UIImage *capturedImage = [self getASnapShotWithAVLayer];
}
- (UIImage *)getASnapShotWithAVLayer {
//Add temporary thumbnail One
UIImageView *temporaryViewForVideoOne = [[UIImageView alloc] initWithFrame:self.videoViewOne.bounds];
temporaryViewForVideoOne.contentMode = UIViewContentModeScaleAspectFill;
UIImage *imageFromCurrentTimeForVideoOne = [self takeVideoSnapShot:_playerItem1];
int orientationFromVideoForVideoOne = [self getTheActualOrientationOfVideo:self.playerItem1];
if(orientationFromVideoForVideoOne == 0)
{
orientationFromVideoForVideoOne = 3;
}
else if (orientationFromVideoForVideoOne == 90)
{
orientationFromVideoForVideoOne = 0;
}
imageFromCurrentTimeForVideoOne =
[UIImage imageWithCGImage:[imageFromCurrentTimeForVideoOne CGImage]
scale:[imageFromCurrentTimeForVideoOne scale]
orientation: orientationFromVideoForVideoOne];
UIImage *rotatedImageFromCurrentContextForVideoOne = [self normalizedImage:imageFromCurrentTimeForVideoOne];
temporaryViewForVideoOne.clipsToBounds = YES;
temporaryViewForVideoOne.image = rotatedImageFromCurrentContextForVideoOne;
[self.videoViewOne addSubview:temporaryViewForVideoOne];
CGSize imageSize = CGSizeZero;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsPortrait(orientation)) {
imageSize = [UIScreen mainScreen].bounds.size;
} else {
imageSize = CGSizeMake([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width);
}
UIGraphicsBeginImageContextWithOptions(imageSize, NO, [[UIScreen mainScreen] scale]);
CGContextRef context = UIGraphicsGetCurrentContext();
for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
CGContextSaveGState(context);
CGContextTranslateCTM(context, window.center.x, window.center.y);
CGContextConcatCTM(context, window.transform);
CGContextTranslateCTM(context, -window.bounds.size.width * window.layer.anchorPoint.x, -window.bounds.size.height * window.layer.anchorPoint.y);
if (orientation == UIInterfaceOrientationLandscapeLeft) {
CGContextRotateCTM(context, M_PI_2);
CGContextTranslateCTM(context, 0, -imageSize.width);
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
CGContextRotateCTM(context, -M_PI_2);
CGContextTranslateCTM(context, -imageSize.height, 0);
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
CGContextRotateCTM(context, M_PI);
CGContextTranslateCTM(context, -imageSize.width, -imageSize.height);
}
if (![window respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
} else {
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
}
CGContextRestoreGState(context);
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[temporaryViewForVideoOne removeFromSuperview];
[temporaryViewForVideoTwo removeFromSuperview];
return image;
}
-(UIImage *)takeVideoSnapShot: (AVPlayerItem *) playerItem{
AVURLAsset *asset = (AVURLAsset *) playerItem.asset;
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.requestedTimeToleranceAfter = kCMTimeZero;
imageGenerator.requestedTimeToleranceBefore = kCMTimeZero;
CGImageRef thumb = [imageGenerator copyCGImageAtTime:playerItem.currentTime
actualTime:NULL
error:NULL];
UIImage *videoImage = [UIImage imageWithCGImage:thumb];
CGImageRelease(thumb);
return videoImage;
}
-(int)getTheActualOrientationOfVideo:(AVPlayerItem *)playerItem
{
AVAsset *asset = playerItem.asset;
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *track = [tracks objectAtIndex:0];
CGAffineTransform videoAssetOrientation_ = [track preferredTransform];
CGFloat videoAngle = RadiansToDegrees(atan2(videoAssetOrientation_.b, videoAssetOrientation_.a));
int orientation = 0;
switch ((int)videoAngle) {
case 0:
orientation = UIImageOrientationRight;
break;
case 90:
orientation = UIImageOrientationUp;
break;
case 180:
orientation = UIImageOrientationLeft;
break;
case -90:
orientation = UIImageOrientationDown;
break;
default:
//Not found
break;
}
return orientation;
}
- (UIImage *)normalizedImage:(UIImage *)imageOf {
if (imageOf.imageOrientation == UIImageOrientationUp) return imageOf;
UIGraphicsBeginImageContextWithOptions(imageOf.size, NO, imageOf.scale);
[imageOf drawInRect:(CGRect){0, 0, imageOf.size}];
UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage;
}
Swift解决方案
func snapShot() {
let capturedImage: UIImage? = getASnapShotWithAVLayer()
}
func getASnapShotWithAVLayer() -> UIImage {
//Add temporary thumbnail One
let temporaryViewForVideoOne = UIImageView(frame: videoViewOne.bounds) //replace videoViewOne with you view which is showing AVPlayerContent
temporaryViewForVideoOne.contentMode = .scaleAspectFill
var imageFromCurrentTimeForVideoOne: UIImage? = takeVideoSnapShot(playerItem1)
var orientationFromVideoForVideoOne: Int = getTheActualOrientationOfVideo(playerItem1)
if orientationFromVideoForVideoOne == 0 {
orientationFromVideoForVideoOne = 3
}
else if orientationFromVideoForVideoOne == 90 {
orientationFromVideoForVideoOne = 0
}
imageFromCurrentTimeForVideoOne = UIImage(cgImage: imageFromCurrentTimeForVideoOne?.cgImage, scale: imageFromCurrentTimeForVideoOne?.scale, orientation: orientationFromVideoForVideoOne)
let rotatedImageFromCurrentContextForVideoOne: UIImage? = normalizedImage(imageFromCurrentTimeForVideoOne)
temporaryViewForVideoOne.clipsToBounds = true
temporaryViewForVideoOne.image = rotatedImageFromCurrentContextForVideoOne
videoViewOne.addSubview(temporaryViewForVideoOne) //Replace videoViewOne with your view containing AVPlayer
var imageSize = CGSize.zero
let orientation: UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
if UIInterfaceOrientationIsPortrait(orientation) {
imageSize = UIScreen.main.bounds.size
}
else {
imageSize = CGSize(width: CGFloat(UIScreen.main.bounds.size.height), height: CGFloat(UIScreen.main.bounds.size.width))
}
UIGraphicsBeginImageContextWithOptions(imageSize, false, UIScreen.main.scale())
let context: CGContext? = UIGraphicsGetCurrentContext()
for window: UIWindow in UIApplication.shared.windows {
context.saveGState()
context.translateBy(x: window.center.x, y: window.center.y)
context.concatenate(window.transform)
context.translateBy(x: -window.bounds.size.width * window.layer.anchorPoint.x, y: -window.bounds.size.height * window.layer.anchorPoint.y)
if orientation == .landscapeLeft {
context.rotate(by: M_PI_2)
context.translateBy(x: 0, y: -imageSize.width)
}
else if orientation == .landscapeRight {
context.rotate(by: -M_PI_2)
context.translateBy(x: -imageSize.height, y: 0)
}
else if orientation == .portraitUpsideDown {
context.rotate(by: .pi)
context.translateBy(x: -imageSize.width, y: -imageSize.height)
}
if !window.responds(to: Selector("drawViewHierarchyInRect:afterScreenUpdates:")) {
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
}
else {
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
}
context.restoreGState()
}
let image: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
temporaryViewForVideoOne.removeFromSuperview()
return image!
}
func takeVideoSnapShot(_ playerItem: AVPlayerItem) -> UIImage {
let asset: AVURLAsset? = (playerItem.asset as? AVURLAsset)
let imageGenerator = AVAssetImageGenerator(asset)
imageGenerator.requestedTimeToleranceAfter = kCMTimeZero
imageGenerator.requestedTimeToleranceBefore = kCMTimeZero
let thumb: CGImageRef? = try? imageGenerator.copyCGImage(atTime: playerItem.currentTime(), actualTime: nil)
let videoImage = UIImage(cgImage: thumb)
CGImageRelease(thumb)
return videoImage
}
func getTheActualOrientationOfVideo(_ playerItem: AVPlayerItem) -> Int {
let asset: AVAsset? = playerItem.asset
let tracks: [Any]? = asset?.tracks(withMediaType: AVMediaTypeVideo)
let track: AVAssetTrack? = (tracks?[0] as? AVAssetTrack)
let videoAssetOrientation_: CGAffineTransform? = track?.preferredTransform
let videoAngle: CGFloat? = RadiansToDegrees(atan2(videoAssetOrientation_?.b, videoAssetOrientation_?.a))
var orientation: Int = 0
switch Int(videoAngle) {
case 0:
orientation = .right
case 90:
orientation = .up
case 180:
orientation = .left
case -90:
orientation = .down
default:
//Not found
}
return orientation
}
func normalizedImage(_ imageOf: UIImage) -> UIImage {
if imageOf.imageOrientation == .up {
return imageOf
}
UIGraphicsBeginImageContextWithOptions(imageOf.size, false, imageOf.scale)
imageOf.draw(in: (CGRect))
let normalizedImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return normalizedImage!
}
答案 1 :(得分:0)
以下是快速运行的代码:
var videoImage = UIImage()
if let url = (player.currentItem?.asset as? AVURLAsset)?.url {
let asset = AVAsset(url: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.requestedTimeToleranceAfter = CMTime.zero
imageGenerator.requestedTimeToleranceBefore = CMTime.zero
if let thumb: CGImage = try? imageGenerator.copyCGImage(at: player.currentTime(), actualTime: nil) {
//print("video img successful")
videoImage = UIImage(cgImage: thumb)
}
}
答案 2 :(得分:0)
这里是截取您的 AVPlayer 屏幕截图的代码,包括您在屏幕截图上也需要的任何 UI。
func takeScreenshot() -> UIImage? {
//1 Hide all UI you do not want on the screenshot
self.hideButtonsForScreenshot()
//2 Create an screenshot from your AVPlayer
if let url = (self.overlayPlayer?.currentItem?.asset as? AVURLAsset)?.url {
let asset = AVAsset(url: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.requestedTimeToleranceAfter = CMTime.zero
imageGenerator.requestedTimeToleranceBefore = CMTime.zero
if let thumb: CGImage = try? imageGenerator.copyCGImage(at: self.overlayPlayer!.currentTime(), actualTime: nil) {
let videoImage = UIImage(cgImage: thumb)
//Note: create an image view on top of you videoPlayer in the exact dimensions, and display it before taking the screenshot
// mine is created in the storyboard
// 3 Put the image from the screenshot in your screenshotPhotoView and unhide it
self.screenshotPhotoView.image = videoImage
self.screenshotPhotoView.isHidden = false
}
}
//4 Take the screenshot
let bounds = UIScreen.main.bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, true, 0.0)
self.view.drawHierarchy(in: bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//5 show all UI again that you didn't want on your screenshot
self.showButtonsForScreenshot()
//6 Now hide the screenshotPhotoView again
self.screenshotPhotoView.isHidden = true
self.screenshotPhotoView.image = nil
return image
}