播放通过 CloudKit 下载的视频作为 CKAsset - iOS

Play video downloaded through CloudKit as CKAsset - iOS

我正在制作一个录制视频的应用程序,使用 CloudKit 和 CKAsset 将其上传到 iCloud,然后下载文件并在 AVPlayer 中播放。这都是写在Swift2.0

我已经下载了数据,我想我已经可以参考它了,但我不确定。当我将 URL 转换为 NSData 对象并将其打印到控制台时,Data/garbage 会打印。但是,视频文件会作为二进制文件下载。我能够转到 CloudKit 仪表板并下载文件并将“.mov”附加到它,它在 Quicktime 中打开没问题。

所以我认为我的主要问题是我不知道如何让视频文件真正播放,因为该文件没有扩展名。我尝试使用 URLByAppendingPathExtension() 将“.mov”附加到末尾,但无济于事。让我知道任何想法!

上传视频

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
    let tempURL = info[UIImagePickerControllerMediaURL] as! NSURL

    dismissViewControllerAnimated(true) { () -> Void in
        self.uploadVideoToiCloud(tempURL)
        print("\n     Before Upload: \(tempURL)\n")
    }
}

func uploadVideoToiCloud(url: NSURL) {
    let videoRecord = CKRecord(recordType: "video", recordID: id)

    videoRecord["title"] = "This is the title"

    let videoAsset = CKAsset(fileURL: url)
    videoRecord["video"] = videoAsset

    CKContainer.defaultContainer().privateCloudDatabase.saveRecord(videoRecord) { (record, error) -> Void in
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            if error == nil {
                print("upload successful")

            } else {
                print(error!)
            }
        })
    }
}

下载视频

func downloadVideo(id: CKRecordID) {

    privateDatabase.fetchRecordWithID(id) { (results, error) -> Void in

        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            if error != nil {

                    print(" Error Fetching Record  " + error!.localizedDescription)
            } else {
                if results != nil {
                    print("pulled record")

                    let record = results!
                    let videoFile = record.objectForKey("video") as! CKAsset

                    self.videoURL = videoFile.fileURL

                    print("     After Download: \(self.videoURL!)")

                    self.videoAsset = AVAsset(URL: self.videoURL!)
                    self.playVideo()

                } else {
                    print("results Empty")
                }
            }
        }
    }
}

最后的解决办法是我在写入数据之前忘记指定文件名。我正在使用 URLByAppendingPathExtension,它搞砸了 URL,最终使用 URLByAppendingPathComponent 并在那里添加了一个文件名。这是对我有用的解决方案!感谢大家的评论。

func downloadVideo(id: CKRecordID) {

    privateDatabase.fetchRecordWithID(id) { (results, error) -> Void in

        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            if error != nil {

                    print(" Error Fetching Record  " + error!.localizedDescription)
            } else {
                if results != nil {
                    print("pulled record")

                    let record = results as CKRecord!
                    let videoFile = record.objectForKey("video") as! CKAsset

                    self.videoURL = videoFile.fileURL as NSURL!
                    let videoData = NSData(contentsOfURL: self.videoURL!)

                    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
                    let destinationPath = NSURL(fileURLWithPath: documentsPath).URLByAppendingPathComponent("filename.mov", isDirectory: false) //This is where I messed up.

                    NSFileManager.defaultManager().createFileAtPath(destinationPath.path!, contents:videoData, attributes:nil)

                    self.videoURL = destinationPath

                    self.videoAsset = AVURLAsset(URL: self.videoURL!)
                    self.playVideo()

                } else {
                    print("results Empty")
                }
            }
        }
    }
}

根本问题是 AVPlayer 需要文件扩展名,例如 .mov,但 CKAssetfileURL 属性 指向缺少扩展名的文件.最干净的解决方案是创建一个硬 link,它可以避免移动兆字节的数据并且不需要磁盘 space:

- (NSURL *)videoURL {
    return [self createHardLinkToVideoFile];
}


- (NSURL *)createHardLinkToVideoFile {
    NSError *err;
    if (![self.hardURL checkResourceIsReachableAndReturnError:nil]) {
        if (![[NSFileManager defaultManager] linkItemAtURL:self.asset.fileURL toURL:self.hardURL error:&err]) {
            // if creating hard link failed it is still possible to create a copy of self.asset.fileURL and return the URL of the copy
        }
    }
    return self.hardURL;
}

- (void)removeHardLinkToVideoFile {
    NSError *err;
    if ([self.hardURL checkResourceIsReachableAndReturnError:nil]) {
        if (![[NSFileManager defaultManager] removeItemAtURL:self.hardURL error:&err]) {
        }
    }
}

- (NSURL *)hardURL {
    return [self.asset.fileURL URLByAppendingPathExtension:@"mov"];
}

然后在视图控制器中,将 AVPlayer 指向 videoURL 而不是 asset.fileURL

这是从 CloudKit 下载多个视频的解决方案。使用此功能,您可以将视频存储在多个目的地并轻松获取文件路径

import AVKit
import CloudKit

var assetForVideo = [CKAsset]()
var videoURLForGetVideo = NSURL()
database.perform(queryForVideo, inZoneWith: nil) { [weak self] record, Error in
    guard  let records = record, Error == nil else {
        return
    }
    DispatchQueue.main.async { [self] in
        self?.assetForVideo = records.compactMap({ [=10=].value(forKey: "video") as? CKAsset })

        for (i,dt) in self!.assetForVideo.enumerated(){
            self!.videoURLForGetVideo = (dt.fileURL as NSURL?)!
            let videoData = NSData(contentsOf: self!.videoURLForGetVideo as URL)
            let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            let destinationPath = NSURL(fileURLWithPath: documentsPath).appendingPathComponent(self!.assetForVideo.count == i ? "filename\(self!.assetForVideo.count).mov" : "filename\(i+1).mov", isDirectory: false)! as NSURL
            FileManager.default.createFile(atPath: destinationPath.path!, contents: videoData as Data?, attributes: nil)
            self?.videoURLForGetVideo = destinationPath
            self!.videoAssett = AVURLAsset(url: self!.videoURLForGetVideo as URL)
            let abc = self!.videoAssett.url
            let videoURL = URL(string: "\(abc)")
        }
    }
}