微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

我的视频只有 (4.0, 3.0) 像素的自然大小,这也是提取的帧大小

如何解决我的视频只有 (4.0, 3.0) 像素的自然大小,这也是提取的帧大小

上下文

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

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

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

到目前为止,我所做的是在我的个人 AVAssetimageGenerator 上对每个人使用 AVAsset,并且无论我使用的是 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}

这是 Playground 的实际屏幕截图(您可以下载 here):

enter image description here

还有一点:

查看 QuickTime 检查器中的“当前比例”信息。视频显示正常,但显示为真正放大(请注意,没有像素模糊或任何其他内容,这与某些元数据有关)

我在 QuickTime 中处理的视频文件

enter image description here

来自互联网的视频文件

enter image description here

问题

  • 该信息是什么元数据以及如何处理它?
  • 为什么在原始曲目中与在不同的作品中不同?
  • 如何在此类视频中提取帧?

解决方法

因此,如果您偶然发现了这篇文章,那可能是您正在尝试了解 Tesla 编写视频的方式。

该问题没有简单的解决方案,这是由 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
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。