web-dev-qa-db-fra.com

Convertir PHAsset (vidéo) en AVAsset, de manière synchrone

Je dois utiliser l'objet AVAsset pour pouvoir le lire avec AVPlayer et AVPlayerLayer. J'ai commencé à utiliser le cadre Photos depuis qu'AssetsLibrary est obsolète. Maintenant, je suis arrivé au point où j'ai un tableau d'objets PHAsset et que je dois les convertir en AVAsset. J'ai essayé d'énumérer via PHFetchResult et d'attribuer un nouvel AVAsset à l'aide de la description localisée de PHAsset, mais il ne semble pas afficher de vidéo lorsque je le lis.

    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];
    }];

Je suppose que la description localisée n'est pas l'URL absolue de la vidéo.

Je suis également tombé sur PHImageManager et sur requestAVAssetForVideo; toutefois, le paramètre options en ce qui concerne la vidéo n'a pas de propriété isSynchrounous, ce qui est le cas du paramètre options d'image.

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

Y a-t-il une manière synchrone de faire ceci?

Merci.

10
Objectif

Non, il n'y en a pas. Mais vous pouvez construire une version synchrone:

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];

Toutefois, si vous faites cela sur le thread principal et que requestAVAssetForVideo: prend trop de temps, vous risquez de verrouiller votre interface utilisateur ou même de vous faire arrêter par le iOS watchdog .

Il est probablement plus sûr de retravailler votre application pour qu'elle fonctionne avec la version de rappel asynchrone. Quelque chose comme ça:

__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];
    });
}];
25
Rhythmic Fistman

Pour Swift 2 , vous pouvez facilement lire la vidéo avec PHAsset en utilisant la méthode ci-dessous,

Importer le fichier

import AVKit

De 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
Sazzad Hissain Khan

Vous pouvez essayer cette astuce, mais elle est pratique lorsque vous souhaitez convertir 3,4 ou 5 phassets au format 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...
       }
        }

En gros, vous pouvez rappeler cette méthode lorsque vous avez converti 1 phasset en AVAsset.Je sais que ce n'est peut-être pas un code efficace, mais il ne devrait pas être interdit pour de petites raisons.

0
Reckoner

Ce qui suit est une implémentation de Swift 4 qui repose sur un sémaphore pour effectuer la demande de manière synchrone.

Le code est commenté pour expliquer les différentes étapes.

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