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
- options.version = .current
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.8",
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",