How to create files with different names using the least amount of processing power in Swift? - swift

I am writing a video app that records video only when triggered and at the end of all the recordings merges the recordings together into one at the end.
I was just wondering if there is process in swift to make sure the name of the next file of a recording is different than the previous one? I know ways of doing this that are fine, but I am a bit of a memory freak and was wondering if swift has a built in answer to this problem?
Edit:
This works with the variable filenamechanger. I just was wondering if there is an even better way.
var filenamechanger = 0
if motiondetected == true {
do{
let documentsDir = try FileManager.default.url(for:.documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
filenamechanger += 1 //name changer
let fileURL = URL(string:"test\(filenamechanger).mp4", relativeTo:documentsDir)!
do {
try FileManager.default.removeItem(at:fileURL)
} catch {
}
self.movieOutput = try MovieOutput(URL:fileURL, size:Size(width:480, height:640), liveVideo:true)
self.camera.audioEncodingTarget = self.movieOutput
self.camera --> self.movieOutput!
self.movieOutput!.startRecording()
sleep(3)
self.camera.audioEncodingTarget = nil
self.movieOutput = nil
motiondetected = false
}
catch{
"recording didn't work"
}
}

Related

Reading Binary File Piecemeal and Converting to Integers With Memory Efficiency

Using Swift, I need to read integers from a binary files but can't read whole files into memory because of their size. I have 61G bytes(7.7 billion Integers) of data written into a dozen files of various sizes. The largest is 18G bytes(2.2 billion Integers). Some of the files might be read completely into memory but the largest is greater than available RAM.
Insert File IO Rant Here.
I have written the code to write the file 10 Million bytes at a time and it works well. I wrote this as a class but none of the rest of the code is object oriented. This is not an App so there is no idle time to do memory cleanup. Here is the code:
class BufferedBinaryIO {
var data = Data(capacity: 10000000)
var data1:Data?
let fileName:String!
let fileurl:URL!
var fileHandle:FileHandle? = nil
var (forWriting,forReading) = (false,false)
var tPointer:UnsafeMutablePointer<UInt8>?
var pointer = 0
init?(forWriting name:String) {
forWriting = true
fileName = name
fileurl = URL(fileURLWithPath:fileName)
if FileManager.default.fileExists(atPath: fileurl.path) {
try! fileHandle = FileHandle(forWritingTo: fileurl)
if fileHandle == nil {
print("Can't open file to write.")
return nil
}
}
else {
// if file does not exist write data for the first time
do{
try data.write(to: fileurl, options: .atomic)
try fileHandle = FileHandle(forWritingTo: fileurl)
} catch {
print("Unable to write in new file.")
return nil
}
}
}
init?(forReading name:String) {
forReading = true
fileName = name
fileurl = URL(fileURLWithPath:fileName)
if FileManager.default.fileExists(atPath: fileurl.path) {
try! fileHandle = FileHandle(forReadingFrom: fileurl)
if fileHandle == nil {
print("Can't open file to write.")
return nil
}
}
else {
// if file does not exist write data for the first time
do{
try fileHandle = FileHandle(forWritingTo: fileurl)
} catch {
print("Unable to write in new file.")
return nil
}
}
}
deinit {
if forWriting {
fileHandle?.seekToEndOfFile()
fileHandle?.write(data)
}
try? fileHandle?.close()
}
func write(_ datum: Data) {
guard forWriting else { return }
self.data.append(datum)
if data.count == 10000000 {
fileHandle?.write(data)
data.removeAll()
}
}
func readInt() -> Int? {
if data1 == nil || pointer == data1!.count {
if #available(macOS 10.15.4, *) {
//data1?.removeAll()
//data1 = nil
data1 = try! fileHandle?.read(upToCount: 10000000)
pointer = 0
} else {
// Fallback on earlier versions
}
}
if data1 != nil && pointer+8 <= data1!.count {
let retValue = data1!.withUnsafeBytes { $0.load(fromByteOffset: pointer,as: Int.self) }
pointer += 8
// data.removeFirst(8)
return retValue
} else {
print("here")
}
return nil
}
}
As I said writing to the file works fine and I can read from the file but I have a problem.
Some of the solutions for reading binary and converting it to various types use code like:
let rData = try! Data(contentsOf: url)
let tPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: rData.count)
rData.copyBytes(to: tPointer, count: rData.count)
The first line reads in the whole file consuming a like amount of memory and the next two lines double the memory consumption. So even if I have 16G bytes of Ram I can only read an 8Gbyte file because it has to double consume memory.
As you can see my code does not use this code. For the read I just read the file into data1, 10 million bytes at a time, and then use data1 like it was a regular data type and access it and can read the data fine, without doubling the memory usage.
The code in the body of the program that uses this code looks like:
file loop .... {
let string = String(format:"~path/filename.data")
let dataPath = String(NSString(string: string).expandingTildeInPath)
let fileBuffer = BufferedBinaryIO(forReading: dataPath)
while let value = fileBuffer!.readInt() {
loop code
}
}
Here is my problem: This code works to read the file into Ints but inside readInt, the code does not release the memory from the previous fileHandle?.read when it does the next fileHandle?.read. So as I go through the file the memory consumption goes up 10 million each time it fills the buffer until the program crashes.
Forgive my code as it is a work in progress. I keep changing it to try out different things to fix this problem. I used data1 as an optional variable for the read portion of the code, thinking setting it to nil would deallocate the memory. It does the same thing when I just over write it.
That being said, this would be a nice way to code this if it worked.
So the question is do I have a memory retention cycle or is there a magic bean I need to use on data1 get it to stop doing this?
Thank you in advance for your consideration of this problem.
You don't show your code that actually reads from your file, so it's a bit hard to be sure what's going on.
From the code you did show we can tell you're using a FileHandle, which allows random access to a file and reading arbitrary-sized blocks of data.
Assuming you're doing that part right and reading 10 million bytes at a time, your problem may be the way iOS and Mac OS handle memory. For some things, the OS puts no-longer-used memory blocks into an "autorelease pool", which gets freed when your code returns and the event loop gets serviced. If you're churning through multiple gigabytes of file data synchronously, it might not get a chance to release the memory before the next pass.
(Explaining Mac OS/iOS memory management in enough detail to cover autoreleasing would be pretty involved. If you're interested, I suggest you look up Apple manual reference counting and automatic reference counting, a.k.a ARC, and look for results that explain what goes on "under the covers".)
Try putting the code that reads 10 million bytes of data into the closure of an autoreleasePool() statement. That will cause any autoreleased memory to actually get released. Something like the pseudo-code below:
while (more data) {
autoreleasepool {
// read a 10 million byte block of data
// process that block
}
}

How to fix thread or handle error in Swift?

I can't get rid of an error in the program using Core ML image recognize!
My code is basically debugged and works with a large number of files, but on 421 files the EXC_BAD_ACCESS error occurs (and I changed the order of processing files to eliminate the problem of broken files)
I have already translated all potentially dangerous objects into singletons to avoid leaks, debugged and tidied the sqlite database, rewritten the code using the recommended generateCGImagesAsynchronously operator to get a picture from a video file, everything is cleaned up and works without leaks, but the error still occurs
Directly the code for extracting text objects is taken from the network and slightly upgraded, maybe there is a problem with it?, help!
CreateAltModel3.shared.handler = VNImageRequestHandler(ciImage: inputImage!)
CreateAltModel3.shared.request = VNCoreMLRequest(model: CreateAltModel3.shared.visionModel!) { [weak self] req3, error in
output_handler(req3.results) }
do {try CreateAltModel3.shared.handler!.perform([CreateAltModel3.shared.request!])} catch {print ("hadler3 error - request3")}
here is the code for the output_handler function
func output_handler(_ results: [Any]?) {
guard let results = results as? [VNClassificationObservation]
else {return}
for class1 in results {
if (class1.confidence > 0.05) { // Only show greater than 5%
let fileID = filesArray[currentFileIndex]?.index
if percentRecogn <= Int(round(class1.confidence*100)) {
findKeyStr = (class1.identifier + " " + String(round(class1.confidence*100)) + "%")
resultKeyStr.0 = Int64(fr)
let comma = (resultKeyStr.1 == "") ? "" : ", "
let cutDubbing = (resultKeyStr.1 == class1.identifier) ? resultKeyStr.1 : resultKeyStr.1 + comma + class1.identifier
resultKeyStr.1 = cutDubbing
resultKeyStr.2 = Int64(fileID!)
}
}
}
}
but the error as I understand it occurs after exiting this function
on the next line of code
more code to spread does not make sense because I will only confuse you with extra information, I will only add that this code works in a loop in the global stream and outputs information to the label and ImageView in the main stream as expected, and everything seems to be debugged but I can not understand the nature of the error, perhaps something related to streams but there is not even an idea where to dig)
You cannot (or at least should not) use a single VNCoreMLRequest from multiple threads at a time.

