I am using ReplayKit to record the screen. However, I have a very strange issue with having video buffers. When user taps record button, the following method is called;
rpScreenRecorder.startCapture { (sampleBuffer, bufferType, error) in
if let error = error {
print(error.localizedDescription)
}
switch bufferType {
case .video:
print("video")
case .audioApp:
print("audioApp")
case .audioMic:
print("audioMic")
#unknown default:
print("default")
}
} completionHandler: { (error) in
if let error = error {
print(error.localizedDescription)
}
}
The problem is, bufferType does not have video. When I print all cases, audioMic and audioApp is returned forever but video is never returned.
I can easily repeat this problem only on the first launch when I remove the app and install it again. It works as expected in other cases.
p.s: I had a look at other issues before asking this question. None of them solved the problem.
After spending days and nights to solve this issue, I finally came up with a solution.
Simply, the reason was about the main window
Main window is important for replay kit because it is recording the main window.
I was using different windows to hide some views from the recorded video. I also changed windowLevel of the main window. Today, I noticed that removing every adjustment related to main window solved the problem. (other windows have no effect.)
Related
I have an iphone 11pro ios 15.0 device. The video is playable but When I call 'deleteAssets' the 'completionHandler' never runs.
PHPhotoLibrary.shared().performChanges {
PHAssetChangeRequest.deleteAssets(deleteAccess as NSFastEnumeration)
} completionHandler: { [weak self] success, error in
//Never run
}
Does anyone know why this happens? Does anyone have a solution for this?
I do not know. But after I reset my iPhone, everything is back to normal. Maybe it's a system error.
When I want to pause the session, this was the only solution for me:
func pauseSession () {
self.sessionQueue.async {
if self.session.isRunning {
self.session.stopRunning()
}
}
}
func resumeSession () {
self.sessionQueue.async {
if !self.session.isRunning {
self.session.startRunning()
}
}
}
This seems to completely stop the session, which is fine, yet looks expensive.
The issue I seem to have is if pause and resume are called near each other in time, the whole app freezes for about 10 seconds, till going back to being responsive. This is mostly due to it still hasn't finished the last process (whether to stop or start).
Is there a solution to this?
The native camera app seems to do this fine. If you open it, open the last photo, you can see the green indicator on the top right going off, meaning the session has paused/stopped. If you swipe down on the photo, the session resumes. If you swipe and let it get canceled, quickly swipe again you can see the session pauses and resumes quickly over and over without any issues.
You may need to change async to sync
self.sessionQueue.sync {
I'm trying to use ReplayKit to create a live broadcast inside of my app.
Basically I want to share my screen and see the screen of the other user.
To get the buffers, ReplayKit offers the next function:
func startCapture(handler captureHandler: ((CMSampleBuffer, RPSampleBufferType, Error?) -> Void)?, completionHandler: ((Error?) -> Void)? = nil)
So this is my method to startup ReplayKit and get the buffers:
private func startRecording() {
RPScreenRecorder.shared().startCapture(handler: { (sampleBuffer, bufferType, error) in
switch bufferType {
case RPSampleBufferType.video:
// Handle buffer and send it to server
break
case RPSampleBufferType.audioApp:
break
case RPSampleBufferType.audioMic:
break
}
}, completionHandler: nil)
}
This works perfectly, but I'm facing the next issue; if the screen gets changes constantly, like a flashing button, when I send the app to background and come back several times ReplayKit stops calling its capture handler.
Maybe the problem is that the function startCapture is made to record the screen for a limited time, and not for a live broadcast.
I have made an example in Github with a flashing button that shows the problem that I'm having; ReplayKit runs normally until going to background repeatedly; then it stops, and the only way to make it work again is to reboot the device.
My app (Xcode 9.2, Swift 4) uses UIManagedDocument as a basic Core Data stack. Everything was working fine for months but lately I've noticed several cases where the app won't load for existing users because the core data init isn't completing. This usually happens after a crash in the app (I think but not sure).
I've been able to recreate the problem on the debugger and narrowed the problem down to the following scenario:
App starts up --> core data is called to start up --> UIManagedDocument object is init'd --> check doc status == closed --> call open() on doc --> open never completes - the callback closure is never called.
I've subclassed UIManagedDocument so I could override configurePersistentStoreCoordinator() to check if it ever reaches that point but it doesn't. The subclass override for handleError() is never called either.
The open() process never reaches that point. What I can see if I pause the debugger is that a couple of threads are blocked on mutex/semaphore related to the open procedure:
The 2nd thread (11) seems to be handling some kind of file conflict but I can't understand what and why. When I check documentState just before opening the file I can see its value is [.normal, .closed]
This is the code to init the doc - pretty straight forward and works as expected for most uses and use cases:
class MyDataManager {
static var sharedInstance = MyDataManager()
var managedDoc : UIManagedDocument!
var docUrl : URL!
var managedObjContext : NSManagedObjectContext {
return managedDoc.managedObjectContext
}
func configureCoreData(forUser: String, completion: #escaping (Bool)->Void) {
let dir = UserProfile.profile.getDocumentsDirectory()
docUrl = dir.appendingPathComponent(forUser + GlobalDataDocUrl, isDirectory: true)
managedDoc = UIManagedDocument(fileURL: docUrl)
//allow the UIManagedDoc to perform lieghtweight migration of the DB in case of small changes in the model
managedDoc.persistentStoreOptions = [
NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true
]
switch (self.managedDoc.documentState)
{
case UIDocumentState.normal:
DDLogInfo("ManagedDocument is ready \(self.docUrl)")
case UIDocumentState.closed:
DDLogInfo("ManagedDocument is closed - will open it")
if FileManager.default.fileExists(atPath: self.docUrl.path) {
self.managedDoc.open() { [unowned self] (success) in
DDLogInfo("ManagedDocument is open result=\(success)")
completion(success)
}
}
else{
self.managedDoc.save(to: self.managedDoc.fileURL, for: .forCreating) { [unowned self] (success) in
DDLogInfo("ManagedDocument created result=\(success) ")
completion(success)
}
}
case UIDocumentState.editingDisabled:
fallthrough
case UIDocumentState.inConflict:
fallthrough
case UIDocumentState.progressAvailable:
fallthrough
case UIDocumentState.savingError:
fallthrough
default:
DDLogWarn("ManagedDocument status is \(self.managedDoc.documentState.rawValue)")
}
}
}
Again - the closure callback for managedDoc.open() never gets called. It seems like the file was left in some kind of bad state and cannot be opened.
BTW, if I copy the app container from the device to my mac and open the SQLLite store I can see everything is there as expected.
I am using replay kit to save a video of my screen during gameplay but randomly on occasion startRecordingWithMicrophoneEnabled and recorder.stopRecordingWithHandler never enters the completion handler
it doesn't throw an error, it just runs and hangs indefinitely.
if recorder.available && recorder.microphoneEnabled {
recorder.startRecordingWithMicrophoneEnabled(true) { [unowned self] (error) in
if let unwrappedError = error {
print(unwrappedError.localizedDescription)
} else {
print("called")
self.manager.instructions.text = "Click to Start Game"
}
}
}
if recorder.available && recorder.microphoneEnabled {
print("initiating stop recording")
recorder.stopRecordingWithHandler { [unowned self] (RPPreviewViewController, error) in
print("in completion handler")
if let previewView = RPPreviewViewController {
print("will transition to gameplay video")
previewView.previewControllerDelegate = self
self.presentViewController(previewView, animated: true, completion: nil)
self.sessionHandler.session.stopRunning()
}
}
}
I was getting this same thing. Was working on one device, and not on another. Only difference was the working device was on iOS version 10.1.0 and the other was iOS version 10.0.2 - upgraded to 10.2.0 and it started working immediately.
I had the same problem and just found its cause (or maybe just a cause). If your device is connected to a WiFi that has no internet access this problem occurs. If you connect it to a WiFi that has internet access or disable WiFi it works just fine. I guess when starting a recording, ReplayKit tries to connect to some Apple servers but never reaches them and also never times out. You can observe the same behavior with the App Store. When you are connected to a WiFi without internet it tries to load the store forever and never times out.