AVURLAsset获取视频尺寸

38

这真的很令人沮丧。我试图获取一个AVURLasset的大小,但尝试避免使用naturalSize,因为Xcode告诉我在iOS5中已经被弃用。

但是:有什么替代方法吗?

我找不到任何线索,不使用«naturalsize»如何获取视频尺寸...

9个回答

54

在 Swift 3 中的分辨率:

func resolutionSizeForLocalVideo(url:NSURL) -> CGSize? {
    guard let track = AVAsset(URL: url).tracksWithMediaType(AVMediaTypeVideo).first else { return nil }
    let size = CGSizeApplyAffineTransform(track.naturalSize, track.preferredTransform)
    return CGSize(width: fabs(size.width), height: fabs(size.height))
}

针对 Swift 4 版本:

func resolutionSizeForLocalVideo(url:NSURL) -> CGSize? {
    guard let track = AVAsset(url: url as URL).tracks(withMediaType: AVMediaType.video).first else { return nil }
    let size = track.naturalSize.applying(track.preferredTransform)
    return CGSize(width: fabs(size.width), height: fabs(size.height))
}

没有 preferredTransform 的解决方案在最新设备上无法为某些视频返回正确的值!


34
我刚刚在线查看了文档,发现AVAsset对象的“naturalSize”方法已被弃用。但是,应该始终存在一个引用AVAsset的AVAssetTrack,并且AVAssetTrack具有可以调用的“naturalSize”方法。 naturalSize
媒体数据的自然尺寸。 (只读)
@property(nonatomic, readonly) CGSize naturalSize 可用性 在iOS 4.0及更高版本中可用。在AVAssetTrack.h中声明。
来源: iOS的AVAssetTrack参考

nautralSize 在 iOS 5 中已被弃用。 - Raptor
刚刚查看了答案中链接的文档,它并没有被弃用,在 ObjC 和 Swift 中都可用。 - C4 - Travis
15
文档中有两个名为naturalSize的属性。AVURLAsset.naturalSize已被弃用,但是[[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize]没有。 - Raptor
我的答案指定了AVAssetTrack方法,这个方法并没有被弃用,正如Dave在下面的第二行代码中所指定的那样...看起来我只是忘记在我的答案中添加这一行代码了。 - C4 - Travis
所以现在每个人都转而做与被弃用的方法基本相同的事情。太好了。 - sudo
你有没有想过如何更改所选视频的分辨率?我需要使用AVAssetExportPreset1280x720吗? - user2786

26

官方文档上的废弃警告建议: "如适用,使用资产视频轨道的naturalSizepreferredTransform(另请参见tracksWithMediaType:)"。

我从以下代码进行了更改:

CGSize size = [movieAsset naturalSize];
"to"
CGSize size = [[[movieAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] naturalSize];

现在不太美观也不太安全,但是当他们使用该方法时不会崩溃。


17

这个弃用警告的意思是:

Use the naturalSize and preferredTransform, as appropriate, 
of the asset’s video tracks instead (see also tracksWithMediaType:).

我们需要一个AVAssetTrack,并且想要它的naturalSizepreferredTransform。可以通过以下方式访问:

AVAssetTrack *track = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
CGSize dimensions = CGSizeApplyAffineTransform(track.naturalSize, track.preferredTransform);

资产显然是您的AVAsset


4
按照您建议的方式应用变换后,我的宽度为负数 :( - Goz
5
请使用 fabsf() 函数对尺寸组件进行归一化处理。 - Ja͢ck

5
这是一个相对简单的 AVAsset 扩展,用于在 Swift 4 中获取视频的大小(如果可用):
extension AVAsset {
    var screenSize: CGSize? {
        if let track = tracks(withMediaType: .video).first {
            let size = __CGSizeApplyAffineTransform(track.naturalSize, track.preferredTransform)
            return CGSize(width: fabs(size.width), height: fabs(size.height))
        } 
        return nil
    }
}

谢谢,该应用是用Objective-C编写的,虽然我还没有重写它。我会检查一下是否能够使用您的扩展... - Swissdude
嗨,我也遇到了同样的问题,你找到解决方案了吗?因为在我的情况下,使用自然大小+首选变换返回了过时的值:(谢谢。 - sarra.srairi

3
为了获取 AVAsset 的尺寸,您需要计算所有视觉轨道矩形的并集(在应用它们的对应首选转换之后):
CGRect unionRect = CGRectZero;
for (AVAssetTrack *track in [asset tracksWithMediaCharacteristic:AVMediaCharacteristicVisual]) {
    CGRect trackRect = CGRectApplyAffineTransform(CGRectMake(0.f,
                                                             0.f,
                                                             track.naturalSize.width,
                                                             track.naturalSize.height),
                                                  track.preferredTransform);
    unionRect = CGRectUnion(unionRect, trackRect);
}
CGSize naturalSize = unionRect.size;

使用依赖于 CGSizeApplyAffineTransform 的方法,当您的资源包含具有非平凡仿射变换(例如45度旋转)的轨道时会失败,或者如果您的资源包含带有不同起点的轨道(例如,两个轨道并排播放,并且第二个轨道的起点增加了第一个轨道的宽度)。

参见:MediaPlayerPrivateAVFoundationCF::sizeChanged(),位于 https://opensource.apple.com/source/WebCore/WebCore-7536.30.2/platform/graphics/avfoundation/cf/MediaPlayerPrivateAVFoundationCF.cpp


虽然对于包含一个视频轨道的AVAsset,@Avt的答案可以正常工作。对吧? - Roi Mulia
1
@RoiMulia:我认为资产具有单个视频轨道和非零偏移量是不常见的。因此,当使用该代码时,您可能永远不会看到该错误。此外,我曾经看到过一些播放器在资产仅包含一个轨道时忽略偏移量。如果您关心处理这些边缘情况,我建议您自己进行实验以验证实际行为。 - David H

3

对于 iOS 版本 15.0 及以上,

extension AVAsset {
    func naturalSize() async -> CGSize? {
        guard let tracks = try? await loadTracks(withMediaType: .video) else { return nil }
        guard let track = tracks.first else { return nil }
        guard let size = try? await track.load(.naturalSize) else { return nil }
        return size
    }
}

如何在viewDidLoad()中使用它? - Md. Ikramul Murad
@Md.IkramulMurad,编辑了原始帖子 - undefined

3

针对Swift 5版本

let assetSize =  asset.tracks(withMediaType: .video)[0].naturalSize

1

Swift 版本的 @David_H 的答案。

extension AVAsset {
    func resolutionSizeForLocalVideo() -> CGSize {
        var unionRect = CGRect.zero;
        for track in self.tracks(withMediaCharacteristic: .visual) {
            let trackRect = CGRect(x: 0, y: 0, width: 
            track.naturalSize.width, height: 
            track.naturalSize.height).applying(track.preferredTransform)
            unionRect = unionRect.union(trackRect)
            
        }
        return unionRect.size
    }
}


为什么它返回可选项? - Evgeny Karkan
它不会返回nil,因为我已经将unionRect设置为CGRect.zero。我已经编辑过了。 - sagarthecoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接