Swift - How to wait for something without making the app hanging

I have a code like this:
print("Migration Execution: Successfully uninstalled MCAfee")
migrationInfoPicture.image = NSImage(named: "Unroll")
migrationInfoText.stringValue = NSLocalizedString("Unrolling from old server... Please wait!", comment: "Unrolling")
while(!readFile(path:logfilePath)!.contains("result: 2 OK")) {
searchLogForError(scriptPath: scriptOnePath)
}
print("Migration Execution: Successfully unrolled from old server")
migrationInfoText.stringValue = NSLocalizedString("Setting up MDM profile... Please wait!", comment: "Setting up MDM")
while(!readFile(path:logfilePath)!.contains("result: 3 OK")) {
searchLogForError(scriptPath: scriptOnePath)
}
It actually works in the background, reading from the file works and logging works but since the GUI will be hanging executing a while loop with a quickly completed task, the image and the text changes will not be visible.
Code for searchForLogError is:
func searchLogForError(scriptPath:String) {
if((readFile(path:logfilePath)!.filter { $0.contains("ERROR") }).contains("ERROR")) {
print("Migration abborted")
migrationInfoPicture.image = NSImage(named: "FatalError")
migrationInfoText.stringValue = NSLocalizedString("An error occured: \n", comment: "Error occurence") + readFile(path:logfilePath)!.filter { $0.contains("ERROR") }[0]
migrationWarningText.stringValue = NSLocalizedString("In order to get further help, please contact: mac.workplace#swisscom.com", comment: "Error support information")
self.view.window?.level = .normal
btnExitApplicationOutlet.isHidden = false
getScriptProcess(path:scriptPath).terminate()
return
}
}
How can I achieve a visible change of NSImage and NSLocalizedString while constantly looking for log file change without a hanging GUI (or even with a hanging GUI, but with enough time to change the visible elements between the while-loops)?
Polling file system resources is a horrible practice. Don't do that. There are dedicated APIs to observe file system resources for example DispatchSourceFileSystemObject
Create a property
var fileSystemObject : DispatchSourceFileSystemObject?
and two methods to start and stop the observer. In the closure of setEventHandler insert the code to read the file
func startObserver(at url: URL)
{
if fileSystemObject != nil { return }
let fileDescriptor : CInt = open(url.path, O_EVTONLY);
if fileDescriptor < 0 {
print("Could not open file descriptor"))
return
}
fileSystemObject = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fileDescriptor, eventMask: [.write, .rename], queue: .global())
if fileSystemObject == nil {
close(fileDescriptor)
print"Could not create Dispatch Source"))
return
}
fileSystemObject!.setEventHandler {
if self.fileSystemObject!.mask.contains(.write) {
// the file has been modified, do something
}
}
fileSystemObject!.setCancelHandler {
close(fileDescriptor)
}
fileSystemObject!.resume()
}
func stopObserver()
{
fileSystemObject?.cancel()
fileSystemObject = nil
}

