AVCaptureVideoPreviewLayer (предварительный просмотр камеры) зависает / застревает после перемещения на задний план и обратно
Не могу понять это. Все работает нормально, когда приложение активно, и иногда, когда я перемещаю приложение в фоновый режим (нажимая кнопку "Домой"), а затем, возвращаясь, слой предварительного просмотра зависает / зависает. Я использую viewWillAppear и viewDidAppear для настройки. Вот как я все настроил:
var backCamera = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
var global_device : AVCaptureDevice!
var captureSession: AVCaptureSession?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
captureSession = AVCaptureSession()
captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
CorrectPosition = AVCaptureDevicePosition.Back
for device in backCamera {
if device.position == AVCaptureDevicePosition.Back {
global_device = device as! AVCaptureDevice
CorrectPosition = AVCaptureDevicePosition.Back
break
}
}
configureCamera()
var error: NSError?
var input = AVCaptureDeviceInput(device: global_device, error: &error)
if error == nil && captureSession!.canAddInput(input) {
captureSession!.addInput(input)
stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
if captureSession!.canAddOutput(stillImageOutput) {
captureSession!.addOutput(stillImageOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
var bounds:CGRect = camera_Preview.layer.bounds
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer?.bounds = bounds
previewLayer?.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))
camera_Preview.layer.addSublayer(previewLayer)
self.view.bringSubviewToFront(camera_Preview)
self.view.bringSubviewToFront(nan_view)
captureSession!.startRunning()
}
}
ViewDidAppear:
var previewLayer: AVCaptureVideoPreviewLayer?
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
previewLayer!.frame = camera_Preview.bounds
}
5 ответов
Для будущих читателей: это правильный процесс настройки камеры внутри вашего приложения.
Прежде всего, спасибо за людей выше, которые нашли время и попытались помочь мне. Они оба направляют меня в правильном направлении. Хотя Билл ошибся насчет viewDidLoad
Теория, которую он действительно дал решение Apple Project.
Эта настройка камеры - правильный путь - немного сложнее, чем я думал, следование документации дало мне отличные результаты. Итак, для кодировщиков Objective-C:
Об ответе Андреа он сказал несколько замечательных указателей, которые вы должны учитывать при создании такого рода приложений. Проверьте их - они очень актуальны (большинство вещей, которые он сказал в проекте Apple, также).
Roi,
Я думаю, что ваша проблема заключается в том, что вы делаете все настройки сеанса и тому подобное в viewWillAppear. Допустим, что captureSession и previewLayer были выделены и работают правильно. Теперь вы кладете приложение на задний план и возвращаете.
Вы немедленно попытаетесь создать новый captureSession и новый PreviewLayer. Я подозреваю, что старые и новые запутываются.
В примере Apple AVCam они выполняют настройку в viewDidLoad. Таким образом, это делается только один раз.
Вы должны переместить все ваши настройки в метод, а затем вызвать метод из viewDidLoad.
законопроект
Просто быстрое обновление в 2017 году, если кто-то страдает от переосмысления такой вещи,
Сделайте то же самое, но измените
stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
заменить его на
stillImageOutput!.outputSettings = [((kCVPixelBufferPixelFormatTypeKey as NSString) as String):NSNumber(value:kCVPixelFormatType_32BGRA)]
это решит проблему. Если нет, то иди сюда и запиши:))
Swift 4 Solution
Вы должны удалить вход камеры, когда пользователь вводит фон, а затем восстановить его, когда он вернется. Посмотрите на этот код:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
createCameraPreview() //Call the setup of the camera here so that if the user enters the view controller from another view controller, the camera is established.
notificationCenter() //Call the notification center function to determine when the user enters and leaves the background.
}
func notificationCenter() {
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: .UIApplicationWillResignActive , object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(openedAgain), name: .UIApplicationDidBecomeActive, object: nil)
}
@objc func openedAgain() {
createCameraPreview() //This is your function that contains the setup for your camera.
}
@objc func willResignActive() {
print("Entered background")
let inputs = captureSession!.inputs
for oldInput:AVCaptureInput in inputs {
captureSession?.removeInput(oldInput)
}
}
Я думаю, что есть разные вещи, которые могут вызвать проблемы:
- Вы должны обернуть всю конфигурацию, которую вы делаете в
-beginConfiguration
а также-commitConfiguration
блок кода. Каждый раз, когда вы настраиваете что-то в сеансе, на это уходит время. Заключение кода конфигурации между этими методами гарантирует, что все изменения будут зафиксированы за один раз, что сокращает общее время создания сеанса. - Приостановка сеанса хороша, когда вы уходите на задний план. Зарегистрируйте свой класс в качестве наблюдателя
UIApplicationDidEnterBackground
а такжеUIApplicationWillEnterForeground
приостановить и начать сеанс заново. - Вы создаете сеанс в
-viewWillAppear
каждый раз, когда вызывается этот метод, вы создаете сеанс, но из вашего кода не совсем понятно, избавитесь ли вы от него. Вы должны отделить и сбалансировать сеанс создания и уничтожения. Обеспечить-setupSession
и-tearDownSession
методы. Убедитесь, что установка вызывается, только если нет активного сеанса, и убедитесь, что когда сеанс вам больше не нужен, вы избавитесь от него, вызвавteardownSession
, В SWIFT вы используете переменную @lazy и уничтожаете сеанс вdeinit()
или же-viewWillDisappear
, - Было бы очень полезно создать или использовать очередь SYNC. Создание и уничтожение сеанса является интенсивной задачей, и вы обычно предпочитаете помещать ее в фоновую очередь, кроме того, это помогает синхронизировать все методы, которые включают сеанс. Создание собственной очереди синхронизации гарантирует, например, синхронизацию между вызовом сеанса установки и разрывным вызовом, один вызывается только после завершения другого.
Я понимаю, что это огромный рефакторинг, но я уверен, что у вас будет меньше проблем в будущем.
Я тоже столкнулся с этой проблемой, я исправил свою, добавив это в свои viewWillAppear и viewWillDissapear. Надеюсь это поможет
var session = AVCaptureSession()
override func viewWillAppear(_ animated: Bool) {
session.startRunning()
}
override func viewWillDisappear(_ animated: Bool) {
session.stopRunning()
}