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

Posted

技术标签:

【中文标题】播放通过 CloudKit 作为 CKAsset 下载的视频 - iOS【英文标题】:Play video downloaded through CloudKit as CKAsset - iOS 【发布时间】:2015-10-01 02:03:56 【问题描述】:

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

我已经下载了数据,我想我可以参考它,但我不确定。当我将 URL 转换为 NSData 对象并将其打印到控制台时,会打印数据/垃圾。然而,视频文件作为二进制文件下载。我能够去 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")
                
            
        
    

【问题讨论】:

在上传之前移动文件并重命名 您需要为每个视频生成一个新的文件名,而不是capturedVideo 能否请您添加代码中提到的Self.playVideo()函数的完整代码 【参考方案1】:

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

- (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

【讨论】:

【参考方案2】:

解决方案是我在写入数据之前忘记指定文件名。我正在使用 URLByAppendingPathExtension 并且它弄乱了 URL,最终使用 URLByAppendingPathComponent 并在那里添加了一个文件名。这是对我有用的解决方案!感谢 cmets 伙计们。

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")
                
            
        
    

【讨论】:

【参考方案3】:

这是从 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( $0.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)")
        
    

【讨论】:

以上是关于播放通过 CloudKit 作为 CKAsset 下载的视频 - iOS的主要内容,如果未能解决你的问题,请参考以下文章

Xcode:CloudKit 中的 CKAsset

CloudKit 的 CKasset 文件大小限制

在 CloudKit 中将图像存储为 CKAsset,图像倒置

将 CloudKit 记录保存到本地文件保存除 CKAsset 之外的所有字段

CKAsset 未保存到 CloudKit - 保存所有其他字段

Swift 3 使用 CKAsset 将图像发送到 CloudKit