【问题标题】:Convert PHAsset (video) to AVAsset, synchronously将 PHAsset(视频)同步转换为 AVAsset
【发布时间】:2015-12-25 23:09:09
【问题描述】:

我需要使用 AVAsset 对象,以便使用 AVPlayer 和 AVPlayerLayer 播放它。我开始使用 Photos 框架,因为 AssetsLibrary 已被弃用。现在我有一个 PHAsset 对象数组,我需要将它们转换为 AVAsset。我尝试通过 PHFetchResult 枚举并使用 PHAsset 的本地化描述分配一个新的 AVAsset,但是当我播放它时它似乎没有显示任何视频。

    PHAssetCollection *assetColl = [self scaryVideosAlbum];

    PHFetchResult *getVideos = [PHAsset fetchAssetsInAssetCollection:assetColl options:nil];

    [getVideos enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
            NSURL *videoUrl = [NSURL URLWithString:asset.localizedDescription];
            AVAsset *avasset = [AVAsset assetWithURL:videoUrl];
            [tempArr addObject:avasset];
    }];

我假设本地化描述不是视频的绝对网址。

我还偶然发现了 PHImageManager 和 requestAVAssetForVideo,但是,当涉及到视频时,options 参数没有 isSynchrounous 属性,这是 image options 参数的情况。

        PHVideoRequestOptions *option = [PHVideoRequestOptions new];
        [[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:option resultHandler:^(AVAsset * _Nullable avasset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {

有同步的方法吗?

谢谢。

【问题讨论】:

    标签: ios video avasset photosframework


    【解决方案1】:

    不,没有。但是您可以构建一个同步版本:

    dispatch_semaphore_t    semaphore = dispatch_semaphore_create(0);
    
    PHVideoRequestOptions *option = [PHVideoRequestOptions new];
    __block AVAsset *resultAsset;
    
    [[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:option resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {
        resultAsset = avasset;
        dispatch_semaphore_signal(semaphore);
    }];
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // yay, we synchronously have the asset
    [self doSomethingWithAsset:resultAsset];
    

    但是,如果您在主线程上执行此操作并且 requestAVAssetForVideo: 花费的时间太长,您可能会锁定您的 UI,甚至可能被 iOS watchdog 终止。

    重新设计您的应用以使用异步回调版本可能更安全。像这样的:

    __weak __typeof(self) weakSelf = self;
    
    [[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:option resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf doSomethingWithAsset:avasset];
        });
    }];
    

    【讨论】:

    • 像魅力一样工作!但是您推荐这两个选项中的哪一个?我正在使用第二个,还有,__weak __typeof(self) weakSelf = self; 是什么?
    • 第二个选项更灵活(可以在主线程上调用而不用担心)所以我会选择它。 weakSelf 成语是一种避免保留循环的方法:stackoverflow.com/q/20030873/22147
    • 更好的体验可能是响应速度更快的应用程序。使用异步过程并让用户知道正在完成的工作。在旧设备上,可能需要更长的时间,所以我认为您不应该依赖同步过程。
    【解决方案2】:

    您可以尝试这个技巧,但当您有 3,4 或 5 个要转换为 AVAsset 的相位集时,它会很方便:

        [[PHImageManager defaultManager] requestAVAssetForVideo:assetsArray[0] options:option resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {
    //do something with this asset
           [[PHImageManager defaultManager] requestAVAssetForVideo:assetsArray[1] options:option resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {
    //so on...
           }
            }
    

    所以基本上,当您将 1 个阶段转换为 AVAsset 后,您可以再次调用此方法。我知道这可能不是一个有效的代码,但不应出于小目的而禁止它。

    【讨论】:

      【解决方案3】:

      对于Swift 2,您可以使用下面这个方法轻松播放PHAsset 的视频,

      导入文件

      import AVKit
      

      来自 PHAsset

      static func playVideo (view:UIViewController, asset:PHAsset) {
      
              guard (asset.mediaType == PHAssetMediaType.Video)
      
                  else {
                      print("Not a valid video media type")
                      return
              }
      
              PHCachingImageManager().requestAVAssetForVideo(asset, options: nil, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [NSObject : AnyObject]?) in
      
                  let asset = asset as! AVURLAsset
      
                  dispatch_async(dispatch_get_main_queue(), {
      
                      let player = AVPlayer(URL: asset.URL)
                      let playerViewController = AVPlayerViewController()
                      playerViewController.player = player
                      view.presentViewController(playerViewController, animated: true) {
                          playerViewController.player!.play()
                      }
                  })
              })
          }
      

      【讨论】:

        【解决方案4】:

        以下是一个 Swift 4 实现,它依赖于信号量来同步发出请求。

        对代码进行注释以解释各个步骤。

        func requestAVAsset(asset: PHAsset) -> AVAsset? {
            // We only want videos here
            guard asset.mediaType == .video else { return nil }
            // Create your semaphore and allow only one thread to access it
            let semaphore = DispatchSemaphore.init(value: 1)
            let imageManager = PHImageManager()
            var avAsset: AVAsset?
            // Lock the thread with the wait() command
            semaphore.wait()
            // Now go fetch the AVAsset for the given PHAsset
            imageManager.requestAVAsset(forVideo: asset, options: nil) { (asset, _, _) in
                // Save your asset to the earlier place holder
                avAsset = asset
                // We're done, let the semaphore know it can unlock now
                semaphore.signal()
            }
        
            return avAsset
        }
        

        【讨论】:

        【解决方案5】:

        导入

        import AVKit
        

        斯威夫特 5

        let phAsset = info[UIImagePickerControllerPHAsset] as? PHAsset
            
        PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: nil) { (avAsset, _, _) in
            print(avAsset)
        }
        

        【讨论】:

        • 这是异步方法
        • 我认为它没有用于同步方法
        【解决方案6】:

        那些来这里是为了异步方法的人。

        Swift 版本:

        func requestAVAsset(asset: PHAsset)-> AVAsset? {
                guard asset.mediaType == .video else { return nil }
                let phVideoOptions = PHVideoRequestOptions()
                phVideoOptions.version = .original
                let group = DispatchGroup()
                let imageManager = PHImageManager.default()
                var avAsset: AVAsset?
                group.enter()
                imageManager.requestAVAsset(forVideo: asset, options: phVideoOptions) { (asset, _, _) in
                    avAsset = asset
                    group.leave()
                    
                }
                group.wait()
                
                return avAsset
            }
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-03-09
          • 1970-01-01
          相关资源
          最近更新 更多