Detect if video is still being written to

So, I'm trying to write a function to detect if a video file is currently being recorded to. The purpose is if it is, than you can't delete the file or rename it. Here's what I have:


public func fileSizeDetection(url: URL) -> Int{
    
    let tempFile = URL(string:(try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).absoluteString) + "tempFile")
    
    let keys : Set<URLResourceKey> = [URLResourceKey.fileSizeKey]
    
    
    do{
        try FileManager.default.copyItem(at: url, to: tempFile!)
        usleep(5000)
    }
    catch{
        print (error.localizedDescription)
    }
    
    let tempValues = try! tempFile!.resourceValues(forKeys: keys)
    let tempSize = tempValues.fileSize!
    let permValues = try! url.resourceValues(forKeys: keys)
    let permSize = permValues.fileSize!
    let difference = permSize - tempSize
    
    do{
        try FileManager.default.removeItem(at: tempFile!)
    }
    catch{
        print (error.localizedDescription)
    }
    
    return difference
}


So, I it starts by passing the URL to test. Than it creates a temporary copy of the file which is supposed to be a snapshot of it. Than it sleeps for a second, and compares the current file size to the copied file size. If there's no difference than it's not a recording in progress. Finally it deletes the temp copy.


The issue I'm having, is that tempFile and the original URL are the same size! I tried to increase usleep, but it still doesn't work. Can anyone please tell me what I'm doing wrong!

I'm trying to write a function to detect if a video file is currently being recorded to.

Who is doing this recording? The usual way to handle this problem is to record using an AV Foundation API that has delegate callbacks to tell you it’s done. My guess is that this won’t work for you, but I’d like to know why.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

External hardware is recording the video. The iOS device is simply a way to store, manage, and rename the recordings. The iOS device does not start the recording.

What's the actual physical hardware setup here? You say "external hardware" is recording the video, but what's the storage device being used?


Among the various possibilities:

- The storage device in question treated the original item as a link, so it created a new link to the same item. Thus, when you checked the size it showed up the same. Of course, if the hardware in question wasn't doing this, the whole idea of making a "temporary copy" of a potentially huge video file would be completely unworkable in several cases.

The iOS device is the storage device.


The video will most likely be under 100MB. Not huge. Albeit that's just a most likely case, I don't expect more than maybe 300MB. So I'm not worried about the file being too big.


If copying the file is just creating a new link, than how can I make a snapshot of the file for comparison? How would you guys proceed?

Have you tried sniffing the buffer to see if it's empty?

The iOS device is the storage device.

OK, so how does the video get on to the iOS device? What is doing the copying here?

It would help if you took a step back and explained all the parts of your system here. While they are obvious to you, I think it’s safe to say that the folks who are trying to help you out here are completely lost.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Ok, step by step:


  1. You plug the iOS device into the DVR (external hardware I mentioned earlier).
  2. You hit the "Record" button on the DVR.
  3. The DVR starts a recording in the documents folder of my app.
  4. Once you're done recording you hit the "Record" button again, and the DVR finalizes the video.


When the DVR starts a recording, it makes an mp4. I have an array of all mp4s in the documents folder. One of these is the file which is being recorded to. I have a for_in loop to pass each URL in the array to the function. Thus I also need this function to be quick if the array of mp4s is large. If I can find a way for it to be constantly running in the background without eating resources so that it shows that the recording is done that'd be awesome too, but that's a wish and not a requirement.


So, Eskimo. The my app is copying the file. The function I keep talking about is the one I'm trying to write that detects if a file is currently in the process of being written to by the DVR so that I can handle it differently.


The purpose of my app is to be a destination for the DVR to record to. To organize and display the videos to the user. As well as give the user the ability to rename and share the videos.


I'm sorry if I'm not clear in some of my posts. I'm self taught with a book, the forums, the internet, and experimentation as my learning sources. You guys are all on a whole other level than me.

3. The DVR starts a recording in the documents folder of my app.

Yeah, that’s the crux of this problem. I’ve no idea how an accessory (the DVR in this case) can write a file to your app’s Documents directory.

How is this accessory plugged in? Via the Lightning connector?

Also, did you integrate an SDK for the accessory? Failing that, how does the accessory know which app’s Documents directory to write to?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yup, you simply plug it in via the Lightning connector. The way that it knows which app's Documents folder to use as the destination directory is that it looks for a specific Bundle Identifier, which as everyone knows is unique to each application. I don't much else other than that.

Thanks for the details. At this point I’ve got to admit that I’m out of my depth. I’ve no idea how that works under the covers. It’s probably tied to MFi, and none of that info is public.

My only suggestion right now is that you try to do a coordinated read of the file using

NSFileCoordinator
. I’m hoping that whatever subsystem that’s writing this file is doing a coordinated write, so your coordinated read should block until the recording is done.

If that doesn’t work then my recommendation is that you open a DTS tech support incident and we can try and find an answer for you there.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks eskimo! I'll be trying that. I'll post what I end up doing.

I found a solution! It's not perfect, but its' good enough for now. I'll be updating it in the future.


func detectIfRecording(url: URL) -> Int {
    
    let df = DateFormatter()
    let timeFormat = DateFormatter.dateFormat(fromTemplate: "M/d/y/EEEE/a/H/m", options: 0, locale: Locale.current)
    df.dateFormat = timeFormat
    let keys : Set<URLResourceKey> = [URLResourceKey.contentModificationDateKey]
    let avAsset = AVAsset(url: url)
    
    do{
        
        let modDate = try url.resourceValues(forKeys: keys).contentModificationDate!
        let today = Date(timeIntervalSinceNow: 0)
        let modDateString = df.string(from: modDate)
        let todayString = df.string(from: today)
        
        if (avAsset.duration.value == 0) && (modDateString == todayString){
            return 1
        }
        else{
            return 0
        }
    }
    catch{
        print (error.localizedDescription)
    }
    
    return 2
}

I’m not sure what you’re trying to do with this code but line 4 is likely to be problematic.

dateFormat(fromTemplate:options:locale:)
is intended to be used for user-visible date strings, and you’re using it for strings that are clearly not user visible. QA1480 NSDateFormatter and Internet Dates discusses this gotcha in depth.

Looking through the rest of your code it seems you’re trying to compare two date values while ignoring the seconds component. There are much easier ways to do this. The one that springs immediately to mind is to convert each to date components (

DateComponents
), set the
second
property on each to fixed value, and then component the date components.

Did you try the coordinated read approach I suggested? Did it work?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

The issue I'm having with what I'm doing right now is that there's a difference in milliseconds. Which means even if I don't compare the seconds, there's a chance that due to the slight difference in milliseconds that there will be a difference in minutes.


I did look at the coordinated read/write, but from what saw it wouldn't work. I'm looking at it again to reconsider.

OK, so NSFileCoordinator does block the main thread. Just like you predicted. I'm working now on trying to come up with a code identify this and handle it properly. I'll post results.

Detect if video is still being written to
 
 
Q