AVFoundation -AVCaptureSession останавливается и начинает работать только при переходе в фоновый режим и обратно с точкоIOS

Программируем под IOS
Ответить Пред. темаСлед. тема
Anonymous
 AVFoundation -AVCaptureSession останавливается и начинает работать только при переходе в фоновый режим и обратно с точко

Сообщение Anonymous »

Эта проблема не возникала в Xcode 10.2.1 и iOS 12. Она началась в Xcode 11.1 и iOS 13

Мое приложение записывает видео, когда приложение запускается на задний план я останавливаю запуск сеанса захвата и удаляю слой предварительного просмотра. Когда приложение возвращается на передний план, я перезапускаю сеанс захвата и снова добавляю слой предварительного просмотра:

let captureSession = AVCaptureSession()
var previewLayer: AVCaptureVideoPreviewLayer?
var movieFileOutput = AVCaptureMovieFileOutput()

// *** I initially didn't remove the preview layer in this example but I did remove it in the other 2 examples below ***
@objc fileprivate func stopCaptureSession() {
DispatchQueue.main.async {
[weak self] in
if self?.captureSession.isRunning == true {
self?.captureSession.stopRunning()
}
}
}

@objc func restartCaptureSession() {
DispatchQueue.main.async {
[weak self] in
if self?.captureSession.isRunning == false {
self?.captureSession.startRunning()
}
}
}


Что происходит, когда я перехожу в фоновый режим и возвращаюсь к слою предварительного просмотра, а пользовательский интерфейс полностью зависает. Но прежде чем перейти в фоновый режим, если я поставлю точку останова в строке if self?.captureSession.isRunning == true и еще одну точку останова в строке if self?.captureSession.isRunning == false, один раз Я запускаю точки останова, слой предварительного просмотра, и пользовательский интерфейс работает нормально.

После дальнейшего исследования я наткнулся на этот вопрос и в комментариях @HotLicks сказал:

Obviously, it's likely that the breakpoint gives time for some async activity to complete before the above code starts mucking with things. However, it's also the case that 0.03 seconds is an awfully short repeat interval for a timer, and it may simply be the case that the breakpoint allows the UI setup to proceed before the timer ties up the CPU.


Я провел еще немного исследования, и Apple сказала:


The startRunning( ) — это блокирующий вызов, который может занять некоторое время,
поэтому вам следует выполнить настройку сеанса в последовательной очереди, чтобы
основная очередь не блокировалась (что обеспечивает отзывчивость пользовательского интерфейса). См.
AVCam-iOS: Использование AVFoundation для захвата изображений и видеороликов для
примера реализации.


Использование комментарий от @HotLicks и информацию от Apple. Я переключился на использование DispatchQueue.main.sync, а затем Dispatch Group, и после возвращения из фонового режима слой предварительного просмотра и пользовательский интерфейс все еще были заморожены. Но как только я добавляю точки останова, как в первом примере, и запускаю их, слой предварительного просмотра и пользовательский интерфейс работают нормально.

Что я делаю не так?

Что я делаю не так?

Что я делаю не так?

Что я делаю не так?

strong>

Обновление


Я переключился из режима отладки в режим выпуска, и это все еще не работало.

Я также попробовал переключиться на использование DispatchQueue.global(qos: .background).async и таймера DispatchQueue.main.asyncAfter(deadline: .now( ) + 1.5), как предложил @MohyG, но это не имело никакого значения.

При дальнейшей проверке без точки останова фоновое уведомление работает нормально, но это уведомление переднего плана. не вызывается, когда приложение входит в fg. По какой-то причине уведомление fg срабатывает только тогда, когда я впервые помещаю точку останова внутри функции stopCaptureSession().

Проблема заключается в уведомлении на переднем плане срабатывает только с точкой останова, которую я описал выше.

Я попробовал DispatchQueue.main.sync:

@objc fileprivate func stopCaptureSession() {

if captureSession.isRunning { // adding a breakpoint here is the only thing that triggers the foreground notification when the the app comes back

DispatchQueue.global(qos: .default).async {
[weak self] in

DispatchQueue.main.sync {
self?.captureSession.stopRunning()
}

DispatchQueue.main.async {
self?.previewLayer?.removeFromSuperlayer()
self?.previewLayer = nil
}
}
}
}

