将 PHAsset(视频)同步转换为 AVAsset
Convert PHAsset (video) to AVAsset, synchronously
我需要使用 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];
}];
我假设本地化描述不是视频的绝对 url。
我也偶然发现了 PHImageManager 和 requestAVAssetForVideo,但是,涉及视频的选项参数没有 isSynchrounous 属性,图像选项参数就是这种情况。
PHVideoRequestOptions *option = [PHVideoRequestOptions new];
[[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:option resultHandler:^(AVAsset * _Nullable avasset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
有同步的方法吗?
谢谢。
不,没有。但是你可以构建一个同步版本:
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.[=14= 终止]
重新设计您的应用以使用异步回调版本可能更安全。像这样:
__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];
});
}];
你可以试试这个技巧,但当你有 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.I 时,您可以再次调用此方法知道这可能不是一个有效的代码,但不应该出于小目的而被禁止。
对于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()
}
})
})
}
下面是Swift4的实现,依赖信号量同步发出请求
对代码进行注释以解释各个步骤。
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
}
导入
import AVKit
Swift 5
let phAsset = info[UIImagePickerControllerPHAsset] as? PHAsset
PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: nil) { (avAsset, _, _) in
print(avAsset)
}
那些来这里异步方法的人。
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
}
我需要使用 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];
}];
我假设本地化描述不是视频的绝对 url。
我也偶然发现了 PHImageManager 和 requestAVAssetForVideo,但是,涉及视频的选项参数没有 isSynchrounous 属性,图像选项参数就是这种情况。
PHVideoRequestOptions *option = [PHVideoRequestOptions new];
[[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:option resultHandler:^(AVAsset * _Nullable avasset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
有同步的方法吗?
谢谢。
不,没有。但是你可以构建一个同步版本:
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.[=14= 终止]
重新设计您的应用以使用异步回调版本可能更安全。像这样:
__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];
});
}];
你可以试试这个技巧,但当你有 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.I 时,您可以再次调用此方法知道这可能不是一个有效的代码,但不应该出于小目的而被禁止。
对于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()
}
})
})
}
下面是Swift4的实现,依赖信号量同步发出请求
对代码进行注释以解释各个步骤。
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
}
导入
import AVKit
Swift 5
let phAsset = info[UIImagePickerControllerPHAsset] as? PHAsset
PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: nil) { (avAsset, _, _) in
print(avAsset)
}
那些来这里异步方法的人。
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
}