- video_player: ExoPlayer (Android) / AVPlayer (iOS/macOS) backend with PixelBufferSink, method-channel adapter, Dart-side XVideoPlayer + testing fake. - insets: XInsets singleton + XAnimatedInsets widget lerp the system viewPadding over 220ms so OS bar visibility toggles (immersiveSticky <-> edgeToEdge) slide bottom-/top-anchored UI into place instead of snapping by the nav-bar / status-bar height.
56 lines
1.7 KiB
Swift
56 lines
1.7 KiB
Swift
import CoreVideo
|
|
import Foundation
|
|
#if canImport(UIKit)
|
|
import Flutter
|
|
#else
|
|
import FlutterMacOS
|
|
#endif
|
|
|
|
/// Single-slot latest-pixel-buffer sink that feeds a `FlutterTexture`.
|
|
/// Mirrors `PreviewSink` in `darwin/Camera/` — receiver writes, the
|
|
/// engine reads via `copyPixelBuffer()`. We retain only the most
|
|
/// recent buffer; older ones get released the moment a new frame
|
|
/// arrives, bounding memory at one frame.
|
|
final class UxVideoPixelBufferSink: NSObject, FlutterTexture {
|
|
private weak var registry: FlutterTextureRegistry?
|
|
private var textureId: Int64 = -1
|
|
|
|
private let bufferQueue = DispatchQueue(
|
|
label: "ux.video.buffer",
|
|
qos: .userInitiated
|
|
)
|
|
private var latestPixelBuffer: CVPixelBuffer?
|
|
|
|
func register(with registry: FlutterTextureRegistry) -> Int64 {
|
|
self.registry = registry
|
|
textureId = registry.register(self)
|
|
return textureId
|
|
}
|
|
|
|
func unregister() {
|
|
registry?.unregisterTexture(textureId)
|
|
bufferQueue.sync { latestPixelBuffer = nil }
|
|
}
|
|
|
|
// MARK: - FlutterTexture
|
|
|
|
func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
|
|
var pb: CVPixelBuffer?
|
|
bufferQueue.sync {
|
|
pb = latestPixelBuffer
|
|
latestPixelBuffer = nil
|
|
}
|
|
if let pb = pb { return Unmanaged.passRetained(pb) }
|
|
return nil
|
|
}
|
|
|
|
/// Receives a new frame from the AVPlayerItemVideoOutput pump.
|
|
/// Cheap — just swaps the pointer + notifies the registry.
|
|
func deliver(pixelBuffer: CVPixelBuffer) {
|
|
bufferQueue.sync { latestPixelBuffer = pixelBuffer }
|
|
if let registry = registry {
|
|
registry.textureFrameAvailable(textureId)
|
|
}
|
|
}
|
|
}
|