[WIP] Call UI

This commit is contained in:
Isaac
2023-12-08 22:33:07 +04:00
parent a6b140d599
commit d1e9e04dc1
27 changed files with 847 additions and 170 deletions

View File

@@ -17,6 +17,7 @@ import ImageBlur
import TelegramVoip
import MetalEngine
import DeviceAccess
import LibYuvBinding
final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeProtocol {
private let sharedContext: SharedAccountContext
@@ -45,6 +46,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
var callEnded: ((Bool) -> Void)?
var dismissedInteractively: (() -> Void)?
var dismissAllTooltips: (() -> Void)?
var restoreUIForPictureInPicture: ((@escaping (Bool) -> Void) -> Void)?
private var emojiKey: (data: Data, resolvedKey: [String])?
private var validLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat)?
@@ -117,6 +119,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
return
}
self.back?()
self.callScreen.beginPictureInPictureIfPossible()
}
self.callScreen.closeAction = { [weak self] in
guard let self else {
@@ -124,6 +127,13 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
}
self.dismissedInteractively?()
}
self.callScreen.restoreUIForPictureInPicture = { [weak self] completion in
guard let self else {
completion(false)
return
}
self.restoreUIForPictureInPicture?(completion)
}
self.callScreenState = PrivateCallScreen.State(
lifecycleState: .connecting,
@@ -312,6 +322,9 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
case let .active(startTime, signalQuality, keyData):
self.callStartTimestamp = startTime
var signalQuality = signalQuality
signalQuality = 4
let _ = keyData
mappedLifecycleState = .active(PrivateCallScreen.State.ActiveState(
startTime: startTime + kCFAbsoluteTimeIntervalSince1970,
@@ -322,7 +335,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
let _ = keyData
mappedLifecycleState = .active(PrivateCallScreen.State.ActiveState(
startTime: startTime + kCFAbsoluteTimeIntervalSince1970,
signalInfo: PrivateCallScreen.State.SignalInfo(quality: 0.0),
signalInfo: PrivateCallScreen.State.SignalInfo(quality: 1.0),
emojiKey: self.resolvedEmojiKey(data: keyData)
))
case .terminating, .terminated:
@@ -518,6 +531,7 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
self.callScreen.update(
size: layout.size,
insets: layout.insets(options: [.statusBar]),
interfaceOrientation: layout.metrics.orientation ?? .portrait,
screenCornerRadius: layout.deviceMetrics.screenCornerRadius,
state: callScreenState,
transition: Transition(transition)
@@ -526,7 +540,100 @@ final class CallControllerNodeV2: ViewControllerTracingNode, CallControllerNodeP
}
}
private func copyI420BufferToNV12Buffer(buffer: OngoingGroupCallContext.VideoFrameData.I420Buffer, pixelBuffer: CVPixelBuffer) -> Bool {
guard CVPixelBufferGetPixelFormatType(pixelBuffer) == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange else {
return false
}
guard CVPixelBufferGetWidthOfPlane(pixelBuffer, 0) == buffer.width else {
return false
}
guard CVPixelBufferGetHeightOfPlane(pixelBuffer, 0) == buffer.height else {
return false
}
let cvRet = CVPixelBufferLockBaseAddress(pixelBuffer, [])
if cvRet != kCVReturnSuccess {
return false
}
defer {
CVPixelBufferUnlockBaseAddress(pixelBuffer, [])
}
guard let dstY = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0) else {
return false
}
let dstStrideY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
guard let dstUV = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1) else {
return false
}
let dstStrideUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
buffer.y.withUnsafeBytes { srcYBuffer in
guard let srcY = srcYBuffer.baseAddress else {
return
}
buffer.u.withUnsafeBytes { srcUBuffer in
guard let srcU = srcUBuffer.baseAddress else {
return
}
buffer.v.withUnsafeBytes { srcVBuffer in
guard let srcV = srcVBuffer.baseAddress else {
return
}
libyuv_I420ToNV12(
srcY.assumingMemoryBound(to: UInt8.self),
Int32(buffer.strideY),
srcU.assumingMemoryBound(to: UInt8.self),
Int32(buffer.strideU),
srcV.assumingMemoryBound(to: UInt8.self),
Int32(buffer.strideV),
dstY.assumingMemoryBound(to: UInt8.self),
Int32(dstStrideY),
dstUV.assumingMemoryBound(to: UInt8.self),
Int32(dstStrideUV),
Int32(buffer.width),
Int32(buffer.height)
)
}
}
}
return true
}
private final class AdaptedCallVideoSource: VideoSource {
final class I420DataBuffer: Output.DataBuffer {
private let buffer: OngoingGroupCallContext.VideoFrameData.I420Buffer
override var pixelBuffer: CVPixelBuffer? {
let ioSurfaceProperties = NSMutableDictionary()
let options = NSMutableDictionary()
options.setObject(ioSurfaceProperties, forKey: kCVPixelBufferIOSurfacePropertiesKey as NSString)
var pixelBuffer: CVPixelBuffer?
CVPixelBufferCreate(
kCFAllocatorDefault,
self.buffer.width,
self.buffer.height,
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
options,
&pixelBuffer
)
if let pixelBuffer, copyI420BufferToNV12Buffer(buffer: buffer, pixelBuffer: pixelBuffer) {
return pixelBuffer
} else {
return nil
}
}
init(buffer: OngoingGroupCallContext.VideoFrameData.I420Buffer) {
self.buffer = buffer
super.init()
}
}
private static let queue = Queue(name: "AdaptedCallVideoSource")
private var onUpdatedListeners = Bag<() -> Void>()
private(set) var currentOutput: Output?
@@ -555,6 +662,8 @@ private final class AdaptedCallVideoSource: VideoSource {
rotationAngle = Float.pi * 3.0 / 2.0
}
let followsDeviceOrientation = videoFrameData.deviceRelativeOrientation != nil
var mirrorDirection: Output.MirrorDirection = []
var sourceId: Int = 0
@@ -616,12 +725,45 @@ private final class AdaptedCallVideoSource: VideoSource {
output = Output(
resolution: CGSize(width: CGFloat(yTexture.width), height: CGFloat(yTexture.height)),
y: yTexture,
uv: uvTexture,
textureLayout: .biPlanar(Output.BiPlanarTextureLayout(
y: yTexture,
uv: uvTexture
)),
dataBuffer: Output.NativeDataBuffer(pixelBuffer: nativeBuffer.pixelBuffer),
rotationAngle: rotationAngle,
followsDeviceOrientation: followsDeviceOrientation,
mirrorDirection: mirrorDirection,
sourceId: sourceId
)
case let .i420(i420Buffer):
let width = i420Buffer.width
let height = i420Buffer.height
let _ = width
let _ = height
return
/*var cvMetalTextureY: CVMetalTexture?
var status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, nativeBuffer.pixelBuffer, nil, .r8Unorm, width, height, 0, &cvMetalTextureY)
guard status == kCVReturnSuccess, let yTexture = CVMetalTextureGetTexture(cvMetalTextureY!) else {
return
}
var cvMetalTextureUV: CVMetalTexture?
status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, nativeBuffer.pixelBuffer, nil, .rg8Unorm, width / 2, height / 2, 1, &cvMetalTextureUV)
guard status == kCVReturnSuccess, let uvTexture = CVMetalTextureGetTexture(cvMetalTextureUV!) else {
return
}
output = Output(
resolution: CGSize(width: CGFloat(yTexture.width), height: CGFloat(yTexture.height)),
y: yTexture,
uv: uvTexture,
dataBuffer: Output.NativeDataBuffer(pixelBuffer: nativeBuffer.pixelBuffer),
rotationAngle: rotationAngle,
followsDeviceOrientation: followsDeviceOrientation,
mirrorDirection: mirrorDirection,
sourceId: sourceId
)*/
default:
return
}