Add expo-bluesky-video-compress native module
Replaces react-native-compressor's video path with a local Expo module. Native pipeline: - iOS: AVAssetReader + AVMutableVideoComposition + AVAssetWriter with VideoToolbox encode, BGRA reader for HDR -> SDR conversion, BT.709 color tagging, DataRateLimits hard cap, AAC re-encode. - Android: MediaExtractor -> MediaCodec(decoder) -> GL pipeline -> MediaCodec(encoder) -> MediaMuxer. BITRATE_MODE_CBR for tight target enforcement, GL transform-matrix rotation (no double-rotation), raw AAC passthrough, hardware encoder selection with software fallback, QTI AVC denylist. Codec selection: 'auto' resolves to h264 (HLS pipeline + licensing). HEVC remains opt-in via codec: 'hevc' for future feature-flagged use. compress.ts adds probe-based smart-skip: clips that are already at or below 5 Mbps / 1920px / 100MB bypass re-encoding entirely. react-native-compressor stays as a dep for now since pickVideo and VideoTranscodeBackdrop still use its non-compression helpers; full removal is a follow-up.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import AVFoundation
|
||||
import ExpoModulesCore
|
||||
|
||||
public class ExpoBlueskyVideoCompressModule: Module {
|
||||
private var currentCompressor: VideoCompressor?
|
||||
|
||||
public func definition() -> ModuleDefinition {
|
||||
Name("ExpoBlueskyVideoCompress")
|
||||
|
||||
Events("onProgress")
|
||||
|
||||
AsyncFunction("probe") { (uri: String) -> [String: Any] in
|
||||
let url = URL(string: uri) ?? URL(fileURLWithPath: uri)
|
||||
return try await VideoProber.probe(url: url)
|
||||
}
|
||||
|
||||
AsyncFunction("compress") { (uri: String, options: [String: Any]) -> [String: Any] in
|
||||
let url = URL(string: uri) ?? URL(fileURLWithPath: uri)
|
||||
let targetBitrate = options["targetBitrate"] as? Int ?? 0
|
||||
let maxSize = options["maxSize"] as? Int ?? 1920
|
||||
let codecPref = options["codec"] as? String ?? "auto"
|
||||
let frameRateCap = options["frameRateCap"] as? Int ?? 30
|
||||
let jobId = options["jobId"] as? Int ?? 0
|
||||
|
||||
let compressor = VideoCompressor(
|
||||
url: url,
|
||||
targetBitrate: targetBitrate,
|
||||
maxSize: maxSize,
|
||||
codecPref: codecPref,
|
||||
frameRateCap: frameRateCap,
|
||||
jobId: jobId,
|
||||
onProgress: { [weak self] id, progress in
|
||||
self?.sendEvent("onProgress", [
|
||||
"id": id,
|
||||
"progress": progress,
|
||||
])
|
||||
}
|
||||
)
|
||||
|
||||
self.currentCompressor = compressor
|
||||
|
||||
do {
|
||||
let result = try await compressor.compress()
|
||||
self.currentCompressor = nil
|
||||
return result
|
||||
} catch {
|
||||
self.currentCompressor = nil
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
Function("cancel") {
|
||||
self.currentCompressor?.cancel()
|
||||
self.currentCompressor = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user