11import AVFoundation
22import HaishinKit
3+ import Photos
34import RTCHaishinKit
45import SwiftUI
56
@@ -20,11 +21,13 @@ final class PublishViewModel: ObservableObject {
2021 }
2122 }
2223 @Published private( set) var audioSources : [ AudioSource ] = [ ]
24+ @Published private( set) var isRecording = false
2325 // If you want to use the multi-camera feature, please make create a MediaMixer with a capture mode.
2426 // let mixer = MediaMixer(captureSesionMode: .multi)
2527 private( set) var mixer = MediaMixer ( captureSessionMode: . multi)
2628 private var tasks : [ Task < Void , Swift . Error > ] = [ ]
2729 private var session : ( any Session ) ?
30+ private var recorder : StreamRecorder ?
2831 private var currentPosition : AVCaptureDevice . Position = . back
2932 private var audioSourceService = AudioSourceService ( )
3033 @ScreenActor private var videoScreenObject : VideoTrackScreenObject ?
@@ -65,6 +68,60 @@ final class PublishViewModel: ObservableObject {
6568 }
6669 }
6770
71+ func toggleRecording( ) {
72+ if isRecording {
73+ Task {
74+ do {
75+ // To use this in a product, you need to consider recovery procedures in case moving to the Photo Library fails.
76+ if let videoFile = try await recorder? . stopRecording ( ) {
77+ Task . detached {
78+ try await PHPhotoLibrary . shared ( ) . performChanges {
79+ let creationRequest = PHAssetCreationRequest . forAsset ( )
80+ creationRequest. addResource ( with: . video, fileURL: videoFile, options: nil )
81+ }
82+ }
83+ }
84+ } catch let error as StreamRecorder . Error {
85+ switch error {
86+ case . failedToFinishWriting( let error) :
87+ self . error = error
88+ if let error {
89+ logger. warn ( error)
90+ }
91+ default :
92+ self . error = error
93+ logger. warn ( error)
94+ }
95+ }
96+ recorder = nil
97+ isRecording = false
98+ }
99+ } else {
100+ Task {
101+ let recorder = StreamRecorder ( )
102+ await mixer. addOutput ( recorder)
103+ do {
104+ // When starting a recording while connected to Xcode, it freezes for about 30 seconds. iOS26 + Xcode26.
105+ try await recorder. startRecording ( )
106+ isRecording = true
107+ self . recorder = recorder
108+ } catch {
109+ self . error = error
110+ logger. warn ( error)
111+ }
112+ for await error in await recorder. error {
113+ switch error {
114+ case . failedToAppend( let error) :
115+ self . error = error
116+ default :
117+ self . error = error
118+ }
119+ break
120+ }
121+ }
122+ }
123+ }
124+
68125 func makeSession( _ preference: PreferenceViewModel ) async {
69126 // Make session.
70127 do {
0 commit comments