add udpserver

switch to hevc in videotoolbox
move capturevideopreview to its own file
reorg
This commit is contained in:
neon443
2026-02-26 19:50:38 +00:00
parent 739b19bfb3
commit d412bc8824
6 changed files with 102 additions and 20 deletions

View File

@@ -8,6 +8,8 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
A94E29F72F09B569006E583D /* ScreamPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94E29F62F09B569006E583D /* ScreamPacket.swift */; }; A94E29F72F09B569006E583D /* ScreamPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94E29F62F09B569006E583D /* ScreamPacket.swift */; };
A98010132F50D19D00A58EF7 /* UDPServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98010122F50D19D00A58EF7 /* UDPServer.swift */; };
A98010152F50D9B100A58EF7 /* CaptureVideoPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98010142F50D9B100A58EF7 /* CaptureVideoPreview.swift */; };
A98E8BF02F05B2A0006D4458 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98E8BEB2F05B2A0006D4458 /* AppDelegate.swift */; }; A98E8BF02F05B2A0006D4458 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98E8BEB2F05B2A0006D4458 /* AppDelegate.swift */; };
A98E8BF12F05B2A0006D4458 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A98E8BEC2F05B2A0006D4458 /* Assets.xcassets */; }; A98E8BF12F05B2A0006D4458 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A98E8BEC2F05B2A0006D4458 /* Assets.xcassets */; };
A98E8BF22F05B2A0006D4458 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A98E8BEE2F05B2A0006D4458 /* MainMenu.xib */; }; A98E8BF22F05B2A0006D4458 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A98E8BEE2F05B2A0006D4458 /* MainMenu.xib */; };
@@ -38,6 +40,8 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
A94E29F62F09B569006E583D /* ScreamPacket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreamPacket.swift; sourceTree = "<group>"; }; A94E29F62F09B569006E583D /* ScreamPacket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreamPacket.swift; sourceTree = "<group>"; };
A98010122F50D19D00A58EF7 /* UDPServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDPServer.swift; sourceTree = "<group>"; };
A98010142F50D9B100A58EF7 /* CaptureVideoPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptureVideoPreview.swift; sourceTree = "<group>"; };
A98E8BC02F05B26B006D4458 /* Scream.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Scream.app; sourceTree = BUILT_PRODUCTS_DIR; }; A98E8BC02F05B26B006D4458 /* Scream.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Scream.app; sourceTree = BUILT_PRODUCTS_DIR; };
A98E8BCE2F05B26D006D4458 /* ScreamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A98E8BCE2F05B26D006D4458 /* ScreamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A98E8BD82F05B26D006D4458 /* ScreamUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreamUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A98E8BD82F05B26D006D4458 /* ScreamUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreamUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -81,9 +85,7 @@
A98E8BB72F05B26B006D4458 = { A98E8BB72F05B26B006D4458 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A94E29F62F09B569006E583D /* ScreamPacket.swift */,
A9D722602F07304C00050BB0 /* Config.xcconfig */, A9D722602F07304C00050BB0 /* Config.xcconfig */,
A9D7225E2F070FE600050BB0 /* CaptureEngine.swift */,
A98E8BEF2F05B2A0006D4458 /* Scream */, A98E8BEF2F05B2A0006D4458 /* Scream */,
A98E8BF42F05B2A2006D4458 /* ScreamTests */, A98E8BF42F05B2A2006D4458 /* ScreamTests */,
A98E8BF82F05B2A5006D4458 /* ScreamUITests */, A98E8BF82F05B2A5006D4458 /* ScreamUITests */,
@@ -109,6 +111,10 @@
A98E8BEC2F05B2A0006D4458 /* Assets.xcassets */, A98E8BEC2F05B2A0006D4458 /* Assets.xcassets */,
A98E8BEE2F05B2A0006D4458 /* MainMenu.xib */, A98E8BEE2F05B2A0006D4458 /* MainMenu.xib */,
A98E8BFC2F05D28D006D4458 /* ScreenRecorder.swift */, A98E8BFC2F05D28D006D4458 /* ScreenRecorder.swift */,
A98010122F50D19D00A58EF7 /* UDPServer.swift */,
A9D7225E2F070FE600050BB0 /* CaptureEngine.swift */,
A94E29F62F09B569006E583D /* ScreamPacket.swift */,
A98010142F50D9B100A58EF7 /* CaptureVideoPreview.swift */,
); );
path = Scream; path = Scream;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -272,6 +278,8 @@
A98E8BF02F05B2A0006D4458 /* AppDelegate.swift in Sources */, A98E8BF02F05B2A0006D4458 /* AppDelegate.swift in Sources */,
A9D7225F2F070FE600050BB0 /* CaptureEngine.swift in Sources */, A9D7225F2F070FE600050BB0 /* CaptureEngine.swift in Sources */,
A98E8BFD2F05D28D006D4458 /* ScreenRecorder.swift in Sources */, A98E8BFD2F05D28D006D4458 /* ScreenRecorder.swift in Sources */,
A98010132F50D19D00A58EF7 /* UDPServer.swift in Sources */,
A98010152F50D9B100A58EF7 /* CaptureVideoPreview.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -10,6 +10,7 @@ import Cocoa
@main @main
class AppDelegate: NSObject, NSApplicationDelegate { class AppDelegate: NSObject, NSApplicationDelegate {
let sr = ScreenRecorder() let sr = ScreenRecorder()
let udpserver = UDPServerImplementation(port: 03067)
@IBOutlet var window: NSWindow! @IBOutlet var window: NSWindow!
@@ -18,6 +19,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
await sr.start() await sr.start()
} }
} }
@IBAction func Button2(_ sender: Any) {
udpserver.start()
}
func applicationDidFinishLaunching(_ aNotification: Notification) { func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application // Insert code here to initialize your application
@@ -25,6 +29,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
preview.frame.size = window.contentView!.frame.size preview.frame.size = window.contentView!.frame.size
window.contentView?.addSubview(preview) window.contentView?.addSubview(preview)
Button(self) Button(self)
Button2(self)
} }
func applicationWillTerminate(_ aNotification: Notification) { func applicationWillTerminate(_ aNotification: Notification) {
@@ -34,19 +39,4 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true return true
} }
}
class CaptureVideoPreview: NSView {
init(layer: CALayer) {
super.init(frame: .zero)
wantsLayer = true
self.layer = layer
layer.autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
}
required init?(coder: NSCoder) {
fatalError()
}
} }

View File

@@ -42,7 +42,7 @@ class CaptureEngine: NSObject {
allocator: kCFAllocatorDefault, allocator: kCFAllocatorDefault,
width: 3200, width: 3200,
height: 1800, height: 1800,
codecType: kCMVideoCodecType_H264, codecType: kCMVideoCodecType_HEVC,
encoderSpecification: videoEncoderSpec, encoderSpecification: videoEncoderSpec,
imageBufferAttributes: sourceImageBufferAttrs, imageBufferAttributes: sourceImageBufferAttrs,
compressedDataAllocator: nil, compressedDataAllocator: nil,
@@ -71,7 +71,7 @@ class CaptureEngine: NSObject {
frameProperties: nil, frameProperties: nil,
infoFlagsOut: nil infoFlagsOut: nil
) { status, infoFlags, sampleBuffer in ) { status, infoFlags, sampleBuffer in
print() // print()
} }
// outputHandler: self.outputHandler) // outputHandler: self.outputHandler)
@@ -144,7 +144,7 @@ class CaptureEngine: NSObject {
err = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ExpectedFrameRate, value: expectedFrameRate as CFNumber) err = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ExpectedFrameRate, value: expectedFrameRate as CFNumber)
if err != noErr { print("failed to set to framerte \(err)") } if err != noErr { print("failed to set to framerte \(err)") }
err = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_Main_AutoLevel) err = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_HEVC_Main_AutoLevel)
if err != noErr { print("failed to set to profile level \(err)") } if err != noErr { print("failed to set to profile level \(err)") }
err = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AverageBitRate, value: 10 as CFNumber) err = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AverageBitRate, value: 10 as CFNumber)

View File

@@ -0,0 +1,22 @@
//
// CaptureVideoPreview.swift
// Scream
//
// Created by neon443 on 26/02/2026.
//
import Foundation
import Cocoa
class CaptureVideoPreview: NSView {
init(layer: CALayer) {
super.init(frame: .zero)
wantsLayer = true
self.layer = layer
layer.autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
}
required init?(coder: NSCoder) {
fatalError()
}
}

62
Scream/UDPServer.swift Normal file
View File

@@ -0,0 +1,62 @@
//
// UDPServer.swift
// Scream
//
// Created by neon443 on 26/02/2026.
//
import Foundation
import Network
final class UDPServerImplementation: Sendable {
private let connectionListener: NWListener
//init a udp server at x port
init(port: UInt16) {
connectionListener = try! NWListener(
using: .udp,
on: NWEndpoint.Port(integerLiteral: port)
)
connectionListener.newConnectionHandler = { [weak self] connection in
connection.start(queue: .global())
self?.receive(on: connection)
}
connectionListener.stateUpdateHandler = { state in
print("state: \(state)")
}
}
}
protocol UDPServer {
func start()
func stop()
}
extension UDPServerImplementation: UDPServer {
func start() {
connectionListener.start(queue: .global())
print("server started")
}
func stop() {
connectionListener.cancel()
print("server stoped")
}
}
private extension UDPServerImplementation {
func receive(on connection: NWConnection) {
connection.receiveMessage { data, contentContext, isComplete, error in
if let error {
print("error \(error)")
}
if let data,
let message = String(data: data, encoding: .utf8) {
print(message)
}
}
}
}