【问题标题】:GLKView.display() method sometimes causes crash. EXC_BAD_ACCESSGLKView.display() 方法有时会导致崩溃。 EXC_BAD_ACCESS
【发布时间】:2018-03-25 03:35:28
【问题描述】:

我正在尝试使用 AVFoundationGLKitCore Image(不使用 @987654326 @)

所以,我找到了这个教程
http://altitudelabs.com/blog/real-time-filter/
它是用 Objective-C 编写的,所以我用 Swift4.0、XCode9 重写了该代码

它看起来工作正常,但有时(很少)它会因以下错误而崩溃。当GLKViewdisplay方法被调用时

EXC_BAD_ACCESS(代码=1,地址+0x********)

崩溃的时间,GLKView 存在(不是 nil),EAGLContext 存在,CIContext 存在。我的代码如下

导入 UIKit 导入 AVFoundation 导入 GLKit 导入 OpenGLES 类视图控制器:UIViewController { var videoDevice : AVCaptureDevice! var captureSession : AVCaptureSession! var captureSessionQueue : DispatchQueue! var videoPreviewView: GLKView! var ciContext: CIContext! var eaglContext: EAGLContext! var videoPreviewViewBounds: CGRect = CGRect.zero 覆盖 func viewDidLoad() { super.viewDidLoad() // 在加载视图后做任何额外的设置,通常是从一个 nib。 // 移除视图的背景颜色;这允许我们不使用 opaque 属性(self.view.opaque = NO),因为我们完全删除了背景颜色绘图 self.view.backgroundColor = UIColor.clear // 为视频/图像预览设置 GLKView 让窗口:UIView = UIApplication.shared.delegate!.window!! eaglContext = EAGLContext(api: .openGLES2) videoPreviewView = GLKView(帧:videoPreviewViewBounds,上下文:eaglContext) videoPreviewView.enableSetNeedsDisplay = false // 因为后置摄像头的原生视频图像在 UIDeviceOrientationLandscapeLeft 中(即主页按钮在右侧),我们需要应用顺时针 90 度变换,以便我们可以像在风景中一样绘制视频预览 -面向视图;如果您使用前置摄像头并且想要进行镜像预览(以便用户在镜子中看到自己),则需要应用额外的水平翻转(通过将 CGAffineTransformMakeScale(-1.0, 1.0) 连接到旋转转变) videoPreviewView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2.0) videoPreviewView.frame = window.bounds // 我们将视频预览视图设为窗口的子视图,并将其发送到后面;这使得 ViewController 的视图(及其 UI 元素)位于视频预览之上,并且还使视频预览不受设备旋转的影响 window.addSubview(videoPreviewView) window.sendSubview(toBack: videoPreviewView) // 绑定帧缓冲区,得到帧缓冲区的宽高; // CIContext 在绘制到 GLKView 时使用的边界以像素(不是点)为单位, // 因此需要从帧缓冲区的宽度和高度中读取; // 此外,由于我们将访问另一个队列 (_captureSessionQueue) 中的边界, // 我们想要获取这条信息,这样我们就不会 // 从另一个线程/队列访问 _videoPreviewView 的属性 videoPreviewView.bindDrawable() videoPreviewViewBounds = CGRect.zero videoPreviewViewBounds.size.width = CGFloat(videoPreviewView.drawableWidth) videoPreviewViewBounds.size.height = CGFloat(videoPreviewView.drawableHeight) // 创建 CIContext 实例,注意这必须在 _videoPreviewView 正确设置后完成 ciContext = CIContext(eaglContext: eaglContext, 选项: [kCIContextWorkingColorSpace: NSNull()]) 如果 AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInTelephotoCamera, .builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices.count > 0 { 开始() } 别的 { print("没有带有 AVMediaTypeVideo 的设备") } } 覆盖 func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // 处理所有可以重新创建的资源。 } 函数开始(){ 让 videoDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices videoDevice = videoDevices.first var videoDeviceInput : AVCaptureInput! 做 { videoDeviceInput = 尝试 AVCaptureDeviceInput(设备:videoDevice) } 捕捉让错误 { print("无法获取视频设备输入,错误:\(error)") 返回 } 让预设 = AVCaptureSession.Preset.high captureSession = AVCaptureSession() captureSession.sessionPreset = 预设 // 核心图像 watns bgra 像素格式 让 outputSetting = [String(kCVPixelBufferPixelFormatTypeKey): kCVPixelFormatType_32BGRA] // 创建和配置视频数据输出 让 videoDataOutput = AVCaptureVideoDataOutput() videoDataOutput.videoSettings = outputSetting // 创建用于处理捕获会话委托方法调用的调度队列 captureSessionQueue = DispatchQueue(标签:“capture_session_queue”) videoDataOutput.setSampleBufferDelegate(self, queue: captureSessionQueue) videoDataOutput.alwaysDiscardsLateVideoFrames = true captureSession.beginConfiguration() 如果 !captureSession.canAddOutput(videoDataOutput) { print("不能添加视频数据输出") 捕获会话 = 无 返回 } captureSession.addInput(videoDeviceInput) captureSession.addOutput(videoDataOutput) captureSession.commitConfiguration() captureSession.startRunning() } } 扩展视图控制器:AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_输出:AVCaptureOutput,didOutput sampleBuffer:CMSampleBuffer,来自连接:AVCaptureConnection){ 让 imageBuffer : CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! 让 sourceImage = CIImage(cvImageBuffer: imageBuffer, options: nil) 让 sourceExtent = sourceImage.extent 让 vignetteFilter = CIFilter(name: "CIVignetteEffect", withInputParameters: nil) vignetteFilter?.setValue(sourceImage, forKey: kCIInputImageKey) vignetteFilter?.setValue(CIVector(x: sourceExtent.size.width/2.0, y: sourceExtent.size.height/2.0), forKey: kCIInputCenterKey) vignetteFilter?.setValue(sourceExtent.width/2.0, forKey: kCIInputRadiusKey) 让filteredImage = vignetteFilter?.outputImage 让 sourceAspect = sourceExtent.width/sourceExtent.height 让 previewAspect = videoPreviewViewBounds.width/videoPreviewViewBounds.height // 我们要保持屏幕大小的宽高比​​,所以我们剪辑视频图像 var drawRect = sourceExtent 如果 sourceAspect > previewAspect { // 使用视频图像的全高,并居中裁剪宽度 drawRect.origin.x += (drawRect.size.width - drawRect.size.height * previewAspect) / 2.0 drawRect.size.width = drawRect.size.height * previewAspect } 别的 { // 使用视频图像的全宽,并居中裁剪高度 drawRect.origin.y += (drawRect.size.height - drawRect.size.width / previewAspect) / 2.0; drawRect.size.height = drawRect.size.width / previewAspect; } videoPreviewView.bindDrawable() 如果 eaglContext != EAGLContext.current() { EAGLContext.setCurrent(eaglContext) } print("当前线程 \(Thread.current)") // 清除老鹰视图为灰色 glClearColor(0.5, 0.5, 0.5, 1.0); glClear(GLbitfield(GL_COLOR_BUFFER_BIT)); // 将混合模式设置为“source over”,以便 CI 使用它 glEnable(GLenum(GL_BLEND)); glBlendFunc(GLenum(GL_ONE), GLenum(GL_ONE_MINUS_SRC_ALPHA)); 如果让过滤图像 = 过滤图像 { ciContext.draw(filteredImage, in: videoPreviewViewBounds, from: drawRect) } videoPreviewView.display() } }

崩溃时的堆栈是

*线程#5,队列='com.apple.avfoundation.videodataoutput.bufferqueue',停止原因=EXC_BAD_ACCESS(代码=1,地址=0x8000000000000000) 帧#0:0x00000001a496f098 AGXGLDriver`___lldb_unnamed_symbol149$$AGXGLDriver + 332 帧 #1: 0x00000001923c029c OpenGLES`-[EAGLContext getParameter:to:] + 80 帧#2:0x000000010038bca4 libglInterpose.dylib`__clang_call_terminate + 1976832 帧#3:0x00000001001ab75c libglInterpose.dylib`__clang_call_terminate + 9400 帧#4:0x000000010038b8b4 libglInterpose.dylib`__clang_call_terminate + 1975824 帧#5:0x00000001001af098 libglInterpose.dylib`__clang_call_terminate + 24052 帧 #6:0x00000001001abe5c libglInterpose.dylib`__clang_call_terminate + 11192 帧#7:0x000000010038f9dc libglInterpose.dylib`__clang_call_terminate + 1992504 帧#8:0x000000010038d5b8 libglInterpose.dylib`__clang_call_terminate + 1983252 第 9 帧:0x000000019a1e2a20 GLKit`-[GLKView _display:] + 308 * 帧#10:0x0000000100065e78 RealTimeCameraPractice`ViewController.captureOutput(输出=0x0000000174034820,sampleBuffer=0x0000000119e25e70,连接=0x0000000174008850,self=0x0000000119d032d0:在ViewController.61) 帧 #11: 0x00000001000662dc RealTimeCameraPractice`@objc ViewController.captureOutput(_:didOutput:from:) at ViewController.swift:0 第 12 帧:0x00000001977ec310 AVFoundation`-[AVCaptureVideoDataOutput _handleRemoteQueueOperation:] + 308 第 13 帧:0x00000001977ec14c AVFoundation`__47-[AVCaptureVideoDataOutput _updateRemoteQueue:]_block_invoke + 100 帧#14:0x00000001926bdf38 CoreMedia`__FigRemoteOperationReceiverCreateMessageReceiver_block_invoke + 260 帧 #15:0x00000001926dce9c CoreMedia`__FigRemoteQueueReceiverSetHandler_block_invoke.2 + 224 帧 #16:0x000000010111da10 libdispatch.dylib`_dispatch_client_callout + 16 帧 #17:0x0000000101129a84 libdispatch.dylib`_dispatch_continuation_pop + 552 帧 #18:0x00000001011381f8 libdispatch.dylib`_dispatch_source_latch_and_call + 204 帧 #19:0x000000010111fa60 libdispatch.dylib`_dispatch_source_invoke + 828 帧 #20:0x000000010112b128 libdispatch.dylib`_dispatch_queue_serial_drain + 692 帧 #21:0x0000000101121634 libdispatch.dylib`_dispatch_queue_invoke + 852 帧 #22:0x000000010112b128 libdispatch.dylib`_dispatch_queue_serial_drain + 692 帧 #23:0x0000000101121634 libdispatch.dylib`_dispatch_queue_invoke + 852 帧 #24:0x000000010112c358 libdispatch.dylib`_dispatch_root_queue_drain_deferred_item + 276 帧 #25:0x000000010113457c libdispatch.dylib`_dispatch_kevent_worker_thread + 764 帧 #26:0x000000018ee56fbc libsystem_pthread.dylib`_pthread_wqthread + 772 帧 #27: 0x000000018ee56cac libsystem_pthread.dylib`start_wqthread + 4

我的项目在github
https://github.com/hegrecom/iOS-RealTimeCameraPractice

【问题讨论】:

  • 下意识的查询:captureOutput 确定是序列化的吗?您不会无意中尝试同时在两个线程上绑定相同的 GL 上下文吗?自从我使用 AVFoundation 以来已经很长时间了,为愚蠢的问题道歉。另外,你能告诉我们关于这个问题是如何触发的吗?如果是在您的应用处于后台或后台运行时,这不足为奇,因为在此期间禁止使用 GPU,而且您似乎没有为此做任何测试。
  • @Tommy 是的,捕获输出交给captureSessionQueue = DispatchQueue(label: "capture_session_queue") 这个队列。这个队列是串行队列。不,只能在该队列中访问 gl 上下文。我希望我知道一些引发问题的触发器。但是,它只是在我的应用程序启动后随机发生。它在前台,绝对不在后台。

标签: ios swift opengl-es glkit


【解决方案1】:

这里的解决方案: iOS 11 beta 4 presentRenderbuffer crash

转到管理方案->选项->GPU帧捕获->禁用

【讨论】:

  • 我一直在寻找的完美解决方案。谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-12-15
  • 1970-01-01
  • 2011-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多