You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在AVFoundation中裁剪显示视频局部?代码异常求助

AVFoundation视频裁剪不符合预期的排查思路

针对你提供的代码,从以下几个方向逐一排查:

  • Core Image与UIKit坐标系不匹配
    CICrop的inputRectangle参数遵循Core Image坐标系(原点在左下角),而你直接使用的是UIKit坐标系(原点在左上角),这会导致裁剪区域位置完全颠倒。先把UIKit风格的裁剪矩形转换为Core Image坐标系:

    guard let videoTrack = item.asset.tracks(withMediaType: .video).first else { return }
    let videoNaturalSize = videoTrack.naturalSize
    // 转换坐标系
    let ciCropRect = CGRect(
        x: cropRect.origin.x,
        y: videoNaturalSize.height - cropRect.origin.y - cropRect.height,
        width: cropRect.width,
        height: cropRect.height
    )
    // 用转换后的rect创建CIVector
    cropFilter.setValue(CIVector(cgRect: ciCropRect), forKey: "inputRectangle")
    
  • 裁剪区域超出视频实际分辨率
    你使用了固定的cropRect数值,但如果视频的实际分辨率和你预期不符(比如视频是1920x1080,你按1280x720设置坐标),会导致裁剪区域偏移或无效。先获取视频的真实尺寸:

    guard let videoTrack = item.asset.tracks(withMediaType: .video).first else { return }
    let naturalSize = videoTrack.naturalSize
    let transform = videoTrack.preferredTransform
    // 修正旋转后的实际显示尺寸
    let actualVideoSize = CGSize(
        width: naturalSize.width * abs(transform.a),
        height: naturalSize.height * abs(transform.d)
    )
    // 检查cropRect是否在有效范围内
    guard cropRect.origin.x + cropRect.width <= actualVideoSize.width,
          cropRect.origin.y + cropRect.height <= actualVideoSize.height else {
        // 裁剪区域超出视频范围,调整或报错
        return
    }
    
  • 视频旋转导致的变换错误
    如果视频带有旋转属性(比如竖屏拍摄的视频),preferredTransform会改变视频的显示方向,此时直接平移裁剪后的图像会出现错位。可以结合视频的transform调整最终输出:

    // 获取视频的旋转角度对应的变换
    let videoTransform = videoTrack.preferredTransform
    // 先做裁剪平移,再应用视频原变换
    let finalImage = imageAtOrigin.transformed(by: videoTransform)
    request.finish(with: finalImage, context: nil)
    
  • Asset未加载完成就创建合成
    直接通过URL创建AVPlayerItem时,asset可能还未完成加载,导致获取的track信息错误。改成异步加载asset的tracks后再创建视频合成:

    let asset = AVAsset(url: nextVideoItem.url)
    asset.loadValuesAsynchronously(forKeys: ["tracks"]) {
        DispatchQueue.main.async {
            guard asset.statusOfValue(forKey: "tracks", error: nil) == .loaded else {
                // 加载失败处理
                return
            }
            let item = AVPlayerItem(asset: asset)
            // 在这里创建cropComposition并设置给item
            let cropRect = CGRect(x: 200, y: 200, width: 300, height: 300)
            let cropComposition = AVMutableVideoComposition(asset: asset, applyingCIFiltersWithHandler: { request in
                // 裁剪逻辑...
            })
            cropComposition.renderSize = cropRect.size
            item.videoComposition = cropComposition
            self.player.replaceCurrentItem(with: item)
            self.player.play()
        }
    }
    
  • 验证CIFilter输出结果
    在request.finish前,临时渲染裁剪后的图像,确认滤镜处理是否符合预期:

    let context = CIContext()
    if let outputImage = cropFilter.outputImage,
       let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
        let testImage = UIImage(cgImage: cgImage)
        print("裁剪后图像尺寸:\(testImage.size)")
        // 可以把testImage显示到临时UIImageView中直观查看
    }
    

内容的提问来源于stack exchange,提问作者NFC.cool

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 20:15:32