我的视频的 naturalSize 仅为 (4.0, 3.0) 像素,这也是提取的帧大小

Posted

技术标签:

【中文标题】我的视频的 naturalSize 仅为 (4.0, 3.0) 像素,这也是提取的帧大小【英文标题】:My videos have a naturalSize of only (4.0, 3.0) pixels, which is also extracted frame size 【发布时间】:2021-03-12 09:54:02 【问题描述】:

上下文

我正在处理 1280x920 的视频文件,这是它们在 QuickTime 中显示或什至在我的 AVPlayer 中播放时的实际像素大小。

我的文件夹中有一堆视频,我需要将它们放在AVMutableComposition 上并播放。

我还需要为每个视频提取最后一帧。

到目前为止,我所做的是在我的个人 AVAsset 上使用 AVAssetImageGenerator,并且无论我使用的是 generateCGImagesAsynchronously 还是 copyCGImage,它都有效。

但我认为在我的作曲资产上运行 generateCGImagesAsynchronously 会更有效,所以我只有一个调用,而不是循环每个原始轨道。

而不是:

                   v-Get Frame
AVAsset1 |---------|
AVAsset2 |---------|
AVAsset3 |---------|

我想做:

                                v----------v----------v- Get Frames
AVMutableComposition: |---------||---------||---------|

问题

这是实际问题:

import AVKit

var video1URL = URL(fileReferenceLiteralResourceName: "video_bad.mp4") // One of my video file
let asset1 = AVAsset(url: video1URL)
let track1 = asset1.tracks(withMediaType: .video).first!

_ = track1.naturalSize // w 4 h 3

var video2URL = URL(fileReferenceLiteralResourceName: "video_ok.mp4") // Some mp4 I got from internet
let asset2 = AVAsset(url: video2URL)
let track2 = asset2.tracks(withMediaType: .video).first!

_ = track2.naturalSize // w 1920 h 1080

这是操场的实际截图(您可以下载here):

还有其他的:

查看 QuickTime 检查器中的“当前比例”信息。视频显示很好,但显示为被放大了(注意没有像素模糊或任何东西,它与一些元数据有关)

我在 QuickTime 中使用的视频文件:

来自网络的视频文件:

问题

这些信息是什么元数据以及如何处理它? 为什么它在原始轨道上与放在不同乐曲上时不同? 如何在此类视频中提取帧?

【问题讨论】:

【参考方案1】:

因此,如果您偶然发现这篇文章,可能是您正在尝试弄清楚特斯拉的视频编写方式。

该问题没有简单的解决方案,这是由 Tesla 软件错误地设置 .mov 视频文件中的元数据引起的。我向 Apple 提出了一个事件,他们能够证实这一点。

所以我写了一些代码,通过重写指示视频轨道大小的字节来实际修复视频文件。

我们开始吧,它很丑,但为了完整起见,我想在这里发布一个解决方案,如果不是最好的。

import Foundation

struct VideoFixer 
    var url: URL
    
    private var fh: FileHandle?
    
    static func fix(_ url: URL) 
        var fixer = VideoFixer(url)
        fixer.fix()
    
    
    init(_ url: URL) 
        self.url = url
    
    
    mutating func fix() 
        guard let fh = try? FileHandle(forUpdating: url) else 
            return
        
        
        var atom = Atom(fh)
        atom.seekTo(AtomType.moov)
        atom.enter()
        if atom.atom_type != AtomType.trak 
            atom.seekTo(AtomType.trak)
        
        atom.enter()
        if atom.atom_type != AtomType.tkhd 
            atom.seekTo(AtomType.tkhd)
        
        atom.seekTo(AtomType.tkhd)
        
        let data = atom.data()
        
        let width = data?.withUnsafeBytes  $0.load(fromByteOffset: 76, as: UInt16.self).bigEndian 
        let height = data?.withUnsafeBytes  $0.load(fromByteOffset: 80, as: UInt16.self).bigEndian 
        
        if width==4 && height==3 
            guard let offset = try? fh.offset() else 
                return
            
            try? fh.seek(toOffset: offset+76)
            //1280x960
            var newWidth = UInt16(1280).byteSwapped
            var newHeight = UInt16(960).byteSwapped
            let dataWidth = Data(bytes: &newWidth, count: 2)
            let dataHeight = Data(bytes: &newHeight, count: 2)
            fh.write(dataWidth)
            try? fh.seek(toOffset: offset+80)
            fh.write(dataHeight)
        
        try? fh.close()

        
    


typealias AtomType = UInt32

extension UInt32 
    static var ftyp = UInt32(1718909296)
    static var mdat = UInt32(1835295092)
    static var free = UInt32(1718773093)
    static var moov = UInt32(1836019574)
    static var trak = UInt32(1953653099)
    static var tkhd = UInt32(1953196132)


struct Atom 
    var fh: FileHandle
    
    var atom_size: UInt32 = 0
    var atom_type: UInt32 = 0
    
    init(_ fh: FileHandle) 
        self.fh = fh
        
        self.read()
    
    mutating func seekTo(_ type:AtomType) 
        while self.atom_type != type 
            self.next()
        
    
    mutating func next() 
        guard var offset = try? fh.offset() else 
            return
        
        
        offset = offset-8+UInt64(atom_size)
        
        if (try? self.fh.seek(toOffset: UInt64(offset))) == nil 
            return
        
        self.read()
    
    mutating func read() 
        self.atom_size = fh.nextUInt32().bigEndian
        self.atom_type = fh.nextUInt32().bigEndian
    
    mutating func enter() 
        self.atom_size = fh.nextUInt32().bigEndian
        self.atom_type = fh.nextUInt32().bigEndian
    
    func data() -> Data? 
        guard let offset = try? fh.offset() else 
            return nil
        
        let data = fh.readData(ofLength: Int(self.atom_size))
        try? fh.seek(toOffset: offset)
        return data
    

extension FileHandle 
    func nextUInt32() -> UInt32 
        let data = self.readData(ofLength: 4)
        let i32array = data.withUnsafeBytes  $0.load(as: UInt32.self) 
        //print(i32array)
        return i32array
    

【讨论】:

以上是关于我的视频的 naturalSize 仅为 (4.0, 3.0) 像素,这也是提取的帧大小的主要内容,如果未能解决你的问题,请参考以下文章

如何仅为从 iOS 中的照片库中选择的视频设置 UIImagePickerController 的allowEditing?

在目标 c 中以编程方式获取视频的尺寸

YouTube V3 API 配额超出每日限额仅为 10k

图片相关

在 python 中使用 OpenCV 2.4.0 创建视频

在 quickblox 中设置视频通话时间