簡體   English   中英

swift:如何截取 AVPlayerLayer() 的截圖

[英]swift: How to take screenshot of AVPlayerLayer()

如何截取 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(設備)上運行相同的代碼時,它返回黑色圖像。我不知道出了什么問題

任何建議都會非常有幫助!

這是在 swift 4 中對我有用的代碼:

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)
       }

}

幾天前,我們也遇到了同樣的問題。 哪里,如果我們截取一個有視頻播放器的屏幕截圖; 屏幕截圖在模擬器中看起來不錯。但是,在設備上,它是黑屏。

經過多次嘗試,我失敗了,最終得到了一個補丁(不確定是否是解決問題的正確方法)。 但是,該解決方案成功了,我也能夠在設備和模擬器上獲得屏幕截圖。

以下是我用來解決問題的方法。

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;
    }

迅捷解決方案

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!
}

這是用於截取您的 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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM