claude's first attempt
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import AVFoundation
|
||||
|
||||
struct VideoProber {
|
||||
static func probe(url: URL) async throws -> [String: Any] {
|
||||
let asset = AVURLAsset(url: url)
|
||||
|
||||
let duration = try await asset.load(.duration)
|
||||
let tracks = try await asset.loadTracks(withMediaType: .video)
|
||||
|
||||
guard let videoTrack = tracks.first else {
|
||||
throw NSError(
|
||||
domain: "ExpoVideoCompress",
|
||||
code: 1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "No video track found"]
|
||||
)
|
||||
}
|
||||
|
||||
let naturalSize = try await videoTrack.load(.naturalSize)
|
||||
let preferredTransform = try await videoTrack.load(.preferredTransform)
|
||||
let estimatedDataRate = try await videoTrack.load(.estimatedDataRate)
|
||||
let nominalFrameRate = try await videoTrack.load(.nominalFrameRate)
|
||||
let formatDescriptions = try await videoTrack.load(.formatDescriptions)
|
||||
|
||||
// Determine codec from format descriptions
|
||||
var codec = "unknown"
|
||||
if let formatDescription = formatDescriptions.first {
|
||||
let mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription)
|
||||
codec = fourCCToString(mediaSubType)
|
||||
}
|
||||
|
||||
// Detect rotation from transform
|
||||
let rotation = rotationFromTransform(preferredTransform)
|
||||
|
||||
// Apply rotation to get display size
|
||||
let isRotated = rotation == 90 || rotation == 270
|
||||
let width = isRotated ? Int(naturalSize.height) : Int(naturalSize.width)
|
||||
let height = isRotated ? Int(naturalSize.width) : Int(naturalSize.height)
|
||||
|
||||
// Check for audio track
|
||||
let audioTracks = try await asset.loadTracks(withMediaType: .audio)
|
||||
let hasAudio = !audioTracks.isEmpty
|
||||
|
||||
// Get file size
|
||||
let fileSize: Int
|
||||
if let attributes = try? FileManager.default.attributesOfItem(atPath: url.path),
|
||||
let size = attributes[.size] as? Int {
|
||||
fileSize = size
|
||||
} else {
|
||||
fileSize = 0
|
||||
}
|
||||
|
||||
// Bitrate: use estimated data rate, or calculate from file size
|
||||
let durationSeconds = CMTimeGetSeconds(duration)
|
||||
var bitrate = Int(estimatedDataRate)
|
||||
if bitrate == 0 && durationSeconds > 0 {
|
||||
bitrate = Int(Double(fileSize * 8) / durationSeconds)
|
||||
}
|
||||
|
||||
return [
|
||||
"width": width,
|
||||
"height": height,
|
||||
"duration": durationSeconds,
|
||||
"bitrate": bitrate,
|
||||
"fileSize": fileSize,
|
||||
"codec": codec,
|
||||
"hasAudio": hasAudio,
|
||||
"frameRate": nominalFrameRate,
|
||||
"rotation": rotation
|
||||
]
|
||||
}
|
||||
|
||||
private static func rotationFromTransform(_ transform: CGAffineTransform) -> Int {
|
||||
let angle = atan2(transform.b, transform.a)
|
||||
let degrees = Int(round(angle * 180.0 / .pi))
|
||||
// Normalize to 0, 90, 180, 270
|
||||
let normalized = ((degrees % 360) + 360) % 360
|
||||
return normalized
|
||||
}
|
||||
|
||||
private static func fourCCToString(_ code: FourCharCode) -> String {
|
||||
let chars: [Character] = [
|
||||
Character(UnicodeScalar((code >> 24) & 0xFF)!),
|
||||
Character(UnicodeScalar((code >> 16) & 0xFF)!),
|
||||
Character(UnicodeScalar((code >> 8) & 0xFF)!),
|
||||
Character(UnicodeScalar(code & 0xFF)!)
|
||||
]
|
||||
return String(chars).trimmingCharacters(in: .whitespaces)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user