@objc func restartCaptureSession() {

if !captureSession.isRunning {

DispatchQueue.global(qos: .default).async {
[weak self] in
DispatchQueue.main.sync {
self?.captureSession.startRunning()
}

DispatchQueue.main.asyncAfter(deadline: .now() + 15) {
self?.previewLayer = AVCaptureVideoPreviewLayer(session: self!.captureSession)
self?.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
guard let previewLayer = self?.previewLayer else { return }
previewLayer.frame = self!.containerViewForPreviewLayer.bounds
self?.containerViewForPreviewLayer.layer.insertSublayer(previewLayer, at: 0)
}
}
}
}


Я попробовал группу отправки:

@objc fileprivate func stopCaptureSession() {

let group = DispatchGroup()

if captureSession.isRunning { // adding a breakpoint here is the only thing that triggers the foreground notification when the the app comes back

group.enter()
DispatchQueue.global(qos: .default).async {
[weak self] in

self?.captureSession.stopRunning()
group.leave()

group.notify(queue: .main) {
self?.previewLayer?.removeFromSuperlayer()
self?.previewLayer = nil
}
}
}
}

@objc func restartCaptureSession() {

let group = DispatchGroup()

if !captureSession.isRunning {

group.enter()

DispatchQueue.global(qos: .default).async {
[weak self] in

self?.captureSession.startRunning()
group.leave()

group.notify(queue: .main) {
self?.previewLayer = AVCaptureVideoPreviewLayer(session: self!.captureSession)
self?.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
guard let previewLayer = self?.previewLayer else { return }
previewLayer.frame = self!.containerViewForPreviewLayer.bounds
self?.containerViewForPreviewLayer.layer.insertSublayer(previewLayer, at: 0)
}
}
}
}


Вот остальная часть кода, если необходимо:

NotificationCenter.default.addObserver(self, selector: #selector(appHasEnteredBackground),
name: UIApplication.willResignActiveNotification,
object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(sessionWasInterrupted),
name: .AVCaptureSessionWasInterrupted,
object: captureSession)

NotificationCenter.default.addObserver(self, selector: #selector(sessionInterruptionEnded),
name: .AVCaptureSessionInterruptionEnded,
object: captureSession)

NotificationCenter.default.addObserver(self, selector: #selector(sessionRuntimeError),
name: .AVCaptureSessionRuntimeError,
object: captureSession)

func stopMovieShowControls() {

if movieFileOutput.isRecording {
movieFileOutput.stopRecording()
}

recordButton.isHidden = false
saveButton.isHidden = false
}

@objc fileprivate func appWillEnterForeground() {

restartCaptureSession()
}

@objc fileprivate func appHasEnteredBackground() {

stopMovieShowControls()

imagePicker.dismiss(animated: false, completion: nil)

stopCaptureSession()
}

@objc func sessionRuntimeError(notification: NSNotification) {
guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else { return }

stopMovieRecordigShowControls()

if error.code == .mediaServicesWereReset {
if !captureSession.isRunning {
DispatchQueue.main.async { [weak self] in
self?.captureSession.startRunning()
}
} else {
restartCaptureSession()
}
} else {
restartCaptureSession()
}
}

@objc func sessionWasInterrupted(notification: NSNotification) {

if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as AnyObject?,
let reasonIntegerValue = userInfoValue.integerValue,
let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue) {

switch reason {

case .videoDeviceNotAvailableInBackground:

stopMovieShowControls()

case .audioDeviceInUseByAnotherClient, .videoDeviceInUseByAnotherClient:

stopMovieShowControls()

case .videoDeviceNotAvailableWithMultipleForegroundApps:

print("2. The toggleButton was pressed")

case .videoDeviceNotAvailableDueToSystemPressure:
// no documentation
break

@unknown default:
break
}
}
}

@objc func sessionInterruptionEnded(notification: NSNotification) {

restartCaptureSession()

stopMovieShowControls()
}


Подробнее здесь: https://stackoverflow.com/questions/583 ... g-to-backg
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «IOS»