I\'m streaming a content of my app to my RTMP server and using RPBroadcastSampleHandler.
One of the methods is
override func processSampleBuffer(_ s
You can try this:
override func broadcastFinished() {
Log(#function)
...
// Need to give the end CMTime, if not set, the video cannot be used
videoWriter.endSession(atSourceTime: ...)
videoWriter.finishWriting {
// Callback cannot be executed here
}
...
// The program has been executed.
}
@Marty's answer should be accepted because he pointed out the problem and its DispatchGroup
solution works perfectly.
Since he used a while
loop and didn't describe how to use DispatchGroup
s, here's the way I implemented it.
override func broadcastFinished() {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
self.writerInput.markAsFinished()
self.writer.finishWriting {
// Do your work to here to make video available
dispatchGroup.leave()
}
dispatchGroup.wait() // <= blocks the thread here
}
From https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwritingwithcompletionhandl
This method returns immediately and causes its work to be performed asynchronously
When broadcastFinished
returns, your extension is killed. The only way I've been able to get this to work is by blocking the method from returning until the video processing is done. I'm not sure if this is the correct way to do it (seems weird), but it works. Something like this:
var finishedWriting = false
videoWriter.finishWriting {
NSLog("DEBUG:::The videoWriter finished writing.")
if videoWriter.status == .completed {
NSLog("DEBUG:::The videoWriter status is completed")
let fileManager = FileManager.default
if fileManager.fileExists(atPath: self.videoOutputFullFileName!) {
NSLog("DEBUG:::The file: \(self.videoOutputFullFileName ?? "") has been saved in documents folder, and is ready to be moved to camera roll")
let sharedFileURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.xxx.com")
guard let documentsPath = sharedFileURL?.path else {
NSLog("ERROR:::No shared file URL path")
finishedWriting = true
return
}
let finalFilename = documentsPath + "/test_capture_video.mp4"
//Check whether file exists
if fileManager.fileExists(atPath: finalFilename) {
NSLog("WARN:::The file: \(finalFilename) exists, will delete the existing file")
do {
try fileManager.removeItem(atPath: finalFilename)
} catch let error as NSError {
NSLog("WARN:::Cannot delete existing file: \(finalFilename), error: \(error.debugDescription)")
}
} else {
NSLog("DEBUG:::The file \(self.videoOutputFullFileName!) doesn't exist")
}
do {
try fileManager.copyItem(at: URL(fileURLWithPath: self.videoOutputFullFileName!), to: URL(fileURLWithPath: finalFilename))
}
catch let error as NSError {
NSLog("ERROR:::\(error.debugDescription)")
}
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: "xxx")
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: finalFilename))
}) { completed, error in
if completed {
NSLog("Video \(self.videoOutputFullFileName ?? "") has been moved to camera roll")
}
if error != nil {
NSLog("ERROR:::Cannot move the video \(self.videoOutputFullFileName ?? "") to camera roll, error: \(error!.localizedDescription)")
}
finishedWriting = true
}
} else {
NSLog("ERROR:::The file: \(self.videoOutputFullFileName ?? "") doesn't exist, so can't move this file camera roll")
finishedWriting = true
}
} else {
NSLog("WARN:::The videoWriter status is not completed, status: \(videoWriter.status)")
finishedWriting = true
}
}
while finishedWriting == false {
// NSLog("DEBUG:::Waiting to finish writing...")
}
I would think you'd have to also call extensionContext.completeRequest
at some point, but mine's working fine without it shrug.