I'm having a weird problem moving a .mov
file created by my app from the documents folder to the camera roll. A bit of background:
The app makes time lapse movies. It works specifically with the devices that have a 12 megapixel 4032x3024 sensor. It created the movies in the app's documents folder. The movies can be saved as either 4k or HD. They can also be saved as a 4:3 aspect ratio movie of the entire sensor, or a 16:9 crop of the sensor. If the user wants the movie to be stored in the Camera Roll of the device, they can set that option. My problem exists when trying to move a full size movie (4032x3024) from the app's documents folder to the Camera Roll. I get this error:
Error Domain=NSCocoaErrorDomain Code=-1 "(null)"
The movie is fine, it's still sitting in the document's folder. It just can't be copied to the Camera Roll. If I do this same operation through the same code with any of the other sizes, no problem. A 4:3 HD (1440x1080) works fine, a 16:9 HD (1920x1080) works fine, a 16:9 4k (3880x2160) works fine. It's just the 4:3 4k (4032x3024) that generates this error when I try to move it.
This is the code that does the move:
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: cameraRollURL!)
The URL is OK because it works with the other sizes just fine.
I did fix this when I switched from pull to push approach (as seen from writter side).
Pull approach is when I have an array of UIImage (captured from camera), and feed writer when it is ready to process next image. Push approach is when I have single UIImage (captured from camera) one by one, and feed writer if it is ready to process next image.
Not sure what's a cause. Maybe processing message loop between AVWritter calls.
Advantage: you do not allocate bunch of GB memory in UIImage array at any time if capturing longer video.
Disadvantage: writer may not be ready to write sample if capturing is happening too fast so frames can be dropped because it is processing in real time.
Swift 4:
func initVideo(videoSettings: [String: Any]) -> (assetWriter: AVAssetWriter, writeInput: AVAssetWriterInput, bufferAdapter:AVAssetWriterInputPixelBufferAdaptor)? {
if(FileManager.default.fileExists(atPath: ImagesToVideoUtils.tempPath)){
guard (try? FileManager.default.removeItem(atPath: ImagesToVideoUtils.tempPath)) != nil else {
print("remove path failed")
return nil
}
}
let assetWriter = try! AVAssetWriter(url: ImagesToVideoUtils.fileURL, fileType: AVFileType.mov)
let writeInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
assert(assetWriter.canAdd(writeInput), "add failed")
assetWriter.add(writeInput)
let bufferAttributes:[String: Any] = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB)]
let bufferAdapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writeInput, sourcePixelBufferAttributes: bufferAttributes)
return (assetWriter, writeInput, bufferAdapter)
}
func exportVideo_start(assetWriter: AVAssetWriter) -> (DispatchQueue) {
assetWriter.startWriting()
assetWriter.startSession(atSourceTime: CMTime.zero)
let mediaInputQueue = DispatchQueue(label: "mediaInputQueue")
return (mediaInputQueue)
}
func exportVideo_write(videoSettings: [String: Any], img: UIImage, assetWriter: AVAssetWriter, writeInput: AVAssetWriterInput, bufferAdapter:AVAssetWriterInputPixelBufferAdaptor, mediaInputQueue: DispatchQueue, timestamp: CMTime) {
if (writeInput.isReadyForMoreMediaData){
var sampleBuffer:CVPixelBuffer?
autoreleasepool{
sampleBuffer = self.newPixelBufferFrom(cgImage: img.cgImage!, videoSettings: videoSettings)
}
bufferAdapter.append(sampleBuffer!, withPresentationTime: timestamp)
print("Adding frame at \(timestamp)")
}
}
func exportVideo_end( assetWriter: AVAssetWriter, writeInput: AVAssetWriterInput) {
writeInput.markAsFinished()
assetWriter.finishWriting {
DispatchQueue.main.sync {
print("Finished writting")
ImagesToVideoUtils.saveToCameraRoll(videoURL: ImagesToVideoUtils.fileURL)
}
}
}
- (void)saveVideoPath:(NSString *)videoPath {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSURL *url = [NSURL fileURLWithPath:videoPath];
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success) {
NSLog(@"succ");
}
if (error) {
NSLog(@"%@",error);
}
}];
});
}
来源:https://stackoverflow.com/questions/45029084/error-domain-nscocoaerrordomain-code-1-null-when-moving-mov-to-camera-roll