I am putting together a simple video editor app for iOS. The videos are exported with a "watermark" in the form of a text overlay (e.g. "This video was made with XYZ").The app (still a prototype) was working until around February this year. Then I got busy, moved to other projects and stopped working on it for a while.About a months ago, I resumed work on the app but suddenly noticed that it was crashing whenever I attempted to export any video.The crash looks like this:libxpc.dylib`_xpc_api_misuse:
0x7fff51c53154 <+0>: pushq %rbp
0x7fff51c53155 <+1>: movq %rsp, %rbp
0x7fff51c53158 <+4>: pushq %rbx
0x7fff51c53159 <+5>: subq $0xa8, %rsp
0x7fff51c53160 <+12>: movq %rdi, %r9
0x7fff51c53163 <+15>: movaps 0xdba6(%rip), %xmm0 ; __xpcVersionNumber + 160
0x7fff51c5316a <+22>: leaq -0xb0(%rbp), %rbx
0x7fff51c53171 <+29>: movaps %xmm0, 0x90(%rbx)
0x7fff51c53178 <+36>: movaps %xmm0, 0x80(%rbx)
0x7fff51c5317f <+43>: movaps %xmm0, 0x70(%rbx)
0x7fff51c53183 <+47>: movaps %xmm0, 0x60(%rbx)
0x7fff51c53187 <+51>: movaps %xmm0, 0x50(%rbx)
0x7fff51c5318b <+55>: movaps %xmm0, 0x40(%rbx)
0x7fff51c5318f <+59>: movaps %xmm0, 0x30(%rbx)
0x7fff51c53193 <+63>: movaps %xmm0, 0x20(%rbx)
0x7fff51c53197 <+67>: movaps %xmm0, 0x10(%rbx)
0x7fff51c5319b <+71>: movaps %xmm0, (%rbx)
0x7fff51c5319e <+74>: leaq 0x1150d(%rip), %r8 ; "XPC API Misuse: %s"
0x7fff51c531a5 <+81>: movl $0xa0, %esi
0x7fff51c531aa <+86>: movl $0xa0, %ecx
0x7fff51c531af <+91>: movq %rbx, %rdi
0x7fff51c531b2 <+94>: movl $0x0, %edx
0x7fff51c531b7 <+99>: xorl %eax, %eax
0x7fff51c531b9 <+101>: callq 0x7fff51c5fe18 ; symbol stub for: __snprintf_chk
0x7fff51c531be <+106>: movq %rbx, 0x380787c3(%rip) ; gCRAnnotations + 8
0x7fff51c531c5 <+113>: leaq 0x114f9(%rip), %rax ; "API Misuse"
0x7fff51c531cc <+120>: movq %rax, 0x380787bd(%rip) ; gCRAnnotations + 16
-> 0x7fff51c531d3 <+127>: ud2 < Thread 55: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)After experimenting a bit by eliminating features, I found out that the app crashes only if the exported video compositions contain the text overlay: if I comment out the code responsible for overlaying the text layer, the issue resolves.This is the code I am using to export the video composition:func export(completion: @escaping (() -> Void), failure: @escaping ((Error) -> Void)) {
let exportQuality = AVAssetExportPresetHighestQuality
guard let exporter = AVAssetExportSession(asset: composition, presetName: exportQuality) else {
return failure(ProjectError.failedToCreateExportSession)
}
guard let documents = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else {
return failure(ProjectError.temporaryOutputDirectoryNotFound)
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd_HHmmss"
let fileName = dateFormatter.string(from: Date())
let fileExtension = "mov"
let fileURL = documents.appendingPathComponent(fileName).appendingPathExtension(fileExtension)
exporter.outputURL = fileURL
exporter.outputFileType = AVFileType.mov
exporter.shouldOptimizeForNetworkUse = true
if shouldAddWatermark {
// Watermark overlay:
let frame = CGRect(origin: .zero, size: videoComposition.renderSize)
let watermark = WatermarkLayer(frame: frame)
let parentLayer = CALayer()
let videoLayer = CALayer()
parentLayer.frame = frame
videoLayer.frame = frame
parentLayer.addSublayer(videoLayer)
parentLayer.addSublayer(watermark)
videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)
}
exporter.videoComposition = videoComposition
exporter.exportAsynchronously {
if exporter.status == .completed {
/*
Composition was successfully saved to the documents folder. Now create a new asset in the
device's camera roll (i.e. photo library):
*/
AssetLibrary.saveVideo(at: fileURL, completion: {
completion()
}, failure: {(error) in
failure(ProjectError.failedToExportToPhotoLibrary(detail: error?.localizedDescription ?? "Unknown"))
})
} else {
DispatchQueue.main.async {
failure(ProjectError.failedToExportComposition(detail: exporter.error?.localizedDescription ?? "Unknown"))
}
}
}
}The WatermarkLayer class used above is defined as follows:class WatermarkLayer: CATextLayer {
// MARK: - Constants
private let defaultFontSize: CGFloat = 48
private let rightMargin: CGFloat = 10
private let bottomMargin: CGFloat = 10
// MARK: - Initialization
init(frame: CGRect) {
super.init()
guard let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String else {
fatalError("!!!")
}
self.foregroundColor = CGColor.srgb(r: 255, g: 255, b: 255, a: 0.5)
self.backgroundColor = CGColor.clear
self.string = String(format: String.watermarkFormat, appName)
self.font = CTFontCreateWithName(String.watermarkFontName as CFString, defaultFontSize, nil)
self.fontSize = defaultFontSize
self.shadowOpacity = 0.75
self.alignmentMode = .right
self.frame = frame
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented. Use init(frame:) instead.")
}
// MARK: - CALayer
override func draw(in ctx: CGContext) {
let height = self.bounds.size.height
let fontSize = self.fontSize
//let yDiff = (height-fontSize)/2 - fontSize/10 // Center
let yDiff = (height-fontSize) - fontSize/10 - bottomMargin // Bottom (minus margin)
ctx.saveGState()
ctx.translateBy(x: -rightMargin, y: yDiff)
super.draw(in: ctx)
ctx.restoreGState()
}
}What's going on? Which API is being "misused"?
Post
Replies
Boosts
Views
Activity
I have successfully incorporated some basic 3D content into my SpriteKit-based app.Next, I want to display some sprites overlaid on top of the 3D content, but can't manage to do so.Manipulating the .zPosition porperty of the SK3DNode and SKSpriteNode involded doesn't seem to do it.This is my code for both the Spritekit scene, and the SceneKit scene whose content is displayed within the SK3DNode:SpriteKit-side:import SpriteKit
class SpriteKitScene: SKScene {
override init(size: CGSize) {
super.init(size: size)
// Scene Background
self.backgroundColor = .red
// 3D Node
let objectNode = SK3DNode(viewportSize: size)
objectNode.scnScene = SceneKitScene()
addChild(objectNode)
objectNode.position = CGPoint(x: size.width/2, y: size.height/2)
let camera = SCNCamera()
let cameraNode = SCNNode()
cameraNode.camera = camera
objectNode.pointOfView = cameraNode
objectNode.pointOfView?.position = SCNVector3(x: 0, y: 0, z: 60)
objectNode.zPosition = -100
// 2D Sprite
let sprite = SKSpriteNode(color: .yellow, size: CGSize(width: 250, height: 60))
sprite.position = objectNode.position
sprite.zPosition = +100
addChild(sprite)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}SceneKit-side:import SceneKit
class SceneKitScene: SCNScene {
override init() {
super.init()
let box = SCNBox(width: 10, height: 10, length: 10, chamferRadius: 0)
let material = SCNMaterial()
material.diffuse.contents = UIColor.green
box.materials = [material]
let boxNode = SCNNode(geometry: box)
boxNode.transform = SCNMatrix4MakeRotation(.pi/2, 1, 1, 1)
self.rootNode.addChildNode(boxNode)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}The sprites seem to be rendered at exatly the "far plane" of the SK3DNode's camera; if I push my geometry back to the far plane, it gets clipped ewxactly where it "intersects" the srpite.