Skip to content

Commit

Permalink
video export fix
Browse files Browse the repository at this point in the history
  • Loading branch information
duzexu committed Nov 14, 2024
1 parent bcde6fe commit ea52534
Show file tree
Hide file tree
Showing 19 changed files with 440 additions and 307 deletions.
1 change: 1 addition & 0 deletions ADPhotoKit/Classes/Base/ADEnum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation
/// Error throw by framework.
public enum ADError: Error {
case noAuthorization
case exportSessionCreateFailed
}

/// Capture resolution.
Expand Down
6 changes: 3 additions & 3 deletions ADPhotoKit/Classes/Base/ADPhotoKitConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,13 @@ public class ADPhotoKitConfiguration {
/// - Note: If contain `.imageStkr`, you must set `imageStickerDataSource` or `customImageStickerSelectVC`.
/// - Note: If contain `.bgMusic`, you must set `videoMusicDataSource` or `customVideoMusicSelectVC`.
public var systemVideoEditTools: ADVideoEditTools = .all

public var customVideoPlayableBlock: ((AVAsset) -> ADVideoPlayable)?
public var customVideoPlayable: ADVideoPlayable.Type?

/// User custom video edit tools. Custom tools is default add after system tools.
public var customVideoEditToolsBlock: (() -> [ADVideoEditTool])?

public var customVideoEditVCBlock: ((AVAsset, ADVideoEditInfo?, ADVideoEditOptions) -> ADVideoEditConfigurable)?
public var customVideoEditVCBlock: ((ADPhotoKitConfig, AVAsset, ADVideoEditInfo?) -> ADVideoEditConfigurable)?

/* =============== bgm =============== */

Expand Down
11 changes: 8 additions & 3 deletions ADPhotoKit/Classes/Core/ADPhotoKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ public struct ADAssetBrowserOptions: OptionSet {
public static let selectThumbnil = ADAssetBrowserOptions(rawValue: 1 << 1)
/// Display the index of the selected photos at navbar.
public static let selectIndex = ADAssetBrowserOptions(rawValue: 1 << 2)
/// Allow framework fetch image when callback.
public static let fetchImage = ADAssetBrowserOptions(rawValue: 1 << 3)
/// Allow framework fetch result when callback.
/// Media type image return `UIImage` and video return 'AVAsset'.
public static let fetchResult = ADAssetBrowserOptions(rawValue: 1 << 3)
/// In single selection mode, whether to display the selection button.
public static let selectBtnWhenSingleSelect = ADAssetBrowserOptions(rawValue: 1 << 4)
/// Whether to display the selected count on the button.
Expand All @@ -114,9 +115,13 @@ public struct ADAssetBrowserOptions: OptionSet {
public static let allowEditImage = ADAssetBrowserOptions(rawValue: 1 << 9)
// Allow edited video.
public static let allowEditVideo = ADAssetBrowserOptions(rawValue: 1 << 10)
// Export video after edited video.
/// Indicates whether to export the video immediately after editing. If not contains `exportVideoAfterEdit`, will be exported in the final callback.
/// - Note: This option only takes effect when contains `fetchResult`.
public static let exportVideoAfterEdit = ADAssetBrowserOptions(rawValue: 1 << 11)

/// Default options.
public static let `default`: ADAssetBrowserOptions = [.selectOriginal, .selectThumbnil, .selectIndex, .fetchImage, .totalOriginalSize, .selectCountOnDoneBtn, .saveImageAfterEdit, .saveVideoAfterEdit, .allowEditImage, .allowEditVideo]
public static let `default`: ADAssetBrowserOptions = [.selectOriginal, .selectThumbnil, .selectIndex, .fetchResult, .totalOriginalSize, .selectCountOnDoneBtn, .saveImageAfterEdit, .saveVideoAfterEdit, .allowEditImage, .allowEditVideo]

public init(rawValue: Int) {
self.rawValue = rawValue
Expand Down
9 changes: 1 addition & 8 deletions ADPhotoKit/Classes/CoreUI/ADAssetBrowserController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -356,14 +356,7 @@ private extension ADAssetBrowserController {
func editVideo(_ asset: AVAsset?) {
#if Module_VideoEdit
if let asset = asset {
var options: ADVideoEditOptions = ADVideoEditOptions()
if let min = config.params.minVideoTime {
options.append(.minTime(CGFloat(min)))
}
if let max = config.params.maxVideoTime {
options.append(.maxTime(CGFloat(max)))
}
let vc = ADVideoEditConfigure.videoEditVC(asset: asset, editInfo: dataSource.current!.videoEditInfo, options: options)
let vc = ADVideoEditConfigure.videoEditVC(config: config, asset: asset, editInfo: dataSource.current!.videoEditInfo)
vc.videoDidEdit = { [weak self] editInfo in
self?.didVideoEditInfoUpdate(editInfo)
}
Expand Down
8 changes: 4 additions & 4 deletions ADPhotoKit/Classes/CoreUI/ADAssetModelBrowserController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ class ADAssetModelBrowserController: ADAssetBrowserController {

override func finishSelection() {
didSelectsUpdate()
if config.browserOpts.contains(.fetchImage) {
let opt = ADAssetOperation.ImageOptConfig(isOriginal: toolBarView.isOriginal, selectAsGif: config.assetOpts.contains(.selectAsGif))
listData.fetchSelectImages(config: opt, inQueue: config.fetchImageQueue) { [weak self] selected in
if config.browserOpts.contains(.fetchResult) {
let opt = ADAssetOperation.OptConfig(isOriginal: toolBarView.isOriginal, selectAsGif: config.assetOpts.contains(.selectAsGif), saveEditVideo: config.browserOpts.contains(.saveVideoAfterEdit))
listData.fetchSelectResults(config: opt, inQueue: config.fetchImageQueue) { [weak self] selected in
self?.config.pickerSelect?(selected, self!.toolBarView.isOriginal)
self?.navigationController?.dismiss(animated: true, completion: nil)
}
}else{
let selected = listData.selects.map { ADPhotoKitUI.Asset($0.asset,$0.result(with: nil),nil) }
let selected = listData.selects.map { ADPhotoKitUI.Asset($0.asset,$0.result(image: nil),nil) }
config.pickerSelect?(selected, toolBarView.isOriginal)
navigationController?.dismiss(animated: true, completion: nil)
}
Expand Down
67 changes: 46 additions & 21 deletions ADPhotoKit/Classes/CoreUI/ADAssetOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,18 @@ import Kingfisher

class ADAssetOperation: Operation {

struct ImageOptConfig {
struct OptConfig {
let isOriginal: Bool
let selectAsGif: Bool
}

struct VideoOptConfig {
let saveEditVideo: Bool
}

let model: ADSelectAssetModel
let imageConfig: ImageOptConfig?
let videoConfig: VideoOptConfig?
let progress: ADPhotoManager.ADAssetProgressHandler?
let config: OptConfig
let completion: ((ADPhotoKitUI.Asset) -> Void)

private var requestID: PHImageRequestID?
private var exporter: ADVideoExporter?

private var _isFinished: Bool = false {
willSet { willChangeValue(forKey: "isFinished") }
Expand All @@ -38,14 +35,10 @@ class ADAssetOperation: Operation {
}

init(model: ADSelectAssetModel,
imageConfig: ImageOptConfig? = nil,
videoConfig: VideoOptConfig? = nil,
progress: ADPhotoManager.ADAssetProgressHandler? = nil,
config: OptConfig,
completion: @escaping ((ADPhotoKitUI.Asset) -> Void)) {
self.model = model
self.imageConfig = imageConfig
self.videoConfig = videoConfig
self.progress = progress
self.config = config
self.completion = completion
super.init()
}
Expand All @@ -59,25 +52,57 @@ class ADAssetOperation: Operation {
_isExecuting = true

if model.asset.mediaType == .video {

#if Module_VideoEdit
if model.videoEditInfo != nil && model.videoEditInfo!.editUrl == nil {
let type = ADPhotoKitConfiguration.default.customVideoPlayable ?? ADVideoPlayerView.self
let exporter = type.exporter(from: model.videoEditInfo!.originAsset, editInfo: model.videoEditInfo!)
let path = NSTemporaryDirectory().appending("\(UUID().uuidString).mp4")
exporter.export(to: path) { [weak self] url, error in
guard let strong = self else { return }
if let url = url {
strong.model.videoEditInfo!.editUrl = url
if strong.config.saveEditVideo {
ADPhotoManager.saveVideoToAlbum(url: url, completion: nil)
}
}
self?.completion((strong.model.asset,strong.model.result(asset: strong.model.videoEditInfo!.originAsset),nil))
self?.done()
}
self.exporter = exporter
return
}
#endif
requestID = ADPhotoManager.fetch(for: model.asset, type: .assert, progress: nil, completion: { [weak self] (data, info, _) in
guard let strong = self else { return }
let error = info?[PHImageErrorKey] as? NSError
self?.completion((strong.model.asset,strong.model.result(asset: data as? AVAsset),error))
self?.done()
})
}else{
if model.asset.isGif && imageConfig?.selectAsGif == true {
requestID = ADPhotoManager.fetch(for: model.asset, type: .originImageData, progress: progress, completion: { [weak self] (data, info, _) in
if model.asset.isGif && config.selectAsGif {
requestID = ADPhotoManager.fetch(for: model.asset, type: .originImageData, progress: nil, completion: { [weak self] (data, info, _) in
guard let strong = self else { return }
if let d = data as? Data {
self?.completion((strong.model.asset,strong.model.result(with: KingfisherWrapper.image(data: d, options: .init())),nil))
self?.completion((strong.model.asset,strong.model.result(image: KingfisherWrapper.image(data: d, options: .init())),nil))
}else{
let error = info?[PHImageErrorKey] as? NSError
self?.completion((strong.model.asset,strong.model.result(with: nil),error))
self?.completion((strong.model.asset,strong.model.result(image: nil),error))
}
self?.done()
})
}else{
let size: CGSize? = imageConfig?.isOriginal == true ? nil : model.asset.browserSize
requestID = ADPhotoManager.fetch(for: model.asset, type: .image(size: size, synchronous: true), progress: progress, completion: { [weak self] (image, info, _) in
#if Module_ImageEdit
if !config.isOriginal && model.imageEditInfo?.originImg != nil {
completion((model.asset,model.result(image: model.imageEditInfo?.originImg),nil))
done()
return
}
#endif
let size: CGSize? = config.isOriginal ? nil : model.asset.browserSize
requestID = ADPhotoManager.fetch(for: model.asset, type: .image(size: size, synchronous: true), progress: nil, completion: { [weak self] (image, info, _) in
guard let strong = self else { return }
let error = info?[PHImageErrorKey] as? NSError
self?.completion((strong.model.asset,strong.model.result(with: image as? UIImage),error))
self?.completion((strong.model.asset,strong.model.result(image: image as? UIImage),error))
self?.done()
})
}
Expand Down
62 changes: 48 additions & 14 deletions ADPhotoKit/Classes/CoreUI/ADPhotoKitUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ public enum ADPickerStyle {

/// Asset fetch result.
public struct ADAssetResult {
/// Image fetch with asset. It's `nil` if `browserOpts` not contain `.fetchImage` or error occur when fetching.
/// Image fetch with asset. It's `nil` if media type is video or `browserOpts` not contain `.fetchResult` or error occur when fetching.
public let image: UIImage?

/// AVAsset fetch with asset. It's `nil` if media type is image or `browserOpts` not contain `.fetchResult` or error occur when fetching.
public let asset: AVAsset?

#if Module_ImageEdit
/// Image edited info. It can be `nil` if image is not edit.
public let imageEditInfo: ADImageEditInfo?
Expand Down Expand Up @@ -68,12 +71,24 @@ public struct ADConstraintParams {
}

extension ADSelectAssetModel {
func result(with image: UIImage?) -> ADAssetResult? {
func result(image: UIImage?) -> ADAssetResult? {
#if Module_ImageEdit || Module_VideoEdit
return ADAssetResult(image: image, imageEditInfo: imageEditInfo, videoEditInfo: videoEditInfo)
return ADAssetResult(image: image, asset: nil, imageEditInfo: imageEditInfo, videoEditInfo: videoEditInfo)
#else
if let img = image {
return ADAssetResult(image: img)
return ADAssetResult(image: img, asset: nil)
}else{
return nil
}
#endif
}

func result(asset: AVAsset?) -> ADAssetResult? {
#if Module_ImageEdit || Module_VideoEdit
return ADAssetResult(image: nil, asset: asset, imageEditInfo: imageEditInfo, videoEditInfo: videoEditInfo)
#else
if let asset = asset {
return ADAssetResult(image: nil, asset: asset)
}else{
return nil
}
Expand Down Expand Up @@ -375,7 +390,7 @@ public class ADPhotoKitConfig {

extension ADAssetListDataSource {

func fetchSelectImages(config: ADAssetOperation.ImageOptConfig, inQueue: OperationQueue, completion: @escaping (([ADPhotoKitUI.Asset])->Void)) {
func fetchSelectResults(config: ADAssetOperation.OptConfig, inQueue: OperationQueue, completion: @escaping (([ADPhotoKitUI.Asset])->Void)) {
let hud = ADProgress.progressHUD()

var timeout: Bool = false
Expand All @@ -389,20 +404,39 @@ extension ADAssetListDataSource {
var result: [ADPhotoKitUI.Asset?] = Array(repeating: nil, count: selects.count)
var operations: [Operation] = []
for (i,item) in selects.enumerated() {
let op = ADAssetOperation(model: item, imageConfig: config, progress: nil) { (asset) in
let op = ADAssetOperation(model: item, config: config) { asset in
result[i] = asset
}
operations.append(op)
}

DispatchQueue.main.async {
inQueue.addOperations(operations, waitUntilFinished: true)
hud.hide()
if !timeout {
completion(result.compactMap { $0 })
}else{
completion([])
if #available(iOS 13, *) {
inQueue.addOperations(operations, waitUntilFinished: false)
inQueue.addBarrierBlock {
DispatchQueue.main.async {
hud.hide()
if !timeout {
completion(result.compactMap { $0 })
}else{
completion([])
}
}
}
}else{
let finishOp = BlockOperation {
DispatchQueue.main.async {
hud.hide()
if !timeout {
completion(result.compactMap { $0 })
}else{
completion([])
}
}
}
for op in operations {
finishOp.addDependency(op)
}
operations.append(finishOp)
inQueue.addOperations(operations, waitUntilFinished: false)
}
}

Expand Down
Loading

0 comments on commit ea52534

Please sign in to comment.