From 2b51baa1981fb445b938e64bdce539e58fe70264 Mon Sep 17 00:00:00 2001
From: Runt <qingingrunt2010@qq.com>
Date: Sat, 26 Jul 2025 13:17:14 +0000
Subject: [PATCH] 小窗缩放, 相机转小窗显示 小窗旋转,相机画面旋转 关闭按钮优化
---
LiveProject/views/VideoRendererView.swift | 6 +
LiveProject/controller/CameraCapture.swift | 19 +++
LiveProject/enum/Icons.swift | 38 +++---
LiveProject/activity/stream/LiveViewModel.swift | 26 +++--
LiveProject/data/IconInfo.swift | 2
LiveProject/activity/stream/LiveActivity.swift | 47 +++++++--
LiveProject/activity/stream/views/MiniWindow.swift | 128 +++++++++++++++++++++----
LiveProject/tool/MetalRenderer.swift | 25 ++++
8 files changed, 223 insertions(+), 68 deletions(-)
diff --git a/LiveProject/activity/stream/LiveActivity.swift b/LiveProject/activity/stream/LiveActivity.swift
index 045e563..d8386f6 100644
--- a/LiveProject/activity/stream/LiveActivity.swift
+++ b/LiveProject/activity/stream/LiveActivity.swift
@@ -34,12 +34,19 @@
Color.clear
.ignoresSafeArea() // 填满全屏
ZStack{
- VideoRendererView(pixelBuffer: $mViewModel.pixelBuffer).background(Color.black).frame(width: mainSize.width,height:mainSize.height)
+ let optionalIntBinding = Binding<Int?>(
+ get: { 0 },
+ set: { newValue in }
+ )
+ VideoRendererView(pixelBuffer: $mViewModel.pixelBuffer,rotate: optionalIntBinding).background(Color.black).frame(width: mainSize.width,height:mainSize.height)
.frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topTrailing)
- ForEach(miniWindows, id: \.id) { miniWindow in
- NewMiniWindow(miniWindow: miniWindow)
- }
- }.border(Color.blue)
+
+ }.border(Color.blue).clipped()
+ .overlay(
+ ForEach(miniWindows, id: \.id) { miniWindow in
+ NewMiniWindow(miniData: miniWindow)
+ }
+ )
VStack{
Spacer()
@@ -64,6 +71,7 @@
}
})
.border(Color.red)
+ .clipped()
.onDisappear {
print("onDisappear 视图消失了!")
@@ -157,13 +165,29 @@
ForEach(devices, id: \.self) { device in
MButton(icon: device.icon,text: device.name){
- mViewModel.newWindowAction(device: device){ status in
+ var miniData = MiniWindowData(streamType: device.type);
+ switch device.type{
+ case .CAMERA:
+ miniData.hasAudio = false;
+ miniData.hasVideo = true;
+ break;
+ case .MICROPHONE:
+ miniData.hasAudio = true;
+ miniData.hasVideo = false;
+ break;
+ default:
+ break
+ }
+ miniData.mainSize = mainSize
+ miniData.name = device.name;
+ mViewModel.newWindowAction(minidata: miniData){ status in
withAnimation{
showDeviceDialog = false;
}
- miniWindows.append(MiniWindowData(streamType: device.type))
+
+ miniWindows.append(miniData);
}
- print("\(device.name) click \(self.miniWindows.count)")
+ print("\(device.name) \(device.type) click \(self.miniWindows.count)")
}
}
}
@@ -228,12 +252,13 @@
}
}
- func NewMiniWindow(miniWindow:MiniWindowData) -> some View{
- MiniWindow(miniData: miniWindow)
+ func NewMiniWindow(miniData:MiniWindowData) -> some View{
+ return MiniWindow(miniData: miniData)
.frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topLeading)
.onCloseClick {
- guard let index = miniWindows.firstIndex(where: { $0.id == miniWindow.id }) else { return }
+ guard let index = miniWindows.firstIndex(where: { $0.id == miniData.id }) else { return }
miniWindows.remove(at: index)
+ mViewModel.closeWindowAction(miniData: miniData)
}
}
diff --git a/LiveProject/activity/stream/LiveViewModel.swift b/LiveProject/activity/stream/LiveViewModel.swift
index bfc3671..bb31219 100644
--- a/LiveProject/activity/stream/LiveViewModel.swift
+++ b/LiveProject/activity/stream/LiveViewModel.swift
@@ -10,15 +10,15 @@
class LiveViewModel: ObservableObject {
@Published var pixelBuffer: CVPixelBuffer?
- let encoder = H264Encoder(width: 1080, height: 1920, fps: 30, bitrate: 1_000_000)
+ //let encoder = H264Encoder(width: 1080, height: 1920, fps: 30, bitrate: 1_000_000)
var frameIndex: Int64 = 0
- let encodeQueue = DispatchQueue(label: "encoder.queue")
+ //let encodeQueue = DispatchQueue(label: "encoder.queue")
lazy var camera = CameraCapture()
var timestamp = Int(Date().timeIntervalSince1970 * 1000)
- func newWindowAction(device:DeviceInfo,completion: @escaping (Bool) -> Void = {b in}){
- switch device.type{
+ func newWindowAction(minidata:MiniWindowData,completion: @escaping (Bool) -> Void = {b in}){
+ switch minidata.streamType{
case StreamType.CAMERA:
requestCameraPermission(mediaType: .video){ staus in
if(staus){
@@ -33,15 +33,18 @@
print("Invalid pixel buffer size: \(width)x\(height)")
return
}
-
+ if(minidata.size.width != CGFloat(width) || minidata.size.height != CGFloat(height)){
+ minidata.size = CGSize(width:width,height:height);
+ }
self.frameIndex += 1
let ts = Int(Date().timeIntervalSince1970 * 1000)
self.timestamp = ts;
let cmTime = CMTimeMake(value: Int64(CACurrentMediaTime() * 1000), timescale: 1000);
- self.encoder.encode(pixelBuffer: buffer, pts: cmTime)
+ //self.encoder.encode(pixelBuffer: buffer, pts: cmTime)
DispatchQueue.main.async {
- self.pixelBuffer = buffer;
+ minidata.pixelBuffer = buffer;
+
}
//print("画面更新")
}
@@ -49,12 +52,12 @@
self.camera.start()
}
print("启动相机")
- self.encoder.onEncoded = { (data: Data, ctime: CMTime, isKey: Bool) in
+ /*self.encoder.onEncoded = { (data: Data, ctime: CMTime, isKey: Bool) in
let timestamp2 = Int(Date().timeIntervalSince1970 * 1000)
print("编码时间2 \(timestamp2 - self.timestamp)")
print("Encoded NALU size: \(data.count), key frame: \(isKey)")
- }
+ }*/
}else{
}
@@ -67,10 +70,11 @@
}
}
- func closeWindowAction(device:DeviceInfo){
- switch device.type{
+ func closeWindowAction(miniData:MiniWindowData){
+ switch miniData.streamType{
case StreamType.CAMERA:
print("关闭相机")
+ camera.stop();
break;
default:
break;
diff --git a/LiveProject/activity/stream/views/MiniWindow.swift b/LiveProject/activity/stream/views/MiniWindow.swift
index 357e6a0..53fba14 100644
--- a/LiveProject/activity/stream/views/MiniWindow.swift
+++ b/LiveProject/activity/stream/views/MiniWindow.swift
@@ -26,22 +26,36 @@
}
@Published var pixelBuffer: CVPixelBuffer? = nil
- @Published var size:CGSize = CGSize(width:300,height:200)
+ //流尺寸(图片和视频的分辨率)
+ @Published var size:CGSize = CGSize(width:16,height:9)
+ //视图比例(相对于 main 尺寸)
+ @Published var viewRate = 0.5
+ //位置
@Published var position:CGSize = CGSize(width: 0, height: 0)
+ //主选框位置
@Published var mainPosition:CGSize = CGSize(width: 0, height: 0)
+ //main 尺寸
@Published var mainSize:CGSize = CGSize(width:200,height:100)
+ //是否加入直播
@Published var videoState:LiveState = .IN_LIVE
@Published var audioState:LiveState = .IN_LIVE
+ //是否静音
@Published var speakerState:MuteState = .UN_MUTE
+ //包含视频和图片
@Published var hasVideo = true;
+ //包含音频
@Published var hasAudio = true;
+ //旋转角度
@Published var rotate = 0;
+ //fps
@Published var frameFPS = 0;
+ //
@Published var name = " 名称";
+ //相机焦距
@Published var minZoom = 0;
@Published var maxZoom = 0;
@Published var zoom = 1.0
-
+ //文本颜色
@Published var textColor = Color.white;
}
struct MiniWindowActions {
@@ -72,11 +86,25 @@
@ObservedObject var miniData:MiniWindowData
@State var lastLocation = CGSize(width: 0, height: 0)
+ @State var lastZoom = 0.0
@Environment(\.miniWindowActions) var actions
var body: some View {
- ZStack(){
- VideoRendererView(pixelBuffer: $miniData.pixelBuffer).background(Color.black)
+ var width = (miniData.mainSize.width * miniData.viewRate);
+ var height = (miniData.mainSize.width / (miniData.size.width / miniData.size.height) * miniData.viewRate);
+ if(miniData.rotate == 90 || miniData.rotate == 270){
+ var temp = width;
+ width = height;
+ height = temp;
+ }
+ return ZStack(){
+ let optionalIntBinding = Binding<Int?>(
+ get: { miniData.rotate },
+ set: { newValue in
+ miniData.rotate = newValue ?? 0
+ }
+ )
+ VideoRendererView(pixelBuffer: $miniData.pixelBuffer,rotate:optionalIntBinding).background(Color.black)
//名称
Text(self.miniData.name)
@@ -104,33 +132,43 @@
.clipShape(RoundedCorner(radius: 20,corners: [.bottomRight]))
.frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topLeading)
//关闭
- IconButton(info: Icons.CLOSE,action:actions.onCloseClick).frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topTrailing)
- .padding(EdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 10))
+ IconButton(info: Icons.CLOSE,action:actions.onCloseClick,width: 40,height: 40).frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topTrailing)
+ .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
if(self.miniData.streamType != .TEXT && self.miniData.hasVideo){
//旋转
VStack{
Spacer()
- IconButton(info: Icons.ROTATE_LEFT,action:actions.onRotateLeftClick).padding(EdgeInsets(top: 0, leading: 13, bottom: 0, trailing: 0))
+ IconButton(info: Icons.ROTATE_LEFT,action:{
+ if (self.miniData.rotate == 0) {
+ self.miniData.rotate = 270
+ } else {
+ self.miniData.rotate -= 90
+ }
+ actions.onRotateLeftClick()
+ },width: 40,height: 40).padding(EdgeInsets(top: 0, leading: 13, bottom: 0, trailing: 0))
Spacer()
- IconButton(info: Icons.ROTATE_RIGHT,action:actions.onRotateRightClick).padding(EdgeInsets(top: 0, leading: 13, bottom: 0, trailing: 11))
+ IconButton(info: Icons.ROTATE_RIGHT,action:{
+ self.miniData.rotate = (self.miniData.rotate + 90) % 360
+ actions.onRotateRightClick()
+ },width: 40,height: 40).padding(EdgeInsets(top: 0, leading: 13, bottom: 0, trailing: 11))
Spacer()
}.frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .topTrailing)
- .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10))
+ .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
}
- BottomBtns().background(Color.gray.opacity(0.4)).frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .bottom)
+ BottomBtns().background(Color.black.opacity(0.4)).frame(maxWidth: .infinity,maxHeight: .infinity,alignment: .bottom)
- }.background(Color.blue).frame(width: miniData.size.width,height:miniData.size.height)
+ }.background(Color.blue).frame(width:width,height:height)
.offset(self.miniData.position)
- .gesture(DragGesture(minimumDistance: 0)
+ .gesture(SimultaneousGesture(DragGesture(minimumDistance: 0)
.onChanged{ val in
let x = self.miniData.position.width
let y = self.miniData.position.height
let newX = x - lastLocation.width + val.translation.width;
let newY = y - lastLocation.height + val.translation.height;
self.miniData.position = CGSize(width: newX, height: newY)
- print(" onChanged \(index) \(self.miniData.position) ")
+ //print(" onChanged \(index) \(self.miniData.position) ")
lastLocation = val.translation;
}.onEnded{ val in
let x = self.miniData.position.width
@@ -138,10 +176,36 @@
let newX = x - lastLocation.width + val.translation.width;
let newY = y - lastLocation.height + val.translation.height;
self.miniData.position = CGSize(width: newX, height: newY)
- print(" onChanged \(index) \(self.miniData.position) ")
+ //print(" onChanged \(index) \(self.miniData.position) ")
lastLocation = CGSize(width: 0, height: 0);
- print(" onEnded \(val)")
- })
+ //print(" onEnded \(val)")
+ },MagnifyGesture().onChanged{ val in
+ if(lastZoom > 0){
+ var l = val.magnification - lastZoom;
+ miniData.viewRate += l;
+ if(miniData.viewRate < 0.4){
+ miniData.viewRate = 0.4
+ }else{
+ var w = miniData.mainSize.width * l / 2
+ self.miniData.position = CGSize(width: self.miniData.position.width - w , height: self.miniData.position.height - w)
+ }
+ }
+ lastZoom = val.magnification;
+ }.onEnded{ val in
+ var l = val.magnification - lastZoom;
+ miniData.viewRate += l;
+ if(miniData.viewRate < 0.4){
+ miniData.viewRate = 0.4
+ }else{
+ var w = miniData.mainSize.width * l / 2
+ self.miniData.position = CGSize(width: self.miniData.position.width - w , height: self.miniData.position.height - w)
+ }
+ lastZoom = 0;
+ }))
+ .onAppear{
+
+ print("onAppear body width = \(width) , height = \(height)")
+ }
}
/**
@@ -179,7 +243,7 @@
actions.onMicClick()
}
Spacer()
- if(self.miniData.streamType != .MICROPHONE && self.miniData.streamType != .UAC && self.miniData.streamType != .SYSTEM ){
+ if(self.miniData.streamType != .MICROPHONE && self.miniData.streamType != .UAC && self.miniData.streamType != .SYSTEM && self.miniData.streamType != .CAMERA){
IconButton(info: self.miniData.speakerState == .UN_MUTE ? Icons.SPEAKER : Icons.SPEAKER_MUTE){
if(self.miniData.speakerState == .MUTE){
self.miniData.speakerState = .UN_MUTE
@@ -202,7 +266,7 @@
.padding(EdgeInsets(top: 5, leading: 15, bottom: 5, trailing: 15))
}
- func IconButton(info:IconInfo,action:@escaping ()->Void = {}, disabled:Bool = true,allow:Bool = true) -> some View{
+ func IconButton(info:IconInfo,action:@escaping ()->Void = {},width:CGFloat = 20,height:CGFloat = 20) -> some View{
Button(action:{
print("IconButton ")
action()
@@ -214,7 +278,8 @@
.frame(width: info.size.width, height: info.size.height)
.aspectRatio(contentMode: .fit)
.foregroundColor(Color.white)
- }.frame(width: 30,height: 30)
+ }.frame(width: width,height: height)
+ .contentShape(Rectangle()) // 明确指定可点击区域
}.buttonStyle(TextBtnStyle())
//.allowsHitTesting(allow) // ✅ 完全禁用点击(不可交互,不触发,不高亮)
//.disabled(disabled) // ✅ 禁用点击事件,但按钮可能变灰(系统默认行为)
@@ -286,14 +351,35 @@
struct MiniWindow_BottomBtns_Previews: PreviewProvider{
@EnvironmentObject var miniData:MiniWindowData
static var previews: some View {
- var miniData = MiniWindowData(streamType: StreamType.CAMERA)
+ var miniData = MiniWindowData(streamType: StreamType.CAMERA);
var miniDataBinding = Binding<MiniWindowData> {
+ miniData.hasAudio = false;
return miniData
} set: { MiniWindowData, Transaction in
}
- MiniWindow(miniData:miniData).environmentObject(miniData);
+ MiniWindow(miniData:miniData).frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
+ .background(
+ GeometryReader { geometry in
+ Color.clear
+ .onAppear {
+ miniData.mainSize = geometry.size;
+
+ print("displaySize:\(miniData.mainSize)")
+ }
+ .onChange(of: geometry.size) { newSize in
+ miniData.mainSize = newSize;
+ print("displaySize:\(miniData.mainSize)")
+ }
+ })
+ .border(Color.red)
+ .onDisappear {
+ print("onDisappear 视图消失了!")
+
+ }.onAppear {
+ print("onAppear 视图出现了!")
+ }.environmentObject(miniData);
}
}
diff --git a/LiveProject/controller/CameraCapture.swift b/LiveProject/controller/CameraCapture.swift
index 3345602..355b1e0 100644
--- a/LiveProject/controller/CameraCapture.swift
+++ b/LiveProject/controller/CameraCapture.swift
@@ -27,19 +27,28 @@
if session.canAddInput(input) {
session.addInput(input)
}
-
+
let output = AVCaptureVideoDataOutput()
output.videoSettings = [
kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA
]
output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "camera.queue"))
-
if session.canAddOutput(output) {
session.addOutput(output)
}
self.videoOutput = output
-
+ // 在相机配置代码中
+ if let videoConnection = output.connection(with: .video) {
+ // 自动旋转(推荐)
+ videoConnection.automaticallyAdjustsVideoMirroring = false
+ videoConnection.videoOrientation = .portrait // 或根据UI方向设置
+
+ // 对于前置摄像头启用镜像
+ /*if videoConnection.isVideoMirroringSupported {
+ videoConnection.isVideoMirrored = (cameraPosition == .front)
+ }*/
+ }
session.commitConfiguration()
session.startRunning()
print("📷 相机已开启")
@@ -51,7 +60,9 @@
guard let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let width = CVPixelBufferGetWidth(buffer)
let height = CVPixelBufferGetHeight(buffer)
- //print("📷 当前帧尺寸: \(width)x\(height)")
+ //print("Buffer Size: \(CVPixelBufferGetWidth(buffer))x\(CVPixelBufferGetHeight(buffer))")
+ //print("Connection orientation: \(connection.videoOrientation.rawValue)")
+ //print("Rotation angle: \(connection.videoRotationAngle)")
onFrame?(buffer)
}
diff --git a/LiveProject/data/IconInfo.swift b/LiveProject/data/IconInfo.swift
index 9a07bbd..a2f1f24 100644
--- a/LiveProject/data/IconInfo.swift
+++ b/LiveProject/data/IconInfo.swift
@@ -8,5 +8,5 @@
struct IconInfo{
var name:String
- var size:CGSize = CGSize(width: 20, height: 20)
+ var size:CGSize = CGSize(width: 15, height: 20)
}
diff --git a/LiveProject/enum/Icons.swift b/LiveProject/enum/Icons.swift
index 56b2ca6..386e318 100644
--- a/LiveProject/enum/Icons.swift
+++ b/LiveProject/enum/Icons.swift
@@ -2,28 +2,28 @@
// Icons.swift
// LiveProject
//
-// Created by 倪路朋 on 7/4/25.
+// Created by 倪路朋 on 7/4/22.
//
import SwiftUI
struct Icons{
- static let CAMERA = IconInfo(name: "camera",size: CGSize(width: 25, height: 20))
- static let MIC = IconInfo(name: "mic",size: CGSize(width: 15, height: 23))
- static let MIC_MUTE = IconInfo(name: "mic.slash",size: CGSize(width: 18, height: 20))
- static let PORTRAIT = IconInfo(name: "ipad",size: CGSize(width: 18, height: 23))
- static let LANDSCAPE = IconInfo(name: "ipad.landscape",size: CGSize(width: 25, height: 20))
- static let BACK = IconInfo(name: "arrow.left",size: CGSize(width: 25, height: 20))
- static let CLOSE = IconInfo(name: "xmark",size: CGSize(width: 22, height: 22))
+ static let CAMERA = IconInfo(name: "camera",size: CGSize(width: 22, height: 17))
+ static let MIC = IconInfo(name: "mic",size: CGSize(width: 13, height: 20))
+ static let MIC_MUTE = IconInfo(name: "mic.slash",size: CGSize(width: 15, height: 17))
+ static let PORTRAIT = IconInfo(name: "ipad",size: CGSize(width: 15, height: 20))
+ static let LANDSCAPE = IconInfo(name: "ipad.landscape",size: CGSize(width: 22, height: 17))
+ static let BACK = IconInfo(name: "arrow.left",size: CGSize(width: 22, height: 17))
+ static let CLOSE = IconInfo(name: "xmark",size: CGSize(width: 19, height: 19))
- static let SPEAKER = IconInfo(name: "speaker",size: CGSize(width: 18, height: 23))
- static let SPEAKER_MUTE = IconInfo(name: "speaker.slash",size: CGSize(width: 18, height: 23))
- static let IMAGE = IconInfo(name: "video",size: CGSize(width: 28, height: 19))
- static let IMAGE_MUTE = IconInfo(name: "video.slash",size: CGSize(width: 25, height: 20))
- static let ROTATE_LEFT = IconInfo(name: "rotate.left",size: CGSize(width: 23, height: 25))
- static let ROTATE_RIGHT = IconInfo(name: "rotate.right",size: CGSize(width: 23, height: 25))
- static let INFO = IconInfo(name: "info.circle",size: CGSize(width: 25, height: 25))
- static let PAINT = IconInfo(name: "paintpalette",size: CGSize(width: 25, height: 25))
- static let HOME = IconInfo(name: "house",size: CGSize(width: 25, height: 23))
- static let ROTATE = IconInfo(name: "camera.rotate",size: CGSize(width: 25, height: 20))
- static let MORE = IconInfo(name: "ellipsis",size: CGSize(width: 25, height: 5))
+ static let SPEAKER = IconInfo(name: "speaker",size: CGSize(width: 15, height: 20))
+ static let SPEAKER_MUTE = IconInfo(name: "speaker.slash",size: CGSize(width: 15, height: 20))
+ static let IMAGE = IconInfo(name: "video",size: CGSize(width: 23, height: 16))
+ static let IMAGE_MUTE = IconInfo(name: "video.slash",size: CGSize(width: 22, height: 17))
+ static let ROTATE_LEFT = IconInfo(name: "rotate.left",size: CGSize(width: 20, height: 22))
+ static let ROTATE_RIGHT = IconInfo(name: "rotate.right",size: CGSize(width: 20, height: 22))
+ static let INFO = IconInfo(name: "info.circle",size: CGSize(width: 22, height: 22))
+ static let PAINT = IconInfo(name: "paintpalette",size: CGSize(width: 22, height: 22))
+ static let HOME = IconInfo(name: "house",size: CGSize(width: 22, height: 20))
+ static let ROTATE = IconInfo(name: "camera.rotate",size: CGSize(width: 22, height: 17))
+ static let MORE = IconInfo(name: "ellipsis",size: CGSize(width: 18, height: 3))
}
diff --git a/LiveProject/tool/MetalRenderer.swift b/LiveProject/tool/MetalRenderer.swift
index 61bd769..92b2fe4 100644
--- a/LiveProject/tool/MetalRenderer.swift
+++ b/LiveProject/tool/MetalRenderer.swift
@@ -16,6 +16,7 @@
private var currentPixelBuffer: CVPixelBuffer?
private let textureCache: CVMetalTextureCache
+ private var rotate:Int = 0;
init(mtkView: MTKView) {
guard let device = MTLCreateSystemDefaultDevice(),
@@ -63,7 +64,24 @@
guard drawableSize.width > 0, drawableSize.height > 0 else { return }
// 加方向修正:顺时针旋转90度
- var ciImage = CIImage(cvPixelBuffer: pixelBuffer).oriented(.right)
+ var orien:CGImagePropertyOrientation = .up;
+ switch self.rotate{
+ case 0:
+ break;
+ case 90:
+ orien = .right;
+ break;
+ case 180:
+ orien = .down;
+ break;
+ case 270:
+ orien = .left;
+ break;
+ default:
+ orien = .up;
+ }
+ print(" roate = \(rotate)")
+ var ciImage = CIImage(cvPixelBuffer: pixelBuffer).oriented(orien)
// 等比缩放后居中
let sourceExtent = ciImage.extent
@@ -99,4 +117,9 @@
//刷新
}
+
+ func updateRotate(angle:Int){
+ rotate = angle;
+ }
+
}
diff --git a/LiveProject/views/VideoRendererView.swift b/LiveProject/views/VideoRendererView.swift
index d157f06..79a63a1 100644
--- a/LiveProject/views/VideoRendererView.swift
+++ b/LiveProject/views/VideoRendererView.swift
@@ -9,6 +9,7 @@
struct VideoRendererView: UIViewRepresentable {
@Binding var pixelBuffer: CVPixelBuffer?
+ @Binding var rotate:Int?;
//用 Coordinator 缓存实例
func makeCoordinator() -> Coordinator {
@@ -24,7 +25,12 @@
//print("updateUIView")
context.coordinator.renderer.display(pixelBuffer: buffer)
}
+ if let angle = rotate{
+ //print("updateUIView rotate \(angle)")
+ context.coordinator.renderer.updateRotate(angle: angle)
+ }
}
+
class Coordinator {
let mtkView: MTKView
--
Gitblit v1.9.1