我的视频只有 (4.0, 3.0) 像素的自然大小,这也是提取的帧大小
My videos have a naturalSize of only (4.0, 3.0) pixels, which is also extracted frame size
上下文
我正在处理 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}
这是 playground 的实际截图(您可以下载 here):
这里还有一些东西:
查看 QuickTime 检查器中的“当前比例”信息。视频显示很好,但显示确实被放大了(注意没有像素模糊之类的,这与一些元数据有关)
我在 QuickTime 中处理的视频文件:
视频文件来自网络:
问题
- 该信息是什么元数据以及如何处理它?
- 为什么在原来的轨道上与在不同的合成上不同?
- 如何提取此类视频的帧?
所以如果你无意中看到这个post,那可能是你想弄明白特斯拉写视频的方式。
该问题没有简单的解决方案,这是由于 Tesla 软件在 .mov
视频文件中错误设置元数据造成的。我向 Apple 发起了一个事件,他们能够证实这一点。
所以我写了一些代码,通过重写指示视频轨道大小的字节来实际修复视频文件。
我们开始吧,它很难看,但为了完整起见,我想 post a 解决方案,如果不是最好的话。
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 { [=10=].load(fromByteOffset: 76, as: UInt16.self).bigEndian }
let height = data?.withUnsafeBytes { [=10=].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 { [=10=].load(as: UInt32.self) }
//print(i32array)
return i32array
}
}
上下文
我正在处理 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}
这是 playground 的实际截图(您可以下载 here):
这里还有一些东西:
查看 QuickTime 检查器中的“当前比例”信息。视频显示很好,但显示确实被放大了(注意没有像素模糊之类的,这与一些元数据有关)
我在 QuickTime 中处理的视频文件:
视频文件来自网络:
问题
- 该信息是什么元数据以及如何处理它?
- 为什么在原来的轨道上与在不同的合成上不同?
- 如何提取此类视频的帧?
所以如果你无意中看到这个post,那可能是你想弄明白特斯拉写视频的方式。
该问题没有简单的解决方案,这是由于 Tesla 软件在 .mov
视频文件中错误设置元数据造成的。我向 Apple 发起了一个事件,他们能够证实这一点。
所以我写了一些代码,通过重写指示视频轨道大小的字节来实际修复视频文件。
我们开始吧,它很难看,但为了完整起见,我想 post a 解决方案,如果不是最好的话。
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 { [=10=].load(fromByteOffset: 76, as: UInt16.self).bigEndian }
let height = data?.withUnsafeBytes { [=10=].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 { [=10=].load(as: UInt32.self) }
//print(i32array)
return i32array
}
}