Мои видео имеют натуральный размер всего (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}

Вот фактический скриншот игровой площадки (которую вы можете скачать здесь ):

введите здесь описание изображения

А вот еще кое-что:

Посмотрите информацию о текущем масштабе в инспекторе QuickTime. Видео отображается просто отлично, но оно показано как действительно увеличенное (обратите внимание, что ни один пиксель не размыт или что-то в этом роде, это связано с некоторыми метаданными)

Видеофайл, с которым я работаю в QuickTime:

введите здесь описание изображения

Видеофайл из Интернета:

введите здесь описание изображения

Вопрос

  • Какие метаданные представляет собой эта информация и как с ней обращаться?
  • Почему на оригинальном треке он отличается от того, когда он наложен на другую композицию?
  • Как я могу извлечь кадр из таких видео?

person Yann Bizeul    schedule 12.03.2021    source источник


Ответы (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
    }
}
person Yann Bizeul    schedule 25.03.2021