expo-pro-video-editor 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Photos
|
|
2
|
+
import AVFoundation
|
|
2
3
|
import Foundation
|
|
3
4
|
|
|
4
5
|
/// Exports a photo-library video PHAsset to a real, playable local file.
|
|
@@ -48,7 +49,11 @@ enum PhotoLibraryVideoExporter {
|
|
|
48
49
|
let options = PHVideoRequestOptions()
|
|
49
50
|
options.isNetworkAccessAllowed = true
|
|
50
51
|
options.deliveryMode = .highQualityFormat
|
|
51
|
-
|
|
52
|
+
// .original (not .current/.unadjusted): requestExportSession can otherwise resolve an
|
|
53
|
+
// adjusted/proxy render for an edited or iCloud-optimized asset, which has been observed
|
|
54
|
+
// to silently drop the source track's portrait preferredTransform — see the defensive
|
|
55
|
+
// re-application below for why that must not be trusted either way.
|
|
56
|
+
options.version = .original
|
|
52
57
|
|
|
53
58
|
let exportSession: AVAssetExportSession? = await withCheckedContinuation { continuation in
|
|
54
59
|
PHImageManager.default().requestExportSession(
|
|
@@ -64,6 +69,37 @@ enum PhotoLibraryVideoExporter {
|
|
|
64
69
|
throw ExportError.exportSessionUnavailable(localIdentifier)
|
|
65
70
|
}
|
|
66
71
|
|
|
72
|
+
// requestExportSession's returned session doesn't reliably preserve the source video
|
|
73
|
+
// track's preferredTransform (the rotation matrix that makes an iOS-recorded portrait
|
|
74
|
+
// video, which is actually stored as landscape pixel data, display upright) — observed
|
|
75
|
+
// as an exported file reporting landscape pixel dimensions for a portrait source,
|
|
76
|
+
// rendering zoomed/cropped under a `cover`-fit vertical player. Re-apply it explicitly
|
|
77
|
+
// via a video composition rather than trusting the export session's default behavior.
|
|
78
|
+
if let sourceAsset = exportSession.asset,
|
|
79
|
+
let videoTrack = sourceAsset.tracks(withMediaType: .video).first
|
|
80
|
+
{
|
|
81
|
+
let transform = videoTrack.preferredTransform
|
|
82
|
+
let isPortrait = abs(transform.b) == 1 && abs(transform.c) == 1
|
|
83
|
+
let naturalSize = videoTrack.naturalSize
|
|
84
|
+
let renderSize = isPortrait
|
|
85
|
+
? CGSize(width: naturalSize.height, height: naturalSize.width)
|
|
86
|
+
: naturalSize
|
|
87
|
+
|
|
88
|
+
let composition = AVMutableVideoComposition()
|
|
89
|
+
composition.renderSize = renderSize
|
|
90
|
+
composition.frameDuration = CMTime(value: 1, timescale: 30)
|
|
91
|
+
|
|
92
|
+
let instruction = AVMutableVideoCompositionInstruction()
|
|
93
|
+
instruction.timeRange = CMTimeRange(start: .zero, duration: sourceAsset.duration)
|
|
94
|
+
|
|
95
|
+
let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
|
|
96
|
+
layerInstruction.setTransform(transform, at: .zero)
|
|
97
|
+
instruction.layerInstructions = [layerInstruction]
|
|
98
|
+
composition.instructions = [instruction]
|
|
99
|
+
|
|
100
|
+
exportSession.videoComposition = composition
|
|
101
|
+
}
|
|
102
|
+
|
|
67
103
|
exportSession.outputURL = destinationURL
|
|
68
104
|
exportSession.outputFileType = .mov
|
|
69
105
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-pro-video-editor",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Native video composition (trim, filters, text/image overlays, audio mixing) for Expo/React Native, adapted from pro_video_editor's AVFoundation and Media3 Transformer implementation.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|