SegmentedRecorder
Gerencia a gravaΓ§Γ£o de mΓΊltiplos segmentos de vΓdeo independentes, cada um em um arquivo temporΓ‘rio separado.
Arquivo: SegmentedRecorder.swift
π VisΓ£o Geral
O SegmentedRecorder
permite:
- Gravar mΓΊltiplos takes independentes
- Armazenar cada segmento em arquivo
.mov
temporΓ‘rio - Notificar via delegate quando gravaΓ§Γ£o termina
- Gerenciar orientaΓ§Γ£o dinΓ’mica
ποΈ Protocol Delegate
protocol SegmentedRecorderDelegate: AnyObject {
func recorder(_ recorder: SegmentedRecorder, didFinishSegment url: URL)
func recorder(_ recorder: SegmentedRecorder, didFailWith error: Error)
}
Uso:
extension CameraViewModel: SegmentedRecorderDelegate {
func recorder(_ recorder: SegmentedRecorder, didFinishSegment url: URL) {
// Gerar thumbnail
// Adicionar a segments array
}
func recorder(_ recorder: SegmentedRecorder, didFailWith error: Error) {
// Mostrar erro
// Resetar UI
}
}
π§ MΓ©todos Principais
InicializaΓ§Γ£o
init(output: AVCaptureMovieFileOutput, delegate: SegmentedRecorderDelegate)
ParΓ’metros:
output
: ReferΓͺncia aoAVCaptureMovieFileOutput
da sessiondelegate
: Objeto que receberΓ‘ callbacks
Iniciar Segmento
func startNewSegment()
Comportamento:
- Cria URL temporΓ‘rio:
NSTemporaryDirectory() + "segment_\(UUID()).mov"
- Define orientaΓ§Γ£o via
updateOrientation(from:)
- Chama
output.startRecording(to: url, recordingDelegate: self)
Exemplo:
// UsuΓ‘rio aperta botΓ£o de gravar
recorder.startNewSegment()
Parar Segmento
func stopCurrentSegment()
Comportamento:
- Chama
output.stopRecording()
- AVFoundation chama delegate
didFinishRecordingTo
quando pronto - Delegate do
SegmentedRecorder
Γ© notificado
Exemplo:
// UsuΓ‘rio aperta botΓ£o de parar
recorder.stopCurrentSegment()
Atualizar OrientaΓ§Γ£o
func updateOrientation(from deviceOrientation: UIDeviceOrientation)
DescriΓ§Γ£o: Atualiza orientaΓ§Γ£o para prΓ³ximas gravaΓ§Γ΅es.
Uso: Chamado quando UIDevice.orientationDidChangeNotification
dispara.
π¬ Fluxo de GravaΓ§Γ£o
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User Action β
β toggleRecording() pressed β
ββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββΌβββββββββββ
β isRecording == false β
βββββββββββββ¬ββββββββββββ
β
βββββββββββββΌβββββββββββββββββββββββββββ
β startNewSegment() β
β 1. Create temp URL β
β 2. Set video orientation β
β 3. output.startRecording(to:) β
βββββββββββββ¬βββββββββββββββββββββββββββ
β
βββββββββββββΌβββββββββββββββββββββββββββ
β Recording in progress... β
β isRecording = true β
βββββββββββββ¬βββββββββββββββββββββββββββ
β
βββββββββββββΌβββββββββββ
β User stops recordingβ
βββββββββββββ¬ββββββββββββ
β
βββββββββββββΌβββββββββββββββββββββββββββ
β stopCurrentSegment() β
β output.stopRecording() β
βββββββββββββ¬βββββββββββββββββββββββββββ
β
βββββββββββββΌβββββββββββββββββββββββββββββββββββ
β AVCaptureFileOutputRecordingDelegate β
β fileOutput(_:didFinishRecordingTo:...) β
βββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
βββββββββββββΌβββββββββββββββββββββββββββ
β delegate.recorder(didFinishSegment:)β
β CameraViewModel receives URL β
ββββββββββββββββββββββββββββββββββββββββ
π AVCaptureFileOutputRecordingDelegate
extension SegmentedRecorder: AVCaptureFileOutputRecordingDelegate {
func fileOutput(
_ output: AVCaptureFileOutput,
didFinishRecordingTo outputFileURL: URL,
from connections: [AVCaptureConnection],
error: Error?
) {
if let error = error {
delegate?.recorder(self, didFailWith: error)
} else {
delegate?.recorder(self, didFinishSegment: outputFileURL)
}
}
}
π Gerenciamento de Arquivos
CriaΓ§Γ£o de Arquivo TemporΓ‘rio
let tempDir = NSTemporaryDirectory()
let filename = "segment_\(UUID().uuidString).mov"
let url = URL(fileURLWithPath: tempDir).appendingPathComponent(filename)
Exemplo de path:
/var/mobile/Containers/Data/Application/.../tmp/segment_12345678-1234-1234-1234-123456789abc.mov
Cleanup
Arquivos temporΓ‘rios sΓ£o removidos apΓ³s concatenaΓ§Γ£o:
// No CameraViewModel, apΓ³s salvar vΓdeo final
for segment in segments {
try? FileManager.default.removeItem(at: segment.url)
}
segments.removeAll()
π― Casos de Uso
GravaΓ§Γ£o Simples (1 Segmento)
// Iniciar
recorder.startNewSegment()
// ... usuΓ‘rio grava ...
// Parar
recorder.stopCurrentSegment()
// Delegate recebe
func recorder(_ recorder: SegmentedRecorder, didFinishSegment url: URL) {
print("Segment saved at: \(url)")
}
GravaΓ§Γ£o MΓΊltipla (3 Segmentos)
// Segmento 1
recorder.startNewSegment()
// ... grava ...
recorder.stopCurrentSegment()
// Segmento 2
recorder.startNewSegment()
// ... grava ...
recorder.stopCurrentSegment()
// Segmento 3
recorder.startNewSegment()
// ... grava ...
recorder.stopCurrentSegment()
// Todos os 3 URLs sΓ£o recebidos via delegate
// CameraViewModel armazena em segments array
βοΈ ConfiguraΓ§Γ£o
saveToPhotoLibrary
var saveToPhotoLibrary: Bool = false
DescriΓ§Γ£o: Se true
, cada segmento Γ© salvo individualmente na galeria.
Uso TΓpico: false
(salvamos apenas o vΓdeo final concatenado).
π§΅ Threading
Callbacks
Delegates sΓ£o chamados na thread onde o output foi configurado (tipicamente main thread ou sessionQueue).
Best Practice: Sempre dispatch para main thread antes de atualizar UI:
func recorder(_ recorder: SegmentedRecorder, didFinishSegment url: URL) {
DispatchQueue.global(qos: .userInitiated).async {
let thumbnail = generateThumbnail(for: url)
DispatchQueue.main.async {
self.segments.append(RecordedSegment(url: url, thumbnail: thumbnail))
}
}
}
π Troubleshooting
Segmento nΓ£o grava
Sintomas: didFinishSegment
nunca Γ© chamado.
Causas:
- Session nΓ£o estΓ‘ rodando
- Output nΓ£o estΓ‘ conectado
- PermissΓ΅es de escrita
SoluΓ§Γ£o:
// Verificar session
print("Session running: \(session.isRunning)")
// Verificar connections
print("Output connections: \(output.connections)")
// Verificar permissΓ΅es
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
print("Can write to temp: \(FileManager.default.isWritableFile(atPath: tempURL.path))")
Erro βRecording already in progressβ
Sintomas: Crash ao chamar startNewSegment()
duas vezes.
SoluΓ§Γ£o:
guard !output.isRecording else {
print("Already recording")
return
}
output.startRecording(to: url, recordingDelegate: self)
π Ver TambΓ©m
- CameraViewModel - CoordenaΓ§Γ£o e concatenaΓ§Γ£o
- Guia de GravaΓ§Γ£o - Tutorial completo
- Fluxo de Dados - Como os dados fluem
β CaptureSessionController | Teleprompter β |