Append multiple VNCoreMLModel ARKit and CoreML

I'm a noob and I don't really know how can I happened multiple CoreML model to the VNCoreMLRequest.
With the code below is just using one model but I want to append also another model (visionModel2 on the example below). Can anyone help me? Thank you!
private func performVisionRequest(pixelBuffer: CVPixelBuffer){
let visionModel = try! VNCoreMLModel(for: self.iFaceModel.model)
let visionModel2 = try! VNCoreMLModel(for: self.ageModel.model)
let request = VNCoreMLRequest(model: visionModel){ request, error in
if error != nil {
return
}
guard let observations = request.results else {
return
}
let observation = observations.first as! VNClassificationObservation
print("Name \(observation.identifier) and confidence is \(observation.confidence)")
DispatchQueue.main.async {
if observation.confidence.isLess(than: 0.04) {
self.displayPredictions(text: "Not recognized")
print("Hidden")
}else {
self.displayPredictions(text: observation.identifier)
}
}
}
To evaluate an image using multiple ML models, you’ll need to perform multiple requests. For example:
let faceModelRequest = VNCoreMLRequest(model: visionModel)
let ageModelRequest = VNCoreMLRequest(model: visionModel2)
let handler = VNImageRequestHandler( /* my image and options */ )
handler.perform([faceModelRequest, ageModelRequest])
guard let faceResults = faceModelRequest.results as? [VNClassificationObservation],
let ageResults = ageModelRequest.results as? [VNClassificationObservation]
else { /*handle errors from each request */ }
(Yes, you can run Vision requests without a completion handler and then collect the results from multiple requests. Might want to check prefersBackgroundProcessing on the requests and dispatch everything to a background queue yourself, though.)
After that, you probably want to iterate the results from both requests together. Here’s a handy way you could do that with Swift standard library sequence functions, but it assumes that both models return information about the same faces in the same order:
for (faceObservation, ageObservation) in zip (faceResults, ageResults) {
print(“face \(faceObservation.classification) confidence \(faceObservation.confidence)”)
print(“age \(ageObservation.classification) confidence \(ageObservation.confidence)”)
// whatever else you want to do with results...
}
Disclaimer: Code written in StackExchange iOS app, not tested. But it’s at least a sketch of what you’re probably looking for — tweak as needed.

Unexpectedly unwrapping an optional to find a nil after an API call to Spotify

So I know this may be a bit specific but I've been staring at my code and am unable to resolve this issue. Basically, I'm making a network call to spotify to obtain a certain playlist and pass a number that will ultimately determine the number of songs I get back. The code is basically as follows:
// A network call is made just above to return somePlaylist
let playlist = somePlaylist as! SPTPartialPlaylist
var songs: [SPTPartialTrack] = []
// load in playlist to receive back songs
SPTPlaylistSnapshot.playlistWithURI(playlist.uri, session: someSession) { (error: NSError!, data: AnyObject!) in
// cast the data into a correct format
let playlistViewer = data as! SPTPlaylistSnapshot
let playlist = playlistViewer.firstTrackPage
// get the songs
for _ in 1...numberOfSongs {
let random = Int(arc4random_uniform(UInt32(playlist.items.count)))
songs.append(playlist.items[random] as! SPTPartialTrack)
}
}
The problem comes at the portion of code that initializes random. In maybe 1 in 20 calls to this function I, for whatever, reason unwrap a nil value for playlist.items.count and can't seem to figure out why. Maybe it's something I don't understand about API calls or something else I'm failing to see but I can't seem to make sense of it.
Anyone have any recommendations on addressing this issue or how to go about debugging this?
Ok, after sleeping on it and working on it some more I seem to have resolved the issue. Here's the error handling I implemented into my code.
if let actualPlaylist = playlist, actualItems = actualPlaylist.items {
if actualItems.count == 0 {
SongScraper.playlistHasSongs = false
print("Empty playlist, loading another playlist")
return
}
for _ in 1...numberOfSongs {
let random = Int(arc4random_uniform(UInt32(actualItems.count)))
songs.append(actualPlaylist.items[random] as! SPTPartialTrack)
}
completionHandler(songs: songs)
}
else {
print("Returned a nil playlist, loading another playlist")
SongScraper.playlistHasSongs = false
return
}