AVAssetReader(asset: asset): The requested URL was not found on this server.

I'm trying the sample app from here:

https://developer.apple.com/documentation/vision/detecting_moving_objects_in_a_video

I made a tweak to read the video from library instead of document picker:

var recordedVideoURL: AVAsset?

@IBAction func uploadVideoForAnalysis(_ sender: Any) {
        var configuration = PHPickerConfiguration()
        configuration.filter = .videos
        configuration.selectionLimit = 1

        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = self

        present(picker, animated: true, completion: nil)
    }

The delegation method:

extension HomeViewController: PHPickerViewControllerDelegate {
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        guard let selectedResult = results.first else {
            print("assetIdentifier: null")
            dismiss(animated: true, completion: nil)
            return
        }

        selectedResult.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { [weak self] url, error in
            guard error == nil, let url = url else {
                print(error?.localizedDescription ?? "Failed to load image")
                return
            }
            let asset = AVAsset(url: url)
            self?.recordedVideoURL = asset 

            DispatchQueue.main.async { [weak self] in

                self?.dismiss(animated: true) { // dismiss the picker
                    self?.performSegue(withIdentifier: ContentAnalysisViewController.segueDestinationId,
                                       sender: self!)
                    self?.recordedVideoURL = nil
                }
            }
        }
    }
}

everything else is pretty much the same. Then, in the camera controller, it raised an error: "The requested URL was not found on this server." I put a debug break point and it show the error was from the line init AssetReader: let reader = AVAssetReader(asset: asset)

func startReadingAsset(_ asset: AVAsset, reader: AVAssetReader? = nil) {
        
        videoRenderView = VideoRenderView(frame: view.bounds)
        setupVideoOutputView(videoRenderView)
        videoFileReadingQueue.async { [weak self] in
            do {
                guard let track = asset.tracks(withMediaType: .video).first else {
                    throw AppError.videoReadingError(reason: "No video tracks found in the asset.")
                }
                
                let reader = AVAssetReader(asset: asset)
                let settings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]
                let output = AVAssetReaderTrackOutput(track: track, outputSettings: settings)
                if reader.canAdd(output) {
                    reader.add(output)
                } else {
                    throw AppError.videoReadingError(reason: "Couldn't add a track to the asset reader.")
                }
                
                if !reader.startReading() {
                    throw AppError.videoReadingError(reason: "Couldn't start the asset reader.")
                }
...

I tried to create a reader directly in the asset creation block, it worked:

 selectedResult.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { [weak self] url, error in
            guard error == nil, let url = url else {
                print(error?.localizedDescription ?? "Failed to load image")
                return
            }
            let asset = AVAsset(url: url)

            do {
                let reader = try AVAssetReader(asset: asset)
                self?.assetReader = reader
                print("reader: \(reader)")
            }
            catch let e { print("No reader: \(e)") }
...

but if I just move it a little bit to the Dispatch.main.async block, it printed out "No reader: The requested URL was not found on this server.

Therefore, I have to keep an instance of the reader and pass it to the cameraVC.

Can someone please explain why is this happening? What's the logic behind this?

Accepted Reply

The URL in loadFileRepresentation is temporary and will be deleted after the completion block. See the documentation here: https://developer.apple.com/documentation/foundation/nsitemprovider/2888338-loadfilerepresentation

This method writes a copy of the file’s data to a temporary file, which the system deletes when the completion handler returns.

I believe the recommendation is to copy the contents of that file to a known URL you control in your apps container if you want to continue to use it after loadFileRepresentation.

Replies

The URL in loadFileRepresentation is temporary and will be deleted after the completion block. See the documentation here: https://developer.apple.com/documentation/foundation/nsitemprovider/2888338-loadfilerepresentation

This method writes a copy of the file’s data to a temporary file, which the system deletes when the completion handler returns.

I believe the recommendation is to copy the contents of that file to a known URL you control in your apps container if you want to continue to use it after loadFileRepresentation.

I guess you're right, but I don't think that's a good solution. As the video can be large, temporary duplicates may cause issues with some devices' storage.

  • On any device or computer with an APFS file system the copies can actually be lightweight clones which do not actually require an exact copy of the data, meaning you will not need double the storage.

Add a Comment