Код: Выделить всё
class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
let captureSession = AVCaptureSession()
let videoOutput = AVCaptureMovieFileOutput()
var previewLayer: AVCaptureVideoPreviewLayer!
var timer: Timer?
var startTime: TimeInterval = 0
var isRecording = false
var recordButton: UIButton!
var timerLabel: UILabel!
let player = AVQueuePlayer()
override func viewDidLoad() {
super.viewDidLoad()
checkCameraAuthorization()
checkPhotoLibraryAuthorization()
checkMicrophoneAuthoization()
setupCamera()
setupUI()
// Set up audio session for volume control
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
} catch {
print("Error setting up audio session: \(error.localizedDescription)")
}
}
func setupCamera() {
// Setup capture session
captureSession.sessionPreset = .high
// Add input device
guard let camera = AVCaptureDevice.default(for: .video),
let input = try? AVCaptureDeviceInput(device: camera) else { return }
captureSession.addInput(input)
// Add audio input device
guard let microphone = AVCaptureDevice.default(for: .audio),
let audioInput = try? AVCaptureDeviceInput(device: microphone) else {
print("Failed to create audio input.")
return
}
captureSession.addInput(audioInput)
// Add output
captureSession.addOutput(videoOutput)
// Setup preview layer
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.videoGravity = .resizeAspectFill
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)
// Start the session
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
self?.captureSession.startRunning()
}
}
func setupUI() {
// Record button
recordButton = UIButton(type: .custom)
recordButton.frame = CGRect(x: (view.bounds.width - 60) / 2, y: view.bounds.height - 100, width: 60, height: 60)
recordButton.backgroundColor = .white
recordButton.layer.cornerRadius = 30
recordButton.addTarget(self, action: #selector(toggleRecording), for: .touchUpInside)
view.addSubview(recordButton)
// Timer label
timerLabel = UILabel()
timerLabel.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 20)
timerLabel.textAlignment = .center
timerLabel.font = UIFont.boldSystemFont(ofSize: 14)
timerLabel.textColor = .white
view.addSubview(timerLabel)
}
@objc func toggleRecording() {
if !isRecording {
// Start recording
startRecording()
} else {
// Stop recording
stopRecording()
}
// Toggle recording state
isRecording = !isRecording
}
func startRecording() {
// Start recording
let timestamp = Date().timeIntervalSince1970
let filename = "video_\(Int(timestamp)).mov"
let outputPath = NSTemporaryDirectory() + filename
let outputURL = URL(fileURLWithPath: outputPath)
if videoOutput.isRecording {
print("Already recording.")
return
}
let dispatchGroup = DispatchGroup()
dispatchGroup.enter() // Enter the dispatch group before starting the recording
// Start recording
DispatchQueue.global().async {
self.videoOutput.startRecording(to: outputURL, recordingDelegate: self)
print("Video is recording")
dispatchGroup.leave() // Leave the dispatch group after starting the recording
}
// Play custom sound
playCustomSound()
// Start timer
startTime = Date().timeIntervalSince1970
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(updateElapsedTime), userInfo: nil, repeats: true)
// Update button color
recordButton.backgroundColor = .red
// Wait for all tasks to finish
dispatchGroup.notify(queue: .main) {
// All tasks are finished
}
}
func stopRecording() {
// Get output URL before stopping the recording
// let outputURL = videoOutput.outputFileURL
// Stop recording
videoOutput.stopRecording()
print("Recording has been stopped.")
// Stop timer
timer?.invalidate()
// Update button color
recordButton.backgroundColor = .white
}
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL)
}) { _, error in
if let error = error {
print(error)
}
}
}
func playCustomSound() {
DispatchQueue.global(qos: .background).async { [weak self] in
guard let url = Bundle.main.url(forResource: "startsound", withExtension: "m4a") else {
print("Sound file not found.")
return
}
do {
let item = AVPlayerItem(url: url)
self?.player.removeAllItems()
self?.player.insert(item, after: nil)
self?.player.play()
print("Audio playback started.")
}
}
}
@objc func updateElapsedTime() {
let elapsedTime = Date().timeIntervalSince1970 - startTime
let milliseconds = Int(elapsedTime * 1000) % 1000
let seconds = Int(elapsedTime) % 60
let minutes = (Int(elapsedTime) / 60) % 60
let formattedTime = String(format: "%02d:%02d.%03d", minutes, seconds, milliseconds)
timerLabel.font = UIFont.boldSystemFont(ofSize: 30)
timerLabel.text = "\(formattedTime)"
}
func checkPhotoLibraryAuthorization() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
// User has authorized access to the photo library.
print("Photo library access authorized.")
case .notDetermined:
// Request photo library access.
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
print("Photo library access granted.")
} else {
print("Photo library access denied.")
}
}
case .denied, .restricted:
// Photo library access is denied or restricted.
print("Photo library access denied or restricted.")
case .limited: // Handle the .limited case
print("Photo library access limited.")
@unknown default:
print("Unknown authorization status.")
}
}
func checkCameraAuthorization() {
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .authorized:
// User has authorized access to the camera.
print("Camera access authorized.")
case .notDetermined:
// Request camera access.
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
print("Camera access granted.")
} else {
print("Camera access denied.")
}
}
case .denied, .restricted:
// Camera access is denied or restricted.
print("Camera access denied or restricted.")
@unknown default:
print("Unknown authorization status.")
}
}
func checkMicrophoneAuthoization() {
let status = AVCaptureDevice.authorizationStatus(for: .audio)
switch status {
case .authorized:
// User has authorized access to the microphone.
print("Microphone access authorized.")
case .notDetermined:
// Request microphone access.
AVCaptureDevice.requestAccess(for: .audio) { granted in
if granted {
print("Microphone access granted.")
} else {
print("Microphone access denied.")
}
}
case .denied, .restricted:
// Microphone access is denied or restricted.
print("Microphone access denied or restricted.")
@unknown default:
print("Unknown authorization status.")
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/784 ... aved-video