Capture d'images vidéo
Cet exemple vous montre comment utiliser un capteur vidéo personnalisé en utilisant la caméra de l'appareil comme source vidéo. Pour utiliser l'implémentation de la caméra, instanciez le capteur de caméra dans votre fichier VonageVideoManager:
publisher.videoCapture = BasicVideoCapturerCamera(preset: AVCaptureSession.Preset.cif352x288, desiredFrameRate: 30)
Initialisation et configuration du capteur vidéo
L'initialisateur appelle size(from:) pour déterminer la résolution et met en place une file d'attente sérielle pour la capture des images, afin de ne pas affecter la file d'attente de l'interface utilisateur.
init(preset: AVCaptureSession.Preset, desiredFrameRate: Int) {
self.sessionPreset = preset
self.desiredFrameRate = desiredFrameRate
self.captureQueue = DispatchQueue(label: "com.vonage.BasicVideoCapturer")
super.init()
let size = self.size(from: self.sessionPreset)
self.imageWidth = Int(size.width)
self.imageHeight = Int(size.height)
}
La mise en œuvre de la initCapture utilise le AVFoundation pour configurer l'appareil photo afin de capturer des images. Il crée un AVCaptureSession, définit le périphérique d'entrée, et configure un AVCaptureVideoDataOutput:
func initCapture() {
let session = AVCaptureSession()
session.beginConfiguration()
// Set device capture
session.sessionPreset = sessionPreset
guard let videoDevice = AVCaptureDevice.default(for: .video),
let deviceInput = try? AVCaptureDeviceInput(device: videoDevice) else { return }
self.inputDevice = deviceInput
if session.canAddInput(deviceInput) {
session.addInput(deviceInput)
}
let outputDevice = AVCaptureVideoDataOutput()
outputDevice.alwaysDiscardsLateVideoFrames = true
outputDevice.videoSettings = [
kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)
]
outputDevice.setSampleBufferDelegate(self, queue: captureQueue)
if session.canAddOutput(outputDevice) {
session.addOutput(outputDevice)
}
// ... Frame rate configuration (see below)
}
Les images capturées à l'aide de cette méthode sont accessibles par l'intermédiaire de la fonction AVCaptureVideoDataOutputSampleBufferDelegate.
La deuxième partie de la initCapture configure la fréquence d'images :
// Set framerate
let bestFPS = self.bestFrameRate(for: videoDevice)
do {
try videoDevice.lockForConfiguration()
let duration = CMTime(value: 1, timescale: CMTimeScale(bestFPS))
videoDevice.activeVideoMinFrameDuration = duration
videoDevice.activeVideoMaxFrameDuration = duration
videoDevice.unlockForConfiguration()
} catch {
print("Error locking configuration")
}
session.commitConfiguration()
self.captureSession = session
self.format = OTVideoFormat(nv12WithWidth: UInt32(imageWidth), height: UInt32(imageHeight))
Les bestFrameRate(for:) renvoie la meilleure fréquence d'images pour le dispositif de capture :
private func bestFrameRate(for device: AVCaptureDevice) -> Double {
var bestRate: Double = 0
for range in device.activeFormat.videoSupportedFrameRateRanges {
let duration = range.minFrameDuration
let currentRate = Double(duration.timescale) / Double(duration.value)
if currentRate > bestRate && currentRate < Double(desiredFrameRate) {
bestRate = currentRate
}
}
return bestRate
}
Capturer des images pour la vidéo de l'éditeur
Les start démarre la méthode AVCaptureSession:
//
// returns:
// - a negative value for error
// - 0 value when all is OK
//
func start() -> Int32 {
self.captureStarted = true
self.captureSession?.startRunning()
return 0
}
La méthode de délégation captureOutput(_:didOutput:from:) est appelée lorsqu'une nouvelle image vidéo est disponible.
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard captureStarted, let format = self.format else { return }
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let frame = OTVideoFrame(format: format)
// Prepare memory copy for NV12 (2 planes)
let planeCount = CVPixelBufferGetPlaneCount(imageBuffer)
let totalSize = CVPixelBufferGetDataSize(imageBuffer)
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: totalSize)
CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
var planePointers = [UnsafeMutablePointer<UInt8>?]()
var currentDestination = buffer
// Copy planes
for i in 0..<planeCount {
guard let sourceBaseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, i) else { continue }
let planeSize = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, i) * CVPixelBufferGetHeightOfPlane(imageBuffer, i)
planePointers.append(currentDestination)
memcpy(currentDestination, sourceBaseAddress, planeSize)
currentDestination += planeSize
}
// Set metadata and consume
if let device = self.inputDevice?.device {
let minDuration = device.activeVideoMinFrameDuration
frame.format?.estimatedFramesPerSecond = Double(minDuration.timescale) / Double(minDuration.value)
}
frame.orientation = self.currentDeviceOrientation()
frame.timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
planePointers.withUnsafeBufferPointer { bufferPointer in
frame.setPlanesWithPointers(bufferPointer.baseAddress, numPlanes: Int(planeCount))
}
videoCaptureConsumer?.consumeFrame(frame)
buffer.deallocate()
CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
}
Cette méthode permet d'effectuer les opérations suivantes :
- Crée un
OTVideoFrameinstance. - Alloue un tampon de mémoire.
- Copie les données d'image de l'écran
CVImageBuffer(format NV12) dans la mémoire tampon manuelle. Le format NV12 comporte deux plans (Y et UV), qui sont copiés de manière séquentielle. - Marque l'image avec un horodatage et une orientation.
- Appels
consumeFrameet transmet le cadre au SDK de Vonage.
Capture vidéo de base
Apprenez à configurer un capteur vidéo de base à l'aide du SDK vidéo de Vonage.