Erfassen von Videobildern
Dieses Beispiel zeigt Ihnen, wie Sie einen benutzerdefinierten Video-Capturer verwenden, der die Gerätekamera als Videoquelle nutzt. Um die Kameraimplementierung zu verwenden, instanziieren Sie den Kamera-Capturer in Ihrem VonageVideoManager:
publisher.videoCapture = BasicVideoCapturerCamera(preset: AVCaptureSession.Preset.cif352x288, desiredFrameRate: 30)
Initialisieren und Konfigurieren des Video-Capturers
Der Initialisierer ruft size(from:) um die Auflösung zu bestimmen und eine serielle Dispatch-Warteschlange für die Erfassung von Bildern einzurichten, um die UI-Warteschlange nicht zu beeinträchtigen.
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)
}
Die Umsetzung der initCapture verwendet die AVFoundation Rahmen, um die Kamera für die Aufnahme von Bildern zu konfigurieren. Es erstellt eine AVCaptureSessionein, stellt das Eingabegerät ein und konfiguriert eine 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)
}
Der Zugriff auf die mit dieser Methode erfassten Bilder erfolgt über die Methode AVCaptureVideoDataOutputSampleBufferDelegate.
Der zweite Teil der initCapture konfiguriert die Bildrate:
// 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))
Die bestFrameRate(for:) Methode gibt die beste Bildrate für das Aufnahmegerät zurück:
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
}
Aufnahme von Bildern für das Video des Herausgebers
Die start Methode startet die AVCaptureSession:
//
// returns:
// - a negative value for error
// - 0 value when all is OK
//
func start() -> Int32 {
self.captureStarted = true
self.captureSession?.startRunning()
return 0
}
Die Delegierungsmethode captureOutput(_:didOutput:from:) wird aufgerufen, wenn ein neues Videobild verfügbar ist.
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)
}
Diese Methode führt Folgendes aus:
- Erzeugt eine
OTVideoFrameInstanz. - Weist einen Speicherpuffer zu.
- Kopiert Bilddaten aus dem
CVImageBuffer(NV12-Format) in den manuellen Puffer. NV12 hat zwei Ebenen (Y und UV), die sequentiell kopiert werden. - Versieht den Rahmen mit einem Zeitstempel und einer Ausrichtung.
- Anrufe
consumeFrameund übergibt den Rahmen an das Vonage SDK.
Einfache Videoaufnahme
Erfahren Sie, wie Sie mit dem Vonage Video SDK einen einfachen Video-Capturer einrichten.