Load AVAsset properties asynchronously - #26041
crazytonyli wants to merge 4 commits into
Conversation
|
| App Name | WordPress | |
| Configuration | Release-Alpha | |
| Build Number | 34646 | |
| Version | PR #26041 | |
| Bundle ID | org.wordpress.alpha | |
| Commit | eee87cc | |
| Installation URL | 6oumr33giijso |
|
| App Name | Jetpack | |
| Configuration | Release-Alpha | |
| Build Number | 34646 | |
| Version | PR #26041 | |
| Bundle ID | com.jetpack.alpha | |
| Commit | eee87cc | |
| Installation URL | 1e3e8l2pkebm8 |
The success handler already ran on the main thread, but the failure handler ran on the exporter's background thread.
Export videos with the async export API, which cancels with the task. If the duration can't be loaded, the export continues, as it did with the synchronous duration property.
ef1b75f to
eee87cc
Compare
| let observer = VideoSessionProgressObserver(videoSession: session, progressHandler: { value in | ||
| progress.completedUnitCount = Int64(Float(MediaExportProgressUnits.done) * value) | ||
| }) | ||
| defer { observer.stop() } |
There was a problem hiding this comment.
stop() doesn't prevent one more progressHandler call. VideoSessionProgressObserver.work() calls the handler before checking interrupt, so a tick that's already queued on the global queue still runs up to 100ms after this defer.
When the export fails or is cancelled, that tick lands after fail() has set completedUnitCount = totalUnitCount and writes it back to done * session.progress (e.g. 40) — so the progress ends unfinished, which is what the fail() doc comment says it prevents. The window between stop() and fail() is microseconds, so this is the likely ordering, not an edge case.
Not a regression — the old code left progress partial on export failure too — but it's a one-line fix to make fail() hold:
private func work() {
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(100)) {
guard !self.interrupt else { return }
self.progressHandler(self.videoSession.progress)
if self.videoSession.progress != 1 {
self.work()
}
}
}(interrupt is still an unsynchronized Bool read across threads, but that's pre-existing, and with the guard first a stale read only costs one extra tick.)


Description
The media upload gate and exporters read synchronous
AVAssetproperties (duration,isExportable,tracks,naturalSize,preferredTransform), which are deprecated since iOS 16. This PR replaces them withload(_:), which makes those code paths async.A few things worth a closer look:
MediaVideoExporternow exports withAVAssetExportSession.export(to:as:). It's back-deployed to iOS 13, so it works with our deployment target. Cancelling the returnedProgresscancels the export task, and the exporter still reportsvideoExportSessionCancelled, which Aztec relies on to remove the attachment silently.Blog.canUploadVideo(from:)is async andnonisolated(nonsending), so it reads the blog on the caller's actor, like any other access to the blog's properties.durationreturned, so the upload gate and the exporter behave as before.