web-dev-qa-db-ja.com

PHAsset(ビデオ)をAVAssetに同期的に変換します

AVPlayerとAVPlayerLayerを使用して再生するには、AVAssetオブジェクトを使用する必要があります。 AssetsLibraryが非推奨になったため、Photosフレームワークの使用を開始しました。これで、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];
    }];

ローカライズされた説明は、ビデオの絶対URLではないと思います。

また、PHImageManagerとrequestAVAssetForVideoに出くわしましたが、ビデオに関しては、optionsパラメーターにisSynchrounousプロパティがありません。これは、imageoptionsパラメーターの場合です。

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

これを行う同期方法はありますか?

ありがとう。

12
Objectif

いいえ、ありません。しかし、あなたはcan同期バージョンを構築できます:

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ウォッチドッグ で終了したりするリスクがあります。

非同期コールバックバージョンで動作するようにアプリを作り直す方がおそらく安全です。このようなもの:

__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];
    });
}];
26

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()
                }
            })
        })
    }
6

あなたはこれを使うことができます:

let asset = info[UIImagePickerControllerPHAsset] as? PHAsset

PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: nil) { (avAsset, _, _) in
    if let avAsset = avAsset {
        print(avAsset)
    }
}
1
Yunus T.

このトリックを試すことはできますが、AVAssetに変換したいフェーズが3,4または5つある場合に便利です。

    [[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に変換したときに、このメソッドを再度呼び出すことができます。これは効率的なコードではない可能性があることはわかっていますが、少しの目的で禁止するべきではありません。

0
Reckoner

以下は、要求を同期的に行うためにセマフォに依存する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
}
0
CodeBender