如何解决我的视频只有 (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):
还有一点:
查看 QuickTime 检查器中的“当前比例”信息。视频显示正常,但显示为真正放大(请注意,没有像素模糊或任何其他内容,这与某些元数据有关)
我在 QuickTime 中处理的视频文件:
来自互联网的视频文件:
问题
- 该信息是什么元数据以及如何处理它?
- 为什么在原始曲目中与在不同的作品中不同?
- 如何在此类视频中提取帧?
解决方法
因此,如果您偶然发现了这篇文章,那可能是您正在尝试了解 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 举报,一经查实,本站将立刻删除。